@pome-sh/cli 0.1.1 → 0.2.0
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/README.md +5 -6
- package/dist/build-info.json +3 -3
- package/dist/src/cli/eval.js +26 -8
- package/dist/src/cli/init-sdk.d.ts +0 -3
- package/dist/src/cli/init-sdk.js +117 -24
- package/dist/src/cli/main.js +23 -69
- package/dist/src/cli/register.d.ts +9 -0
- package/dist/src/cli/register.js +51 -5
- package/dist/src/cli/session.d.ts +7 -1
- package/dist/src/cli/session.js +87 -27
- package/dist/src/demo/runDemo.js +3 -2
- package/dist/src/hosted/client.d.ts +41 -5
- package/dist/src/hosted/client.js +333 -19
- package/dist/src/hosted/errors.d.ts +11 -1
- package/dist/src/hosted/errors.js +8 -1
- package/dist/src/hosted/evalResultView.d.ts +2 -0
- package/dist/src/hosted/evalResultView.js +22 -0
- package/dist/src/hosted/uploadAndFinalize.d.ts +18 -1
- package/dist/src/hosted/uploadAndFinalize.js +51 -3
- package/dist/src/recorder/inspect.js +20 -2
- package/dist/src/runner/mergeAdapterSignals.js +2 -2
- package/dist/src/runner/ports.js +3 -0
- package/dist/src/runner/runScenario.d.ts +79 -2
- package/dist/src/runner/runScenario.js +94 -36
- package/dist/src/runner/runScenarioHosted.d.ts +37 -0
- package/dist/src/runner/runScenarioHosted.js +257 -58
- package/dist/src/runner/runTrialGroup.d.ts +1 -3
- package/dist/src/runner/runTrialGroup.js +1 -3
- package/dist/src/scenario/parseScenario.d.ts +5 -0
- package/dist/src/scenario/parseScenario.js +135 -8
- package/dist/src/scenario/scenarioSchema.d.ts +200 -3
- package/dist/src/scenario/scenarioSchema.js +12 -1
- package/dist/src/twin/twinHarness.d.ts +10 -0
- package/dist/src/twin/twinHarness.js +6 -2
- package/dist/src/twin/twinStart.d.ts +19 -0
- package/dist/src/twin/twinStart.js +156 -0
- package/node_modules/@pome-sh/sdk/dist/fidelity-inventory.d.ts +121 -0
- package/node_modules/@pome-sh/sdk/dist/fidelity-inventory.js +204 -0
- package/node_modules/@pome-sh/sdk/dist/fidelity-inventory.js.map +1 -0
- package/node_modules/@pome-sh/sdk/dist/parity.d.ts +55 -0
- package/node_modules/@pome-sh/sdk/dist/parity.js +112 -0
- package/node_modules/@pome-sh/sdk/dist/parity.js.map +1 -0
- package/node_modules/@pome-sh/sdk/dist/server-helpers.d.ts +15 -0
- package/node_modules/@pome-sh/sdk/dist/server-helpers.js +96 -0
- package/node_modules/@pome-sh/sdk/dist/server-helpers.js.map +1 -1
- package/node_modules/@pome-sh/sdk/dist/server.d.ts +1 -1
- package/node_modules/@pome-sh/sdk/dist/server.js +9 -7
- package/node_modules/@pome-sh/sdk/dist/server.js.map +1 -1
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/errors.d.ts +39 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/errors.js +33 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/errors.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/evaluator-hooks.d.ts +13 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/evaluator-hooks.js +6 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/evaluator-hooks.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/finalize-shapes.d.ts +215 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/finalize-shapes.js +83 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/finalize-shapes.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/github-access-control.d.ts +175 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/github-access-control.js +182 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/github-access-control.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/identity.d.ts +90 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/identity.js +72 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/identity.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/index.d.ts +27 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/index.js +34 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/index.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/event-schema.d.ts +286 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/event-schema.js +33 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/event-schema.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.d.ts +65 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.js +455 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/index.d.ts +39 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/index.js +47 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/index.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/index.d.ts +25 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/index.js +27 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/index.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.d.ts +100 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js +185 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/map-span.d.ts +64 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/map-span.js +112 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/map-span.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/nano.d.ts +26 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/nano.js +61 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/nano.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/project.d.ts +42 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/project.js +96 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/project.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/semconv.d.ts +51 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/semconv.js +74 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/semconv.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/span-event.d.ts +134 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/span-event.js +152 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/span-event.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/recorder-events.d.ts +418 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/recorder-events.js +234 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/recorder-events.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/redaction.d.ts +2 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/redaction.js +152 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/redaction.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/rest.d.ts +198 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/rest.js +225 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/rest.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/run.d.ts +201 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/run.js +166 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/run.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/seed-state.d.ts +446 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/seed-state.js +167 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/seed-state.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/sessions.d.ts +53 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/sessions.js +51 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/sessions.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task-vocab.d.ts +66 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task-vocab.js +84 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task-vocab.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task.d.ts +483 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task.js +60 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task.js.map +1 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/package.json +54 -0
- package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/trace-contract.json +41 -0
- package/node_modules/@pome-sh/sdk/package.json +6 -2
- package/node_modules/@pome-sh/shared-types/dist/errors.d.ts +39 -0
- package/node_modules/@pome-sh/shared-types/dist/errors.js +33 -0
- package/node_modules/@pome-sh/shared-types/dist/errors.js.map +1 -0
- package/node_modules/@pome-sh/shared-types/dist/finalize-shapes.d.ts +252 -0
- package/node_modules/@pome-sh/shared-types/dist/finalize-shapes.js +122 -0
- package/node_modules/@pome-sh/shared-types/dist/finalize-shapes.js.map +1 -0
- package/node_modules/@pome-sh/shared-types/dist/identity.d.ts +90 -0
- package/node_modules/@pome-sh/shared-types/dist/identity.js +72 -0
- package/node_modules/@pome-sh/shared-types/dist/identity.js.map +1 -0
- package/node_modules/@pome-sh/shared-types/dist/index.d.ts +13 -1353
- package/node_modules/@pome-sh/shared-types/dist/index.js +14 -568
- package/node_modules/@pome-sh/shared-types/dist/index.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/otel/event-schema.d.ts +45 -0
- package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js +8 -0
- package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/otel/semconv.js +7 -0
- package/node_modules/@pome-sh/shared-types/dist/otel/semconv.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/recorder-events.d.ts +62 -0
- package/node_modules/@pome-sh/shared-types/dist/recorder-events.js +36 -0
- package/node_modules/@pome-sh/shared-types/dist/recorder-events.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/rest.d.ts +245 -0
- package/node_modules/@pome-sh/shared-types/dist/rest.js +303 -0
- package/node_modules/@pome-sh/shared-types/dist/rest.js.map +1 -0
- package/node_modules/@pome-sh/shared-types/dist/run.d.ts +7 -0
- package/node_modules/@pome-sh/shared-types/dist/run.js +4 -0
- package/node_modules/@pome-sh/shared-types/dist/run.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/seed-envelope.d.ts +14 -0
- package/node_modules/@pome-sh/shared-types/dist/seed-envelope.js +46 -0
- package/node_modules/@pome-sh/shared-types/dist/seed-envelope.js.map +1 -0
- package/node_modules/@pome-sh/shared-types/dist/seed-state.d.ts +446 -0
- package/node_modules/@pome-sh/shared-types/dist/seed-state.js +167 -0
- package/node_modules/@pome-sh/shared-types/dist/seed-state.js.map +1 -0
- package/node_modules/@pome-sh/shared-types/dist/sessions.d.ts +53 -0
- package/node_modules/@pome-sh/shared-types/dist/sessions.js +51 -0
- package/node_modules/@pome-sh/shared-types/dist/sessions.js.map +1 -0
- package/node_modules/@pome-sh/shared-types/dist/task.d.ts +485 -0
- package/node_modules/@pome-sh/shared-types/dist/task.js +60 -0
- package/node_modules/@pome-sh/shared-types/dist/task.js.map +1 -0
- package/node_modules/@pome-sh/shared-types/package.json +2 -1
- package/node_modules/@pome-sh/shared-types/trace-contract.json +1 -1
- package/node_modules/@pome-sh/twin-github/FIDELITY.md +127 -67
- package/node_modules/@pome-sh/twin-github/README.md +3 -7
- package/node_modules/@pome-sh/twin-github/dist/examples/claude-github-agent.js +4 -20
- package/node_modules/@pome-sh/twin-github/dist/examples/claude-github-agent.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/access-control.d.ts +3 -3
- package/node_modules/@pome-sh/twin-github/dist/src/domain.d.ts +141 -3
- package/node_modules/@pome-sh/twin-github/dist/src/domain.js +0 -0
- package/node_modules/@pome-sh/twin-github/dist/src/domain.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/routes.js +2 -0
- package/node_modules/@pome-sh/twin-github/dist/src/routes.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/seed.d.ts +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/serializers.d.ts +2 -2
- package/node_modules/@pome-sh/twin-github/dist/src/server.js +7 -6
- package/node_modules/@pome-sh/twin-github/dist/src/server.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/tools.d.ts +168 -18
- package/node_modules/@pome-sh/twin-github/dist/src/tools.js +23 -0
- package/node_modules/@pome-sh/twin-github/dist/src/tools.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/fidelity.inventory.json +773 -0
- package/node_modules/@pome-sh/twin-github/package.json +5 -4
- package/node_modules/@pome-sh/twin-slack/FIDELITY.md +121 -35
- package/node_modules/@pome-sh/twin-slack/README.md +2 -6
- package/node_modules/@pome-sh/twin-slack/dist/scripts/fidelity-parity.d.ts +1 -0
- package/node_modules/@pome-sh/twin-slack/dist/scripts/fidelity-parity.js +92 -0
- package/node_modules/@pome-sh/twin-slack/dist/scripts/fidelity-parity.js.map +1 -0
- package/node_modules/@pome-sh/twin-slack/dist/scripts/smoke.js +3 -3
- package/node_modules/@pome-sh/twin-slack/dist/scripts/smoke.js.map +1 -1
- package/node_modules/@pome-sh/twin-slack/dist/scripts/validate-mcp.js +1 -1
- package/node_modules/@pome-sh/twin-slack/dist/scripts/validate-mcp.js.map +1 -1
- package/node_modules/@pome-sh/twin-slack/dist/src/server.js +7 -6
- package/node_modules/@pome-sh/twin-slack/dist/src/server.js.map +1 -1
- package/node_modules/@pome-sh/twin-slack/dist/src/tools.d.ts +30 -4
- package/node_modules/@pome-sh/twin-slack/dist/src/tools.js +48 -1
- package/node_modules/@pome-sh/twin-slack/dist/src/tools.js.map +1 -1
- package/node_modules/@pome-sh/twin-slack/fidelity.inventory.json +377 -0
- package/node_modules/@pome-sh/twin-slack/package.json +5 -4
- package/node_modules/@pome-sh/twin-stripe/FIDELITY.md +151 -50
- package/node_modules/@pome-sh/twin-stripe/README.md +42 -45
- package/node_modules/@pome-sh/twin-stripe/dist/scripts/fidelity-parity.d.ts +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/scripts/fidelity-parity.js +186 -0
- package/node_modules/@pome-sh/twin-stripe/dist/scripts/fidelity-parity.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/scripts/smoke.js +8 -4
- package/node_modules/@pome-sh/twin-stripe/dist/scripts/smoke.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/db.js +26 -2
- package/node_modules/@pome-sh/twin-stripe/dist/src/db.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/billing.d.ts +101 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/billing.js +195 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/billing.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/charges.d.ts +15 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/charges.js +17 -5
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/charges.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/customers.d.ts +47 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/customers.js +103 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/customers.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/events.d.ts +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/events.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/index.d.ts +6 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/index.js +3 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/index.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-intents.d.ts +61 -2
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-intents.js +214 -52
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-intents.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-methods.d.ts +45 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-methods.js +159 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-methods.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/refunds.d.ts +6 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/refunds.js +16 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/refunds.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/schema.d.ts +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/schema.js +144 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/schema.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/stripe-domain.d.ts +420 -43
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/stripe-domain.js +442 -13
- package/node_modules/@pome-sh/twin-stripe/dist/src/domain/stripe-domain.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/errors.d.ts +19 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/errors.js +15 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/errors.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/ids.d.ts +2 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/ids.js +2 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/ids.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/_helpers.d.ts +8 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/_helpers.js +105 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/_helpers.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/billing.d.ts +4 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/billing.js +117 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/billing.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/customers.d.ts +4 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/customers.js +47 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/customers.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/index.js +6 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/index.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-intents.js +28 -102
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-intents.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-methods.d.ts +4 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-methods.js +33 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-methods.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/seed.d.ts +10 -8
- package/node_modules/@pome-sh/twin-stripe/dist/src/seed.js +3 -2
- package/node_modules/@pome-sh/twin-stripe/dist/src/seed.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/serializers.d.ts +181 -21
- package/node_modules/@pome-sh/twin-stripe/dist/src/serializers.js +257 -34
- package/node_modules/@pome-sh/twin-stripe/dist/src/serializers.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/server.js +7 -6
- package/node_modules/@pome-sh/twin-stripe/dist/src/server.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/tools.d.ts +107 -5
- package/node_modules/@pome-sh/twin-stripe/dist/src/tools.js +139 -6
- package/node_modules/@pome-sh/twin-stripe/dist/src/tools.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/types.d.ts +73 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/upstream-types.d.ts +8 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/x402.js +6 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/x402.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/fidelity.inventory.json +425 -0
- package/node_modules/@pome-sh/twin-stripe/package.json +5 -4
- package/package.json +8 -8
- package/skills/pome-setup/SKILL.md +1 -1
|
@@ -1,8 +1,108 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
import { createEvalSessionResponseSchema, createSessionResponseSchema, finalizeResponseSchema, sessionPublicSchema, submitResultResponseSchema, } from "../types/shared.js";
|
|
3
|
+
import { z } from "zod";
|
|
3
4
|
import { HostedAuthError, HostedOrchError, HostedQuotaError, } from "./errors.js";
|
|
5
|
+
// Multi-twin (M3): provenance marker for `per_twin`. A single-twin OLD cloud
|
|
6
|
+
// ships NO `per_twin` key; `createSessionResponseSchema` then SYNTHESIZES one
|
|
7
|
+
// (host-rewrites api.pome.sh→mcp.pome.sh with no `/mcp` suffix). Consumers that
|
|
8
|
+
// build agent env from `per_twin[twin].mcp_url` must NOT trust that synthesized
|
|
9
|
+
// value — it would drift POME_*_MCP_URL off origin/main's `${twin_url}/mcp`. We
|
|
10
|
+
// record, on the parsed object as a NON-ENUMERABLE symbol (never serialized,
|
|
11
|
+
// never spread), whether the raw wire body actually carried a `per_twin` key.
|
|
12
|
+
const PER_TWIN_PROVENANCE = Symbol("pome.perTwinFromCloud");
|
|
13
|
+
function markPerTwinProvenance(session, fromCloud) {
|
|
14
|
+
Object.defineProperty(session, PER_TWIN_PROVENANCE, {
|
|
15
|
+
value: fromCloud,
|
|
16
|
+
enumerable: false,
|
|
17
|
+
configurable: true,
|
|
18
|
+
});
|
|
19
|
+
return session;
|
|
20
|
+
}
|
|
21
|
+
/** True when the cloud's create-session body actually returned a `per_twin`
|
|
22
|
+
* key (empty `{}` counts) — i.e. `per_twin` was NOT synthesized by the schema
|
|
23
|
+
* from a legacy `twin_url`-only response. Consumers use this to decide whether
|
|
24
|
+
* `per_twin[twin].mcp_url` is trustworthy. */
|
|
25
|
+
export function perTwinReturnedByCloud(session) {
|
|
26
|
+
return (session[PER_TWIN_PROVENANCE] === true);
|
|
27
|
+
}
|
|
28
|
+
/** Whether a raw create-session wire body carried a usable `per_twin` map
|
|
29
|
+
* (before schema synthesis). A `per_twin: null` counts as absent — the schema
|
|
30
|
+
* would synthesize entries for it, and synthesized URLs must not be trusted.
|
|
31
|
+
* Exported so the runner's env unit test can model the exact provenance the
|
|
32
|
+
* client stamps. */
|
|
33
|
+
export function rawBodyHadPerTwin(raw) {
|
|
34
|
+
if (typeof raw !== "object" || raw === null)
|
|
35
|
+
return false;
|
|
36
|
+
const pt = raw.per_twin;
|
|
37
|
+
return typeof pt === "object" && pt !== null;
|
|
38
|
+
}
|
|
39
|
+
// Keep these readers wire-identical to the additive exports in
|
|
40
|
+
// @pome-sh/shared-types 0.6.1. The CLI intentionally remains installable
|
|
41
|
+
// against 0.6.0 until that package batch has published.
|
|
42
|
+
// Scored finalize responses stay non-strict so additive M7 keys strip.
|
|
43
|
+
const finalizeScoredResponseSchema = finalizeResponseSchema.strip();
|
|
44
|
+
const finalizeStatusUrlSchema = z
|
|
45
|
+
.string()
|
|
46
|
+
.min(1)
|
|
47
|
+
.refine((value) => {
|
|
48
|
+
if (value.startsWith("/") && !value.startsWith("//"))
|
|
49
|
+
return true;
|
|
50
|
+
try {
|
|
51
|
+
new URL(value);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}, { message: "Invalid status_url" });
|
|
58
|
+
const finalizeAcceptedResponseSchema = z.object({
|
|
59
|
+
evaluation_id: z.string().min(1),
|
|
60
|
+
run_id: z.string().min(1),
|
|
61
|
+
status: z.literal("queued"),
|
|
62
|
+
status_url: finalizeStatusUrlSchema,
|
|
63
|
+
}).strict();
|
|
64
|
+
const finalizeStatusIdentitySchema = {
|
|
65
|
+
evaluation_id: z.string().min(1),
|
|
66
|
+
run_id: z.string().min(1),
|
|
67
|
+
};
|
|
68
|
+
const finalizeFailureErrorSchema = z.object({
|
|
69
|
+
type: z.string().min(1),
|
|
70
|
+
message: z.string().min(1),
|
|
71
|
+
details: z.record(z.string(), z.unknown()).optional(),
|
|
72
|
+
}).strict();
|
|
73
|
+
const finalizeStatusResponseSchema = z.discriminatedUnion("status", [
|
|
74
|
+
z.object({
|
|
75
|
+
...finalizeStatusIdentitySchema,
|
|
76
|
+
status: z.literal("queued"),
|
|
77
|
+
}).strict(),
|
|
78
|
+
z.object({
|
|
79
|
+
...finalizeStatusIdentitySchema,
|
|
80
|
+
status: z.literal("running"),
|
|
81
|
+
}).strict(),
|
|
82
|
+
z.object({
|
|
83
|
+
...finalizeStatusIdentitySchema,
|
|
84
|
+
status: z.literal("failed"),
|
|
85
|
+
error: finalizeFailureErrorSchema,
|
|
86
|
+
}).strict(),
|
|
87
|
+
z.object({
|
|
88
|
+
...finalizeStatusIdentitySchema,
|
|
89
|
+
status: z.literal("completed"),
|
|
90
|
+
result: finalizeScoredResponseSchema,
|
|
91
|
+
}).strict(),
|
|
92
|
+
]);
|
|
93
|
+
class RetryableFinalizeStatusError extends HostedOrchError {
|
|
94
|
+
retryAfterMs;
|
|
95
|
+
constructor(error, retryAfterMs) {
|
|
96
|
+
super(error.message, error.requestId, error.status);
|
|
97
|
+
this.retryAfterMs = retryAfterMs;
|
|
98
|
+
this.name = "RetryableFinalizeStatusError";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
4
101
|
export function createHostedClient(config) {
|
|
5
102
|
const timeoutMs = config.timeoutMs ?? 30_000;
|
|
103
|
+
const finalizeTimeoutMs = config.finalizeTimeoutMs ?? 5 * 60_000;
|
|
104
|
+
const finalizePollInitialDelayMs = Math.max(1, config.finalizePollInitialDelayMs ?? 500);
|
|
105
|
+
const finalizePollMaxDelayMs = Math.max(finalizePollInitialDelayMs, config.finalizePollMaxDelayMs ?? 5_000);
|
|
6
106
|
const authHeaders = config.authScheme === "bearer"
|
|
7
107
|
? { authorization: `Bearer ${config.apiKey}` }
|
|
8
108
|
: { "x-api-key": config.apiKey };
|
|
@@ -13,7 +113,7 @@ export function createHostedClient(config) {
|
|
|
13
113
|
json = text.length ? JSON.parse(text) : {};
|
|
14
114
|
}
|
|
15
115
|
catch {
|
|
16
|
-
throw new HostedOrchError(`non-JSON response (status ${res.status})
|
|
116
|
+
throw new HostedOrchError(`non-JSON response (status ${res.status})`, undefined, res.status);
|
|
17
117
|
}
|
|
18
118
|
if (!res.ok) {
|
|
19
119
|
// Cloud's REST envelope is `{ error: { type, message, request_id } }`.
|
|
@@ -33,37 +133,56 @@ export function createHostedClient(config) {
|
|
|
33
133
|
// labeled at-capacity state instead of a generic quota message.
|
|
34
134
|
throw new HostedQuotaError(msg, reqId, cloudDetails);
|
|
35
135
|
}
|
|
36
|
-
|
|
136
|
+
// Carry the envelope `error.type` (e.g. `multi_twin_unsupported`) so
|
|
137
|
+
// callers can map specific rejections to friendly hints.
|
|
138
|
+
throw new HostedOrchError(msg, reqId, res.status, cloudErr?.type);
|
|
37
139
|
}
|
|
38
140
|
try {
|
|
39
|
-
return parse(json);
|
|
141
|
+
return parse(json, res);
|
|
40
142
|
}
|
|
41
143
|
catch (err) {
|
|
42
|
-
throw new HostedOrchError(`unexpected response shape: ${err instanceof Error ? err.message : String(err)}
|
|
144
|
+
throw new HostedOrchError(`unexpected response shape: ${err instanceof Error ? err.message : String(err)}`, undefined, res.status);
|
|
43
145
|
}
|
|
44
146
|
}
|
|
45
|
-
async function postJson(path, body, parse) {
|
|
147
|
+
async function postJson(path, body, parse, options) {
|
|
46
148
|
const ctrl = new AbortController();
|
|
47
|
-
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
48
|
-
|
|
149
|
+
const timer = setTimeout(() => ctrl.abort(), options?.requestTimeoutMs ?? timeoutMs);
|
|
150
|
+
const abortFromCaller = () => ctrl.abort(options?.signal?.reason);
|
|
151
|
+
options?.signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
152
|
+
if (options?.signal?.aborted)
|
|
153
|
+
abortFromCaller();
|
|
49
154
|
try {
|
|
50
|
-
res = await fetch(`${config.baseUrl}${path}`, {
|
|
155
|
+
const res = await fetch(`${config.baseUrl}${path}`, {
|
|
51
156
|
method: "POST",
|
|
52
157
|
headers: {
|
|
53
158
|
...authHeaders,
|
|
54
159
|
"content-type": "application/json",
|
|
160
|
+
...options?.headers,
|
|
55
161
|
},
|
|
56
162
|
body: JSON.stringify(body),
|
|
57
163
|
signal: ctrl.signal,
|
|
58
164
|
});
|
|
165
|
+
if (res.ok &&
|
|
166
|
+
options?.acceptedStatuses &&
|
|
167
|
+
!options.acceptedStatuses.includes(res.status)) {
|
|
168
|
+
throw new HostedOrchError(`unexpected HTTP status ${res.status}`, undefined, res.status);
|
|
169
|
+
}
|
|
170
|
+
// Keep the timeout and caller cancellation active while consuming the
|
|
171
|
+
// body. fetch() resolves after headers, before a streamed body finishes.
|
|
172
|
+
return await readResponse(res, parse);
|
|
59
173
|
}
|
|
60
174
|
catch (err) {
|
|
175
|
+
if (err instanceof HostedAuthError ||
|
|
176
|
+
err instanceof HostedQuotaError ||
|
|
177
|
+
err instanceof HostedOrchError) {
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
61
180
|
throw new HostedOrchError(err instanceof Error ? err.message : "network error");
|
|
62
181
|
}
|
|
63
182
|
finally {
|
|
64
183
|
clearTimeout(timer);
|
|
184
|
+
options?.signal?.removeEventListener("abort", abortFromCaller);
|
|
65
185
|
}
|
|
66
|
-
return readResponse(res, parse);
|
|
67
186
|
}
|
|
68
187
|
async function getJsonBearer(url, bearer, parse) {
|
|
69
188
|
const ctrl = new AbortController();
|
|
@@ -103,6 +222,170 @@ export function createHostedClient(config) {
|
|
|
103
222
|
}
|
|
104
223
|
return readResponse(res, parse);
|
|
105
224
|
}
|
|
225
|
+
function retryAfterMs(value) {
|
|
226
|
+
if (value === null)
|
|
227
|
+
return null;
|
|
228
|
+
const seconds = Number(value);
|
|
229
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
230
|
+
return seconds * 1_000;
|
|
231
|
+
}
|
|
232
|
+
const at = Date.parse(value);
|
|
233
|
+
return Number.isNaN(at) ? null : Math.max(0, at - Date.now());
|
|
234
|
+
}
|
|
235
|
+
function nextFinalizePollDelayMs(currentDelayMs, serverDelayMs) {
|
|
236
|
+
if (serverDelayMs !== null) {
|
|
237
|
+
// Retry-After: 0 is legal, but accepting it literally on every response
|
|
238
|
+
// creates a hot authenticated loop. The configured initial delay is the
|
|
239
|
+
// lower bound for server hints.
|
|
240
|
+
return Math.max(serverDelayMs, finalizePollInitialDelayMs);
|
|
241
|
+
}
|
|
242
|
+
return Math.min(Math.max(currentDelayMs * 2, finalizePollInitialDelayMs), finalizePollMaxDelayMs);
|
|
243
|
+
}
|
|
244
|
+
async function sleepForPoll(ms, signal) {
|
|
245
|
+
if (signal?.aborted) {
|
|
246
|
+
throw new HostedOrchError("finalize aborted");
|
|
247
|
+
}
|
|
248
|
+
if (ms <= 0)
|
|
249
|
+
return;
|
|
250
|
+
await new Promise((resolve, reject) => {
|
|
251
|
+
let settled = false;
|
|
252
|
+
const timer = setTimeout(() => {
|
|
253
|
+
settled = true;
|
|
254
|
+
signal?.removeEventListener("abort", onAbort);
|
|
255
|
+
resolve();
|
|
256
|
+
}, ms);
|
|
257
|
+
const onAbort = () => {
|
|
258
|
+
if (settled)
|
|
259
|
+
return;
|
|
260
|
+
settled = true;
|
|
261
|
+
clearTimeout(timer);
|
|
262
|
+
signal?.removeEventListener("abort", onAbort);
|
|
263
|
+
reject(new HostedOrchError("finalize aborted"));
|
|
264
|
+
};
|
|
265
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
266
|
+
// Re-check after subscribe — abort between the early check and
|
|
267
|
+
// addEventListener would otherwise hang until the delay elapses.
|
|
268
|
+
if (signal?.aborted)
|
|
269
|
+
onAbort();
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function resolveFinalizeStatusUrl(statusUrl) {
|
|
273
|
+
const apiOrigin = new URL(config.baseUrl).origin;
|
|
274
|
+
let resolved;
|
|
275
|
+
try {
|
|
276
|
+
resolved = new URL(statusUrl, apiOrigin);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
throw new HostedOrchError("finalize status_url is not a valid URL");
|
|
280
|
+
}
|
|
281
|
+
if (resolved.origin !== apiOrigin) {
|
|
282
|
+
throw new HostedOrchError("finalize status_url must use the configured API origin");
|
|
283
|
+
}
|
|
284
|
+
return resolved;
|
|
285
|
+
}
|
|
286
|
+
async function getFinalizeStatus(url, signal, requestTimeoutMs) {
|
|
287
|
+
const ctrl = new AbortController();
|
|
288
|
+
const timer = setTimeout(() => ctrl.abort(), requestTimeoutMs);
|
|
289
|
+
const abortFromCaller = () => ctrl.abort(signal?.reason);
|
|
290
|
+
signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
291
|
+
if (signal?.aborted)
|
|
292
|
+
abortFromCaller();
|
|
293
|
+
try {
|
|
294
|
+
const res = await fetch(url, {
|
|
295
|
+
method: "GET",
|
|
296
|
+
headers: authHeaders,
|
|
297
|
+
signal: ctrl.signal,
|
|
298
|
+
// Node fetch preserves custom x-api-key headers across a cross-origin
|
|
299
|
+
// redirect. Refuse redirects so same-origin validation remains true
|
|
300
|
+
// for the server that actually receives tenant credentials.
|
|
301
|
+
redirect: "manual",
|
|
302
|
+
});
|
|
303
|
+
if (res.status >= 300 && res.status < 400) {
|
|
304
|
+
throw new HostedOrchError("finalize status request must not redirect", undefined, res.status);
|
|
305
|
+
}
|
|
306
|
+
const delay = retryAfterMs(res.headers.get("retry-after"));
|
|
307
|
+
// Keep both timeout signals active until the full body is consumed.
|
|
308
|
+
let status;
|
|
309
|
+
try {
|
|
310
|
+
status = await readResponse(res, (raw) => finalizeStatusResponseSchema.parse(raw));
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
if (err instanceof HostedOrchError &&
|
|
314
|
+
err.status !== undefined &&
|
|
315
|
+
err.status >= 500) {
|
|
316
|
+
throw new RetryableFinalizeStatusError(err, delay);
|
|
317
|
+
}
|
|
318
|
+
throw err;
|
|
319
|
+
}
|
|
320
|
+
return { status, retryAfterMs: delay };
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
if (signal?.aborted) {
|
|
324
|
+
throw new HostedOrchError("finalize aborted");
|
|
325
|
+
}
|
|
326
|
+
if (err instanceof HostedAuthError ||
|
|
327
|
+
err instanceof HostedQuotaError ||
|
|
328
|
+
err instanceof HostedOrchError) {
|
|
329
|
+
throw err;
|
|
330
|
+
}
|
|
331
|
+
throw new HostedOrchError(err instanceof Error ? err.message : "network error");
|
|
332
|
+
}
|
|
333
|
+
finally {
|
|
334
|
+
clearTimeout(timer);
|
|
335
|
+
signal?.removeEventListener("abort", abortFromCaller);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async function pollFinalize(accepted, deadline, signal) {
|
|
339
|
+
const statusUrl = resolveFinalizeStatusUrl(accepted.status_url);
|
|
340
|
+
let delayMs = finalizePollInitialDelayMs;
|
|
341
|
+
while (true) {
|
|
342
|
+
const remainingBeforeDelay = deadline - Date.now();
|
|
343
|
+
if (remainingBeforeDelay <= 0) {
|
|
344
|
+
throw new HostedOrchError("asynchronous finalize timed out");
|
|
345
|
+
}
|
|
346
|
+
await sleepForPoll(Math.min(delayMs, remainingBeforeDelay), signal);
|
|
347
|
+
const remainingBeforeRequest = deadline - Date.now();
|
|
348
|
+
if (remainingBeforeRequest <= 0) {
|
|
349
|
+
throw new HostedOrchError("asynchronous finalize timed out");
|
|
350
|
+
}
|
|
351
|
+
let polled;
|
|
352
|
+
try {
|
|
353
|
+
polled = await getFinalizeStatus(statusUrl, signal, Math.min(timeoutMs, remainingBeforeRequest));
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
if (signal?.aborted)
|
|
357
|
+
throw err;
|
|
358
|
+
if (err instanceof HostedOrchError &&
|
|
359
|
+
(err.status === undefined || err.status >= 500)) {
|
|
360
|
+
// The evaluation is durable. A transient status transport failure
|
|
361
|
+
// must not turn a still-running evaluation into a CLI failure.
|
|
362
|
+
delayMs = nextFinalizePollDelayMs(delayMs, err instanceof RetryableFinalizeStatusError
|
|
363
|
+
? err.retryAfterMs
|
|
364
|
+
: null);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
throw err;
|
|
368
|
+
}
|
|
369
|
+
if (polled.status.evaluation_id !== accepted.evaluation_id ||
|
|
370
|
+
polled.status.run_id !== accepted.run_id) {
|
|
371
|
+
throw new HostedOrchError("finalize status identifiers do not match the accepted evaluation");
|
|
372
|
+
}
|
|
373
|
+
if (polled.status.status === "completed") {
|
|
374
|
+
if (polled.status.result.run_id !== accepted.run_id) {
|
|
375
|
+
throw new HostedOrchError("finalize result run_id does not match the accepted evaluation");
|
|
376
|
+
}
|
|
377
|
+
return polled.status.result;
|
|
378
|
+
}
|
|
379
|
+
if (polled.status.status === "failed") {
|
|
380
|
+
const { type, message, details } = polled.status.error;
|
|
381
|
+
if (type === "quota_exceeded") {
|
|
382
|
+
throw new HostedQuotaError(message, undefined, details);
|
|
383
|
+
}
|
|
384
|
+
throw new HostedOrchError(`${type}: ${message}`);
|
|
385
|
+
}
|
|
386
|
+
delayMs = nextFinalizePollDelayMs(delayMs, polled.retryAfterMs);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
106
389
|
return {
|
|
107
390
|
async createSession(input) {
|
|
108
391
|
const body = {
|
|
@@ -121,7 +404,7 @@ export function createHostedClient(config) {
|
|
|
121
404
|
if (input.groupId) {
|
|
122
405
|
body.group_id = input.groupId;
|
|
123
406
|
}
|
|
124
|
-
return postJson("/v1/sessions", body, (raw) => createSessionResponseSchema.parse(raw));
|
|
407
|
+
return postJson("/v1/sessions", body, (raw) => markPerTwinProvenance(createSessionResponseSchema.parse(raw), rawBodyHadPerTwin(raw)));
|
|
125
408
|
},
|
|
126
409
|
async abandonSession(sessionId, input) {
|
|
127
410
|
const body = {};
|
|
@@ -210,22 +493,39 @@ export function createHostedClient(config) {
|
|
|
210
493
|
throw new HostedOrchError("POST /v1/sessions/:id/meta-upload-url returned unexpected shape");
|
|
211
494
|
});
|
|
212
495
|
},
|
|
213
|
-
async requestStateUploadUrl(sessionId) {
|
|
214
|
-
|
|
496
|
+
async requestStateUploadUrl(sessionId, twins) {
|
|
497
|
+
// Multi-twin (M3): a `{ twins }` body asks the cloud to sign a per-twin
|
|
498
|
+
// pair for each twin (returned under `per_twin`). Single-twin callers
|
|
499
|
+
// pass nothing and the body stays `{}` — byte-identical to pre-M3.
|
|
500
|
+
const body = twins && twins.length > 0 ? { twins } : {};
|
|
501
|
+
return postJson(`/v1/sessions/${encodeURIComponent(sessionId)}/state-upload-url`, body, (raw) => {
|
|
215
502
|
const isEntry = (x) => typeof x === "object" &&
|
|
216
503
|
x !== null &&
|
|
217
504
|
typeof x.url === "string" &&
|
|
218
505
|
typeof x.key === "string";
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
isEntry(
|
|
222
|
-
isEntry(
|
|
223
|
-
|
|
506
|
+
const isPair = (x) => typeof x === "object" &&
|
|
507
|
+
x !== null &&
|
|
508
|
+
isEntry(x.state_initial) &&
|
|
509
|
+
isEntry(x.state_final);
|
|
510
|
+
if (isPair(raw)) {
|
|
511
|
+
// Validate `per_twin` when present; drop it silently if malformed
|
|
512
|
+
// rather than failing the whole request (best-effort, like the
|
|
513
|
+
// rest of the upload surface).
|
|
514
|
+
const perTwinRaw = raw.per_twin;
|
|
515
|
+
let perTwin;
|
|
516
|
+
if (typeof perTwinRaw === "object" &&
|
|
517
|
+
perTwinRaw !== null &&
|
|
518
|
+
!Array.isArray(perTwinRaw) &&
|
|
519
|
+
Object.values(perTwinRaw).every(isPair)) {
|
|
520
|
+
perTwin = perTwinRaw;
|
|
521
|
+
}
|
|
522
|
+
return { ...raw, per_twin: perTwin };
|
|
224
523
|
}
|
|
225
524
|
throw new HostedOrchError("POST /v1/sessions/:id/state-upload-url returned unexpected shape");
|
|
226
525
|
});
|
|
227
526
|
},
|
|
228
|
-
async finalize(sessionId, input) {
|
|
527
|
+
async finalize(sessionId, input, options) {
|
|
528
|
+
const deadline = Date.now() + finalizeTimeoutMs;
|
|
229
529
|
const body = {
|
|
230
530
|
stop_reason: input.stopReason,
|
|
231
531
|
exit_code: input.exitCode,
|
|
@@ -250,7 +550,21 @@ export function createHostedClient(config) {
|
|
|
250
550
|
if (input.signalsStorageKey !== undefined) {
|
|
251
551
|
body.signals_storage_key = input.signalsStorageKey;
|
|
252
552
|
}
|
|
253
|
-
|
|
553
|
+
if (input.perTwinStateKeys !== undefined &&
|
|
554
|
+
Object.keys(input.perTwinStateKeys).length > 0) {
|
|
555
|
+
body.per_twin_state_keys = input.perTwinStateKeys;
|
|
556
|
+
}
|
|
557
|
+
const initial = await postJson(`/v1/sessions/${encodeURIComponent(sessionId)}/finalize`, body, (raw, response) => response.status === 202
|
|
558
|
+
? finalizeAcceptedResponseSchema.parse(raw)
|
|
559
|
+
: finalizeScoredResponseSchema.parse(raw), {
|
|
560
|
+
headers: { prefer: "respond-async" },
|
|
561
|
+
signal: options?.signal,
|
|
562
|
+
acceptedStatuses: [200, 201, 202],
|
|
563
|
+
requestTimeoutMs: Math.min(timeoutMs, finalizeTimeoutMs),
|
|
564
|
+
});
|
|
565
|
+
return "status_url" in initial
|
|
566
|
+
? pollFinalize(initial, deadline, options?.signal)
|
|
567
|
+
: initial;
|
|
254
568
|
},
|
|
255
569
|
async submitResult(sessionId, input) {
|
|
256
570
|
return postJson(`/v1/sessions/${encodeURIComponent(sessionId)}/result`, {
|
|
@@ -14,10 +14,20 @@ export declare class HostedQuotaError extends Error {
|
|
|
14
14
|
export declare class HostedOrchError extends Error {
|
|
15
15
|
readonly requestId?: string | undefined;
|
|
16
16
|
readonly status?: number | undefined;
|
|
17
|
+
/** Machine-readable `error.type` from the cloud envelope, when present
|
|
18
|
+
* (e.g. `multi_twin_unsupported` so the CLI can map an old-cloud
|
|
19
|
+
* multi-twin rejection to a friendly hint). Undefined for
|
|
20
|
+
* network/parse failures and the twin-pod 401 shape. */
|
|
21
|
+
readonly type?: string | undefined;
|
|
17
22
|
/** `status` is the HTTP status that produced this error, when one exists
|
|
18
23
|
* (network/parse failures leave it undefined). `pome eval` uses it to
|
|
19
24
|
* scope its reaped-session retry to 404/410 only (FDRS-656 review). */
|
|
20
|
-
constructor(message: string, requestId?: string | undefined, status?: number | undefined
|
|
25
|
+
constructor(message: string, requestId?: string | undefined, status?: number | undefined,
|
|
26
|
+
/** Machine-readable `error.type` from the cloud envelope, when present
|
|
27
|
+
* (e.g. `multi_twin_unsupported` so the CLI can map an old-cloud
|
|
28
|
+
* multi-twin rejection to a friendly hint). Undefined for
|
|
29
|
+
* network/parse failures and the twin-pod 401 shape. */
|
|
30
|
+
type?: string | undefined);
|
|
21
31
|
}
|
|
22
32
|
/** Bad invocation — missing scenario file, unknown flag value, etc. Maps
|
|
23
33
|
* to documented exit code 5 ("usage error"). F0-5a. */
|
|
@@ -49,13 +49,20 @@ export class HostedQuotaError extends Error {
|
|
|
49
49
|
export class HostedOrchError extends Error {
|
|
50
50
|
requestId;
|
|
51
51
|
status;
|
|
52
|
+
type;
|
|
52
53
|
/** `status` is the HTTP status that produced this error, when one exists
|
|
53
54
|
* (network/parse failures leave it undefined). `pome eval` uses it to
|
|
54
55
|
* scope its reaped-session retry to 404/410 only (FDRS-656 review). */
|
|
55
|
-
constructor(message, requestId, status
|
|
56
|
+
constructor(message, requestId, status,
|
|
57
|
+
/** Machine-readable `error.type` from the cloud envelope, when present
|
|
58
|
+
* (e.g. `multi_twin_unsupported` so the CLI can map an old-cloud
|
|
59
|
+
* multi-twin rejection to a friendly hint). Undefined for
|
|
60
|
+
* network/parse failures and the twin-pod 401 shape. */
|
|
61
|
+
type) {
|
|
56
62
|
super(message);
|
|
57
63
|
this.requestId = requestId;
|
|
58
64
|
this.status = status;
|
|
65
|
+
this.type = type;
|
|
59
66
|
this.name = "HostedOrchError";
|
|
60
67
|
}
|
|
61
68
|
}
|
|
@@ -33,6 +33,8 @@ export type ScoreStatus = "pass" | "fail" | "unevaluated";
|
|
|
33
33
|
export declare function scoreStatus(score: Score, passThreshold: number): ScoreStatus;
|
|
34
34
|
export declare function scenarioPassed(score: Score, passThreshold: number): boolean;
|
|
35
35
|
export declare function markerFor(outcome: CriterionOutcome): string;
|
|
36
|
+
export declare function criterionMarkerLabel(criterion: WireCriterion): string;
|
|
37
|
+
export declare function twinSkipSuffix(result: CriterionResult): string;
|
|
36
38
|
export declare function scoreCountsSummary(score: Score): string;
|
|
37
39
|
export declare function runScoreLine(score: Score, passThreshold: number, unevaluatedNumericLabel: string): string;
|
|
38
40
|
export {};
|
|
@@ -52,6 +52,28 @@ export function markerFor(outcome) {
|
|
|
52
52
|
return "-";
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
// Multi-twin (M3): the per-criterion bracket for terminal display —
|
|
56
|
+
// `[code]` / `[model]`, plus the `:<twin>` suffix when the criterion attributes
|
|
57
|
+
// to a specific twin (so a `[D:slack]`/`[P:github]` marker survives into the
|
|
58
|
+
// UNEVAL / criteria list). A bare (primary-twin) criterion renders `[code]`
|
|
59
|
+
// unchanged.
|
|
60
|
+
export function criterionMarkerLabel(criterion) {
|
|
61
|
+
return criterion.twin ? `[${criterion.type}:${criterion.twin}]` : `[${criterion.type}]`;
|
|
62
|
+
}
|
|
63
|
+
// Multi-twin (M3): when the cloud could not evaluate a criterion for a
|
|
64
|
+
// twin-related reason (a twin-tagged criterion, or a `no_matching_predicate`
|
|
65
|
+
// skip), name the twin inline so the UNEVAL line explains WHICH twin's timeline
|
|
66
|
+
// came up empty. Returns "" when there's nothing twin-specific to add.
|
|
67
|
+
export function twinSkipSuffix(result) {
|
|
68
|
+
const twin = result.criterion.twin;
|
|
69
|
+
if (!twin)
|
|
70
|
+
return "";
|
|
71
|
+
const outcome = outcomeOf(result);
|
|
72
|
+
const twinRelated = outcome === "skipped" ||
|
|
73
|
+
outcome === "errored" ||
|
|
74
|
+
/no_matching_predicate|no matching predicate/i.test(result.reason);
|
|
75
|
+
return twinRelated ? ` (twin: ${twin})` : "";
|
|
76
|
+
}
|
|
55
77
|
export function scoreCountsSummary(score) {
|
|
56
78
|
return `${score.passed ?? 0} passed, ${score.failed ?? 0} failed, ${score.skipped ?? 0} skipped, ${score.errored ?? 0} errored`;
|
|
57
79
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HostedClient } from "./client.js";
|
|
2
|
-
import type { FinalizeResponse } from "../types/shared.js";
|
|
2
|
+
import type { FinalizeResponse, PerTwinStateKeys } from "../types/shared.js";
|
|
3
3
|
import type { Score } from "./evalResultView.js";
|
|
4
4
|
/** The narrow client surface the upload orchestration needs. `pome eval`
|
|
5
5
|
* mocks exactly this in tests instead of the full HostedClient. */
|
|
@@ -14,12 +14,29 @@ export interface RunBlobs {
|
|
|
14
14
|
/** D18.1 — the run's meta.json (spec_version + twin_versions + the rest of
|
|
15
15
|
* writeRunArtifactsCore's payload), as written to / read from disk. */
|
|
16
16
|
metaJson: string;
|
|
17
|
+
/** Multi-twin (M3): the session's twins. When >1, per-twin state upload URLs
|
|
18
|
+
* are requested and each twin's state blobs are uploaded under its own
|
|
19
|
+
* storage prefix. Absent / length ≤1 keeps the single-twin path unchanged. */
|
|
20
|
+
twins?: string[];
|
|
21
|
+
/** Multi-twin (M3): per-twin initial/final state JSON, keyed by twin id.
|
|
22
|
+
* Includes the primary twin (whose blobs also go to the legacy top-level
|
|
23
|
+
* pair). Only consumed when `twins.length > 1` and the cloud returns a
|
|
24
|
+
* `per_twin` upload block. */
|
|
25
|
+
perTwinState?: Record<string, {
|
|
26
|
+
initialJson: string;
|
|
27
|
+
finalJson: string;
|
|
28
|
+
}>;
|
|
17
29
|
}
|
|
18
30
|
export interface UploadedBlobKeys {
|
|
19
31
|
eventsKey: string | null;
|
|
20
32
|
stateInitialKey: string | null;
|
|
21
33
|
stateFinalKey: string | null;
|
|
22
34
|
signalsKey: string | null;
|
|
35
|
+
/** Multi-twin (M3): per-twin state storage keys, keyed by twin id. Each entry
|
|
36
|
+
* carries at least one uploaded key (empty entries are dropped so the
|
|
37
|
+
* finalize contract's ">=1 key per entry" invariant holds). Undefined when
|
|
38
|
+
* the session is single-twin or the cloud returned no `per_twin` block. */
|
|
39
|
+
perTwinStateKeys?: PerTwinStateKeys;
|
|
23
40
|
/** D18.1 — the storage key the presigned PUT landed at, or null whenever
|
|
24
41
|
* the upload didn't happen, INCLUDING the feature-detection case (older
|
|
25
42
|
* control plane 404s meta-upload-url). Never distinguished from any other
|
|
@@ -6,16 +6,29 @@
|
|
|
6
6
|
// without duplicating them. Behavior-preserving for the hosted run path:
|
|
7
7
|
// warning text, the 30s PUT timeout, and the null-key fallbacks all match
|
|
8
8
|
// the pre-extraction runner.
|
|
9
|
+
import { gzipSync } from "node:zlib";
|
|
9
10
|
import { outcomeOf } from "./evalResultView.js";
|
|
10
11
|
import { redactSecrets } from "../recorder/redaction.js";
|
|
12
|
+
// Gzip EVERY blob before PUT. The storage edge (Cloudflare in front of
|
|
13
|
+
// Supabase) runs a WAF managed rule that 403-blocks any PUT whose plaintext
|
|
14
|
+
// body contains certain literal keys (e.g. an admin-flag key present in slack
|
|
15
|
+
// twin state exports), which silently drops per-twin state uploads and makes
|
|
16
|
+
// their criteria skip as state_missing. Gzipped bodies sail past the rule.
|
|
17
|
+
// One code path here so every caller (events, per-twin state, signals,
|
|
18
|
+
// meta.json) inherits it; the cloud's blob readers transparently gunzip via
|
|
19
|
+
// the content-encoding header (shipped in the paired reader release before
|
|
20
|
+
// this CLI change is released).
|
|
11
21
|
async function putBlob(url, body, contentType, label) {
|
|
12
22
|
const ctrl = new AbortController();
|
|
13
23
|
const timer = setTimeout(() => ctrl.abort(), 30_000);
|
|
14
24
|
try {
|
|
15
25
|
const putRes = await fetch(url, {
|
|
16
26
|
method: "PUT",
|
|
17
|
-
headers: {
|
|
18
|
-
|
|
27
|
+
headers: {
|
|
28
|
+
"content-type": contentType,
|
|
29
|
+
"content-encoding": "gzip",
|
|
30
|
+
},
|
|
31
|
+
body: gzipSync(Buffer.from(body, "utf8")),
|
|
19
32
|
signal: ctrl.signal,
|
|
20
33
|
});
|
|
21
34
|
if (!putRes.ok) {
|
|
@@ -72,15 +85,49 @@ export async function uploadRunBlobs(client, sessionId, blobs) {
|
|
|
72
85
|
}
|
|
73
86
|
}
|
|
74
87
|
async function uploadStates() {
|
|
88
|
+
const multiTwin = (blobs.twins?.length ?? 0) > 1;
|
|
75
89
|
try {
|
|
76
|
-
|
|
90
|
+
// Multi-twin (M3): ask for a per-twin URL block; single-twin passes
|
|
91
|
+
// nothing so the request body stays `{}` (byte-identical to pre-M3).
|
|
92
|
+
const upload = await client.requestStateUploadUrl(sessionId, multiTwin ? blobs.twins : undefined);
|
|
93
|
+
// Top-level (primary-twin) pair — always uploaded, legacy state_*_storage_key.
|
|
77
94
|
const [initialOk, finalOk] = await Promise.all([
|
|
78
95
|
putBlob(upload.state_initial.url, blobs.stateInitialJson, "application/json", "state_initial.json"),
|
|
79
96
|
putBlob(upload.state_final.url, blobs.stateFinalJson, "application/json", "state_final.json"),
|
|
80
97
|
]);
|
|
98
|
+
// Multi-twin: upload each twin's blobs to its own storage prefix and
|
|
99
|
+
// collect the keys. Only when the cloud actually returned a `per_twin`
|
|
100
|
+
// block (an older cloud omits it → we fall back to the primary-only
|
|
101
|
+
// top-level keys, degrading gracefully). Entries with no uploaded key are
|
|
102
|
+
// dropped so the finalize contract's ">=1 key per entry" holds.
|
|
103
|
+
let perTwinStateKeys;
|
|
104
|
+
if (multiTwin && upload.per_twin) {
|
|
105
|
+
const collected = {};
|
|
106
|
+
for (const twin of blobs.twins ?? []) {
|
|
107
|
+
const pair = upload.per_twin[twin];
|
|
108
|
+
const state = blobs.perTwinState?.[twin];
|
|
109
|
+
if (!pair || !state)
|
|
110
|
+
continue;
|
|
111
|
+
const [iOk, fOk] = await Promise.all([
|
|
112
|
+
putBlob(pair.state_initial.url, state.initialJson, "application/json", `state_initial.${twin}.json`),
|
|
113
|
+
putBlob(pair.state_final.url, state.finalJson, "application/json", `state_final.${twin}.json`),
|
|
114
|
+
]);
|
|
115
|
+
const entry = {};
|
|
116
|
+
if (iOk)
|
|
117
|
+
entry.state_initial_key = pair.state_initial.key;
|
|
118
|
+
if (fOk)
|
|
119
|
+
entry.state_final_key = pair.state_final.key;
|
|
120
|
+
if (entry.state_initial_key !== undefined || entry.state_final_key !== undefined) {
|
|
121
|
+
collected[twin] = entry;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (Object.keys(collected).length > 0)
|
|
125
|
+
perTwinStateKeys = collected;
|
|
126
|
+
}
|
|
81
127
|
return {
|
|
82
128
|
initialKey: initialOk ? upload.state_initial.key : null,
|
|
83
129
|
finalKey: finalOk ? upload.state_final.key : null,
|
|
130
|
+
perTwinStateKeys,
|
|
84
131
|
};
|
|
85
132
|
}
|
|
86
133
|
catch (err) {
|
|
@@ -124,6 +171,7 @@ export async function uploadRunBlobs(client, sessionId, blobs) {
|
|
|
124
171
|
eventsKey,
|
|
125
172
|
stateInitialKey: stateKeys.initialKey,
|
|
126
173
|
stateFinalKey: stateKeys.finalKey,
|
|
174
|
+
perTwinStateKeys: stateKeys.perTwinStateKeys,
|
|
127
175
|
metaKey,
|
|
128
176
|
signalsKey,
|
|
129
177
|
};
|