@inneranimalmedia/agentsam-sdk 2.6.7 → 2.6.8

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.
Files changed (318) hide show
  1. package/README.md +37 -0
  2. package/docs/REPO_FILE_MAP.txt +132 -0
  3. package/migrations/d1/0018_goap_control_plane_remaster.sql +40 -0
  4. package/migrations/d1/0019_agentsam_agent_run_plan_todo.sql +14 -0
  5. package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +20 -9
  6. package/migrations/runtime/0002_goap_blackboard_v1.sql +15 -0
  7. package/migrations/runtime/0004_agent_run_plan_linkage.sql +8 -0
  8. package/native/agentsam-machine/cli/Cargo.toml +8 -1
  9. package/native/agentsam-machine/cli/README.md +21 -0
  10. package/native/agentsam-machine/core/Cargo.toml +7 -0
  11. package/native/agentsam-machine/core/README.md +14 -0
  12. package/native/agentsam-machine/core/build.rs +33 -0
  13. package/native/agentsam-machine/core/contracts/source-types.v1.json +353 -0
  14. package/native/agentsam-machine/core/src/lib.rs +6 -0
  15. package/native/agentsam-machine/core/src/source_types.rs +2 -2
  16. package/package.json +16 -6
  17. package/packages/agentsam-errors/package.json +1 -1
  18. package/packages/agentsam-errors/src/index.js +1 -0
  19. package/packages/agentsam-errors/src/tool.js +56 -0
  20. package/packages/agentsam-errors/test/tool.test.mjs +58 -0
  21. package/packages/agentsam-goap/README.md +52 -0
  22. package/packages/agentsam-goap/package.json +37 -0
  23. package/packages/agentsam-goap/src/adapters/d1-sqlite.js +570 -0
  24. package/packages/agentsam-goap/src/adapters/memory.js +199 -0
  25. package/packages/agentsam-goap/src/contracts.js +226 -0
  26. package/packages/agentsam-goap/src/control-plane.js +112 -0
  27. package/packages/agentsam-goap/src/errors.js +157 -0
  28. package/packages/agentsam-goap/src/index.js +5 -0
  29. package/packages/agentsam-goap/src/world.js +202 -0
  30. package/packages/agentsam-goap/test/goap-control-plane.test.mjs +245 -0
  31. package/packages/agentsam-goap/test/world.test.mjs +79 -0
  32. package/packages/agentsam-nav/dist/Nav.d.ts +43 -0
  33. package/packages/agentsam-nav/dist/NavCollapsible.d.ts +10 -0
  34. package/packages/agentsam-nav/dist/NavContent.d.ts +2 -0
  35. package/packages/agentsam-nav/dist/NavFooter.d.ts +2 -0
  36. package/packages/agentsam-nav/dist/NavGroup.d.ts +3 -0
  37. package/packages/agentsam-nav/dist/NavHeader.d.ts +2 -0
  38. package/packages/agentsam-nav/dist/NavMenu.d.ts +22 -0
  39. package/packages/agentsam-nav/dist/NavProvider.d.ts +35 -0
  40. package/packages/agentsam-nav/dist/NavTrigger.d.ts +2 -0
  41. package/packages/agentsam-nav/dist/Surfaces.d.ts +41 -0
  42. package/packages/agentsam-nav/dist/browser.d.ts +11 -0
  43. package/packages/agentsam-nav/dist/browser.global.js +135 -0
  44. package/packages/agentsam-nav/dist/browser.js +7290 -0
  45. package/packages/agentsam-nav/dist/contracts.d.ts +76 -0
  46. package/packages/agentsam-nav/dist/index.d.ts +11 -0
  47. package/packages/agentsam-nav/dist/index.js +7291 -0
  48. package/packages/agentsam-nav/dist/legacy.css +83 -0
  49. package/packages/agentsam-nav/dist/shell.css +113 -0
  50. package/packages/agentsam-nav/dist/theme.css +113 -0
  51. package/packages/agentsam-nav/dist/utils.d.ts +1 -0
  52. package/packages/agentsam-repository/src/goap.js +13 -14
  53. package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +670 -0
  54. package/packages/agentsam-repository/src/tickets.js +15 -22
  55. package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +19 -8
  56. package/packages/connectors/cloudflare/package.json +3 -1
  57. package/packages/identity/tests/lane1-packed-consumer.test.mjs +1 -1
  58. package/python/agentsam_sdk/repository/intelligence/discovery.py +27 -2
  59. package/python/agentsam_sdk/repository/intelligence/git.py +4 -0
  60. package/python/tests/test_repository_intelligence.py +30 -0
  61. package/src/cli/command-catalog.js +2 -1
  62. package/src/cli.js +10 -1
  63. package/src/commands/goap-world.js +134 -0
  64. package/src/commands/goap.js +11 -4
  65. package/src/commands/machine-binary.js +33 -27
  66. package/src/commands/machine-runtime.js +266 -0
  67. package/src/commands/machine.js +122 -51
  68. package/src/commands/package.js +530 -0
  69. package/src/commands/plan-ledger.js +370 -0
  70. package/src/commands/plan.js +183 -0
  71. package/src/commands/skill.js +78 -17
  72. package/src/knowledge/source.js +1 -1
  73. package/src/lib/slash-commands.js +1 -1
  74. package/src/local/runtime-store.js +24 -2
  75. package/src/local/sqlite.js +17 -0
  76. package/src/sam/operations/package-audit.js +41 -0
  77. package/src/sam/seed.js +2 -0
  78. package/skills/grok-auth/SKILL.md +0 -110
  79. package/skills/grok-auth/agentsam.skill.json +0 -25
  80. package/skills/grok-auth/references/grok-identity.md +0 -64
  81. package/skills/grok-auth/references/per-user-data.md +0 -49
  82. package/skills/grok-auth/references/prewired-and-env.md +0 -54
  83. package/skills/grok-auth/references/session-ui.md +0 -133
  84. package/skills/grok-auth/references/sign-in-methods.md +0 -55
  85. package/skills/grok-auth/references/wiring.md +0 -71
  86. package/skills/grok-building-games/SKILL.md +0 -213
  87. package/skills/grok-building-games/agentsam.skill.json +0 -25
  88. package/skills/grok-building-games/references/3d-libs.md +0 -64
  89. package/skills/grok-building-games/references/ai-pathfinding.md +0 -122
  90. package/skills/grok-building-games/references/audio.md +0 -129
  91. package/skills/grok-building-games/references/babylon.md +0 -202
  92. package/skills/grok-building-games/references/collision-physics.md +0 -122
  93. package/skills/grok-building-games/references/ecs-architecture.md +0 -111
  94. package/skills/grok-building-games/references/game-feel-juice.md +0 -110
  95. package/skills/grok-building-games/references/genres/board-card-chess.md +0 -89
  96. package/skills/grok-building-games/references/genres/endless-runner.md +0 -83
  97. package/skills/grok-building-games/references/genres/fps.md +0 -105
  98. package/skills/grok-building-games/references/genres/platformer-2d.md +0 -97
  99. package/skills/grok-building-games/references/genres/puzzle-match3-tetris.md +0 -131
  100. package/skills/grok-building-games/references/genres/racing-kart.md +0 -106
  101. package/skills/grok-building-games/references/genres/topdown-twin-stick.md +0 -97
  102. package/skills/grok-building-games/references/genres/tower-defense.md +0 -84
  103. package/skills/grok-building-games/references/genres/voxel-minecraft.md +0 -89
  104. package/skills/grok-building-games/references/input.md +0 -139
  105. package/skills/grok-building-games/references/phaser.md +0 -277
  106. package/skills/grok-building-games/references/procedural-generation.md +0 -141
  107. package/skills/grok-building-games/references/save-persistence.md +0 -108
  108. package/skills/grok-building-games/references/threejs-foundational.md +0 -148
  109. package/skills/grok-controls/SKILL.md +0 -274
  110. package/skills/grok-controls/agentsam.skill.json +0 -25
  111. package/skills/grok-design-ui/SKILL.md +0 -122
  112. package/skills/grok-design-ui/agentsam.skill.json +0 -25
  113. package/skills/grok-design-ui/references/animations.md +0 -379
  114. package/skills/grok-design-ui/references/performance.md +0 -88
  115. package/skills/grok-design-ui/references/refined-ui.md +0 -384
  116. package/skills/grok-design-ui/references/surfaces.md +0 -247
  117. package/skills/grok-design-ui/references/typography.md +0 -123
  118. package/skills/grok-game-animation-frames/SKILL.md +0 -84
  119. package/skills/grok-game-animation-frames/agentsam.skill.json +0 -25
  120. package/skills/grok-game-asset-core/SKILL.md +0 -69
  121. package/skills/grok-game-asset-core/agentsam.skill.json +0 -25
  122. package/skills/grok-game-character-consistency/SKILL.md +0 -69
  123. package/skills/grok-game-character-consistency/agentsam.skill.json +0 -25
  124. package/skills/grok-game-tilesets/SKILL.md +0 -65
  125. package/skills/grok-game-tilesets/agentsam.skill.json +0 -25
  126. package/skills/grok-game-ui-icons/SKILL.md +0 -56
  127. package/skills/grok-game-ui-icons/agentsam.skill.json +0 -25
  128. package/skills/grok-generate2dmap/LICENSE +0 -21
  129. package/skills/grok-generate2dmap/SKILL.md +0 -120
  130. package/skills/grok-generate2dmap/SOURCE.md +0 -6
  131. package/skills/grok-generate2dmap/agentsam.skill.json +0 -25
  132. package/skills/grok-generate2dmap/references/deliverables.md +0 -101
  133. package/skills/grok-generate2dmap/references/layered-map-contract.md +0 -209
  134. package/skills/grok-generate2dmap/references/map-strategies.md +0 -341
  135. package/skills/grok-generate2dmap/references/object-production-gate.md +0 -24
  136. package/skills/grok-generate2dmap/references/pipeline.md +0 -149
  137. package/skills/grok-generate2dmap/references/prop-pack-contract.md +0 -174
  138. package/skills/grok-generate2dmap/references/side-scroll-stages.md +0 -29
  139. package/skills/grok-generate2dmap/scripts/compose_layered_preview.py +0 -157
  140. package/skills/grok-generate2dmap/scripts/extract_prop_pack.py +0 -369
  141. package/skills/grok-generate2dsprite/LICENSE +0 -21
  142. package/skills/grok-generate2dsprite/SKILL.md +0 -313
  143. package/skills/grok-generate2dsprite/SOURCE.md +0 -6
  144. package/skills/grok-generate2dsprite/agentsam.skill.json +0 -25
  145. package/skills/grok-generate2dsprite/references/modes.md +0 -124
  146. package/skills/grok-generate2dsprite/references/prompt-rules.md +0 -315
  147. package/skills/grok-generate2dsprite/scripts/generate2dsprite.py +0 -1031
  148. package/skills/grok-generate2dsprite/scripts/make_layout_guide.py +0 -109
  149. package/skills/grok-imagine/SKILL.md +0 -201
  150. package/skills/grok-imagine/agentsam.skill.json +0 -25
  151. package/skills/grok-multiplayer-p2p/SKILL.md +0 -111
  152. package/skills/grok-multiplayer-p2p/agentsam.skill.json +0 -25
  153. package/skills/grok-multiplayer-p2p/references/react-binding.md +0 -144
  154. package/skills/grok-multiplayer-p2p/references/signaling-relay.md +0 -269
  155. package/skills/grok-neon/SKILL.md +0 -157
  156. package/skills/grok-neon/agentsam.skill.json +0 -25
  157. package/skills/grok-og/SKILL.md +0 -102
  158. package/skills/grok-og/agentsam.skill.json +0 -25
  159. package/skills/grok-og/references/brand-pass.md +0 -53
  160. package/skills/grok-og/references/custom-card.md +0 -107
  161. package/skills/grok-og/references/favicon-and-icons.md +0 -48
  162. package/skills/grok-og/references/og-type-contract.md +0 -18
  163. package/skills/grok-og/references/placeholder-card.md +0 -21
  164. package/skills/grok-og/references/x-banner.md +0 -41
  165. package/skills/grok-threejs/SKILL.md +0 -67
  166. package/skills/grok-threejs/agentsam.skill.json +0 -25
  167. package/skills/grok-threejs/references/llms-full.txt +0 -2780
  168. package/skills/grok-video2dsprite/LICENSE +0 -21
  169. package/skills/grok-video2dsprite/SKILL.md +0 -236
  170. package/skills/grok-video2dsprite/SOURCE.md +0 -6
  171. package/skills/grok-video2dsprite/agentsam.skill.json +0 -25
  172. package/skills/grok-video2dsprite/references/pipeline.md +0 -90
  173. package/skills/grok-video2dsprite/references/prompt-rules.md +0 -63
  174. package/skills/grok-video2dsprite/scripts/video2dsprite.py +0 -490
  175. package/skills/grok-xai-api/SKILL.md +0 -152
  176. package/skills/grok-xai-api/agentsam.skill.json +0 -25
  177. package/test/account-session.test.mjs +0 -101
  178. package/test/agent-capabilities.test.mjs +0 -67
  179. package/test/app-authority-context.test.mjs +0 -32
  180. package/test/app-building-skills.test.mjs +0 -61
  181. package/test/apps-scaffold-contract.test.mjs +0 -115
  182. package/test/blender-cad.test.mjs +0 -146
  183. package/test/cad-docker.test.mjs +0 -96
  184. package/test/capabilities.test.mjs +0 -133
  185. package/test/cli/api-key-env-whoami.test.mjs +0 -129
  186. package/test/cli/catalog-authority.test.mjs +0 -64
  187. package/test/cli/codebaseindex-plan-ux.test.mjs +0 -37
  188. package/test/cli/eval-live.test.mjs +0 -219
  189. package/test/cli/go.test.mjs +0 -128
  190. package/test/cli/local-vault-and-provider-setup.test.mjs +0 -268
  191. package/test/cli/machine-command.test.mjs +0 -34
  192. package/test/cli/mcp-client.test.mjs +0 -256
  193. package/test/cli/preferences-runtime.test.mjs +0 -11
  194. package/test/cli/runtime-persist.test.mjs +0 -56
  195. package/test/cli/runtime-ui.test.mjs +0 -141
  196. package/test/cli/rust-command.test.mjs +0 -21
  197. package/test/cli/skill-npm-and-env.test.mjs +0 -52
  198. package/test/cli/status-actions.test.mjs +0 -88
  199. package/test/cli/wireframes-go-registry.test.mjs +0 -139
  200. package/test/cli-preferences.test.mjs +0 -46
  201. package/test/cloudflare-connector.test.mjs +0 -93
  202. package/test/cloudflare-runtime.test.mjs +0 -82
  203. package/test/context.test.mjs +0 -159
  204. package/test/credentials-audit.test.mjs +0 -113
  205. package/test/deploy-health-scan.test.mjs +0 -95
  206. package/test/deploy-receipt.test.mjs +0 -91
  207. package/test/dockerize.test.mjs +0 -93
  208. package/test/error-diagnostics.test.mjs +0 -124
  209. package/test/eval-context.test.mjs +0 -37
  210. package/test/execution-approvals.test.mjs +0 -27
  211. package/test/fixtures/cad/courtyard-house.json +0 -208
  212. package/test/fixtures/knowledge-rpc-worker.mjs +0 -16
  213. package/test/fixtures/project-authority/customer-app/.agentsam/app.json +0 -5
  214. package/test/fixtures/project-authority/customer-app/apps/my-product/index.ts +0 -1
  215. package/test/fixtures/project-authority/customer-app/wrangler.toml +0 -17
  216. package/test/gcloud-auth.test.mjs +0 -111
  217. package/test/go/build-source-identity.test.mjs +0 -31
  218. package/test/go/cloudflare-probe.test.mjs +0 -317
  219. package/test/go/distribution.test.mjs +0 -28
  220. package/test/google-cloud-connection.test.mjs +0 -83
  221. package/test/google-cloud-inventory.test.mjs +0 -64
  222. package/test/indexing-provider.test.mjs +0 -29
  223. package/test/init-options.test.mjs +0 -35
  224. package/test/integration/ai-access-onboarding.test.mjs +0 -47
  225. package/test/integration/app-command.test.mjs +0 -85
  226. package/test/integration/autorag-project-authority.integration.test.mjs +0 -51
  227. package/test/integration/autorag-runtime-discovery.test.mjs +0 -107
  228. package/test/integration/cad-discovery.test.mjs +0 -158
  229. package/test/integration/cad-docker-executor.test.mjs +0 -249
  230. package/test/integration/cad-execution.test.mjs +0 -184
  231. package/test/integration/cad-project-app.test.mjs +0 -31
  232. package/test/integration/cad-project-d1.test.mjs +0 -25
  233. package/test/integration/cad-project-native.test.mjs +0 -24
  234. package/test/integration/cad-project-workflow.test.mjs +0 -9
  235. package/test/integration/cad-project.test.mjs +0 -85
  236. package/test/integration/cli-help.test.mjs +0 -41
  237. package/test/integration/cms-app-scaffold.test.mjs +0 -77
  238. package/test/integration/cms-site-tenancy-contract.test.mjs +0 -268
  239. package/test/integration/codebaseindex-ingest.test.mjs +0 -255
  240. package/test/integration/core-client.integration.test.mjs +0 -55
  241. package/test/integration/database-hosted-honesty.test.mjs +0 -49
  242. package/test/integration/desktop-keychain-boundary.test.mjs +0 -45
  243. package/test/integration/error-runtime-rpc.test.mjs +0 -71
  244. package/test/integration/features-resolve.test.mjs +0 -147
  245. package/test/integration/hosted-work-host-authority.test.mjs +0 -47
  246. package/test/integration/icon-registry.test.mjs +0 -67
  247. package/test/integration/ingest-discover-models.test.mjs +0 -30
  248. package/test/integration/inspect-path-cli.test.mjs +0 -46
  249. package/test/integration/install-script.test.mjs +0 -110
  250. package/test/integration/inventory-core.test.mjs +0 -53
  251. package/test/integration/inventory-eligibility.test.mjs +0 -59
  252. package/test/integration/inventory-evidence.test.mjs +0 -125
  253. package/test/integration/knowledge-rpc.test.mjs +0 -112
  254. package/test/integration/local-fs.test.mjs +0 -113
  255. package/test/integration/local-provider-bridge.test.mjs +0 -65
  256. package/test/integration/local-studio-settings-production.test.mjs +0 -40
  257. package/test/integration/lockfile-sync.test.mjs +0 -26
  258. package/test/integration/merkle-cli.test.mjs +0 -61
  259. package/test/integration/merkle-persistence-identity.test.mjs +0 -48
  260. package/test/integration/plugins-cloudflare.integration.test.mjs +0 -28
  261. package/test/integration/provider-env-cli.test.mjs +0 -60
  262. package/test/integration/provider-factory.test.mjs +0 -197
  263. package/test/integration/repository-company-graph.test.mjs +0 -90
  264. package/test/integration/runtime-migrations.test.mjs +0 -87
  265. package/test/integration/runtime-status.integration.test.mjs +0 -227
  266. package/test/integration/sam-activity-recovery.test.mjs +0 -147
  267. package/test/integration/sam-decision.test.mjs +0 -584
  268. package/test/integration/sam-kernel.test.mjs +0 -99
  269. package/test/integration/sam-planning-astar.test.mjs +0 -279
  270. package/test/integration/scaffold-cloudflare.test.mjs +0 -46
  271. package/test/integration/skill-runtime.test.mjs +0 -240
  272. package/test/integration/storage-architecture.test.mjs +0 -154
  273. package/test/integration/store-discovery.test.mjs +0 -44
  274. package/test/integration/studio-fs-pty-e2e.test.mjs +0 -294
  275. package/test/integration/terminal-exec.integration.test.mjs +0 -86
  276. package/test/integration/theme-package-registry.test.mjs +0 -31
  277. package/test/integration/tool-runtime.test.mjs +0 -124
  278. package/test/integration/tool-schema.test.mjs +0 -59
  279. package/test/jr-dev-skill.test.mjs +0 -26
  280. package/test/knowledge-context-pack.test.mjs +0 -19
  281. package/test/knowledge-postgres.test.mjs +0 -40
  282. package/test/knowledge-service.test.mjs +0 -110
  283. package/test/knowledge.test.mjs +0 -206
  284. package/test/live/terminal-transport.live.test.mjs +0 -24
  285. package/test/local-sessions.test.mjs +0 -57
  286. package/test/local-studio-deploy.test.mjs +0 -83
  287. package/test/loopback-oauth.test.mjs +0 -53
  288. package/test/merkle-render.test.mjs +0 -31
  289. package/test/mini.test.mjs +0 -140
  290. package/test/model-catalog.test.mjs +0 -70
  291. package/test/models.test.mjs +0 -309
  292. package/test/npm10-lock.test.mjs +0 -29
  293. package/test/ollama.test.mjs +0 -137
  294. package/test/open-url.test.mjs +0 -64
  295. package/test/openai-responses.test.mjs +0 -155
  296. package/test/portable-context.test.mjs +0 -94
  297. package/test/project-config.test.mjs +0 -81
  298. package/test/project-rules.test.mjs +0 -44
  299. package/test/provider-credentials.test.mjs +0 -106
  300. package/test/rehydrate.test.mjs +0 -25
  301. package/test/release-hygiene.test.mjs +0 -42
  302. package/test/repository-snapshot-view.test.mjs +0 -112
  303. package/test/responses-runner.test.mjs +0 -465
  304. package/test/runtime-activity.test.mjs +0 -98
  305. package/test/sdk-worker-contract.test.mjs +0 -72
  306. package/test/security.test.mjs +0 -266
  307. package/test/setup-planner.test.mjs +0 -74
  308. package/test/setup-runtime.test.mjs +0 -51
  309. package/test/shell.test.mjs +0 -281
  310. package/test/skills.test.mjs +0 -22
  311. package/test/smoke.mjs +0 -122
  312. package/test/telemetry.test.mjs +0 -79
  313. package/test/terminal/local-pty.mock.test.mjs +0 -153
  314. package/test/terminal-machine-identity.test.mjs +0 -88
  315. package/test/theme-portability.test.mjs +0 -14
  316. package/test/tools-search.test.mjs +0 -73
  317. package/test/unit/website-assets-bindings.test.mjs +0 -60
  318. package/test/whoami-resume.test.mjs +0 -157
