diff --git a/.clawker.yaml b/.clawker.yaml index d5c2a0e..c31c5e9 100644 --- a/.clawker.yaml +++ b/.clawker.yaml @@ -1,63 +1,60 @@ -# Clawker configuration for openclaw-docker -# Go CLI that generates OpenClaw Docker deployment artifacts -# Learn more about Clawker at https://clawker.dev; https://docs.clawker.dev; https://github.com/schmitthub/clawker - -build: - image: "node:24" - - packages: - - ca-certificates - - openssh-client - - inject: - after_user_switch: - - ENV NVM_DIR="/home/claude/.nvm" - - ENV NODE_VERSION="24" - - ENV PATH="/home/claude/.pulumi/bin:$PATH" - - instructions: - # Install nvm, Node.js, and pnpm as the claude user - user_run: - - cmd: curl -fsSL https://get.pulumi.com | sh - - cmd: curl -LsSf https://astral.sh/uv/install.sh | sh - - cmd: | - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash - - cmd: | - . "$NVM_DIR/nvm.sh" && \ - nvm install $NODE_VERSION && \ - nvm use $NODE_VERSION && \ - nvm alias default $NODE_VERSION - - cmd: | - . "$NVM_DIR/nvm.sh" && \ - npm install -g pnpm typescript - - cmd: | - echo 'export NVM_DIR="$HOME/.nvm"' >> ~/.bashrc && \ - echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> ~/.bashrc && \ - echo '[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"' >> ~/.bashrc - agent: - from_env: - - GH_TOKEN - - CONTEXT7_API_KEY claude_code: config: strategy: copy use_host_auth: true enable_shared_dir: true + from_env: + - GH_TOKEN + - CONTEXT7_API_KEY post_init: | claude mcp add -s local serena -- uvx --from git+https://github.com/oraios/serena serena start-mcp-server --context claude-code --project "$(pwd)" --enable-web-dashboard false claude mcp add -s user --header "CONTEXT7_API_KEY: $CONTEXT7_API_KEY" --transport http context7 https://mcp.context7.com/mcp claude mcp add -s user -t http deepwiki https://mcp.deepwiki.com/mcp - -workspace: - default_mode: bind - +build: + image: node:24 + inject: + after_user_switch: + - ENV NVM_DIR="/home/claude/.nvm" + - ENV NODE_VERSION="24" + - ENV PATH="/home/claude/.pulumi/bin:$PATH" + instructions: + user_run: + - curl -fsSL https://get.pulumi.com | sh + - curl -LsSf https://astral.sh/uv/install.sh | sh + - | + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash + - | + . "$NVM_DIR/nvm.sh" && \ + nvm install $NODE_VERSION && \ + nvm use $NODE_VERSION && \ + nvm alias default $NODE_VERSION + - | + . "$NVM_DIR/nvm.sh" && \ + npm install -g pnpm typescript + - | + echo 'export NVM_DIR="$HOME/.nvm"' >> ~/.bashrc && \ + echo '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> ~/.bashrc && \ + echo '[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"' >> ~/.bashrc + packages: + - ca-certificates + - openssh-client +loop: + calls_per_hour: 100 + completion_threshold: 2 + loop_delay_seconds: 3 + max_consecutive_test_loops: 3 + max_loops: 50 + output_decline_threshold: 70 + safety_completion_threshold: 5 + same_error_threshold: 5 + session_expiration_hours: 24 + skip_permissions: true + stagnation_threshold: 3 + timeout_minutes: 15 security: + docker_socket: true firewall: - enable: true - ip_range_sources: - - name: github - - name: google # Required for Go modules (proxy.golang.org uses GCS) add_domains: - registry.yarnpkg.com - mcp.deepwiki.com @@ -69,23 +66,14 @@ security: - files.pythonhosted.org - get.pulumi.com - www.pulumi.com - docker_socket: true + enable: true + ip_range_sources: + - name: github + - name: google git_credentials: + copy_git_config: true + forward_gpg: true forward_https: true forward_ssh: true - forward_gpg: true - copy_git_config: true - -loop: - max_loops: 50 # Maximum loops before stopping (default: 50) - stagnation_threshold: 3 # Loops without progress before circuit trips (default: 3) - timeout_minutes: 15 # Per-loop timeout in minutes (default: 15) - calls_per_hour: 100 # Rate limit: max calls per hour, 0 to disable (default: 100) - completion_threshold: 2 # Completion indicators required for strict mode (default: 2) - session_expiration_hours: 24 # Session TTL, auto-reset if older (default: 24) - same_error_threshold: 5 # Same error repetitions before circuit trips (default: 5) - output_decline_threshold: 70 # Output decline percentage that triggers trip (default: 70) - max_consecutive_test_loops: 3 # Test-only loops before circuit trips (default: 3) - loop_delay_seconds: 3 # Seconds to wait between loop iterations (default: 3) - safety_completion_threshold: 5 # Force exit after N loops with completion indicators (default: 5) - skip_permissions: true # Pass --dangerously-skip-permissions to claude (default: false) +workspace: + default_mode: bind diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index c26a000..4aa5125 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -10,6 +10,16 @@ permissions: actions: read jobs: + dependency-review: + name: Dependency Review (SCA) + runs-on: ubuntu-latest + timeout-minutes: 5 + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4 + with: + fail-on-severity: high semgrep: name: Semgrep SAST runs-on: ubuntu-latest diff --git a/.serena/project.yml b/.serena/project.yml index 42237c6..a18c927 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -134,3 +134,17 @@ read_only_memory_patterns: [] # Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) # This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. line_ending: + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} diff --git a/components/gateway.ts b/components/gateway.ts index 5e886ca..7f30b1f 100644 --- a/components/gateway.ts +++ b/components/gateway.ts @@ -22,6 +22,8 @@ export interface GatewayArgs { env?: Record; /** Individual secret env vars — each key is a separate Pulumi secret */ envVars?: Record>; + /** IANA timezone (e.g. "America/Los_Angeles") — sets TZ env var on the container */ + timezone?: string; auth: { mode: "token"; token: pulumi.Input }; initHash: string; /** Hash of rendered configs (envoy.yaml + Corefile) — triggers container replacement on policy change */ @@ -30,8 +32,8 @@ export interface GatewayArgs { imageDigest: pulumi.Input; } -// Keys that cannot be overridden via gatewayEnv-- (set by the component itself) -const RESERVED_ENV_KEYS = new Set(["OPENCLAW_GATEWAY_TOKEN"]); +// Keys that cannot be overridden via env or gatewayEnv-- (set by the component itself) +const RESERVED_ENV_KEYS = new Set(["OPENCLAW_GATEWAY_TOKEN", "TZ"]); export class Gateway extends pulumi.ComponentResource { public readonly containerId: pulumi.Output; @@ -63,13 +65,29 @@ export class Gateway extends pulumi.ComponentResource { { parent: this, provider: dockerProvider }, ); - // Base env vars + user-defined env + // Filter reserved keys from user-defined env + const userEnvEntries = Object.entries(args.env ?? {}); + const envConflicts = userEnvEntries + .map(([k]) => k) + .filter((k) => RESERVED_ENV_KEYS.has(k)); + if (envConflicts.length > 0) { + pulumi.log.warn( + `Gateway "${args.profile}" env contains reserved key(s) that will be ignored: ${envConflicts.join(", ")}`, + this, + ); + } + const filteredEnv = userEnvEntries.filter( + ([k]) => !RESERVED_ENV_KEYS.has(k), + ); + + // Base env vars + filtered user-defined env const envs: pulumi.Input[] = [ `HOME=/home/node`, `TERM=xterm-256color`, `NODE_EXTRA_CA_CERTS=${ENVOY_CA_CERT_PATH}`, pulumi.interpolate`OPENCLAW_GATEWAY_TOKEN=${args.auth.token}`, - ...Object.entries(args.env ?? {}).map(([k, v]) => `${k}=${v}`), + ...(args.timezone ? [`TZ=${args.timezone}`] : []), + ...filteredEnv.map(([k, v]) => `${k}=${v}`), ]; // Merge base envs with individual secret env vars, filtering reserved keys diff --git a/index.ts b/index.ts index 74f9433..c35f5cd 100644 --- a/index.ts +++ b/index.ts @@ -241,6 +241,7 @@ const gatewayInstances = gateways.map((gw, gwIndex) => { corefilePath: envoy.corefilePath, env: gw.env, envVars, + timezone: cfg.get("timezone"), auth: { mode: "token", token }, initHash: init.contentHash, configHash: envoy.configHash, diff --git a/package-lock.json b/package-lock.json index 9a30e44..a6c08cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2537,9 +2537,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -3238,9 +3238,9 @@ } }, "node_modules/flatted": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz", - "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -4454,9 +4454,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.2.tgz", + "integrity": "sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==", "license": "MIT", "engines": { "node": ">=10" @@ -5042,9 +5042,9 @@ } }, "node_modules/tar": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz", - "integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", + "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -5091,9 +5091,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -5369,9 +5369,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -5460,9 +5460,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": {