diff --git a/.claude/skills/setup/SKILL.md b/.claude/skills/setup/SKILL.md index 86726ff..e12e0ea 100644 --- a/.claude/skills/setup/SKILL.md +++ b/.claude/skills/setup/SKILL.md @@ -98,6 +98,13 @@ Run `npx tsx setup/index.ts --step environment` and parse the status block. - If HAS_REGISTERED_GROUPS=true → note existing config, offer to skip or reconfigure - Record APPLE_CONTAINER and DOCKER values for step 3 +## 2a. Timezone + +Run `npx tsx setup/index.ts --step timezone` and parse the status block. + +- If NEEDS_USER_INPUT=true → The system timezone could not be autodetected (e.g. POSIX-style TZ like `IST-2`). AskUserQuestion: "What is your timezone?" with common options (America/New_York, Europe/London, Asia/Jerusalem, Asia/Tokyo) and an "Other" escape. Then re-run: `npx tsx setup/index.ts --step timezone -- --tz `. +- If STATUS=success → Timezone is configured. Note RESOLVED_TZ for reference. + ## 3. Container Runtime ### 3a. Choose runtime @@ -278,6 +285,6 @@ Tell user to test: send a message in their registered chat. Show: `tail -f logs/ **Unload service:** macOS: `launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist` | Linux: `systemctl --user stop nanoclaw` -## Diagnostics +## 9. Diagnostics -Read and follow [diagnostics.md](diagnostics.md). +Send diagnostics data by following `.claude/skills/setup/diagnostics.md`. diff --git a/.claude/skills/setup/diagnostics.md b/.claude/skills/setup/diagnostics.md index a76b4ab..c6a42db 100644 --- a/.claude/skills/setup/diagnostics.md +++ b/.claude/skills/setup/diagnostics.md @@ -42,5 +42,5 @@ rm /tmp/nanoclaw-diagnostics.json **Never ask again**: 1. Replace contents of `.claude/skills/setup/diagnostics.md` with `# Diagnostics — opted out` 2. Replace contents of `.claude/skills/update-nanoclaw/diagnostics.md` with `# Diagnostics — opted out` -3. Remove the "Diagnostics" section from both `.claude/skills/setup/SKILL.md` and `.claude/skills/update-nanoclaw/SKILL.md` +3. Remove the `## 9. Diagnostics` section from `.claude/skills/setup/SKILL.md` and the `## Diagnostics` section from `.claude/skills/update-nanoclaw/SKILL.md` 4. `rm /tmp/nanoclaw-diagnostics.json` diff --git a/.claude/skills/update-nanoclaw/SKILL.md b/.claude/skills/update-nanoclaw/SKILL.md index 0af5713..2207910 100644 --- a/.claude/skills/update-nanoclaw/SKILL.md +++ b/.claude/skills/update-nanoclaw/SKILL.md @@ -237,4 +237,4 @@ Tell the user: ## Diagnostics -Read and follow [diagnostics.md](diagnostics.md). +Send diagnostics data by following `.claude/skills/update-nanoclaw/diagnostics.md`. diff --git a/.claude/skills/update-nanoclaw/diagnostics.md b/.claude/skills/update-nanoclaw/diagnostics.md index bc659ab..8b06aa4 100644 --- a/.claude/skills/update-nanoclaw/diagnostics.md +++ b/.claude/skills/update-nanoclaw/diagnostics.md @@ -45,5 +45,5 @@ rm /tmp/nanoclaw-diagnostics.json **Never ask again**: 1. Replace contents of `.claude/skills/setup/diagnostics.md` with `# Diagnostics — opted out` 2. Replace contents of `.claude/skills/update-nanoclaw/diagnostics.md` with `# Diagnostics — opted out` -3. Remove the "Diagnostics" section from both `.claude/skills/setup/SKILL.md` and `.claude/skills/update-nanoclaw/SKILL.md` +3. Remove the `## 9. Diagnostics` section from `.claude/skills/setup/SKILL.md` and the `## Diagnostics` section from `.claude/skills/update-nanoclaw/SKILL.md` 4. `rm /tmp/nanoclaw-diagnostics.json` diff --git a/.claude/skills/use-native-credential-proxy/SKILL.md b/.claude/skills/use-native-credential-proxy/SKILL.md new file mode 100644 index 0000000..4cdda4c --- /dev/null +++ b/.claude/skills/use-native-credential-proxy/SKILL.md @@ -0,0 +1,157 @@ +--- +name: use-native-credential-proxy +description: Replace OneCLI gateway with the built-in credential proxy. For users who want simple .env-based credential management without installing OneCLI. Reads API key or OAuth token from .env and injects into container API requests. +--- + +# Use Native Credential Proxy + +This skill replaces the OneCLI gateway with NanoClaw's built-in credential proxy. Containers get credentials injected via a local HTTP proxy that reads from `.env` — no external services needed. + +## Phase 1: Pre-flight + +### Check if already applied + +Check if `src/credential-proxy.ts` is imported in `src/index.ts`: + +```bash +grep "credential-proxy" src/index.ts +``` + +If it shows an import for `startCredentialProxy`, the native proxy is already active. Skip to Phase 3 (Setup). + +### Check if OneCLI is active + +```bash +grep "@onecli-sh/sdk" package.json +``` + +If `@onecli-sh/sdk` appears, OneCLI is the active credential provider. Proceed with Phase 2 to replace it. + +If neither check matches, you may be on an older version. Run `/update-nanoclaw` first, then retry. + +## Phase 2: Apply Code Changes + +### Ensure upstream remote + +```bash +git remote -v +``` + +If `upstream` is missing, add it: + +```bash +git remote add upstream https://github.com/qwibitai/nanoclaw.git +``` + +### Merge the skill branch + +```bash +git fetch upstream skill/native-credential-proxy +git merge upstream/skill/native-credential-proxy || { + git checkout --theirs package-lock.json + git add package-lock.json + git merge --continue +} +``` + +This merges in: +- `src/credential-proxy.ts` and `src/credential-proxy.test.ts` (the proxy implementation) +- Restored credential proxy usage in `src/index.ts`, `src/container-runner.ts`, `src/container-runtime.ts`, `src/config.ts` +- Removed `@onecli-sh/sdk` dependency +- Restored `CREDENTIAL_PROXY_PORT` config (default 3001) +- Restored platform-aware proxy bind address detection +- Reverted setup skill to `.env`-based credential instructions + +If the merge reports conflicts beyond `package-lock.json`, resolve them by reading the conflicted files and understanding the intent of both sides. + +### Validate code changes + +```bash +npm install +npm run build +npx vitest run src/credential-proxy.test.ts src/container-runner.test.ts +``` + +All tests must pass and build must be clean before proceeding. + +## Phase 3: Setup Credentials + +AskUserQuestion: Do you want to use your **Claude subscription** (Pro/Max) or an **Anthropic API key**? + +1. **Claude subscription (Pro/Max)** — description: "Uses your existing Claude Pro or Max subscription. You'll run `claude setup-token` in another terminal to get your token." +2. **Anthropic API key** — description: "Pay-per-use API key from console.anthropic.com." + +### Subscription path + +Tell the user to run `claude setup-token` in another terminal and copy the token it outputs. Do NOT collect the token in chat. + +Once they have the token, add it to `.env`: + +```bash +# Add to .env (create file if needed) +echo 'CLAUDE_CODE_OAUTH_TOKEN=' >> .env +``` + +Note: `ANTHROPIC_AUTH_TOKEN` is also supported as a fallback. + +### API key path + +Tell the user to get an API key from https://console.anthropic.com/settings/keys if they don't have one. + +Add it to `.env`: + +```bash +echo 'ANTHROPIC_API_KEY=' >> .env +``` + +### After either path + +**If the user's response happens to contain a token or key** (starts with `sk-ant-` or looks like a token): write it to `.env` on their behalf using the appropriate variable name. + +**Optional:** If the user needs a custom API endpoint, they can add `ANTHROPIC_BASE_URL=` to `.env` (defaults to `https://api.anthropic.com`). + +## Phase 4: Verify + +1. Rebuild and restart: + +```bash +npm run build +``` + +Then restart the service: +- macOS: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw` +- Linux: `systemctl --user restart nanoclaw` +- WSL/manual: stop and re-run `bash start-nanoclaw.sh` + +2. Check logs for successful proxy startup: + +```bash +tail -20 logs/nanoclaw.log | grep "Credential proxy" +``` + +Expected: `Credential proxy started` with port and auth mode. + +3. Send a test message in the registered chat to verify the agent responds. + +4. Note: after applying this skill, the OneCLI credential steps in `/setup` no longer apply. `.env` is now the credential source. + +## Troubleshooting + +**"Credential proxy upstream error" in logs:** Check that `.env` has a valid `ANTHROPIC_API_KEY` or `CLAUDE_CODE_OAUTH_TOKEN`. Verify the API is reachable: `curl -s https://api.anthropic.com/v1/messages -H "x-api-key: test" | head`. + +**Port 3001 already in use:** Set `CREDENTIAL_PROXY_PORT=` in `.env` or as an environment variable. + +**Container can't reach proxy (Linux):** The proxy binds to the `docker0` bridge IP by default. If that interface doesn't exist (e.g. rootless Docker), set `CREDENTIAL_PROXY_HOST=0.0.0.0` as an environment variable. + +**OAuth token expired (401 errors):** Re-run `claude setup-token` in a terminal and update the token in `.env`. + +## Removal + +To revert to OneCLI gateway: + +1. Find the merge commit: `git log --oneline --merges -5` +2. Revert it: `git revert -m 1` (undoes the skill branch merge, keeps your other changes) +3. `npm install` (re-adds `@onecli-sh/sdk`) +4. `npm run build` +5. Follow `/setup` step 4 to configure OneCLI credentials +6. Remove `ANTHROPIC_API_KEY` / `CLAUDE_CODE_OAUTH_TOKEN` from `.env` diff --git a/.env.example b/.env.example index 8b13789..b90e6c9 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ - +TELEGRAM_BOT_TOKEN= diff --git a/.github/workflows/merge-forward-skills.yml b/.github/workflows/merge-forward-skills.yml deleted file mode 100644 index 093130a..0000000 --- a/.github/workflows/merge-forward-skills.yml +++ /dev/null @@ -1,160 +0,0 @@ -name: Merge-forward skill branches - -on: - push: - branches: [main] - -permissions: - contents: write - issues: write - -jobs: - merge-forward: - if: github.repository == 'qwibitai/nanoclaw' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm - - - name: Configure git - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - - name: Merge main into each skill branch - id: merge - run: | - FAILED="" - SUCCEEDED="" - - # List all remote skill branches - SKILL_BRANCHES=$(git branch -r --list 'origin/skill/*' | sed 's|origin/||' | xargs) - - if [ -z "$SKILL_BRANCHES" ]; then - echo "No skill branches found." - exit 0 - fi - - for BRANCH in $SKILL_BRANCHES; do - SKILL_NAME=$(echo "$BRANCH" | sed 's|skill/||') - echo "" - echo "=== Processing $BRANCH ===" - - # Checkout the skill branch - git checkout -B "$BRANCH" "origin/$BRANCH" - - # Attempt merge - if ! git merge main --no-edit; then - echo "::warning::Merge conflict in $BRANCH" - git merge --abort - FAILED="$FAILED $SKILL_NAME" - continue - fi - - # Check if there's anything new to push - if git diff --quiet "origin/$BRANCH"; then - echo "$BRANCH is already up to date with main." - SUCCEEDED="$SUCCEEDED $SKILL_NAME" - continue - fi - - # Install deps and validate - npm ci - - if ! npm run build; then - echo "::warning::Build failed for $BRANCH" - git reset --hard "origin/$BRANCH" - FAILED="$FAILED $SKILL_NAME" - continue - fi - - if ! npm test 2>/dev/null; then - echo "::warning::Tests failed for $BRANCH" - git reset --hard "origin/$BRANCH" - FAILED="$FAILED $SKILL_NAME" - continue - fi - - # Push the updated branch - git push origin "$BRANCH" - SUCCEEDED="$SUCCEEDED $SKILL_NAME" - echo "$BRANCH merged and pushed successfully." - done - - echo "" - echo "=== Results ===" - echo "Succeeded: $SUCCEEDED" - echo "Failed: $FAILED" - - # Export for issue creation - echo "failed=$FAILED" >> "$GITHUB_OUTPUT" - echo "succeeded=$SUCCEEDED" >> "$GITHUB_OUTPUT" - - - name: Open issue for failed merges - if: steps.merge.outputs.failed != '' - uses: actions/github-script@v7 - with: - script: | - const failed = '${{ steps.merge.outputs.failed }}'.trim().split(/\s+/); - const sha = context.sha.substring(0, 7); - const body = [ - `The merge-forward workflow failed to merge \`main\` (${sha}) into the following skill branches:`, - '', - ...failed.map(s => `- \`skill/${s}\`: merge conflict, build failure, or test failure`), - '', - 'Please resolve manually:', - '```bash', - ...failed.map(s => [ - `git checkout skill/${s}`, - `git merge main`, - `# resolve conflicts, then: git push`, - '' - ]).flat(), - '```', - '', - `Triggered by push to main: ${context.sha}` - ].join('\n'); - - await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: `Merge-forward failed for ${failed.length} skill branch(es) after ${sha}`, - body, - labels: ['skill-maintenance'] - }); - - - name: Notify channel forks - if: always() - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.FORK_DISPATCH_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const forks = [ - 'nanoclaw-whatsapp', - 'nanoclaw-telegram', - 'nanoclaw-discord', - 'nanoclaw-slack', - 'nanoclaw-gmail', - 'nanoclaw-docker-sandboxes', - ]; - const sha = context.sha.substring(0, 7); - for (const repo of forks) { - try { - await github.rest.repos.createDispatchEvent({ - owner: 'qwibitai', - repo, - event_type: 'upstream-main-updated', - client_payload: { sha: context.sha }, - }); - console.log(`Notified ${repo}`); - } catch (e) { - console.log(`Failed to notify ${repo}: ${e.message}`); - } - } diff --git a/CLAUDE.md b/CLAUDE.md index 6351ff4..2084578 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,6 +21,10 @@ Single Node.js process with skill-based channel system. Channels (WhatsApp, Tele | `groups/{name}/CLAUDE.md` | Per-group memory (isolated) | | `container/skills/` | Skills loaded inside agent containers (browser, status, formatting) | +## Secrets / Credentials / Proxy (OneCLI) + +API keys, secret keys, OAuth tokens, and auth credentials are managed by the OneCLI gateway — which handles secret injection into containers at request time, so no keys or tokens are ever passed to containers directly. Run `onecli --help`. + ## Skills Four types of skills exist in NanoClaw. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full taxonomy and guidelines. diff --git a/container/Dockerfile b/container/Dockerfile index 2fe1b22..e8537c3 100644 --- a/container/Dockerfile +++ b/container/Dockerfile @@ -1,7 +1,7 @@ # NanoClaw Agent Container # Runs Claude Agent SDK in isolated Linux VM with browser automation -FROM node:24-slim +FROM node:22-slim # Install system dependencies for Chromium RUN apt-get update && apt-get install -y \ diff --git a/package-lock.json b/package-lock.json index cd2dbef..1e0a7e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "nanoclaw", - "version": "1.2.22", + "version": "1.2.27", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nanoclaw", - "version": "1.2.22", + "version": "1.2.27", "dependencies": { "@onecli-sh/sdk": "^0.2.0", - "better-sqlite3": "^11.8.1", - "cron-parser": "^5.5.0", + "better-sqlite3": "11.10.0", + "cron-parser": "5.5.0", "pino": "^9.6.0", "pino-pretty": "^13.0.0", "yaml": "^2.8.2", @@ -542,7 +542,6 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, - "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -561,7 +560,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -574,7 +572,6 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -584,7 +581,6 @@ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -599,7 +595,6 @@ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0" }, @@ -612,7 +607,6 @@ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -625,7 +619,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", @@ -649,7 +642,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -662,7 +654,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -675,7 +666,6 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -688,7 +678,6 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -698,7 +687,6 @@ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -712,7 +700,6 @@ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } @@ -722,7 +709,6 @@ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -736,7 +722,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -750,7 +735,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -791,7 +775,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/@onecli-sh/sdk/-/sdk-0.2.0.tgz", "integrity": "sha512-u7PqWROEvTV9f0ADVkjigTrd2AZn3klbPrv7GGpeRHIJpjAxJUdlWqxr5kiGt6qTDKL8t3nq76xr4X2pxTiyBg==", - "license": "MIT", "engines": { "node": ">=20" } @@ -1198,8 +1181,7 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/node": { "version": "22.19.11", @@ -1212,17 +1194,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", - "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", + "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/type-utils": "8.57.1", - "@typescript-eslint/utils": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/type-utils": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -1235,7 +1216,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.1", + "@typescript-eslint/parser": "^8.57.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -1245,22 +1226,20 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", - "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", + "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3" }, "engines": { @@ -1276,14 +1255,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", - "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.1", - "@typescript-eslint/types": "^8.57.1", + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", "debug": "^4.4.3" }, "engines": { @@ -1298,14 +1276,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", - "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1" + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1316,11 +1293,10 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", - "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1333,15 +1309,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", - "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", + "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/utils": "8.57.1", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -1358,11 +1333,10 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", - "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1372,16 +1346,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", - "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.1", - "@typescript-eslint/tsconfig-utils": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1404,17 +1377,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/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==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, @@ -1427,7 +1398,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" }, @@ -1439,16 +1409,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", - "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1" + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1463,13 +1432,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", - "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/types": "8.57.2", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1485,7 +1453,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, @@ -1640,7 +1607,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -1653,7 +1619,6 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -1663,7 +1628,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1680,7 +1644,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -1695,8 +1658,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "dev": true }, "node_modules/assertion-error": { "version": "2.0.1", @@ -1733,8 +1695,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/base64-js": { "version": "1.5.1", @@ -1792,7 +1753,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1827,7 +1787,6 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -1847,7 +1806,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1870,7 +1828,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -1882,8 +1839,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/colorette": { "version": "2.0.20", @@ -1895,8 +1851,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/cron-parser": { "version": "5.5.0", @@ -1915,7 +1870,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1980,8 +1934,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/detect-libc": { "version": "2.1.2", @@ -2055,7 +2008,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -2068,7 +2020,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2128,7 +2079,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-no-catch-all/-/eslint-plugin-no-catch-all-1.1.0.tgz", "integrity": "sha512-VkP62jLTmccPrFGN/W6V7a3SEwdtTZm+Su2k4T3uyJirtkm0OMMm97h7qd8pRFAHus/jQg9FpUpLRc7sAylBEQ==", "dev": true, - "license": "MIT", "peerDependencies": { "eslint": ">=2.0.0" } @@ -2138,7 +2088,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -2155,7 +2104,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2168,7 +2116,6 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -2186,7 +2133,6 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -2199,7 +2145,6 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -2212,7 +2157,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -2232,7 +2176,6 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -2266,22 +2209,19 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-safe-stringify": { "version": "2.1.1", @@ -2312,7 +2252,6 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -2331,7 +2270,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -2348,7 +2286,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -2361,8 +2298,7 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/fs-constants": { "version": "1.0.0", @@ -2409,7 +2345,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -2422,7 +2357,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -2494,7 +2428,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } @@ -2504,7 +2437,6 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2521,7 +2453,6 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -2543,7 +2474,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2553,7 +2483,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -2565,8 +2494,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -2628,7 +2556,6 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2640,29 +2567,25 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -2672,7 +2595,6 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -2686,7 +2608,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -2701,8 +2622,7 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/luxon": { "version": "3.7.2", @@ -2768,7 +2688,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2827,8 +2746,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/node-abi": { "version": "3.87.0", @@ -2876,7 +2794,6 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -2894,7 +2811,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -2910,7 +2826,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -2926,7 +2841,6 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -2939,7 +2853,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -2949,7 +2862,6 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -3111,7 +3023,6 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -3163,7 +3074,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -3226,7 +3136,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -3348,7 +3257,6 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -3361,7 +3269,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -3580,7 +3487,6 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, - "license": "MIT", "engines": { "node": ">=18.12" }, @@ -3625,7 +3531,6 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -3648,16 +3553,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.1.tgz", - "integrity": "sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", + "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.1", - "@typescript-eslint/parser": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/utils": "8.57.1" + "@typescript-eslint/eslint-plugin": "8.57.2", + "@typescript-eslint/parser": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3683,7 +3587,6 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -3852,7 +3755,6 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -3885,7 +3787,6 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3916,7 +3817,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index 3457b67..91746b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nanoclaw", - "version": "1.2.22", + "version": "1.2.27", "description": "Personal Claude assistant. Lightweight, secure, customizable.", "type": "module", "main": "dist/index.js", @@ -22,8 +22,8 @@ }, "dependencies": { "@onecli-sh/sdk": "^0.2.0", - "better-sqlite3": "^11.8.1", - "cron-parser": "^5.5.0", + "better-sqlite3": "11.10.0", + "cron-parser": "5.5.0", "pino": "^9.6.0", "pino-pretty": "^13.0.0", "yaml": "^2.8.2", diff --git a/repo-tokens/badge.svg b/repo-tokens/badge.svg index b268ecc..301a593 100644 --- a/repo-tokens/badge.svg +++ b/repo-tokens/badge.svg @@ -1,5 +1,5 @@ - - 40.7k tokens, 20% of context window + + 40.1k tokens, 20% of context window @@ -15,8 +15,8 @@ tokens - - 40.7k + + 40.1k diff --git a/setup/index.ts b/setup/index.ts index 7ac13e2..7e10ddc 100644 --- a/setup/index.ts +++ b/setup/index.ts @@ -9,6 +9,7 @@ const STEPS: Record< string, () => Promise<{ run: (args: string[]) => Promise }> > = { + timezone: () => import('./timezone.js'), environment: () => import('./environment.js'), container: () => import('./container.js'), groups: () => import('./groups.js'), diff --git a/setup/timezone.ts b/setup/timezone.ts new file mode 100644 index 0000000..22c0394 --- /dev/null +++ b/setup/timezone.ts @@ -0,0 +1,67 @@ +/** + * Step: timezone — Detect, validate, and persist the user's timezone. + * Writes TZ to .env if a valid IANA timezone is resolved. + * Emits NEEDS_USER_INPUT=true when autodetection fails. + */ +import fs from 'fs'; +import path from 'path'; + +import { isValidTimezone } from '../src/timezone.js'; +import { logger } from '../src/logger.js'; +import { emitStatus } from './status.js'; + +export async function run(args: string[]): Promise { + const projectRoot = process.cwd(); + const envFile = path.join(projectRoot, '.env'); + + // Check what's already in .env + let envFileTz: string | undefined; + if (fs.existsSync(envFile)) { + const content = fs.readFileSync(envFile, 'utf-8'); + const match = content.match(/^TZ=(.+)$/m); + if (match) envFileTz = match[1].trim().replace(/^["']|["']$/g, ''); + } + + const systemTz = Intl.DateTimeFormat().resolvedOptions().timeZone; + const envTz = process.env.TZ; + + // Accept --tz flag from CLI (used when setup skill collects from user) + const tzFlagIdx = args.indexOf('--tz'); + const userTz = tzFlagIdx !== -1 ? args[tzFlagIdx + 1] : undefined; + + // Resolve: user-provided > .env > process.env > system autodetect + let resolvedTz: string | undefined; + for (const candidate of [userTz, envFileTz, envTz, systemTz]) { + if (candidate && isValidTimezone(candidate)) { + resolvedTz = candidate; + break; + } + } + + const needsUserInput = !resolvedTz; + + if (resolvedTz && resolvedTz !== envFileTz) { + // Write/update TZ in .env + if (fs.existsSync(envFile)) { + let content = fs.readFileSync(envFile, 'utf-8'); + if (/^TZ=/m.test(content)) { + content = content.replace(/^TZ=.*$/m, `TZ=${resolvedTz}`); + } else { + content = content.trimEnd() + `\nTZ=${resolvedTz}\n`; + } + fs.writeFileSync(envFile, content); + } else { + fs.writeFileSync(envFile, `TZ=${resolvedTz}\n`); + } + logger.info({ timezone: resolvedTz }, 'Set TZ in .env'); + } + + emitStatus('TIMEZONE', { + SYSTEM_TZ: systemTz || 'unknown', + ENV_TZ: envTz || 'unset', + ENV_FILE_TZ: envFileTz || 'unset', + RESOLVED_TZ: resolvedTz || 'none', + NEEDS_USER_INPUT: needsUserInput, + STATUS: needsUserInput ? 'needs_input' : 'success', + }); +} diff --git a/src/config.ts b/src/config.ts index 63d1207..d5005a0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,9 +2,15 @@ import os from 'os'; import path from 'path'; import { readEnvFile } from './env.js'; +import { isValidTimezone } from './timezone.js'; // Read config values from .env (falls back to process.env). -const envConfig = readEnvFile(['ASSISTANT_NAME', 'ASSISTANT_HAS_OWN_NUMBER', 'ONECLI_URL']); +const envConfig = readEnvFile([ + 'ASSISTANT_NAME', + 'ASSISTANT_HAS_OWN_NUMBER', + 'ONECLI_URL', + 'TZ', +]); export const ASSISTANT_NAME = process.env.ASSISTANT_NAME || envConfig.ASSISTANT_NAME || 'Andy'; @@ -63,7 +69,17 @@ export const TRIGGER_PATTERN = new RegExp( 'i', ); -// Timezone for scheduled tasks (cron expressions, etc.) -// Uses system timezone by default -export const TIMEZONE = - process.env.TZ || Intl.DateTimeFormat().resolvedOptions().timeZone; +// Timezone for scheduled tasks, message formatting, etc. +// Validates each candidate is a real IANA identifier before accepting. +function resolveConfigTimezone(): string { + const candidates = [ + process.env.TZ, + envConfig.TZ, + Intl.DateTimeFormat().resolvedOptions().timeZone, + ]; + for (const tz of candidates) { + if (tz && isValidTimezone(tz)) return tz; + } + return 'UTC'; +} +export const TIMEZONE = resolveConfigTimezone(); diff --git a/src/container-runner.test.ts b/src/container-runner.test.ts index 2de45c5..64c3455 100644 --- a/src/container-runner.test.ts +++ b/src/container-runner.test.ts @@ -56,7 +56,9 @@ vi.mock('@onecli-sh/sdk', () => ({ OneCLI: class { applyContainerConfig = vi.fn().mockResolvedValue(true); createAgent = vi.fn().mockResolvedValue({ id: 'test' }); - ensureAgent = vi.fn().mockResolvedValue({ name: 'test', identifier: 'test', created: true }); + ensureAgent = vi + .fn() + .mockResolvedValue({ name: 'test', identifier: 'test', created: true }); }, })); diff --git a/src/container-runner.ts b/src/container-runner.ts index 47a8387..4f6f5c1 100644 --- a/src/container-runner.ts +++ b/src/container-runner.ts @@ -241,7 +241,10 @@ async function buildContainerArgs( if (onecliApplied) { logger.info({ containerName }, 'OneCLI gateway config applied'); } else { - logger.warn({ containerName }, 'OneCLI gateway not reachable — container will have no credentials'); + logger.warn( + { containerName }, + 'OneCLI gateway not reachable — container will have no credentials', + ); } // Runtime-specific args for host gateway resolution @@ -288,7 +291,11 @@ export async function runContainerAgent( const agentIdentifier = input.isMain ? undefined : group.folder.toLowerCase().replace(/_/g, '-'); - const containerArgs = await buildContainerArgs(mounts, containerName, agentIdentifier); + const containerArgs = await buildContainerArgs( + mounts, + containerName, + agentIdentifier, + ); logger.debug( { diff --git a/src/credential-proxy.test.ts b/src/credential-proxy.test.ts deleted file mode 100644 index de76c89..0000000 --- a/src/credential-proxy.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; -import http from 'http'; -import type { AddressInfo } from 'net'; - -const mockEnv: Record = {}; -vi.mock('./env.js', () => ({ - readEnvFile: vi.fn(() => ({ ...mockEnv })), -})); - -vi.mock('./logger.js', () => ({ - logger: { info: vi.fn(), error: vi.fn(), debug: vi.fn(), warn: vi.fn() }, -})); - -import { startCredentialProxy } from './credential-proxy.js'; - -function makeRequest( - port: number, - options: http.RequestOptions, - body = '', -): Promise<{ - statusCode: number; - body: string; - headers: http.IncomingHttpHeaders; -}> { - return new Promise((resolve, reject) => { - const req = http.request( - { ...options, hostname: '127.0.0.1', port }, - (res) => { - const chunks: Buffer[] = []; - res.on('data', (c) => chunks.push(c)); - res.on('end', () => { - resolve({ - statusCode: res.statusCode!, - body: Buffer.concat(chunks).toString(), - headers: res.headers, - }); - }); - }, - ); - req.on('error', reject); - req.write(body); - req.end(); - }); -} - -describe('credential-proxy', () => { - let proxyServer: http.Server; - let upstreamServer: http.Server; - let proxyPort: number; - let upstreamPort: number; - let lastUpstreamHeaders: http.IncomingHttpHeaders; - - beforeEach(async () => { - lastUpstreamHeaders = {}; - - upstreamServer = http.createServer((req, res) => { - lastUpstreamHeaders = { ...req.headers }; - res.writeHead(200, { 'content-type': 'application/json' }); - res.end(JSON.stringify({ ok: true })); - }); - await new Promise((resolve) => - upstreamServer.listen(0, '127.0.0.1', resolve), - ); - upstreamPort = (upstreamServer.address() as AddressInfo).port; - }); - - afterEach(async () => { - await new Promise((r) => proxyServer?.close(() => r())); - await new Promise((r) => upstreamServer?.close(() => r())); - for (const key of Object.keys(mockEnv)) delete mockEnv[key]; - }); - - async function startProxy(env: Record): Promise { - Object.assign(mockEnv, env, { - ANTHROPIC_BASE_URL: `http://127.0.0.1:${upstreamPort}`, - }); - proxyServer = await startCredentialProxy(0); - return (proxyServer.address() as AddressInfo).port; - } - - it('API-key mode injects x-api-key and strips placeholder', async () => { - proxyPort = await startProxy({ ANTHROPIC_API_KEY: 'sk-ant-real-key' }); - - await makeRequest( - proxyPort, - { - method: 'POST', - path: '/v1/messages', - headers: { - 'content-type': 'application/json', - 'x-api-key': 'placeholder', - }, - }, - '{}', - ); - - expect(lastUpstreamHeaders['x-api-key']).toBe('sk-ant-real-key'); - }); - - it('OAuth mode replaces Authorization when container sends one', async () => { - proxyPort = await startProxy({ - CLAUDE_CODE_OAUTH_TOKEN: 'real-oauth-token', - }); - - await makeRequest( - proxyPort, - { - method: 'POST', - path: '/api/oauth/claude_cli/create_api_key', - headers: { - 'content-type': 'application/json', - authorization: 'Bearer placeholder', - }, - }, - '{}', - ); - - expect(lastUpstreamHeaders['authorization']).toBe( - 'Bearer real-oauth-token', - ); - }); - - it('OAuth mode does not inject Authorization when container omits it', async () => { - proxyPort = await startProxy({ - CLAUDE_CODE_OAUTH_TOKEN: 'real-oauth-token', - }); - - // Post-exchange: container uses x-api-key only, no Authorization header - await makeRequest( - proxyPort, - { - method: 'POST', - path: '/v1/messages', - headers: { - 'content-type': 'application/json', - 'x-api-key': 'temp-key-from-exchange', - }, - }, - '{}', - ); - - expect(lastUpstreamHeaders['x-api-key']).toBe('temp-key-from-exchange'); - expect(lastUpstreamHeaders['authorization']).toBeUndefined(); - }); - - it('strips hop-by-hop headers', async () => { - proxyPort = await startProxy({ ANTHROPIC_API_KEY: 'sk-ant-real-key' }); - - await makeRequest( - proxyPort, - { - method: 'POST', - path: '/v1/messages', - headers: { - 'content-type': 'application/json', - connection: 'keep-alive', - 'keep-alive': 'timeout=5', - 'transfer-encoding': 'chunked', - }, - }, - '{}', - ); - - // Proxy strips client hop-by-hop headers. Node's HTTP client may re-add - // its own Connection header (standard HTTP/1.1 behavior), but the client's - // custom keep-alive and transfer-encoding must not be forwarded. - expect(lastUpstreamHeaders['keep-alive']).toBeUndefined(); - expect(lastUpstreamHeaders['transfer-encoding']).toBeUndefined(); - }); - - it('returns 502 when upstream is unreachable', async () => { - Object.assign(mockEnv, { - ANTHROPIC_API_KEY: 'sk-ant-real-key', - ANTHROPIC_BASE_URL: 'http://127.0.0.1:59999', - }); - proxyServer = await startCredentialProxy(0); - proxyPort = (proxyServer.address() as AddressInfo).port; - - const res = await makeRequest( - proxyPort, - { - method: 'POST', - path: '/v1/messages', - headers: { 'content-type': 'application/json' }, - }, - '{}', - ); - - expect(res.statusCode).toBe(502); - expect(res.body).toBe('Bad Gateway'); - }); -}); diff --git a/src/credential-proxy.ts b/src/credential-proxy.ts deleted file mode 100644 index 8a893dd..0000000 --- a/src/credential-proxy.ts +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Credential proxy for container isolation. - * Containers connect here instead of directly to the Anthropic API. - * The proxy injects real credentials so containers never see them. - * - * Two auth modes: - * API key: Proxy injects x-api-key on every request. - * OAuth: Container CLI exchanges its placeholder token for a temp - * API key via /api/oauth/claude_cli/create_api_key. - * Proxy injects real OAuth token on that exchange request; - * subsequent requests carry the temp key which is valid as-is. - */ -import { createServer, Server } from 'http'; -import { request as httpsRequest } from 'https'; -import { request as httpRequest, RequestOptions } from 'http'; - -import { readEnvFile } from './env.js'; -import { logger } from './logger.js'; - -export type AuthMode = 'api-key' | 'oauth'; - -export interface ProxyConfig { - authMode: AuthMode; -} - -export function startCredentialProxy( - port: number, - host = '127.0.0.1', -): Promise { - const secrets = readEnvFile([ - 'ANTHROPIC_API_KEY', - 'CLAUDE_CODE_OAUTH_TOKEN', - 'ANTHROPIC_AUTH_TOKEN', - 'ANTHROPIC_BASE_URL', - ]); - - const authMode: AuthMode = secrets.ANTHROPIC_API_KEY ? 'api-key' : 'oauth'; - const oauthToken = - secrets.CLAUDE_CODE_OAUTH_TOKEN || secrets.ANTHROPIC_AUTH_TOKEN; - - const upstreamUrl = new URL( - secrets.ANTHROPIC_BASE_URL || 'https://api.anthropic.com', - ); - const isHttps = upstreamUrl.protocol === 'https:'; - const makeRequest = isHttps ? httpsRequest : httpRequest; - - return new Promise((resolve, reject) => { - const server = createServer((req, res) => { - const chunks: Buffer[] = []; - req.on('data', (c) => chunks.push(c)); - req.on('end', () => { - const body = Buffer.concat(chunks); - const headers: Record = - { - ...(req.headers as Record), - host: upstreamUrl.host, - 'content-length': body.length, - }; - - // Strip hop-by-hop headers that must not be forwarded by proxies - delete headers['connection']; - delete headers['keep-alive']; - delete headers['transfer-encoding']; - - if (authMode === 'api-key') { - // API key mode: inject x-api-key on every request - delete headers['x-api-key']; - headers['x-api-key'] = secrets.ANTHROPIC_API_KEY; - } else { - // OAuth mode: replace placeholder Bearer token with the real one - // only when the container actually sends an Authorization header - // (exchange request + auth probes). Post-exchange requests use - // x-api-key only, so they pass through without token injection. - if (headers['authorization']) { - delete headers['authorization']; - if (oauthToken) { - headers['authorization'] = `Bearer ${oauthToken}`; - } - } - } - - const upstream = makeRequest( - { - hostname: upstreamUrl.hostname, - port: upstreamUrl.port || (isHttps ? 443 : 80), - path: req.url, - method: req.method, - headers, - } as RequestOptions, - (upRes) => { - res.writeHead(upRes.statusCode!, upRes.headers); - upRes.pipe(res); - }, - ); - - upstream.on('error', (err) => { - logger.error( - { err, url: req.url }, - 'Credential proxy upstream error', - ); - if (!res.headersSent) { - res.writeHead(502); - res.end('Bad Gateway'); - } - }); - - upstream.write(body); - upstream.end(); - }); - }); - - server.listen(port, host, () => { - logger.info({ port, host, authMode }, 'Credential proxy started'); - resolve(server); - }); - - server.on('error', reject); - }); -} - -/** Detect which auth mode the host is configured for. */ -export function detectAuthMode(): AuthMode { - const secrets = readEnvFile(['ANTHROPIC_API_KEY']); - return secrets.ANTHROPIC_API_KEY ? 'api-key' : 'oauth'; -} diff --git a/src/timezone.test.ts b/src/timezone.test.ts index df0525f..1003a61 100644 --- a/src/timezone.test.ts +++ b/src/timezone.test.ts @@ -1,6 +1,10 @@ import { describe, it, expect } from 'vitest'; -import { formatLocalTime } from './timezone.js'; +import { + formatLocalTime, + isValidTimezone, + resolveTimezone, +} from './timezone.js'; // --- formatLocalTime --- @@ -26,4 +30,44 @@ describe('formatLocalTime', () => { expect(ny).toContain('8:00'); expect(tokyo).toContain('9:00'); }); + + it('does not throw on invalid timezone, falls back to UTC', () => { + expect(() => + formatLocalTime('2026-01-01T00:00:00.000Z', 'IST-2'), + ).not.toThrow(); + const result = formatLocalTime('2026-01-01T12:00:00.000Z', 'IST-2'); + // Should format as UTC (noon UTC = 12:00 PM) + expect(result).toContain('12:00'); + expect(result).toContain('PM'); + }); +}); + +describe('isValidTimezone', () => { + it('accepts valid IANA identifiers', () => { + expect(isValidTimezone('America/New_York')).toBe(true); + expect(isValidTimezone('UTC')).toBe(true); + expect(isValidTimezone('Asia/Tokyo')).toBe(true); + expect(isValidTimezone('Asia/Jerusalem')).toBe(true); + }); + + it('rejects invalid timezone strings', () => { + expect(isValidTimezone('IST-2')).toBe(false); + expect(isValidTimezone('XYZ+3')).toBe(false); + }); + + it('rejects empty and garbage strings', () => { + expect(isValidTimezone('')).toBe(false); + expect(isValidTimezone('NotATimezone')).toBe(false); + }); +}); + +describe('resolveTimezone', () => { + it('returns the timezone if valid', () => { + expect(resolveTimezone('America/New_York')).toBe('America/New_York'); + }); + + it('falls back to UTC for invalid timezone', () => { + expect(resolveTimezone('IST-2')).toBe('UTC'); + expect(resolveTimezone('')).toBe('UTC'); + }); }); diff --git a/src/timezone.ts b/src/timezone.ts index e7569f4..d8cc6cc 100644 --- a/src/timezone.ts +++ b/src/timezone.ts @@ -1,11 +1,32 @@ +/** + * Check whether a timezone string is a valid IANA identifier + * that Intl.DateTimeFormat can use. + */ +export function isValidTimezone(tz: string): boolean { + try { + Intl.DateTimeFormat(undefined, { timeZone: tz }); + return true; + } catch { + return false; + } +} + +/** + * Return the given timezone if valid IANA, otherwise fall back to UTC. + */ +export function resolveTimezone(tz: string): string { + return isValidTimezone(tz) ? tz : 'UTC'; +} + /** * Convert a UTC ISO timestamp to a localized display string. * Uses the Intl API (no external dependencies). + * Falls back to UTC if the timezone is invalid. */ export function formatLocalTime(utcIso: string, timezone: string): string { const date = new Date(utcIso); return date.toLocaleString('en-US', { - timeZone: timezone, + timeZone: resolveTimezone(timezone), year: 'numeric', month: 'short', day: 'numeric',