@inneranimalmedia/agentsam-sdk 2.6.4 → 2.6.5

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 (418) hide show
  1. package/AGENTSAM.md +11 -0
  2. package/README.md +15 -9
  3. package/apps/client-cms-editor/AGENTS.md +12 -0
  4. package/apps/client-cms-editor/IMPORT_PROVENANCE.json +8 -0
  5. package/apps/client-cms-editor/README.md +72 -0
  6. package/apps/client-cms-editor/agentsam.app.json +57 -0
  7. package/apps/client-cms-editor/backend/README.md +5 -0
  8. package/apps/client-cms-editor/backend/package.json +22 -0
  9. package/apps/client-cms-editor/backend/src/api/client.ts +200 -0
  10. package/apps/client-cms-editor/backend/src/demo-bootstrap.ts +145 -0
  11. package/apps/client-cms-editor/backend/src/index.ts +6 -0
  12. package/apps/client-cms-editor/backend/src/model.ts +115 -0
  13. package/apps/client-cms-editor/backend/src/preview/bridge.ts +79 -0
  14. package/apps/client-cms-editor/backend/src/preview/urls.ts +55 -0
  15. package/apps/client-cms-editor/backend/src/routing/cms-route.js +293 -0
  16. package/apps/client-cms-editor/backend/src/routing/index.js +10 -0
  17. package/apps/client-cms-editor/backend/src/storefront.ts +52 -0
  18. package/apps/client-cms-editor/backend/tsconfig.json +15 -0
  19. package/apps/client-cms-editor/backend/worker/index.js +34 -0
  20. package/apps/client-cms-editor/backend/wrangler.jsonc +11 -0
  21. package/apps/client-cms-editor/bin/agentsam-cms.mjs +229 -0
  22. package/apps/client-cms-editor/frontend/index.html +12 -0
  23. package/apps/client-cms-editor/frontend/package.json +33 -0
  24. package/apps/client-cms-editor/frontend/src/AgentSamDrawer.tsx +188 -0
  25. package/apps/client-cms-editor/frontend/src/CmsAgentSurface.tsx +191 -0
  26. package/apps/client-cms-editor/frontend/src/CmsEditor.tsx +1345 -0
  27. package/apps/client-cms-editor/frontend/src/env.d.ts +14 -0
  28. package/apps/client-cms-editor/frontend/src/index.ts +8 -0
  29. package/apps/client-cms-editor/frontend/src/lib/content-safety.ts +63 -0
  30. package/apps/client-cms-editor/frontend/src/main.tsx +17 -0
  31. package/apps/client-cms-editor/frontend/src/mount.tsx +51 -0
  32. package/apps/client-cms-editor/frontend/src/styles/agentsam-drawer.css +214 -0
  33. package/apps/client-cms-editor/frontend/src/styles/cmsShell.css +1739 -0
  34. package/apps/client-cms-editor/frontend/src/styles/studio.css +106 -0
  35. package/apps/client-cms-editor/frontend/src/surfaces/CmsDashboard.tsx +313 -0
  36. package/apps/client-cms-editor/frontend/src/surfaces/CmsGuidedChatHero.tsx +121 -0
  37. package/apps/client-cms-editor/frontend/src/surfaces/CmsHubPage.tsx +132 -0
  38. package/apps/client-cms-editor/frontend/src/surfaces/CmsSiteSwitcher.tsx +176 -0
  39. package/apps/client-cms-editor/frontend/src/surfaces/index.ts +5 -0
  40. package/apps/client-cms-editor/frontend/src/surfaces/types.ts +48 -0
  41. package/apps/client-cms-editor/frontend/tsconfig.json +25 -0
  42. package/apps/client-cms-editor/frontend/vite.config.ts +13 -0
  43. package/apps/client-cms-editor/package-lock.json +3832 -0
  44. package/apps/client-cms-editor/package.json +23 -0
  45. package/apps/client-cms-editor/reference/DONOR_README.md +52 -0
  46. package/apps/client-cms-editor/reference/DONOR_package-lock.json +1253 -0
  47. package/apps/client-cms-editor/reference/DONOR_tsconfig.json +15 -0
  48. package/apps/client-cms-editor/shared/cms/package.json +21 -0
  49. package/apps/client-cms-editor/shared/cms/src/agent-context.ts +35 -0
  50. package/apps/client-cms-editor/shared/cms/src/cloudflare-bindings.ts +51 -0
  51. package/apps/client-cms-editor/shared/cms/src/editor-types.ts +58 -0
  52. package/apps/client-cms-editor/shared/cms/src/index.ts +5 -0
  53. package/apps/client-cms-editor/shared/cms/src/publication.ts +24 -0
  54. package/apps/client-cms-editor/shared/cms/src/routes.ts +11 -0
  55. package/apps/client-cms-editor/shared/cms/tsconfig.json +12 -0
  56. package/bin/agentsam-rapid-rust +36 -0
  57. package/docs/AUTH_IDENTITY_CONTRACT.md +2 -0
  58. package/docs/MACHINE_INTELLIGENCE.md +120 -0
  59. package/docs/WINDOWS_RUNTIME_INSTALL.md +30 -0
  60. package/docs/architecture/AGENTSAM_APPLICATION_CONTRACT.md +188 -0
  61. package/docs/architecture/AGENTSAM_RUNTIME_PROTOCOL.md +151 -0
  62. package/docs/architecture/LOCAL_STUDIO_CLOUDFLARE_OAUTH.md +12 -4
  63. package/docs/architecture/R2_SLUG_NAMESPACE.md +20 -0
  64. package/docs/architecture/WORK_PRODUCT_EXTRACTION.md +97 -0
  65. package/docs/brand/AGENT_SAM_ICON_GRAMMAR.md +70 -0
  66. package/docs/content-studio/HOST_MOUNT_CHECKLIST.md +71 -0
  67. package/docs/content-studio/REVISION_GATE_2026-09-27.md +501 -0
  68. package/docs/contracts/capability-install-planner.md +67 -0
  69. package/docs/contracts/environment-vocabulary.md +195 -0
  70. package/docs/contracts/google-cloud-oauth-setup.md +199 -0
  71. package/docs/local-studio/WORKMODE_DONOR_NOTES.md +1 -1
  72. package/docs/plans/LOCAL-STUDIO-GRADUATION-2026-09-26.md +290 -0
  73. package/docs/plans/LOCAL-STUDIO-REAL-APP-CONTRACT-ADDENDUM-2026-09-27.md +394 -0
  74. package/docs/plans/MACHINE-FIRST-APPS-TUI-2026-09-24.md +227 -0
  75. package/docs/plans/TOMORROW-EXTENSIVE-TODO-2026-09-26.md +221 -0
  76. package/docs/plans/TOMORROW-MACHINE-FIRST-BRIEFING.md +145 -0
  77. package/docs/plans/UNIVERSAL-INGEST-MACHINE-FIRST-2026-09-24.md +215 -0
  78. package/docs/plugins.md +2 -2
  79. package/docs/product/AGENTSAM_PRODUCT_EXPERIENCE_STANDARD.md +280 -0
  80. package/docs/product/LOCAL_STUDIO_DESKTOP_EXPERIENCE.md +139 -0
  81. package/docs/product/README.md +8 -0
  82. package/docs/rapid-rust-wasm.md +35 -0
  83. package/migrations/d1/0016_cloudflare_oauth_user_oauth_tokens_ssot.sql +14 -0
  84. package/package.json +73 -14
  85. package/packages/agentsam-brand/README.md +106 -11
  86. package/packages/agentsam-brand/bin/agentsam-brand.mjs +9 -0
  87. package/packages/agentsam-brand/package.json +39 -7
  88. package/packages/agentsam-brand/src/adapters/cloudflare-images.js +453 -0
  89. package/packages/agentsam-brand/src/adapters/cloudflare-r2.js +74 -0
  90. package/packages/agentsam-brand/src/adapters/d1-registry.js +26 -0
  91. package/packages/agentsam-brand/src/adapters/filesystem.js +59 -0
  92. package/packages/agentsam-brand/src/adapters/memory.js +31 -0
  93. package/packages/agentsam-brand/src/cli/controller.js +503 -0
  94. package/packages/agentsam-brand/src/cli/wizard.js +526 -0
  95. package/packages/agentsam-brand/src/core/capabilities.js +201 -0
  96. package/packages/agentsam-brand/src/core/derivatives.js +211 -0
  97. package/packages/agentsam-brand/src/core/errors.js +12 -0
  98. package/packages/agentsam-brand/src/core/ingest.js +195 -0
  99. package/packages/agentsam-brand/src/core/inspect.js +109 -0
  100. package/packages/agentsam-brand/src/core/keys.js +83 -0
  101. package/packages/agentsam-brand/src/core/naming.js +142 -0
  102. package/packages/agentsam-brand/src/core/optimize.js +316 -0
  103. package/packages/agentsam-brand/src/core/pack.js +305 -0
  104. package/packages/agentsam-brand/src/core/plan.js +276 -0
  105. package/packages/agentsam-brand/src/core/preview.js +159 -0
  106. package/packages/agentsam-brand/src/core/processors/cloudflare.js +68 -0
  107. package/packages/agentsam-brand/src/core/processors/contract.js +203 -0
  108. package/packages/agentsam-brand/src/core/processors/index.js +148 -0
  109. package/packages/agentsam-brand/src/core/processors/native.js +82 -0
  110. package/packages/agentsam-brand/src/core/processors/sharp.js +113 -0
  111. package/packages/agentsam-brand/src/core/processors/squoosh-binary.js +195 -0
  112. package/packages/agentsam-brand/src/core/promote.js +288 -0
  113. package/packages/agentsam-brand/src/core/receipts.js +155 -0
  114. package/packages/agentsam-brand/src/core/v2/compile.js +280 -0
  115. package/packages/agentsam-brand/src/core/v2/derivatives-semantic.js +207 -0
  116. package/packages/agentsam-brand/src/core/v2/ingest-graph.js +387 -0
  117. package/packages/agentsam-brand/src/core/v2/roles.js +180 -0
  118. package/packages/agentsam-brand/src/core/v2/schema.js +209 -0
  119. package/packages/agentsam-brand/src/core/v2/templates.js +114 -0
  120. package/packages/agentsam-brand/src/index.js +193 -36
  121. package/packages/agentsam-brand/src/presets/app-icon.json +16 -0
  122. package/packages/agentsam-brand/src/presets/favicon.json +17 -0
  123. package/packages/agentsam-brand/src/presets/hero.json +12 -0
  124. package/packages/agentsam-brand/src/presets/index.js +40 -0
  125. package/packages/agentsam-brand/src/presets/logo.json +12 -0
  126. package/packages/agentsam-brand/src/presets/model.json +8 -0
  127. package/packages/agentsam-brand/src/presets/og-image.json +10 -0
  128. package/packages/agentsam-brand/src/presets/product-shot.json +11 -0
  129. package/packages/agentsam-brand/src/presets/social-image.json +10 -0
  130. package/packages/agentsam-brand/src/presets/web-image.json +10 -0
  131. package/packages/agentsam-contracts/src/artifacts.ts +41 -0
  132. package/packages/agentsam-knowledge/src/autorag/index.js +1 -1
  133. package/packages/agentsam-knowledge/src/providers/index.js +6 -4
  134. package/packages/agentsam-knowledge/src/providers/ollama-endpoint.js +31 -0
  135. package/packages/agentsam-knowledge/test/autorag.test.mjs +22 -1
  136. package/packages/agentsam-nav/package.json +2 -2
  137. package/packages/agentsam-rapid-rust/Cargo.lock +344 -0
  138. package/packages/agentsam-rapid-rust/Cargo.toml +18 -0
  139. package/packages/agentsam-rapid-rust/README.md +5 -0
  140. package/packages/agentsam-rapid-rust/src/main.rs +718 -0
  141. package/packages/agentsam-rapid-rust/templates/api/Cargo.toml.tmpl +18 -0
  142. package/packages/agentsam-rapid-rust/templates/api/src/lib.rs.tmpl +17 -0
  143. package/packages/agentsam-rapid-rust/templates/common/README.md.tmpl +22 -0
  144. package/packages/agentsam-rapid-rust/templates/common/package.json.tmpl +12 -0
  145. package/packages/agentsam-rapid-rust/templates/common/wrangler.toml.tmpl +6 -0
  146. package/packages/agentsam-rapid-rust/templates/crypto-auth/Cargo.toml.tmpl +21 -0
  147. package/packages/agentsam-rapid-rust/templates/crypto-auth/src/lib.rs.tmpl +32 -0
  148. package/packages/agentsam-rapid-rust/templates/data-processor/Cargo.toml.tmpl +18 -0
  149. package/packages/agentsam-rapid-rust/templates/data-processor/src/lib.rs.tmpl +41 -0
  150. package/packages/agentsam-rapid-rust/templates/shared-core/Cargo.toml.tmpl +9 -0
  151. package/packages/agentsam-rapid-rust/templates/shared-core/core/Cargo.toml.tmpl +7 -0
  152. package/packages/agentsam-rapid-rust/templates/shared-core/core/src/lib.rs.tmpl +23 -0
  153. package/packages/agentsam-rapid-rust/templates/shared-core/worker/Cargo.toml.tmpl +12 -0
  154. package/packages/agentsam-rapid-rust/templates/shared-core/worker/src/lib.rs.tmpl +16 -0
  155. package/packages/agentsam-rapid-rust/templates/shared-core/wrangler.toml.tmpl +6 -0
  156. package/packages/agentsam-repository/src/index.js +1 -0
  157. package/packages/agentsam-repository/src/merkle/semantic.js +3 -10
  158. package/packages/agentsam-repository/src/source-types.js +85 -0
  159. package/packages/agentsam-vault/README.md +96 -0
  160. package/packages/agentsam-vault/package.json +32 -0
  161. package/packages/agentsam-vault/src/contracts/credential.d.ts +78 -0
  162. package/packages/agentsam-vault/src/contracts/credential.js +132 -0
  163. package/packages/agentsam-vault/src/crypto/aes-gcm.d.ts +30 -0
  164. package/packages/agentsam-vault/src/crypto/aes-gcm.js +107 -0
  165. package/packages/agentsam-vault/src/crypto/oauth-envelope.js +150 -0
  166. package/packages/agentsam-vault/src/index.d.ts +9 -0
  167. package/packages/agentsam-vault/src/index.js +27 -0
  168. package/packages/agentsam-vault/src/providers/registry.js +90 -0
  169. package/packages/agentsam-vault/src/runtime/resolver.js +77 -0
  170. package/packages/agentsam-workbench/README.md +24 -0
  171. package/packages/agentsam-workbench/package.json +38 -0
  172. package/packages/agentsam-workbench/src/agent/AgentPanel.tsx +41 -0
  173. package/packages/agentsam-workbench/src/agent/ArtifactCard.tsx +9 -0
  174. package/packages/agentsam-workbench/src/agent/Composer.tsx +102 -0
  175. package/packages/agentsam-workbench/src/agent/ConnectedAgentPanel.tsx +194 -0
  176. package/packages/agentsam-workbench/src/agent/ContextualComposer.tsx +788 -0
  177. package/packages/agentsam-workbench/src/agent/GoalStatusStrip.tsx +38 -0
  178. package/packages/agentsam-workbench/src/agent/MiniAgentSam.tsx +86 -0
  179. package/packages/agentsam-workbench/src/agent/ModelSelect.tsx +19 -0
  180. package/packages/agentsam-workbench/src/agent/Thread.tsx +56 -0
  181. package/packages/agentsam-workbench/src/agent/ToolReceipt.tsx +11 -0
  182. package/packages/agentsam-workbench/src/agent/contextual-composer.css +611 -0
  183. package/packages/agentsam-workbench/src/agent/index.ts +10 -0
  184. package/packages/agentsam-workbench/src/agent/mini-agentsam.css +12 -0
  185. package/packages/agentsam-workbench/src/browser/BrowserFrame.tsx +37 -0
  186. package/packages/agentsam-workbench/src/browser/index.ts +1 -0
  187. package/packages/agentsam-workbench/src/index.ts +6 -0
  188. package/packages/agentsam-workbench/src/project-control.css +160 -0
  189. package/packages/agentsam-workbench/src/projects/ProjectsSurface.tsx +131 -0
  190. package/packages/agentsam-workbench/src/projects/index.ts +1 -0
  191. package/packages/agentsam-workbench/src/shell/Breadcrumb.tsx +35 -0
  192. package/packages/agentsam-workbench/src/shell/ResizableStage.tsx +99 -0
  193. package/packages/agentsam-workbench/src/shell/SidePanel.tsx +5 -0
  194. package/packages/agentsam-workbench/src/shell/index.ts +4 -0
  195. package/packages/agentsam-workbench/src/shell/surfaceRegistry.ts +29 -0
  196. package/packages/agentsam-workbench/src/terminal/TerminalSurface.tsx +5 -0
  197. package/packages/agentsam-workbench/src/terminal/index.ts +1 -0
  198. package/packages/agentsam-workbench/src/timeline/GanttBoard.tsx +258 -0
  199. package/packages/agentsam-workbench/src/timeline/index.ts +1 -0
  200. package/packages/agentsam-workbench/tsconfig.json +13 -0
  201. package/packages/connectors/cloudflare/OAUTH_STORAGE_AUDIT.md +53 -0
  202. package/packages/connectors/cloudflare/package.json +3 -1
  203. package/packages/connectors/cloudflare/src/api-client.js +279 -0
  204. package/packages/connectors/cloudflare/src/capabilities.js +585 -0
  205. package/packages/connectors/cloudflare/src/credential.js +318 -0
  206. package/packages/connectors/cloudflare/src/families/brand-protection.js +123 -0
  207. package/packages/connectors/cloudflare/src/families/keyless.js +114 -0
  208. package/packages/connectors/cloudflare/src/families/pages.js +151 -0
  209. package/packages/connectors/cloudflare/src/families/scanner.js +123 -0
  210. package/packages/connectors/cloudflare/src/families/snippets.js +207 -0
  211. package/packages/connectors/cloudflare/src/families/tag-gateway.js +46 -0
  212. package/packages/connectors/cloudflare/src/families/tags.js +124 -0
  213. package/packages/connectors/cloudflare/src/families/token-validation.js +95 -0
  214. package/packages/connectors/cloudflare/src/families/workflows.js +156 -0
  215. package/packages/connectors/cloudflare/src/index.js +102 -265
  216. package/packages/connectors/cloudflare/src/oauth-persist.js +336 -0
  217. package/packages/connectors/cloudflare/src/owner.js +42 -12
  218. package/packages/connectors/cloudflare/src/permission-options.js +1064 -0
  219. package/packages/connectors/cloudflare/src/routes.js +309 -144
  220. package/packages/connectors/cloudflare/src/scope-catalog.js +345 -0
  221. package/packages/connectors/cloudflare/src/vault.js +2 -2
  222. package/packages/connectors/cloudflare/tests/capabilities.test.mjs +276 -0
  223. package/packages/connectors/cloudflare/tests/connector.test.mjs +26 -30
  224. package/packages/connectors/cloudflare/tests/oauth-persist.test.mjs +125 -0
  225. package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +22 -2
  226. package/packages/identity/.agentsam/features/oauth-login-portal/resources.json +10 -4
  227. package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +20 -0
  228. package/packages/identity/README.md +1 -1
  229. package/packages/identity/backend/README.md +1 -1
  230. package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +3 -2
  231. package/packages/identity/migrations/sqlite/004_identity_oauth_consent_catalog.sql +48 -0
  232. package/packages/identity/package.json +4 -3
  233. package/packages/identity/scripts/local-identity-bridge.mjs +184 -0
  234. package/packages/identity/scripts/preview-auth-portal.mjs +7 -7
  235. package/packages/identity/src/adapters/cloudflare-d1/index.js +31 -2
  236. package/packages/identity/src/adapters/sqlite/index.js +4 -1
  237. package/packages/identity/src/contracts/auth-config.js +28 -8
  238. package/packages/identity/src/contracts/external-identity.js +1 -1
  239. package/packages/identity/src/contracts/provider.js +1 -0
  240. package/packages/identity/src/frontend/auth-portal/README.md +2 -1
  241. package/packages/identity/src/frontend/auth-portal/pages/login.html +6 -5
  242. package/packages/identity/src/frontend/auth-portal/pages/reset.html +6 -5
  243. package/packages/identity/src/frontend/auth-portal/pages/signup.html +6 -5
  244. package/packages/identity/src/frontend/auth-portal/shared/company-branding.js +1 -1
  245. package/packages/identity/src/frontend/auth-portal/shared/desktop-identity-bridge.js +207 -0
  246. package/packages/identity/src/index.js +16 -0
  247. package/packages/identity/src/migrations/0001_identity_core.sql +19 -2
  248. package/packages/identity/src/migrations/0003_accounts_ssot.sql +20 -0
  249. package/packages/identity/src/migrations/README.md +5 -2
  250. package/packages/identity/src/oauth/README.md +10 -5
  251. package/packages/identity/src/oauth/consent-view.js +388 -0
  252. package/packages/identity/src/oauth/credentials.js +31 -11
  253. package/packages/identity/src/oauth/google-desktop-exchange.js +173 -0
  254. package/packages/identity/src/providers/cloudflare/oauth.js +3 -4
  255. package/packages/identity/src/providers/cloudflare/profile.js +49 -14
  256. package/packages/identity/src/providers/iam/index.js +27 -5
  257. package/packages/identity/src/server/identity-service.js +31 -2
  258. package/packages/identity/src/server/worker-router.js +111 -81
  259. package/packages/identity/tests/auth-config.test.mjs +3 -2
  260. package/packages/identity/tests/identity-init-scaffold.test.mjs +3 -0
  261. package/packages/identity/tests/identity-service.test.mjs +16 -0
  262. package/packages/identity/tests/native-session-worker.test.mjs +64 -0
  263. package/packages/identity/tests/oauth-consent-view.test.mjs +106 -0
  264. package/packages/identity/tests/oauth-credentials.test.mjs +17 -4
  265. package/packages/identity/tests/oauth-presets-accounts-ssot.test.mjs +58 -0
  266. package/packages/identity/tests/preview-auth-portal.test.mjs +3 -3
  267. package/packages/runtime-protocol/src/index.js +154 -0
  268. package/packages/theme-scenes/README.md +164 -0
  269. package/packages/theme-scenes/package.json +53 -0
  270. package/packages/theme-scenes/src/blocks/index.js +30 -0
  271. package/packages/theme-scenes/src/contracts/index.js +286 -0
  272. package/packages/theme-scenes/src/demos/index.js +65 -0
  273. package/packages/theme-scenes/src/index.js +75 -0
  274. package/packages/theme-scenes/src/presets/index.js +263 -0
  275. package/packages/theme-scenes/src/runtime/compile.js +119 -0
  276. package/packages/theme-scenes/src/scenes/index.js +146 -0
  277. package/packages/theme-scenes/src/shell/index.js +47 -0
  278. package/packages/theme-scenes/src/themes/index.js +129 -0
  279. package/packages/theme-scenes/src/visuals/index.js +41 -0
  280. package/packages/theme-scenes/test/theme-scenes.test.mjs +179 -0
  281. package/packages/work-graph/src/index.js +34 -1
  282. package/packages/work-graph/src/renderers/gantt-model.js +24 -0
  283. package/packages/work-graph/test/work-graph.test.mjs +27 -7
  284. package/protocol/README.md +15 -7
  285. package/protocol/apps/agentsam.app.v1.schema.json +89 -7
  286. package/protocol/apps/agentsam.host-install.v1.schema.json +36 -0
  287. package/protocol/assets/README.md +9 -0
  288. package/protocol/assets/asset-record.v1.schema.json +64 -0
  289. package/protocol/runtime/agentsam.runtime.v1.schema.json +75 -0
  290. package/protocol/scoring/README.md +10 -0
  291. package/protocol/scoring/scorecard.v1.schema.json +65 -0
  292. package/protocol/ui/icon-registry.mjs +1 -0
  293. package/registry/cms-rust-machine-20260928/installable-products.json +31 -0
  294. package/registry/cms-rust-machine-20260928/manifest.json +22 -0
  295. package/registry/cms-rust-machine-20260928/migration.sql +237 -0
  296. package/registry/cms-rust-machine-20260928/receipt.json +21 -0
  297. package/registry/cms-rust-machine-20260928/receipt.sql +27 -0
  298. package/scripts/build_rapid_rust_cli.py +83 -0
  299. package/src/attachments/index.js +6 -0
  300. package/src/attachments/intake.js +235 -0
  301. package/src/capabilities/repository-snapshot.js +19 -2
  302. package/src/cli/command-catalog.js +8 -0
  303. package/src/cli.js +182 -26
  304. package/src/commands/account-auth.js +94 -8
  305. package/src/commands/app.js +307 -3
  306. package/src/commands/billing.js +127 -0
  307. package/src/commands/brand.js +74 -60
  308. package/src/commands/cheat-sheet.js +154 -0
  309. package/src/commands/cloudflare-login.js +222 -0
  310. package/src/commands/cloudflare.js +453 -23
  311. package/src/commands/codebaseindex.js +6 -1
  312. package/src/commands/compute.js +4 -0
  313. package/src/commands/connections.js +11 -5
  314. package/src/commands/credentials.js +116 -0
  315. package/src/commands/gcloud-auth.js +427 -0
  316. package/src/commands/google-cloud.js +375 -0
  317. package/src/commands/identity-init.js +2 -2
  318. package/src/commands/mcp.js +226 -33
  319. package/src/commands/ollama.js +3 -2
  320. package/src/commands/plugins.js +7 -2
  321. package/src/commands/product.js +36 -0
  322. package/src/commands/runtime-status.js +28 -9
  323. package/src/commands/runtime.js +313 -0
  324. package/src/commands/rust.js +24 -0
  325. package/src/commands/setup.js +279 -0
  326. package/src/commands/shell.js +16 -3
  327. package/src/commands/start-local.js +2 -2
  328. package/src/commands/terminal.js +171 -0
  329. package/src/commands/update.js +140 -0
  330. package/src/commands/whoami.js +329 -65
  331. package/src/features/resolve.js +1 -0
  332. package/src/index.js +4 -0
  333. package/src/indexing/ingest/inventory.js +254 -19
  334. package/src/indexing/ingest/materials.js +3 -9
  335. package/src/indexing/ingest/style-evidence.js +181 -0
  336. package/src/knowledge/source.js +3 -3
  337. package/src/lib/account-session.js +11 -4
  338. package/src/lib/app-authority.js +172 -0
  339. package/src/lib/auth.js +94 -21
  340. package/src/lib/core-client.js +5 -6
  341. package/src/lib/credentials-audit.js +451 -0
  342. package/src/lib/deploy/health.js +24 -6
  343. package/src/lib/detect-context.js +7 -1
  344. package/src/lib/google-cloud-connection.js +270 -0
  345. package/src/lib/google-cloud-inventory.js +385 -0
  346. package/src/lib/google-desktop-oauth.js +372 -0
  347. package/src/lib/google-studio-brokered-oauth.js +158 -0
  348. package/src/lib/identity-scaffold.js +24 -6
  349. package/src/lib/init-options.js +192 -0
  350. package/src/lib/local-scaffold.js +48 -16
  351. package/src/lib/provider-command-remediation.js +308 -0
  352. package/src/lib/provider-credentials.js +8 -0
  353. package/src/lib/scaffold/index.js +8 -2
  354. package/src/lib/scaffold/mcp-server.js +43 -0
  355. package/src/lib/scaffold/templates/mcp-server/index.js +397 -0
  356. package/src/lib/scaffold/templates/mcp-server/presets.js +39 -0
  357. package/src/lib/scaffold/wizards/cms.js +93 -0
  358. package/src/lib/scaffold/wizards/mcp-server.js +61 -0
  359. package/src/lib/setup/contract.js +26 -0
  360. package/src/lib/setup/discover.js +116 -0
  361. package/src/lib/setup/execute.js +205 -0
  362. package/src/lib/setup/planner.js +134 -0
  363. package/src/lib/setup/recipes.js +235 -0
  364. package/src/lib/setup/runtime-persist.js +197 -0
  365. package/src/lib/setup/runtime.js +394 -0
  366. package/src/lib/slash-commands.js +2 -0
  367. package/src/lib/terminal/machine-identity.js +95 -0
  368. package/src/lib/terminal-local.js +164 -0
  369. package/src/lib/terminal-scopes.js +52 -0
  370. package/src/lib/whoami-capabilities.js +184 -0
  371. package/src/local-pty/server.js +34 -5
  372. package/src/mcp/authority.js +58 -108
  373. package/src/mcp/client-adapters.js +68 -144
  374. package/src/mcp/client.js +142 -122
  375. package/src/mcp/cloudflare-bundles.js +225 -0
  376. package/src/mcp/index.js +12 -0
  377. package/src/plugins/cloudflare.js +1 -1
  378. package/src/providers/ollama-chat.js +2 -1
  379. package/src/sam/types.js +27 -2
  380. package/src/security/inventory.js +12 -2
  381. package/src/security/local-vault.js +2 -0
  382. package/src/security/scan.js +1 -1
  383. package/test/account-session.test.mjs +3 -3
  384. package/test/app-authority-context.test.mjs +32 -0
  385. package/test/cli/codebaseindex-plan-ux.test.mjs +1 -1
  386. package/test/cli/mcp-client.test.mjs +21 -10
  387. package/test/cli/runtime-persist.test.mjs +56 -0
  388. package/test/cli/rust-command.test.mjs +21 -0
  389. package/test/credentials-audit.test.mjs +113 -0
  390. package/test/deploy-health-scan.test.mjs +29 -1
  391. package/test/gcloud-auth.test.mjs +111 -0
  392. package/test/google-cloud-connection.test.mjs +83 -0
  393. package/test/google-cloud-inventory.test.mjs +64 -0
  394. package/test/init-options.test.mjs +35 -0
  395. package/test/integration/app-command.test.mjs +30 -0
  396. package/test/integration/cad-project.test.mjs +4 -1
  397. package/test/integration/cms-app-scaffold.test.mjs +71 -0
  398. package/test/integration/cms-site-tenancy-contract.test.mjs +3 -2
  399. package/test/integration/codebaseindex-ingest.test.mjs +48 -0
  400. package/test/integration/features-resolve.test.mjs +30 -11
  401. package/test/integration/inspect-path-cli.test.mjs +46 -0
  402. package/test/integration/inventory-evidence.test.mjs +125 -0
  403. package/test/integration/runtime-status.integration.test.mjs +2 -1
  404. package/test/knowledge.test.mjs +35 -1
  405. package/test/loopback-oauth.test.mjs +53 -0
  406. package/test/ollama.test.mjs +22 -0
  407. package/test/security.test.mjs +23 -0
  408. package/test/setup-planner.test.mjs +74 -0
  409. package/test/setup-runtime.test.mjs +51 -0
  410. package/test/terminal/local-pty.mock.test.mjs +3 -1
  411. package/test/terminal-machine-identity.test.mjs +88 -0
  412. package/test/whoami-resume.test.mjs +27 -6
  413. package/packages/agentsam-brand/test/brand-scan.test.mjs +0 -106
  414. package/packages/work-graph/prototype/README.md +0 -32
  415. package/packages/work-graph/prototype/data/demo-workgraph.js +0 -47
  416. package/packages/work-graph/prototype/renderers/timeline-model.js +0 -21
  417. package/packages/work-graph/prototype/renderers/workgraph-runtime.js +0 -38
  418. package/packages/work-graph/prototype-gnantt.html +0 -655
