@rozek/nanoclaw 1.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +1 -0
- package/.claude/skills/add-compact/SKILL.md +135 -0
- package/.claude/skills/add-discord/SKILL.md +203 -0
- package/.claude/skills/add-gmail/SKILL.md +220 -0
- package/.claude/skills/add-image-vision/SKILL.md +94 -0
- package/.claude/skills/add-ollama-tool/SKILL.md +153 -0
- package/.claude/skills/add-parallel/SKILL.md +290 -0
- package/.claude/skills/add-pdf-reader/SKILL.md +104 -0
- package/.claude/skills/add-reactions/SKILL.md +117 -0
- package/.claude/skills/add-slack/SKILL.md +207 -0
- package/.claude/skills/add-telegram/SKILL.md +222 -0
- package/.claude/skills/add-telegram-swarm/SKILL.md +384 -0
- package/.claude/skills/add-voice-transcription/SKILL.md +148 -0
- package/.claude/skills/add-whatsapp/SKILL.md +372 -0
- package/.claude/skills/convert-to-apple-container/SKILL.md +175 -0
- package/.claude/skills/customize/SKILL.md +110 -0
- package/.claude/skills/debug/SKILL.md +349 -0
- package/.claude/skills/get-qodo-rules/SKILL.md +122 -0
- package/.claude/skills/get-qodo-rules/references/output-format.md +41 -0
- package/.claude/skills/get-qodo-rules/references/pagination.md +33 -0
- package/.claude/skills/get-qodo-rules/references/repository-scope.md +26 -0
- package/.claude/skills/qodo-pr-resolver/SKILL.md +326 -0
- package/.claude/skills/qodo-pr-resolver/resources/providers.md +329 -0
- package/.claude/skills/setup/SKILL.md +218 -0
- package/.claude/skills/update-nanoclaw/SKILL.md +235 -0
- package/.claude/skills/update-skills/SKILL.md +130 -0
- package/.claude/skills/use-local-whisper/SKILL.md +152 -0
- package/.claude/skills/x-integration/SKILL.md +417 -0
- package/.claude/skills/x-integration/agent.ts +243 -0
- package/.claude/skills/x-integration/host.ts +159 -0
- package/.claude/skills/x-integration/lib/browser.ts +148 -0
- package/.claude/skills/x-integration/lib/config.ts +62 -0
- package/.claude/skills/x-integration/scripts/like.ts +56 -0
- package/.claude/skills/x-integration/scripts/post.ts +66 -0
- package/.claude/skills/x-integration/scripts/quote.ts +80 -0
- package/.claude/skills/x-integration/scripts/reply.ts +74 -0
- package/.claude/skills/x-integration/scripts/retweet.ts +62 -0
- package/.claude/skills/x-integration/scripts/setup.ts +87 -0
- package/.env.example +1 -0
- package/.github/CODEOWNERS +10 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/.github/workflows/bump-version.yml +32 -0
- package/.github/workflows/ci.yml +25 -0
- package/.github/workflows/merge-forward-skills.yml +160 -0
- package/.github/workflows/update-tokens.yml +42 -0
- package/.husky/pre-commit +1 -0
- package/.mcp.json +3 -0
- package/.nvmrc +1 -0
- package/.prettierrc +3 -0
- package/CHANGELOG.md +8 -0
- package/CLAUDE.md +64 -0
- package/CONTRIBUTING.md +23 -0
- package/CONTRIBUTORS.md +15 -0
- package/LICENSE +21 -0
- package/NanoClaw_with_Web-Support.md +290 -0
- package/README.md +261 -0
- package/README_zh.md +200 -0
- package/assets/nanoclaw-favicon.png +0 -0
- package/assets/nanoclaw-icon.png +0 -0
- package/assets/nanoclaw-logo-dark.png +0 -0
- package/assets/nanoclaw-logo.png +0 -0
- package/assets/nanoclaw-profile.jpeg +0 -0
- package/assets/nanoclaw-sales.png +0 -0
- package/assets/social-preview.jpg +0 -0
- package/config-examples/mount-allowlist.json +25 -0
- package/container/Dockerfile +70 -0
- package/container/agent-runner/package-lock.json +1524 -0
- package/container/agent-runner/package.json +21 -0
- package/container/agent-runner/src/index.ts +558 -0
- package/container/agent-runner/src/ipc-mcp-stdio.ts +338 -0
- package/container/agent-runner/tsconfig.json +15 -0
- package/container/build.sh +23 -0
- package/container/skills/agent-browser/SKILL.md +159 -0
- package/container/skills/capabilities/SKILL.md +100 -0
- package/container/skills/status/SKILL.md +104 -0
- package/dist/channels/index.d.ts +2 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js +9 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/registry.d.ts +13 -0
- package/dist/channels/registry.d.ts.map +1 -0
- package/dist/channels/registry.js +11 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/registry.test.d.ts +2 -0
- package/dist/channels/registry.test.d.ts.map +1 -0
- package/dist/channels/registry.test.js +32 -0
- package/dist/channels/registry.test.js.map +1 -0
- package/dist/channels/web.d.ts +2 -0
- package/dist/channels/web.d.ts.map +1 -0
- package/dist/channels/web.js +1738 -0
- package/dist/channels/web.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +182 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/container-runner.d.ts +44 -0
- package/dist/container-runner.d.ts.map +1 -0
- package/dist/container-runner.js +467 -0
- package/dist/container-runner.js.map +1 -0
- package/dist/container-runner.test.d.ts +2 -0
- package/dist/container-runner.test.d.ts.map +1 -0
- package/dist/container-runner.test.js +150 -0
- package/dist/container-runner.test.js.map +1 -0
- package/dist/container-runtime.d.ts +22 -0
- package/dist/container-runtime.d.ts.map +1 -0
- package/dist/container-runtime.js +96 -0
- package/dist/container-runtime.js.map +1 -0
- package/dist/container-runtime.test.d.ts +2 -0
- package/dist/container-runtime.test.d.ts.map +1 -0
- package/dist/container-runtime.test.js +93 -0
- package/dist/container-runtime.test.js.map +1 -0
- package/dist/credential-proxy.d.ts +21 -0
- package/dist/credential-proxy.d.ts.map +1 -0
- package/dist/credential-proxy.js +95 -0
- package/dist/credential-proxy.js.map +1 -0
- package/dist/credential-proxy.test.d.ts +2 -0
- package/dist/credential-proxy.test.d.ts.map +1 -0
- package/dist/credential-proxy.test.js +134 -0
- package/dist/credential-proxy.test.js.map +1 -0
- package/dist/db.d.ts +115 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +549 -0
- package/dist/db.js.map +1 -0
- package/dist/db.test.d.ts +2 -0
- package/dist/db.test.d.ts.map +1 -0
- package/dist/db.test.js +360 -0
- package/dist/db.test.js.map +1 -0
- package/dist/env.d.ts +8 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +42 -0
- package/dist/env.js.map +1 -0
- package/dist/formatting.test.d.ts +2 -0
- package/dist/formatting.test.d.ts.map +1 -0
- package/dist/formatting.test.js +183 -0
- package/dist/formatting.test.js.map +1 -0
- package/dist/group-folder.d.ts +5 -0
- package/dist/group-folder.d.ts.map +1 -0
- package/dist/group-folder.js +44 -0
- package/dist/group-folder.js.map +1 -0
- package/dist/group-folder.test.d.ts +2 -0
- package/dist/group-folder.test.d.ts.map +1 -0
- package/dist/group-folder.test.js +29 -0
- package/dist/group-folder.test.js.map +1 -0
- package/dist/group-queue.d.ts +34 -0
- package/dist/group-queue.d.ts.map +1 -0
- package/dist/group-queue.js +263 -0
- package/dist/group-queue.js.map +1 -0
- package/dist/group-queue.test.d.ts +2 -0
- package/dist/group-queue.test.d.ts.map +1 -0
- package/dist/group-queue.test.js +341 -0
- package/dist/group-queue.test.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +518 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc-auth.test.d.ts +2 -0
- package/dist/ipc-auth.test.d.ts.map +1 -0
- package/dist/ipc-auth.test.js +434 -0
- package/dist/ipc-auth.test.js.map +1 -0
- package/dist/ipc.d.ts +32 -0
- package/dist/ipc.d.ts.map +1 -0
- package/dist/ipc.js +311 -0
- package/dist/ipc.js.map +1 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +14 -0
- package/dist/logger.js.map +1 -0
- package/dist/mount-security.d.ts +34 -0
- package/dist/mount-security.d.ts.map +1 -0
- package/dist/mount-security.js +325 -0
- package/dist/mount-security.js.map +1 -0
- package/dist/remote-control.d.ts +32 -0
- package/dist/remote-control.d.ts.map +1 -0
- package/dist/remote-control.js +185 -0
- package/dist/remote-control.js.map +1 -0
- package/dist/remote-control.test.d.ts +2 -0
- package/dist/remote-control.test.d.ts.map +1 -0
- package/dist/remote-control.test.js +321 -0
- package/dist/remote-control.test.js.map +1 -0
- package/dist/router.d.ts +8 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +37 -0
- package/dist/router.js.map +1 -0
- package/dist/routing.test.d.ts +2 -0
- package/dist/routing.test.d.ts.map +1 -0
- package/dist/routing.test.js +81 -0
- package/dist/routing.test.js.map +1 -0
- package/dist/sender-allowlist.d.ts +14 -0
- package/dist/sender-allowlist.d.ts.map +1 -0
- package/dist/sender-allowlist.js +79 -0
- package/dist/sender-allowlist.js.map +1 -0
- package/dist/sender-allowlist.test.d.ts +2 -0
- package/dist/sender-allowlist.test.d.ts.map +1 -0
- package/dist/sender-allowlist.test.js +186 -0
- package/dist/sender-allowlist.test.js.map +1 -0
- package/dist/session-commands.d.ts +47 -0
- package/dist/session-commands.d.ts.map +1 -0
- package/dist/session-commands.js +102 -0
- package/dist/session-commands.js.map +1 -0
- package/dist/session-commands.test.d.ts +2 -0
- package/dist/session-commands.test.d.ts.map +1 -0
- package/dist/session-commands.test.js +190 -0
- package/dist/session-commands.test.js.map +1 -0
- package/dist/task-scheduler.d.ts +22 -0
- package/dist/task-scheduler.d.ts.map +1 -0
- package/dist/task-scheduler.js +210 -0
- package/dist/task-scheduler.js.map +1 -0
- package/dist/task-scheduler.test.d.ts +2 -0
- package/dist/task-scheduler.test.d.ts.map +1 -0
- package/dist/task-scheduler.test.js +107 -0
- package/dist/task-scheduler.test.js.map +1 -0
- package/dist/timezone.d.ts +6 -0
- package/dist/timezone.d.ts.map +1 -0
- package/dist/timezone.js +17 -0
- package/dist/timezone.js.map +1 -0
- package/dist/timezone.test.d.ts +2 -0
- package/dist/timezone.test.d.ts.map +1 -0
- package/dist/timezone.test.js +23 -0
- package/dist/timezone.test.js.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/APPLE-CONTAINER-NETWORKING.md +90 -0
- package/docs/DEBUG_CHECKLIST.md +143 -0
- package/docs/REQUIREMENTS.md +196 -0
- package/docs/SDK_DEEP_DIVE.md +643 -0
- package/docs/SECURITY.md +122 -0
- package/docs/SPEC.md +785 -0
- package/docs/docker-sandboxes.md +359 -0
- package/docs/nanoclaw-architecture-final.md +1063 -0
- package/docs/nanorepo-architecture.md +168 -0
- package/docs/skills-as-branches.md +662 -0
- package/groups/global/CLAUDE.md +58 -0
- package/groups/main/CLAUDE.md +246 -0
- package/launchd/com.nanoclaw.plist +32 -0
- package/package.json +45 -0
- package/repo-tokens/README.md +113 -0
- package/repo-tokens/action.yml +186 -0
- package/repo-tokens/badge.svg +23 -0
- package/repo-tokens/examples/green.svg +14 -0
- package/repo-tokens/examples/red.svg +14 -0
- package/repo-tokens/examples/yellow-green.svg +14 -0
- package/repo-tokens/examples/yellow.svg +14 -0
- package/scripts/run-migrations.ts +105 -0
- package/setup/container.ts +144 -0
- package/setup/environment.test.ts +121 -0
- package/setup/environment.ts +94 -0
- package/setup/groups.ts +229 -0
- package/setup/index.ts +58 -0
- package/setup/mounts.ts +115 -0
- package/setup/platform.test.ts +120 -0
- package/setup/platform.ts +132 -0
- package/setup/register.test.ts +257 -0
- package/setup/register.ts +177 -0
- package/setup/service.test.ts +187 -0
- package/setup/service.ts +362 -0
- package/setup/status.ts +16 -0
- package/setup/verify.ts +192 -0
- package/setup.sh +161 -0
- package/src/channels/index.ts +12 -0
- package/src/channels/registry.test.ts +42 -0
- package/src/channels/registry.ts +32 -0
- package/src/channels/web.ts +1856 -0
- package/src/cli.ts +209 -0
- package/src/config.ts +73 -0
- package/src/container-runner.test.ts +210 -0
- package/src/container-runner.ts +707 -0
- package/src/container-runtime.test.ts +149 -0
- package/src/container-runtime.ts +127 -0
- package/src/credential-proxy.test.ts +192 -0
- package/src/credential-proxy.ts +125 -0
- package/src/db.test.ts +484 -0
- package/src/db.ts +803 -0
- package/src/env.ts +42 -0
- package/src/formatting.test.ts +256 -0
- package/src/group-folder.test.ts +43 -0
- package/src/group-folder.ts +44 -0
- package/src/group-queue.test.ts +484 -0
- package/src/group-queue.ts +365 -0
- package/src/index.ts +731 -0
- package/src/ipc-auth.test.ts +679 -0
- package/src/ipc.ts +461 -0
- package/src/logger.ts +16 -0
- package/src/mount-security.ts +419 -0
- package/src/remote-control.test.ts +397 -0
- package/src/remote-control.ts +224 -0
- package/src/router.ts +52 -0
- package/src/routing.test.ts +170 -0
- package/src/sender-allowlist.test.ts +216 -0
- package/src/sender-allowlist.ts +128 -0
- package/src/session-commands.test.ts +247 -0
- package/src/session-commands.ts +163 -0
- package/src/task-scheduler.test.ts +129 -0
- package/src/task-scheduler.ts +295 -0
- package/src/timezone.test.ts +29 -0
- package/src/timezone.ts +16 -0
- package/src/types.ts +107 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +7 -0
- package/vitest.skills.config.ts +7 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="97" height="20" role="img" aria-label="40.6k tokens, 20% of context window">
|
|
2
|
+
<title>40.6k tokens, 20% of context window</title>
|
|
3
|
+
<linearGradient id="s" x2="0" y2="100%">
|
|
4
|
+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
5
|
+
<stop offset="1" stop-opacity=".1"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<clipPath id="r">
|
|
8
|
+
<rect width="97" height="20" rx="3" fill="#fff"/>
|
|
9
|
+
</clipPath>
|
|
10
|
+
<a xlink:href="https://github.com/qwibitai/nanoclaw/tree/main/repo-tokens">
|
|
11
|
+
<g clip-path="url(#r)">
|
|
12
|
+
<rect width="52" height="20" fill="#555"/>
|
|
13
|
+
<rect x="52" width="45" height="20" fill="#4c1"/>
|
|
14
|
+
<rect width="97" height="20" fill="url(#s)"/>
|
|
15
|
+
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
|
|
16
|
+
<text aria-hidden="true" x="26" y="15" fill="#010101" fill-opacity=".3">tokens</text>
|
|
17
|
+
<text x="26" y="14">tokens</text>
|
|
18
|
+
<text aria-hidden="true" x="74" y="15" fill="#010101" fill-opacity=".3">40.6k</text>
|
|
19
|
+
<text x="74" y="14">40.6k</text>
|
|
20
|
+
</g>
|
|
21
|
+
</g>
|
|
22
|
+
</a>
|
|
23
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="97" height="20" role="img" aria-label="12.4k tokens">
|
|
2
|
+
<title>12.4k tokens</title>
|
|
3
|
+
<linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient>
|
|
4
|
+
<clipPath id="r"><rect width="97" height="20" rx="3"/></clipPath>
|
|
5
|
+
<g clip-path="url(#r)">
|
|
6
|
+
<rect width="52" height="20" fill="#555"/>
|
|
7
|
+
<rect x="52" width="45" height="20" fill="#4c1"/>
|
|
8
|
+
<rect width="97" height="20" fill="url(#s)"/>
|
|
9
|
+
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
|
|
10
|
+
<text x="26" y="15" fill="#010101" fill-opacity=".3">tokens</text><text x="26" y="14">tokens</text>
|
|
11
|
+
<text x="74" y="15" fill="#010101" fill-opacity=".3">12.4k</text><text x="74" y="14">12.4k</text>
|
|
12
|
+
</g>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="20" role="img" aria-label="158k tokens">
|
|
2
|
+
<title>158k tokens</title>
|
|
3
|
+
<linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient>
|
|
4
|
+
<clipPath id="r"><rect width="90" height="20" rx="3"/></clipPath>
|
|
5
|
+
<g clip-path="url(#r)">
|
|
6
|
+
<rect width="52" height="20" fill="#555"/>
|
|
7
|
+
<rect x="52" width="38" height="20" fill="#e05d44"/>
|
|
8
|
+
<rect width="90" height="20" fill="url(#s)"/>
|
|
9
|
+
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
|
|
10
|
+
<text x="26" y="15" fill="#010101" fill-opacity=".3">tokens</text><text x="26" y="14">tokens</text>
|
|
11
|
+
<text x="71" y="15" fill="#010101" fill-opacity=".3">158k</text><text x="71" y="14">158k</text>
|
|
12
|
+
</g>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="97" height="20" role="img" aria-label="74.8k tokens">
|
|
2
|
+
<title>74.8k tokens</title>
|
|
3
|
+
<linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient>
|
|
4
|
+
<clipPath id="r"><rect width="97" height="20" rx="3"/></clipPath>
|
|
5
|
+
<g clip-path="url(#r)">
|
|
6
|
+
<rect width="52" height="20" fill="#555"/>
|
|
7
|
+
<rect x="52" width="45" height="20" fill="#97ca00"/>
|
|
8
|
+
<rect width="97" height="20" fill="url(#s)"/>
|
|
9
|
+
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
|
|
10
|
+
<text x="26" y="15" fill="#010101" fill-opacity=".3">tokens</text><text x="26" y="14">tokens</text>
|
|
11
|
+
<text x="74" y="15" fill="#010101" fill-opacity=".3">74.8k</text><text x="74" y="14">74.8k</text>
|
|
12
|
+
</g>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="20" role="img" aria-label="120k tokens">
|
|
2
|
+
<title>120k tokens</title>
|
|
3
|
+
<linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient>
|
|
4
|
+
<clipPath id="r"><rect width="90" height="20" rx="3"/></clipPath>
|
|
5
|
+
<g clip-path="url(#r)">
|
|
6
|
+
<rect width="52" height="20" fill="#555"/>
|
|
7
|
+
<rect x="52" width="38" height="20" fill="#dfb317"/>
|
|
8
|
+
<rect width="90" height="20" fill="url(#s)"/>
|
|
9
|
+
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
|
|
10
|
+
<text x="26" y="15" fill="#010101" fill-opacity=".3">tokens</text><text x="26" y="14">tokens</text>
|
|
11
|
+
<text x="71" y="15" fill="#010101" fill-opacity=".3">120k</text><text x="71" y="14">120k</text>
|
|
12
|
+
</g>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { execFileSync, execSync } from 'child_process';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
function compareSemver(a: string, b: string): number {
|
|
7
|
+
const partsA = a.split('.').map(Number);
|
|
8
|
+
const partsB = b.split('.').map(Number);
|
|
9
|
+
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
10
|
+
const diff = (partsA[i] || 0) - (partsB[i] || 0);
|
|
11
|
+
if (diff !== 0) return diff;
|
|
12
|
+
}
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Resolve tsx binary once to avoid npx race conditions across migrations
|
|
17
|
+
function resolveTsx(): string {
|
|
18
|
+
// Check local node_modules first
|
|
19
|
+
const local = path.resolve('node_modules/.bin/tsx');
|
|
20
|
+
if (fs.existsSync(local)) return local;
|
|
21
|
+
// Fall back to whichever tsx is in PATH
|
|
22
|
+
try {
|
|
23
|
+
return execSync('which tsx', { encoding: 'utf-8' }).trim();
|
|
24
|
+
} catch {
|
|
25
|
+
return 'npx'; // last resort
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const tsxBin = resolveTsx();
|
|
30
|
+
|
|
31
|
+
const fromVersion = process.argv[2];
|
|
32
|
+
const toVersion = process.argv[3];
|
|
33
|
+
const newCorePath = process.argv[4];
|
|
34
|
+
|
|
35
|
+
if (!fromVersion || !toVersion || !newCorePath) {
|
|
36
|
+
console.error(
|
|
37
|
+
'Usage: tsx scripts/run-migrations.ts <from-version> <to-version> <new-core-path>',
|
|
38
|
+
);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface MigrationResult {
|
|
43
|
+
version: string;
|
|
44
|
+
success: boolean;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const results: MigrationResult[] = [];
|
|
49
|
+
|
|
50
|
+
// Look for migrations in the new core
|
|
51
|
+
const migrationsDir = path.join(newCorePath, 'migrations');
|
|
52
|
+
|
|
53
|
+
if (!fs.existsSync(migrationsDir)) {
|
|
54
|
+
console.log(JSON.stringify({ migrationsRun: 0, results: [] }, null, 2));
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Discover migration directories (version-named)
|
|
59
|
+
const entries = fs.readdirSync(migrationsDir, { withFileTypes: true });
|
|
60
|
+
const migrationVersions = entries
|
|
61
|
+
.filter((e) => e.isDirectory() && /^\d+\.\d+\.\d+$/.test(e.name))
|
|
62
|
+
.map((e) => e.name)
|
|
63
|
+
.filter(
|
|
64
|
+
(v) =>
|
|
65
|
+
compareSemver(v, fromVersion) > 0 && compareSemver(v, toVersion) <= 0,
|
|
66
|
+
)
|
|
67
|
+
.sort(compareSemver);
|
|
68
|
+
|
|
69
|
+
const projectRoot = process.cwd();
|
|
70
|
+
|
|
71
|
+
for (const version of migrationVersions) {
|
|
72
|
+
const migrationIndex = path.join(migrationsDir, version, 'index.ts');
|
|
73
|
+
if (!fs.existsSync(migrationIndex)) {
|
|
74
|
+
results.push({
|
|
75
|
+
version,
|
|
76
|
+
success: false,
|
|
77
|
+
error: `Migration ${version}/index.ts not found`,
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const tsxArgs = tsxBin.endsWith('npx')
|
|
84
|
+
? ['tsx', migrationIndex, projectRoot]
|
|
85
|
+
: [migrationIndex, projectRoot];
|
|
86
|
+
execFileSync(tsxBin, tsxArgs, {
|
|
87
|
+
stdio: 'pipe',
|
|
88
|
+
cwd: projectRoot,
|
|
89
|
+
timeout: 120_000,
|
|
90
|
+
});
|
|
91
|
+
results.push({ version, success: true });
|
|
92
|
+
} catch (err) {
|
|
93
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
94
|
+
results.push({ version, success: false, error: message });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log(
|
|
99
|
+
JSON.stringify({ migrationsRun: results.length, results }, null, 2),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Exit with error if any migration failed
|
|
103
|
+
if (results.some((r) => !r.success)) {
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Step: container — Build container image and verify with test run.
|
|
3
|
+
* Replaces 03-setup-container.sh
|
|
4
|
+
*/
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
import { logger } from '../src/logger.js';
|
|
9
|
+
import { commandExists } from './platform.js';
|
|
10
|
+
import { emitStatus } from './status.js';
|
|
11
|
+
|
|
12
|
+
function parseArgs(args: string[]): { runtime: string } {
|
|
13
|
+
let runtime = '';
|
|
14
|
+
for (let i = 0; i < args.length; i++) {
|
|
15
|
+
if (args[i] === '--runtime' && args[i + 1]) {
|
|
16
|
+
runtime = args[i + 1];
|
|
17
|
+
i++;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return { runtime };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function run(args: string[]): Promise<void> {
|
|
24
|
+
const projectRoot = process.cwd();
|
|
25
|
+
const { runtime } = parseArgs(args);
|
|
26
|
+
const image = 'nanoclaw-agent:latest';
|
|
27
|
+
const logFile = path.join(projectRoot, 'logs', 'setup.log');
|
|
28
|
+
|
|
29
|
+
if (!runtime) {
|
|
30
|
+
emitStatus('SETUP_CONTAINER', {
|
|
31
|
+
RUNTIME: 'unknown',
|
|
32
|
+
IMAGE: image,
|
|
33
|
+
BUILD_OK: false,
|
|
34
|
+
TEST_OK: false,
|
|
35
|
+
STATUS: 'failed',
|
|
36
|
+
ERROR: 'missing_runtime_flag',
|
|
37
|
+
LOG: 'logs/setup.log',
|
|
38
|
+
});
|
|
39
|
+
process.exit(4);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate runtime availability
|
|
43
|
+
if (runtime === 'apple-container' && !commandExists('container')) {
|
|
44
|
+
emitStatus('SETUP_CONTAINER', {
|
|
45
|
+
RUNTIME: runtime,
|
|
46
|
+
IMAGE: image,
|
|
47
|
+
BUILD_OK: false,
|
|
48
|
+
TEST_OK: false,
|
|
49
|
+
STATUS: 'failed',
|
|
50
|
+
ERROR: 'runtime_not_available',
|
|
51
|
+
LOG: 'logs/setup.log',
|
|
52
|
+
});
|
|
53
|
+
process.exit(2);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (runtime === 'docker') {
|
|
57
|
+
if (!commandExists('docker')) {
|
|
58
|
+
emitStatus('SETUP_CONTAINER', {
|
|
59
|
+
RUNTIME: runtime,
|
|
60
|
+
IMAGE: image,
|
|
61
|
+
BUILD_OK: false,
|
|
62
|
+
TEST_OK: false,
|
|
63
|
+
STATUS: 'failed',
|
|
64
|
+
ERROR: 'runtime_not_available',
|
|
65
|
+
LOG: 'logs/setup.log',
|
|
66
|
+
});
|
|
67
|
+
process.exit(2);
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
71
|
+
} catch {
|
|
72
|
+
emitStatus('SETUP_CONTAINER', {
|
|
73
|
+
RUNTIME: runtime,
|
|
74
|
+
IMAGE: image,
|
|
75
|
+
BUILD_OK: false,
|
|
76
|
+
TEST_OK: false,
|
|
77
|
+
STATUS: 'failed',
|
|
78
|
+
ERROR: 'runtime_not_available',
|
|
79
|
+
LOG: 'logs/setup.log',
|
|
80
|
+
});
|
|
81
|
+
process.exit(2);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!['apple-container', 'docker'].includes(runtime)) {
|
|
86
|
+
emitStatus('SETUP_CONTAINER', {
|
|
87
|
+
RUNTIME: runtime,
|
|
88
|
+
IMAGE: image,
|
|
89
|
+
BUILD_OK: false,
|
|
90
|
+
TEST_OK: false,
|
|
91
|
+
STATUS: 'failed',
|
|
92
|
+
ERROR: 'unknown_runtime',
|
|
93
|
+
LOG: 'logs/setup.log',
|
|
94
|
+
});
|
|
95
|
+
process.exit(4);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const buildCmd =
|
|
99
|
+
runtime === 'apple-container' ? 'container build' : 'docker build';
|
|
100
|
+
const runCmd = runtime === 'apple-container' ? 'container' : 'docker';
|
|
101
|
+
|
|
102
|
+
// Build
|
|
103
|
+
let buildOk = false;
|
|
104
|
+
logger.info({ runtime }, 'Building container');
|
|
105
|
+
try {
|
|
106
|
+
execSync(`${buildCmd} -t ${image} .`, {
|
|
107
|
+
cwd: path.join(projectRoot, 'container'),
|
|
108
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
109
|
+
});
|
|
110
|
+
buildOk = true;
|
|
111
|
+
logger.info('Container build succeeded');
|
|
112
|
+
} catch (err) {
|
|
113
|
+
logger.error({ err }, 'Container build failed');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Test
|
|
117
|
+
let testOk = false;
|
|
118
|
+
if (buildOk) {
|
|
119
|
+
logger.info('Testing container');
|
|
120
|
+
try {
|
|
121
|
+
const output = execSync(
|
|
122
|
+
`echo '{}' | ${runCmd} run -i --rm --entrypoint /bin/echo ${image} "Container OK"`,
|
|
123
|
+
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] },
|
|
124
|
+
);
|
|
125
|
+
testOk = output.includes('Container OK');
|
|
126
|
+
logger.info({ testOk }, 'Container test result');
|
|
127
|
+
} catch {
|
|
128
|
+
logger.error('Container test failed');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const status = buildOk && testOk ? 'success' : 'failed';
|
|
133
|
+
|
|
134
|
+
emitStatus('SETUP_CONTAINER', {
|
|
135
|
+
RUNTIME: runtime,
|
|
136
|
+
IMAGE: image,
|
|
137
|
+
BUILD_OK: buildOk,
|
|
138
|
+
TEST_OK: testOk,
|
|
139
|
+
STATUS: status,
|
|
140
|
+
LOG: 'logs/setup.log',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (status === 'failed') process.exit(1);
|
|
144
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
import Database from 'better-sqlite3';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Tests for the environment check step.
|
|
8
|
+
*
|
|
9
|
+
* Verifies: config detection, Docker/AC detection, DB queries.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
describe('environment detection', () => {
|
|
13
|
+
it('detects platform correctly', async () => {
|
|
14
|
+
const { getPlatform } = await import('./platform.js');
|
|
15
|
+
const platform = getPlatform();
|
|
16
|
+
expect(['macos', 'linux', 'unknown']).toContain(platform);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('registered groups DB query', () => {
|
|
21
|
+
let db: Database.Database;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
db = new Database(':memory:');
|
|
25
|
+
db.exec(`CREATE TABLE IF NOT EXISTS registered_groups (
|
|
26
|
+
jid TEXT PRIMARY KEY,
|
|
27
|
+
name TEXT NOT NULL,
|
|
28
|
+
folder TEXT NOT NULL UNIQUE,
|
|
29
|
+
trigger_pattern TEXT NOT NULL,
|
|
30
|
+
added_at TEXT NOT NULL,
|
|
31
|
+
container_config TEXT,
|
|
32
|
+
requires_trigger INTEGER DEFAULT 1
|
|
33
|
+
)`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('returns 0 for empty table', () => {
|
|
37
|
+
const row = db
|
|
38
|
+
.prepare('SELECT COUNT(*) as count FROM registered_groups')
|
|
39
|
+
.get() as { count: number };
|
|
40
|
+
expect(row.count).toBe(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns correct count after inserts', () => {
|
|
44
|
+
db.prepare(
|
|
45
|
+
`INSERT INTO registered_groups (jid, name, folder, trigger_pattern, added_at, requires_trigger)
|
|
46
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
47
|
+
).run(
|
|
48
|
+
'123@g.us',
|
|
49
|
+
'Group 1',
|
|
50
|
+
'group-1',
|
|
51
|
+
'@Andy',
|
|
52
|
+
'2024-01-01T00:00:00.000Z',
|
|
53
|
+
1,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
db.prepare(
|
|
57
|
+
`INSERT INTO registered_groups (jid, name, folder, trigger_pattern, added_at, requires_trigger)
|
|
58
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
59
|
+
).run(
|
|
60
|
+
'456@g.us',
|
|
61
|
+
'Group 2',
|
|
62
|
+
'group-2',
|
|
63
|
+
'@Andy',
|
|
64
|
+
'2024-01-01T00:00:00.000Z',
|
|
65
|
+
1,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const row = db
|
|
69
|
+
.prepare('SELECT COUNT(*) as count FROM registered_groups')
|
|
70
|
+
.get() as { count: number };
|
|
71
|
+
expect(row.count).toBe(2);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('credentials detection', () => {
|
|
76
|
+
it('detects ANTHROPIC_API_KEY in env content', () => {
|
|
77
|
+
const content =
|
|
78
|
+
'SOME_KEY=value\nANTHROPIC_API_KEY=sk-ant-test123\nOTHER=foo';
|
|
79
|
+
const hasCredentials =
|
|
80
|
+
/^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY)=/m.test(content);
|
|
81
|
+
expect(hasCredentials).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('detects CLAUDE_CODE_OAUTH_TOKEN in env content', () => {
|
|
85
|
+
const content = 'CLAUDE_CODE_OAUTH_TOKEN=token123';
|
|
86
|
+
const hasCredentials =
|
|
87
|
+
/^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY)=/m.test(content);
|
|
88
|
+
expect(hasCredentials).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('returns false when no credentials', () => {
|
|
92
|
+
const content = 'ASSISTANT_NAME="Andy"\nOTHER=foo';
|
|
93
|
+
const hasCredentials =
|
|
94
|
+
/^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY)=/m.test(content);
|
|
95
|
+
expect(hasCredentials).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('Docker detection logic', () => {
|
|
100
|
+
it('commandExists returns boolean', async () => {
|
|
101
|
+
const { commandExists } = await import('./platform.js');
|
|
102
|
+
expect(typeof commandExists('docker')).toBe('boolean');
|
|
103
|
+
expect(typeof commandExists('nonexistent_binary_xyz')).toBe('boolean');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('channel auth detection', () => {
|
|
108
|
+
it('detects non-empty auth directory', () => {
|
|
109
|
+
const hasAuth = (authDir: string) => {
|
|
110
|
+
try {
|
|
111
|
+
return fs.existsSync(authDir) && fs.readdirSync(authDir).length > 0;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Non-existent directory
|
|
118
|
+
expect(hasAuth('/tmp/nonexistent_auth_dir_xyz')).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Step: environment — Detect OS, Node, container runtimes, existing config.
|
|
3
|
+
* Replaces 01-check-environment.sh
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
import Database from 'better-sqlite3';
|
|
9
|
+
|
|
10
|
+
import { STORE_DIR } from '../src/config.js';
|
|
11
|
+
import { logger } from '../src/logger.js';
|
|
12
|
+
import { commandExists, getPlatform, isHeadless, isWSL } from './platform.js';
|
|
13
|
+
import { emitStatus } from './status.js';
|
|
14
|
+
|
|
15
|
+
export async function run(_args: string[]): Promise<void> {
|
|
16
|
+
const projectRoot = process.cwd();
|
|
17
|
+
|
|
18
|
+
logger.info('Starting environment check');
|
|
19
|
+
|
|
20
|
+
const platform = getPlatform();
|
|
21
|
+
const wsl = isWSL();
|
|
22
|
+
const headless = isHeadless();
|
|
23
|
+
|
|
24
|
+
// Check Apple Container
|
|
25
|
+
let appleContainer: 'installed' | 'not_found' = 'not_found';
|
|
26
|
+
if (commandExists('container')) {
|
|
27
|
+
appleContainer = 'installed';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check Docker
|
|
31
|
+
let docker: 'running' | 'installed_not_running' | 'not_found' = 'not_found';
|
|
32
|
+
if (commandExists('docker')) {
|
|
33
|
+
try {
|
|
34
|
+
const { execSync } = await import('child_process');
|
|
35
|
+
execSync('docker info', { stdio: 'ignore' });
|
|
36
|
+
docker = 'running';
|
|
37
|
+
} catch {
|
|
38
|
+
docker = 'installed_not_running';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check existing config
|
|
43
|
+
const hasEnv = fs.existsSync(path.join(projectRoot, '.env'));
|
|
44
|
+
|
|
45
|
+
const authDir = path.join(projectRoot, 'store', 'auth');
|
|
46
|
+
const hasAuth = fs.existsSync(authDir) && fs.readdirSync(authDir).length > 0;
|
|
47
|
+
|
|
48
|
+
let hasRegisteredGroups = false;
|
|
49
|
+
// Check JSON file first (pre-migration)
|
|
50
|
+
if (fs.existsSync(path.join(projectRoot, 'data', 'registered_groups.json'))) {
|
|
51
|
+
hasRegisteredGroups = true;
|
|
52
|
+
} else {
|
|
53
|
+
// Check SQLite directly using better-sqlite3 (no sqlite3 CLI needed)
|
|
54
|
+
const dbPath = path.join(STORE_DIR, 'messages.db');
|
|
55
|
+
if (fs.existsSync(dbPath)) {
|
|
56
|
+
try {
|
|
57
|
+
const db = new Database(dbPath, { readonly: true });
|
|
58
|
+
const row = db
|
|
59
|
+
.prepare('SELECT COUNT(*) as count FROM registered_groups')
|
|
60
|
+
.get() as { count: number };
|
|
61
|
+
if (row.count > 0) hasRegisteredGroups = true;
|
|
62
|
+
db.close();
|
|
63
|
+
} catch {
|
|
64
|
+
// Table might not exist yet
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
logger.info(
|
|
70
|
+
{
|
|
71
|
+
platform,
|
|
72
|
+
wsl,
|
|
73
|
+
appleContainer,
|
|
74
|
+
docker,
|
|
75
|
+
hasEnv,
|
|
76
|
+
hasAuth,
|
|
77
|
+
hasRegisteredGroups,
|
|
78
|
+
},
|
|
79
|
+
'Environment check complete',
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
emitStatus('CHECK_ENVIRONMENT', {
|
|
83
|
+
PLATFORM: platform,
|
|
84
|
+
IS_WSL: wsl,
|
|
85
|
+
IS_HEADLESS: headless,
|
|
86
|
+
APPLE_CONTAINER: appleContainer,
|
|
87
|
+
DOCKER: docker,
|
|
88
|
+
HAS_ENV: hasEnv,
|
|
89
|
+
HAS_AUTH: hasAuth,
|
|
90
|
+
HAS_REGISTERED_GROUPS: hasRegisteredGroups,
|
|
91
|
+
STATUS: 'success',
|
|
92
|
+
LOG: 'logs/setup.log',
|
|
93
|
+
});
|
|
94
|
+
}
|