settld 0.1.5 → 0.2.1
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 +32 -0
- package/SETTLD_VERSION +1 -1
- package/bin/settld.js +63 -0
- package/docs/CIRCLE_SANDBOX_E2E.md +12 -0
- package/docs/QUICKSTART_MCP.md +41 -1
- package/docs/QUICKSTART_MCP_HOSTS.md +172 -89
- package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
- package/docs/QUICKSTART_PROFILES.md +198 -0
- package/docs/README.md +18 -0
- package/docs/RELEASE_CHECKLIST.md +26 -0
- package/docs/RELEASING.md +1 -0
- package/docs/SLO.md +62 -1
- package/docs/SUMMARY.md +1 -0
- package/docs/gitbook/README.md +13 -1
- package/docs/gitbook/quickstart.md +57 -58
- package/docs/integrations/README.md +1 -0
- package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +108 -0
- package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
- package/docs/ops/KERNEL_V0_SHIP_GATE.md +3 -1
- package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +8 -6
- package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +46 -9
- package/docs/ops/TRUST_CONFIG_WIZARD.md +37 -24
- package/docs/ops/VERCEL_MONOREPO_DEPLOY.md +42 -0
- package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
- package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
- package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
- package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
- package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
- package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
- package/docs/spec/OperatorAction.v1.md +90 -0
- package/docs/spec/PolicyDecision.v1.md +83 -0
- package/docs/spec/README.md +5 -0
- package/docs/spec/SettlementDecisionRecord.v2.md +2 -0
- package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
- package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +1 -0
- package/docs/spec/x402-error-codes.v1.txt +14 -0
- package/package.json +14 -1
- package/scripts/ci/build-launch-cutover-packet.mjs +177 -21
- package/scripts/ci/run-10x-throughput-drill.mjs +76 -4
- package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +49 -6
- package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
- package/scripts/ci/run-mcp-host-smoke.mjs +203 -5
- package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
- package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
- package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
- package/scripts/ci/run-production-cutover-gate.mjs +540 -0
- package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
- package/scripts/ci/run-release-promotion-guard.mjs +756 -0
- package/scripts/doctor/mcp-host.mjs +120 -0
- package/scripts/mcp/settld-mcp-server.mjs +330 -20
- package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
- package/scripts/ops/hosted-baseline-evidence.mjs +286 -77
- package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
- package/scripts/policy/cli.mjs +600 -0
- package/scripts/profile/cli.mjs +1324 -0
- package/scripts/register-entity-secret.mjs +102 -0
- package/scripts/setup/circle-bootstrap.mjs +310 -0
- package/scripts/setup/host-config.mjs +617 -0
- package/scripts/setup/login.mjs +299 -0
- package/scripts/setup/onboard.mjs +1578 -0
- package/scripts/setup/openclaw-onboard.mjs +423 -0
- package/scripts/setup/session-store.mjs +65 -0
- package/scripts/setup/wizard.mjs +986 -0
- package/scripts/slo/check.mjs +123 -62
- package/scripts/spec/generate-protocol-vectors.mjs +88 -0
- package/scripts/test/run.sh +23 -9
- package/scripts/vercel/ignore-dashboard.sh +23 -0
- package/scripts/vercel/ignore-mkdocs.sh +2 -0
- package/services/x402-gateway/src/server.js +147 -36
- package/src/api/app.js +2345 -267
- package/src/api/middleware/trust-kernel.js +114 -0
- package/src/api/openapi.js +598 -3
- package/src/api/persistence.js +184 -0
- package/src/api/store.js +277 -0
- package/src/core/agent-wallets.js +134 -0
- package/src/core/event-policy.js +21 -2
- package/src/core/operator-action.js +303 -0
- package/src/core/policy-decision.js +322 -0
- package/src/core/policy-packs.js +207 -0
- package/src/core/profile-fingerprint.js +27 -0
- package/src/core/profile-simulation-reasons.js +84 -0
- package/src/core/profile-templates.js +242 -0
- package/src/core/settlement-kernel.js +27 -1
- package/src/core/wallet-assignment-resolver.js +129 -0
- package/src/core/wallet-provider-bootstrap.js +365 -0
- package/src/db/store-pg.js +631 -0
package/scripts/slo/check.mjs
CHANGED
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
const METRICS_PATH = process.env.SLO_METRICS_PATH ?? "/metrics";
|
|
6
|
-
const METRICS_FILE = process.env.SLO_METRICS_FILE ?? null;
|
|
4
|
+
const SLO_CHECK_SCHEMA_VERSION = "OperationalSloCheck.v1";
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
function normalizeOptionalString(value) {
|
|
7
|
+
if (typeof value !== "string") return null;
|
|
8
|
+
const trimmed = value.trim();
|
|
9
|
+
return trimmed === "" ? null : trimmed;
|
|
10
|
+
}
|
|
13
11
|
|
|
14
|
-
function assertFiniteNumber(n, name) {
|
|
12
|
+
export function assertFiniteNumber(n, name) {
|
|
15
13
|
if (!Number.isFinite(n)) throw new TypeError(`${name} must be finite`);
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
export function parseSloThresholds(env = process.env) {
|
|
17
|
+
return {
|
|
18
|
+
maxHttp5xxTotal: Number(env.SLO_MAX_HTTP_5XX_TOTAL ?? "0"),
|
|
19
|
+
maxOutboxPending: Number(env.SLO_MAX_OUTBOX_PENDING ?? "200"),
|
|
20
|
+
maxDeliveryDlq: Number(env.SLO_MAX_DELIVERY_DLQ ?? "0"),
|
|
21
|
+
maxDeliveriesPending: Number(env.SLO_MAX_DELIVERIES_PENDING ?? "0"),
|
|
22
|
+
maxDeliveriesFailed: Number(env.SLO_MAX_DELIVERIES_FAILED ?? "0")
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function validateSloThresholds(thresholds) {
|
|
27
|
+
for (const [k, v] of [
|
|
28
|
+
["SLO_MAX_HTTP_5XX_TOTAL", thresholds.maxHttp5xxTotal],
|
|
29
|
+
["SLO_MAX_OUTBOX_PENDING", thresholds.maxOutboxPending],
|
|
30
|
+
["SLO_MAX_DELIVERY_DLQ", thresholds.maxDeliveryDlq],
|
|
31
|
+
["SLO_MAX_DELIVERIES_PENDING", thresholds.maxDeliveriesPending],
|
|
32
|
+
["SLO_MAX_DELIVERIES_FAILED", thresholds.maxDeliveriesFailed]
|
|
33
|
+
]) {
|
|
34
|
+
assertFiniteNumber(v, k);
|
|
35
|
+
if (v < 0) throw new TypeError(`${k} must be >= 0`);
|
|
36
|
+
}
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
function sleep(ms) {
|
|
30
|
-
return new Promise((
|
|
40
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
async function fetchTextWithTimeout(url, timeoutMs = 5000) {
|
|
@@ -43,7 +53,6 @@ async function fetchTextWithTimeout(url, timeoutMs = 5000) {
|
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
function unescapeLabelValue(value) {
|
|
46
|
-
// Prometheus exposition escaping.
|
|
47
56
|
return String(value).replaceAll("\\\\", "\\").replaceAll("\\n", "\n").replaceAll('\\"', '"');
|
|
48
57
|
}
|
|
49
58
|
|
|
@@ -94,13 +103,12 @@ function parseLabels(src) {
|
|
|
94
103
|
return labels;
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
function parsePrometheusText(text) {
|
|
106
|
+
export function parsePrometheusText(text) {
|
|
98
107
|
const series = [];
|
|
99
108
|
const lines = String(text ?? "").split("\n");
|
|
100
109
|
for (const lineRaw of lines) {
|
|
101
110
|
const line = lineRaw.trim();
|
|
102
111
|
if (!line || line.startsWith("#")) continue;
|
|
103
|
-
// name{labels} value
|
|
104
112
|
const m = /^([a-zA-Z_:][a-zA-Z0-9_:]*)(\{[^}]*\})?\s+([-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?|NaN|Inf|-Inf)\s*$/.exec(line);
|
|
105
113
|
if (!m) continue;
|
|
106
114
|
const name = m[1];
|
|
@@ -112,67 +120,120 @@ function parsePrometheusText(text) {
|
|
|
112
120
|
return series;
|
|
113
121
|
}
|
|
114
122
|
|
|
115
|
-
function sumWhere(series, { name, where = () => true } = {}) {
|
|
123
|
+
export function sumWhere(series, { name, where = () => true } = {}) {
|
|
116
124
|
let sum = 0;
|
|
117
|
-
for (const
|
|
118
|
-
if (
|
|
119
|
-
if (!where(
|
|
120
|
-
const
|
|
121
|
-
if (!Number.isFinite(
|
|
122
|
-
sum +=
|
|
125
|
+
for (const sample of series) {
|
|
126
|
+
if (sample.name !== name) continue;
|
|
127
|
+
if (!where(sample.labels, sample.value)) continue;
|
|
128
|
+
const value = Number(sample.value);
|
|
129
|
+
if (!Number.isFinite(value)) continue;
|
|
130
|
+
sum += value;
|
|
123
131
|
}
|
|
124
132
|
return sum;
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
function getOne(series, { name, where = () => true } = {}) {
|
|
128
|
-
for (const
|
|
129
|
-
if (
|
|
130
|
-
if (!where(
|
|
131
|
-
return Number(
|
|
135
|
+
export function getOne(series, { name, where = () => true } = {}) {
|
|
136
|
+
for (const sample of series) {
|
|
137
|
+
if (sample.name !== name) continue;
|
|
138
|
+
if (!where(sample.labels, sample.value)) continue;
|
|
139
|
+
return Number(sample.value);
|
|
132
140
|
}
|
|
133
141
|
return null;
|
|
134
142
|
}
|
|
135
143
|
|
|
136
|
-
|
|
137
|
-
let metricsText;
|
|
138
|
-
if (METRICS_FILE) {
|
|
139
|
-
metricsText = await fs.readFile(METRICS_FILE, "utf8");
|
|
140
|
-
} else {
|
|
141
|
-
// Give the server a moment to flush post-lifecycle gauges.
|
|
142
|
-
await sleep(250);
|
|
143
|
-
const r = await fetchTextWithTimeout(`${API_BASE_URL}${METRICS_PATH}`, 10_000);
|
|
144
|
-
assert.equal(r.status, 200, `GET ${METRICS_PATH} failed: http ${r.status}`);
|
|
145
|
-
metricsText = r.text;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const series = parsePrometheusText(metricsText);
|
|
149
|
-
|
|
144
|
+
export function collectOperationalSloSummary(series) {
|
|
150
145
|
const http5xxTotal = sumWhere(series, {
|
|
151
146
|
name: "http_requests_total",
|
|
152
147
|
where: (labels) => typeof labels.status === "string" && labels.status.startsWith("5")
|
|
153
148
|
});
|
|
154
|
-
|
|
155
149
|
const outboxPending = sumWhere(series, { name: "outbox_pending_gauge" });
|
|
156
|
-
const deliveryDlq =
|
|
157
|
-
const deliveriesPending =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
150
|
+
const deliveryDlq = sumWhere(series, { name: "delivery_dlq_pending_total_gauge" });
|
|
151
|
+
const deliveriesPending = sumWhere(series, {
|
|
152
|
+
name: "deliveries_pending_gauge",
|
|
153
|
+
where: (labels) => labels.state === "pending"
|
|
154
|
+
});
|
|
155
|
+
const deliveriesFailed = sumWhere(series, {
|
|
156
|
+
name: "deliveries_pending_gauge",
|
|
157
|
+
where: (labels) => labels.state === "failed"
|
|
158
|
+
});
|
|
159
|
+
return {
|
|
161
160
|
http5xxTotal,
|
|
162
161
|
outboxPending,
|
|
163
162
|
deliveryDlq,
|
|
164
163
|
deliveriesPending,
|
|
165
164
|
deliveriesFailed
|
|
166
165
|
};
|
|
167
|
-
|
|
168
|
-
console.log(JSON.stringify({ slo: summary }));
|
|
166
|
+
}
|
|
169
167
|
|
|
170
|
-
|
|
171
|
-
assert.ok(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
export function assertOperationalSlo(summary, thresholds) {
|
|
169
|
+
assert.ok(
|
|
170
|
+
summary.http5xxTotal <= thresholds.maxHttp5xxTotal,
|
|
171
|
+
`SLO breach: http 5xx total ${summary.http5xxTotal} > ${thresholds.maxHttp5xxTotal}`
|
|
172
|
+
);
|
|
173
|
+
assert.ok(
|
|
174
|
+
summary.outboxPending <= thresholds.maxOutboxPending,
|
|
175
|
+
`SLO breach: outbox pending ${summary.outboxPending} > ${thresholds.maxOutboxPending}`
|
|
176
|
+
);
|
|
177
|
+
assert.ok(
|
|
178
|
+
summary.deliveryDlq <= thresholds.maxDeliveryDlq,
|
|
179
|
+
`SLO breach: delivery DLQ ${summary.deliveryDlq} > ${thresholds.maxDeliveryDlq}`
|
|
180
|
+
);
|
|
181
|
+
assert.ok(
|
|
182
|
+
summary.deliveriesPending <= thresholds.maxDeliveriesPending,
|
|
183
|
+
`SLO breach: deliveries pending ${summary.deliveriesPending} > ${thresholds.maxDeliveriesPending}`
|
|
184
|
+
);
|
|
185
|
+
assert.ok(
|
|
186
|
+
summary.deliveriesFailed <= thresholds.maxDeliveriesFailed,
|
|
187
|
+
`SLO breach: deliveries failed ${summary.deliveriesFailed} > ${thresholds.maxDeliveriesFailed}`
|
|
188
|
+
);
|
|
175
189
|
}
|
|
176
190
|
|
|
177
|
-
|
|
191
|
+
export async function loadMetricsText({
|
|
192
|
+
metricsFile = null,
|
|
193
|
+
apiBaseUrl = "http://127.0.0.1:3000",
|
|
194
|
+
metricsPath = "/metrics",
|
|
195
|
+
flushDelayMs = 250
|
|
196
|
+
} = {}) {
|
|
197
|
+
if (metricsFile) {
|
|
198
|
+
return await fs.readFile(metricsFile, "utf8");
|
|
199
|
+
}
|
|
200
|
+
await sleep(flushDelayMs);
|
|
201
|
+
const response = await fetchTextWithTimeout(`${apiBaseUrl}${metricsPath}`, 10_000);
|
|
202
|
+
assert.equal(response.status, 200, `GET ${metricsPath} failed: http ${response.status}`);
|
|
203
|
+
return response.text;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function runSloCheck({ env = process.env } = {}) {
|
|
207
|
+
const thresholds = parseSloThresholds(env);
|
|
208
|
+
validateSloThresholds(thresholds);
|
|
209
|
+
const metricsFile = normalizeOptionalString(env.SLO_METRICS_FILE);
|
|
210
|
+
const metricsText = await loadMetricsText({
|
|
211
|
+
metricsFile,
|
|
212
|
+
apiBaseUrl: env.SLO_API_BASE_URL ?? "http://127.0.0.1:3000",
|
|
213
|
+
metricsPath: env.SLO_METRICS_PATH ?? "/metrics"
|
|
214
|
+
});
|
|
215
|
+
const series = parsePrometheusText(metricsText);
|
|
216
|
+
const summary = collectOperationalSloSummary(series);
|
|
217
|
+
assertOperationalSlo(summary, thresholds);
|
|
218
|
+
return summary;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function main() {
|
|
222
|
+
const summary = await runSloCheck({ env: process.env });
|
|
223
|
+
console.log(JSON.stringify({ schemaVersion: SLO_CHECK_SCHEMA_VERSION, slo: summary }));
|
|
224
|
+
}
|
|
178
225
|
|
|
226
|
+
const isDirectExecution = (() => {
|
|
227
|
+
try {
|
|
228
|
+
return import.meta.url === new URL(`file://${process.argv[1]}`).href;
|
|
229
|
+
} catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
})();
|
|
233
|
+
|
|
234
|
+
if (isDirectExecution) {
|
|
235
|
+
main().catch((err) => {
|
|
236
|
+
process.stderr.write(`${err?.stack ?? err?.message ?? String(err)}\n`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
@@ -22,6 +22,8 @@ import { buildDisputeOpenEnvelopeV1 } from "../../src/core/dispute-open-envelope
|
|
|
22
22
|
import { buildAgreementDelegationV1 } from "../../src/core/agreement-delegation.js";
|
|
23
23
|
import { buildToolCallAgreementV1 } from "../../src/core/tool-call-agreement.js";
|
|
24
24
|
import { buildToolCallEvidenceV1 } from "../../src/core/tool-call-evidence.js";
|
|
25
|
+
import { buildPolicyDecisionV1 } from "../../src/core/policy-decision.js";
|
|
26
|
+
import { computeOperatorActionHashV1, signOperatorActionV1 } from "../../src/core/operator-action.js";
|
|
25
27
|
|
|
26
28
|
function bytes(text) {
|
|
27
29
|
return new TextEncoder().encode(text);
|
|
@@ -499,6 +501,7 @@ async function main() {
|
|
|
499
501
|
decisionReason: null,
|
|
500
502
|
verificationStatus: "green",
|
|
501
503
|
policyHashUsed: "3".repeat(64),
|
|
504
|
+
profileHashUsed: "a".repeat(64),
|
|
502
505
|
verificationMethodHashUsed: "4".repeat(64),
|
|
503
506
|
policyRef: {
|
|
504
507
|
policyHash: "3".repeat(64),
|
|
@@ -518,6 +521,70 @@ async function main() {
|
|
|
518
521
|
});
|
|
519
522
|
const settlementDecisionRecordV2Canonical = canonicalJsonStringify(settlementDecisionRecordV2);
|
|
520
523
|
|
|
524
|
+
const policyDecision = buildPolicyDecisionV1({
|
|
525
|
+
decisionId: "pdec_run_vectors_0001_auto",
|
|
526
|
+
tenantId,
|
|
527
|
+
runId: agentRun.runId,
|
|
528
|
+
settlementId: agentRunSettlement.settlementId,
|
|
529
|
+
gateId: "gate_vectors_0001",
|
|
530
|
+
policyInput: {
|
|
531
|
+
policyId: "policy_vectors_0001",
|
|
532
|
+
policyVersion: 1
|
|
533
|
+
},
|
|
534
|
+
policyHashUsed: "3".repeat(64),
|
|
535
|
+
verificationMethodHashUsed: "4".repeat(64),
|
|
536
|
+
policyDecision: {
|
|
537
|
+
decisionMode: "automatic",
|
|
538
|
+
verificationStatus: "green",
|
|
539
|
+
runStatus: "completed",
|
|
540
|
+
shouldAutoResolve: true,
|
|
541
|
+
settlementStatus: "released",
|
|
542
|
+
releaseRatePct: 100,
|
|
543
|
+
releaseAmountCents: 1250,
|
|
544
|
+
refundAmountCents: 0,
|
|
545
|
+
reasonCodes: []
|
|
546
|
+
},
|
|
547
|
+
createdAt: "2026-02-01T00:02:00.000Z",
|
|
548
|
+
signerKeyId: keyId,
|
|
549
|
+
signerPrivateKeyPem: privateKeyPem
|
|
550
|
+
});
|
|
551
|
+
const policyDecisionCanonical = canonicalJsonStringify(policyDecision);
|
|
552
|
+
|
|
553
|
+
const operatorActionCore = {
|
|
554
|
+
schemaVersion: "OperatorAction.v1",
|
|
555
|
+
actionId: "opact_vectors_0001",
|
|
556
|
+
caseRef: {
|
|
557
|
+
kind: "dispute",
|
|
558
|
+
caseId: "dsp_run_vectors_0001"
|
|
559
|
+
},
|
|
560
|
+
action: "OVERRIDE_ALLOW",
|
|
561
|
+
justificationCode: "OPS_OVERRIDE_APPROVED",
|
|
562
|
+
justification: "vector override approved",
|
|
563
|
+
actor: {
|
|
564
|
+
operatorId: "op_vectors_0001",
|
|
565
|
+
role: "incident_commander",
|
|
566
|
+
tenantId,
|
|
567
|
+
sessionId: "ops_session_vectors_01",
|
|
568
|
+
metadata: {
|
|
569
|
+
source: "ops-console",
|
|
570
|
+
ticketId: "INC-VECTORS-1"
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
actedAt: "2026-02-01T00:02:10.000Z",
|
|
574
|
+
metadata: {
|
|
575
|
+
severity: "critical",
|
|
576
|
+
checklist: ["evidence-reviewed", "lead-approved"]
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const operatorAction = signOperatorActionV1({
|
|
580
|
+
action: operatorActionCore,
|
|
581
|
+
signedAt: "2026-02-01T00:02:11.000Z",
|
|
582
|
+
publicKeyPem,
|
|
583
|
+
privateKeyPem
|
|
584
|
+
});
|
|
585
|
+
const operatorActionCanonical = canonicalJsonStringify(operatorAction);
|
|
586
|
+
const operatorActionCoreCanonical = canonicalJsonStringify(operatorActionCore);
|
|
587
|
+
|
|
521
588
|
const settlementReceipt = buildSettlementReceipt({
|
|
522
589
|
receiptId: "rcpt_run_vectors_0001_auto",
|
|
523
590
|
tenantId,
|
|
@@ -843,6 +910,27 @@ async function main() {
|
|
|
843
910
|
canonicalJson: settlementDecisionRecordV2Canonical,
|
|
844
911
|
sha256: sha256Hex(settlementDecisionRecordV2Canonical)
|
|
845
912
|
},
|
|
913
|
+
policyDecision: {
|
|
914
|
+
schemaVersion: policyDecision.schemaVersion,
|
|
915
|
+
decisionId: policyDecision.decisionId,
|
|
916
|
+
policyDecisionHash: policyDecision.policyDecisionHash,
|
|
917
|
+
evaluationHash: policyDecision.evaluationHash,
|
|
918
|
+
canonicalJson: policyDecisionCanonical,
|
|
919
|
+
sha256: sha256Hex(policyDecisionCanonical),
|
|
920
|
+
signatureKeyId: policyDecision.signature?.signerKeyId ?? null,
|
|
921
|
+
signature: policyDecision.signature?.signature ?? null
|
|
922
|
+
},
|
|
923
|
+
operatorAction: {
|
|
924
|
+
schemaVersion: operatorAction.schemaVersion,
|
|
925
|
+
actionId: operatorAction.actionId ?? null,
|
|
926
|
+
actionHash: operatorAction.signature?.actionHash ?? computeOperatorActionHashV1({ action: operatorActionCore }),
|
|
927
|
+
canonicalJson: operatorActionCanonical,
|
|
928
|
+
sha256: sha256Hex(operatorActionCanonical),
|
|
929
|
+
coreCanonicalJson: operatorActionCoreCanonical,
|
|
930
|
+
coreSha256: sha256Hex(operatorActionCoreCanonical),
|
|
931
|
+
signatureKeyId: operatorAction.signature?.keyId ?? null,
|
|
932
|
+
signature: operatorAction.signature?.signatureBase64 ?? null
|
|
933
|
+
},
|
|
846
934
|
settlementReceipt: {
|
|
847
935
|
schemaVersion: settlementReceipt.schemaVersion,
|
|
848
936
|
receiptId: settlementReceipt.receiptId,
|
package/scripts/test/run.sh
CHANGED
|
@@ -4,6 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
cd "$(dirname "$0")/../.."
|
|
5
5
|
|
|
6
6
|
PROBLEM_TESTS=(
|
|
7
|
+
"test/api-e2e-x402-authorize-payment.test.js"
|
|
7
8
|
"test/api-python-sdk-first-paid-task-smoke.test.js"
|
|
8
9
|
"test/api-python-sdk-first-verified-run-smoke.test.js"
|
|
9
10
|
"test/magic-link-onboarding-live-contract.test.js"
|
|
@@ -15,19 +16,32 @@ PROBLEM_TESTS=(
|
|
|
15
16
|
"test/trust-config-wizard-cli.test.js"
|
|
16
17
|
)
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
NOO_REGRESSION_TEST_FILE="test/api-e2e-x402-authorize-payment.test.js"
|
|
20
|
+
REQUIRED_NOO_REGRESSION_TESTS=(
|
|
21
|
+
"API e2e: x402 authorize-payment and verify fail closed on missing or revoked passport when required"
|
|
22
|
+
"API e2e: x402 authorize-payment requires valid execution intent when enabled"
|
|
23
|
+
"API e2e: verify enforces strict request binding evidence for quote-bound authorization"
|
|
24
|
+
)
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
for test_name in "${REQUIRED_NOO_REGRESSION_TESTS[@]}"; do
|
|
27
|
+
if ! grep -F "test(\"${test_name}\"" "$NOO_REGRESSION_TEST_FILE" >/dev/null; then
|
|
28
|
+
echo "missing required NOO regression test: ${test_name}" >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
done
|
|
24
32
|
|
|
25
33
|
SAFE_TESTS=()
|
|
26
|
-
for fp in
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
for fp in $(ls test/*.test.js | sort); do
|
|
35
|
+
is_problem_test=0
|
|
36
|
+
for problem in "${PROBLEM_TESTS[@]}"; do
|
|
37
|
+
if [[ "$fp" == "$problem" ]]; then
|
|
38
|
+
is_problem_test=1
|
|
39
|
+
break
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
42
|
+
if [[ "$is_problem_test" -eq 0 ]]; then
|
|
43
|
+
SAFE_TESTS+=("$fp")
|
|
29
44
|
fi
|
|
30
|
-
SAFE_TESTS+=("$fp")
|
|
31
45
|
done
|
|
32
46
|
|
|
33
47
|
# Phase 1: bulk suite (fast).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Vercel "ignoreCommand" contract:
|
|
5
|
+
# - exit 0 => skip deployment
|
|
6
|
+
# - exit 1 => continue with deployment
|
|
7
|
+
|
|
8
|
+
if ! git rev-parse --verify HEAD^ >/dev/null 2>&1; then
|
|
9
|
+
# No parent commit context available; build to stay safe.
|
|
10
|
+
exit 1
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if git diff --quiet HEAD^ HEAD -- \
|
|
14
|
+
dashboard/ \
|
|
15
|
+
scripts/vercel/ignore-dashboard.sh \
|
|
16
|
+
.github/workflows/release.yml \
|
|
17
|
+
.github/workflows/tests.yml; then
|
|
18
|
+
# No website changes; skip dashboard deployment.
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Relevant website/deploy files changed; run deployment.
|
|
23
|
+
exit 1
|