@@ -4,14 +4,34 @@ import { fileURLToPath } from 'node:url';
4
4
  import { spawnSync } from 'node:child_process';
5
5
 
6
6
  const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
7
+ const KEBAB = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
8
+ const FORBIDDEN_STRING_APP = /\bconst\s+APP\s*=\s*["'`][^"'`]+["'`]/;
9
+ const HOST_PRODUCT_FIELDS = new Set([
10
+ 'id',
11
+ 'name',
12
+ 'package',
13
+ 'bin',
14
+ 'commands',
15
+ 'routes',
16
+ 'surfaces',
17
+ 'capabilities',
18
+ 'entrypoints',
19
+ 'version',
20
+ 'kind',
21
+ 'product_id',
22
+ ]);
7
23
 
8
24
  function clean(value) {
9
25
  return value == null ? '' : String(value).trim();
10
26
  }
11
27
 
28
+ function readJson(filePath) {
29
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
30
+ }
31
+
12
32
  function readManifest(manifestPath, fallbackId) {
13
33
  try {
14
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
34
+ const manifest = readJson(manifestPath);
15
35
  return {
16
36
  id: manifest.id || fallbackId,
17
37
  name: manifest.name || fallbackId,
@@ -74,6 +94,264 @@ function runAppBin(app, args, options = {}) {
74
94
  if ((result.status ?? 1) !== 0) process.exit(result.status ?? 1);
75
95
  }
76
96
 
97
+ function walkFiles(dir, out = []) {
98
+ if (!fs.existsSync(dir)) return out;
99
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
100
+ if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.output' || entry.name === 'target') {
101
+ continue;
102
+ }
103
+ const full = path.join(dir, entry.name);
104
+ if (entry.isDirectory()) walkFiles(full, out);
105
+ else if (/\.(js|mjs|cjs|ts|tsx)$/.test(entry.name)) out.push(full);
106
+ }
107
+ return out;
108
+ }
109
+
110
+ function walkPackageJsonFiles(dir, out = []) {
111
+ if (!fs.existsSync(dir)) return out;
112
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
113
+ if (['node_modules', 'dist', '.output', 'target', '.git'].includes(entry.name)) continue;
114
+ const full = path.join(dir, entry.name);
115
+ if (entry.isDirectory()) walkPackageJsonFiles(full, out);
116
+ else if (entry.name === 'package.json') out.push(full);
117
+ }
118
+ return out;
119
+ }
120
+
121
+ function npmDistributionWarnings(app) {
122
+ const warnings = [];
123
+ const installCommand = clean(app.manifest?.install?.command);
124
+ const declaresNpmInstall = /^npm\s+(?:i|install)\b/.test(installCommand);
125
+ if (!declaresNpmInstall) return warnings;
126
+
127
+ const rootPackagePath = path.join(app.dir, 'package.json');
128
+ if (!fs.existsSync(rootPackagePath)) {
129
+ return [`${app.id}: distribution: install.command advertises npm but package.json is missing`];
130
+ }
131
+
132
+ let rootPackage;
133
+ try {
134
+ rootPackage = readJson(rootPackagePath);
135
+ } catch (error) {
136
+ return [`${app.id}: distribution: package.json is invalid JSON: ${error.message}`];
137
+ }
138
+
139
+ if (app.manifest.package && rootPackage.name !== app.manifest.package) {
140
+ warnings.push(`${app.id}: distribution: manifest package ${app.manifest.package} does not match package.json name ${rootPackage.name || '(missing)'}`);
141
+ }
142
+ if (rootPackage.private === true) {
143
+ warnings.push(`${app.id}: distribution: package.json private=true but install.command advertises npm installation`);
144
+ }
145
+
146
+ const expectedBin = clean(app.manifest.bin || app.manifest?.entrypoints?.cli);
147
+ const packageBins = typeof rootPackage.bin === 'string'
148
+ ? [rootPackage.bin]
149
+ : Object.values(rootPackage.bin || {}).map(clean).filter(Boolean);
150
+ if (expectedBin && !packageBins.includes(expectedBin)) {
151
+ warnings.push(`${app.id}: distribution: package.json does not expose manifest CLI bin ${expectedBin}`);
152
+ }
153
+ if (!Array.isArray(rootPackage.files) || rootPackage.files.length === 0) {
154
+ warnings.push(`${app.id}: distribution: package.json has no explicit files allowlist`);
155
+ }
156
+
157
+ const escaping = [];
158
+ for (const packageFile of walkPackageJsonFiles(app.dir)) {
159
+ let nested;
160
+ try { nested = readJson(packageFile); } catch { continue; }
161
+ for (const group of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {
162
+ for (const [name, spec] of Object.entries(nested[group] || {})) {
163
+ if (typeof spec !== 'string' || !spec.startsWith('file:')) continue;
164
+ const target = path.resolve(path.dirname(packageFile), spec.slice(5));
165
+ const insideApp = target === app.dir || target.startsWith(`${app.dir}${path.sep}`);
166
+ if (!insideApp) {
167
+ escaping.push(`${path.relative(app.dir, packageFile)}:${group}:${name}=${spec}`);
168
+ }
169
+ }
170
+ }
171
+ }
172
+ if (escaping.length) {
173
+ warnings.push(`${app.id}: distribution: ${escaping.length} repo-local file: dependencies escape the app package (${escaping.join(', ')})`);
174
+ }
175
+
176
+ return warnings;
177
+ }
178
+
179
+ function validateOneApp(app, knownIds, root) {
180
+ const errors = [];
181
+ const warnings = [];
182
+ const m = app.manifest;
183
+
184
+ if (m.schema !== 'agentsam.app.v1') {
185
+ errors.push(`${app.id}: schema must be "agentsam.app.v1"`);
186
+ }
187
+ if (!KEBAB.test(app.id)) {
188
+ errors.push(`${app.id}: id must be kebab-case (^[a-z][a-z0-9]*(-[a-z0-9]+)*$)`);
189
+ }
190
+ const dirId = path.basename(app.dir);
191
+ if (dirId !== app.id && !m.directory_id_exception) {
192
+ errors.push(`${app.id}: directory basename "${dirId}" must equal id (set directory_id_exception if intentional)`);
193
+ }
194
+ if (m.kind !== 'app') {
195
+ errors.push(`${app.id}: kind must be "app"`);
196
+ }
197
+ if (m.product_id != null && !KEBAB.test(m.product_id)) {
198
+ errors.push(`${app.id}: product_id must be kebab-case`);
199
+ }
200
+ if (m.package && m.package === app.id) {
201
+ errors.push(`${app.id}: package must not equal APP.id (PACKAGE namespace is separate)`);
202
+ }
203
+ if (typeof m.package === 'string' && m.package.startsWith('@') === false && m.package.includes('/')) {
204
+ warnings.push(`${app.id}: package looks unusual: ${m.package}`);
205
+ }
206
+
207
+ const caps = m.capabilities;
208
+ if (caps == null) {
209
+ warnings.push(`${app.id}: capabilities not declared`);
210
+ } else if (!Array.isArray(caps) && (typeof caps !== 'object' || caps === null)) {
211
+ errors.push(`${app.id}: capabilities must be an array or boolean map`);
212
+ }
213
+
214
+ if (!m.entrypoints && !m.bin) {
215
+ warnings.push(`${app.id}: neither entrypoints nor bin declared`);
216
+ }
217
+
218
+ warnings.push(...npmDistributionWarnings(app));
219
+
220
+ const hostPath = path.join(app.dir, '.agentsam', 'app.json');
221
+ if (fs.existsSync(hostPath)) {
222
+ let host;
223
+ try {
224
+ host = readJson(hostPath);
225
+ } catch (err) {
226
+ errors.push(`${app.id}: .agentsam/app.json invalid JSON: ${err.message}`);
227
+ host = null;
228
+ }
229
+ if (host) {
230
+ if (host.schema === 'agentsam.app.v1' || host.id) {
231
+ errors.push(
232
+ `${app.id}: .agentsam/app.json must be host/install state (agentsam.host-install.v1 with app_id), not a second product definition`,
233
+ );
234
+ }
235
+ if (host.schema !== 'agentsam.host-install.v1') {
236
+ errors.push(`${app.id}: .agentsam/app.json schema must be "agentsam.host-install.v1"`);
237
+ }
238
+ if (!host.app_id) {
239
+ errors.push(`${app.id}: .agentsam/app.json missing app_id`);
240
+ } else if (!knownIds.has(host.app_id)) {
241
+ errors.push(`${app.id}: .agentsam/app.json app_id "${host.app_id}" does not resolve to a known APP`);
242
+ } else if (host.app_id !== app.id) {
243
+ errors.push(`${app.id}: .agentsam/app.json app_id "${host.app_id}" must match this APP.id`);
244
+ }
245
+ for (const field of HOST_PRODUCT_FIELDS) {
246
+ if (Object.prototype.hasOwnProperty.call(host, field)) {
247
+ errors.push(`${app.id}: .agentsam/app.json must not redefine product field "${field}"`);
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ for (const file of walkFiles(path.join(app.dir, 'backend'))) {
254
+ const text = fs.readFileSync(file, 'utf8');
255
+ if (FORBIDDEN_STRING_APP.test(text)) {
256
+ const rel = path.relative(root, file);
257
+ errors.push(`${app.id}: forbidden string APP identity in ${rel} — import agentsam.app.json instead`);
258
+ }
259
+ }
260
+
261
+ return { id: app.id, errors, warnings };
262
+ }
263
+
264
+ function validateDesktopManifests(root, knownIds) {
265
+ const errors = [];
266
+ const warnings = [];
267
+ const dir = path.join(root, 'packages/agentsam-desktop-shell/manifests');
268
+ if (!fs.existsSync(dir)) return { errors, warnings };
269
+ for (const name of fs.readdirSync(dir)) {
270
+ if (!name.endsWith('.json') || name === 'schema.json' || name === 'example.json') continue;
271
+ const file = path.join(dir, name);
272
+ let manifest;
273
+ try {
274
+ manifest = readJson(file);
275
+ } catch (err) {
276
+ errors.push(`desktop ${name}: invalid JSON: ${err.message}`);
277
+ continue;
278
+ }
279
+ const appId = manifest.app_id;
280
+ if (!appId) {
281
+ errors.push(`desktop ${name}: missing app_id`);
282
+ continue;
283
+ }
284
+ if (!KEBAB.test(appId)) {
285
+ errors.push(`desktop ${name}: app_id must be kebab-case`);
286
+ }
287
+ // Brand shells (meauxbility, etc.) may not be APP products — only fail when
288
+ // the filename suggests a product APP or when app_id is a known near-miss.
289
+ const productish = ['cad-creator', 'client-cms-editor', 'local-studio', 'cms-editor'].includes(appId)
290
+ || ['cad-creator', 'client-cms-editor', 'cms-editor', 'local-studio'].some((n) => name.startsWith(n));
291
+ if (productish && !knownIds.has(appId)) {
292
+ errors.push(`desktop ${name}: app_id "${appId}" does not resolve to a known APP manifest`);
293
+ }
294
+ const included = Array.isArray(manifest.included_apps) ? manifest.included_apps : [];
295
+ for (const bundledId of included) {
296
+ if (!KEBAB.test(bundledId)) {
297
+ errors.push(`desktop ${name}: included_apps entry "${bundledId}" must be kebab-case`);
298
+ continue;
299
+ }
300
+ if (!knownIds.has(bundledId)) {
301
+ errors.push(
302
+ `desktop ${name}: included_apps entry "${bundledId}" does not resolve to a known APP manifest`,
303
+ );
304
+ }
305
+ }
306
+ }
307
+ return { errors, warnings };
308
+ }
309
+
310
+ export function validateApps(options = {}) {
311
+ const root = options.root || REPO_ROOT;
312
+ const filterId = options.appId || null;
313
+ const apps = listAppManifests(root);
314
+ const knownIds = new Set(apps.map((a) => a.id));
315
+ const selected = filterId ? apps.filter((a) => a.id === filterId || path.basename(a.dir) === filterId) : apps;
316
+
317
+ if (filterId && selected.length === 0) {
318
+ throw new Error(`unknown AgentSam app: ${filterId}`);
319
+ }
320
+
321
+ const idCounts = new Map();
322
+ for (const app of apps) {
323
+ idCounts.set(app.id, (idCounts.get(app.id) || 0) + 1);
324
+ }
325
+
326
+ const results = [];
327
+ const allErrors = [];
328
+ const allWarnings = [];
329
+
330
+ for (const [id, count] of idCounts) {
331
+ if (count > 1) allErrors.push(`duplicate APP.id across manifests: ${id}`);
332
+ }
333
+
334
+ for (const app of selected) {
335
+ const result = validateOneApp(app, knownIds, root);
336
+ results.push(result);
337
+ allErrors.push(...result.errors);
338
+ allWarnings.push(...result.warnings);
339
+ }
340
+
341
+ if (!filterId) {
342
+ const desktop = validateDesktopManifests(root, knownIds);
343
+ allErrors.push(...desktop.errors);
344
+ allWarnings.push(...desktop.warnings);
345
+ }
346
+
347
+ return {
348
+ ok: allErrors.length === 0,
349
+ apps: results,
350
+ errors: allErrors,
351
+ warnings: allWarnings,
352
+ };
353
+ }
354
+
77
355
  export async function runApp(argv = [], options = {}) {
78
356
  const write = options.write || ((text) => process.stdout.write(text));
79
357
  const [command = 'list', appId, ...rest] = argv;
@@ -81,6 +359,8 @@ export async function runApp(argv = [], options = {}) {
81
359
  write([
82
360
  'agentsam app list',
83
361
  'agentsam app info <id>',
362
+ 'agentsam app inspect <id>',
363
+ 'agentsam app validate [--all|<id>]',
84
364
  'agentsam app install <id>',
85
365
  'agentsam app doctor <id>',
86
366
  'agentsam app preview <id>',
@@ -100,10 +380,34 @@ export async function runApp(argv = [], options = {}) {
100
380
  return apps;
101
381
  }
102
382
 
383
+ if (command === 'validate') {
384
+ const target = appId === '--all' || appId == null ? null : appId;
385
+ const report = validateApps({ root: options.root || REPO_ROOT, appId: target });
386
+ if (report.warnings.length) {
387
+ write('\n Warnings\n');
388
+ for (const w of report.warnings) write(` • ${w}\n`);
389
+ }
390
+ if (report.errors.length) {
391
+ write('\n Errors\n');
392
+ for (const e of report.errors) write(` ✗ ${e}\n`);
393
+ write('\n');
394
+ if (!options.noExit) process.exitCode = 1;
395
+ return report;
396
+ }
397
+ write(`\n ✓ agentsam app validate${target ? ` ${target}` : ' --all'} (${report.apps.length} app(s))\n\n`);
398
+ return report;
399
+ }
400
+
103
401
  if (!appId) throw new Error('agentsam app requires an app id');
104
402
  const app = resolveApp(appId, options.root || REPO_ROOT);
105
403
 
106
- if (command === 'info') {
404
+ if (command === 'info' || command === 'inspect') {
405
+ if (command === 'inspect') {
406
+ const report = validateApps({ root: options.root || REPO_ROOT, appId: app.id });
407
+ write(`${JSON.stringify({ manifest: app.manifest, validation: report }, null, 2)}\n`);
408
+ if (!report.ok && !options.noExit) process.exitCode = 1;
409
+ return { manifest: app.manifest, validation: report };
410
+ }
107
411
  write(`${JSON.stringify(app.manifest, null, 2)}\n`);
108
412
  return app.manifest;
109
413
  }
@@ -132,7 +436,7 @@ export async function runApp(argv = [], options = {}) {
132
436
  }
133
437
 
134
438
  export function appCommandHelp() {
135
- return 'agentsam app list|info|install|doctor|preview|scaffold';
439
+ return 'agentsam app list|info|inspect|validate|install|doctor|preview|scaffold';
136
440
  }
137
441
 
138
442
  export { clean };
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Billing relationships — not --billing-project (quota attribution).
3
+ */
4
+ import { execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+
7
+ const execFileAsync = promisify(execFile);
8
+
9
+ function writeLine(write, value = '') {
10
+ write(`${value}\n`);
11
+ }
12
+
13
+ async function runGcloud(args, env) {
14
+ try {
15
+ const { stdout, stderr } = await execFileAsync('gcloud', args, {
16
+ timeout: 30000,
17
+ env,
18
+ maxBuffer: 1024 * 1024,
19
+ });
20
+ return { ok: true, stdout: String(stdout || ''), stderr: String(stderr || '') };
21
+ } catch (error) {
22
+ return {
23
+ ok: false,
24
+ stdout: String(error?.stdout || ''),
25
+ stderr: String(error?.stderr || error?.message || ''),
26
+ };
27
+ }
28
+ }
29
+
30
+ function printHelp(write) {
31
+ writeLine(write, '');
32
+ writeLine(write, ' agentsam billing');
33
+ writeLine(write, ' agentsam billing accounts');
34
+ writeLine(write, ' agentsam billing project [PROJECT_ID]');
35
+ writeLine(write, ' agentsam billing budgets --account XXXXXX-XXXXXX-XXXXXX');
36
+ writeLine(write, '');
37
+ writeLine(write, ' Note: gcloud --billing-project only attributes quota for one CLI call.');
38
+ writeLine(write, ' It does not show historical spend. Use Cloud Billing reports / BigQuery export for costs.');
39
+ writeLine(write, ' Future: agentsam costs gcp|cloudflare|models');
40
+ writeLine(write, '');
41
+ }
42
+
43
+ export async function runBilling(argv = [], options = {}) {
44
+ const write = options.write || ((s) => process.stdout.write(s));
45
+ const env = options.env || process.env;
46
+ const json = argv.includes('--json');
47
+ const args = argv.filter((a) => a !== '--json');
48
+
49
+ if (!args.length || args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
50
+ printHelp(write);
51
+ if (!args.length) {
52
+ // Default: accounts list
53
+ args.push('accounts');
54
+ } else {
55
+ return 0;
56
+ }
57
+ }
58
+
59
+ if (args[0] === 'accounts') {
60
+ const result = await runGcloud(['billing', 'accounts', 'list', '--format=table(name,displayName,open)'], env);
61
+ if (!result.ok) {
62
+ write(result.stderr || result.stdout);
63
+ return 1;
64
+ }
65
+ if (json) write(JSON.stringify({ ok: true, stdout: result.stdout }, null, 2) + '\n');
66
+ else {
67
+ writeLine(write, '');
68
+ writeLine(write, ' Agent Sam · billing accounts');
69
+ writeLine(write, ' (relationship inventory — not a spend dashboard)');
70
+ writeLine(write, '');
71
+ write(result.stdout.endsWith('\n') ? result.stdout : `${result.stdout}\n`);
72
+ }
73
+ return 0;
74
+ }
75
+
76
+ if (args[0] === 'project') {
77
+ let project = args[1] || '';
78
+ if (!project) {
79
+ const cfg = await runGcloud(['config', 'get-value', 'project'], env);
80
+ project = cfg.stdout.trim();
81
+ }
82
+ if (!project || project === '(unset)') {
83
+ writeLine(write, ' ✕ Pass a project id or set gcloud config project.');
84
+ return 1;
85
+ }
86
+ const result = await runGcloud(['billing', 'projects', 'describe', project, '--format=yaml(billingAccountName,billingEnabled,projectId)'], env);
87
+ if (!result.ok) {
88
+ write(result.stderr || result.stdout);
89
+ return 1;
90
+ }
91
+ write(result.stdout.endsWith('\n') ? result.stdout : `${result.stdout}\n`);
92
+ return 0;
93
+ }
94
+
95
+ if (args[0] === 'budgets') {
96
+ let account = '';
97
+ for (let i = 1; i < args.length; i += 1) {
98
+ if (args[i] === '--account') account = args[++i] || '';
99
+ else if (args[i].startsWith('--account=')) account = args[i].slice('--account='.length);
100
+ else if (!args[i].startsWith('-') && !account) account = args[i];
101
+ }
102
+ if (!account || account.includes('XXXX')) {
103
+ writeLine(write, ' ✕ Pass a real billing account id from: agentsam billing accounts');
104
+ writeLine(write, ' Example: agentsam billing budgets --account 017E01-4426EF-21356F');
105
+ return 1;
106
+ }
107
+ const name = account.startsWith('billingAccounts/') ? account : `billingAccounts/${account}`;
108
+ const result = await runGcloud(['billing', 'budgets', 'list', '--billing-account', name.replace(/^billingAccounts\//, ''), '--format=table(displayName,amount,budgetFilter)'], env);
109
+ // gcloud wants the id without prefix sometimes; try both on failure
110
+ if (!result.ok) {
111
+ const retry = await runGcloud(['billing', 'budgets', 'list', `--billing-account=${account.replace(/^billingAccounts\//, '')}`], env);
112
+ if (!retry.ok) {
113
+ write(retry.stderr || result.stderr || retry.stdout);
114
+ writeLine(write, '');
115
+ writeLine(write, ' Tip: copy ACCOUNT_ID from `agentsam billing accounts` — not the placeholder XXXXXX-XXXXXX-XXXXXX.');
116
+ return 1;
117
+ }
118
+ write(retry.stdout.endsWith('\n') ? retry.stdout : `${retry.stdout}\n`);
119
+ return 0;
120
+ }
121
+ write(result.stdout.endsWith('\n') ? result.stdout : `${result.stdout}\n`);
122
+ return 0;
123
+ }
124
+
125
+ printHelp(write);
126
+ return 1;
127
+ }