@inneranimalmedia/agentsam-sdk 2.6.1 → 2.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTSAM.md +45 -1
- package/README.md +80 -0
- package/apps/README.md +34 -0
- package/apps/cad-creator/README.md +728 -0
- package/apps/cad-creator/shared/cad/package.json +19 -0
- package/apps/cad-creator/shared/cad/src/domain/operations.ts +7 -0
- package/apps/cad-creator/shared/cad/src/domain/project-contract.d.ts +3 -0
- package/apps/cad-creator/shared/cad/src/domain/project-contract.js +75 -0
- package/apps/cad-creator/shared/cad/src/domain/project-tools.d.ts +1 -0
- package/apps/cad-creator/shared/cad/src/domain/project-tools.js +24 -0
- package/apps/cad-creator/shared/cad/src/domain/validation.ts +191 -0
- package/apps/cad-creator/shared/cad/src/index.ts +4 -0
- package/apps/cad-creator/shared/cad/src/robotics/types.ts +232 -0
- package/apps/cad-creator/shared/cad/src/types.ts +507 -0
- package/apps/cad-creator/shared/cad/tsconfig.json +14 -0
- package/docs/AUTH_IDENTITY_CONTRACT.md +25 -27
- package/docs/AUTORAG.md +58 -0
- package/docs/BRAND_INTELLIGENCE.md +30 -0
- package/docs/CLI_SHELL.md +4 -2
- package/docs/CMS_CONTEXTUAL_COMPOSER.md +38 -0
- package/docs/MERKLE.md +4 -0
- package/docs/PROJECT_CONFIG.md +3 -0
- package/docs/REPOSITORY_KNOWLEDGE.md +2 -0
- package/docs/SDK_WORKER.md +4 -3
- package/docs/THEME_REFINERY.md +120 -0
- package/docs/architecture/PROVIDER-TOOLS-AND-COMPACTION.md +60 -0
- package/docs/architecture/STORAGE.md +87 -0
- package/docs/cad-project-tools.md +69 -0
- package/docs/cms-scaffolding-pattern-reference.md +73 -0
- package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS-codex-tui-20260919.md +113 -0
- package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS.md +85 -0
- package/docs/plans/AGENTSAM-MCP-CLIENT-2026-09-19.md +181 -0
- package/docs/plans/ASBD-PORTABLE-WORKGRAPH-UI-DEFERRED.md +34 -0
- package/docs/plans/ASTRA-BRIEF-2026-09-18.md +258 -0
- package/docs/plans/MULTI-AGENT-SPRINTS-2026-09-19.md +373 -0
- package/docs/plans/NEXT-AGENT-HANDOFF.md +167 -0
- package/docs/plans/RECONCILIATION-2026-09-19.md +178 -0
- package/docs/plugins.md +43 -0
- package/docs/tools/README.md +57 -0
- package/docs/tools/agent-and-orchestration.md +49 -0
- package/docs/tools/browser-and-research.md +46 -0
- package/docs/tools/cloudflare-platform.md +32 -0
- package/docs/tools/cms.md +25 -0
- package/docs/tools/communications.md +41 -0
- package/docs/tools/database-and-storage.md +62 -0
- package/docs/tools/design-and-media.md +64 -0
- package/docs/tools/filesystem-and-code.md +55 -0
- package/docs/tools/github.md +56 -0
- package/docs/tools/integrations-and-misc.md +16 -0
- package/docs/tools/memory.md +21 -0
- package/docs/tools/terminal-and-execution.md +29 -0
- package/docs/tools/tickets-and-platform.md +30 -0
- package/migrations/d1/0001_agentsam_plugin_runtime.sql +177 -0
- package/migrations/d1/0002_agentsam_ticket_ownership.sql +8 -0
- package/migrations/d1/0002_dynamic_model_policies.sql +23 -0
- package/migrations/d1/0003_expand_tool_handler_families.sql +18 -0
- package/migrations/d1/0004_project_aware_retrieval_lanes.sql +24 -0
- package/migrations/d1/0005_portable_knowledge_identity.sql +104 -0
- package/migrations/d1/0006_repair_ops_trail_account_scope.sql +14 -0
- package/migrations/d1/0007_repair_ops_trail_agent_run_account.sql +13 -0
- package/migrations/d1/0008_backfill_knowledge_run_legacy_tenant.sql +14 -0
- package/migrations/d1/0009_normalize_cloudflare_plugin_installations.sql +137 -0
- package/migrations/runtime/0001_cli_runtime.sql +67 -0
- package/migrations/runtime/0002_project_sessions.sql +15 -0
- package/migrations/runtime/0003_provider_continuations.sql +8 -0
- package/package.json +92 -12
- package/packages/agentsam-brand/README.md +21 -0
- package/packages/agentsam-brand/package.json +22 -0
- package/packages/agentsam-brand/src/contract.js +59 -0
- package/packages/agentsam-brand/src/extract.js +126 -0
- package/packages/agentsam-brand/src/goap-actions.js +73 -0
- package/packages/agentsam-brand/src/index.js +48 -0
- package/packages/agentsam-brand/src/plan.js +115 -0
- package/packages/agentsam-brand/src/resolve.js +139 -0
- package/packages/agentsam-brand/src/scan.js +250 -0
- package/packages/agentsam-brand/test/brand-scan.test.mjs +106 -0
- package/packages/agentsam-contracts/README.md +23 -0
- package/packages/agentsam-contracts/package.json +32 -0
- package/packages/agentsam-contracts/src/adapter.ts +25 -0
- package/packages/agentsam-contracts/src/agent.ts +35 -0
- package/packages/agentsam-contracts/src/artifacts.ts +33 -0
- package/packages/agentsam-contracts/src/authority.ts +29 -0
- package/packages/agentsam-contracts/src/context.ts +16 -0
- package/packages/agentsam-contracts/src/errors.ts +46 -0
- package/packages/agentsam-contracts/src/events.ts +47 -0
- package/packages/agentsam-contracts/src/execution.ts +50 -0
- package/packages/agentsam-contracts/src/hooks.ts +40 -0
- package/packages/agentsam-contracts/src/identity.ts +16 -0
- package/packages/agentsam-contracts/src/index.ts +14 -0
- package/packages/agentsam-contracts/src/models.ts +9 -0
- package/packages/agentsam-contracts/src/providers.ts +33 -0
- package/packages/agentsam-contracts/src/repository.ts +64 -0
- package/packages/agentsam-contracts/src/tools.ts +98 -0
- package/packages/agentsam-contracts/test/contracts.test.mjs +16 -0
- package/packages/agentsam-contracts/test/errors-catalog.test.mjs +25 -0
- package/packages/agentsam-contracts/test/repository.test.mjs +18 -0
- package/packages/agentsam-contracts/tsconfig.json +12 -0
- package/packages/agentsam-errors/package.json +17 -0
- package/packages/agentsam-errors/src/adapters/anthropic.js +15 -0
- package/packages/agentsam-errors/src/adapters/cad.js +109 -0
- package/packages/agentsam-errors/src/adapters/cloudflare.js +21 -0
- package/packages/agentsam-errors/src/adapters/cursor.js +4 -0
- package/packages/agentsam-errors/src/adapters/device.js +38 -0
- package/packages/agentsam-errors/src/adapters/docker.js +28 -0
- package/packages/agentsam-errors/src/adapters/gcp.js +14 -0
- package/packages/agentsam-errors/src/adapters/github.js +22 -0
- package/packages/agentsam-errors/src/adapters/google.js +17 -0
- package/packages/agentsam-errors/src/adapters/index.js +55 -0
- package/packages/agentsam-errors/src/adapters/internal.js +40 -0
- package/packages/agentsam-errors/src/adapters/oauth.js +24 -0
- package/packages/agentsam-errors/src/adapters/openai.js +17 -0
- package/packages/agentsam-errors/src/adapters/process.js +35 -0
- package/packages/agentsam-errors/src/adapters/provider.js +114 -0
- package/packages/agentsam-errors/src/envelope.js +186 -0
- package/packages/agentsam-errors/src/error.js +24 -0
- package/packages/agentsam-errors/src/fingerprint.js +35 -0
- package/packages/agentsam-errors/src/index.js +10 -0
- package/packages/agentsam-errors/src/normalize.js +30 -0
- package/packages/agentsam-errors/src/redaction.js +52 -0
- package/packages/agentsam-errors/src/render.js +16 -0
- package/packages/agentsam-errors/src/retry.js +10 -0
- package/packages/agentsam-errors/src/transport.js +52 -0
- package/packages/agentsam-errors/src/vocabulary.js +320 -0
- package/packages/agentsam-errors/test/envelope.test.mjs +70 -0
- package/packages/agentsam-errors/test/providers.test.mjs +99 -0
- package/packages/agentsam-errors/test/redaction.test.mjs +21 -0
- package/packages/agentsam-errors/test/runtime-cad-device.test.mjs +76 -0
- package/packages/agentsam-errors/test/transport.test.mjs +46 -0
- package/packages/agentsam-knowledge/package.json +15 -0
- package/packages/agentsam-knowledge/src/autorag/index.js +64 -0
- package/packages/agentsam-knowledge/src/backends/index.js +26 -0
- package/packages/agentsam-knowledge/src/index.js +3 -0
- package/packages/agentsam-knowledge/src/providers/index.js +95 -0
- package/packages/agentsam-knowledge/test/autorag.test.mjs +42 -0
- package/packages/agentsam-nav/README.md +40 -0
- package/packages/agentsam-nav/dist/Nav.d.ts +43 -0
- package/packages/agentsam-nav/dist/NavCollapsible.d.ts +10 -0
- package/packages/agentsam-nav/dist/NavContent.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavFooter.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavGroup.d.ts +3 -0
- package/packages/agentsam-nav/dist/NavHeader.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavMenu.d.ts +22 -0
- package/packages/agentsam-nav/dist/NavProvider.d.ts +35 -0
- package/packages/agentsam-nav/dist/NavTrigger.d.ts +2 -0
- package/packages/agentsam-nav/dist/Surfaces.d.ts +41 -0
- package/packages/agentsam-nav/dist/browser.d.ts +11 -0
- package/packages/agentsam-nav/dist/browser.global.js +132 -0
- package/packages/agentsam-nav/dist/browser.js +613 -0
- package/packages/agentsam-nav/dist/contracts.d.ts +76 -0
- package/packages/agentsam-nav/dist/index.d.ts +11 -0
- package/packages/agentsam-nav/dist/index.js +614 -0
- package/packages/agentsam-nav/dist/legacy.css +83 -0
- package/packages/agentsam-nav/dist/shell.css +113 -0
- package/packages/agentsam-nav/dist/theme.css +113 -0
- package/packages/agentsam-nav/dist/utils.d.ts +1 -0
- package/packages/agentsam-nav/package.json +43 -0
- package/packages/agentsam-queue-control/README.md +107 -0
- package/packages/agentsam-queue-control/package.json +31 -0
- package/packages/agentsam-queue-control/schemas/job-envelope.schema.json +56 -0
- package/packages/agentsam-queue-control/src/adapters/cloudflare.js +152 -0
- package/packages/agentsam-queue-control/src/adapters/memory.js +38 -0
- package/packages/agentsam-queue-control/src/contracts.js +130 -0
- package/packages/agentsam-queue-control/src/control-plane.js +141 -0
- package/packages/agentsam-queue-control/src/dispatch.js +43 -0
- package/packages/agentsam-queue-control/src/index.js +7 -0
- package/packages/agentsam-queue-control/src/policy.js +88 -0
- package/packages/agentsam-queue-control/src/topology.js +66 -0
- package/packages/agentsam-queue-control/test/queue-control.test.mjs +100 -0
- package/packages/agentsam-repository/src/goap.js +718 -0
- package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +670 -0
- package/packages/agentsam-repository/src/index.js +5 -0
- package/packages/agentsam-repository/src/merkle/cloudflare-persistence.js +2 -2
- package/packages/agentsam-repository/src/tickets.js +184 -0
- package/packages/agentsam-repository/src/work-tracking.js +199 -0
- package/packages/agentsam-repository/src/workspace-state.js +176 -0
- package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +152 -0
- package/packages/connectors/cloudflare/src/index.js +297 -15
- package/packages/connectors/cloudflare/src/owner.js +43 -40
- package/packages/connectors/cloudflare/src/routes.js +167 -26
- package/packages/connectors/cloudflare/tests/connector.test.mjs +112 -2
- package/packages/heuristic-theme/README.md +11 -0
- package/packages/heuristic-theme/package.json +25 -0
- package/packages/heuristic-theme/src/index.js +2 -0
- package/packages/heuristic-theme/src/registry.js +36 -0
- package/packages/heuristic-theme/src/theme.js +41 -0
- package/packages/heuristic-theme/test/theme.test.mjs +26 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +64 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +96 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/resources.json +47 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +74 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/ui.json +35 -0
- package/packages/identity/package.json +4 -3
- package/packages/identity/src/adapters/cloudflare-d1/ids.js +4 -0
- package/packages/identity/src/adapters/cloudflare-d1/index.js +39 -1
- package/packages/identity/src/contracts/auth-config.js +23 -40
- package/packages/identity/src/contracts/provider.js +3 -2
- package/packages/identity/src/frontend/auth-portal/pages/login.html +7 -0
- package/packages/identity/src/index.js +2 -0
- package/packages/identity/src/oauth/credentials.js +13 -0
- package/packages/identity/src/oauth/iam-platform.js +5 -5
- package/packages/identity/src/providers/cloudflare/index.js +19 -0
- package/packages/identity/src/providers/cloudflare/mapper.js +21 -0
- package/packages/identity/src/providers/cloudflare/oauth.js +48 -0
- package/packages/identity/src/providers/cloudflare/profile.js +28 -0
- package/packages/identity/src/providers/index.js +18 -2
- package/packages/identity/src/server/identity-service.js +13 -3
- package/packages/identity/src/server/worker-router.js +63 -8
- package/packages/identity/tests/auth-config.test.mjs +14 -23
- package/packages/identity/tests/identity-service.test.mjs +15 -0
- package/packages/providers/completeful/README.md +38 -0
- package/packages/providers/completeful/package.json +29 -0
- package/packages/providers/completeful/src/client.js +162 -0
- package/packages/providers/completeful/src/events.js +49 -0
- package/packages/providers/completeful/src/index.js +22 -0
- package/packages/providers/completeful/src/tools.js +579 -0
- package/packages/providers/completeful/src/webhooks.js +55 -0
- package/packages/providers/completeful/test/provider.test.mjs +196 -0
- package/packages/theme-church-site/README.md +10 -0
- package/packages/theme-church-site/package.json +30 -0
- package/packages/theme-church-site/src/harvest.json +14 -0
- package/packages/theme-church-site/src/index.js +84 -0
- package/packages/theme-church-site/test/theme.test.mjs +19 -0
- package/packages/theme-companions-site/README.md +10 -0
- package/packages/theme-companions-site/package.json +30 -0
- package/packages/theme-companions-site/src/harvest.json +12 -0
- package/packages/theme-companions-site/src/index.js +82 -0
- package/packages/theme-companions-site/test/theme.test.mjs +19 -0
- package/packages/theme-floors-site/README.md +10 -0
- package/packages/theme-floors-site/package.json +30 -0
- package/packages/theme-floors-site/src/harvest.json +14 -0
- package/packages/theme-floors-site/src/index.js +82 -0
- package/packages/theme-floors-site/test/theme.test.mjs +19 -0
- package/packages/theme-fuelnfree-site/README.md +10 -0
- package/packages/theme-fuelnfree-site/package.json +30 -0
- package/packages/theme-fuelnfree-site/src/harvest.json +13 -0
- package/packages/theme-fuelnfree-site/src/index.js +81 -0
- package/packages/theme-fuelnfree-site/test/theme.test.mjs +19 -0
- package/packages/theme-handyman-site/README.md +10 -0
- package/packages/theme-handyman-site/package.json +30 -0
- package/packages/theme-handyman-site/src/harvest.json +10 -0
- package/packages/theme-handyman-site/src/index.js +83 -0
- package/packages/theme-handyman-site/test/theme.test.mjs +19 -0
- package/packages/theme-insurance-site/README.md +10 -0
- package/packages/theme-insurance-site/package.json +30 -0
- package/packages/theme-insurance-site/src/harvest.json +10 -0
- package/packages/theme-insurance-site/src/index.js +83 -0
- package/packages/theme-insurance-site/test/theme.test.mjs +19 -0
- package/packages/theme-shinshu-site/README.md +10 -0
- package/packages/theme-shinshu-site/package.json +30 -0
- package/packages/theme-shinshu-site/src/harvest.json +12 -0
- package/packages/theme-shinshu-site/src/index.js +83 -0
- package/packages/theme-shinshu-site/test/theme.test.mjs +19 -0
- package/packages/work-graph/README.md +49 -0
- package/packages/work-graph/package.json +19 -0
- package/packages/work-graph/prototype/README.md +32 -0
- package/packages/work-graph/prototype/data/demo-workgraph.js +47 -0
- package/packages/work-graph/prototype/renderers/timeline-model.js +21 -0
- package/packages/work-graph/prototype/renderers/workgraph-runtime.js +38 -0
- package/packages/work-graph/prototype-gnantt.html +655 -0
- package/packages/work-graph/src/adapters/business/projects.js +23 -0
- package/packages/work-graph/src/adapters/engineering/git.js +22 -0
- package/packages/work-graph/src/adapters/engineering/mcp.js +22 -0
- package/packages/work-graph/src/index.js +76 -0
- package/packages/work-graph/src/renderers/gantt-model.js +11 -0
- package/packages/work-graph/src/renderers/timeline.js +16 -0
- package/packages/work-graph/test/work-graph.test.mjs +37 -0
- package/protocol/README.md +12 -0
- package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +30 -6
- package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +18 -4
- package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +59 -10
- package/protocol/capabilities/knowledge-search-input.schema.json +35 -0
- package/protocol/capabilities/manifest.json +163 -1
- package/protocol/capabilities/repository-audit-input.schema.json +39 -6
- package/protocol/capabilities/repository-snapshot-input.schema.json +15 -3
- package/protocol/capabilities/terminal-exec-input.schema.json +55 -0
- package/protocol/commerce/catalog.v1.json +50 -0
- package/protocol/errors/error-catalog.json +164 -0
- package/protocol/errors/error-envelope.schema.json +544 -0
- package/protocol/features/README.md +57 -0
- package/protocol/features/agentsam.feature.v1.schema.json +290 -0
- package/protocol/features/catalog.json +65 -0
- package/protocol/features/examples/provider-resend.feature.json +17 -0
- package/protocol/go/agentsam-go-runtime.v1.json +17 -0
- package/protocol/knowledge/LIFECYCLE.md +140 -0
- package/protocol/knowledge/README.md +4 -0
- package/protocol/presets/catalog.json +128 -19
- package/protocol/rpc/v1/errors.proto +24 -0
- package/protocol/theme-refinery/d1-reconciliation.md +48 -0
- package/protocol/theme-refinery/product-metadata.schema.json +27 -0
- package/protocol/theme-refinery/receipts/products-registry-batch-20260924.json +318 -0
- package/protocol/theme-refinery/receipts/theme-harvest-20260924.json +472 -0
- package/protocol/theme-refinery/reconciliation-receipt.schema.json +83 -0
- package/services/cad/server.py +249 -2
- package/src/agent/capability-adapter.js +15 -5
- package/src/agent/index.js +2 -1
- package/src/agent/model-turn.js +51 -0
- package/src/agent/responses-runner.js +304 -38
- package/src/capabilities/index.js +1 -0
- package/src/capabilities/terminal-exec.js +78 -0
- package/src/cli.js +129 -31
- package/src/cloudflare/index.js +6 -0
- package/src/cloudflare/runtime-status.js +393 -0
- package/src/cloudflare/wrangler.js +17 -1
- package/src/commands/account-auth.js +1 -1
- package/src/commands/app.js +106 -0
- package/src/commands/autorag.js +77 -0
- package/src/commands/brand.js +293 -0
- package/src/commands/cad.js +1 -0
- package/src/commands/connections.js +126 -29
- package/src/commands/context-economics.js +8 -0
- package/src/commands/db.js +7 -0
- package/src/commands/deploy.js +2 -2
- package/src/commands/env.js +5 -4
- package/src/commands/eval.js +209 -14
- package/src/commands/go.js +365 -0
- package/src/commands/goap.js +176 -0
- package/src/commands/identity-init.js +27 -3
- package/src/commands/identity-protocol.js +195 -0
- package/src/commands/knowledge.js +64 -5
- package/src/commands/mcp.js +341 -0
- package/src/commands/merkle-persist.js +3 -1
- package/src/commands/merkle.js +18 -3
- package/src/commands/models.js +109 -23
- package/src/commands/plugins.js +175 -0
- package/src/commands/preferences.js +28 -8
- package/src/commands/product.js +53 -10
- package/src/commands/providers.js +389 -0
- package/src/commands/resume.js +9 -5
- package/src/commands/runtime-status.js +154 -0
- package/src/commands/shell.js +469 -54
- package/src/commands/status.js +82 -7
- package/src/commands/tunnel.js +228 -195
- package/src/commands/whoami.js +115 -22
- package/src/context/budget.js +6 -0
- package/src/context/index.js +2 -1
- package/src/context/project-card.js +75 -0
- package/src/errors/contract.js +34 -211
- package/src/errors/diagnostic.js +95 -50
- package/src/errors/index.js +6 -13
- package/src/eval/index.js +6 -0
- package/src/eval/live.js +439 -0
- package/src/features/resolve.js +242 -0
- package/src/go/build.js +108 -0
- package/src/go/cloudflare.js +207 -0
- package/src/go/contract.js +139 -0
- package/src/go/discover.js +96 -0
- package/src/go/index.js +13 -0
- package/src/go/receipts.js +77 -0
- package/src/go/registry.js +223 -0
- package/src/go/verify.js +31 -0
- package/src/index.js +100 -1
- package/src/knowledge/config.js +13 -5
- package/src/knowledge/engine.js +23 -8
- package/src/knowledge/index.js +1 -0
- package/src/knowledge/lane-registry.js +46 -0
- package/src/knowledge/providers/gemini.js +48 -8
- package/src/knowledge/service/client.js +13 -1
- package/src/knowledge/service/grpc-codec.js +156 -53
- package/src/knowledge/service/job-engine.js +133 -11
- package/src/knowledge/service/job-worker.js +34 -5
- package/src/knowledge/service/server.js +10 -1
- package/src/knowledge/store-discovery.js +77 -0
- package/src/lib/account-session.js +100 -25
- package/src/lib/agent-instructions.js +3 -0
- package/src/lib/auth.js +318 -51
- package/src/lib/cad/blender.js +42 -3
- package/src/lib/cad/d1-project-store.js +43 -0
- package/src/lib/cad/discovery.js +616 -0
- package/src/lib/cad/docker-executor.js +352 -0
- package/src/lib/cad/freecad.js +253 -0
- package/src/lib/cad/index.js +40 -0
- package/src/lib/cad/openscad.js +198 -0
- package/src/lib/cad/project-cli.d.ts +2 -0
- package/src/lib/cad/project-cli.js +21 -0
- package/src/lib/cad/project-contract.d.ts +3 -0
- package/src/lib/cad/project-contract.js +1 -0
- package/src/lib/cad/project-recipe.js +30 -0
- package/src/lib/cad/project-runtime.d.ts +2 -0
- package/src/lib/cad/project-runtime.js +53 -0
- package/src/lib/cad/project-store.js +25 -0
- package/src/lib/cad/project-workflows.js +31 -0
- package/src/lib/cad-docker.js +21 -2
- package/src/lib/cli-preferences.js +17 -2
- package/src/lib/core-client.js +97 -33
- package/src/lib/deploy/local-studio.js +6 -5
- package/src/lib/detect-context.js +14 -16
- package/src/lib/dockerize.js +3 -0
- package/src/lib/execution-approvals.js +1 -0
- package/src/lib/identity-scaffold.js +4 -1
- package/src/lib/knowledge-docker.js +9 -1
- package/src/lib/local-sessions.js +124 -34
- package/src/lib/local-status.js +35 -14
- package/src/lib/project-config.js +9 -0
- package/src/lib/provider-credentials.js +233 -55
- package/src/lib/scaffold/resolve-cloudflare-account.js +37 -0
- package/src/lib/scaffold/wizards/cms.js +2 -8
- package/src/lib/scaffold/wizards/worker-api.js +2 -8
- package/src/lib/slash-commands.js +10 -2
- package/src/local/migrations.js +109 -1
- package/src/local/runtime-store.js +31 -5
- package/src/local/sqlite.js +36 -1
- package/src/mcp/authority.js +348 -0
- package/src/mcp/client-adapters.js +431 -0
- package/src/mcp/client.js +172 -0
- package/src/mcp/index.js +47 -0
- package/src/mcp/telemetry.js +103 -0
- package/src/models/catalog.js +329 -51
- package/src/models/discovery.js +59 -4
- package/src/models/index.js +2 -0
- package/src/models/inventory-core.js +182 -0
- package/src/models/policy.js +20 -0
- package/src/plugins/cloudflare.js +103 -0
- package/src/plugins/completeful.js +69 -0
- package/src/plugins/contracts.js +86 -0
- package/src/plugins/index.js +11 -0
- package/src/plugins/registry.js +207 -0
- package/src/plugins/resource-scope.js +45 -0
- package/src/plugins/runtime.js +118 -0
- package/src/plugins/vectorize.js +31 -0
- package/src/progression/engine.js +171 -0
- package/src/progression/index.js +1 -0
- package/src/providers/cloudflare-chat.js +1 -1
- package/src/providers/gemini-generate-content.js +2 -1
- package/src/providers/openai-responses.js +45 -16
- package/src/providers/tool-schema.js +85 -0
- package/src/repository/index.js +29 -0
- package/src/rpc/generated/errors_pb.js +961 -1
- package/src/security/local-vault.js +415 -0
- package/src/security/process.js +20 -4
- package/src/status/actions.js +192 -0
- package/src/tools/hydrate.js +5 -1
- package/src/tools/index.js +7 -0
- package/src/tools/redact.js +64 -0
- package/src/tools/registry.js +83 -0
- package/src/tools/runtime.js +257 -0
- package/src/tools/search.js +21 -8
- package/src/ui/ansi.js +117 -3
- package/src/ui/cli/activity.js +76 -7
- package/src/ui/cli/footer.js +110 -9
- package/src/ui/cli/help.js +14 -5
- package/src/ui/wireframes.js +166 -0
- package/test/account-session.test.mjs +76 -11
- package/test/apps-scaffold-contract.test.mjs +58 -0
- package/test/capabilities.test.mjs +1 -1
- package/test/cli/eval-live.test.mjs +219 -0
- package/test/cli/go.test.mjs +71 -0
- package/test/cli/local-vault-and-provider-setup.test.mjs +268 -0
- package/test/cli/mcp-client.test.mjs +245 -0
- package/test/cli/runtime-ui.test.mjs +69 -2
- package/test/cli/status-actions.test.mjs +88 -0
- package/test/cli/wireframes-go-registry.test.mjs +54 -0
- package/test/cloudflare-connector.test.mjs +3 -6
- package/test/cloudflare-runtime.test.mjs +8 -1
- package/test/context.test.mjs +15 -0
- package/test/error-diagnostics.test.mjs +9 -0
- package/test/fixtures/cad/courtyard-house.json +208 -0
- package/test/go/cloudflare-probe.test.mjs +53 -0
- package/test/integration/app-command.test.mjs +55 -0
- package/test/integration/cad-discovery.test.mjs +158 -0
- package/test/integration/cad-docker-executor.test.mjs +249 -0
- package/test/integration/cad-execution.test.mjs +184 -0
- package/test/integration/cad-project-app.test.mjs +31 -0
- package/test/integration/cad-project-d1.test.mjs +25 -0
- package/test/integration/cad-project-native.test.mjs +24 -0
- package/test/integration/cad-project-workflow.test.mjs +9 -0
- package/test/integration/cad-project.test.mjs +82 -0
- package/test/integration/cli-help.test.mjs +4 -0
- package/test/integration/cms-site-tenancy-contract.test.mjs +256 -0
- package/test/integration/core-client.integration.test.mjs +55 -0
- package/test/integration/error-runtime-rpc.test.mjs +71 -0
- package/test/integration/features-resolve.test.mjs +128 -0
- package/test/integration/install-script.test.mjs +107 -0
- package/test/integration/inventory-core.test.mjs +53 -0
- package/test/integration/plugins-cloudflare.integration.test.mjs +28 -0
- package/test/integration/provider-env-cli.test.mjs +1 -1
- package/test/integration/runtime-migrations.test.mjs +6 -1
- package/test/integration/runtime-status.integration.test.mjs +211 -0
- package/test/integration/scaffold-cloudflare.test.mjs +46 -0
- package/test/integration/storage-architecture.test.mjs +154 -0
- package/test/integration/store-discovery.test.mjs +44 -0
- package/test/integration/terminal-exec.integration.test.mjs +86 -0
- package/test/integration/tool-runtime.test.mjs +124 -0
- package/test/integration/tool-schema.test.mjs +59 -0
- package/test/knowledge-service.test.mjs +4 -0
- package/test/local-sessions.test.mjs +11 -2
- package/test/local-studio-deploy.test.mjs +1 -1
- package/test/model-catalog.test.mjs +28 -1
- package/test/models.test.mjs +159 -5
- package/test/openai-responses.test.mjs +60 -0
- package/test/provider-credentials.test.mjs +15 -5
- package/test/responses-runner.test.mjs +321 -6
- package/test/sdk-worker-contract.test.mjs +5 -1
- package/test/shell.test.mjs +149 -4
- package/test/smoke.mjs +4 -3
- package/test/tools-search.test.mjs +33 -0
- package/test/unit/website-assets-bindings.test.mjs +60 -0
- package/test/whoami-resume.test.mjs +87 -7
- package/examples/gorilla-shell/README.md +0 -15
- package/src/lib/gorilla-template.js +0 -54
- package/src/lib/prompt-byok.js +0 -57
- package/src/lib/save-sdk-token.js +0 -19
- package/templates/gorilla-shell/App.tsx +0 -639
- package/templates/gorilla-shell/README.md +0 -34
- package/templates/gorilla-shell/index.html +0 -15
- package/templates/gorilla-shell/main.jsx +0 -9
- package/templates/gorilla-shell/package.json +0 -19
- package/templates/gorilla-shell/vite.config.js +0 -21
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import http from 'node:http';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_CAD_DOCKER_PORT,
|
|
9
|
+
DEFAULT_CAD_DOCKER_URL,
|
|
10
|
+
resolveDockerServiceConfig,
|
|
11
|
+
probeDockerServiceHealth,
|
|
12
|
+
executeOpenScadDocker,
|
|
13
|
+
executeFreeCadDocker,
|
|
14
|
+
executeBlenderDocker,
|
|
15
|
+
} from '../../src/lib/cad/docker-executor.js';
|
|
16
|
+
import { discoverAllCadTools } from '../../src/lib/cad/discovery.js';
|
|
17
|
+
|
|
18
|
+
test('resolveDockerServiceConfig resolves URL and token from env or options', () => {
|
|
19
|
+
const conf1 = resolveDockerServiceConfig({
|
|
20
|
+
serviceUrl: 'http://custom-host:9999/',
|
|
21
|
+
token: 'secret-token-123',
|
|
22
|
+
});
|
|
23
|
+
assert.equal(conf1.url, 'http://custom-host:9999');
|
|
24
|
+
assert.equal(conf1.token, 'secret-token-123');
|
|
25
|
+
|
|
26
|
+
const conf2 = resolveDockerServiceConfig({
|
|
27
|
+
env: {
|
|
28
|
+
AGENTSAM_CAD_SERVICE_URL: 'http://env-host:8888',
|
|
29
|
+
AGENTSAM_CAD_TOKEN: 'env-tok',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
assert.equal(conf2.url, 'http://env-host:8888');
|
|
33
|
+
assert.equal(conf2.token, 'env-tok');
|
|
34
|
+
|
|
35
|
+
const confDefault = resolveDockerServiceConfig({ env: {} });
|
|
36
|
+
assert.equal(confDefault.url, DEFAULT_CAD_DOCKER_URL);
|
|
37
|
+
assert.equal(confDefault.token, null);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('probeDockerServiceHealth returns offline gracefully when unreachable', async () => {
|
|
41
|
+
const result = await probeDockerServiceHealth({
|
|
42
|
+
serviceUrl: 'http://127.0.0.1:54321', // Unused port
|
|
43
|
+
timeoutMs: 150,
|
|
44
|
+
});
|
|
45
|
+
assert.equal(result.available, false);
|
|
46
|
+
assert.equal(result.url, 'http://127.0.0.1:54321');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('Docker CAD service executor routes compile, build, and inspect requests', async () => {
|
|
50
|
+
const recordedRequests = [];
|
|
51
|
+
const mockServer = http.createServer((req, res) => {
|
|
52
|
+
let body = '';
|
|
53
|
+
req.on('data', chunk => { body += chunk; });
|
|
54
|
+
req.on('end', () => {
|
|
55
|
+
recordedRequests.push({
|
|
56
|
+
url: req.url,
|
|
57
|
+
method: req.method,
|
|
58
|
+
auth: req.headers['authorization'],
|
|
59
|
+
body: body ? JSON.parse(body) : null,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (req.url === '/healthz') {
|
|
63
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
64
|
+
res.end(JSON.stringify({
|
|
65
|
+
ok: true,
|
|
66
|
+
service: 'agentsam-cad',
|
|
67
|
+
version: '1',
|
|
68
|
+
tools: {
|
|
69
|
+
openscad: { installed: true, version: 'OpenSCAD 2021.01-docker' },
|
|
70
|
+
freecad: { installed: true, version: 'FreeCAD 0.21.2-docker' },
|
|
71
|
+
blender: { installed: true, version: 'Blender 4.2.0-docker' },
|
|
72
|
+
},
|
|
73
|
+
}));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (req.url === '/v1/openscad/compile') {
|
|
78
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
79
|
+
res.end(JSON.stringify({
|
|
80
|
+
ok: true,
|
|
81
|
+
tool: 'openscad',
|
|
82
|
+
format: 'stl',
|
|
83
|
+
filename: 'bracket.stl',
|
|
84
|
+
size_bytes: 128,
|
|
85
|
+
sha256: 'abc123sha',
|
|
86
|
+
artifact_base64: Buffer.from('solid bracket\nendsolid').toString('base64'),
|
|
87
|
+
logs: ['[DOCKER] OpenSCAD compiled successfully'],
|
|
88
|
+
}));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (req.url === '/v1/freecad/execute') {
|
|
93
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
94
|
+
res.end(JSON.stringify({
|
|
95
|
+
ok: true,
|
|
96
|
+
tool: 'freecad',
|
|
97
|
+
format: 'step',
|
|
98
|
+
filename: 'part.step',
|
|
99
|
+
size_bytes: 512,
|
|
100
|
+
sha256: 'step123sha',
|
|
101
|
+
artifact_base64: Buffer.from('ISO-10303-21;').toString('base64'),
|
|
102
|
+
metrics: { volume: 1500, surface_area: 800, faces_count: 6 },
|
|
103
|
+
logs: ['[DOCKER] FreeCAD built STEP model'],
|
|
104
|
+
}));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (req.url === '/v1/blender/execute') {
|
|
109
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
110
|
+
res.end(JSON.stringify({
|
|
111
|
+
ok: true,
|
|
112
|
+
tool: 'blender',
|
|
113
|
+
operation: 'build',
|
|
114
|
+
format: 'glb',
|
|
115
|
+
filename: 'scene.glb',
|
|
116
|
+
size_bytes: 1024,
|
|
117
|
+
sha256: 'glb123sha',
|
|
118
|
+
artifact_base64: Buffer.from('glTF binary mock').toString('base64'),
|
|
119
|
+
result: { ok: true, blender_version: '4.2.0', objects: ['Cube', 'Camera'] },
|
|
120
|
+
logs: ['[DOCKER] Blender rendered glb scene'],
|
|
121
|
+
}));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
126
|
+
res.end(JSON.stringify({ ok: false, error: 'not_found' }));
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await new Promise((resolve) => mockServer.listen(0, '127.0.0.1', resolve));
|
|
131
|
+
const port = mockServer.address().port;
|
|
132
|
+
const serviceUrl = `http://127.0.0.1:${port}`;
|
|
133
|
+
const token = 'test-token-xyz';
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// 1. Health probe
|
|
137
|
+
const health = await probeDockerServiceHealth({ serviceUrl, token });
|
|
138
|
+
assert.equal(health.available, true);
|
|
139
|
+
assert.equal(health.tools.openscad.installed, true);
|
|
140
|
+
assert.equal(health.tools.freecad.installed, true);
|
|
141
|
+
assert.equal(health.tools.blender.installed, true);
|
|
142
|
+
|
|
143
|
+
// 2. OpenSCAD execution
|
|
144
|
+
const oscadRes = await executeOpenScadDocker({
|
|
145
|
+
source: 'cube([10, 10, 10]);',
|
|
146
|
+
outputFormat: 'stl',
|
|
147
|
+
filename: 'bracket.stl',
|
|
148
|
+
serviceUrl,
|
|
149
|
+
token,
|
|
150
|
+
});
|
|
151
|
+
assert.equal(oscadRes.success, true);
|
|
152
|
+
assert.equal(oscadRes.filename, 'bracket.stl');
|
|
153
|
+
assert.match(oscadRes.artifactContent, /solid bracket/);
|
|
154
|
+
assert.equal(oscadRes.mimeType, 'model/stl');
|
|
155
|
+
|
|
156
|
+
// 3. FreeCAD execution
|
|
157
|
+
const freecadRes = await executeFreeCadDocker({
|
|
158
|
+
operations: [{ op: 'box', length: 10, width: 10, height: 10 }],
|
|
159
|
+
format: 'step',
|
|
160
|
+
filename: 'part.step',
|
|
161
|
+
serviceUrl,
|
|
162
|
+
token,
|
|
163
|
+
});
|
|
164
|
+
assert.equal(freecadRes.success, true);
|
|
165
|
+
assert.equal(freecadRes.format, 'step');
|
|
166
|
+
assert.equal(freecadRes.metrics.volume, 1500);
|
|
167
|
+
assert.match(freecadRes.artifactText, /ISO-10303-21;/);
|
|
168
|
+
|
|
169
|
+
// 4. Blender execution
|
|
170
|
+
const blenderRes = await executeBlenderDocker({
|
|
171
|
+
operation: 'build',
|
|
172
|
+
operations: [{ op: 'add', primitive: 'cube' }],
|
|
173
|
+
format: 'glb',
|
|
174
|
+
serviceUrl,
|
|
175
|
+
token,
|
|
176
|
+
});
|
|
177
|
+
assert.equal(blenderRes.success, true);
|
|
178
|
+
assert.equal(blenderRes.format, 'glb');
|
|
179
|
+
assert.equal(blenderRes.result.blender_version, '4.2.0');
|
|
180
|
+
|
|
181
|
+
// 5. Verify recorded request signatures
|
|
182
|
+
assert.equal(recordedRequests[0].url, '/healthz');
|
|
183
|
+
assert.equal(recordedRequests[0].auth, 'Bearer test-token-xyz');
|
|
184
|
+
assert.equal(recordedRequests[1].url, '/v1/openscad/compile');
|
|
185
|
+
assert.equal(recordedRequests[2].url, '/v1/freecad/execute');
|
|
186
|
+
assert.equal(recordedRequests[3].url, '/v1/blender/execute');
|
|
187
|
+
} finally {
|
|
188
|
+
await new Promise((resolve) => mockServer.close(resolve));
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('discoverAllCadTools adopts Docker service execution lane when host tools are absent', async () => {
|
|
193
|
+
const mockServer = http.createServer((req, res) => {
|
|
194
|
+
if (req.url === '/healthz') {
|
|
195
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
196
|
+
res.end(JSON.stringify({
|
|
197
|
+
ok: true,
|
|
198
|
+
service: 'agentsam-cad',
|
|
199
|
+
version: '1',
|
|
200
|
+
tools: {
|
|
201
|
+
openscad: { installed: true, version: 'OpenSCAD 2021.01-docker' },
|
|
202
|
+
freecad: { installed: true, version: 'FreeCAD 0.21.2-docker' },
|
|
203
|
+
blender: { installed: true, version: 'Blender 4.2.0-docker' },
|
|
204
|
+
},
|
|
205
|
+
}));
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
res.writeHead(404);
|
|
209
|
+
res.end();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
await new Promise((resolve) => mockServer.listen(0, '127.0.0.1', resolve));
|
|
213
|
+
const port = mockServer.address().port;
|
|
214
|
+
const serviceUrl = `http://127.0.0.1:${port}`;
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const report = await discoverAllCadTools({
|
|
218
|
+
openscadBin: '/nonexistent/openscad',
|
|
219
|
+
freecadBin: '/nonexistent/freecad',
|
|
220
|
+
blenderBin: '/nonexistent/blender',
|
|
221
|
+
env: {
|
|
222
|
+
AGENTSAM_CAD_SERVICE_URL: serviceUrl,
|
|
223
|
+
PATH: '/empty',
|
|
224
|
+
},
|
|
225
|
+
cache: false,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const openscad = report.tools.find(t => t.tool === 'openscad');
|
|
229
|
+
const freecad = report.tools.find(t => t.tool === 'freecad');
|
|
230
|
+
const blender = report.tools.find(t => t.tool === 'blender');
|
|
231
|
+
|
|
232
|
+
assert.equal(openscad.available, true);
|
|
233
|
+
assert.equal(openscad.execution_lane, 'docker_service');
|
|
234
|
+
assert.equal(openscad.source, 'docker_service');
|
|
235
|
+
assert.match(openscad.binary, /^docker:\/\//);
|
|
236
|
+
|
|
237
|
+
assert.equal(freecad.available, true);
|
|
238
|
+
assert.equal(freecad.execution_lane, 'docker_service');
|
|
239
|
+
assert.equal(freecad.source, 'docker_service');
|
|
240
|
+
assert.match(freecad.binary, /^docker:\/\//);
|
|
241
|
+
|
|
242
|
+
assert.equal(blender.available, true);
|
|
243
|
+
assert.equal(blender.execution_lane, 'docker_service');
|
|
244
|
+
assert.equal(blender.source, 'docker_service');
|
|
245
|
+
assert.match(blender.binary, /^docker:\/\//);
|
|
246
|
+
} finally {
|
|
247
|
+
await new Promise((resolve) => mockServer.close(resolve));
|
|
248
|
+
}
|
|
249
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {
|
|
7
|
+
openScadCompile,
|
|
8
|
+
validateOpenScadSource,
|
|
9
|
+
freeCadBuild,
|
|
10
|
+
freeCadInspect,
|
|
11
|
+
blenderBuild,
|
|
12
|
+
blenderInspect,
|
|
13
|
+
blenderExport,
|
|
14
|
+
discoverAllCadTools,
|
|
15
|
+
} from '../../src/lib/cad/index.js';
|
|
16
|
+
|
|
17
|
+
// These tests exercise real local CAD/3D engine binaries (OpenSCAD, FreeCAD,
|
|
18
|
+
// Blender). They are not installed on the generic CI runner and are heavy
|
|
19
|
+
// desktop applications — deliberately not force-installed there (matches
|
|
20
|
+
// the local-first storage/tooling stance elsewhere in this repo). Each
|
|
21
|
+
// execution test below skips itself with a clear reason when its engine
|
|
22
|
+
// isn't present, rather than failing CI for an environment gap. On a
|
|
23
|
+
// machine that has the tool installed (e.g. local dev), the test runs for
|
|
24
|
+
// real with the full original assertions, unchanged.
|
|
25
|
+
const cadReport = await discoverAllCadTools();
|
|
26
|
+
const toolAvailable = (name) => cadReport.tools.find((t) => t.tool === name)?.available === true;
|
|
27
|
+
|
|
28
|
+
test('CAD Tool Discovery identifies local engines on the machine (schema is always validated; per-tool availability is informational)', async () => {
|
|
29
|
+
const report = await discoverAllCadTools();
|
|
30
|
+
assert.equal(report.schema_version, 1);
|
|
31
|
+
assert.ok(report.total_tools >= 5);
|
|
32
|
+
|
|
33
|
+
for (const toolName of ['openscad', 'freecad', 'blender']) {
|
|
34
|
+
const tool = report.tools.find((t) => t.tool === toolName);
|
|
35
|
+
assert.ok(tool, `${toolName} entry missing from discovery report`);
|
|
36
|
+
assert.equal(typeof tool.available, 'boolean');
|
|
37
|
+
if (tool.available) {
|
|
38
|
+
assert.ok(tool.binary, `${toolName} reported available but has no binary path`);
|
|
39
|
+
assert.ok(tool.version, `${toolName} reported available but has no version`);
|
|
40
|
+
} else {
|
|
41
|
+
assert.ok(tool.install_guidance, `${toolName} reported unavailable but has no install_guidance`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('OpenSCAD execution rejects security-violating scripts', async () => {
|
|
47
|
+
assert.throws(
|
|
48
|
+
() => validateOpenScadSource('use <child_process>;'),
|
|
49
|
+
/Security Violation/
|
|
50
|
+
);
|
|
51
|
+
assert.throws(
|
|
52
|
+
() => validateOpenScadSource('exec("cat /etc/passwd");'),
|
|
53
|
+
/Security Violation/
|
|
54
|
+
);
|
|
55
|
+
assert.throws(
|
|
56
|
+
() => validateOpenScadSource('import("../../secret.scad");'),
|
|
57
|
+
/Security Violation/
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('OpenSCAD real compilation produces watertight geometry and returns typed receipt', { skip: !toolAvailable('openscad') && 'openscad binary not available on this machine' }, async (t) => {
|
|
62
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-openscad-test-'));
|
|
63
|
+
t.after(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
64
|
+
|
|
65
|
+
const source = `
|
|
66
|
+
difference() {
|
|
67
|
+
cube([width, depth, height], center=true);
|
|
68
|
+
cylinder(h=height + 2, r=holeRadius, center=true, $fn=24);
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
const result = await openScadCompile({
|
|
73
|
+
source,
|
|
74
|
+
outputFormat: 'stl',
|
|
75
|
+
parameters: {
|
|
76
|
+
width: 30,
|
|
77
|
+
depth: 20,
|
|
78
|
+
height: 10,
|
|
79
|
+
holeRadius: 4,
|
|
80
|
+
},
|
|
81
|
+
filename: 'flange.stl',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
assert.equal(result.success, true);
|
|
85
|
+
assert.equal(result.engine, 'openscad-native');
|
|
86
|
+
assert.equal(result.filename, 'flange.stl');
|
|
87
|
+
assert.equal(result.outputFormat, 'stl');
|
|
88
|
+
assert.equal(result.mimeType, 'model/stl');
|
|
89
|
+
assert.ok(result.sizeBytes > 0);
|
|
90
|
+
assert.ok(result.artifactContent.includes('solid OpenSCAD_Model'));
|
|
91
|
+
assert.ok(result.artifactContent.includes('facet normal'));
|
|
92
|
+
assert.ok(result.artifactContent.includes('outer loop'));
|
|
93
|
+
assert.ok(result.logs.some(l => l.includes('[KERNEL] OpenSCAD Native Compiler')));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('FreeCAD solid kernel executes precision B-Rep operations and exports valid ISO STEP', { skip: !toolAvailable('freecad') && 'freecad binary not available on this machine' }, async (t) => {
|
|
97
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-freecad-test-'));
|
|
98
|
+
t.after(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
99
|
+
|
|
100
|
+
const stepOutput = path.join(tmpDir, 'bracket.step');
|
|
101
|
+
|
|
102
|
+
const recipe = {
|
|
103
|
+
operations: [
|
|
104
|
+
{ op: 'box', name: 'main_block', length: 40.0, width: 25.0, height: 12.0 },
|
|
105
|
+
{ op: 'cylinder', name: 'bore', radius: 5.0, height: 14.0, translate: [20.0, 12.5, -1.0] },
|
|
106
|
+
{ op: 'cut', base: 'main_block', tool: 'bore' },
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const buildResult = await freeCadBuild({
|
|
111
|
+
recipe,
|
|
112
|
+
output: stepOutput,
|
|
113
|
+
format: 'step',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
assert.equal(buildResult.ok, true);
|
|
117
|
+
assert.equal(buildResult.capability, 'freecad.build');
|
|
118
|
+
assert.equal(buildResult.format, 'step');
|
|
119
|
+
assert.ok(buildResult.sizeBytes > 0);
|
|
120
|
+
assert.ok(fs.existsSync(stepOutput));
|
|
121
|
+
|
|
122
|
+
// Verify solid shape metrics
|
|
123
|
+
const expectedSolidVolume = 40.0 * 25.0 * 12.0 - Math.PI * 25.0 * 12.0;
|
|
124
|
+
assert.ok(Math.abs(buildResult.metrics.volume - expectedSolidVolume) < 1.0, 'Volume matches geometry');
|
|
125
|
+
assert.equal(buildResult.metrics.faces_count, 7);
|
|
126
|
+
|
|
127
|
+
// Read raw STEP content to verify ISO-10303 signature
|
|
128
|
+
const stepText = fs.readFileSync(stepOutput, 'utf8');
|
|
129
|
+
assert.ok(stepText.includes('ISO-10303-21;'));
|
|
130
|
+
assert.ok(stepText.includes('Open CASCADE STEP processor'));
|
|
131
|
+
|
|
132
|
+
// Inspect the exported STEP file using FreeCAD inspection
|
|
133
|
+
const inspectResult = await freeCadInspect({ input: stepOutput });
|
|
134
|
+
assert.equal(inspectResult.ok, true);
|
|
135
|
+
assert.equal(inspectResult.capability, 'freecad.inspect');
|
|
136
|
+
assert.ok(Math.abs(inspectResult.metrics.volume - expectedSolidVolume) < 1.0);
|
|
137
|
+
assert.equal(inspectResult.metrics.faces_count, 7);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('Blender real execution builds, inspects, and exports production 3D assets', { skip: !toolAvailable('blender') && 'blender binary not available on this machine' }, async (t) => {
|
|
141
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-blender-test-'));
|
|
142
|
+
t.after(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
143
|
+
|
|
144
|
+
const blendPath = path.join(tmpDir, 'scene.blend');
|
|
145
|
+
const glbPath = path.join(tmpDir, 'scene.glb');
|
|
146
|
+
|
|
147
|
+
const recipe = {
|
|
148
|
+
schema_version: 1,
|
|
149
|
+
operations: [
|
|
150
|
+
{ op: 'clear_scene' },
|
|
151
|
+
{ op: 'add', primitive: 'cylinder', name: 'Pillar', radius: 1.5, depth: 4.0 },
|
|
152
|
+
{ op: 'add_light', light_type: 'SUN', energy: 2.5 },
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// 1. Build .blend file
|
|
157
|
+
const buildResult = await blenderBuild({
|
|
158
|
+
recipe,
|
|
159
|
+
output: blendPath,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
assert.equal(buildResult.ok, true);
|
|
163
|
+
assert.equal(buildResult.capability, 'blender.build');
|
|
164
|
+
assert.ok(fs.existsSync(blendPath));
|
|
165
|
+
assert.ok(buildResult.objects.includes('Pillar'));
|
|
166
|
+
|
|
167
|
+
// 2. Inspect built scene
|
|
168
|
+
const inspectResult = await blenderInspect({ input: blendPath });
|
|
169
|
+
assert.equal(inspectResult.ok, true);
|
|
170
|
+
assert.equal(inspectResult.capability, 'blender.inspect');
|
|
171
|
+
assert.ok(inspectResult.scene.objects.some(o => o.name === 'Pillar'));
|
|
172
|
+
|
|
173
|
+
// 3. Export to GLB
|
|
174
|
+
const exportResult = await blenderExport({
|
|
175
|
+
input: blendPath,
|
|
176
|
+
output: glbPath,
|
|
177
|
+
format: 'glb',
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
assert.equal(exportResult.ok, true);
|
|
181
|
+
assert.equal(exportResult.capability, 'blender.export');
|
|
182
|
+
assert.ok(fs.existsSync(glbPath));
|
|
183
|
+
assert.ok(fs.statSync(glbPath).size > 0);
|
|
184
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {spawn} from 'node:child_process';
|
|
7
|
+
import {localProjectRuntime} from '../../src/lib/cad/project-cli.js';
|
|
8
|
+
test('packaged app API shares CLI revisions, emits events and rejects stale writes',{skip:process.env.AGENTSAM_TEST_CAD_APP!=='1'},async t=>{
|
|
9
|
+
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cad-app-proof-')),port=31987;
|
|
10
|
+
const app=new URL('../../apps/cad-creator/backend/',import.meta.url);
|
|
11
|
+
const child=spawn(process.execPath,['dist/server.mjs'],{cwd:app,env:{...process.env,NODE_ENV:'production',PORT:String(port),HOST:'127.0.0.1',AGENTSAM_CAD_PROJECT_ROOT:root},stdio:['ignore','pipe','pipe']});
|
|
12
|
+
let log='';child.stdout.on('data',d=>log+=d);child.stderr.on('data',d=>log+=d);
|
|
13
|
+
t.after(()=>{child.kill();fs.rmSync(root,{recursive:true,force:true});});
|
|
14
|
+
const base='http://127.0.0.1:'+port;
|
|
15
|
+
let ready=false;
|
|
16
|
+
for(let i=0;i<80;i++){try{const r=await fetch(base);if(r.ok){ready=true;break;}}catch{}await new Promise(r=>setTimeout(r,100));}
|
|
17
|
+
assert.ok(ready,log);
|
|
18
|
+
async function call(name,input){const res=await fetch(base+'/api/cad/project/tools/'+name,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(input)});return {status:res.status,body:await res.json()};}
|
|
19
|
+
const project=JSON.parse(fs.readFileSync(new URL('../fixtures/cad/courtyard-house.json',import.meta.url),'utf8'));
|
|
20
|
+
const saved=await call('design_project_save',{project,expected_revision:0});assert.equal(saved.status,200);
|
|
21
|
+
const runtime=localProjectRuntime(root);assert.equal((await runtime.execute('design_project_get',{project_id:project.id})).content_hash,saved.body.result.content_hash);
|
|
22
|
+
const abort=new AbortController();t.after(()=>abort.abort());
|
|
23
|
+
const events=await fetch(base+'/api/cad/project/events',{signal:abort.signal});const reader=events.body.getReader();
|
|
24
|
+
const edited=await runtime.execute('design_apply_operation',{project_id:project.id,expected_revision:1,operation:{type:'update_wall',wallId:'wall-0',updates:{height3D:132}}});
|
|
25
|
+
const event=await Promise.race([reader.read(),new Promise((_,reject)=>setTimeout(()=>reject(new Error('event timeout')),5000))]);
|
|
26
|
+
assert.match(new TextDecoder().decode(event.value),/"revision":2/);
|
|
27
|
+
const reopened=await call('design_project_get',{project_id:project.id,expected_content_hash:edited.content_hash});assert.equal(reopened.body.result.revision,2);
|
|
28
|
+
assert.equal((await call('design_project_save',{project,expected_revision:1})).status,409);
|
|
29
|
+
const denied=await fetch(base+'/api/cad/project/tools/design_project_get',{method:'POST',headers:{'content-type':'application/json',origin:'https://other.example'},body:JSON.stringify({project_id:project.id})});assert.equal(denied.status,403);
|
|
30
|
+
abort.abort();
|
|
31
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import {DatabaseSync} from 'node:sqlite';
|
|
5
|
+
import {D1ProjectStore} from '../../src/lib/cad/d1-project-store.js';
|
|
6
|
+
const schema="CREATE TABLE designstudio_design_blueprints (\n id TEXT PRIMARY KEY DEFAULT ('dsb_' || lower(hex(randomblob(8)))),\n\n tenant_id TEXT NOT NULL DEFAULT 'tenant_inneranimalmedia',\n workspace_id TEXT NOT NULL DEFAULT 'ws_inneranimalmedia',\n project_id TEXT,\n\n title TEXT NOT NULL,\n description TEXT,\n original_prompt TEXT,\n\n intent_json TEXT NOT NULL DEFAULT '{}',\n\n cad_script TEXT,\n cad_engine TEXT DEFAULT 'openscad',\n\n sketch_json TEXT DEFAULT '{}',\n preview_image_url TEXT,\n\n generation_config_json TEXT DEFAULT '{}',\n\n latest_asset_id TEXT,\n latest_run_id TEXT,\n\n status TEXT NOT NULL DEFAULT 'draft'\n CHECK (status IN (\n 'draft',\n 'structured',\n 'generated',\n 'validated',\n 'exported',\n 'failed'\n )),\n\n quality_score REAL DEFAULT 0,\n success_rate REAL DEFAULT 0,\n\n tags TEXT DEFAULT '[]',\n notes TEXT,\n\n total_tokens INTEGER DEFAULT 0,\n total_cost_usd REAL DEFAULT 0,\n avg_latency_ms REAL DEFAULT 0,\n\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n, preview_svg_url TEXT);\nCREATE TABLE \"scene_snapshots\" (\n id TEXT PRIMARY KEY DEFAULT ('scene_' || lower(hex(randomblob(8)))),\n workspace_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n tenant_id TEXT NOT NULL DEFAULT 'tenant_sam_primeaux',\n name TEXT NOT NULL DEFAULT 'Untitled Scene',\n project_type TEXT NOT NULL DEFAULT 'SANDBOX',\n entity_count INTEGER NOT NULL DEFAULT 0,\n r2_key TEXT NOT NULL,\n r2_bucket TEXT NOT NULL DEFAULT 'inneranimalmedia',\n public_url TEXT,\n thumbnail_r2_key TEXT,\n thumbnail_url TEXT,\n tags TEXT DEFAULT '[]',\n description TEXT,\n is_autosave INTEGER NOT NULL DEFAULT 0,\n version INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL DEFAULT (unixepoch()),\n updated_at INTEGER NOT NULL DEFAULT (unixepoch())\n, project_id TEXT, glb_r2_key TEXT, style_preset TEXT, voxel_count INTEGER, cad_job_id TEXT);";
|
|
7
|
+
const project=JSON.parse(fs.readFileSync(new URL('../fixtures/cad/courtyard-house.json',import.meta.url),'utf8'));
|
|
8
|
+
test('existing D1 tables support scoped immutable revisions and optimistic concurrency',async t=>{
|
|
9
|
+
const sqlite=new DatabaseSync(':memory:');t.after(()=>sqlite.close());sqlite.exec(schema);
|
|
10
|
+
const db={prepare(sql){return {bind(...params){return {sql,params,async first(){return sqlite.prepare(sql).get(...params)||null;}};}};},
|
|
11
|
+
async batch(statements){sqlite.exec('BEGIN');try{const results=statements.map(s=>({meta:{changes:Number(sqlite.prepare(s.sql).run(...s.params).changes)}}));sqlite.exec('COMMIT');return results;}catch(e){sqlite.exec('ROLLBACK');throw e;}}};
|
|
12
|
+
const blobs=new Map(),objects={async put(k,v){blobs.set(k,v);},async get(k){return blobs.has(k)?{async text(){return blobs.get(k);}}:null;}};
|
|
13
|
+
const scope={account_id:'test-owner',workspace_id:'test-workspace',tenant_id:'test-tenant'};
|
|
14
|
+
const store=new D1ProjectStore({db,objects,bucket:'test-bucket',scope});
|
|
15
|
+
await store.save(project,0);
|
|
16
|
+
await store.save({...project,name:'Revision two'},1);
|
|
17
|
+
await assert.rejects(store.save(project,1),/revision_conflict/);
|
|
18
|
+
assert.equal(sqlite.prepare('SELECT count(*) n FROM scene_snapshots').get().n,2);
|
|
19
|
+
assert.equal((await store.read(project.id,1)).project.name,project.name);
|
|
20
|
+
assert.equal((await store.restore(project.id,1,2)).revision,3);
|
|
21
|
+
const other=new D1ProjectStore({db,objects,bucket:'test-bucket',scope:{...scope,account_id:'other'}});
|
|
22
|
+
await assert.rejects(other.read(project.id),/project_not_found/);
|
|
23
|
+
await assert.rejects(other.save(project,3),/revision_conflict/);
|
|
24
|
+
assert.equal((await store.read(project.id)).revision,3);
|
|
25
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {localProjectRuntime} from '../../src/lib/cad/project-cli.js';
|
|
7
|
+
const enabled=process.env.AGENTSAM_TEST_NATIVE_CAD==='1';
|
|
8
|
+
test('courtyard real build inspect export render and restored revision rebuild',{skip:!enabled&&'Set AGENTSAM_TEST_NATIVE_CAD=1; enabled runs must find Blender'},async t=>{
|
|
9
|
+
const root=process.env.AGENTSAM_CAD_TEST_OUTPUT||fs.mkdtempSync(path.join(os.tmpdir(),'cad-house-native-'));
|
|
10
|
+
if(!process.env.AGENTSAM_CAD_TEST_OUTPUT)t.after(()=>fs.rmSync(root,{recursive:true,force:true}));
|
|
11
|
+
const p=JSON.parse(fs.readFileSync(new URL('../fixtures/cad/courtyard-house.json',import.meta.url),'utf8'));
|
|
12
|
+
p.id='courtyard-'+Date.now();
|
|
13
|
+
const run=localProjectRuntime(root),saved=await run.execute('design_project_save',{project:p,expected_revision:0});
|
|
14
|
+
const input={project_id:p.id,revision:saved.revision};
|
|
15
|
+
const build=await run.execute('design_model_build',input);assert.ok(build.artifact.size_bytes>0);
|
|
16
|
+
const inspection=await run.execute('design_model_inspect',input);assert.ok(inspection.ok);
|
|
17
|
+
const glb=await run.execute('design_model_export',{...input,format:'glb'});assert.equal(fs.readFileSync(glb.artifact.path).subarray(0,4).toString(),'glTF');
|
|
18
|
+
const png=await run.execute('design_model_render',input);assert.equal(fs.readFileSync(png.artifact.path).subarray(1,4).toString(),'PNG');
|
|
19
|
+
const edited=await run.execute('design_apply_operation',{project_id:p.id,expected_revision:1,operation:{type:'update_wall',wallId:'wall-0',updates:{height3D:132}}});
|
|
20
|
+
assert.equal(edited.revision,2);
|
|
21
|
+
const restored=await run.execute('design_project_restore',{project_id:p.id,revision:1,expected_revision:2});assert.equal(restored.project.walls[0].height3D,120);
|
|
22
|
+
await run.execute('design_model_build',{project_id:p.id,revision:3});
|
|
23
|
+
console.log(JSON.stringify({root,project_id:p.id,build:build.artifact,glb:glb.artifact,png:png.artifact,restored_revision:3}));
|
|
24
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {runProjectWorkflow} from '../../src/lib/cad/project-workflows.js';
|
|
4
|
+
test('workflow halts on invalid validation and records no claimed effects',async()=>{
|
|
5
|
+
let calls=0;await assert.rejects(runProjectWorkflow({execute:async()=>{calls++;return {valid:false,errors:['bad wall']};}},'cad.house_baseline',{project:{}}),/project_invalid/);assert.equal(calls,1);
|
|
6
|
+
});
|
|
7
|
+
test('edit workflow refuses stale expected revision before edit',async()=>{
|
|
8
|
+
const calls=[];await assert.rejects(runProjectWorkflow({execute:async(name)=>{calls.push(name);return {revision:2,project:{id:'p'}};}},'cad.edit_preview',{project_id:'p',expected_revision:1,operation:{}}),/revision_conflict/);assert.deepEqual(calls,['design_project_get']);
|
|
9
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {validateProject,applyProjectOperation} from '../../src/lib/cad/project-contract.js';
|
|
7
|
+
import {FileProjectStore} from '../../src/lib/cad/project-store.js';
|
|
8
|
+
import {createProjectRuntime} from '../../src/lib/cad/project-runtime.js';
|
|
9
|
+
import {localProjectRuntime} from '../../src/lib/cad/project-cli.js';
|
|
10
|
+
import {projectToBlenderRecipe} from '../../src/lib/cad/project-recipe.js';
|
|
11
|
+
import {createCapabilityAdapter} from '../../src/agent/capability-adapter.js';
|
|
12
|
+
const project=JSON.parse(fs.readFileSync(new URL('../fixtures/cad/courtyard-house.json',import.meta.url),'utf8'));
|
|
13
|
+
test('invalid references and non-finite geometry fail before mutation',()=>{
|
|
14
|
+
const p=structuredClone(project);p.walls[0].x1=Infinity;assert.equal(validateProject(p).valid,false);
|
|
15
|
+
p.walls[0].x1=0;p.doors[0].wallId='missing';assert.equal(validateProject(p).valid,false);
|
|
16
|
+
assert.equal(validateProject({...p,walls:{}}).valid,false);
|
|
17
|
+
});
|
|
18
|
+
test('batch is atomic, absent targets fail, and host deletion cascades',()=>{
|
|
19
|
+
const before=JSON.stringify(project);
|
|
20
|
+
assert.throws(()=>applyProjectOperation(project,{type:'batch_operations',operations:[{type:'delete_wall',wallId:'wall-4'},{type:'update_wall',wallId:'absent',updates:{height3D:96}}]}),/element_not_found/);
|
|
21
|
+
assert.equal(JSON.stringify(project),before);
|
|
22
|
+
assert.equal(applyProjectOperation(project,{type:'delete_wall',wallId:'wall-4'}).doors.length,0);
|
|
23
|
+
});
|
|
24
|
+
test('save reopen restore and stale revision checks preserve immutable snapshots',async t=>{
|
|
25
|
+
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'cad-project-'));t.after(()=>fs.rmSync(dir,{recursive:true,force:true}));
|
|
26
|
+
const store=new FileProjectStore(dir);
|
|
27
|
+
await store.save(project,0);
|
|
28
|
+
const next=applyProjectOperation(project,{type:'update_wall',wallId:'wall-0',updates:{height3D:132}});
|
|
29
|
+
await store.save(next,1);
|
|
30
|
+
await assert.rejects(store.save(project,1),/revision_conflict/);
|
|
31
|
+
const restored=await new FileProjectStore(dir).restore(project.id,1,2);
|
|
32
|
+
assert.equal(restored.revision,3);assert.equal(restored.project.walls[0].height3D,120);assert.equal((await store.read(project.id,2)).project.walls[0].height3D,132);
|
|
33
|
+
});
|
|
34
|
+
test('native recipe converts inches to metres and preserves a real door gap',()=>{
|
|
35
|
+
const recipe=projectToBlenderRecipe(project);
|
|
36
|
+
assert.equal(recipe.operations.find(o=>o.name==='wall-0:0').scale[0],864*0.0254);
|
|
37
|
+
const pieces=recipe.operations.filter(o=>o.name?.startsWith('wall-4:'));
|
|
38
|
+
assert.equal(pieces.length,3);
|
|
39
|
+
assert.throws(()=>projectToBlenderRecipe({...project,roofType:'gable'}),/roof_geometry_not_supported/);
|
|
40
|
+
});
|
|
41
|
+
test('agent and runtime share executable CAD handlers',async t=>{
|
|
42
|
+
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cad-agent-'));t.after(()=>fs.rmSync(root,{recursive:true,force:true}));
|
|
43
|
+
const adapter=createCapabilityAdapter({projectRoot:root});
|
|
44
|
+
assert.ok(adapter.toolDescriptors().some(t=>t.name==='design_project_save'));
|
|
45
|
+
await adapter.invoke('design_project_save',{project,expected_revision:0});
|
|
46
|
+
const result=await adapter.invoke('design_apply_operation',{project_id:project.id,expected_revision:1,operation:{type:'update_wall',wallId:'wall-0',updates:{height3D:132}}});
|
|
47
|
+
assert.equal(result.result.revision,2);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
test('CAD registry projects the app into tools workflows and CLI graph relationships',()=>{
|
|
52
|
+
const manifest=JSON.parse(fs.readFileSync(new URL('../../registry/cad-project/manifest.json',import.meta.url),'utf8'));
|
|
53
|
+
const migration=fs.readFileSync(new URL('../../registry/cad-project/migration.sql',import.meta.url),'utf8');
|
|
54
|
+
assert.equal(manifest.product.slug,'agentsam-cad-creator');
|
|
55
|
+
assert.deepEqual(manifest.product.capability_keys,['design.read','design.write','design.export']);
|
|
56
|
+
assert.equal(manifest.product.cli_commands[0].id,'agentsam:cad:project');
|
|
57
|
+
assert.equal(manifest.tools.length,9);
|
|
58
|
+
assert.equal(manifest.workflows.length,2);
|
|
59
|
+
assert.match(migration,/WHERE slug='agentsam-cad-creator'/);
|
|
60
|
+
assert.equal((migration.match(/'exposes_tool'/g)||[]).length,9);
|
|
61
|
+
assert.equal((migration.match(/'provides_workflow'/g)||[]).length,2);
|
|
62
|
+
assert.equal((migration.match(/'exposes_command'/g)||[]).length,1);
|
|
63
|
+
assert.match(migration,/'cli_command','agentsam:cad:project'/);
|
|
64
|
+
assert.match(migration,/ON CONFLICT\(id\) DO UPDATE SET tool_name=excluded\.tool_name/);
|
|
65
|
+
assert.doesNotMatch(migration,/DO UPDATE SET[^;]*is_active=excluded\.is_active/);
|
|
66
|
+
assert.doesNotMatch(migration,/DO UPDATE SET[^;]*lifecycle=excluded\.lifecycle/);
|
|
67
|
+
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
test('CAD validate requires project or project_id and model-facing CAD tools declare non-strict rich payloads',async()=>{
|
|
72
|
+
const root=fs.mkdtempSync(path.join(os.tmpdir(),'cad-validate-source-'));
|
|
73
|
+
const runtime=localProjectRuntime(root);
|
|
74
|
+
await assert.rejects(()=>runtime.execute('design_project_validate',{}),/argument_required:project_or_project_id/);
|
|
75
|
+
const adapter=createCapabilityAdapter({projectRoot:root});
|
|
76
|
+
const save=adapter.toolDescriptors().find(t=>t.name==='design_project_save');
|
|
77
|
+
const edit=adapter.toolDescriptors().find(t=>t.name==='design_apply_operation');
|
|
78
|
+
assert.equal(save.strict,false);
|
|
79
|
+
assert.equal(edit.strict,false);
|
|
80
|
+
assert.equal(save.input_schema.properties.project.additionalProperties,true);
|
|
81
|
+
assert.equal(edit.input_schema.properties.operation.additionalProperties,true);
|
|
82
|
+
});
|
|
@@ -30,6 +30,10 @@ test('agentsam help is concise, topic-aware, and deterministic outside a TTY', (
|
|
|
30
30
|
assert.match(all.stdout, /Build \/ inspect/);
|
|
31
31
|
assert.match(all.stdout, /Create \/ extend/);
|
|
32
32
|
|
|
33
|
+
const allAlias = run('help', 'all');
|
|
34
|
+
assert.equal(allAlias.status, 0, allAlias.stderr);
|
|
35
|
+
assert.equal(allAlias.stdout, all.stdout);
|
|
36
|
+
|
|
33
37
|
const flag = run('--help');
|
|
34
38
|
assert.equal(flag.status, 0, flag.stderr);
|
|
35
39
|
assert.match(flag.stdout, /agentsam help <topic>/);
|