@@ -1,82 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import test from 'node:test';
6
- import { buildWranglerInvocation, listWranglerNativeCommands, runWranglerNative, summarizeCloudflareCpuProfile, summarizeCloudflareCpuProfileFile } from '../src/cloudflare/index.js';
7
- import { createCapabilityAdapter } from '../src/agent/capability-adapter.js';
8
-
9
- function tempRoot() { return fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-cf-')); }
10
-
11
- test('Cloudflare native command catalog exposes bounded read operations and never an auth-token secret read', () => {
12
- const ids = listWranglerNativeCommands().map((row) => row.id);
13
- assert.deepEqual(ids, ['whoami', 'deployments.list', 'versions.list', 'versions.view', 'types.check', 'queues.list', 'tunnel.list', 'tunnel.info']);
14
- assert.ok(!ids.some((id) => id.includes('token') || id.includes('secret') || id === 'deploy'));
15
- });
16
-
17
- test('Wrangler version view requires an exact version and keeps secret values unavailable', () => {
18
- const root = tempRoot();
19
- const plan = buildWranglerInvocation('versions.view', { cwd: root, name: 'demo', version_id: 'ver_123' });
20
- assert.deepEqual(plan.args, ['versions', 'view', 'ver_123', '--json', '--name', 'demo']);
21
- assert.throws(() => buildWranglerInvocation('versions.view', { cwd: root, name: 'demo' }), /version_id_required/);
22
- });
23
-
24
- test('Wrangler native invocation is argv-based and cwd/config scoped', () => {
25
- const root = tempRoot();
26
- fs.writeFileSync(path.join(root, 'wrangler.jsonc'), '{}');
27
- const plan = buildWranglerInvocation('deployments.list', { cwd: root, name: 'demo' });
28
- assert.equal(plan.cwd, root);
29
- assert.deepEqual(plan.args.slice(0, 4), ['deployments', 'list', '--json', '--name']);
30
- assert.equal(plan.args[4], 'demo');
31
- assert.ok(plan.args.includes('--config'));
32
- assert.throws(() => buildWranglerInvocation('whoami', { cwd: root, config: '../outside.toml' }), /outside_cwd/);
33
- });
34
-
35
- test('Wrangler native result preserves JSON and real exit code on failure', async () => {
36
- const root = tempRoot();
37
- const success = await runWranglerNative('whoami', { cwd: root }, { run: async () => ({ code: 0, stdout: JSON.stringify({ email: 'dev@example.com' }), stderr: '' }) });
38
- assert.equal(success.data.email, 'dev@example.com');
39
- await assert.rejects(
40
- runWranglerNative('versions.list', { cwd: root }, { run: async () => ({ code: 7, stdout: '', stderr: 'upstream failed' }) }),
41
- (error) => error.diagnostic?.exit_code === 7 && error.diagnostic?.code === 'wrangler_exit_nonzero',
42
- );
43
- await assert.rejects(
44
- runWranglerNative('deployments.list', { cwd: root }, { run: async () => ({ code: 1, stdout: '', stderr: 'Authentication error [code: 10000]\nRequest ID: req_cf_1\nCF-Ray: ray-123' }) }),
45
- (error) => error.diagnostic?.code === '10000' && error.diagnostic?.request_id === 'req_cf_1' && error.diagnostic?.ray_id === 'ray-123',
46
- );
47
- });
48
-
49
- test('CPU profile summary ranks self-time hotspots and explicitly rejects production timer inference', () => {
50
- const profile = {
51
- nodes: [
52
- { id: 1, callFrame: { functionName: 'fetch', url: 'worker.js', lineNumber: 0, columnNumber: 0 } },
53
- { id: 2, callFrame: { functionName: 'heavyLoop', url: 'worker.js', lineNumber: 10, columnNumber: 2 } },
54
- { id: 3, callFrame: { functionName: '(garbage collector)', url: '', lineNumber: -1, columnNumber: -1 } },
55
- ],
56
- samples: [1, 2, 2, 3],
57
- timeDeltas: [100, 1200, 800, 400],
58
- };
59
- const summary = summarizeCloudflareCpuProfile(profile);
60
- assert.equal(summary.top_frames[0].function, 'heavyLoop');
61
- assert.equal(summary.top_frames[0].self_us, 2000);
62
- assert.equal(summary.garbage_collection_ms, 0.4);
63
- assert.match(summary.timer_semantics, /do not advance during CPU-only execution/);
64
- });
65
-
66
- test('CPU profile file cannot escape runtime cwd', () => {
67
- const root = tempRoot();
68
- fs.writeFileSync(path.join(root, 'profile.cpuprofile'), JSON.stringify({ nodes: [], samples: [], timeDeltas: [] }));
69
- assert.equal(summarizeCloudflareCpuProfileFile({ cwd: root, file: 'profile.cpuprofile' }).file, 'profile.cpuprofile');
70
- assert.throws(() => summarizeCloudflareCpuProfileFile({ cwd: root, file: '../profile.cpuprofile' }), /outside_cwd/);
71
- });
72
-
73
- test('explicitly executable experimental Cloudflare capabilities enter cards-first discovery without exposing unavailable commands', () => {
74
- const adapter = createCapabilityAdapter();
75
- const rows = adapter.toolDescriptors();
76
- const ids = rows.map((row) => row.name);
77
- assert.ok(ids.includes('cloudflare.wrangler.native'));
78
- assert.ok(ids.includes('cloudflare.cpu.profile'));
79
- assert.ok(!ids.includes('cloudflare.cpu.audit'));
80
- assert.ok(!ids.includes('blender.build'));
81
- assert.equal(rows.find((row) => row.name === 'cloudflare.wrangler.native').risk, 'read');
82
- });
@@ -1,159 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import test from 'node:test';
6
- import {
7
- DEFAULT_RESULT_POLICY,
8
- assessContextUsage,
9
- compileAgentInstructions,
10
- createContextBudget,
11
- normalizeResultPolicy,
12
- resolveContext,
13
- resolveProjectContext,
14
- } from '../src/context/index.js';
15
-
16
- test('default result policy is bounded and callers may request less', () => {
17
- assert.deepEqual(DEFAULT_RESULT_POLICY, { max_items: 8, max_chars: 24_000, detail: 'excerpt' });
18
- assert.deepEqual(normalizeResultPolicy({ max_items: 3, max_chars: 8_000, detail: 'card' }), { max_items: 3, max_chars: 8_000, detail: 'card' });
19
- assert.throws(() => normalizeResultPolicy({ max_items: 9 }), /higher_detail_required:max_items/);
20
- assert.throws(() => normalizeResultPolicy({ max_chars: 24_001 }), /higher_detail_required:max_chars/);
21
- assert.throws(() => normalizeResultPolicy({ detail: 'full' }), /higher_detail_required:detail/);
22
- assert.equal(normalizeResultPolicy({ detail: 'full', max_items: 20, max_chars: 100_000 }, { operation: 'higher-detail' }).detail, 'full');
23
- });
24
-
25
- test('legacy ratio context budget remains compatible for ordinary windows', () => {
26
- const budget = createContextBudget({ windowTokens: 250_000 });
27
- assert.equal(budget.targetInputTokens, 150_000);
28
- assert.equal(budget.hardInputTokens, 212_500);
29
- assert.equal(budget.windowChars, 1_000_000);
30
- assert.equal(budget.maxSystemChars, 50_000);
31
- assert.equal(budget.maxToolSchemaChars, 40_000);
32
- assert.equal(budget.maxEvidenceChars, 100_000);
33
- assert.equal(budget.maxFileCharsPerRead, 32_768);
34
- assert.equal(budget.maxFileCharsPerTurn, 131_072);
35
- assert.equal(budget.maxToolResultChars, 24_000);
36
- });
37
-
38
- test('large model capacity is independent from AgentSam working-set and pricing policy', () => {
39
- const budget = createContextBudget({
40
- windowTokens: 1_050_000,
41
- targetInputTokens: 120_000,
42
- compactAtTokens: 180_000,
43
- interveneAtTokens: 220_000,
44
- maxNormalInputTokens: 250_000,
45
- pricingThresholdTokens: 272_000,
46
- safetyMarginTokens: 22_000,
47
- });
48
- assert.equal(budget.windowTokens, 1_050_000);
49
- assert.equal(budget.targetInputTokens, 120_000);
50
- assert.equal(budget.compactAtTokens, 180_000);
51
- assert.equal(budget.interveneAtTokens, 220_000);
52
- assert.equal(budget.maxNormalInputTokens, 250_000);
53
- assert.equal(budget.pricingThresholdTokens, 272_000);
54
- assert.ok(budget.targetInputRatio < 0.12);
55
-
56
- assert.equal(assessContextUsage(179_999, budget).shouldCompact, false);
57
- assert.equal(assessContextUsage(180_000, budget).shouldCompact, true);
58
- assert.equal(assessContextUsage(272_000, budget).pricingThresholdCrossed, false);
59
- const crossed = assessContextUsage(272_001, budget);
60
- assert.equal(crossed.pricingThresholdCrossed, true);
61
- assert.equal(crossed.tokensUntilPricingThreshold, -1);
62
- });
63
-
64
- test('context.resolve separates composition classes and returns an economic receipt', () => {
65
- const budget = createContextBudget({
66
- windowTokens: 32_000,
67
- maxEvidenceChars: 90,
68
- maxFileCharsPerRead: 50,
69
- maxFileCharsPerTurn: 60,
70
- maxToolResultChars: 30,
71
- });
72
- const pack = resolveContext({
73
- objective: 'fix terminal cwd persistence',
74
- refs: ['repo:demo'],
75
- budget,
76
- instructionChars: 20,
77
- toolSchemaChars: 12,
78
- historyChars: 8,
79
- items: [
80
- { ref: 'file:low', kind: 'file', priority: 1, content: 'L'.repeat(50) },
81
- { ref: 'tool:high', kind: 'tool_result', priority: 10, content: 'T'.repeat(50) },
82
- { ref: 'file:high', kind: 'file', priority: 8, content: 'F'.repeat(80) },
83
- { ref: 'memory:later', kind: 'memory', priority: 0, content: 'M'.repeat(40) },
84
- ],
85
- });
86
-
87
- assert.deepEqual(pack.items.map((item) => item.ref), ['tool:high', 'file:high', 'file:low']);
88
- assert.equal(pack.items[0].chars, 30);
89
- assert.equal(pack.items[1].chars, 50);
90
- assert.equal(pack.items[2].chars, 10);
91
- assert.equal(pack.receipt.tool_result_chars, 30);
92
- assert.equal(pack.receipt.evidence_chars, 60);
93
- assert.equal(pack.receipt.instruction_chars, 20);
94
- assert.equal(pack.receipt.tool_schema_chars, 12);
95
- assert.equal(pack.receipt.history_chars, 8);
96
- assert.equal(pack.receipt.chars, 130);
97
- assert.equal(pack.receipt.estimate_kind, 'local');
98
- assert.equal(pack.receipt.window_tokens, 32_000);
99
- assert.equal(pack.receipt.sources_considered, 4);
100
- assert.equal(pack.receipt.sources_included, 3);
101
- assert.ok(pack.receipt.sources_deferred >= 3);
102
- assert.ok(pack.receipt.deferred_refs.includes('memory:later'));
103
- assert.deepEqual(pack.receipt.rehydratable_refs, ['tool:high', 'file:high', 'file:low']);
104
- });
105
-
106
- test('AgentSam instructions compile in stable then repository-specific precedence', t => {
107
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-instructions-'));
108
- t.after(() => fs.rmSync(root, { recursive: true, force: true }));
109
- fs.mkdirSync(path.join(root, '.git'));
110
- fs.writeFileSync(path.join(root, 'AGENTSAM.md'), '# Stable\nbase law\n');
111
- fs.writeFileSync(path.join(root, '.agentsamrules'), '# Repo\nlocal law\n');
112
- const compiled = compileAgentInstructions(root);
113
- assert.deepEqual(compiled.precedence, ['AGENTSAM.md', '.agentsamrules']);
114
- assert.equal(compiled.sources.length, 2);
115
- assert.ok(compiled.content.indexOf('base law') < compiled.content.indexOf('local law'));
116
- });
117
-
118
- test('compatibility shims are not compiled into AgentSam instruction authority', t => {
119
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-instruction-shims-'));
120
- t.after(() => fs.rmSync(root, { recursive: true, force: true }));
121
- fs.mkdirSync(path.join(root, '.git'));
122
- fs.writeFileSync(path.join(root, 'AGENTSAM.md'), '# Stable\ncanonical only\n');
123
- fs.writeFileSync(path.join(root, 'AGENTS.md'), 'shim should not become policy\n');
124
- fs.writeFileSync(path.join(root, 'CLAUDE.md'), 'claude shim should not become policy\n');
125
- const compiled = compileAgentInstructions(root);
126
- assert.equal(compiled.sources.length, 1);
127
- assert.equal(compiled.sources[0].filename, 'AGENTSAM.md');
128
- assert.match(compiled.content, /canonical only/);
129
- assert.doesNotMatch(compiled.content, /shim should not become policy/);
130
- assert.doesNotMatch(compiled.content, /claude shim should not become policy/);
131
- });
132
-
133
- test('project context loads bounded compiled AgentSam instructions', t => {
134
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-context-rules-'));
135
- t.after(() => fs.rmSync(root, { recursive: true, force: true }));
136
- fs.mkdirSync(path.join(root, '.git'));
137
- fs.writeFileSync(path.join(root, 'AGENTSAM.md'), '# stable\n' + 'a'.repeat(300));
138
- fs.writeFileSync(path.join(root, '.agentsamrules'), '# rules\n' + 'x'.repeat(5000) + '\n');
139
- const pack = resolveProjectContext({
140
- cwd: root,
141
- objective: 'inspect repository',
142
- budget: createContextBudget({ windowTokens: 8_000, maxSystemChars: 1_000, maxEvidenceChars: 1_000 }),
143
- items: [],
144
- });
145
- assert.equal(pack.rules.found, true);
146
- assert.equal(pack.rules.chars, 1_000);
147
- assert.equal(pack.rules.truncated, true);
148
- assert.equal(pack.receipt.system_chars, 1_000);
149
- });
150
-
151
- test('consumed tool results compact to 4k while preserving rehydration identity', async () => {
152
- const { compactConsumedToolResult } = await import('../src/context/index.js');
153
- const item = compactConsumedToolResult('x'.repeat(10_000), { ref: 'tool:call_123', hash: 'sha256:abc' });
154
- assert.equal(item.ref, 'tool:call_123');
155
- assert.equal(item.hash, 'sha256:abc');
156
- assert.equal(item.chars, 4_000);
157
- assert.equal(item.compacted, true);
158
- assert.equal(item.source_chars, 10_000);
159
- });
@@ -1,113 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import test from 'node:test';
3
- import {
4
- remediateGcloudEdgeServiceAccountKey,
5
- remediateGcloudPermissionDenied,
6
- remediateGithubTokenShadow,
7
- remediateProviderFailure,
8
- renderRemediationCard,
9
- recommendedCommand,
10
- } from '../src/lib/provider-command-remediation.js';
11
- import { runCredentials } from '../src/commands/credentials.js';
12
- import { listCheatSheetGroups, renderCheatSheet } from '../src/commands/cheat-sheet.js';
13
- import { collectCredentialsAudit } from '../src/lib/credentials-audit.js';
14
-
15
- test('edge-cloud service-account key create remediates to IAM / compute intents', () => {
16
- const card = remediateGcloudEdgeServiceAccountKey({
17
- argv: ['gcloud', 'edge-cloud', 'service-accounts', 'keys', 'create'],
18
- projectId: 'gen-lang-client-0684066529',
19
- instance: 'iam-tunnel',
20
- zone: 'us-central1-f',
21
- });
22
- assert.ok(card);
23
- assert.equal(card.provider, 'google_cloud');
24
- assert.equal(card.kind, 'command_incomplete_wrong_family');
25
- assert.match(card.summary, /Distributed Cloud Edge/i);
26
- assert.match(card.summary, /Compute Engine/i);
27
- const commands = card.actions.map((row) => row.command).join('\n');
28
- assert.match(commands, /gcloud compute instances describe iam-tunnel/);
29
- assert.match(commands, /gcloud iam service-accounts list/);
30
- assert.match(commands, /gcloud iam service-accounts keys create/);
31
- assert.match(commands, /agentsam connections/);
32
- const rendered = renderRemediationCard(card);
33
- assert.match(rendered, /Command incomplete/);
34
- assert.match(rendered, /\[enter\]/);
35
- assert.equal(recommendedCommand(card), card.actions[0].command);
36
- });
37
-
38
- test('remediateProviderFailure matches edge-cloud argv fixture', () => {
39
- const card = remediateProviderFailure({
40
- argv: ['gcloud', 'edge-cloud', 'service-accounts', 'keys', 'create'],
41
- stderr: 'ERROR: argument OUTPUT_FILE (--service-account : --location): Must be specified.',
42
- });
43
- assert.ok(card);
44
- assert.equal(card.kind, 'command_incomplete_wrong_family');
45
- });
46
-
47
- test('permission denied remediation exposes reauth + IAM console actions', () => {
48
- const card = remediateGcloudPermissionDenied({
49
- operation: 'compute.instances.list',
50
- projectId: 'gen-lang-client-0684066529',
51
- connection: 'Google Cloud · InnerAnimalMedia',
52
- });
53
- assert.equal(card.kind, 'permission_denied');
54
- assert.match(renderRemediationCard(card), /Permission missing/);
55
- assert.match(renderRemediationCard(card), /Re-authorize Google Cloud/);
56
- assert.ok(card.actions.some((row) => /console\.cloud\.google\.com\/iam-admin/i.test(row.docs_url || '')));
57
- });
58
-
59
- test('github token shadow remediation prefers unset + keyring status', () => {
60
- const card = remediateGithubTokenShadow({
61
- envTokenPresent: true,
62
- keyringHealthy: true,
63
- scopes: ['repo', 'read:org'],
64
- });
65
- assert.ok(card);
66
- assert.equal(card.kind, 'credential_shadow');
67
- assert.match(recommendedCommand(card), /unset GITHUB_TOKEN/);
68
- });
69
-
70
- test('credentials remediate CLI prints edge-cloud card without secrets', async () => {
71
- const chunks = [];
72
- const code = await runCredentials(
73
- ['remediate', '--', 'gcloud', 'edge-cloud', 'service-accounts', 'keys', 'create'],
74
- { write: (s) => chunks.push(s) },
75
- );
76
- assert.equal(code, 0);
77
- const out = chunks.join('');
78
- assert.match(out, /Google Cloud/);
79
- assert.match(out, /gcloud iam service-accounts list/);
80
- assert.doesNotMatch(out, /gho_/);
81
- assert.doesNotMatch(out, /AIza/);
82
- });
83
-
84
- test('credentials audit --names-only never includes secret-looking values', async () => {
85
- const report = await collectCredentialsAudit({
86
- env: {
87
- HOME: process.env.HOME,
88
- AGENTSAM_API_KEY: 'aak_test_should_never_appear_in_output_fields_as_value',
89
- GITHUB_TOKEN: 'ghp_should_never_be_echoed',
90
- OPENAI_API_KEY: 'sk-should-never-be-echoed',
91
- },
92
- verify: false,
93
- drift: true,
94
- namesOnly: true,
95
- noNetwork: true,
96
- });
97
- const blob = JSON.stringify(report);
98
- assert.doesNotMatch(blob, /aak_test_should_never/);
99
- assert.doesNotMatch(blob, /ghp_should_never/);
100
- assert.doesNotMatch(blob, /sk-should-never/);
101
- assert.equal(report.shell_presence.GITHUB_TOKEN, true);
102
- assert.equal(report.shell_presence.AGENTSAM_API_KEY, true);
103
- assert.ok(report.drift.some((row) => row.code === 'github_env_token_present'));
104
- });
105
-
106
- test('cheat-sheet exposes System + Providers groups', () => {
107
- const groups = listCheatSheetGroups();
108
- assert.ok(groups.some((g) => g.id === 'system'));
109
- assert.ok(groups.some((g) => g.id === 'providers'));
110
- const text = renderCheatSheet('2.6.4');
111
- assert.match(text, /credentials audit/);
112
- assert.match(text, /cheat-sheet/);
113
- });
@@ -1,95 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { describe, it } from 'node:test';
3
- import { scanTextForSecrets } from '../src/lib/deploy/secret-scan.js';
4
- import { HEALTH_USER_AGENT, parseWranglerVersionId, probeDeployHealth, resolveHealthOrigin } from '../src/lib/deploy/health.js';
5
-
6
- describe('deploy secret scan', () => {
7
- it('allows fixture oauth values', () => {
8
- const findings = scanTextForSecrets(
9
- 'CLOUDFLARE_OAUTH_CLIENT_ID=sillynotreal\nCLOUDFLARE_OAUTH_CLIENT_SECRET=sillynotreal-secret\n',
10
- { filename: 'apps/local-studio/.env.cloudflare.example' },
11
- );
12
- assert.equal(findings.length, 0);
13
- });
14
-
15
- it('flags live openai-like keys', () => {
16
- const findings = scanTextForSecrets('OPENAI_API_KEY=sk-abcdefghijklmnopqrstuvwxyz012345');
17
- assert.equal(findings.some((f) => f.label === 'openai-like'), true);
18
- });
19
- });
20
-
21
- describe('deploy health', () => {
22
- it('parses wrangler version ids', () => {
23
- assert.equal(
24
- parseWranglerVersionId('Current Version ID: e9747d3a-4db6-4e02-b6fe-aa5a47f1588b'),
25
- 'e9747d3a-4db6-4e02-b6fe-aa5a47f1588b',
26
- );
27
- });
28
-
29
- it('reads hostname from wrangler.jsonc', () => {
30
- const origin = resolveHealthOrigin({
31
- wranglerConfigText: '{"routes":[{"pattern":"agentsam.inneranimalmedia.com","custom_domain":true}]}',
32
- });
33
- assert.equal(origin, 'https://agentsam.inneranimalmedia.com');
34
- });
35
-
36
- it('fails loud when wrangler routes pattern is missing', () => {
37
- assert.throws(
38
- () => resolveHealthOrigin({ wranglerConfigText: '{}' }),
39
- (err) => err?.code === 'health_origin_required',
40
- );
41
- });
42
-
43
- it('treats /health ok:true with connector unconfigured as healthy', async () => {
44
- const fetchImpl = async (url) => {
45
- if (String(url).endsWith('/health')) {
46
- return {
47
- status: 200,
48
- json: async () => ({ ok: true, connections: { cloudflare: { configured: false } } }),
49
- };
50
- }
51
- return { status: 200, json: async () => ({}) };
52
- };
53
- const health = await probeDeployHealth('https://agentsam.inneranimalmedia.com', { fetchImpl });
54
- assert.equal(health.ok, true);
55
- assert.equal(health.results['/health'].appOk, true);
56
- assert.equal(health.results['/health'].cloudflareConfigured, false);
57
- assert.equal(health.results['/privacy'].ok, true);
58
- assert.equal(health.results['/terms'].ok, true);
59
- });
60
-
61
- it('fails deploy health when /privacy or /terms are not HTTP 200', async () => {
62
- const fetchImpl = async (url) => {
63
- const path = String(url).replace(/^https?:\/\/[^/]+/, '');
64
- if (path === '/health') {
65
- return {
66
- status: 200,
67
- json: async () => ({ ok: true, connections: { cloudflare: { configured: true } } }),
68
- };
69
- }
70
- if (path === '/privacy' || path === '/terms') {
71
- return { status: 404, json: async () => ({}) };
72
- }
73
- return { status: 200, json: async () => ({}) };
74
- };
75
- const health = await probeDeployHealth('https://agentsam.inneranimalmedia.com', { fetchImpl });
76
- assert.equal(health.ok, false);
77
- assert.equal(health.results['/privacy'].ok, false);
78
- assert.equal(health.results['/terms'].ok, false);
79
- });
80
-
81
- it('sends a browser-safe User-Agent so WAF does not 403 the probe', async () => {
82
- const seen = [];
83
- const fetchImpl = async (url, init = {}) => {
84
- seen.push({ url, init });
85
- return {
86
- status: 200,
87
- json: async () => ({ ok: true, connections: { cloudflare: { configured: false } } }),
88
- };
89
- };
90
- await probeDeployHealth('https://agentsam.inneranimalmedia.com', { fetchImpl, paths: ['/health'] });
91
- assert.equal(seen.length, 1);
92
- assert.equal(seen[0].init.headers['User-Agent'], HEALTH_USER_AGENT);
93
- assert.match(seen[0].init.headers.Accept, /application\/json/);
94
- });
95
- });
@@ -1,91 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { test } from 'node:test';
3
- import fs from 'node:fs/promises';
4
- import os from 'node:os';
5
- import path from 'node:path';
6
- import { execFileSync, spawnSync } from 'node:child_process';
7
- import { fileURLToPath } from 'node:url';
8
- import {
9
- captureDeployReceipt,
10
- finalizeDeployReceipt,
11
- showLatestDeployReceipt,
12
- } from '../src/lib/deploy-receipt/index.js';
13
-
14
- const cli = fileURLToPath(new URL('../src/cli.js', import.meta.url));
15
-
16
- async function fixture(t) {
17
- const root = await fs.mkdtemp(path.join(os.tmpdir(), 'agentsam-deploy-receipt-'));
18
- t.after(() => fs.rm(root, { recursive: true, force: true }));
19
- execFileSync('git', ['init', '-q', root]);
20
- execFileSync('git', ['-C', root, 'config', 'user.email', 'receipt-test@example.invalid']);
21
- execFileSync('git', ['-C', root, 'config', 'user.name', 'Receipt Test']);
22
- await fs.mkdir(path.join(root, 'src'), { recursive: true });
23
- await fs.writeFile(path.join(root, 'src/app.js'), 'export const value = 1;\n');
24
- execFileSync('git', ['-C', root, 'add', '.']);
25
- execFileSync('git', ['-C', root, 'commit', '-qm', 'fixture']);
26
- return root;
27
- }
28
-
29
- function run(args, cwd) {
30
- return spawnSync(process.execPath, [cli, 'deploy-receipt', ...args], { cwd, encoding: 'utf8', timeout: 15000 });
31
- }
32
-
33
- test('successful finalize advances baseline while failure preserves the last trusted tree', async (t) => {
34
- const root = await fixture(t);
35
- const first = await captureDeployReceipt({ root, project: 'fixture' });
36
- assert.equal(first.receipt.has_baseline, false);
37
- assert.equal(first.receipt.baseline_source, 'none');
38
- assert.equal(first.receipt.working_tree_dirty, false);
39
- assert.ok(!first.snapshot.entries.some((entry) => entry.path.startsWith('.agentsam/deploy-merkle')));
40
-
41
- const promoted = await finalizeDeployReceipt({ root, status: 'success', deploymentId: 'dep_1' });
42
- assert.equal(promoted.receipt.status, 'success');
43
- assert.equal(promoted.receipt.deployment_id, 'dep_1');
44
- assert.equal((await showLatestDeployReceipt({ root })).root_hash, first.receipt.root_hash);
45
-
46
- const clean = await captureDeployReceipt({ root, project: 'fixture' });
47
- assert.equal(clean.receipt.has_baseline, true);
48
- assert.equal(clean.receipt.baseline_source, 'local');
49
- assert.deepEqual(clean.receipt.diff_stats, { unchanged: 1, modified: 0, added: 0, removed: 0 });
50
- assert.deepEqual(clean.receipt.changed_files, []);
51
- assert.equal(clean.receipt.root_hash, first.receipt.root_hash);
52
- assert.equal(clean.receipt.working_tree_dirty, false, 'runtime receipt state must not make the source tree dirty');
53
-
54
- await fs.writeFile(path.join(root, 'src/app.js'), 'export const value = 2;\n');
55
- const changed = await captureDeployReceipt({ root, project: 'fixture' });
56
- assert.equal(changed.receipt.working_tree_dirty, true);
57
- assert.deepEqual(changed.receipt.diff_stats, { unchanged: 0, modified: 1, added: 0, removed: 0 });
58
- assert.deepEqual(changed.receipt.changed_files, ['src/app.js']);
59
- assert.notEqual(changed.receipt.root_hash, first.receipt.root_hash);
60
-
61
- await finalizeDeployReceipt({ root, status: 'failed', deploymentId: 'dep_2' });
62
- assert.equal((await showLatestDeployReceipt({ root })).root_hash, first.receipt.root_hash, 'failed run must not advance baseline');
63
-
64
- const retry = await captureDeployReceipt({ root, project: 'fixture' });
65
- assert.deepEqual(retry.receipt.changed_files, ['src/app.js']);
66
- await finalizeDeployReceipt({ root, status: 'success', deploymentId: 'dep_3' });
67
- const settled = await captureDeployReceipt({ root, project: 'fixture' });
68
- assert.deepEqual(settled.receipt.diff_stats, { unchanged: 1, modified: 0, added: 0, removed: 0 });
69
- assert.deepEqual(settled.receipt.changed_files, []);
70
- });
71
-
72
- test('deploy-receipt CLI emits machine-readable capture/finalize/show receipts', async (t) => {
73
- const root = await fixture(t);
74
- const capture = run(['capture', '.', '--project', 'cli-fixture', '--json'], root);
75
- assert.equal(capture.status, 0, capture.stderr);
76
- const captured = JSON.parse(capture.stdout);
77
- assert.equal(captured.format, 'agentsam-deploy-merkle');
78
- assert.equal(captured.project, 'cli-fixture');
79
- assert.equal(captured.status, 'captured');
80
-
81
- const success = run(['success', '.', '--deployment-id', 'dep_cli', '--worker-version', 'worker_v1', '--json'], root);
82
- assert.equal(success.status, 0, success.stderr);
83
- const finalized = JSON.parse(success.stdout);
84
- assert.equal(finalized.status, 'success');
85
- assert.equal(finalized.deployment_id, 'dep_cli');
86
- assert.equal(finalized.worker_version_id, 'worker_v1');
87
-
88
- const show = run(['show', '.', '--json'], root);
89
- assert.equal(show.status, 0, show.stderr);
90
- assert.equal(JSON.parse(show.stdout).root_hash, finalized.root_hash);
91
- });
@@ -1,93 +0,0 @@
1
- // Pure unit tests — no Docker daemon required, safe for CI/smoke.
2
- // Integration coverage (actual docker build/run) is manual: see docs/DOCKERIZE.md.
3
- import { test } from 'node:test';
4
- import assert from 'node:assert/strict';
5
- import fs from 'node:fs';
6
- import os from 'node:os';
7
- import path from 'node:path';
8
- import {
9
- DOCKER_APP_TYPES,
10
- DOCKER_APP_TYPE_LABELS,
11
- slugifyForDocker,
12
- generateDockerFileSet,
13
- computeFileSetHash,
14
- buildDockerDeployPlan,
15
- writeDockerFileSet,
16
- readManifest,
17
- } from '../src/lib/dockerize.js';
18
-
19
- test('slugifyForDocker normalizes to a safe image/container name', () => {
20
- assert.equal(slugifyForDocker('My Cool App!'), 'my-cool-app');
21
- assert.equal(slugifyForDocker(' --leading-- '), 'leading');
22
- assert.equal(slugifyForDocker(''), 'app');
23
- });
24
-
25
- test('every declared app type has a label and generates a valid file set', () => {
26
- for (const type of DOCKER_APP_TYPES) {
27
- assert.ok(DOCKER_APP_TYPE_LABELS[type]?.label, `missing label for ${type}`);
28
- const files = generateDockerFileSet(type, { appSlug: 'test-app', port: 9999 });
29
- assert.match(files.dockerfile, /^FROM /m, `${type} Dockerfile must start with FROM`);
30
- assert.ok(files.dockerignore.includes('node_modules'), `${type} .dockerignore should ignore node_modules`);
31
- assert.ok(files.compose.includes('services:'), `${type} compose file must define services`);
32
- }
33
- });
34
-
35
- test('unknown app type throws a clear error instead of generating garbage', () => {
36
- assert.throws(() => generateDockerFileSet('not_a_real_type', { appSlug: 'x' }), /Unknown Docker app type/);
37
- });
38
-
39
- test('buildDockerDeployPlan picks correct container port for static/vite (80) vs service types (host port)', () => {
40
- const staticPlan = buildDockerDeployPlan('static', { appSlug: 'x', port: 8080 });
41
- assert.equal(staticPlan.containerPort, 80);
42
- assert.equal(staticPlan.hostPort, 8080);
43
-
44
- const nodePlan = buildDockerDeployPlan('node_service', { appSlug: 'x', port: 4000 });
45
- assert.equal(nodePlan.containerPort, 4000);
46
- assert.equal(nodePlan.hostPort, 4000);
47
- });
48
-
49
- test('buildDockerDeployPlan falls back to sensible default ports when none given', () => {
50
- const plan = buildDockerDeployPlan('wrangler_dev', { appSlug: 'x' });
51
- assert.equal(plan.hostPort, 8787);
52
- assert.equal(plan.imageTag, 'x:local'.replace('local', plan.hash));
53
- });
54
-
55
- test('computeFileSetHash is deterministic: identical content -> identical hash', () => {
56
- const a = generateDockerFileSet('node_service', { appSlug: 'same', port: 3000 });
57
- const b = generateDockerFileSet('node_service', { appSlug: 'same', port: 3000 });
58
- assert.equal(computeFileSetHash(a), computeFileSetHash(b));
59
- });
60
-
61
- test('computeFileSetHash changes when content changes (different port -> different hash)', () => {
62
- const a = generateDockerFileSet('node_service', { appSlug: 'same', port: 3000 });
63
- const b = generateDockerFileSet('node_service', { appSlug: 'same', port: 4000 });
64
- assert.notEqual(computeFileSetHash(a), computeFileSetHash(b));
65
- });
66
-
67
- test('buildDockerDeployPlan image tag and hashtag are hash-derived and match the slug', () => {
68
- const plan = buildDockerDeployPlan('static', { appSlug: 'My App', port: 8080 });
69
- assert.equal(plan.slug, 'my-app');
70
- assert.equal(plan.imageTag, `my-app:${plan.hash}`);
71
- assert.equal(plan.latestTag, 'my-app:latest');
72
- assert.equal(plan.hashtag, `#docker-my-app-${plan.hash}`);
73
- });
74
-
75
- test('writeDockerFileSet writes into .agentsam/docker/<hash>/ and .dockerignore at root, and the manifest round-trips', () => {
76
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-dockerize-test-'));
77
- try {
78
- const files = generateDockerFileSet('static', { appSlug: 'tmp-app', port: 9090 });
79
- const hash = computeFileSetHash(files);
80
- const written = writeDockerFileSet(tmp, files, hash);
81
-
82
- assert.equal(written.dockerfilePath, path.join(tmp, '.agentsam', 'docker', hash, 'Dockerfile'));
83
- assert.ok(fs.existsSync(written.dockerfilePath));
84
- assert.ok(fs.existsSync(written.composePath));
85
- assert.ok(fs.existsSync(path.join(tmp, '.dockerignore')));
86
-
87
- // manifest is populated by dockerize()/writeManifestEntry, not writeDockerFileSet itself —
88
- // confirm readManifest returns {} when nothing has written to it yet
89
- assert.deepEqual(readManifest(tmp), {});
90
- } finally {
91
- fs.rmSync(tmp, { recursive: true, force: true });
92
- }
93
- });