@agenthippo-ai/agenthippo 1.110.9 → 1.110.19
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/out/agentpack/types.js +2 -5
- package/out/analytics/dashboard/artifactManager.js +2 -439
- package/out/analytics/dashboard/fleetDisplayUtils.js +2 -162
- package/out/analytics/dashboard/fleetOrchestrator.js +3 -142
- package/out/analytics/dashboard/fleetRegistry.js +2 -79
- package/out/analytics/dashboard/fleetRowDefaults.js +2 -87
- package/out/analytics/dashboard/fleetScriptAdapter.js +2 -146
- package/out/analytics/dashboard/fleetTypes.js +2 -29
- package/out/analytics/dashboard/index.js +2 -17
- package/out/analytics/dashboard/indexingService.js +2 -422
- package/out/analytics/dashboard/localFleetAdapter.js +2 -222
- package/out/analytics/dashboard/performanceMonitor.js +5 -76
- package/out/analytics/dashboard/processManager.js +2 -209
- package/out/analytics/dashboard/provider.js +38 -3817
- package/out/analytics/dashboard/queryRunner.js +62 -750
- package/out/analytics/dashboard/scheduledFleetStorage.js +2 -241
- package/out/analytics/dashboard/sessionJsonlFleet.js +3 -204
- package/out/analytics/dashboard/teamViewDiscovery.js +2 -106
- package/out/analytics/dashboard/webview/panel.js +34 -2318
- package/out/analytics/dashboard/webview/runtime-bridge.js +2 -82
- package/out/analytics/lifecycle.js +2 -43
- package/out/analytics/service.js +8 -1277
- package/out/auth/authBaseUrl.js +2 -59
- package/out/auth/bootstrapApply.js +2 -71
- package/out/auth/brokerClient.js +2 -160
- package/out/auth/callbackUri.js +2 -59
- package/out/auth/githubStoreGate.js +2 -70
- package/out/auth/loopbackAuthServer.js +4 -159
- package/out/auth/onboardingPrompt.js +2 -112
- package/out/auth/pkce.js +2 -9
- package/out/auth/provider.js +2 -226
- package/out/auth/sessionStore.js +2 -156
- package/out/auth/signInFlow.js +2 -183
- package/out/cli/index.js +619 -551
- package/out/cli/run-custom-engine-runner.js +3 -318
- package/out/cli/vscode-shim.js +2 -86
- package/out/engines/factory.js +2 -45
- package/out/engines/types.js +2 -17
- package/out/examples/run-headless-repl.js +6 -286
- package/out/examples/run-headless.js +2 -187
- package/out/extension.js +32 -4473
- package/out/gateway/gatewayRouteIndex.js +6 -371
- package/out/gateway/gatewaySessionStore.js +3 -243
- package/out/gateway/index.js +2 -31
- package/out/gateway/resetMappings.js +2 -52
- package/out/gateway/service.js +5 -1249
- package/out/mcp/publish.js +2 -143
- package/out/remote/handleRemoteChat.js +2 -169
- package/out/remote/remotePackStatusBar.js +3 -103
- package/out/remote/remoteTrust.js +6 -277
- package/out/remote/serveClient.js +4 -277
- package/out/remoteSkills.js +3 -191
- package/out/runtime/index.js +2 -32
- package/out/runtime/vscode/index.js +2 -20
- package/out/runtime/vscode/vscode-config-provider.js +2 -77
- package/out/runtime/vscode/vscode-edit-tracker.js +2 -122
- package/out/runtime/vscode/vscode-logger.js +2 -30
- package/out/runtime/vscode/vscode-permission-handler.js +2 -72
- package/out/scripts/agentide-litellm.sh +0 -1
- package/out/scripts/agentide_litellm_hooks_v1.py +170 -418
- package/out/scripts/analytics/agent-fleet-widget.js +18 -257
- package/out/scripts/analytics/agentide-litellm-otel.config.yaml +4 -4
- package/out/scripts/analytics/duckdbLocator.js +2 -28
- package/out/scripts/analytics/generate-default-charts.py +43 -323
- package/out/scripts/analytics/hotspot-analysis-widget.js +4 -430
- package/out/scripts/analytics/index-traces.py +78 -1036
- package/out/scripts/analytics/session-comparison-widget.js +26 -472
- package/out/scripts/analytics/session-timeline-widget.js +25 -846
- package/out/scripts/analytics/setup-analytics.ps1 +8 -1
- package/out/scripts/analytics/setup-analytics.sh +13 -2
- package/out/scripts/analytics/token-cost-flamegraph-widget.js +4 -461
- package/out/scripts/analytics/token-cost-trend-widget.js +4 -534
- package/out/scripts/analytics/workflows-collector.cjs +4 -314
- package/out/scripts/analytics/workflows-daemon.mjs +9 -185
- package/out/scripts/analytics/workflows-widget.cjs +15 -159
- package/out/scripts/analytics/wow-model-widget.js +10 -120
- package/out/scripts/vendor-tools.mjs +2 -233
- package/out/skills/publish.js +3 -178
- package/out/store/github/setup.js +7 -212
- package/out/store/oramaIndex.js +2 -884
- package/out/sync.js +26 -1829
- package/out/terminalLogger.js +5 -358
- package/out/utils/pathSafety.js +2 -60
- package/out/utils/pythonRunner.js +4 -258
- package/out/utils/toolPath.js +2 -83
- package/out/workspaceFolders.js +2 -59
- package/package.json +1 -1
- package/out/agentpack/github.js +0 -547
- package/out/agentpack/index.js +0 -67
- package/out/agentpack/install.js +0 -297
- package/out/agentpack/manager.js +0 -441
- package/out/agentpack/manifest.js +0 -135
- package/out/agentpack/paths.js +0 -498
- package/out/agentpack/publish.js +0 -248
- package/out/agentpack/stores.js +0 -554
- package/out/analytics/dashboard/fleetStatusUtils.js +0 -30
- package/out/analytics/dashboard/fleetTitleUtils.js +0 -68
- package/out/analytics/dashboard/refreshPolicy.js +0 -16
- package/out/analytics/dashboard/scheduledFleetFingerprint.js +0 -22
- package/out/analytics/dashboard/scheduledFleetUtils.js +0 -22
- package/out/analytics/dashboard/sessionEventFleetStats.js +0 -118
- package/out/analytics/dashboard/viewsConfigMerge.js +0 -221
- package/out/analytics/dashboard/webview/codicon.ttf +0 -0
- package/out/analytics/identity.js +0 -0
- package/out/analytics/sessionEvents.js +0 -323
- package/out/auth/callbackUriParse.js +0 -59
- package/out/auth/types.js +0 -12
- package/out/auth-broker/config.js +0 -112
- package/out/auth-broker/jwt.js +0 -105
- package/out/auth-broker/pkce.js +0 -59
- package/out/auth-broker/server.js +0 -677
- package/out/auth-broker/store.js +0 -275
- package/out/authority/warrant.js +0 -206
- package/out/base-prompts/mcp-servers/.mcp.json +0 -13
- package/out/base-prompts/mcp-servers/langfuse-analytics/README.md +0 -142
- package/out/base-prompts/mcp-servers/langfuse-analytics/mcp.json +0 -13
- package/out/base-prompts/mcp-servers/langfuse-analytics/package-lock.json +0 -1192
- package/out/base-prompts/mcp-servers/langfuse-analytics/package.json +0 -23
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/run-with-env.sh +0 -12
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/sdk-test.js +0 -102
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/simple-test.js +0 -137
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-client.js +0 -118
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-server.js +0 -209
- package/out/base-prompts/mcp-servers/langfuse-analytics/src/client.ts +0 -431
- package/out/base-prompts/mcp-servers/langfuse-analytics/src/index.ts +0 -270
- package/out/base-prompts/mcp-servers/langfuse-analytics/tsconfig.json +0 -18
- package/out/base-prompts/rules/AGENTS.md +0 -204
- package/out/base-prompts/skills/agent-browser/SKILL.md +0 -102
- package/out/base-prompts/skills/agent-builder/SKILL.md +0 -73
- package/out/base-prompts/skills/agent-builder/references/agent-pack.md +0 -63
- package/out/base-prompts/skills/agent-builder/references/crewai.md +0 -66
- package/out/base-prompts/skills/agent-builder/references/langchain.md +0 -60
- package/out/base-prompts/skills/agent-builder/references/raw-sdk.md +0 -73
- package/out/base-prompts/skills/agent-engine-creator/SKILL.md +0 -586
- package/out/base-prompts/skills/agent-engine-installer/SKILL.md +0 -106
- package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.mjs +0 -259
- package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.ps1 +0 -189
- package/out/base-prompts/skills/agenthippo-cli/SKILL.md +0 -69
- package/out/base-prompts/skills/agenthippo-cli/scripts/install.sh +0 -63
- package/out/base-prompts/skills/agentpack-creator/SKILL.md +0 -375
- package/out/base-prompts/skills/agentpack-creator/references/agent-templates.md +0 -261
- package/out/base-prompts/skills/agentpack-creator/references/agents-md-guide.md +0 -195
- package/out/base-prompts/skills/agentpack-creator/scripts/init_pack.py +0 -391
- package/out/base-prompts/skills/agentpack-creator/scripts/init_pack_test.py +0 -294
- package/out/base-prompts/skills/agentpack-testbench/SKILL.md +0 -135
- package/out/base-prompts/skills/algorithmic-art/LICENSE.txt +0 -202
- package/out/base-prompts/skills/algorithmic-art/SKILL.md +0 -405
- package/out/base-prompts/skills/algorithmic-art/templates/generator_template.js +0 -223
- package/out/base-prompts/skills/algorithmic-art/templates/viewer.html +0 -599
- package/out/base-prompts/skills/debug-agent/SKILL.md +0 -83
- package/out/base-prompts/skills/desktop-automation/SKILL.md +0 -166
- package/out/base-prompts/skills/desktop-automation/references/agenthippo-ui-recipes.md +0 -441
- package/out/base-prompts/skills/desktop-automation/scripts/windows-desktop-uia.psm1 +0 -280
- package/out/base-prompts/skills/docx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/docx/SKILL.md +0 -197
- package/out/base-prompts/skills/docx/docx-js.md +0 -350
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/out/base-prompts/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/out/base-prompts/skills/docx/ooxml/scripts/pack.py +0 -159
- package/out/base-prompts/skills/docx/ooxml/scripts/unpack.py +0 -29
- package/out/base-prompts/skills/docx/ooxml/scripts/validate.py +0 -69
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/base.py +0 -951
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/docx.py +0 -274
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
- package/out/base-prompts/skills/docx/ooxml.md +0 -610
- package/out/base-prompts/skills/docx/scripts/__init__.py +0 -1
- package/out/base-prompts/skills/docx/scripts/document.py +0 -1276
- package/out/base-prompts/skills/docx/scripts/templates/comments.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsExtended.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsIds.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/people.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/utilities.py +0 -374
- package/out/base-prompts/skills/gateway-outbound/SKILL.md +0 -136
- package/out/base-prompts/skills/mcp-builder/LICENSE.txt +0 -202
- package/out/base-prompts/skills/mcp-builder/SKILL.md +0 -328
- package/out/base-prompts/skills/mcp-builder/reference/evaluation.md +0 -602
- package/out/base-prompts/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
- package/out/base-prompts/skills/mcp-builder/reference/node_mcp_server.md +0 -970
- package/out/base-prompts/skills/mcp-builder/reference/python_mcp_server.md +0 -719
- package/out/base-prompts/skills/mcp-builder/scripts/connections.py +0 -151
- package/out/base-prompts/skills/mcp-builder/scripts/evaluation.py +0 -373
- package/out/base-prompts/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
- package/out/base-prompts/skills/mcp-builder/scripts/requirements.txt +0 -2
- package/out/base-prompts/skills/openclaw/SKILL.md +0 -406
- package/out/base-prompts/skills/openclaw/scripts/run-smoke.sh +0 -61
- package/out/base-prompts/skills/pdf/LICENSE.txt +0 -30
- package/out/base-prompts/skills/pdf/SKILL.md +0 -294
- package/out/base-prompts/skills/pdf/forms.md +0 -205
- package/out/base-prompts/skills/pdf/reference.md +0 -612
- package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes.py +0 -70
- package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
- package/out/base-prompts/skills/pdf/scripts/check_fillable_fields.py +0 -12
- package/out/base-prompts/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
- package/out/base-prompts/skills/pdf/scripts/create_validation_image.py +0 -41
- package/out/base-prompts/skills/pdf/scripts/extract_form_field_info.py +0 -152
- package/out/base-prompts/skills/pdf/scripts/fill_fillable_fields.py +0 -114
- package/out/base-prompts/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
- package/out/base-prompts/skills/pptx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/pptx/SKILL.md +0 -484
- package/out/base-prompts/skills/pptx/html2pptx.md +0 -625
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/out/base-prompts/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/out/base-prompts/skills/pptx/ooxml/scripts/pack.py +0 -159
- package/out/base-prompts/skills/pptx/ooxml/scripts/unpack.py +0 -29
- package/out/base-prompts/skills/pptx/ooxml/scripts/validate.py +0 -69
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/base.py +0 -951
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
- package/out/base-prompts/skills/pptx/ooxml.md +0 -427
- package/out/base-prompts/skills/pptx/scripts/html2pptx.js +0 -984
- package/out/base-prompts/skills/pptx/scripts/inventory.py +0 -1020
- package/out/base-prompts/skills/pptx/scripts/rearrange.py +0 -231
- package/out/base-prompts/skills/pptx/scripts/replace.py +0 -385
- package/out/base-prompts/skills/pptx/scripts/thumbnail.py +0 -450
- package/out/base-prompts/skills/prompt-lab/SKILL.md +0 -149
- package/out/base-prompts/skills/prompt-lab/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/skill-creator/LICENSE.txt +0 -202
- package/out/base-prompts/skills/skill-creator/SKILL.md +0 -371
- package/out/base-prompts/skills/skill-creator/references/output-patterns.md +0 -82
- package/out/base-prompts/skills/skill-creator/references/workflows.md +0 -28
- package/out/base-prompts/skills/skill-creator/scripts/init_skill.py +0 -303
- package/out/base-prompts/skills/skill-creator/scripts/package_skill.py +0 -110
- package/out/base-prompts/skills/skill-creator/scripts/quick_validate.py +0 -95
- package/out/base-prompts/skills/skill-installer/SKILL.md +0 -251
- package/out/base-prompts/skills/skill-installer/references/store-api.md +0 -219
- package/out/base-prompts/skills/spotlight-dashboard/SKILL.md +0 -83
- package/out/base-prompts/skills/spotlight-insights/SKILL.md +0 -101
- package/out/base-prompts/skills/spotlight-optimize/SKILL.md +0 -93
- package/out/base-prompts/skills/spotlight-optimize/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/spotlight-query/SKILL.md +0 -314
- package/out/base-prompts/skills/spotlight-query/references/otel-schema.md +0 -163
- package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.test.sh +0 -744
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/sessions/sess-test-1.jsonl +0 -14
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/.duckdb-index.json +0 -7
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/sample.jsonl +0 -5
- package/out/base-prompts/skills/spotlight-report/SKILL.md +0 -86
- package/out/base-prompts/skills/spotlight-visualize/SKILL.md +0 -92
- package/out/base-prompts/skills/spotlight-visualize/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/xlsx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/xlsx/SKILL.md +0 -289
- package/out/base-prompts/skills/xlsx/recalc.py +0 -178
- package/out/branding.js +0 -49
- package/out/cli/commands/anywhere.js +0 -66
- package/out/cli/commands/ask.js +0 -246
- package/out/cli/commands/auth-broker.js +0 -24
- package/out/cli/commands/auth.js +0 -219
- package/out/cli/commands/chat.js +0 -357
- package/out/cli/commands/conformance.js +0 -249
- package/out/cli/commands/data-plane.js +0 -67
- package/out/cli/commands/deploy.js +0 -263
- package/out/cli/commands/gateway.js +0 -218
- package/out/cli/commands/idp.js +0 -85
- package/out/cli/commands/pack.js +0 -399
- package/out/cli/commands/probe-custom-engine.js +0 -82
- package/out/cli/commands/sandbox.js +0 -332
- package/out/cli/commands/serve.js +0 -1758
- package/out/cli/commands/store.js +0 -927
- package/out/cli/credential-context.js +0 -91
- package/out/cli/data-plane/broker.js +0 -78
- package/out/cli/data-plane/connector-host.js +0 -121
- package/out/cli/data-plane/egress.js +0 -189
- package/out/cli/data-plane/gateway.js +0 -218
- package/out/cli/data-plane/index.js +0 -152
- package/out/cli/data-plane/registry.js +0 -89
- package/out/cli/data-plane/remote.js +0 -106
- package/out/cli/data-plane/sidecar.js +0 -161
- package/out/cli/deploy/blueprints/registry.js +0 -84
- package/out/cli/deploy/layers/identity.js +0 -172
- package/out/cli/deploy-init.js +0 -3023
- package/out/cli/deps.js +0 -342
- package/out/cli/idp-init.js +0 -338
- package/out/cli/pack-install-ref.js +0 -15
- package/out/cli/pack-loader.js +0 -418
- package/out/cli/pack-runtime.js +0 -61
- package/out/cli/runtime.js +0 -450
- package/out/cli/serve-hardening.js +0 -607
- package/out/cli/state-sync.js +0 -424
- package/out/cli/utils.js +0 -231
- package/out/engines/base.js +0 -188
- package/out/engines/claude.js +0 -1254
- package/out/engines/codex.js +0 -862
- package/out/engines/custom.js +0 -674
- package/out/engines/customEngineIpc.js +0 -95
- package/out/engines/customEngineRunnerEmbed.js +0 -107
- package/out/engines/customRegistry.js +0 -274
- package/out/engines/embeddedCustomEngineRunner.generated.js +0 -9
- package/out/engines/engineApiKey.js +0 -138
- package/out/engines/install.js +0 -372
- package/out/engines/persistentEngineWorker.js +0 -320
- package/out/engines/publish.js +0 -341
- package/out/evidence/sessionShip.js +0 -610
- package/out/gateway/bridge.js +0 -843
- package/out/gateway/cli-agent-id.js +0 -36
- package/out/gateway/client.js +0 -336
- package/out/gateway/client.test.js +0 -395
- package/out/gateway/gateway-scope-errors.js +0 -14
- package/out/gateway/gatewayBackup.js +0 -76
- package/out/gateway/installSource.js +0 -68
- package/out/gateway/lifecycle.js +0 -3190
- package/out/gateway/openclaw-channel-routing.js +0 -317
- package/out/gateway/outboundHandles.js +0 -373
- package/out/gateway/outboundPaths.js +0 -75
- package/out/gateway/outboundSend.js +0 -254
- package/out/gateway/protocol.js +0 -48
- package/out/gateway/secretsPreflight.js +0 -90
- package/out/gateway/sessionRouting.js +0 -77
- package/out/headless/index.js +0 -539
- package/out/headless/sync.js +0 -473
- package/out/live-e2e/mcp-demo/server.mjs +0 -32
- package/out/live-probe/connector.mjs +0 -8
- package/out/live-probe/env-report.mjs +0 -8
- package/out/mcp.js +0 -654
- package/out/pack/caps.js +0 -271
- package/out/pack/governor.js +0 -309
- package/out/pack/lease-schema.js +0 -270
- package/out/pack/lease.js +0 -640
- package/out/pack/reconcile.js +0 -217
- package/out/pack/signature.js +0 -839
- package/out/permissions.js +0 -108
- package/out/permissions.mapping.yaml +0 -68
- package/out/registry/index.js +0 -332
- package/out/runner/host.js +0 -131
- package/out/runner/protocol.js +0 -108
- package/out/runner/remoteEngine.js +0 -130
- package/out/runtime/interfaces/config-provider.js +0 -32
- package/out/runtime/interfaces/edit-tracker.js +0 -24
- package/out/runtime/interfaces/index.js +0 -29
- package/out/runtime/interfaces/logger.js +0 -40
- package/out/runtime/interfaces/permission-handler.js +0 -66
- package/out/runtime/interfaces/session-store.js +0 -342
- package/out/runtime/vscode/vscode-session-store.js +0 -22
- package/out/sandbox/containerDriver.js +0 -570
- package/out/sandbox/processDriver.js +0 -315
- package/out/sandbox/sandboxRunner.js +0 -191
- package/out/sandbox/support.js +0 -301
- package/out/sandbox/types.js +0 -55
- package/out/scripts/analytics/fleet-local-adapter.js +0 -246
- package/out/scripts/analytics/live-fleet-4-collector.cjs +0 -315
- package/out/scripts/analytics/live-fleet-4-daemon.mjs +0 -134
- package/out/scripts/analytics/live-fleet-4-widget.cjs +0 -433
- package/out/scripts/gateway/Dockerfile.serve +0 -45
- package/out/scripts/gateway/docker-serve.sh +0 -248
- package/out/scripts/gateway/setup-gateway-server.ps1 +0 -953
- package/out/scripts/gateway/setup-gateway-server.sh +0 -935
- package/out/session.js +0 -272
- package/out/shared/agentEngines.js +0 -170
- package/out/shared/index.js +0 -109
- package/out/shared/models.js +0 -136
- package/out/shared/providers.js +0 -205
- package/out/spotlight/install.js +0 -345
- package/out/spotlight/publish.js +0 -544
- package/out/spotlight/yaml.js +0 -31
- package/out/store/index.js +0 -786
- package/out/store/shared.js +0 -241
- package/out/test/agentEnvFile.test.js +0 -97
- package/out/test/agentYaml.test.js +0 -373
- package/out/test/agentpack.test.js +0 -639
- package/out/test/analytics-lifecycle.test.js +0 -174
- package/out/test/analytics.test.js +0 -756
- package/out/test/auth-baseurl.test.js +0 -132
- package/out/test/auth-bootstrapApply.test.js +0 -123
- package/out/test/auth-broker-hardening.test.js +0 -227
- package/out/test/auth-broker.test.js +0 -541
- package/out/test/auth-callback.test.js +0 -62
- package/out/test/blueprint-registry.test.js +0 -105
- package/out/test/branding.test.js +0 -242
- package/out/test/caps-governor.test.js +0 -455
- package/out/test/cli-agent-id.test.js +0 -55
- package/out/test/codex-new-file-survival.test.js +0 -119
- package/out/test/configuration-scopes.test.js +0 -131
- package/out/test/credential-context.test.js +0 -160
- package/out/test/custom-env-layering.test.js +0 -151
- package/out/test/customEngineInstall.test.js +0 -100
- package/out/test/customRegistry.test.js +0 -122
- package/out/test/data-plane-engine-wiring.test.js +0 -250
- package/out/test/data-plane.test.js +0 -311
- package/out/test/deploy-init.test.js +0 -854
- package/out/test/deps-auto-install.test.js +0 -81
- package/out/test/egress-broker.test.js +0 -172
- package/out/test/engine-integration.test.js +0 -297
- package/out/test/enginePaths.test.js +0 -195
- package/out/test/enginePublish.test.js +0 -82
- package/out/test/fleetDisplayUtils.test.js +0 -100
- package/out/test/gateway-client.test.js +0 -409
- package/out/test/gateway-install-source.test.js +0 -111
- package/out/test/gateway-lifecycle.test.js +0 -608
- package/out/test/gateway-outbound.test.js +0 -538
- package/out/test/gateway-reset-mappings.test.js +0 -221
- package/out/test/gateway-route-index.test.js +0 -235
- package/out/test/gateway-secrets-preflight.test.js +0 -127
- package/out/test/gateway-session-routing.test.js +0 -65
- package/out/test/gateway-session-store.test.js +0 -216
- package/out/test/github.test.js +0 -232
- package/out/test/governor-hardening.test.js +0 -217
- package/out/test/headless-sync.test.js +0 -301
- package/out/test/helpers/manifest-fixtures.js +0 -27
- package/out/test/idp-init.test.js +0 -151
- package/out/test/lease-signature.test.js +0 -357
- package/out/test/lease-trust-root.test.js +0 -273
- package/out/test/lease-validation.test.js +0 -169
- package/out/test/lease.test.js +0 -114
- package/out/test/mcp-obo.test.js +0 -107
- package/out/test/mcp.test.js +0 -389
- package/out/test/model-routing.test.js +0 -133
- package/out/test/openclaw-channel-routing.test.js +0 -191
- package/out/test/pack-admission.test.js +0 -216
- package/out/test/pack-digest-parity.test.js +0 -140
- package/out/test/pack-reconcile.test.js +0 -183
- package/out/test/pack-signature-artifact.test.js +0 -103
- package/out/test/pack-signature.test.js +0 -176
- package/out/test/pack-trust-root.test.js +0 -202
- package/out/test/pathSafety.test.js +0 -63
- package/out/test/permissions.test.js +0 -175
- package/out/test/persistent-engine-worker.test.js +0 -220
- package/out/test/refreshPolicy.test.js +0 -60
- package/out/test/registry.test.js +0 -351
- package/out/test/remote-trust.test.js +0 -242
- package/out/test/runner-protocol.test.js +0 -249
- package/out/test/runtime.test.js +0 -349
- package/out/test/sandbox-driver.test.js +0 -553
- package/out/test/sandbox-hardening.test.js +0 -395
- package/out/test/sandbox-runner.test.js +0 -343
- package/out/test/scheduledFleetStorage.test.js +0 -72
- package/out/test/serve-gateway-turn.test.js +0 -267
- package/out/test/serve-hardening.test.js +0 -418
- package/out/test/serveClient.test.js +0 -209
- package/out/test/sessionJsonlFleet.test.js +0 -78
- package/out/test/sessionShip.test.js +0 -560
- package/out/test/shared.test.js +0 -173
- package/out/test/skills-mcp.test.js +0 -299
- package/out/test/spotlightPublish.test.js +0 -327
- package/out/test/spotlightYaml.test.js +0 -58
- package/out/test/state-sync-peer.test.js +0 -156
- package/out/test/store-install-safe.test.js +0 -69
- package/out/test/store.test.js +0 -331
- package/out/test/stores.test.js +0 -504
- package/out/test/sync-switch.test.js +0 -251
- package/out/test/teamViewDiscovery.test.js +0 -90
- package/out/test/tls-bypass.test.js +0 -90
- package/out/test/toolPath.test.js +0 -120
- package/out/test/utils.test.js +0 -308
- package/out/test/viewsConfigMerge.test.js +0 -180
- package/out/test/warrant.test.js +0 -138
- package/out/test/windows-command-line.test.js +0 -75
- package/out/utils/agentEnvFile.js +0 -131
- package/out/utils/binary.js +0 -175
- package/out/utils/index.js +0 -37
- package/out/utils/network.js +0 -74
- package/out/utils/paths.js +0 -116
- package/out/utils/pid.js +0 -180
- package/out/utils/resolveNodeBinary.js +0 -168
- package/out/utils/windowsCommandLine.js +0 -29
- package/out/utils.js +0 -968
|
@@ -1,854 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Deploy Init Test Suite (serve-v3 §A4)
|
|
4
|
-
*
|
|
5
|
-
* Verifies the generator emits well-formed artifacts for every target:
|
|
6
|
-
* YAML/JSON parse, Dockerfile shape, shell scripts pass `bash -n`, and the
|
|
7
|
-
* .env.example scan reflects the pack's ${env:...} references.
|
|
8
|
-
*--------------------------------------------------------------------------------------------*/
|
|
9
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
-
}
|
|
15
|
-
Object.defineProperty(o, k2, desc);
|
|
16
|
-
}) : (function(o, m, k, k2) {
|
|
17
|
-
if (k2 === undefined) k2 = k;
|
|
18
|
-
o[k2] = m[k];
|
|
19
|
-
}));
|
|
20
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
-
}) : function(o, v) {
|
|
23
|
-
o["default"] = v;
|
|
24
|
-
});
|
|
25
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
-
var ownKeys = function(o) {
|
|
27
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
-
var ar = [];
|
|
29
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
-
return ar;
|
|
31
|
-
};
|
|
32
|
-
return ownKeys(o);
|
|
33
|
-
};
|
|
34
|
-
return function (mod) {
|
|
35
|
-
if (mod && mod.__esModule) return mod;
|
|
36
|
-
var result = {};
|
|
37
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
-
__setModuleDefault(result, mod);
|
|
39
|
-
return result;
|
|
40
|
-
};
|
|
41
|
-
})();
|
|
42
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
require("mocha");
|
|
44
|
-
const assert = __importStar(require("assert"));
|
|
45
|
-
const fs = __importStar(require("fs"));
|
|
46
|
-
const os = __importStar(require("os"));
|
|
47
|
-
const path = __importStar(require("path"));
|
|
48
|
-
const child_process_1 = require("child_process");
|
|
49
|
-
const yaml = __importStar(require("js-yaml"));
|
|
50
|
-
const deploy_init_1 = require("../cli/deploy-init");
|
|
51
|
-
const deploy_1 = require("../cli/commands/deploy");
|
|
52
|
-
const idp_init_1 = require("../cli/idp-init");
|
|
53
|
-
const CTX = {
|
|
54
|
-
packName: 'databricks-analyst',
|
|
55
|
-
packVersion: '0.1.0',
|
|
56
|
-
image: 'ghcr.io/agenthippo/runtime:2026.06',
|
|
57
|
-
port: 8080,
|
|
58
|
-
engine: 'claude',
|
|
59
|
-
envVars: ['DATABRICKS_HOST', 'DATABRICKS_HTTP_PATH'],
|
|
60
|
-
publicHostname: deploy_init_1.DEPLOY_PUBLIC_HOSTNAME,
|
|
61
|
-
};
|
|
62
|
-
suite('deploy-init: target registry', () => {
|
|
63
|
-
test('isDeployTarget recognizes known and rejects unknown', () => {
|
|
64
|
-
for (const t of deploy_init_1.DEPLOY_TARGETS) {
|
|
65
|
-
assert.ok((0, deploy_init_1.isDeployTarget)(t));
|
|
66
|
-
}
|
|
67
|
-
assert.strictEqual((0, deploy_init_1.isDeployTarget)('heroku'), false);
|
|
68
|
-
assert.strictEqual((0, deploy_init_1.isDeployTarget)('all'), false); // handled separately by the command
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
suite('deploy-init: scanEnvRefs', () => {
|
|
72
|
-
let dir;
|
|
73
|
-
setup(() => {
|
|
74
|
-
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ah-scan-'));
|
|
75
|
-
});
|
|
76
|
-
teardown(() => {
|
|
77
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
78
|
-
});
|
|
79
|
-
test('collects ${env:VAR} across files, de-dupes and sorts', () => {
|
|
80
|
-
fs.mkdirSync(path.join(dir, 'mcp'), { recursive: true });
|
|
81
|
-
fs.writeFileSync(path.join(dir, 'mcp', 'databricks.mcp.json'), JSON.stringify({ env: { H: '${env:DATABRICKS_HOST}', T: '${env:DATABRICKS_TOKEN}' } }));
|
|
82
|
-
fs.writeFileSync(path.join(dir, 'agent.yaml'), 'x: ${env:DATABRICKS_HOST}\n'); // dup
|
|
83
|
-
assert.deepStrictEqual((0, deploy_init_1.scanEnvRefs)(dir), ['DATABRICKS_HOST', 'DATABRICKS_TOKEN']);
|
|
84
|
-
});
|
|
85
|
-
test('skips node_modules / deploy / .git', () => {
|
|
86
|
-
fs.mkdirSync(path.join(dir, 'node_modules'), { recursive: true });
|
|
87
|
-
fs.writeFileSync(path.join(dir, 'node_modules', 'x.json'), '${env:SHOULD_NOT_APPEAR}');
|
|
88
|
-
fs.mkdirSync(path.join(dir, 'deploy'), { recursive: true });
|
|
89
|
-
fs.writeFileSync(path.join(dir, 'deploy', 'y.json'), '${env:ALSO_NOT}');
|
|
90
|
-
fs.writeFileSync(path.join(dir, 'agent.yaml'), 'name: ${env:REAL_ONE}');
|
|
91
|
-
assert.deepStrictEqual((0, deploy_init_1.scanEnvRefs)(dir), ['REAL_ONE']);
|
|
92
|
-
});
|
|
93
|
-
test('empty pack → no vars', () => {
|
|
94
|
-
fs.writeFileSync(path.join(dir, 'agent.yaml'), 'metadata:\n name: x\n');
|
|
95
|
-
assert.deepStrictEqual((0, deploy_init_1.scanEnvRefs)(dir), []);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
suite('deploy-init: renderEnvExample', () => {
|
|
99
|
-
test('lists scanned vars and model credential', () => {
|
|
100
|
-
const out = (0, deploy_init_1.renderEnvExample)(['FOO', 'BAR']);
|
|
101
|
-
assert.ok(!out.includes('AGENTHIPPO_SERVE_API_KEY='), 'serve bearer key must not appear — auth is the deploy layer');
|
|
102
|
-
assert.ok(out.includes('ANTHROPIC_API_KEY='));
|
|
103
|
-
assert.ok(out.includes('FOO='));
|
|
104
|
-
assert.ok(out.includes('BAR='));
|
|
105
|
-
});
|
|
106
|
-
test('edge .env keeps only the public hostname; auth secrets live in .env.auth', () => {
|
|
107
|
-
const out = (0, deploy_init_1.renderEnvExample)([], { edge: true });
|
|
108
|
-
assert.ok(out.includes(`AGENTHIPPO_PUBLIC_HOSTNAME=${deploy_init_1.DEPLOY_PUBLIC_HOSTNAME}`));
|
|
109
|
-
// The agent loads .env — the token-signing key and OAuth secrets must not be here.
|
|
110
|
-
assert.ok(!out.includes('AGENTHIPPO_AUTH_JWT_SECRET'), 'JWT secret must not be in the agent-shared .env');
|
|
111
|
-
assert.ok(!out.includes('OAUTH2_PROXY_CLIENT_SECRET'), 'OAuth secret must not be in the agent-shared .env');
|
|
112
|
-
assert.ok(out.includes('.env.auth'), 'edge .env should point operators at .env.auth');
|
|
113
|
-
});
|
|
114
|
-
test('renderAuthEnvExample carries the oauth2-proxy + JWT secrets', () => {
|
|
115
|
-
const out = (0, deploy_init_1.renderAuthEnvExample)();
|
|
116
|
-
assert.ok(out.includes('OAUTH2_PROXY_CLIENT_ID='));
|
|
117
|
-
assert.ok(out.includes('OAUTH2_PROXY_REDIRECT_URL='));
|
|
118
|
-
assert.ok(out.includes('OAUTH2_PROXY_REVERSE_PROXY=true'));
|
|
119
|
-
assert.ok(out.includes('AGENTHIPPO_AUTH_JWT_SECRET='));
|
|
120
|
-
assert.ok(out.includes('AGENTHIPPO_AUTH_DEV=0'), 'AUTH_DEV must default off in generated auth env');
|
|
121
|
-
});
|
|
122
|
-
test('renderAuthEnvExample honors custom hostname', () => {
|
|
123
|
-
const out = (0, deploy_init_1.renderAuthEnvExample)({ hostname: 'agents.example.com' });
|
|
124
|
-
assert.ok(out.includes('https://agents.example.com/oauth2/callback'));
|
|
125
|
-
assert.ok(!out.includes('auth.agenthippo.ai'));
|
|
126
|
-
});
|
|
127
|
-
test('vm target embeds custom hostname in cloudflared and README', () => {
|
|
128
|
-
const files = (0, deploy_init_1.renderTarget)('vm', { ...CTX, publicHostname: 'agents.example.com' });
|
|
129
|
-
assert.ok(files['cloudflared.yml'].includes('agents.example.com'));
|
|
130
|
-
assert.ok(files['README.md'].includes('https://agents.example.com'));
|
|
131
|
-
assert.ok(files['run-edge-tunnel.sh'].includes('https://agents.example.com'));
|
|
132
|
-
});
|
|
133
|
-
test('cursor engine seeds CURSOR_API_KEY instead of ANTHROPIC', () => {
|
|
134
|
-
const out = (0, deploy_init_1.renderEnvExample)([], { engine: 'cursor' });
|
|
135
|
-
assert.ok(out.includes('CURSOR_API_KEY='));
|
|
136
|
-
assert.ok(!out.includes('ANTHROPIC_API_KEY='));
|
|
137
|
-
});
|
|
138
|
-
test('serveCmdArgs includes custom engine and model', () => {
|
|
139
|
-
const args = (0, deploy_init_1.serveCmdArgs)({ ...CTX, engine: 'cursor', model: 'auto', image: 'x', envVars: [] });
|
|
140
|
-
assert.ok(args.includes('--engine'));
|
|
141
|
-
assert.ok(args.includes('cursor'));
|
|
142
|
-
assert.ok(args.includes('--model'));
|
|
143
|
-
assert.ok(args.includes('auto'));
|
|
144
|
-
assert.ok(!args.includes('--host'), 'bind address is set via AGENTHIPPO_SERVE_HOST in Dockerfile, not --host');
|
|
145
|
-
});
|
|
146
|
-
test('serveCmdArgs can include www-dir for cloudrun browser UI', () => {
|
|
147
|
-
const args = (0, deploy_init_1.serveCmdArgs)({ ...CTX, image: 'x', envVars: [] }, { wwwDir: '/app/pack/deploy/www' });
|
|
148
|
-
assert.ok(args.includes('--www-dir'));
|
|
149
|
-
assert.ok(args.includes('/app/pack/deploy/www'));
|
|
150
|
-
});
|
|
151
|
-
test('defaultRuntimeImage picks cursor layer for cursor engine', () => {
|
|
152
|
-
assert.ok((0, deploy_init_1.defaultRuntimeImage)('cursor').includes('cursor'));
|
|
153
|
-
assert.ok(!(0, deploy_init_1.defaultRuntimeImage)('claude').includes('cursor'));
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
suite('deploy-init: renderTarget produces well-formed artifacts', () => {
|
|
157
|
-
test('every target includes a scanned .env.example with the pack vars', () => {
|
|
158
|
-
for (const t of deploy_init_1.DEPLOY_TARGETS) {
|
|
159
|
-
const files = (0, deploy_init_1.renderTarget)(t, CTX);
|
|
160
|
-
assert.ok(files['.env.example'], `${t} missing .env.example`);
|
|
161
|
-
assert.ok(files['.env.example'].includes('DATABRICKS_HOST'), `${t} .env.example missing scanned var`);
|
|
162
|
-
assert.ok(files['README.md'], `${t} missing README.md`);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
test('every target ships a DATA-ACCESS.md summary (governance layer)', () => {
|
|
166
|
-
for (const t of deploy_init_1.DEPLOY_TARGETS) {
|
|
167
|
-
const files = (0, deploy_init_1.renderTarget)(t, CTX);
|
|
168
|
-
const doc = files['DATA-ACCESS.md'];
|
|
169
|
-
assert.ok(doc, `${t} missing DATA-ACCESS.md`);
|
|
170
|
-
assert.ok(doc.includes('## Secrets') && doc.includes('## Egress') && doc.includes('## Blast radius per component'), `${t} DATA-ACCESS.md missing a required section`);
|
|
171
|
-
assert.ok(doc.includes('api.anthropic.com:443'), `${t} DATA-ACCESS.md must name the model API host`);
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
test('DATA-ACCESS.md lists auth secrets + oauth2-proxy blast radius only for self-issued edge targets', () => {
|
|
175
|
-
const selfIssued = (0, deploy_init_1.renderTarget)('vm', CTX)['DATA-ACCESS.md'];
|
|
176
|
-
assert.ok(selfIssued.includes('AGENTHIPPO_AUTH_JWT_SECRET'));
|
|
177
|
-
assert.ok(selfIssued.includes('OAUTH2_PROXY_CLIENT_SECRET'));
|
|
178
|
-
assert.ok(selfIssued.includes('**oauth2-proxy**'));
|
|
179
|
-
const federated = (0, deploy_init_1.renderTarget)('vm', { ...CTX, ingress: 'edge-fed', idpUrl: 'https://auth.corp.example' })['DATA-ACCESS.md'];
|
|
180
|
-
assert.ok(federated.includes('AGENTHIPPO_AUTH_JWT_SECRET'));
|
|
181
|
-
assert.ok(!federated.includes('OAUTH2_PROXY_CLIENT_SECRET'), 'federated deployments mint no Google OAuth client');
|
|
182
|
-
assert.ok(!federated.includes('**oauth2-proxy**'), 'federated deployments run no oauth2-proxy');
|
|
183
|
-
const noEdge = (0, deploy_init_1.renderTarget)('databricks-app', CTX)['DATA-ACCESS.md'];
|
|
184
|
-
assert.ok(!noEdge.includes('AGENTHIPPO_AUTH_JWT_SECRET'), 'platform-native targets mint no JWT secret');
|
|
185
|
-
});
|
|
186
|
-
test('image-based targets emit pack provenance header and OCI labels', () => {
|
|
187
|
-
const marker = 'Generated from Agent Pack databricks-analyst@0.1.0 — packs.agenthippo.ai';
|
|
188
|
-
for (const t of ['vm', 'compose', 'cloudrun', 'spcs', 'cloudflare']) {
|
|
189
|
-
const files = (0, deploy_init_1.renderTarget)(t, CTX);
|
|
190
|
-
const df = files['Dockerfile'];
|
|
191
|
-
assert.ok(df.includes(marker), `${t} Dockerfile missing attribution`);
|
|
192
|
-
assert.ok(df.includes('LABEL ai.agenthippo.pack.name="databricks-analyst"'), `${t} missing pack.name label`);
|
|
193
|
-
assert.ok(df.includes('LABEL ai.agenthippo.pack.version="0.1.0"'), `${t} missing pack.version label`);
|
|
194
|
-
assert.ok(df.includes('LABEL ai.agenthippo.pack.engine="claude"'), `${t} missing pack.engine label`);
|
|
195
|
-
assert.ok(files['.env.example'].includes(marker), `${t} .env.example missing attribution`);
|
|
196
|
-
assert.ok(files['README.md'].includes(marker), `${t} README missing attribution`);
|
|
197
|
-
}
|
|
198
|
-
const vmRun = (0, deploy_init_1.renderTarget)('vm', CTX)['run.sh'];
|
|
199
|
-
assert.ok(vmRun.startsWith('#!'), 'run.sh keeps shebang first');
|
|
200
|
-
assert.ok(vmRun.includes(`# ${marker}`), 'run.sh attributes after shebang');
|
|
201
|
-
});
|
|
202
|
-
test('image-based targets emit a valid 5-line-style Dockerfile referencing the runtime image', () => {
|
|
203
|
-
for (const t of ['vm', 'compose', 'cloudrun', 'spcs', 'cloudflare']) {
|
|
204
|
-
const df = (0, deploy_init_1.renderTarget)(t, CTX)['Dockerfile'];
|
|
205
|
-
assert.ok(df, `${t} missing Dockerfile`);
|
|
206
|
-
assert.ok(df.includes('FROM ghcr.io/agenthippo/runtime:2026.06'), `${t} Dockerfile missing FROM`);
|
|
207
|
-
assert.ok(df.includes('COPY . /app/pack/'), `${t} Dockerfile missing COPY`);
|
|
208
|
-
assert.ok(/EXPOSE 8080/.test(df), `${t} Dockerfile missing EXPOSE`);
|
|
209
|
-
assert.ok(df.includes('CMD ["serve"'), `${t} Dockerfile CMD must append to runtime ENTRYPOINT`);
|
|
210
|
-
assert.ok(!df.includes('CMD ["agenthippo"'), `${t} Dockerfile must not duplicate agenthippo in CMD`);
|
|
211
|
-
assert.ok(df.includes('ENV AGENTHIPPO_SERVE_HOST=0.0.0.0'), `${t} Dockerfile must bind on all interfaces in-container`);
|
|
212
|
-
assert.ok(!df.includes('"--host"'), `${t} Dockerfile must not pass unsupported --host to older runtimes`);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
test('image-based targets ship a .dockerignore that keeps skills and drops node_modules', () => {
|
|
216
|
-
for (const t of ['vm', 'compose', 'cloudrun', 'spcs', 'cloudflare']) {
|
|
217
|
-
const di = (0, deploy_init_1.renderTarget)(t, CTX)['.dockerignore'];
|
|
218
|
-
assert.ok(di, `${t} missing .dockerignore`);
|
|
219
|
-
assert.ok(di.includes('**/node_modules'), `${t} .dockerignore should exclude node_modules`);
|
|
220
|
-
assert.ok(di.includes('.git'), `${t} .dockerignore should exclude .git`);
|
|
221
|
-
// Ship only deploy/www/ from deploy/ (drops deploy/.env, credentials, build artifacts).
|
|
222
|
-
assert.ok(di.includes('deploy/*'), `${t} should exclude deploy/*`);
|
|
223
|
-
assert.ok(di.includes('!deploy/www/**'), `${t} should re-include deploy/www/**`);
|
|
224
|
-
// Defensive secret ignores — any-extension credentials + common private-key formats,
|
|
225
|
-
// not just *credentials*.json (see isSecretLikeBasename in deploy-init.ts).
|
|
226
|
-
assert.ok(di.includes('**/.env'), `${t} should ignore **/.env`);
|
|
227
|
-
assert.ok(di.includes('**/*credentials*'), `${t} should ignore credentials of any extension`);
|
|
228
|
-
assert.ok(di.includes('**/*.pem'), `${t} should ignore .pem files`);
|
|
229
|
-
assert.ok(di.includes('**/*.key'), `${t} should ignore .key files`);
|
|
230
|
-
assert.ok(di.includes('**/id_rsa'), `${t} should ignore id_rsa`);
|
|
231
|
-
// skills/, scripts/, references/ must NOT be ignored lines.
|
|
232
|
-
const ignored = di.split('\n').map((l) => l.trim()).filter((l) => l && !l.startsWith('#'));
|
|
233
|
-
for (const keep of ['skills/', 'scripts/', 'references/', 'skills', 'scripts', 'references']) {
|
|
234
|
-
assert.ok(!ignored.includes(keep), `${t} .dockerignore must not ignore ${keep}`);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
// Source-based target ships no Dockerfile and no .dockerignore.
|
|
238
|
-
assert.strictEqual((0, deploy_init_1.renderTarget)('databricks-app', CTX)['.dockerignore'], undefined);
|
|
239
|
-
assert.strictEqual((0, deploy_init_1.renderTarget)('databricks-app', CTX)['Dockerfile'], undefined);
|
|
240
|
-
});
|
|
241
|
-
test('build scripts anchor to the staging root so they run from any cwd', () => {
|
|
242
|
-
// cloudrun + vm run.sh use `docker build -f deploy/Dockerfile .` → must cd to staging root.
|
|
243
|
-
assert.ok((0, deploy_init_1.renderTarget)('cloudrun', CTX)['cloudrun-deploy.sh'].includes('cd "$(dirname "$0")/.."'), 'cloudrun-deploy.sh must anchor to staging root');
|
|
244
|
-
assert.ok((0, deploy_init_1.renderTarget)('vm', CTX)['run.sh'].includes('cd "$(dirname "$0")/.."'), 'vm run.sh must anchor to staging root');
|
|
245
|
-
// edge tunnel uses docker compose (paths resolve relative to the compose file), so it
|
|
246
|
-
// anchors to deploy/ itself, not the staging root.
|
|
247
|
-
assert.ok((0, deploy_init_1.renderTarget)('vm', CTX)['run-edge-tunnel.sh'].includes('cd "$(dirname "$0")"'));
|
|
248
|
-
assert.ok(!(0, deploy_init_1.renderTarget)('vm', CTX)['run-edge-tunnel.sh'].includes('cd "$(dirname "$0")/.."'));
|
|
249
|
-
});
|
|
250
|
-
test('image-based Dockerfiles install skill requirements.txt/package.json deps at build time', () => {
|
|
251
|
-
for (const t of ['vm', 'compose', 'cloudrun', 'spcs', 'cloudflare']) {
|
|
252
|
-
const df = (0, deploy_init_1.renderTarget)(t, CTX)['Dockerfile'];
|
|
253
|
-
assert.ok(df.includes('requirements.txt'), `${t} Dockerfile should install python skill deps`);
|
|
254
|
-
assert.ok(df.includes('package.json'), `${t} Dockerfile should install node skill deps`);
|
|
255
|
-
assert.ok(df.includes('USER root') && df.includes('USER agent'), `${t} skill-deps step must run as root then drop back to agent`);
|
|
256
|
-
// Must not clobber the runtime ENTRYPOINT/CMD.
|
|
257
|
-
assert.ok(df.includes('CMD ["serve"'), `${t} CMD still present after skill-deps step`);
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
test('databricks-app is source-based (no Dockerfile) with OBO scopes', () => {
|
|
261
|
-
const files = (0, deploy_init_1.renderTarget)('databricks-app', CTX);
|
|
262
|
-
assert.strictEqual(files['Dockerfile'], undefined);
|
|
263
|
-
const app = yaml.load(files['app.yaml']);
|
|
264
|
-
assert.deepStrictEqual(app.user_authorization?.scopes, ['sql']);
|
|
265
|
-
const pkg = JSON.parse(files['package.json']);
|
|
266
|
-
assert.ok(pkg.dependencies['@anthropic-ai/claude-code']);
|
|
267
|
-
assert.ok(pkg.dependencies['@agenthippo-ai/agenthippo']);
|
|
268
|
-
});
|
|
269
|
-
test('YAML artifacts parse', () => {
|
|
270
|
-
assert.doesNotThrow(() => yaml.load((0, deploy_init_1.renderTarget)('vm', CTX)['docker-compose.yml']));
|
|
271
|
-
assert.doesNotThrow(() => yaml.load((0, deploy_init_1.renderTarget)('compose', CTX)['docker-compose.yml']));
|
|
272
|
-
assert.doesNotThrow(() => yaml.load((0, deploy_init_1.renderTarget)('spcs', CTX)['spec.yaml']));
|
|
273
|
-
assert.doesNotThrow(() => yaml.load((0, deploy_init_1.renderTarget)('databricks-app', CTX)['app.yaml']));
|
|
274
|
-
});
|
|
275
|
-
test('JSON artifacts parse (wrangler, package.json)', () => {
|
|
276
|
-
assert.doesNotThrow(() => JSON.parse((0, deploy_init_1.renderTarget)('cloudflare', CTX)['wrangler.jsonc']));
|
|
277
|
-
assert.doesNotThrow(() => JSON.parse((0, deploy_init_1.renderTarget)('databricks-app', CTX)['package.json']));
|
|
278
|
-
});
|
|
279
|
-
test('shell scripts are syntactically valid (bash -n)', () => {
|
|
280
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ah-sh-'));
|
|
281
|
-
try {
|
|
282
|
-
const scripts = [
|
|
283
|
-
['run.sh', (0, deploy_init_1.renderTarget)('vm', CTX)['run.sh']],
|
|
284
|
-
['run-edge-tunnel.sh', (0, deploy_init_1.renderTarget)('vm', CTX)['run-edge-tunnel.sh']],
|
|
285
|
-
['cloudrun-deploy.sh', (0, deploy_init_1.renderTarget)('cloudrun', CTX)['cloudrun-deploy.sh']],
|
|
286
|
-
['cloudrun-edge-deploy.sh', (0, deploy_init_1.renderTarget)('cloudrun', { ...CTX, ingress: 'edge' })['cloudrun-deploy.sh']],
|
|
287
|
-
];
|
|
288
|
-
for (const [name, content] of scripts) {
|
|
289
|
-
const p = path.join(tmp, name);
|
|
290
|
-
fs.writeFileSync(p, content);
|
|
291
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', p]), `${name} failed bash -n`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
finally {
|
|
295
|
-
fs.rmSync(tmp, { recursive: true, force: true });
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
test('cloudrun IAP is applied on the beta track with IAM bindings (no GA --iap on deploy)', () => {
|
|
299
|
-
const sh = (0, deploy_init_1.renderTarget)('cloudrun', CTX)['cloudrun-deploy.sh'];
|
|
300
|
-
assert.ok(sh.includes('--iap'), 'IAP enablement still referenced');
|
|
301
|
-
// Beta track: gcloud beta run services update --iap (GA `gcloud run ... --iap` is unsupported).
|
|
302
|
-
assert.ok(sh.includes('gcloud beta run services update'), 'IAP must be enabled on the beta track');
|
|
303
|
-
// Public access is governed by the run.invoker binding, stripped here; IAP role granted per IAP_MEMBERS.
|
|
304
|
-
assert.ok(sh.includes('remove-iam-policy-binding'), 'must strip allUsers invoker binding to keep service private');
|
|
305
|
-
assert.ok(sh.includes('roles/iap.httpsResourceAccessor'), 'must grant IAP-secured Web User role');
|
|
306
|
-
assert.ok(sh.includes('IAP_MEMBERS'), 'must surface IAP_MEMBERS for per-principal grants');
|
|
307
|
-
// The old GA `services update --no-allow-unauthenticated` (not a real flag) and inline IAP_FLAG are gone.
|
|
308
|
-
assert.ok(!sh.includes('IAP_FLAG'), 'IAP_FLAG array removed in favor of the beta-track block');
|
|
309
|
-
assert.ok(!sh.includes('--no-allow-unauthenticated "${IAP_FLAG[@]}"'), 'old GA IAP update line removed');
|
|
310
|
-
assert.ok(!sh.includes('AGENTHIPPO_SERVE_API_KEY'), 'serve bearer key must not appear — auth is IAP');
|
|
311
|
-
assert.ok(sh.includes('docker build'));
|
|
312
|
-
assert.ok(sh.includes('deploy/Dockerfile'));
|
|
313
|
-
});
|
|
314
|
-
test('cloudrun --ingress edge (Option B) fronts the agent with the AgentHippo edge, no IAP', () => {
|
|
315
|
-
const files = (0, deploy_init_1.renderTarget)('cloudrun', { ...CTX, ingress: 'edge' });
|
|
316
|
-
const svc = files['service.yaml'];
|
|
317
|
-
const caddy = files['Caddyfile'];
|
|
318
|
-
const sh = files['cloudrun-deploy.sh'];
|
|
319
|
-
// Edge artifacts present; IAP artifacts absent.
|
|
320
|
-
assert.ok(caddy, 'Caddyfile present');
|
|
321
|
-
assert.ok(svc.includes('name: caddy') && svc.includes('name: auth-broker') && svc.includes('name: oauth2-proxy'));
|
|
322
|
-
assert.ok(svc.includes('name: agent') && svc.includes('name: litellm') && svc.includes('name: otel-collector'), 'Spotlight sidecars still present behind the edge');
|
|
323
|
-
// Caddy is the ONLY ingress (one port); the agent is no longer ingress.
|
|
324
|
-
assert.strictEqual((svc.match(/containerPort:/g) || []).length, 1);
|
|
325
|
-
assert.ok(/- name: caddy\n image: caddy:/.test(svc), 'caddy is the first/ingress container');
|
|
326
|
-
// Agent listens on the internal edge port (not $PORT), so it doesn't clash with Caddy.
|
|
327
|
-
assert.ok(svc.includes('"--port", "8090"'), 'agent args override the port to the internal edge port');
|
|
328
|
-
assert.ok(svc.includes('"--www-dir", "/app/pack/deploy/www"'), 'agent still serves the www landing page');
|
|
329
|
-
// Sidecars share the pod's localhost (Cloud Run multi-container), not docker-DNS names.
|
|
330
|
-
assert.ok(!caddy.includes('oauth2-proxy:4180') && !caddy.includes('auth-broker:9780') && !caddy.includes('agent:8080'), 'edge Caddyfile must use localhost upstreams, not docker-DNS names');
|
|
331
|
-
assert.ok(caddy.includes('localhost:4180') && caddy.includes('localhost:9780') && caddy.includes('localhost:8090'), 'edge Caddyfile routes to oauth2-proxy / auth-broker / agent on localhost');
|
|
332
|
-
assert.ok(caddy.includes(':{$PORT}') && caddy.includes('auto_https off'), 'Caddy listens on HTTP :$PORT (Cloud Run terminates TLS)');
|
|
333
|
-
// IDE PKCE authorize must be SSO-gated before the catch-all /oauth/* → broker route.
|
|
334
|
-
assert.ok(caddy.includes('handle /oauth/authorize {'), 'edge Caddyfile gates /oauth/authorize');
|
|
335
|
-
assert.ok(/handle \/oauth\/authorize \{[\s\S]*?forward_auth localhost:4180[\s\S]*?copy_headers X-Auth-Request-Email/.test(caddy), '/oauth/authorize forward_auth copies X-Auth-Request-Email via localhost oauth2-proxy');
|
|
336
|
-
assert.ok(caddy.indexOf('handle /oauth/authorize {') < caddy.indexOf('handle /oauth/* {'), '/oauth/authorize must precede the catch-all /oauth/* handle');
|
|
337
|
-
assert.ok(caddy.includes('handle /oauth/browser-login {'), 'edge Caddyfile gates /oauth/browser-login');
|
|
338
|
-
// Caddyfile is mounted from a Secret (the deploy script upserts it).
|
|
339
|
-
assert.ok(svc.includes('${SERVICE}-caddyfile'), 'Caddyfile mounted from a secret');
|
|
340
|
-
// auth-broker reuses the agent image (CLI ships `auth-broker`).
|
|
341
|
-
assert.ok(/- name: auth-broker\n image: \$\{IMAGE\}/.test(svc) && svc.includes('args: ["auth-broker"]'), 'auth-broker reuses the agent image with `auth-broker` args');
|
|
342
|
-
// No IAP anywhere in the edge path.
|
|
343
|
-
assert.ok(!sh.includes('--iap') && !sh.includes('iap.httpsResourceAccessor') && !sh.includes('IAP_MEMBERS'), 'edge deploy must not enable IAP');
|
|
344
|
-
// Cloud Run is public (allUsers invoker) so traffic can reach Caddy to be authed.
|
|
345
|
-
assert.ok(sh.includes('add-iam-policy-binding') && sh.includes('allUsers') && sh.includes('roles/run.invoker'), 'edge deploy grants allUsers invoker (Caddy is the gate, not Cloud Run)');
|
|
346
|
-
// Edge secrets upserted.
|
|
347
|
-
assert.ok(sh.includes('${SERVICE}-caddyfile') && sh.includes('${SERVICE}-auth-jwt-secret')
|
|
348
|
-
&& sh.includes('${SERVICE}-oauth-client-id') && sh.includes('${SERVICE}-oauth-cookie-secret'), 'edge deploy upserts Caddyfile + JWT + OAuth secrets');
|
|
349
|
-
// Render includes the edge placeholders.
|
|
350
|
-
assert.ok(sh.includes('${PUBLIC_HOSTNAME}') && sh.includes('${ALLOWED_DOMAINS}'), 'deploy renders edge hostname/domain placeholders');
|
|
351
|
-
// The edge split auth env is shipped (so the agent container never loads the JWT/OAuth secrets).
|
|
352
|
-
assert.ok(files['.env.auth.example'], 'edge ships .env.auth.example (split auth secrets)');
|
|
353
|
-
assert.ok(files['.env.auth.example'].includes('OAUTH2_PROXY_CLIENT_ID') && files['.env.auth.example'].includes('AGENTHIPPO_AUTH_JWT_SECRET'));
|
|
354
|
-
// Note: service.yaml is a sed-template (${SERVICE}/${IMAGE}/${PUBLIC_HOSTNAME}/…), so it is
|
|
355
|
-
// only valid YAML after the deploy script substitutes placeholders — same as the IAP template.
|
|
356
|
-
assert.ok(files['README.md'].includes('**Egress:**'), 'edge README documents the egress allowlist');
|
|
357
|
-
});
|
|
358
|
-
test('cloudrun --ingress edge-fed --model-routing gateway drops LiteLLM/OTEL — agent calls gateway.agenthippo.ai', () => {
|
|
359
|
-
const files = (0, deploy_init_1.renderTarget)('cloudrun', {
|
|
360
|
-
...CTX, ingress: 'edge-fed', modelRouting: 'gateway', model: 'ah-auto',
|
|
361
|
-
idpUrl: 'https://auth.agenthippo.ai', cookieDomain: '.agenthippo.ai',
|
|
362
|
-
});
|
|
363
|
-
const svc = files['service.yaml'];
|
|
364
|
-
const sh = files['cloudrun-deploy.sh'];
|
|
365
|
-
assert.ok(svc.includes('name: caddy') && svc.includes('name: auth-broker') && svc.includes('name: agent'));
|
|
366
|
-
assert.ok(!svc.includes('name: litellm') && !svc.includes('name: otel-collector'), 'no Spotlight sidecars');
|
|
367
|
-
assert.ok(svc.includes('https://gateway.agenthippo.ai/v1'), 'agent points at org gateway');
|
|
368
|
-
assert.ok(!svc.includes('http://localhost:4443'), 'agent must not route through in-pod LiteLLM');
|
|
369
|
-
assert.ok(!files['otel-collector.yaml'], 'no otel config artifact');
|
|
370
|
-
assert.ok(!sh.includes('LITELLM_KEY') && !sh.includes('otel-config'), 'deploy skips litellm/otel secrets');
|
|
371
|
-
assert.ok(sh.includes('${SERVICE}-provider-key'), 'gateway key stored as provider-key secret');
|
|
372
|
-
assert.ok(files['.env.example'].includes('AGENTHIPPO_LITELLM_BASE_URL=https://gateway.agenthippo.ai/v1'));
|
|
373
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', '-c', sh]));
|
|
374
|
-
const rendered = svc
|
|
375
|
-
.replace(/\$\{SERVICE\}/g, 'emoji-gateway')
|
|
376
|
-
.replace(/\$\{IMAGE\}/g, 'us-docker.pkg.dev/x/y/z:latest')
|
|
377
|
-
.replace(/\$\{PUBLIC_HOSTNAME\}/g, 'emoji.agenthippo.ai')
|
|
378
|
-
.replace(/\$\{ALLOWED_DOMAINS\}/g, 'agenthippo.ai');
|
|
379
|
-
assert.doesNotThrow(() => yaml.load(rendered), 'rendered gateway-direct service.yaml is valid YAML');
|
|
380
|
-
});
|
|
381
|
-
test('cloudrun --ingress edge-fed (A′) verifies the shared JWT — no oauth2-proxy, no per-service OAuth', () => {
|
|
382
|
-
const files = (0, deploy_init_1.renderTarget)('cloudrun', {
|
|
383
|
-
...CTX, ingress: 'edge-fed', idpUrl: 'https://auth.agenthippo.ai', cookieDomain: '.agenthippo.ai',
|
|
384
|
-
});
|
|
385
|
-
const svc = files['service.yaml'];
|
|
386
|
-
const caddy = files['Caddyfile'];
|
|
387
|
-
const sh = files['cloudrun-deploy.sh'];
|
|
388
|
-
// Federated artifacts present.
|
|
389
|
-
assert.ok(caddy && svc && sh);
|
|
390
|
-
// Caddy + auth-broker + agent + Spotlight sidecars, but NO oauth2-proxy container.
|
|
391
|
-
assert.ok(svc.includes('name: caddy') && svc.includes('name: auth-broker') && svc.includes('name: agent'));
|
|
392
|
-
assert.ok(svc.includes('name: litellm') && svc.includes('name: otel-collector'), 'Spotlight sidecars present');
|
|
393
|
-
assert.ok(!svc.includes('name: oauth2-proxy'), 'edge-fed must NOT ship an oauth2-proxy container');
|
|
394
|
-
assert.strictEqual((svc.match(/containerPort:/g) || []).length, 1, 'Caddy is the only ingress port');
|
|
395
|
-
// auth-broker carries the federated cookie/IdP config + the shared JWT secret.
|
|
396
|
-
assert.ok(svc.includes('AGENTHIPPO_AUTH_COOKIE_DOMAIN') && svc.includes('AGENTHIPPO_AUTH_IDP_URL'), 'auth-broker gets the federated cookie domain + IdP URL');
|
|
397
|
-
assert.ok(svc.includes('${SERVICE}-auth-jwt-secret'), 'shared JWT secret mounted from a secret');
|
|
398
|
-
assert.ok(svc.includes('AGENTHIPPO_AUTH_COOKIE_NAME'), 'cookie name configured');
|
|
399
|
-
// In-memory state (no GCS bucket → no STATE_BUCKET requirement, no gcsfuse).
|
|
400
|
-
assert.ok(svc.includes('emptyDir') && svc.includes('medium: Memory'), 'state is in-memory emptyDir');
|
|
401
|
-
assert.ok(!svc.includes('gcsfuse') && !svc.includes('${STATE_BUCKET}'), 'no GCS state volume');
|
|
402
|
-
// Caddyfile: forward_auth to auth-broker only; no oauth2-proxy routes; IdP browser-login redirect.
|
|
403
|
-
assert.ok(caddy.includes('forward_auth localhost:9780'), 'Caddy verifies via auth-broker');
|
|
404
|
-
assert.ok(!caddy.includes('localhost:4180') && !caddy.includes('/oauth2/'), 'no oauth2-proxy routes in Caddyfile');
|
|
405
|
-
assert.ok(caddy.includes('https://auth.agenthippo.ai/oauth/browser-login'), 'browser 401 redirects to the central IdP browser-login endpoint');
|
|
406
|
-
assert.ok(caddy.includes('return_to='), 'redirect carries a return_to for the round trip');
|
|
407
|
-
// Deploy script: NO OAuth client/cookie secrets; upserts caddyfile + JWT + provider/litellm;
|
|
408
|
-
// allUsers invoker; domain-mappings create; no IAP; no STATE_BUCKET.
|
|
409
|
-
assert.ok(!sh.includes('OAUTH_CLIENT_ID') && !sh.includes('OAUTH_CLIENT_SECRET') && !sh.includes('OAUTH_COOKIE_SECRET'), 'edge-fed deploy must not reference any Google OAuth client');
|
|
410
|
-
assert.ok(sh.includes('${SERVICE}-caddyfile') && sh.includes('${SERVICE}-auth-jwt-secret')
|
|
411
|
-
&& sh.includes('${SERVICE}-provider-key') && sh.includes('${SERVICE}-litellm-key'), 'edge-fed deploy upserts caddyfile + shared JWT + provider/litellm secrets');
|
|
412
|
-
assert.ok(!sh.includes('STATE_BUCKET'), 'edge-fed has no STATE_BUCKET (in-memory state)');
|
|
413
|
-
assert.ok(sh.includes('allUsers') && sh.includes('roles/run.invoker'), 'allUsers invoker (Caddy is the gate)');
|
|
414
|
-
assert.ok(sh.includes('domain-mappings create'), 'maps the *.agenthippo.ai subdomain');
|
|
415
|
-
assert.ok(!sh.includes('--iap') && !sh.includes('iap.httpsResourceAccessor'), 'no IAP');
|
|
416
|
-
// Split auth env is federated: shared JWT secret + cookie domain + IdP URL, NO Google OAuth client.
|
|
417
|
-
const auth = files['.env.auth.example'];
|
|
418
|
-
assert.ok(auth, 'edge-fed ships .env.auth.example');
|
|
419
|
-
assert.ok(auth.includes('AGENTHIPPO_AUTH_JWT_SECRET') && auth.includes('AGENTHIPPO_AUTH_COOKIE_DOMAIN')
|
|
420
|
-
&& auth.includes('AGENTHIPPO_AUTH_IDP_URL'), 'federated auth env carries the shared JWT + cookie/IdP config');
|
|
421
|
-
assert.ok(!auth.includes('OAUTH2_PROXY_CLIENT_ID'), 'federated auth env has no Google OAuth client');
|
|
422
|
-
// Shell + rendered YAML are well-formed.
|
|
423
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', '-c', sh]));
|
|
424
|
-
const rendered = svc
|
|
425
|
-
.replace(/\$\{SERVICE\}/g, 'customer-service-edge')
|
|
426
|
-
.replace(/\$\{IMAGE\}/g, 'us-docker.pkg.dev/x/y/z:latest')
|
|
427
|
-
.replace(/\$\{LITELLM_IMAGE\}/g, 'ghcr.io/agenthippo/litellm-gateway:2026.06')
|
|
428
|
-
.replace(/\$\{PUBLIC_HOSTNAME\}/g, 'cs.agenthippo.ai')
|
|
429
|
-
.replace(/\$\{ALLOWED_DOMAINS\}/g, 'agenthippo.ai');
|
|
430
|
-
assert.doesNotThrow(() => yaml.load(rendered), 'rendered service.yaml is valid YAML');
|
|
431
|
-
assert.ok(files['README.md'].includes('**Egress:**'), 'edge-fed README documents the egress allowlist');
|
|
432
|
-
});
|
|
433
|
-
test('renderAuthEnvExample fed variant has no Google OAuth client', () => {
|
|
434
|
-
const out = (0, deploy_init_1.renderAuthEnvExample)({ hostname: 'cs.agenthippo.ai', fed: true, idpUrl: 'https://auth.agenthippo.ai', cookieDomain: '.agenthippo.ai' });
|
|
435
|
-
assert.ok(out.includes('AGENTHIPPO_AUTH_JWT_SECRET'));
|
|
436
|
-
assert.ok(out.includes('AGENTHIPPO_AUTH_COOKIE_DOMAIN=.agenthippo.ai'));
|
|
437
|
-
assert.ok(out.includes('AGENTHIPPO_AUTH_IDP_URL=https://auth.agenthippo.ai'));
|
|
438
|
-
assert.ok(!out.includes('OAUTH2_PROXY_CLIENT_ID'));
|
|
439
|
-
});
|
|
440
|
-
test('cloudrun claude pack ships the Spotlight multi-container stack (litellm + otel sidecars)', () => {
|
|
441
|
-
const files = (0, deploy_init_1.renderTarget)('cloudrun', CTX); // CTX engine = claude → model-routed → Spotlight
|
|
442
|
-
const svc = files['service.yaml'];
|
|
443
|
-
assert.ok(svc, 'service.yaml present');
|
|
444
|
-
assert.ok(svc.includes('name: agent') && svc.includes('name: litellm') && svc.includes('name: otel-collector'));
|
|
445
|
-
// Agent routes through the in-pod LiteLLM; LiteLLM exports to the in-pod collector.
|
|
446
|
-
assert.ok(svc.includes('http://localhost:4443'), 'agent → litellm on localhost');
|
|
447
|
-
assert.ok(svc.includes('http://localhost:4318'), 'litellm → collector on localhost');
|
|
448
|
-
assert.ok(svc.includes('gcsfuse.run.googleapis.com'), 'GCS-backed /state volume');
|
|
449
|
-
assert.ok(files['otel-collector.yaml'].includes('file/jsonl'), 'collector writes JSONL traces');
|
|
450
|
-
// Exactly one container exposes a port (Cloud Run ingress requirement).
|
|
451
|
-
assert.strictEqual((svc.match(/containerPort:/g) || []).length, 1);
|
|
452
|
-
// The CLAUDE_CODE_USE_VERTEX-vs-LiteLLM contradiction is gone from the active deploy.
|
|
453
|
-
assert.ok(!svc.includes('CLAUDE_CODE_USE_VERTEX'));
|
|
454
|
-
assert.ok(!files['cloudrun-deploy.sh'].includes('CLAUDE_CODE_USE_VERTEX'));
|
|
455
|
-
});
|
|
456
|
-
test('cloudrun cursor pack wires CURSOR_API_KEY secret and engine flags', () => {
|
|
457
|
-
const ctx = { ...CTX, packName: 'customer-service', engine: 'cursor', model: 'auto', image: 'ghcr.io/agenthippo/runtime:latest-cursor' };
|
|
458
|
-
const sh = (0, deploy_init_1.renderTarget)('cloudrun', ctx)['cloudrun-deploy.sh'];
|
|
459
|
-
const df = (0, deploy_init_1.renderTarget)('cloudrun', ctx)['Dockerfile'];
|
|
460
|
-
assert.ok(sh.includes('CURSOR_API_KEY=agenthippo-cursor-key:latest'));
|
|
461
|
-
assert.ok(sh.includes('AGENTHIPPO_CUSTOM_ENGINES_DIR=/engines'));
|
|
462
|
-
assert.ok(df.includes('"--engine","cursor"'));
|
|
463
|
-
assert.ok(df.includes('"--model","auto"'));
|
|
464
|
-
assert.ok(df.includes('runtime:latest-cursor'));
|
|
465
|
-
assert.ok(df.includes('"--www-dir","/app/pack/deploy/www"'));
|
|
466
|
-
assert.ok((0, deploy_init_1.renderTarget)('cloudrun', ctx)['www/index.html'].includes('<h1>customer-service</h1>'));
|
|
467
|
-
const env = (0, deploy_init_1.renderTarget)('cloudrun', ctx)['.env.example'];
|
|
468
|
-
assert.ok(env.includes('CURSOR_API_KEY='));
|
|
469
|
-
});
|
|
470
|
-
test('cloudrun IAP paths pin AGENTHIPPO_IDENTITY_HEADER so X-Agent-User cannot spoof identity', () => {
|
|
471
|
-
// Claude/codex → Spotlight multi-container path (renderCloudrunServiceYaml).
|
|
472
|
-
const spotlight = (0, deploy_init_1.renderTarget)('cloudrun', CTX);
|
|
473
|
-
assert.ok(spotlight['service.yaml'].includes('AGENTHIPPO_IDENTITY_HEADER'), 'Spotlight service.yaml must declare AGENTHIPPO_IDENTITY_HEADER');
|
|
474
|
-
assert.ok(spotlight['cloudrun-deploy.sh'].includes('x-goog-authenticated-user-email'), 'Spotlight deploy script must fill the pin with the IAP header when ENABLE_IAP=1');
|
|
475
|
-
// Cursor → T1-generic single-container path.
|
|
476
|
-
const cursorCtx = { ...CTX, engine: 'cursor', model: 'auto', image: 'ghcr.io/agenthippo/runtime:latest-cursor' };
|
|
477
|
-
const generic = (0, deploy_init_1.renderTarget)('cloudrun', cursorCtx);
|
|
478
|
-
assert.ok(generic['cloudrun-deploy.sh'].includes('AGENTHIPPO_IDENTITY_HEADER=x-goog-authenticated-user-email'), 'T1-generic deploy script must pin AGENTHIPPO_IDENTITY_HEADER to the IAP header');
|
|
479
|
-
// Both must skip the pin when ENABLE_IAP=0 (IAP never sets the header then — pinning
|
|
480
|
-
// unconditionally would make every request resolve to anonymous during bootstrap).
|
|
481
|
-
assert.ok(spotlight['cloudrun-deploy.sh'].includes('ENABLE_IAP'), 'Spotlight pin must be ENABLE_IAP-conditional');
|
|
482
|
-
assert.ok(generic['cloudrun-deploy.sh'].includes('IDENTITY_ENV='), 'T1-generic pin must be ENABLE_IAP-conditional');
|
|
483
|
-
});
|
|
484
|
-
test('databricks-app pins AGENTHIPPO_IDENTITY_HEADER to x-forwarded-email', () => {
|
|
485
|
-
const start = (0, deploy_init_1.renderTarget)('databricks-app', CTX)['start.js'];
|
|
486
|
-
assert.ok(start.includes("AGENTHIPPO_IDENTITY_HEADER: 'x-forwarded-email'"), 'Databricks App start.js must pin identity to the workspace SSO header');
|
|
487
|
-
// Bootstrap escape hatch (analogous to Cloud Run ENABLE_IAP=0).
|
|
488
|
-
assert.ok(start.includes("AGENTHIPPO_PIN_IDENTITY === '0'"), 'Databricks pin must be skippable via AGENTHIPPO_PIN_IDENTITY=0');
|
|
489
|
-
});
|
|
490
|
-
test('spcs egress is limited to the model API via one EAI', () => {
|
|
491
|
-
const sql = (0, deploy_init_1.renderTarget)('spcs', CTX)['deploy.sql'];
|
|
492
|
-
assert.ok(sql.includes('api.anthropic.com:443'));
|
|
493
|
-
assert.ok(/EXTERNAL ACCESS INTEGRATION/.test(sql));
|
|
494
|
-
});
|
|
495
|
-
test('aws-ecs bare (no ingress) is a single-container task with no edge opinion', () => {
|
|
496
|
-
const files = (0, deploy_init_1.renderTarget)('aws-ecs', CTX);
|
|
497
|
-
assert.ok(!files['Caddyfile'], 'bare aws-ecs ships no Caddy');
|
|
498
|
-
assert.ok(!files['.env.auth.example'], 'bare aws-ecs has no local auth secrets');
|
|
499
|
-
const task = JSON.parse(files['task-definition.json']);
|
|
500
|
-
assert.strictEqual(task.networkMode, 'awsvpc');
|
|
501
|
-
assert.deepStrictEqual(task.requiresCompatibilities, ['FARGATE']);
|
|
502
|
-
assert.strictEqual(task.containerDefinitions.length, 1, 'exactly one container: the agent');
|
|
503
|
-
assert.strictEqual(task.containerDefinitions[0].name, 'agent');
|
|
504
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', '-c', files['deploy.sh']]));
|
|
505
|
-
});
|
|
506
|
-
test('aws-ecs --ingress edge reuses the Cloud Run edge Caddyfile verbatim (shared sidecar identity layer)', () => {
|
|
507
|
-
const files = (0, deploy_init_1.renderTarget)('aws-ecs', { ...CTX, ingress: 'edge' });
|
|
508
|
-
const task = JSON.parse(files['task-definition.json']);
|
|
509
|
-
const names = task.containerDefinitions.map((c) => c.name);
|
|
510
|
-
assert.deepStrictEqual(names, ['caddy', 'oauth2-proxy', 'auth-broker', 'agent']);
|
|
511
|
-
assert.strictEqual(task.networkMode, 'awsvpc', 'one task, containers share localhost — same shape as Cloud Run sidecars');
|
|
512
|
-
assert.ok(files['Caddyfile'].includes('localhost:4180') && files['Caddyfile'].includes('localhost:9780'), 'localhost addressing (awsvpc shared network), not docker-DNS names');
|
|
513
|
-
assert.ok(files['.env.auth.example'].includes('OAUTH2_PROXY_CLIENT_ID'));
|
|
514
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', '-c', files['deploy.sh']]));
|
|
515
|
-
});
|
|
516
|
-
test('aws-ecs --ingress edge-fed drops oauth2-proxy from the task and Caddyfile', () => {
|
|
517
|
-
const files = (0, deploy_init_1.renderTarget)('aws-ecs', { ...CTX, ingress: 'edge-fed', idpUrl: 'https://auth.corp.example' });
|
|
518
|
-
const task = JSON.parse(files['task-definition.json']);
|
|
519
|
-
const names = task.containerDefinitions.map((c) => c.name);
|
|
520
|
-
assert.deepStrictEqual(names, ['caddy', 'auth-broker', 'agent']);
|
|
521
|
-
assert.ok(!files['Caddyfile'].includes('localhost:4180'), 'no oauth2-proxy upstream in a federated Caddyfile');
|
|
522
|
-
assert.ok(files['Caddyfile'].includes('forward_auth localhost:9780'), 'verifies via auth-broker');
|
|
523
|
-
assert.ok(files['Caddyfile'].includes('https://auth.corp.example/oauth/browser-login'));
|
|
524
|
-
assert.ok(!files['.env.auth.example'].includes('OAUTH2_PROXY_CLIENT_ID'));
|
|
525
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', '-c', files['deploy.sh']]));
|
|
526
|
-
});
|
|
527
|
-
test('azure-container-apps bare (no ingress) is a single-container app with no edge opinion', () => {
|
|
528
|
-
const files = (0, deploy_init_1.renderTarget)('azure-container-apps', CTX);
|
|
529
|
-
assert.ok(!files['Caddyfile'], 'bare azure-container-apps ships no Caddy');
|
|
530
|
-
assert.ok(!files['.env.auth.example'], 'bare azure-container-apps has no local auth secrets');
|
|
531
|
-
const spec = JSON.parse(files['containerapp.json']);
|
|
532
|
-
assert.strictEqual(spec.properties.template.containers.length, 1, 'exactly one container: the agent');
|
|
533
|
-
assert.strictEqual(spec.properties.template.containers[0].name, 'agent');
|
|
534
|
-
assert.strictEqual(spec.properties.configuration.ingress.external, true);
|
|
535
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', '-c', files['deploy.sh']]));
|
|
536
|
-
});
|
|
537
|
-
test('azure-container-apps --ingress edge reuses the Cloud Run edge Caddyfile verbatim (shared sidecar identity layer)', () => {
|
|
538
|
-
const files = (0, deploy_init_1.renderTarget)('azure-container-apps', { ...CTX, ingress: 'edge' });
|
|
539
|
-
const spec = JSON.parse(files['containerapp.json']);
|
|
540
|
-
const names = spec.properties.template.containers.map((c) => c.name);
|
|
541
|
-
assert.deepStrictEqual(names, ['caddy', 'oauth2-proxy', 'auth-broker', 'agent']);
|
|
542
|
-
assert.ok(files['Caddyfile'].includes('localhost:4180') && files['Caddyfile'].includes('localhost:9780'), 'localhost addressing (containers in one replica share it), not docker-DNS names');
|
|
543
|
-
assert.ok(files['.env.auth.example'].includes('OAUTH2_PROXY_CLIENT_ID'));
|
|
544
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', '-c', files['deploy.sh']]));
|
|
545
|
-
});
|
|
546
|
-
test('azure-container-apps --ingress edge-fed drops oauth2-proxy from the spec and Caddyfile', () => {
|
|
547
|
-
const files = (0, deploy_init_1.renderTarget)('azure-container-apps', { ...CTX, ingress: 'edge-fed', idpUrl: 'https://auth.corp.example' });
|
|
548
|
-
const spec = JSON.parse(files['containerapp.json']);
|
|
549
|
-
const names = spec.properties.template.containers.map((c) => c.name);
|
|
550
|
-
assert.deepStrictEqual(names, ['caddy', 'auth-broker', 'agent']);
|
|
551
|
-
assert.ok(!files['Caddyfile'].includes('localhost:4180'), 'no oauth2-proxy upstream in a federated Caddyfile');
|
|
552
|
-
assert.ok(files['Caddyfile'].includes('forward_auth localhost:9780'), 'verifies via auth-broker');
|
|
553
|
-
assert.ok(files['Caddyfile'].includes('https://auth.corp.example/oauth/browser-login'));
|
|
554
|
-
assert.ok(!files['.env.auth.example'].includes('OAUTH2_PROXY_CLIENT_ID'));
|
|
555
|
-
assert.doesNotThrow(() => (0, child_process_1.execFileSync)('bash', ['-n', '-c', files['deploy.sh']]));
|
|
556
|
-
});
|
|
557
|
-
// Governance (PLAN/deploy.md §6): every target must document what it can reach outbound —
|
|
558
|
-
// spcs enforces it via a network rule (tested above); every other target must at least name
|
|
559
|
-
// the host in its README so a security review doesn't have to reverse-engineer it.
|
|
560
|
-
test('every non-spcs target documents its egress allowlist in README', () => {
|
|
561
|
-
for (const target of deploy_init_1.DEPLOY_TARGETS) {
|
|
562
|
-
if (target === 'spcs') {
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
const readme = (0, deploy_init_1.renderTarget)(target, CTX)['README.md'];
|
|
566
|
-
assert.ok(readme?.includes('**Egress:**'), `${target} README must document its egress allowlist`);
|
|
567
|
-
assert.ok(readme.includes('api.anthropic.com:443'), `${target} README must name the model API host`);
|
|
568
|
-
}
|
|
569
|
-
});
|
|
570
|
-
// Governance (PLAN/deploy.md §6): a container that might ever be reached directly (bypassing
|
|
571
|
-
// its fronting proxy/Worker) must pin AGENTHIPPO_IDENTITY_HEADER so it can't be spoofed by an
|
|
572
|
-
// unauthenticated caller naming themselves via the same header name serve otherwise trusts as
|
|
573
|
-
// an unpinned alias. iap/databricks-app are covered by their own tests above; cloudflare here.
|
|
574
|
-
test('cloudflare pins AGENTHIPPO_IDENTITY_HEADER so a directly-reached container cannot be spoofed', () => {
|
|
575
|
-
const files = (0, deploy_init_1.renderTarget)('cloudflare', CTX);
|
|
576
|
-
assert.ok(files['Dockerfile'].includes('ENV AGENTHIPPO_IDENTITY_HEADER=x-agent-user'), 'cloudflare Dockerfile must pin identity to the header its own Worker sets');
|
|
577
|
-
assert.ok(files['README.md'].includes('Identity pin'), 'README must document the pin for whoever wires the Worker');
|
|
578
|
-
});
|
|
579
|
-
test('vm target includes edge compose profile; agent has no SSO-bypass host port', () => {
|
|
580
|
-
const files = (0, deploy_init_1.renderTarget)('vm', CTX);
|
|
581
|
-
const compose = files['docker-compose.yml'];
|
|
582
|
-
assert.ok(compose.includes('profiles: ["edge"]'));
|
|
583
|
-
assert.ok(compose.includes('profiles: ["tunnel"]'));
|
|
584
|
-
assert.ok(compose.includes('cloudflared:'));
|
|
585
|
-
assert.ok(compose.includes('oauth2-proxy'));
|
|
586
|
-
assert.ok(compose.includes('caddy:'));
|
|
587
|
-
assert.ok(!compose.includes('127.0.0.1:8080:8080'), 'agent must not publish host port in compose (SSO bypass)');
|
|
588
|
-
assert.ok(compose.includes('OAUTH2_PROXY_REVERSE_PROXY'));
|
|
589
|
-
assert.ok(compose.includes('./www:/srv/www'));
|
|
590
|
-
assert.ok(files['run.sh'].includes('127.0.0.1:8080'));
|
|
591
|
-
assert.ok(files['run-edge-tunnel.sh'].includes('--profile edge --profile tunnel'));
|
|
592
|
-
assert.ok(files['Caddyfile'].includes('lb_policy header X-Agent-User'));
|
|
593
|
-
assert.ok(files['Caddyfile'].includes('reverse_proxy agent:8080'));
|
|
594
|
-
assert.ok(files['Caddyfile'].includes('{$AGENTHIPPO_PUBLIC_HOSTNAME}'));
|
|
595
|
-
assert.ok(files['Caddyfile'].includes('@health path /health /ready'));
|
|
596
|
-
assert.ok(!files['Caddyfile'].includes('@api path /v1/* /health /ready'));
|
|
597
|
-
// IDE PKCE authorize must be SSO-gated (oauth2-proxy → X-Auth-Request-Email), not dumped via /oauth/*.
|
|
598
|
-
const caddy = files['Caddyfile'];
|
|
599
|
-
assert.ok(caddy.includes('handle /oauth/authorize {'), 'Caddyfile gates /oauth/authorize');
|
|
600
|
-
assert.ok(/handle \/oauth\/authorize \{[\s\S]*?forward_auth oauth2-proxy:4180[\s\S]*?copy_headers X-Auth-Request-Email/.test(caddy), '/oauth/authorize forward_auth copies X-Auth-Request-Email');
|
|
601
|
-
assert.ok(caddy.indexOf('handle /oauth/authorize {') < caddy.indexOf('handle /oauth/* {'), '/oauth/authorize must precede the catch-all /oauth/* handle');
|
|
602
|
-
assert.ok(caddy.includes('handle /oauth/browser-login {'), 'Caddyfile gates /oauth/browser-login');
|
|
603
|
-
assert.ok(files['www/index.html'].includes('<form id="chat">'));
|
|
604
|
-
// In-compose config-file tunnel (the proven-live path): cloudflared container → caddy:80.
|
|
605
|
-
assert.ok(files['cloudflared.yml'].includes('http://caddy:80'), 'in-compose cloudflared reaches Caddy on the docker network');
|
|
606
|
-
assert.ok(!files['cloudflared.yml'].includes('http://127.0.0.1:80'), 'a container cannot reach Caddy via 127.0.0.1');
|
|
607
|
-
assert.ok(!files['cloudflared.yml'].includes('localhost:8080'));
|
|
608
|
-
assert.ok(compose.includes('tunnel --config /etc/cloudflared/config.yml run'), 'config-file mode');
|
|
609
|
-
assert.ok(compose.includes('./cloudflared.yml:/etc/cloudflared/config.yml'), 'mounts the routing config');
|
|
610
|
-
assert.ok(compose.includes('./tunnel-credentials.json:/etc/cloudflared/credentials.json'), 'mounts the creds');
|
|
611
|
-
assert.ok(!compose.includes('TUNNEL_TOKEN'), 'config-file mode does not use a token');
|
|
612
|
-
assert.ok(files['agenthippo-edge.service'].includes('--profile tunnel'));
|
|
613
|
-
assert.ok(files['.env.auth.example'].includes('OAUTH2_PROXY_CLIENT_ID'));
|
|
614
|
-
assert.ok(!files['.env.example'].includes('OAUTH2_PROXY_CLIENT_SECRET'), 'OAuth secret lives in .env.auth, not the agent-shared .env');
|
|
615
|
-
assert.ok(files['.env.example'].includes('tunnel-credentials.json'), 'edge env documents the creds file, not a token');
|
|
616
|
-
assert.ok(!files['.env.example'].includes('CLOUDFLARE_TUNNEL_TOKEN'), 'token mode removed');
|
|
617
|
-
assert.ok(files['.env.example'].includes(`AGENTHIPPO_PUBLIC_HOSTNAME=${deploy_init_1.DEPLOY_PUBLIC_HOSTNAME}`));
|
|
618
|
-
});
|
|
619
|
-
test('compose target includes edge profile and Caddyfile', () => {
|
|
620
|
-
const files = (0, deploy_init_1.renderTarget)('compose', CTX);
|
|
621
|
-
assert.ok(files['docker-compose.yml'].includes('profiles: ["edge"]'));
|
|
622
|
-
assert.ok(files['Caddyfile']);
|
|
623
|
-
assert.ok(files['.env.auth.example'].includes('OAUTH2_PROXY_PROVIDER'));
|
|
624
|
-
});
|
|
625
|
-
for (const target of ['vm', 'compose']) {
|
|
626
|
-
test(`${target} --ingress edge-fed verifies the shared JWT — no oauth2-proxy, no per-agent OAuth`, () => {
|
|
627
|
-
const fedCtx = {
|
|
628
|
-
...CTX, ingress: 'edge-fed', idpUrl: 'https://auth.corp.example', cookieDomain: '.corp.example',
|
|
629
|
-
};
|
|
630
|
-
const files = (0, deploy_init_1.renderTarget)(target, fedCtx);
|
|
631
|
-
const compose = files['docker-compose.yml'];
|
|
632
|
-
const caddy = files['Caddyfile'];
|
|
633
|
-
const auth = files['.env.auth.example'];
|
|
634
|
-
assert.ok(!/^\s*oauth2-proxy:/m.test(compose), `${target} edge-fed must not ship an oauth2-proxy container`);
|
|
635
|
-
assert.ok(compose.includes('depends_on: [agent, auth-broker]'), 'caddy no longer depends on oauth2-proxy');
|
|
636
|
-
assert.ok(!caddy.includes('oauth2-proxy:4180') && !caddy.includes('/oauth2/*'), 'no oauth2-proxy routes in a federated Caddyfile');
|
|
637
|
-
assert.ok(caddy.includes('forward_auth auth-broker:9780'), 'Caddy verifies via auth-broker');
|
|
638
|
-
assert.ok(caddy.includes('https://auth.corp.example/oauth/browser-login'), 'browser 401 redirects to the central IdP browser-login endpoint');
|
|
639
|
-
assert.ok(caddy.includes('return_to='), 'redirect carries a return_to for the round trip');
|
|
640
|
-
// Target-specific behavior (sticky lb / static file split) must survive federation —
|
|
641
|
-
// identity mode and deployment shape are independent axes.
|
|
642
|
-
assert.ok(caddy.includes('lb_policy header X-Agent-User'), 'sticky agent proxy unaffected by identity mode');
|
|
643
|
-
assert.ok(caddy.includes('root * /srv/www'), 'static file serving unaffected by identity mode');
|
|
644
|
-
assert.ok(auth.includes('AGENTHIPPO_AUTH_JWT_SECRET') && auth.includes('AGENTHIPPO_AUTH_IDP_URL'));
|
|
645
|
-
assert.ok(!auth.includes('OAUTH2_PROXY_CLIENT_ID'), 'federated auth env has no Google OAuth client');
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
for (const target of ['vm', 'compose']) {
|
|
649
|
-
test(`${target} --ingress edge-fed --colocate ships only the agent — no Caddy/oauth2-proxy/auth-broker`, () => {
|
|
650
|
-
const colocateCtx = {
|
|
651
|
-
...CTX, ingress: 'edge-fed', idpUrl: 'https://auth.corp.example', colocate: true,
|
|
652
|
-
};
|
|
653
|
-
const files = (0, deploy_init_1.renderTarget)(target, colocateCtx);
|
|
654
|
-
const compose = files['docker-compose.yml'];
|
|
655
|
-
const snippet = files['agent-route.caddy'];
|
|
656
|
-
assert.ok(!files['Caddyfile'], `${target} colocate must not ship its own Caddyfile`);
|
|
657
|
-
assert.ok(!files['.env.auth.example'], `${target} colocate must not ship any local auth secrets`);
|
|
658
|
-
assert.ok(!/^\s*(caddy|oauth2-proxy|auth-broker):/m.test(compose), `${target} colocate compose must contain only the agent service`);
|
|
659
|
-
assert.ok(compose.includes('container_name: databricks-analyst-agent'), 'fixed, predictable container name');
|
|
660
|
-
assert.ok(compose.includes('networks: [colocate]') && compose.includes('external: true'), 'agent joins the IdP\'s external colocate network');
|
|
661
|
-
assert.ok(!compose.includes('ports:'), 'no host port — reached only via the IdP\'s Caddy, over the shared network');
|
|
662
|
-
assert.ok(snippet, 'route snippet generated for the operator to drop onto the IdP host');
|
|
663
|
-
// http:// (not https://) — must match the IdP Caddyfile's TLS model: auto_https is
|
|
664
|
-
// off there and TLS terminates in front, so an imported https:// block wouldn't
|
|
665
|
-
// match plaintext traffic forwarded from the front proxy.
|
|
666
|
-
assert.ok(snippet.includes(`http://${CTX.publicHostname} {`), 'snippet is a plain-HTTP site block, matching the IdP\'s own');
|
|
667
|
-
assert.ok(!snippet.includes(`https://${CTX.publicHostname}`), 'must not use https:// — the IdP Caddyfile has auto_https off');
|
|
668
|
-
assert.ok(snippet.includes('reverse_proxy databricks-analyst-agent:8080'), 'snippet proxies to the fixed container name');
|
|
669
|
-
assert.ok(snippet.includes('forward_auth auth-broker:9780'), 'identity is verified via the IdP\'s own auth-broker');
|
|
670
|
-
assert.ok(!snippet.includes('oauth2-proxy'), 'no oauth2-proxy — federated identity only');
|
|
671
|
-
assert.ok(snippet.includes('https://auth.corp.example/oauth/browser-login'), 'unauthenticated browsers redirect to the IdP');
|
|
672
|
-
const dataAccess = files['DATA-ACCESS.md'];
|
|
673
|
-
assert.ok(!dataAccess.includes('AGENTHIPPO_AUTH_JWT_SECRET'), 'colocated agent holds no JWT secret of its own');
|
|
674
|
-
assert.ok(!dataAccess.includes('**oauth2-proxy**'), 'colocated agent runs no oauth2-proxy');
|
|
675
|
-
assert.ok(files['www/index.html'], 'colocated agent ships browser landing page at GET /');
|
|
676
|
-
assert.ok(files['Dockerfile'].includes('--www-dir'), 'serve enables static chat UI after SSO');
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
test('colocate + edgeTurnConnector emits the P1 edge turn-registration block (obo-inprogress §2)', () => {
|
|
680
|
-
const ctx = {
|
|
681
|
-
...CTX, ingress: 'edge-fed', idpUrl: 'https://auth.corp.example', colocate: true,
|
|
682
|
-
edgeTurnConnector: 'databricks-analyst-connector:9091',
|
|
683
|
-
};
|
|
684
|
-
const snippet = (0, deploy_init_1.renderTarget)('vm', ctx)['agent-route.caddy'];
|
|
685
|
-
// Hygiene: a client-supplied nonce header must never pass through.
|
|
686
|
-
assert.ok(snippet.includes('request_header -X-Agent-Turn-Nonce'), 'inbound X-Agent-Turn-Nonce is stripped at the edge');
|
|
687
|
-
// Only POST chat turns register.
|
|
688
|
-
assert.ok(snippet.includes('@turnStart'), 'chat-turn matcher present');
|
|
689
|
-
assert.ok(/@turnStart\s*\{\s*\n\s*method POST\s*\n\s*path \/v1\/chat \/v1\/chat\/stream/m.test(snippet), '@turnStart matches POST /v1/chat and /v1/chat/stream only');
|
|
690
|
-
// The registration hop itself: forward_auth to the connector control API, nonce copied
|
|
691
|
-
// onto the proxied request, register token from the EDGE stack env (never the agent).
|
|
692
|
-
assert.ok(snippet.includes('forward_auth @turnStart databricks-analyst-connector:9091'), 'turn registration targets the connector control API');
|
|
693
|
-
assert.ok(snippet.includes('uri /edge/turn'), 'registration path is /edge/turn');
|
|
694
|
-
assert.ok(snippet.includes('copy_headers X-Agent-Turn-Nonce'), 'minted nonce copied onto the request');
|
|
695
|
-
assert.ok(snippet.includes('header_up Authorization "Bearer {$DATABRICKS_ANALYST_DATA_PLANE_TOKEN}"'), 'register token comes from the derived edge-stack env var');
|
|
696
|
-
// Ordering (the Caddy footgun the hand-built overlay documents): two forward_auth in one
|
|
697
|
-
// handle do NOT run in source order unless wrapped in route {} — identity must run first
|
|
698
|
-
// so X-Agent-User exists when the sidecar mints the nonce.
|
|
699
|
-
assert.ok(snippet.includes('route {'), 'gated sequence wrapped in route {} to force source order');
|
|
700
|
-
const apiBlock = snippet.slice(snippet.indexOf('handle @apiWithAuth'));
|
|
701
|
-
const identityIdx = apiBlock.indexOf('forward_auth auth-broker:9780');
|
|
702
|
-
const turnIdx = apiBlock.indexOf('forward_auth @turnStart');
|
|
703
|
-
const proxyIdx = apiBlock.indexOf('reverse_proxy databricks-analyst-agent:8080');
|
|
704
|
-
assert.ok(identityIdx >= 0 && turnIdx > identityIdx && proxyIdx > turnIdx, 'order inside route {}: identity → turn registration → proxy');
|
|
705
|
-
// Both gated paths carry the hop: Bearer API clients AND cookie-auth browser clients.
|
|
706
|
-
assert.strictEqual(snippet.split('uri /edge/turn').length - 1, 2, 'turn registration on both the @apiWithAuth and browser catch-all paths');
|
|
707
|
-
// The operator README says where the token goes (IdP stack env, never the agent).
|
|
708
|
-
const readme = (0, deploy_init_1.renderTarget)('vm', ctx)['README.md'];
|
|
709
|
-
assert.ok(readme.includes('DATABRICKS_ANALYST_DATA_PLANE_TOKEN'), 'README documents the edge-stack register token env var');
|
|
710
|
-
});
|
|
711
|
-
test('without edgeTurnConnector the route snippet is unchanged — no /edge/turn, no route {}', () => {
|
|
712
|
-
const ctx = {
|
|
713
|
-
...CTX, ingress: 'edge-fed', idpUrl: 'https://auth.corp.example', colocate: true,
|
|
714
|
-
};
|
|
715
|
-
const snippet = (0, deploy_init_1.renderTarget)('vm', ctx)['agent-route.caddy'];
|
|
716
|
-
assert.ok(!snippet.includes('/edge/turn') && !snippet.includes('@turnStart')
|
|
717
|
-
&& !snippet.includes('X-Agent-Turn-Nonce') && !snippet.includes('route {'), 'turn registration is strictly opt-in; default output must not change');
|
|
718
|
-
});
|
|
719
|
-
test('--colocate is a no-op without --ingress edge-fed (still ships the normal self-issued edge)', () => {
|
|
720
|
-
const files = (0, deploy_init_1.renderTarget)('vm', { ...CTX, colocate: true });
|
|
721
|
-
assert.ok(files['Caddyfile'], 'no ingress edge-fed → colocate does not apply, normal vm artifacts render');
|
|
722
|
-
assert.ok(!files['agent-route.caddy']);
|
|
723
|
-
});
|
|
724
|
-
test('agent service does not load .env.auth; broker + oauth2-proxy do', () => {
|
|
725
|
-
const compose = (0, deploy_init_1.renderTarget)('compose', CTX)['docker-compose.yml'];
|
|
726
|
-
// The agent runs model-driven tool calls — it must never receive the token-signing key.
|
|
727
|
-
const agentBlock = compose.slice(compose.indexOf(' agent:'), compose.indexOf(' oauth2-proxy:'));
|
|
728
|
-
assert.ok(agentBlock.includes('env_file:'), 'sanity: located the agent service block');
|
|
729
|
-
assert.ok(!agentBlock.includes('.env.auth'), 'agent must not load .env.auth (JWT signing key + OAuth secrets)');
|
|
730
|
-
assert.ok(compose.includes('.env.auth'), 'broker/oauth2-proxy must load .env.auth');
|
|
731
|
-
});
|
|
732
|
-
});
|
|
733
|
-
suite('deploy-init: deployCommand stages a self-contained pack', () => {
|
|
734
|
-
let pack;
|
|
735
|
-
let staging;
|
|
736
|
-
setup(() => {
|
|
737
|
-
pack = fs.mkdtempSync(path.join(os.tmpdir(), 'ah-pack-'));
|
|
738
|
-
staging = fs.mkdtempSync(path.join(os.tmpdir(), 'ah-stage-'));
|
|
739
|
-
fs.writeFileSync(path.join(pack, 'agent.yaml'), 'apiVersion: agenthippo.ai/v1\nkind: Agent\nmetadata:\n name: cs-test\n version: 0.1.0\nspec:\n engine: claude\n model: litellm/gpt-5.5\n');
|
|
740
|
-
fs.writeFileSync(path.join(pack, 'AGENTS.md'), '# cs-test\n');
|
|
741
|
-
fs.mkdirSync(path.join(pack, 'skills', 'demo', 'scripts'), { recursive: true });
|
|
742
|
-
fs.writeFileSync(path.join(pack, 'skills', 'demo', 'scripts', 'run.sh'), 'echo hi\n');
|
|
743
|
-
fs.writeFileSync(path.join(pack, 'skills', 'demo', 'scripts', 'requirements.txt'), 'requests\n');
|
|
744
|
-
// Ignored by the copy filter: dev cache, secrets, source deploy/.
|
|
745
|
-
fs.mkdirSync(path.join(pack, 'node_modules', 'foo'), { recursive: true });
|
|
746
|
-
fs.writeFileSync(path.join(pack, 'node_modules', 'foo', 'index.js'), 'x');
|
|
747
|
-
fs.writeFileSync(path.join(pack, '.env'), 'SECRET=1\n');
|
|
748
|
-
fs.writeFileSync(path.join(pack, 'my-credentials.json'), '{}\n');
|
|
749
|
-
fs.writeFileSync(path.join(pack, 'service-account.pem'), 'FAKE PEM\n');
|
|
750
|
-
fs.writeFileSync(path.join(pack, 'id_rsa'), 'FAKE PRIVATE KEY\n');
|
|
751
|
-
fs.mkdirSync(path.join(pack, 'deploy'), { recursive: true });
|
|
752
|
-
fs.writeFileSync(path.join(pack, 'deploy', 'leftover.yaml'), 'stale\n');
|
|
753
|
-
});
|
|
754
|
-
teardown(() => {
|
|
755
|
-
fs.rmSync(pack, { recursive: true, force: true });
|
|
756
|
-
fs.rmSync(staging, { recursive: true, force: true });
|
|
757
|
-
});
|
|
758
|
-
test('copies pack into staging root, drops node_modules/.env/credentials, writes deploy/ + root .dockerignore', async () => {
|
|
759
|
-
await (0, deploy_1.deployCommand)('init', pack, { target: 'cloudrun', out: staging, port: 8080, force: true });
|
|
760
|
-
// Pack content at the staging root.
|
|
761
|
-
assert.ok(fs.existsSync(path.join(staging, 'agent.yaml')), 'agent.yaml copied to staging root');
|
|
762
|
-
assert.ok(fs.existsSync(path.join(staging, 'AGENTS.md')), 'AGENTS.md copied to staging root');
|
|
763
|
-
assert.ok(fs.existsSync(path.join(staging, 'skills', 'demo', 'scripts', 'requirements.txt')), 'skill requirements.txt copied (needed by the build-time install step)');
|
|
764
|
-
// Filtered out (mirrors .dockerignore): no dev caches, no secrets.
|
|
765
|
-
assert.ok(!fs.existsSync(path.join(staging, 'node_modules')), 'node_modules must not be copied into staging');
|
|
766
|
-
assert.ok(!fs.existsSync(path.join(staging, '.env')), '.env must not be copied into staging');
|
|
767
|
-
assert.ok(!fs.existsSync(path.join(staging, 'my-credentials.json')), 'credentials json must not be copied');
|
|
768
|
-
assert.ok(!fs.existsSync(path.join(staging, 'service-account.pem')), '.pem key must not be copied');
|
|
769
|
-
assert.ok(!fs.existsSync(path.join(staging, 'id_rsa')), 'id_rsa key must not be copied');
|
|
770
|
-
// The source pack's own deploy/ is not copied (the generator owns staging/deploy/).
|
|
771
|
-
assert.ok(!fs.existsSync(path.join(staging, 'deploy', 'leftover.yaml')), 'source deploy/ must not be copied');
|
|
772
|
-
// Generated artifacts under staging/deploy/.
|
|
773
|
-
assert.ok(fs.existsSync(path.join(staging, 'deploy', 'Dockerfile')), 'Dockerfile at staging/deploy/Dockerfile');
|
|
774
|
-
assert.ok(fs.existsSync(path.join(staging, 'deploy', 'service.yaml')), 'service.yaml at staging/deploy/');
|
|
775
|
-
assert.ok(fs.existsSync(path.join(staging, 'deploy', 'cloudrun-deploy.sh')), 'cloudrun-deploy.sh at staging/deploy/');
|
|
776
|
-
// .dockerignore at the staging root (build-context root), NOT under deploy/.
|
|
777
|
-
assert.ok(fs.existsSync(path.join(staging, '.dockerignore')), '.dockerignore at staging root (context root)');
|
|
778
|
-
assert.ok(!fs.existsSync(path.join(staging, 'deploy', '.dockerignore')), '.dockerignore must not be under deploy/ (Docker would ignore it)');
|
|
779
|
-
// The generated build script self-anchors to the staging root.
|
|
780
|
-
const sh = fs.readFileSync(path.join(staging, 'deploy', 'cloudrun-deploy.sh'), 'utf8');
|
|
781
|
-
assert.ok(sh.includes('cd "$(dirname "$0")/.."'), 'cloudrun-deploy.sh anchors to staging root');
|
|
782
|
-
assert.ok(sh.includes('docker build') && sh.includes('deploy/Dockerfile') && sh.includes('-t "$IMAGE"'), 'build sources deploy/Dockerfile (sed-rewritten to AR base) with the staging root as context');
|
|
783
|
-
});
|
|
784
|
-
test('--packdir flag is honored as an alternative to the positional arg', async () => {
|
|
785
|
-
await (0, deploy_1.deployCommand)('init', undefined, { target: 'cloudrun', out: staging, port: 8080, force: true, packdir: pack });
|
|
786
|
-
assert.ok(fs.existsSync(path.join(staging, 'agent.yaml')), 'packdir flag resolved the pack');
|
|
787
|
-
assert.ok(fs.existsSync(path.join(staging, 'deploy', 'Dockerfile')), 'artifacts generated from packdir pack');
|
|
788
|
-
});
|
|
789
|
-
test('--blueprint team-pilot --platform cloudrun resolves to IAP (no Caddyfile, no oauth2-proxy)', async () => {
|
|
790
|
-
await (0, deploy_1.deployCommand)('init', pack, {
|
|
791
|
-
target: 'vm', blueprint: 'team-pilot', platform: 'cloudrun', out: staging, port: 8080, force: true,
|
|
792
|
-
});
|
|
793
|
-
assert.ok(fs.existsSync(path.join(staging, 'deploy', 'cloudrun-deploy.sh')), 'cloudrun artifacts generated');
|
|
794
|
-
assert.ok(!fs.existsSync(path.join(staging, 'deploy', 'Caddyfile')), 'team-pilot on cloudrun is IAP — no Caddy edge');
|
|
795
|
-
const sh = fs.readFileSync(path.join(staging, 'deploy', 'cloudrun-deploy.sh'), 'utf8');
|
|
796
|
-
assert.ok(sh.includes('--iap'), 'blueprint-resolved --ingress iap reaches the actual IAP deploy path');
|
|
797
|
-
});
|
|
798
|
-
test('--blueprint enterprise-fleet --platform vm resolves to a federated (no oauth2-proxy) edge', async () => {
|
|
799
|
-
await (0, deploy_1.deployCommand)('init', pack, {
|
|
800
|
-
target: 'cloudrun', blueprint: 'enterprise-fleet', platform: 'vm', out: staging, port: 8080, force: true,
|
|
801
|
-
idpUrl: 'https://auth.corp.example',
|
|
802
|
-
});
|
|
803
|
-
const caddy = fs.readFileSync(path.join(staging, 'deploy', 'Caddyfile'), 'utf8');
|
|
804
|
-
const compose = fs.readFileSync(path.join(staging, 'deploy', 'docker-compose.yml'), 'utf8');
|
|
805
|
-
assert.ok(!caddy.includes('oauth2-proxy:4180'), 'enterprise-fleet federates — no oauth2-proxy in the Caddyfile');
|
|
806
|
-
assert.ok(!/^\s*oauth2-proxy:/m.test(compose), 'enterprise-fleet federates — no oauth2-proxy container');
|
|
807
|
-
});
|
|
808
|
-
});
|
|
809
|
-
suite('deploy-init: copyPackToStaging dereferences symlinks + skips broken ones', () => {
|
|
810
|
-
let pack;
|
|
811
|
-
let staging;
|
|
812
|
-
setup(() => {
|
|
813
|
-
pack = fs.mkdtempSync(path.join(os.tmpdir(), 'ah-pack-'));
|
|
814
|
-
staging = fs.mkdtempSync(path.join(os.tmpdir(), 'ah-stage-'));
|
|
815
|
-
fs.writeFileSync(path.join(pack, 'agent.yaml'), 'apiVersion: agenthippo.ai/v1\nkind: Agent\nmetadata:\n name: link-test\n version: 0.1.0\nspec:\n engine: claude\n');
|
|
816
|
-
// Real skill content + a shared file the skill links to (relative symlink → resolved on copy).
|
|
817
|
-
fs.mkdirSync(path.join(pack, 'skills', 'demo'), { recursive: true });
|
|
818
|
-
fs.writeFileSync(path.join(pack, 'skills', 'demo', 'REAL.md'), '# real content\n');
|
|
819
|
-
fs.symlinkSync('REAL.md', path.join(pack, 'skills', 'demo', 'SKILL.md')); // valid relative symlink
|
|
820
|
-
// Broken symlink: target does not exist (the data-analyst hydration bug pattern).
|
|
821
|
-
fs.mkdirSync(path.join(pack, 'skills', 'broken'), { recursive: true });
|
|
822
|
-
fs.symlinkSync('/nonexistent/abs/path/SKILL.md', path.join(pack, 'skills', 'broken', 'SKILL.md'));
|
|
823
|
-
});
|
|
824
|
-
teardown(() => {
|
|
825
|
-
fs.rmSync(pack, { recursive: true, force: true });
|
|
826
|
-
fs.rmSync(staging, { recursive: true, force: true });
|
|
827
|
-
});
|
|
828
|
-
test('valid symlinks are dereferenced to real files; broken symlinks are skipped + reported', () => {
|
|
829
|
-
const broken = (0, deploy_1.copyPackToStaging)(pack, staging);
|
|
830
|
-
// Valid symlink → real file content in staging (not a symlink). Docker-safe.
|
|
831
|
-
const staged = path.join(staging, 'skills', 'demo', 'SKILL.md');
|
|
832
|
-
assert.ok(fs.existsSync(staged), 'valid symlink content copied to staging');
|
|
833
|
-
assert.ok(!fs.lstatSync(staged).isSymbolicLink(), 'staged entry is a real file, not a symlink');
|
|
834
|
-
assert.strictEqual(fs.readFileSync(staged, 'utf8'), '# real content\n');
|
|
835
|
-
// Broken symlink → not staged, and reported in the broken list.
|
|
836
|
-
assert.ok(!fs.existsSync(path.join(staging, 'skills', 'broken', 'SKILL.md')), 'broken symlink must not be staged');
|
|
837
|
-
assert.ok(broken.includes('skills/broken/SKILL.md'), 'broken symlink reported in returned list');
|
|
838
|
-
});
|
|
839
|
-
});
|
|
840
|
-
suite('deploy-init: enterprise-deploy/idp/ (customer 0) stays in sync with `agenthippo idp init`', () => {
|
|
841
|
-
// PLAN/deploy.md — AgentHippo's own auth.agenthippo.ai instance is "customer 0": it must be
|
|
842
|
-
// stood up with the exact same `agenthippo idp init` a real enterprise customer would use,
|
|
843
|
-
// not a bespoke hand-maintained copy. This fails loudly if the checked-in files drift from
|
|
844
|
-
// what the generator produces for the same hostname (regenerate with:
|
|
845
|
-
// `agenthippo idp init --hostname auth.agenthippo.ai --out enterprise-deploy/idp --force`).
|
|
846
|
-
test('checked-in files are byte-identical to renderIdpArtifacts({ hostname: "auth.agenthippo.ai" })', () => {
|
|
847
|
-
const idpDir = path.join(__dirname, '..', '..', '..', '..', 'enterprise-deploy', 'idp');
|
|
848
|
-
const files = (0, idp_init_1.renderIdpArtifacts)({ hostname: 'auth.agenthippo.ai' });
|
|
849
|
-
for (const [rel, expected] of Object.entries(files)) {
|
|
850
|
-
const actual = fs.readFileSync(path.join(idpDir, rel), 'utf8');
|
|
851
|
-
assert.strictEqual(actual, expected, `enterprise-deploy/idp/${rel} is stale — regenerate with: agenthippo idp init --hostname auth.agenthippo.ai --out enterprise-deploy/idp --force`);
|
|
852
|
-
}
|
|
853
|
-
});
|
|
854
|
-
});
|