@hasna/skills 0.8.12 → 0.9.2
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 +21 -5
- package/bin/index.js +271 -19
- package/bin/mcp.js +248 -154
- package/bin/migrate.js +100 -10
- package/bin/server.js +596 -51
- package/bin/worker.js +283 -20
- package/dist/cli/commands/registry.d.ts +1 -0
- package/dist/index.js +122 -13
- package/dist/lib/agent-integration.d.ts +26 -0
- package/dist/lib/cloud-executions.d.ts +20 -0
- package/dist/lib/registry-types.d.ts +3 -0
- package/dist/lib/remote-client.d.ts +11 -0
- package/dist/lib/revision.d.ts +3 -0
- package/dist/sdk/execution/admission.d.ts +2 -1
- package/dist/sdk/execution/types.d.ts +9 -0
- package/dist/sdk/index.d.ts +1 -0
- package/dist/sdk/index.js +1301 -502
- package/dist/server/runtime-policy.d.ts +4 -2
- package/dist/server/runtime-pure-contract.d.ts +51 -0
- package/dist/server/runtime-store.d.ts +4 -0
- package/dist/server/runtime-worker.d.ts +2 -1
- package/dist/server/selection-store.d.ts +9 -1
- package/dist/server/skills-api.d.ts +2 -1
- package/dist/server/sqlite-store.d.ts +4 -1
- package/dist/server/store.d.ts +6 -1
- package/dist/server/types.d.ts +27 -0
- package/migrations/postgres/0010_skill_archive_lifecycle.sql +5 -0
- package/migrations/sqlite/0010_skill_archive_lifecycle.sql +5 -0
- package/package.json +1 -1
package/dist/sdk/index.js
CHANGED
|
@@ -32370,9 +32370,196 @@ var cachedClient;
|
|
|
32370
32370
|
var init_event_sink = __esm(() => {
|
|
32371
32371
|
init_dist();
|
|
32372
32372
|
});
|
|
32373
|
+
// package.json
|
|
32374
|
+
var package_default = {
|
|
32375
|
+
name: "@hasna/skills",
|
|
32376
|
+
version: "0.9.2",
|
|
32377
|
+
description: "Skills library for AI coding agents",
|
|
32378
|
+
type: "module",
|
|
32379
|
+
bin: {
|
|
32380
|
+
skills: "bin/index.js",
|
|
32381
|
+
"skills-mcp": "bin/mcp.js",
|
|
32382
|
+
"skills-serve": "bin/server.js",
|
|
32383
|
+
"skills-server": "bin/server.js",
|
|
32384
|
+
"skills-worker": "bin/worker.js",
|
|
32385
|
+
"skills-migrate": "bin/migrate.js"
|
|
32386
|
+
},
|
|
32387
|
+
exports: {
|
|
32388
|
+
".": {
|
|
32389
|
+
import: "./dist/index.js",
|
|
32390
|
+
types: "./dist/index.d.ts"
|
|
32391
|
+
},
|
|
32392
|
+
"./storage": {
|
|
32393
|
+
import: "./dist/storage.js",
|
|
32394
|
+
types: "./dist/storage.d.ts"
|
|
32395
|
+
},
|
|
32396
|
+
"./sdk": {
|
|
32397
|
+
import: "./dist/sdk/index.js",
|
|
32398
|
+
types: "./dist/sdk/index.d.ts"
|
|
32399
|
+
},
|
|
32400
|
+
"./admin-contract": {
|
|
32401
|
+
import: "./dist/admin-contract.js",
|
|
32402
|
+
types: "./dist/admin-contract.d.ts"
|
|
32403
|
+
}
|
|
32404
|
+
},
|
|
32405
|
+
files: [
|
|
32406
|
+
"dist/",
|
|
32407
|
+
"!dist/**/*.test.d.ts",
|
|
32408
|
+
"!dist/**/*.fixture.d.ts",
|
|
32409
|
+
"!dist/test-preload.d.ts",
|
|
32410
|
+
"!dist/platform",
|
|
32411
|
+
"bin/",
|
|
32412
|
+
"migrations/",
|
|
32413
|
+
"docs/skill-standard.md",
|
|
32414
|
+
"docs/plugin-admission.md",
|
|
32415
|
+
"schemas/",
|
|
32416
|
+
"LICENSE",
|
|
32417
|
+
"README.md",
|
|
32418
|
+
"!dist/**/*test-utils.d.ts",
|
|
32419
|
+
"!dist/**/*fixtures.d.ts"
|
|
32420
|
+
],
|
|
32421
|
+
main: "./dist/index.js",
|
|
32422
|
+
types: "./dist/index.d.ts",
|
|
32423
|
+
scripts: {
|
|
32424
|
+
clean: "rm -rf bin/ dist/",
|
|
32425
|
+
build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
32426
|
+
"build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
32427
|
+
test: "bun test --timeout 30000",
|
|
32428
|
+
dev: "bun run ./src/cli/index.tsx",
|
|
32429
|
+
"dev:watch": "bun --watch run ./src/cli/index.tsx",
|
|
32430
|
+
"dev:mcp": "bun --watch run ./src/mcp/index.ts",
|
|
32431
|
+
"dev:server": "bun --watch run ./src/server/index.ts",
|
|
32432
|
+
"dev:worker": "bun --watch run ./src/server/worker.ts",
|
|
32433
|
+
"docker:check": "bash scripts/docker-build-check.sh",
|
|
32434
|
+
migrate: "bun run ./src/server/migrate.ts",
|
|
32435
|
+
typecheck: "tsc --noEmit",
|
|
32436
|
+
"verify:release": "bun run scripts/release-guard.ts",
|
|
32437
|
+
"verify:consumer-types": "bun run scripts/consumer-types.ts",
|
|
32438
|
+
prepack: "bun run verify:generated && bun run verify:release && bun run verify:consumer-types",
|
|
32439
|
+
prepublishOnly: "bun run verify:producer && bun run typecheck && bun run test",
|
|
32440
|
+
"verify:producer": "bun run scripts/release-dependencies.ts",
|
|
32441
|
+
"verify:generated": "HASNA_SKILLS_RELEASE_ARTIFACT_TEST=1 bun test src/lib/release-artifacts.test.ts",
|
|
32442
|
+
"verify:consumer-credentials": "bun run scripts/consumer-credentials.ts"
|
|
32443
|
+
},
|
|
32444
|
+
keywords: [
|
|
32445
|
+
"skills",
|
|
32446
|
+
"ai",
|
|
32447
|
+
"agent",
|
|
32448
|
+
"cli",
|
|
32449
|
+
"typescript",
|
|
32450
|
+
"bun",
|
|
32451
|
+
"claude",
|
|
32452
|
+
"codex",
|
|
32453
|
+
"gemini",
|
|
32454
|
+
"mcp",
|
|
32455
|
+
"model-context-protocol",
|
|
32456
|
+
"open-source",
|
|
32457
|
+
"skills-library",
|
|
32458
|
+
"automation"
|
|
32459
|
+
],
|
|
32460
|
+
author: "Hasna",
|
|
32461
|
+
license: "Apache-2.0",
|
|
32462
|
+
devDependencies: {
|
|
32463
|
+
"@hasna/contracts": "1.1.0",
|
|
32464
|
+
"@types/bun": "1.3.14",
|
|
32465
|
+
"@types/node": "25.2.3",
|
|
32466
|
+
"@types/react": "^18.2.0",
|
|
32467
|
+
"bun-types": "1.3.14",
|
|
32468
|
+
"react-devtools-core": "^7.0.1",
|
|
32469
|
+
typescript: "^5",
|
|
32470
|
+
yaml: "^2.9.0"
|
|
32471
|
+
},
|
|
32472
|
+
dependencies: {
|
|
32473
|
+
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
32474
|
+
"@aws-sdk/client-s3": "^3.1079.0",
|
|
32475
|
+
"@hasna/events": "0.1.18",
|
|
32476
|
+
"@hasna/secrets": "0.4.2",
|
|
32477
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
32478
|
+
chalk: "^5.3.0",
|
|
32479
|
+
commander: "^12.1.0",
|
|
32480
|
+
ink: "^5.0.1",
|
|
32481
|
+
"ink-select-input": "^6.0.0",
|
|
32482
|
+
"ink-spinner": "^5.0.0",
|
|
32483
|
+
"ink-text-input": "^6.0.0",
|
|
32484
|
+
react: "^18.2.0",
|
|
32485
|
+
zod: "^4.3.6"
|
|
32486
|
+
},
|
|
32487
|
+
engines: {
|
|
32488
|
+
bun: ">=1.0.0"
|
|
32489
|
+
},
|
|
32490
|
+
publishConfig: {
|
|
32491
|
+
registry: "https://registry.npmjs.org",
|
|
32492
|
+
access: "public"
|
|
32493
|
+
},
|
|
32494
|
+
repository: {
|
|
32495
|
+
type: "git",
|
|
32496
|
+
url: "https://github.com/hasna/apps.git",
|
|
32497
|
+
directory: "apps/skills"
|
|
32498
|
+
},
|
|
32499
|
+
homepage: "https://github.com/hasna/apps/tree/main/apps/skills",
|
|
32500
|
+
bugs: {
|
|
32501
|
+
url: "https://github.com/hasna/apps/issues"
|
|
32502
|
+
}
|
|
32503
|
+
};
|
|
32373
32504
|
|
|
32374
|
-
// src/lib/
|
|
32375
|
-
import {
|
|
32505
|
+
// src/lib/remote-files.ts
|
|
32506
|
+
import { createHash } from "crypto";
|
|
32507
|
+
var MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
32508
|
+
function describeRemoteFiles(files) {
|
|
32509
|
+
if (files.length > 10)
|
|
32510
|
+
throw new Error("At most 10 input files are supported");
|
|
32511
|
+
const names = new Set;
|
|
32512
|
+
let total = 0;
|
|
32513
|
+
return files.map((file) => {
|
|
32514
|
+
if (typeof file.name !== "string" || file.name.length === 0 || file.name.length > 255 || /[/\\\x00-\x1f\x7f]/.test(file.name) || /[\ud800-\udfff]/u.test(file.name) || file.name === "." || file.name === ".." || names.has(file.name))
|
|
32515
|
+
throw new Error("Input file names must be unique safe basenames");
|
|
32516
|
+
names.add(file.name);
|
|
32517
|
+
total += file.bytes.byteLength;
|
|
32518
|
+
if (file.bytes.byteLength > 20 * 1024 * 1024 || total > 50 * 1024 * 1024)
|
|
32519
|
+
throw new Error("Input files exceed the supported size limit");
|
|
32520
|
+
return { name: file.name, sizeBytes: file.bytes.byteLength, sha256: sha256(file.bytes), contentType: file.contentType ?? "application/octet-stream" };
|
|
32521
|
+
});
|
|
32522
|
+
}
|
|
32523
|
+
function sha256(bytes) {
|
|
32524
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
32525
|
+
}
|
|
32526
|
+
async function readBoundedResponse(response, maximum) {
|
|
32527
|
+
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
32528
|
+
throw new Error("Invalid artifact size limit");
|
|
32529
|
+
const length = response.headers.get("content-length");
|
|
32530
|
+
if (length && (!/^\d+$/.test(length) || Number(length) > maximum)) {
|
|
32531
|
+
await response.body?.cancel();
|
|
32532
|
+
throw new Error("Artifact exceeds its declared size limit");
|
|
32533
|
+
}
|
|
32534
|
+
const reader = response.body?.getReader();
|
|
32535
|
+
if (!reader)
|
|
32536
|
+
return new Uint8Array;
|
|
32537
|
+
const chunks = [];
|
|
32538
|
+
let size = 0;
|
|
32539
|
+
try {
|
|
32540
|
+
while (true) {
|
|
32541
|
+
const next = await reader.read();
|
|
32542
|
+
if (next.done)
|
|
32543
|
+
break;
|
|
32544
|
+
size += next.value.byteLength;
|
|
32545
|
+
if (size > maximum)
|
|
32546
|
+
throw new Error("Artifact exceeds its declared size limit");
|
|
32547
|
+
chunks.push(next.value);
|
|
32548
|
+
}
|
|
32549
|
+
} catch (error) {
|
|
32550
|
+
await reader.cancel().catch(() => {});
|
|
32551
|
+
throw error;
|
|
32552
|
+
} finally {
|
|
32553
|
+
reader.releaseLock();
|
|
32554
|
+
}
|
|
32555
|
+
const bytes = new Uint8Array(size);
|
|
32556
|
+
let offset = 0;
|
|
32557
|
+
for (const chunk of chunks) {
|
|
32558
|
+
bytes.set(chunk, offset);
|
|
32559
|
+
offset += chunk.byteLength;
|
|
32560
|
+
}
|
|
32561
|
+
return bytes;
|
|
32562
|
+
}
|
|
32376
32563
|
|
|
32377
32564
|
// node_modules/@hasna/contracts/dist/client/transport.js
|
|
32378
32565
|
import { isIP } from "net";
|
|
@@ -33591,64 +33778,152 @@ class MissingSkillsFleetError extends Error {
|
|
|
33591
33778
|
}
|
|
33592
33779
|
}
|
|
33593
33780
|
|
|
33594
|
-
// src/lib/
|
|
33595
|
-
|
|
33596
|
-
|
|
33597
|
-
|
|
33598
|
-
|
|
33599
|
-
|
|
33600
|
-
|
|
33601
|
-
let total = 0;
|
|
33602
|
-
return files.map((file) => {
|
|
33603
|
-
if (typeof file.name !== "string" || file.name.length === 0 || file.name.length > 255 || /[/\\\x00-\x1f\x7f]/.test(file.name) || /[\ud800-\udfff]/u.test(file.name) || file.name === "." || file.name === ".." || names.has(file.name))
|
|
33604
|
-
throw new Error("Input file names must be unique safe basenames");
|
|
33605
|
-
names.add(file.name);
|
|
33606
|
-
total += file.bytes.byteLength;
|
|
33607
|
-
if (file.bytes.byteLength > 20 * 1024 * 1024 || total > 50 * 1024 * 1024)
|
|
33608
|
-
throw new Error("Input files exceed the supported size limit");
|
|
33609
|
-
return { name: file.name, sizeBytes: file.bytes.byteLength, sha256: sha256(file.bytes), contentType: file.contentType ?? "application/octet-stream" };
|
|
33610
|
-
});
|
|
33611
|
-
}
|
|
33612
|
-
function sha256(bytes) {
|
|
33613
|
-
return createHash("sha256").update(bytes).digest("hex");
|
|
33614
|
-
}
|
|
33615
|
-
async function readBoundedResponse(response, maximum) {
|
|
33616
|
-
if (!Number.isSafeInteger(maximum) || maximum < 0 || maximum > MAX_REMOTE_FILE_BYTES)
|
|
33617
|
-
throw new Error("Invalid artifact size limit");
|
|
33618
|
-
const length = response.headers.get("content-length");
|
|
33619
|
-
if (length && (!/^\d+$/.test(length) || Number(length) > maximum)) {
|
|
33620
|
-
await response.body?.cancel();
|
|
33621
|
-
throw new Error("Artifact exceeds its declared size limit");
|
|
33781
|
+
// src/lib/cloud-executions.ts
|
|
33782
|
+
class CloudExecutionClient {
|
|
33783
|
+
apiOrigin;
|
|
33784
|
+
apiKey;
|
|
33785
|
+
constructor(apiOrigin, apiKey) {
|
|
33786
|
+
this.apiOrigin = apiOrigin;
|
|
33787
|
+
this.apiKey = apiKey;
|
|
33622
33788
|
}
|
|
33623
|
-
|
|
33624
|
-
|
|
33625
|
-
|
|
33626
|
-
|
|
33627
|
-
|
|
33628
|
-
|
|
33629
|
-
|
|
33630
|
-
|
|
33631
|
-
|
|
33789
|
+
static async configured() {
|
|
33790
|
+
const c = await resolveSkillsConnection();
|
|
33791
|
+
if (!c)
|
|
33792
|
+
throw Error("Cloud execution requires a configured Skills API credential");
|
|
33793
|
+
return new CloudExecutionClient(c.apiOrigin, c.apiKey);
|
|
33794
|
+
}
|
|
33795
|
+
async request(route, method = "GET", value) {
|
|
33796
|
+
const response = await fetch(skillsApiRequestUrl(this.apiOrigin, "/api/v1/executions/" + route), {
|
|
33797
|
+
method,
|
|
33798
|
+
headers: {
|
|
33799
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
33800
|
+
"User-Agent": `hasna-skills/${package_default.version}`,
|
|
33801
|
+
...value === undefined ? {} : { "content-type": "application/json" }
|
|
33802
|
+
},
|
|
33803
|
+
...value === undefined ? {} : { body: JSON.stringify(value) },
|
|
33804
|
+
redirect: "error",
|
|
33805
|
+
signal: AbortSignal.timeout(15000)
|
|
33806
|
+
});
|
|
33807
|
+
if (!response.ok) {
|
|
33808
|
+
response.body?.cancel();
|
|
33809
|
+
throw Error(`Skills cloud execution refused: HTTP ${response.status}`);
|
|
33810
|
+
}
|
|
33811
|
+
return response;
|
|
33812
|
+
}
|
|
33813
|
+
async eligibility(slug, version, bundleDigest) {
|
|
33814
|
+
if (!/^[a-z0-9-]+$/.test(slug) || !/^\d+\.\d+\.\d+$/.test(version) || bundleDigest !== undefined && !/^(sha256:)?[a-f0-9]{64}$/.test(bundleDigest))
|
|
33815
|
+
throw Error("Cloud eligibility requires an exact skill version and valid digest");
|
|
33816
|
+
const query = new URLSearchParams({ version, ...bundleDigest ? { bundleDigest } : {} });
|
|
33817
|
+
const v = await jsonBody(await this.request(`${slug}/eligibility?${query}`));
|
|
33818
|
+
if (!v || v.contractVersion !== 1 || typeof v.eligible !== "boolean" || v.skill !== slug || v.version !== version || !/^[a-f0-9]{64}$/.test(v.bundleDigest) || bundleDigest && bundleDigest.replace(/^sha256:/, "") !== v.bundleDigest || v.eligible && (!v.executionContract || !["pdf.v1", "regex-test.v1"].includes(v.executionContract.id)))
|
|
33819
|
+
throw Error("Invalid cloud eligibility receipt");
|
|
33820
|
+
return v;
|
|
33821
|
+
}
|
|
33822
|
+
async submit(slug, version, input, key, selection) {
|
|
33823
|
+
if (!/^[a-z0-9-]+$/.test(slug) || !/^\d+\.\d+\.\d+$/.test(version))
|
|
33824
|
+
throw Error("Cloud execution requires skill@exact-version");
|
|
33825
|
+
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(key))
|
|
33826
|
+
throw Error("Invalid idempotency key");
|
|
33827
|
+
if (selection?.authority && selection.authority !== skillsApiRequestUrl(this.apiOrigin, "/api/v1/").replace(/\/$/, ""))
|
|
33828
|
+
throw Error("Selected skill belongs to another Skills authority");
|
|
33829
|
+
const execution = parseExecution(await jsonBody(await this.request(slug, "POST", {
|
|
33830
|
+
version,
|
|
33831
|
+
input,
|
|
33832
|
+
idempotencyKey: key,
|
|
33833
|
+
...selection ? {
|
|
33834
|
+
bundleDigest: selection.bundleDigest,
|
|
33835
|
+
workspaceId: selection.workspaceId
|
|
33836
|
+
} : {}
|
|
33837
|
+
})));
|
|
33838
|
+
if (execution.skill !== slug || execution.version !== version || selection && execution.bundleDigest.replace(/^sha256:/, "") !== selection.bundleDigest.replace(/^sha256:/, ""))
|
|
33839
|
+
throw Error("Cloud execution receipt does not match the selected exact skill");
|
|
33840
|
+
return execution;
|
|
33841
|
+
}
|
|
33842
|
+
async get(id) {
|
|
33843
|
+
return parseExecution(await jsonBody(await this.request(runId(id))));
|
|
33844
|
+
}
|
|
33845
|
+
async logs(id) {
|
|
33846
|
+
return jsonBody(await this.request(runId(id) + "/logs"));
|
|
33847
|
+
}
|
|
33848
|
+
async artifacts(id) {
|
|
33849
|
+
return (await this.get(id)).artifacts;
|
|
33850
|
+
}
|
|
33851
|
+
async download(id, name) {
|
|
33852
|
+
if (!["document.pdf", "document.html"].includes(name))
|
|
33853
|
+
throw Error("Invalid cloud artifact name");
|
|
33854
|
+
const response = await this.request(runId(id) + "/artifacts/" + encodeURIComponent(name));
|
|
33855
|
+
if (!response.body)
|
|
33856
|
+
throw Error("Cloud artifact body is missing");
|
|
33857
|
+
const reader = response.body.getReader();
|
|
33858
|
+
const chunks = [];
|
|
33859
|
+
let size = 0;
|
|
33860
|
+
for (;; ) {
|
|
33861
|
+
const r2 = await reader.read();
|
|
33862
|
+
if (r2.done)
|
|
33632
33863
|
break;
|
|
33633
|
-
size +=
|
|
33634
|
-
if (size >
|
|
33635
|
-
|
|
33636
|
-
|
|
33864
|
+
size += r2.value.length;
|
|
33865
|
+
if (size > 2000000) {
|
|
33866
|
+
await reader.cancel();
|
|
33867
|
+
throw Error("Cloud artifact exceeds limit");
|
|
33868
|
+
}
|
|
33869
|
+
chunks.push(r2.value);
|
|
33637
33870
|
}
|
|
33638
|
-
|
|
33639
|
-
await reader.cancel().catch(() => {});
|
|
33640
|
-
throw error;
|
|
33641
|
-
} finally {
|
|
33642
|
-
reader.releaseLock();
|
|
33871
|
+
return Buffer.concat(chunks);
|
|
33643
33872
|
}
|
|
33644
|
-
|
|
33645
|
-
|
|
33646
|
-
|
|
33647
|
-
|
|
33648
|
-
|
|
33873
|
+
async cancel(id) {
|
|
33874
|
+
return parseExecution(await jsonBody(await this.request(runId(id) + "/cancel", "POST", {})));
|
|
33875
|
+
}
|
|
33876
|
+
async wait(id, options = {}) {
|
|
33877
|
+
const timeout = options.timeoutMs ?? 300000, interval = options.intervalMs ?? 1000;
|
|
33878
|
+
if (!Number.isFinite(timeout) || timeout < 1 || timeout > 1200000 || !Number.isFinite(interval) || interval < 100 || interval > 60000)
|
|
33879
|
+
throw Error("Invalid cloud polling bounds");
|
|
33880
|
+
const until = Date.now() + timeout;
|
|
33881
|
+
for (;; ) {
|
|
33882
|
+
const run = await this.get(id);
|
|
33883
|
+
if (["succeeded", "failed", "cancelled"].includes(run.status))
|
|
33884
|
+
return run;
|
|
33885
|
+
if (Date.now() >= until)
|
|
33886
|
+
throw Error("Cloud execution is still running; poll the returned execution id");
|
|
33887
|
+
await Bun.sleep(interval);
|
|
33888
|
+
}
|
|
33889
|
+
}
|
|
33890
|
+
}
|
|
33891
|
+
function runId(id) {
|
|
33892
|
+
if (!/^run_[a-z0-9_]+$/.test(id))
|
|
33893
|
+
throw Error("Invalid cloud execution id");
|
|
33894
|
+
return id;
|
|
33895
|
+
}
|
|
33896
|
+
function parseExecution(value) {
|
|
33897
|
+
const v = value;
|
|
33898
|
+
if (!v || v.contractVersion !== 1 || v.target !== "cloud" || ![
|
|
33899
|
+
"admitted",
|
|
33900
|
+
"leased",
|
|
33901
|
+
"running",
|
|
33902
|
+
"succeeded",
|
|
33903
|
+
"failed",
|
|
33904
|
+
"cancelled"
|
|
33905
|
+
].includes(v.status) || typeof v.skill !== "string" || typeof v.version !== "string" || !Array.isArray(v.artifacts))
|
|
33906
|
+
throw Error("Unsupported cloud execution response");
|
|
33907
|
+
runId(v.id);
|
|
33908
|
+
for (const field of [
|
|
33909
|
+
"bundleDigest",
|
|
33910
|
+
"inputDigest",
|
|
33911
|
+
"runtimeImageDigest"
|
|
33912
|
+
])
|
|
33913
|
+
if (typeof v[field] !== "string" || !/^(sha256:)?[a-f0-9]{64}$/.test(v[field]))
|
|
33914
|
+
throw Error("Invalid cloud execution integrity receipt");
|
|
33915
|
+
return v;
|
|
33916
|
+
}
|
|
33917
|
+
async function jsonBody(response) {
|
|
33918
|
+
const bytes = await readBoundedResponse(response, 1e5);
|
|
33919
|
+
try {
|
|
33920
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
33921
|
+
} catch {
|
|
33922
|
+
throw Error("Invalid cloud execution JSON response");
|
|
33649
33923
|
}
|
|
33650
|
-
return bytes;
|
|
33651
33924
|
}
|
|
33925
|
+
// src/lib/execution-grant-client.ts
|
|
33926
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
33652
33927
|
|
|
33653
33928
|
// src/lib/selection-cache.ts
|
|
33654
33929
|
import { createHash as createHash3, randomUUID } from "crypto";
|
|
@@ -36701,138 +36976,6 @@ async function resolveExecutionGrant(input) {
|
|
|
36701
36976
|
}
|
|
36702
36977
|
return value;
|
|
36703
36978
|
}
|
|
36704
|
-
// package.json
|
|
36705
|
-
var package_default = {
|
|
36706
|
-
name: "@hasna/skills",
|
|
36707
|
-
version: "0.8.12",
|
|
36708
|
-
description: "Skills library for AI coding agents",
|
|
36709
|
-
type: "module",
|
|
36710
|
-
bin: {
|
|
36711
|
-
skills: "bin/index.js",
|
|
36712
|
-
"skills-mcp": "bin/mcp.js",
|
|
36713
|
-
"skills-serve": "bin/server.js",
|
|
36714
|
-
"skills-server": "bin/server.js",
|
|
36715
|
-
"skills-worker": "bin/worker.js",
|
|
36716
|
-
"skills-migrate": "bin/migrate.js"
|
|
36717
|
-
},
|
|
36718
|
-
exports: {
|
|
36719
|
-
".": {
|
|
36720
|
-
import: "./dist/index.js",
|
|
36721
|
-
types: "./dist/index.d.ts"
|
|
36722
|
-
},
|
|
36723
|
-
"./storage": {
|
|
36724
|
-
import: "./dist/storage.js",
|
|
36725
|
-
types: "./dist/storage.d.ts"
|
|
36726
|
-
},
|
|
36727
|
-
"./sdk": {
|
|
36728
|
-
import: "./dist/sdk/index.js",
|
|
36729
|
-
types: "./dist/sdk/index.d.ts"
|
|
36730
|
-
},
|
|
36731
|
-
"./admin-contract": {
|
|
36732
|
-
import: "./dist/admin-contract.js",
|
|
36733
|
-
types: "./dist/admin-contract.d.ts"
|
|
36734
|
-
}
|
|
36735
|
-
},
|
|
36736
|
-
files: [
|
|
36737
|
-
"dist/",
|
|
36738
|
-
"!dist/**/*.test.d.ts",
|
|
36739
|
-
"!dist/**/*.fixture.d.ts",
|
|
36740
|
-
"!dist/test-preload.d.ts",
|
|
36741
|
-
"!dist/platform",
|
|
36742
|
-
"bin/",
|
|
36743
|
-
"migrations/",
|
|
36744
|
-
"docs/skill-standard.md",
|
|
36745
|
-
"docs/plugin-admission.md",
|
|
36746
|
-
"schemas/",
|
|
36747
|
-
"LICENSE",
|
|
36748
|
-
"README.md",
|
|
36749
|
-
"!dist/**/*test-utils.d.ts",
|
|
36750
|
-
"!dist/**/*fixtures.d.ts"
|
|
36751
|
-
],
|
|
36752
|
-
main: "./dist/index.js",
|
|
36753
|
-
types: "./dist/index.d.ts",
|
|
36754
|
-
scripts: {
|
|
36755
|
-
clean: "rm -rf bin/ dist/",
|
|
36756
|
-
build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
36757
|
-
"build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
36758
|
-
test: "bun test --timeout 30000",
|
|
36759
|
-
dev: "bun run ./src/cli/index.tsx",
|
|
36760
|
-
"dev:watch": "bun --watch run ./src/cli/index.tsx",
|
|
36761
|
-
"dev:mcp": "bun --watch run ./src/mcp/index.ts",
|
|
36762
|
-
"dev:server": "bun --watch run ./src/server/index.ts",
|
|
36763
|
-
"dev:worker": "bun --watch run ./src/server/worker.ts",
|
|
36764
|
-
"docker:check": "bash scripts/docker-build-check.sh",
|
|
36765
|
-
migrate: "bun run ./src/server/migrate.ts",
|
|
36766
|
-
typecheck: "tsc --noEmit",
|
|
36767
|
-
"verify:release": "bun run scripts/release-guard.ts",
|
|
36768
|
-
"verify:consumer-types": "bun run scripts/consumer-types.ts",
|
|
36769
|
-
prepack: "bun run verify:generated && bun run verify:release && bun run verify:consumer-types",
|
|
36770
|
-
prepublishOnly: "bun run verify:producer && bun run typecheck && bun run test",
|
|
36771
|
-
"verify:producer": "bun run scripts/release-dependencies.ts",
|
|
36772
|
-
"verify:generated": "HASNA_SKILLS_RELEASE_ARTIFACT_TEST=1 bun test src/lib/release-artifacts.test.ts",
|
|
36773
|
-
"verify:consumer-credentials": "bun run scripts/consumer-credentials.ts"
|
|
36774
|
-
},
|
|
36775
|
-
keywords: [
|
|
36776
|
-
"skills",
|
|
36777
|
-
"ai",
|
|
36778
|
-
"agent",
|
|
36779
|
-
"cli",
|
|
36780
|
-
"typescript",
|
|
36781
|
-
"bun",
|
|
36782
|
-
"claude",
|
|
36783
|
-
"codex",
|
|
36784
|
-
"gemini",
|
|
36785
|
-
"mcp",
|
|
36786
|
-
"model-context-protocol",
|
|
36787
|
-
"open-source",
|
|
36788
|
-
"skills-library",
|
|
36789
|
-
"automation"
|
|
36790
|
-
],
|
|
36791
|
-
author: "Hasna",
|
|
36792
|
-
license: "Apache-2.0",
|
|
36793
|
-
devDependencies: {
|
|
36794
|
-
"@hasna/contracts": "1.1.0",
|
|
36795
|
-
"@types/bun": "1.3.14",
|
|
36796
|
-
"@types/node": "25.2.3",
|
|
36797
|
-
"@types/react": "^18.2.0",
|
|
36798
|
-
"bun-types": "1.3.14",
|
|
36799
|
-
"react-devtools-core": "^7.0.1",
|
|
36800
|
-
typescript: "^5",
|
|
36801
|
-
yaml: "^2.9.0"
|
|
36802
|
-
},
|
|
36803
|
-
dependencies: {
|
|
36804
|
-
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
36805
|
-
"@aws-sdk/client-s3": "^3.1079.0",
|
|
36806
|
-
"@hasna/events": "0.1.18",
|
|
36807
|
-
"@hasna/secrets": "0.4.2",
|
|
36808
|
-
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
36809
|
-
chalk: "^5.3.0",
|
|
36810
|
-
commander: "^12.1.0",
|
|
36811
|
-
ink: "^5.0.1",
|
|
36812
|
-
"ink-select-input": "^6.0.0",
|
|
36813
|
-
"ink-spinner": "^5.0.0",
|
|
36814
|
-
"ink-text-input": "^6.0.0",
|
|
36815
|
-
react: "^18.2.0",
|
|
36816
|
-
zod: "^4.3.6"
|
|
36817
|
-
},
|
|
36818
|
-
engines: {
|
|
36819
|
-
bun: ">=1.0.0"
|
|
36820
|
-
},
|
|
36821
|
-
publishConfig: {
|
|
36822
|
-
registry: "https://registry.npmjs.org",
|
|
36823
|
-
access: "public"
|
|
36824
|
-
},
|
|
36825
|
-
repository: {
|
|
36826
|
-
type: "git",
|
|
36827
|
-
url: "https://github.com/hasna/apps.git",
|
|
36828
|
-
directory: "apps/skills"
|
|
36829
|
-
},
|
|
36830
|
-
homepage: "https://github.com/hasna/apps/tree/main/apps/skills",
|
|
36831
|
-
bugs: {
|
|
36832
|
-
url: "https://github.com/hasna/apps/issues"
|
|
36833
|
-
}
|
|
36834
|
-
};
|
|
36835
|
-
|
|
36836
36979
|
// src/lib/profile-client.ts
|
|
36837
36980
|
function object(value) {
|
|
36838
36981
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -40518,6 +40661,35 @@ function treeHash(root2) {
|
|
|
40518
40661
|
visit2(root2);
|
|
40519
40662
|
return hash2.digest("hex");
|
|
40520
40663
|
}
|
|
40664
|
+
function selectNativeMigrationTargets(inventory, manifest) {
|
|
40665
|
+
if (manifest.schema !== "hasna.skills-native-migration-targets.v1" || !/^[0-9a-f]{64}$/.test(manifest.digest) || !Array.isArray(manifest.targets) || manifest.targets.length === 0)
|
|
40666
|
+
throw new Error("Invalid native migration target manifest");
|
|
40667
|
+
const identities = new Set;
|
|
40668
|
+
const selected = [];
|
|
40669
|
+
for (const target of manifest.targets) {
|
|
40670
|
+
const projectRoot = resolve14(target.projectRoot), normalized = relative3(projectRoot, resolve14(projectRoot, target.path));
|
|
40671
|
+
if (!isAbsolute15(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync6(projectRoot) || !lstatSync17(projectRoot).isDirectory())
|
|
40672
|
+
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
40673
|
+
assertSafePath(projectRoot);
|
|
40674
|
+
if (isAbsolute15(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep4}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
|
|
40675
|
+
throw new Error(`Invalid native migration target: ${target.path}`);
|
|
40676
|
+
const identity3 = `${target.agent}\x00${projectRoot}\x00${normalized}`;
|
|
40677
|
+
if (identities.has(identity3))
|
|
40678
|
+
throw new Error(`Duplicate native migration target: ${target.path}`);
|
|
40679
|
+
identities.add(identity3);
|
|
40680
|
+
const expected = resolve14(target.projectRoot, target.path);
|
|
40681
|
+
const matches = inventory.filter((entry2) => entry2.agent === target.agent && resolve14(entry2.path) === expected);
|
|
40682
|
+
if (matches.length !== 1)
|
|
40683
|
+
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
40684
|
+
const entry = matches[0];
|
|
40685
|
+
if (entry.bridge || entry.system || entry.vendor)
|
|
40686
|
+
throw new Error(`Native migration target is protected or vendor-owned: ${entry.path}`);
|
|
40687
|
+
if (entry.hash !== target.treeSha256 || treeHash(entry.path) !== target.treeSha256)
|
|
40688
|
+
throw new Error(`Native migration target changed after review: ${entry.path}`);
|
|
40689
|
+
selected.push(entry);
|
|
40690
|
+
}
|
|
40691
|
+
return selected;
|
|
40692
|
+
}
|
|
40521
40693
|
function projectAncestorDirectories(projects) {
|
|
40522
40694
|
const directories = new Set;
|
|
40523
40695
|
for (const project of projects) {
|
|
@@ -41006,12 +41178,13 @@ function archiveNativeSkills(inventory, options) {
|
|
|
41006
41178
|
if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
|
|
41007
41179
|
throw new Error(`Skills bridge changed after planning: ${entry.path}`);
|
|
41008
41180
|
}
|
|
41009
|
-
const selected = inventory.filter((entry) => !entry.bridge && !entry.system && (entry.vendor ? options.includeVendor : entry.managed || options.includeUnmanaged));
|
|
41181
|
+
const selected = options.targetManifest ? selectNativeMigrationTargets(inventory, options.targetManifest) : inventory.filter((entry) => !entry.bridge && !entry.system && (entry.vendor ? options.includeVendor : entry.managed || options.includeUnmanaged));
|
|
41010
41182
|
for (const entry of selected)
|
|
41011
41183
|
if (treeHash(entry.path) !== entry.hash)
|
|
41012
41184
|
throw new Error(`Native skill changed after planning: ${entry.path}`);
|
|
41185
|
+
const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
|
|
41013
41186
|
if (!selected.length)
|
|
41014
|
-
return { entries: [], ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41187
|
+
return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41015
41188
|
const operationId = randomUUID3(), archiveRoot = join21(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join21(archiveRoot, "receipt.json");
|
|
41016
41189
|
const entries = selected.map((entry, index) => ({
|
|
41017
41190
|
source: entry.vendor ? join21(entry.path, "SKILL.md") : entry.path,
|
|
@@ -41020,7 +41193,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
41020
41193
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
41021
41194
|
status: "planned"
|
|
41022
41195
|
}));
|
|
41023
|
-
const journal = { version: 2, operationId, status: "planned", entries, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41196
|
+
const journal = { version: 2, operationId, status: "planned", entries, ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41024
41197
|
const moved = [];
|
|
41025
41198
|
const verifyArchive = (entry) => {
|
|
41026
41199
|
assertSafePath(entry.archive);
|
|
@@ -41114,7 +41287,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
41114
41287
|
} catch {}
|
|
41115
41288
|
throw new Error(`Native archive failed; inspect the recovery journal at ${receiptPath} before retrying.`, { cause: error });
|
|
41116
41289
|
}
|
|
41117
|
-
return { entries: entries.map(({ status, conflict, ...entry }) => entry), receiptPath, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41290
|
+
return { entries: entries.map(({ status, conflict, ...entry }) => entry), receiptPath, ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
41118
41291
|
}
|
|
41119
41292
|
function assertNativeExportAllowed(dataDir2 = getDataDirReadOnly()) {
|
|
41120
41293
|
if (requiresCliSkillLoading(dataDir2))
|
|
@@ -41726,6 +41899,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
|
|
|
41726
41899
|
const version = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
41727
41900
|
const kind = parseSkillKind(stringField(jsonManifest, "kind") ?? frontmatter?.kind);
|
|
41728
41901
|
const commands = parseManifestCommands(jsonManifest) ?? (kind === "instruction" ? [] : inferPackageCommands(pkg, name)) ?? [];
|
|
41902
|
+
const runtime = parseManifestRuntime(jsonManifest) ?? inferRuntimeFromCommands(commands);
|
|
41729
41903
|
return {
|
|
41730
41904
|
$schema: stringField(jsonManifest, "$schema") ?? PORTABLE_SKILL_SCHEMA,
|
|
41731
41905
|
standard: stringField(jsonManifest, "standard") ?? PORTABLE_SKILL_STANDARD,
|
|
@@ -41738,7 +41912,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
|
|
|
41738
41912
|
...kind ? { kind } : {},
|
|
41739
41913
|
inputs: kind === "instruction" ? parseManifestInputs(jsonManifest) ?? [] : parseManifestInputs(jsonManifest) ?? DEFAULT_INPUTS,
|
|
41740
41914
|
commands,
|
|
41741
|
-
...
|
|
41915
|
+
...runtime ? { runtime } : {},
|
|
41742
41916
|
...parseManifestProvenance(jsonManifest) ? { provenance: parseManifestProvenance(jsonManifest) } : {}
|
|
41743
41917
|
};
|
|
41744
41918
|
}
|
|
@@ -42257,6 +42431,12 @@ function inferPackageCommands(pkg, fallbackName) {
|
|
|
42257
42431
|
}
|
|
42258
42432
|
return;
|
|
42259
42433
|
}
|
|
42434
|
+
function inferRuntimeFromCommands(commands) {
|
|
42435
|
+
const entrypoint = commands.find((command) => command.entry)?.entry;
|
|
42436
|
+
if (!entrypoint || !entrypoint.toLowerCase().endsWith(".py"))
|
|
42437
|
+
return;
|
|
42438
|
+
return { ...defaultRuntimeContract(entrypoint), runtime: "python3" };
|
|
42439
|
+
}
|
|
42260
42440
|
function readJsonObject(path) {
|
|
42261
42441
|
const parsed = JSON.parse(readFileSync15(path, "utf-8"));
|
|
42262
42442
|
if (!isRecord2(parsed))
|
|
@@ -54107,12 +54287,12 @@ class ArtifactStorage {
|
|
|
54107
54287
|
get usesS3() {
|
|
54108
54288
|
return Boolean(this.bucket);
|
|
54109
54289
|
}
|
|
54110
|
-
objectKeyFor(tenantId,
|
|
54290
|
+
objectKeyFor(tenantId, runId2, relativePath) {
|
|
54111
54291
|
const safeRelativePath = relativePath.replace(/^\/+/, "").replace(/\.\.(?:\/|$)/g, "");
|
|
54112
|
-
return `${this.runPrefix}/${tenantId}/${
|
|
54292
|
+
return `${this.runPrefix}/${tenantId}/${runId2}/${safeRelativePath}`;
|
|
54113
54293
|
}
|
|
54114
|
-
quarantineKeyFor(tenantId,
|
|
54115
|
-
return `${this.runPrefix}/quarantine/${tenantId}/${
|
|
54294
|
+
quarantineKeyFor(tenantId, runId2, artifactId) {
|
|
54295
|
+
return `${this.runPrefix}/quarantine/${tenantId}/${runId2}/${artifactId}`;
|
|
54116
54296
|
}
|
|
54117
54297
|
async materialize(run, artifact, body) {
|
|
54118
54298
|
if (!this.bucket) {
|
|
@@ -54363,13 +54543,13 @@ function createCancelService(options) {
|
|
|
54363
54543
|
const governanceStore = options.governanceStore;
|
|
54364
54544
|
const storage = options.storage ?? new ArtifactStorage;
|
|
54365
54545
|
return {
|
|
54366
|
-
async cancel(principal,
|
|
54546
|
+
async cancel(principal, runId2, requestedBy) {
|
|
54367
54547
|
if (!store.transitionRun) {
|
|
54368
|
-
throw new GovernanceError(GOVERNANCE_ERROR_CODES.FENCING_UNSUPPORTED, `cannot cancel run ${
|
|
54548
|
+
throw new GovernanceError(GOVERNANCE_ERROR_CODES.FENCING_UNSUPPORTED, `cannot cancel run ${runId2}: the store has no transitionRun generation fencing`, { gate: "transitionRun" });
|
|
54369
54549
|
}
|
|
54370
|
-
const run = await store.getRun(principal,
|
|
54550
|
+
const run = await store.getRun(principal, runId2);
|
|
54371
54551
|
if (!run)
|
|
54372
|
-
throw new GovernanceError(GOVERNANCE_ERROR_CODES.STALE_LEASE_GENERATION, `run ${
|
|
54552
|
+
throw new GovernanceError(GOVERNANCE_ERROR_CODES.STALE_LEASE_GENERATION, `run ${runId2} not found`, { gate: "run" });
|
|
54373
54553
|
const terminalStatuses = new Set(["succeeded", "failed", "cancelled", "expired", "refunded"]);
|
|
54374
54554
|
if (terminalStatuses.has(run.status)) {
|
|
54375
54555
|
const receipt2 = await governanceStore.appendReceipt({
|
|
@@ -54382,11 +54562,11 @@ function createCancelService(options) {
|
|
|
54382
54562
|
return { run, fencedGeneration: run.leaseGeneration, quarantined: [], receipt: receipt2, alreadyTerminal: true };
|
|
54383
54563
|
}
|
|
54384
54564
|
const fencedGeneration = run.leaseGeneration + 1;
|
|
54385
|
-
const cancelling = await store.transitionRun(
|
|
54565
|
+
const cancelling = await store.transitionRun(runId2, { status: "cancel_requested", leaseGeneration: fencedGeneration }, run.leaseGeneration);
|
|
54386
54566
|
if (!cancelling) {
|
|
54387
|
-
throw new GovernanceError(GOVERNANCE_ERROR_CODES.STALE_LEASE_GENERATION, `run ${
|
|
54567
|
+
throw new GovernanceError(GOVERNANCE_ERROR_CODES.STALE_LEASE_GENERATION, `run ${runId2} changed while cancelling`, { gate: "transition" });
|
|
54388
54568
|
}
|
|
54389
|
-
const artifacts = await store.listArtifacts(principal,
|
|
54569
|
+
const artifacts = await store.listArtifacts(principal, runId2);
|
|
54390
54570
|
const quarantined = [];
|
|
54391
54571
|
for (const artifact of artifacts) {
|
|
54392
54572
|
const quarantineKey = await storage.moveToQuarantine?.(artifact);
|
|
@@ -54403,9 +54583,9 @@ function createCancelService(options) {
|
|
|
54403
54583
|
});
|
|
54404
54584
|
quarantined.push({ ...artifact, ...quarantineKey ? { storageKey: quarantineKey } : {} });
|
|
54405
54585
|
}
|
|
54406
|
-
const cancelled = await store.transitionRun(
|
|
54586
|
+
const cancelled = await store.transitionRun(runId2, { status: "cancelled", completedAt: new Date().toISOString() }, fencedGeneration);
|
|
54407
54587
|
if (!cancelled) {
|
|
54408
|
-
throw new GovernanceError(GOVERNANCE_ERROR_CODES.STALE_LEASE_GENERATION, `run ${
|
|
54588
|
+
throw new GovernanceError(GOVERNANCE_ERROR_CODES.STALE_LEASE_GENERATION, `run ${runId2} moved on while finalising cancellation`, { gate: "transition" });
|
|
54409
54589
|
}
|
|
54410
54590
|
const receipt = await governanceStore.appendReceipt({
|
|
54411
54591
|
kind: "cancel",
|
|
@@ -54623,6 +54803,9 @@ class MemorySkillSelectionStore {
|
|
|
54623
54803
|
async getProfile(p2, id2) {
|
|
54624
54804
|
return structuredClone(this.profiles.get(this.key(p2.orgId, id2)) ?? null);
|
|
54625
54805
|
}
|
|
54806
|
+
async profilesReferencingSkill(p2, slug) {
|
|
54807
|
+
return [...this.profiles.values()].filter((profile2) => profile2.workspaceId === p2.orgId && profile2.selections.some((selection2) => selection2.slug === slug)).map((profile2) => profile2.id).sort();
|
|
54808
|
+
}
|
|
54626
54809
|
async saveProfile(p2, id2, selected, expected) {
|
|
54627
54810
|
const key = this.key(p2.orgId, id2), previous = this.profiles.get(key);
|
|
54628
54811
|
if ((previous?.revision ?? null) !== expected)
|
|
@@ -54660,14 +54843,25 @@ class SqliteSkillSelectionStore {
|
|
|
54660
54843
|
constructor(db) {
|
|
54661
54844
|
this.db = db;
|
|
54662
54845
|
}
|
|
54846
|
+
async profilesReferencingSkill(p2, slug) {
|
|
54847
|
+
const rows = this.db.query("SELECT profile_id,selections_json FROM skills_profiles WHERE org_id=?").all(p2.orgId);
|
|
54848
|
+
return rows.filter((row) => selections(row.selections_json).some((selection2) => selection2.slug === slug)).map((row) => String(row.profile_id)).sort();
|
|
54849
|
+
}
|
|
54663
54850
|
async getProfile(p2, id2) {
|
|
54664
54851
|
const row = this.db.query("SELECT * FROM skills_profiles WHERE org_id=? AND profile_id=?").get(p2.orgId, id2);
|
|
54665
54852
|
return row ? profile(row) : null;
|
|
54666
54853
|
}
|
|
54667
54854
|
async saveProfile(p2, id2, selected, expected) {
|
|
54668
|
-
|
|
54669
|
-
|
|
54670
|
-
|
|
54855
|
+
return this.db.transaction(() => {
|
|
54856
|
+
for (const selection2 of [...selected].sort((a3, b3) => a3.slug.localeCompare(b3.slug))) {
|
|
54857
|
+
const skill = this.db.query("SELECT lifecycle FROM skills_registry WHERE org_id=? AND slug=?").get(p2.orgId, selection2.slug);
|
|
54858
|
+
if (skill?.lifecycle === "archived")
|
|
54859
|
+
return null;
|
|
54860
|
+
}
|
|
54861
|
+
const revision = randomUUID5(), timestamp = new Date().toISOString();
|
|
54862
|
+
const row = expected === null ? this.db.query("INSERT INTO skills_profiles(org_id,profile_id,revision,selections_json,updated_at) VALUES(?,?,?,?,?) ON CONFLICT(org_id,profile_id) DO NOTHING RETURNING *").get(p2.orgId, id2, revision, JSON.stringify(selected), timestamp) : this.db.query("UPDATE skills_profiles SET revision=?,selections_json=?,updated_at=? WHERE org_id=? AND profile_id=? AND revision=? RETURNING *").get(revision, JSON.stringify(selected), timestamp, p2.orgId, id2, expected);
|
|
54863
|
+
return row ? profile(row) : null;
|
|
54864
|
+
}).immediate();
|
|
54671
54865
|
}
|
|
54672
54866
|
async getStationState(p2, id2) {
|
|
54673
54867
|
const row = this.db.query("SELECT * FROM skills_station_state WHERE org_id=? AND actor_id=? AND station_id=?").get(p2.orgId, p2.userId, id2);
|
|
@@ -54686,14 +54880,25 @@ class PostgresSkillSelectionStore {
|
|
|
54686
54880
|
constructor(sql) {
|
|
54687
54881
|
this.sql = sql;
|
|
54688
54882
|
}
|
|
54883
|
+
async profilesReferencingSkill(p2, slug) {
|
|
54884
|
+
const rows = await this.sql`SELECT profile_id,selections_json FROM skills_profiles WHERE org_id=${p2.orgId}`;
|
|
54885
|
+
return rows.filter((row) => selections(row.selections_json).some((selection2) => selection2.slug === slug)).map((row) => String(row.profile_id)).sort();
|
|
54886
|
+
}
|
|
54689
54887
|
async getProfile(p2, id2) {
|
|
54690
54888
|
const rows = await this.sql`SELECT * FROM skills_profiles WHERE org_id=${p2.orgId} AND profile_id=${id2}`;
|
|
54691
54889
|
return rows[0] ? profile(rows[0]) : null;
|
|
54692
54890
|
}
|
|
54693
54891
|
async saveProfile(p2, id2, selected, expected) {
|
|
54694
|
-
|
|
54695
|
-
|
|
54696
|
-
|
|
54892
|
+
return this.sql.begin(async (tx) => {
|
|
54893
|
+
for (const selection2 of [...selected].sort((a3, b3) => a3.slug.localeCompare(b3.slug))) {
|
|
54894
|
+
const rows2 = await tx`SELECT lifecycle FROM skills_registry WHERE org_id=${p2.orgId} AND slug=${selection2.slug} FOR UPDATE`;
|
|
54895
|
+
if (rows2[0]?.lifecycle === "archived")
|
|
54896
|
+
return null;
|
|
54897
|
+
}
|
|
54898
|
+
const revision = randomUUID5(), timestamp = new Date().toISOString();
|
|
54899
|
+
const rows = expected === null ? await tx`INSERT INTO skills_profiles(org_id,profile_id,revision,selections_json,updated_at) VALUES(${p2.orgId},${id2},${revision},${JSON.stringify(selected)}::jsonb,${timestamp}) ON CONFLICT(org_id,profile_id) DO NOTHING RETURNING *` : await tx`UPDATE skills_profiles SET revision=${revision},selections_json=${JSON.stringify(selected)}::jsonb,updated_at=${timestamp} WHERE org_id=${p2.orgId} AND profile_id=${id2} AND revision=${expected} RETURNING *`;
|
|
54900
|
+
return rows[0] ? profile(rows[0]) : null;
|
|
54901
|
+
});
|
|
54697
54902
|
}
|
|
54698
54903
|
async getStationState(p2, id2) {
|
|
54699
54904
|
const rows = await this.sql`SELECT * FROM skills_station_state WHERE org_id=${p2.orgId} AND actor_id=${p2.userId} AND station_id=${id2}`;
|
|
@@ -54814,7 +55019,7 @@ function nowIso() {
|
|
|
54814
55019
|
function normalizeLimit(limit) {
|
|
54815
55020
|
return Number.isFinite(limit) ? Math.max(0, Math.trunc(limit)) : 0;
|
|
54816
55021
|
}
|
|
54817
|
-
function
|
|
55022
|
+
function runId2() {
|
|
54818
55023
|
return `run_${Date.now().toString(36)}_${randomUUID6().slice(0, 8)}`;
|
|
54819
55024
|
}
|
|
54820
55025
|
function rowToRun(row) {
|
|
@@ -54887,7 +55092,11 @@ function rowToSkill(row) {
|
|
|
54887
55092
|
revisionId: String(row.revision_id ?? ""),
|
|
54888
55093
|
revisionNumber: Number(row.revision_number ?? 0),
|
|
54889
55094
|
...row.tombstoned_at ? { tombstonedAt: dateString(row.tombstoned_at) } : {},
|
|
54890
|
-
...row.tombstone_purge_after ? { tombstonePurgeAfter: dateString(row.tombstone_purge_after) } : {}
|
|
55095
|
+
...row.tombstone_purge_after ? { tombstonePurgeAfter: dateString(row.tombstone_purge_after) } : {},
|
|
55096
|
+
lifecycle: row.lifecycle === "archived" ? "archived" : "active",
|
|
55097
|
+
...row.archived_at ? { archivedAt: dateString(row.archived_at) } : {},
|
|
55098
|
+
...row.archive_reason ? { archiveReason: String(row.archive_reason) } : {},
|
|
55099
|
+
...row.replacement_slug ? { replacementSlug: String(row.replacement_slug) } : {}
|
|
54891
55100
|
};
|
|
54892
55101
|
}
|
|
54893
55102
|
function rowToPin(row) {
|
|
@@ -54968,10 +55177,10 @@ class StaleLeaseGenerationError extends Error {
|
|
|
54968
55177
|
expectedGeneration;
|
|
54969
55178
|
currentGeneration;
|
|
54970
55179
|
status;
|
|
54971
|
-
constructor(
|
|
54972
|
-
super(`late transition rejected for run ${
|
|
55180
|
+
constructor(runId3, expectedGeneration, currentGeneration, status) {
|
|
55181
|
+
super(`late transition rejected for run ${runId3}: expected lease_generation ${expectedGeneration}, ` + `current ${currentGeneration} (status ${status})`);
|
|
54973
55182
|
this.name = "StaleLeaseGenerationError";
|
|
54974
|
-
this.runId =
|
|
55183
|
+
this.runId = runId3;
|
|
54975
55184
|
this.expectedGeneration = expectedGeneration;
|
|
54976
55185
|
this.currentGeneration = currentGeneration;
|
|
54977
55186
|
this.status = status;
|
|
@@ -55006,11 +55215,22 @@ class SkillVersionExistsError extends Error {
|
|
|
55006
55215
|
}
|
|
55007
55216
|
}
|
|
55008
55217
|
|
|
55218
|
+
class SkillLifecycleConflictError extends Error {
|
|
55219
|
+
slug;
|
|
55220
|
+
profiles;
|
|
55221
|
+
constructor(slug, profiles) {
|
|
55222
|
+
super(`cannot archive '${slug}' while active profiles reference it: ${profiles.join(", ")}`);
|
|
55223
|
+
this.slug = slug;
|
|
55224
|
+
this.profiles = profiles;
|
|
55225
|
+
this.name = "SkillLifecycleConflictError";
|
|
55226
|
+
}
|
|
55227
|
+
}
|
|
55228
|
+
|
|
55009
55229
|
// src/lib/revision.ts
|
|
55010
55230
|
import { createHash as createHash20 } from "crypto";
|
|
55011
55231
|
var REVISION_ID_PATTERN = /^[0-9a-f]{64}$/;
|
|
55012
55232
|
function revisionIdOf(content) {
|
|
55013
|
-
const
|
|
55233
|
+
const canonicalContent = {
|
|
55014
55234
|
slug: content.slug,
|
|
55015
55235
|
displayName: content.displayName,
|
|
55016
55236
|
description: content.description,
|
|
@@ -55022,7 +55242,13 @@ function revisionIdOf(content) {
|
|
|
55022
55242
|
skillMd: content.skillMd ?? null,
|
|
55023
55243
|
bundleSha256: content.bundleSha256 ?? null,
|
|
55024
55244
|
bundleByteSize: content.bundleByteSize ?? null
|
|
55025
|
-
}
|
|
55245
|
+
};
|
|
55246
|
+
if (content.lifecycle === "archived" || content.archiveReason !== undefined || content.replacementSlug !== undefined) {
|
|
55247
|
+
canonicalContent.lifecycle = content.lifecycle ?? "active";
|
|
55248
|
+
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
55249
|
+
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
55250
|
+
}
|
|
55251
|
+
const canonical4 = JSON.stringify(canonicalContent);
|
|
55026
55252
|
return createHash20("sha256").update(canonical4).digest("hex");
|
|
55027
55253
|
}
|
|
55028
55254
|
function revisionIdOfRecord(record3) {
|
|
@@ -55125,6 +55351,28 @@ class SqliteSkillsStore {
|
|
|
55125
55351
|
scopes: parseScopes(row.scopes_json)
|
|
55126
55352
|
};
|
|
55127
55353
|
}
|
|
55354
|
+
async updateApiKeyScopes(actor, keyId, expectedScopes, addScopes) {
|
|
55355
|
+
const row = this.get("SELECT scopes_json FROM api_keys WHERE id = ? AND org_id = ? AND revoked_at IS NULL LIMIT 1", [keyId, actor.orgId]);
|
|
55356
|
+
if (!row)
|
|
55357
|
+
return { kind: "not_found" };
|
|
55358
|
+
const current = parseScopes(row.scopes_json);
|
|
55359
|
+
if (current.length !== expectedScopes.length || current.some((scope, index) => scope !== expectedScopes[index])) {
|
|
55360
|
+
return { kind: "stale", scopes: current };
|
|
55361
|
+
}
|
|
55362
|
+
const scopes = [...current, ...addScopes.filter((scope) => !current.includes(scope))];
|
|
55363
|
+
const tx = this.db.transaction(() => {
|
|
55364
|
+
const changed = this.db.run("UPDATE api_keys SET scopes_json = ? WHERE id = ? AND org_id = ? AND revoked_at IS NULL AND scopes_json = ?", [JSON.stringify(scopes), keyId, actor.orgId, JSON.stringify(expectedScopes)]);
|
|
55365
|
+
if (changed.changes !== 1)
|
|
55366
|
+
return false;
|
|
55367
|
+
this.db.run("INSERT INTO skills_audit_events (org_id, user_id, api_key_id, action, target_type, target_id, metadata_json) VALUES (?, ?, ?, ?, ?, ?, ?)", [actor.orgId, actor.userId, actor.apiKeyId, "api_key_scopes_added", "api_key", keyId, JSON.stringify({ added: addScopes, scopes })]);
|
|
55368
|
+
return true;
|
|
55369
|
+
});
|
|
55370
|
+
if (!tx()) {
|
|
55371
|
+
const latest = this.get("SELECT scopes_json FROM api_keys WHERE id = ? AND org_id = ? AND revoked_at IS NULL LIMIT 1", [keyId, actor.orgId]);
|
|
55372
|
+
return { kind: "stale", scopes: latest ? parseScopes(latest.scopes_json) : current };
|
|
55373
|
+
}
|
|
55374
|
+
return { kind: "updated", scopes };
|
|
55375
|
+
}
|
|
55128
55376
|
async createRun(input) {
|
|
55129
55377
|
if (input.idempotencyKey) {
|
|
55130
55378
|
const existing = this.get("SELECT * FROM skills_runs WHERE org_id = ? AND idempotency_key = ? LIMIT 1", [input.principal.orgId, input.idempotencyKey]);
|
|
@@ -55134,7 +55382,7 @@ class SqliteSkillsStore {
|
|
|
55134
55382
|
const row = this.get(`INSERT INTO skills_runs (id, org_id, user_id, skill_slug, requested_slug, status, input_json, args_json, idempotency_key, correlation_id, created_at)
|
|
55135
55383
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
55136
55384
|
RETURNING *`, [
|
|
55137
|
-
|
|
55385
|
+
runId2(),
|
|
55138
55386
|
input.principal.orgId,
|
|
55139
55387
|
input.principal.userId,
|
|
55140
55388
|
input.slug,
|
|
@@ -55446,6 +55694,39 @@ class SqliteSkillsStore {
|
|
|
55446
55694
|
const row = this.get("SELECT * FROM skills_registry WHERE org_id = ? AND slug = ? LIMIT 1", [principal.orgId, slug]);
|
|
55447
55695
|
return row ? rowToSkill(row) : null;
|
|
55448
55696
|
}
|
|
55697
|
+
async setSkillLifecycle(principal, slug, patch, expectedRevisionId) {
|
|
55698
|
+
return this.db.transaction(() => {
|
|
55699
|
+
const rowCurrent = this.get("SELECT * FROM skills_registry WHERE org_id=? AND slug=? LIMIT 1", [principal.orgId, slug]);
|
|
55700
|
+
const current = rowCurrent ? rowToSkill(rowCurrent) : null;
|
|
55701
|
+
if (!current || current.tombstonedAt)
|
|
55702
|
+
return null;
|
|
55703
|
+
if (expectedRevisionId !== current.revisionId)
|
|
55704
|
+
throw new SkillRevisionConflictError(slug, expectedRevisionId, current.revisionId);
|
|
55705
|
+
if (patch.lifecycle === "archived" && current.lifecycle !== "archived") {
|
|
55706
|
+
const profiles = this.db.query("SELECT profile_id,selections_json FROM skills_profiles WHERE org_id=?").all(principal.orgId);
|
|
55707
|
+
const referencing = profiles.filter((profile2) => {
|
|
55708
|
+
const selections2 = typeof profile2.selections_json === "string" ? JSON.parse(profile2.selections_json) : profile2.selections_json;
|
|
55709
|
+
return Array.isArray(selections2) && selections2.some((selection2) => selection2?.slug === slug);
|
|
55710
|
+
}).map((profile2) => String(profile2.profile_id)).sort();
|
|
55711
|
+
if (referencing.length)
|
|
55712
|
+
throw new SkillLifecycleConflictError(slug, referencing);
|
|
55713
|
+
}
|
|
55714
|
+
const next2 = { ...current, lifecycle: patch.lifecycle, updatedAt: nowIso(), ...patch.lifecycle === "archived" ? { archivedAt: current.archivedAt ?? nowIso(), ...patch.reason ? { archiveReason: patch.reason } : {}, ...patch.replacementSlug ? { replacementSlug: patch.replacementSlug } : {} } : {} };
|
|
55715
|
+
if (patch.lifecycle === "active") {
|
|
55716
|
+
delete next2.archivedAt;
|
|
55717
|
+
delete next2.archiveReason;
|
|
55718
|
+
delete next2.replacementSlug;
|
|
55719
|
+
}
|
|
55720
|
+
const row = this.get("UPDATE skills_registry SET lifecycle=?, archived_at=?, archive_reason=?, replacement_slug=?, revision_id=?, revision_number=revision_number+1, updated_at=? WHERE org_id=? AND slug=? AND tombstoned_at IS NULL AND revision_id=? RETURNING *", [next2.lifecycle, next2.archivedAt ?? null, next2.archiveReason ?? null, next2.replacementSlug ?? null, revisionIdOfRecord(next2), next2.updatedAt, principal.orgId, slug, current.revisionId]);
|
|
55721
|
+
if (!row)
|
|
55722
|
+
throw new SkillRevisionConflictError(slug, expectedRevisionId, this.getSkillSync(principal, slug)?.revisionId ?? null);
|
|
55723
|
+
return rowToSkill(row);
|
|
55724
|
+
}).immediate();
|
|
55725
|
+
}
|
|
55726
|
+
getSkillSync(principal, slug) {
|
|
55727
|
+
const row = this.get("SELECT * FROM skills_registry WHERE org_id=? AND slug=? LIMIT 1", [principal.orgId, slug]);
|
|
55728
|
+
return row ? rowToSkill(row) : null;
|
|
55729
|
+
}
|
|
55449
55730
|
async updateSkill(principal, slug, patch, expectedRevisionId) {
|
|
55450
55731
|
const current = await this.getSkill(principal, slug);
|
|
55451
55732
|
if (!current || current.tombstonedAt)
|
|
@@ -55558,13 +55839,13 @@ class SqliteSkillsStore {
|
|
|
55558
55839
|
}
|
|
55559
55840
|
async listTags(principal) {
|
|
55560
55841
|
await this.purgeExpiredTombstones(principal);
|
|
55561
|
-
return this.all("SELECT DISTINCT tag FROM skills_tags WHERE org_id = ? ORDER BY tag ASC", [principal.orgId]).map((row) => String(row.tag));
|
|
55842
|
+
return this.all("SELECT DISTINCT t.tag FROM skills_tags t JOIN skills_registry s ON s.org_id=t.org_id AND s.slug=t.slug WHERE t.org_id = ? AND s.lifecycle='active' ORDER BY t.tag ASC", [principal.orgId]).map((row) => String(row.tag));
|
|
55562
55843
|
}
|
|
55563
55844
|
async listSkillsByTag(principal, tag) {
|
|
55564
55845
|
await this.purgeExpiredTombstones(principal);
|
|
55565
55846
|
return this.all(`SELECT s.* FROM skills_registry s
|
|
55566
55847
|
JOIN skills_tags t ON t.org_id = s.org_id AND t.slug = s.slug
|
|
55567
|
-
WHERE t.org_id = ? AND t.tag = ? AND s.tombstoned_at IS NULL
|
|
55848
|
+
WHERE t.org_id = ? AND t.tag = ? AND s.tombstoned_at IS NULL AND s.lifecycle = 'active'
|
|
55568
55849
|
ORDER BY s.slug ASC`, [principal.orgId, tag]).map(rowToSkill);
|
|
55569
55850
|
}
|
|
55570
55851
|
async listPinsByTag(principal, tag) {
|
|
@@ -55572,7 +55853,7 @@ class SqliteSkillsStore {
|
|
|
55572
55853
|
return this.all(`SELECT p.* FROM skills_pins p
|
|
55573
55854
|
JOIN skills_tags t ON t.org_id = p.org_id AND t.slug = p.slug
|
|
55574
55855
|
JOIN skills_registry s ON s.org_id = p.org_id AND s.slug = p.slug
|
|
55575
|
-
WHERE p.org_id = ? AND p.principal = ? AND t.tag = ? AND s.tombstoned_at IS NULL
|
|
55856
|
+
WHERE p.org_id = ? AND p.principal = ? AND t.tag = ? AND s.tombstoned_at IS NULL AND s.lifecycle = 'active'
|
|
55576
55857
|
ORDER BY p.slug ASC`, [principal.orgId, principal.apiKeyId, tag]).map(rowToPin);
|
|
55577
55858
|
}
|
|
55578
55859
|
async listPublishedSlugs(principal) {
|
|
@@ -55694,8 +55975,8 @@ class MemoryGovernanceStore {
|
|
|
55694
55975
|
this.receipts.push(next2);
|
|
55695
55976
|
return next2;
|
|
55696
55977
|
}
|
|
55697
|
-
async listReceipts(orgId,
|
|
55698
|
-
return this.receipts.filter((receipt) => receipt.orgId === orgId && receipt.runId ===
|
|
55978
|
+
async listReceipts(orgId, runId3) {
|
|
55979
|
+
return this.receipts.filter((receipt) => receipt.orgId === orgId && receipt.runId === runId3);
|
|
55699
55980
|
}
|
|
55700
55981
|
async createReservation(input) {
|
|
55701
55982
|
const estimatedCents = input.estimatedCents;
|
|
@@ -55704,8 +55985,8 @@ class MemoryGovernanceStore {
|
|
|
55704
55985
|
this.reservations.push(reservation);
|
|
55705
55986
|
return reservation;
|
|
55706
55987
|
}
|
|
55707
|
-
async reservationsForRun(orgId,
|
|
55708
|
-
return this.reservations.filter((reservation) => reservation.orgId === orgId && reservation.runId ===
|
|
55988
|
+
async reservationsForRun(orgId, runId3) {
|
|
55989
|
+
return this.reservations.filter((reservation) => reservation.orgId === orgId && reservation.runId === runId3);
|
|
55709
55990
|
}
|
|
55710
55991
|
async reconcileReservation(reservationId2, actualCents, status) {
|
|
55711
55992
|
assertReservationCents(actualCents, "actualCents");
|
|
@@ -55777,8 +56058,8 @@ class SqliteGovernanceStore {
|
|
|
55777
56058
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [next2.id, next2.kind, next2.orgId, next2.runId, next2.artifactId ?? null, next2.requestedBy, JSON.stringify(next2.metadata), next2.createdAt]);
|
|
55778
56059
|
return next2;
|
|
55779
56060
|
}
|
|
55780
|
-
async listReceipts(orgId,
|
|
55781
|
-
const rows = this.db.query("SELECT * FROM skills_lifecycle_receipts WHERE org_id = ? AND run_id = ? ORDER BY created_at ASC, rowid ASC").all(orgId,
|
|
56061
|
+
async listReceipts(orgId, runId3) {
|
|
56062
|
+
const rows = this.db.query("SELECT * FROM skills_lifecycle_receipts WHERE org_id = ? AND run_id = ? ORDER BY created_at ASC, rowid ASC").all(orgId, runId3);
|
|
55782
56063
|
return rows.map((row) => ({
|
|
55783
56064
|
id: String(row.id),
|
|
55784
56065
|
kind: String(row.kind),
|
|
@@ -55798,8 +56079,8 @@ class SqliteGovernanceStore {
|
|
|
55798
56079
|
VALUES (?, ?, ?, ?, ?, ?)`, [reservation.id, reservation.orgId, reservation.runId, reservation.estimatedCents, reservation.status, reservation.createdAt]);
|
|
55799
56080
|
return reservation;
|
|
55800
56081
|
}
|
|
55801
|
-
async reservationsForRun(orgId,
|
|
55802
|
-
const rows = this.db.query("SELECT * FROM skills_credit_reservations WHERE org_id = ? AND run_id = ? ORDER BY created_at ASC, rowid ASC").all(orgId,
|
|
56082
|
+
async reservationsForRun(orgId, runId3) {
|
|
56083
|
+
const rows = this.db.query("SELECT * FROM skills_credit_reservations WHERE org_id = ? AND run_id = ? ORDER BY created_at ASC, rowid ASC").all(orgId, runId3);
|
|
55803
56084
|
return rows.map((row) => this.reservationFrom(row));
|
|
55804
56085
|
}
|
|
55805
56086
|
async reconcileReservation(reservationId2, actualCents, status) {
|
|
@@ -55873,9 +56154,9 @@ class PostgresGovernanceStore {
|
|
|
55873
56154
|
`;
|
|
55874
56155
|
return next2;
|
|
55875
56156
|
}
|
|
55876
|
-
async listReceipts(orgId,
|
|
56157
|
+
async listReceipts(orgId, runId3) {
|
|
55877
56158
|
const rows = await this.sql`
|
|
55878
|
-
SELECT * FROM skills_lifecycle_receipts WHERE org_id = ${orgId} AND run_id = ${
|
|
56159
|
+
SELECT * FROM skills_lifecycle_receipts WHERE org_id = ${orgId} AND run_id = ${runId3} ORDER BY created_at ASC
|
|
55879
56160
|
`;
|
|
55880
56161
|
return rows.map((row) => ({
|
|
55881
56162
|
id: String(row.id),
|
|
@@ -55898,9 +56179,9 @@ class PostgresGovernanceStore {
|
|
|
55898
56179
|
`;
|
|
55899
56180
|
return reservation;
|
|
55900
56181
|
}
|
|
55901
|
-
async reservationsForRun(orgId,
|
|
56182
|
+
async reservationsForRun(orgId, runId3) {
|
|
55902
56183
|
const rows = await this.sql`
|
|
55903
|
-
SELECT * FROM skills_credit_reservations WHERE org_id = ${orgId} AND run_id = ${
|
|
56184
|
+
SELECT * FROM skills_credit_reservations WHERE org_id = ${orgId} AND run_id = ${runId3} ORDER BY created_at ASC
|
|
55904
56185
|
`;
|
|
55905
56186
|
return rows.map((row) => this.reservationFrom(row));
|
|
55906
56187
|
}
|
|
@@ -56016,6 +56297,15 @@ class SkillRequestError extends Error {
|
|
|
56016
56297
|
this.code = code;
|
|
56017
56298
|
}
|
|
56018
56299
|
}
|
|
56300
|
+
function skillLifecyclePatch(body) {
|
|
56301
|
+
if (body.lifecycle !== "active" && body.lifecycle !== "archived")
|
|
56302
|
+
throw new SkillRequestError(400, "INVALID_LIFECYCLE", "lifecycle must be active or archived");
|
|
56303
|
+
if (body.reason !== undefined && (typeof body.reason !== "string" || body.reason.length > 512))
|
|
56304
|
+
throw new SkillRequestError(400, "INVALID_LIFECYCLE", "reason must be at most 512 characters");
|
|
56305
|
+
if (body.replacementSlug !== undefined && (typeof body.replacementSlug !== "string" || !SLUG_PATTERN.test(body.replacementSlug)))
|
|
56306
|
+
throw new SkillRequestError(400, "INVALID_LIFECYCLE", "replacementSlug must be a valid skill slug");
|
|
56307
|
+
return { lifecycle: body.lifecycle, ...body.reason ? { reason: body.reason } : {}, ...body.replacementSlug ? { replacementSlug: body.replacementSlug } : {} };
|
|
56308
|
+
}
|
|
56019
56309
|
function publishedSkillMeta(record3) {
|
|
56020
56310
|
return {
|
|
56021
56311
|
name: record3.slug,
|
|
@@ -56040,7 +56330,11 @@ function publishedPayload(record3, opts = {}) {
|
|
|
56040
56330
|
publishedAt: record3.createdAt,
|
|
56041
56331
|
updatedAt: record3.updatedAt,
|
|
56042
56332
|
revisionId: record3.revisionId,
|
|
56043
|
-
revisionNumber: record3.revisionNumber
|
|
56333
|
+
revisionNumber: record3.revisionNumber,
|
|
56334
|
+
lifecycle: record3.lifecycle,
|
|
56335
|
+
...record3.archivedAt ? { archivedAt: record3.archivedAt } : {},
|
|
56336
|
+
...record3.archiveReason ? { archiveReason: record3.archiveReason } : {},
|
|
56337
|
+
...record3.replacementSlug ? { replacementSlug: record3.replacementSlug } : {}
|
|
56044
56338
|
};
|
|
56045
56339
|
}
|
|
56046
56340
|
function revisionEtag(revisionId) {
|
|
@@ -56080,7 +56374,7 @@ async function tombstoneStatus(store, artifactStorage, principal, record3) {
|
|
|
56080
56374
|
};
|
|
56081
56375
|
}
|
|
56082
56376
|
async function listMergedSkills(store, principal) {
|
|
56083
|
-
return (await store.listSkills(principal)).sort((a3, b3) => a3.slug.localeCompare(b3.slug)).map((record3) => publishedPayload(record3));
|
|
56377
|
+
return (await store.listSkills(principal)).filter((record3) => record3.lifecycle === "active").sort((a3, b3) => a3.slug.localeCompare(b3.slug)).map((record3) => publishedPayload(record3));
|
|
56084
56378
|
}
|
|
56085
56379
|
async function resolvePublishedSkill(store, artifactStorage, principal, slug) {
|
|
56086
56380
|
const record3 = await store.getSkill(principal, slug);
|
|
@@ -56097,7 +56391,7 @@ async function listOrgTags(store, principal) {
|
|
|
56097
56391
|
return [...new Set((await store.listTags(principal)).filter((tag) => tag.trim()))].sort();
|
|
56098
56392
|
}
|
|
56099
56393
|
async function listMergedSkillsByTag(store, principal, tag) {
|
|
56100
|
-
return (await store.listSkillsByTag(principal, tag)).sort((a3, b3) => a3.slug.localeCompare(b3.slug)).map((record3) => publishedPayload(record3));
|
|
56394
|
+
return (await store.listSkillsByTag(principal, tag)).filter((record3) => record3.lifecycle === "active").sort((a3, b3) => a3.slug.localeCompare(b3.slug)).map((record3) => publishedPayload(record3));
|
|
56101
56395
|
}
|
|
56102
56396
|
function skillSummary(skill) {
|
|
56103
56397
|
return {
|
|
@@ -56659,6 +56953,8 @@ async function validatePublished(store, principal, selections2) {
|
|
|
56659
56953
|
const current = await store.getSkill(principal, selection2.slug);
|
|
56660
56954
|
if (!current || current.tombstonedAt)
|
|
56661
56955
|
throw new SkillRequestError(current?.tombstonedAt ? 410 : 404, "SELECTION_UNAVAILABLE", `Selected skill '${selection2.slug}' is unavailable`);
|
|
56956
|
+
if (current.lifecycle === "archived")
|
|
56957
|
+
throw new SkillRequestError(409, "SELECTION_ARCHIVED", `Selected skill '${selection2.slug}' is archived`);
|
|
56662
56958
|
const version2 = await store.getSkillVersion(principal, selection2.slug, selection2.version);
|
|
56663
56959
|
if (!version2 || `sha256:${version2.bundleSha256}` !== selection2.bundleDigest)
|
|
56664
56960
|
throw new SkillRequestError(409, "SELECTION_VERSION_MISMATCH", `Selected version of '${selection2.slug}' does not match its published digest`);
|
|
@@ -56895,8 +57191,8 @@ class RuntimeExecutionStore {
|
|
|
56895
57191
|
this.sqlite?.close();
|
|
56896
57192
|
await this.postgres?.close();
|
|
56897
57193
|
}
|
|
56898
|
-
async job(
|
|
56899
|
-
const row = this.sqlite ? this.sqlite.query("SELECT payload FROM skills_runtime_jobs WHERE run_id=?").get(
|
|
57194
|
+
async job(runId3) {
|
|
57195
|
+
const row = this.sqlite ? this.sqlite.query("SELECT payload FROM skills_runtime_jobs WHERE run_id=?").get(runId3) : (await this.postgres`SELECT payload FROM skills_runtime_jobs WHERE run_id=${runId3}`)[0];
|
|
56900
57196
|
return row ? JSON.parse(String(row.payload)) : null;
|
|
56901
57197
|
}
|
|
56902
57198
|
async mutate(id2, fn) {
|
|
@@ -57229,19 +57525,19 @@ class MemoryRunExecutionStore {
|
|
|
57229
57525
|
}), admission.runId);
|
|
57230
57526
|
return row;
|
|
57231
57527
|
}
|
|
57232
|
-
async getRun(
|
|
57233
|
-
return this.runs.get(
|
|
57528
|
+
async getRun(runId3) {
|
|
57529
|
+
return this.runs.get(runId3) ?? null;
|
|
57234
57530
|
}
|
|
57235
57531
|
async getRunByKey(tenantId, idempotencyKey) {
|
|
57236
|
-
const
|
|
57237
|
-
return
|
|
57532
|
+
const runId3 = this.byKey.get(tenantId + "\x00" + idempotencyKey);
|
|
57533
|
+
return runId3 ? this.runs.get(runId3) ?? null : null;
|
|
57238
57534
|
}
|
|
57239
57535
|
async getRunByDigests(input) {
|
|
57240
|
-
const
|
|
57241
|
-
return
|
|
57536
|
+
const runId3 = this.byDigests.get(digestKey(input));
|
|
57537
|
+
return runId3 ? this.runs.get(runId3) ?? null : null;
|
|
57242
57538
|
}
|
|
57243
|
-
async listAttempts(
|
|
57244
|
-
return Array.from(this.attempts.values()).filter((attempt) => attempt.runId ===
|
|
57539
|
+
async listAttempts(runId3) {
|
|
57540
|
+
return Array.from(this.attempts.values()).filter((attempt) => attempt.runId === runId3).sort((a3, b3) => a3.attemptNumber - b3.attemptNumber);
|
|
57245
57541
|
}
|
|
57246
57542
|
async createAttempt(input) {
|
|
57247
57543
|
const attemptId = `${input.runId}/attempt/${input.attemptNumber}`;
|
|
@@ -57321,7 +57617,7 @@ class MemoryRunExecutionStore {
|
|
|
57321
57617
|
async recordTransition(transition) {
|
|
57322
57618
|
this.transitions.push(transition);
|
|
57323
57619
|
}
|
|
57324
|
-
async markAttemptTerminal(
|
|
57620
|
+
async markAttemptTerminal(runId3, attemptId) {
|
|
57325
57621
|
const attempt = this.attempts.get(attemptId);
|
|
57326
57622
|
if (!attempt)
|
|
57327
57623
|
return null;
|
|
@@ -57333,15 +57629,15 @@ class MemoryRunExecutionStore {
|
|
|
57333
57629
|
this.receipts.set(receipt.runId + "\x00" + receipt.attemptId, receipt);
|
|
57334
57630
|
return receipt;
|
|
57335
57631
|
}
|
|
57336
|
-
async getReceipt(
|
|
57337
|
-
return this.receipts.get(
|
|
57632
|
+
async getReceipt(runId3, attemptId) {
|
|
57633
|
+
return this.receipts.get(runId3 + "\x00" + attemptId) ?? null;
|
|
57338
57634
|
}
|
|
57339
|
-
async finalizeRun(
|
|
57340
|
-
const run = this.runs.get(
|
|
57635
|
+
async finalizeRun(runId3, status, receiptId2) {
|
|
57636
|
+
const run = this.runs.get(runId3);
|
|
57341
57637
|
if (!run)
|
|
57342
57638
|
return null;
|
|
57343
57639
|
for (const [attemptId, attempt] of this.attempts) {
|
|
57344
|
-
if (attempt.runId ===
|
|
57640
|
+
if (attempt.runId === runId3 && attempt.status !== "terminal") {
|
|
57345
57641
|
this.attempts.set(attemptId, { ...attempt, status: "terminal" });
|
|
57346
57642
|
}
|
|
57347
57643
|
}
|
|
@@ -57351,15 +57647,15 @@ class MemoryRunExecutionStore {
|
|
|
57351
57647
|
terminalReceiptId: receiptId2,
|
|
57352
57648
|
updatedAt: new Date().toISOString()
|
|
57353
57649
|
};
|
|
57354
|
-
this.runs.set(
|
|
57650
|
+
this.runs.set(runId3, next2);
|
|
57355
57651
|
return next2;
|
|
57356
57652
|
}
|
|
57357
|
-
async setRunStatus(
|
|
57358
|
-
const run = this.runs.get(
|
|
57653
|
+
async setRunStatus(runId3, status) {
|
|
57654
|
+
const run = this.runs.get(runId3);
|
|
57359
57655
|
if (!run)
|
|
57360
57656
|
return null;
|
|
57361
57657
|
const next2 = { ...run, status, updatedAt: new Date().toISOString() };
|
|
57362
|
-
this.runs.set(
|
|
57658
|
+
this.runs.set(runId3, next2);
|
|
57363
57659
|
return next2;
|
|
57364
57660
|
}
|
|
57365
57661
|
}
|
|
@@ -57377,21 +57673,28 @@ class SqliteRunExecutionStore {
|
|
|
57377
57673
|
this.db = new Database4(path);
|
|
57378
57674
|
this.db.run("PRAGMA journal_mode = WAL");
|
|
57379
57675
|
this.db.run("PRAGMA foreign_keys = ON");
|
|
57380
|
-
this.db.
|
|
57676
|
+
this.db.transaction(() => {
|
|
57677
|
+
this.db.run(SCHEMA_SQL);
|
|
57678
|
+
for (const table of ["execution_runs", "execution_receipts"]) {
|
|
57679
|
+
const columns = this.db.query(`PRAGMA table_info(${table})`).all();
|
|
57680
|
+
if (!columns.some((column) => column.name === "execution_contract_json"))
|
|
57681
|
+
this.db.run(`ALTER TABLE ${table} ADD COLUMN execution_contract_json TEXT`);
|
|
57682
|
+
}
|
|
57683
|
+
}).immediate();
|
|
57381
57684
|
}
|
|
57382
57685
|
async admit(admission) {
|
|
57383
57686
|
this.db.query(`INSERT OR IGNORE INTO execution_runs (
|
|
57384
57687
|
run_id, contract_version, tenant_id, skill_id, skill_version, bundle_digest,
|
|
57385
57688
|
runtime_image_digest, dependency_layer_tag, input_digest, runtime,
|
|
57386
|
-
policy_json, limits_json, idempotency_key, status, created_at, updated_at
|
|
57387
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(admission.runId, admission.contractVersion, admission.tenantId, admission.skillId, admission.skillVersion, admission.bundleDigest, admission.runtimeImageDigest, admission.dependencyLayerTag, admission.inputDigest, admission.runtime, JSON.stringify(admission.policy), JSON.stringify(admission.limits), admission.idempotencyKey, "admitted", admission.createdAt, admission.createdAt);
|
|
57689
|
+
policy_json, limits_json, idempotency_key, status, created_at, updated_at, execution_contract_json
|
|
57690
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(admission.runId, admission.contractVersion, admission.tenantId, admission.skillId, admission.skillVersion, admission.bundleDigest, admission.runtimeImageDigest, admission.dependencyLayerTag, admission.inputDigest, admission.runtime, JSON.stringify(admission.policy), JSON.stringify(admission.limits), admission.idempotencyKey, "admitted", admission.createdAt, admission.createdAt, admission.executionContract ? JSON.stringify(admission.executionContract) : null);
|
|
57388
57691
|
const row = this.readRun(admission.runId);
|
|
57389
57692
|
if (!row)
|
|
57390
57693
|
throw new Error(`admission did not persist run ${admission.runId}`);
|
|
57391
57694
|
return row;
|
|
57392
57695
|
}
|
|
57393
|
-
async getRun(
|
|
57394
|
-
return this.readRun(
|
|
57696
|
+
async getRun(runId3) {
|
|
57697
|
+
return this.readRun(runId3);
|
|
57395
57698
|
}
|
|
57396
57699
|
async getRunByKey(tenantId, idempotencyKey) {
|
|
57397
57700
|
const row = this.db.query(`SELECT * FROM execution_runs WHERE tenant_id = ? AND idempotency_key = ? LIMIT 1`).get(tenantId, idempotencyKey);
|
|
@@ -57403,8 +57706,8 @@ class SqliteRunExecutionStore {
|
|
|
57403
57706
|
LIMIT 1`).get(input.tenantId, input.skillId, input.skillVersion, input.bundleDigest, input.inputDigest);
|
|
57404
57707
|
return row ? rowToRun2(row) : null;
|
|
57405
57708
|
}
|
|
57406
|
-
async listAttempts(
|
|
57407
|
-
const rows = this.db.query(`SELECT * FROM execution_attempts WHERE run_id = ? ORDER BY attempt_number ASC`).all(
|
|
57709
|
+
async listAttempts(runId3) {
|
|
57710
|
+
const rows = this.db.query(`SELECT * FROM execution_attempts WHERE run_id = ? ORDER BY attempt_number ASC`).all(runId3);
|
|
57408
57711
|
return rows.map(rowToAttempt);
|
|
57409
57712
|
}
|
|
57410
57713
|
async createAttempt(input) {
|
|
@@ -57474,8 +57777,8 @@ class SqliteRunExecutionStore {
|
|
|
57474
57777
|
this.db.query(`INSERT INTO execution_transitions (run_id, attempt_id, from_status, to_status, at)
|
|
57475
57778
|
VALUES (?, ?, ?, ?, ?)`).run(transition.runId, transition.attemptId, transition.from, transition.to, transition.at);
|
|
57476
57779
|
}
|
|
57477
|
-
async markAttemptTerminal(
|
|
57478
|
-
this.db.query(`UPDATE execution_attempts SET status = 'terminal' WHERE attempt_id = ? AND run_id = ? AND status != 'terminal'`).run(attemptId,
|
|
57780
|
+
async markAttemptTerminal(runId3, attemptId) {
|
|
57781
|
+
this.db.query(`UPDATE execution_attempts SET status = 'terminal' WHERE attempt_id = ? AND run_id = ? AND status != 'terminal'`).run(attemptId, runId3);
|
|
57479
57782
|
return this.readAttempt(attemptId);
|
|
57480
57783
|
}
|
|
57481
57784
|
async writeReceipt(receipt) {
|
|
@@ -57483,28 +57786,28 @@ class SqliteRunExecutionStore {
|
|
|
57483
57786
|
run_id, attempt_id, lease_generation, client_token, request_digest, started_by,
|
|
57484
57787
|
task_id, launched_at, completed_at, runtime_image_digest, bundle_digest,
|
|
57485
57788
|
dependency_layer_tag, policy_json, limits_json, exit_code, status,
|
|
57486
|
-
artifact_pointers_json, log_pointers_json, cost_cents
|
|
57487
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(receipt.runId, receipt.attemptId, receipt.leaseGeneration, receipt.clientToken, receipt.requestDigest, receipt.startedBy, receipt.taskId, receipt.launchedAt, receipt.completedAt, receipt.runtimeImageDigest, receipt.bundleDigest, receipt.dependencyLayerTag, JSON.stringify(receipt.policy), JSON.stringify(receipt.limits), receipt.exitCode, receipt.status, JSON.stringify(receipt.artifactPointers), JSON.stringify(receipt.logPointers), receipt.costCents);
|
|
57789
|
+
artifact_pointers_json, log_pointers_json, cost_cents, execution_contract_json
|
|
57790
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(receipt.runId, receipt.attemptId, receipt.leaseGeneration, receipt.clientToken, receipt.requestDigest, receipt.startedBy, receipt.taskId, receipt.launchedAt, receipt.completedAt, receipt.runtimeImageDigest, receipt.bundleDigest, receipt.dependencyLayerTag, JSON.stringify(receipt.policy), JSON.stringify(receipt.limits), receipt.exitCode, receipt.status, JSON.stringify(receipt.artifactPointers), JSON.stringify(receipt.logPointers), receipt.costCents, receipt.executionContract ? JSON.stringify(receipt.executionContract) : null);
|
|
57488
57791
|
return receipt;
|
|
57489
57792
|
}
|
|
57490
|
-
async getReceipt(
|
|
57491
|
-
const row = this.db.query(`SELECT * FROM execution_receipts WHERE run_id = ? AND attempt_id = ?`).get(
|
|
57793
|
+
async getReceipt(runId3, attemptId) {
|
|
57794
|
+
const row = this.db.query(`SELECT * FROM execution_receipts WHERE run_id = ? AND attempt_id = ?`).get(runId3, attemptId);
|
|
57492
57795
|
return row ? rowToReceipt(row) : null;
|
|
57493
57796
|
}
|
|
57494
|
-
async finalizeRun(
|
|
57495
|
-
this.db.query(`UPDATE execution_runs SET status = ?, terminal_receipt_id = ?, updated_at = ? WHERE run_id = ?`).run(status, receiptId2, new Date().toISOString(),
|
|
57496
|
-
this.db.query(`UPDATE execution_attempts SET status = 'terminal' WHERE run_id = ? AND status != 'terminal'`).run(
|
|
57497
|
-
return this.readRun(
|
|
57797
|
+
async finalizeRun(runId3, status, receiptId2) {
|
|
57798
|
+
this.db.query(`UPDATE execution_runs SET status = ?, terminal_receipt_id = ?, updated_at = ? WHERE run_id = ?`).run(status, receiptId2, new Date().toISOString(), runId3);
|
|
57799
|
+
this.db.query(`UPDATE execution_attempts SET status = 'terminal' WHERE run_id = ? AND status != 'terminal'`).run(runId3);
|
|
57800
|
+
return this.readRun(runId3);
|
|
57498
57801
|
}
|
|
57499
|
-
async setRunStatus(
|
|
57500
|
-
this.db.query(`UPDATE execution_runs SET status = ?, updated_at = ? WHERE run_id = ?`).run(status, new Date().toISOString(),
|
|
57501
|
-
return this.readRun(
|
|
57802
|
+
async setRunStatus(runId3, status) {
|
|
57803
|
+
this.db.query(`UPDATE execution_runs SET status = ?, updated_at = ? WHERE run_id = ?`).run(status, new Date().toISOString(), runId3);
|
|
57804
|
+
return this.readRun(runId3);
|
|
57502
57805
|
}
|
|
57503
57806
|
async close() {
|
|
57504
57807
|
this.db.close();
|
|
57505
57808
|
}
|
|
57506
|
-
readRun(
|
|
57507
|
-
const row = this.db.query(`SELECT * FROM execution_runs WHERE run_id = ?`).get(
|
|
57809
|
+
readRun(runId3) {
|
|
57810
|
+
const row = this.db.query(`SELECT * FROM execution_runs WHERE run_id = ?`).get(runId3);
|
|
57508
57811
|
return row ? rowToRun2(row) : null;
|
|
57509
57812
|
}
|
|
57510
57813
|
readAttempt(attemptId) {
|
|
@@ -57523,6 +57826,7 @@ function rowToRun2(row) {
|
|
|
57523
57826
|
bundleDigest: row.bundle_digest,
|
|
57524
57827
|
runtimeImageDigest: row.runtime_image_digest,
|
|
57525
57828
|
dependencyLayerTag: row.dependency_layer_tag,
|
|
57829
|
+
...row.execution_contract_json ? { executionContract: JSON.parse(row.execution_contract_json) } : {},
|
|
57526
57830
|
inputDigest: row.input_digest,
|
|
57527
57831
|
runtime: row.runtime,
|
|
57528
57832
|
policy: JSON.parse(row.policy_json),
|
|
@@ -57566,6 +57870,7 @@ function rowToReceipt(row) {
|
|
|
57566
57870
|
runtimeImageDigest: row.runtime_image_digest,
|
|
57567
57871
|
bundleDigest: row.bundle_digest,
|
|
57568
57872
|
dependencyLayerTag: row.dependency_layer_tag,
|
|
57873
|
+
...row.execution_contract_json ? { executionContract: JSON.parse(row.execution_contract_json) } : {},
|
|
57569
57874
|
policy: JSON.parse(row.policy_json),
|
|
57570
57875
|
limits: JSON.parse(row.limits_json),
|
|
57571
57876
|
exitCode: row.exit_code,
|
|
@@ -57585,6 +57890,7 @@ CREATE TABLE IF NOT EXISTS execution_runs (
|
|
|
57585
57890
|
bundle_digest TEXT NOT NULL,
|
|
57586
57891
|
runtime_image_digest TEXT NOT NULL,
|
|
57587
57892
|
dependency_layer_tag TEXT,
|
|
57893
|
+
execution_contract_json TEXT,
|
|
57588
57894
|
input_digest TEXT NOT NULL,
|
|
57589
57895
|
runtime TEXT NOT NULL,
|
|
57590
57896
|
policy_json TEXT NOT NULL,
|
|
@@ -57638,6 +57944,7 @@ CREATE TABLE IF NOT EXISTS execution_receipts (
|
|
|
57638
57944
|
runtime_image_digest TEXT NOT NULL,
|
|
57639
57945
|
bundle_digest TEXT NOT NULL,
|
|
57640
57946
|
dependency_layer_tag TEXT,
|
|
57947
|
+
execution_contract_json TEXT,
|
|
57641
57948
|
policy_json TEXT NOT NULL,
|
|
57642
57949
|
limits_json TEXT NOT NULL,
|
|
57643
57950
|
exit_code INTEGER,
|
|
@@ -57672,6 +57979,7 @@ function createSubmitRunService(options) {
|
|
|
57672
57979
|
const policy = { ...DEFAULT_RUN_POLICY, ...input.policy };
|
|
57673
57980
|
input = {
|
|
57674
57981
|
...input,
|
|
57982
|
+
...input.executionContract ? { executionContract: { ...input.executionContract } } : {},
|
|
57675
57983
|
systemDeps: [...input.systemDeps ?? []],
|
|
57676
57984
|
policy: { ...policy, egressAllowlist: [...policy.egressAllowlist] },
|
|
57677
57985
|
limits: { ...DEFAULT_RUN_LIMITS, ...input.limits }
|
|
@@ -57680,7 +57988,7 @@ function createSubmitRunService(options) {
|
|
|
57680
57988
|
const byKey = await store.getRunByKey(input.tenantId, input.idempotencyKey);
|
|
57681
57989
|
if (byKey)
|
|
57682
57990
|
return { run: ownedAdmission(byKey.admission), created: false };
|
|
57683
|
-
const byDigests = await store.getRunByDigests({
|
|
57991
|
+
const byDigests = input.executionContract ? null : await store.getRunByDigests({
|
|
57684
57992
|
tenantId: input.tenantId,
|
|
57685
57993
|
skillId: input.skillId,
|
|
57686
57994
|
skillVersion: input.skillVersion,
|
|
@@ -57702,6 +58010,7 @@ function createSubmitRunService(options) {
|
|
|
57702
58010
|
bundleDigest: input.bundleDigest,
|
|
57703
58011
|
runtimeImageDigest: image.runtimeImageDigest,
|
|
57704
58012
|
dependencyLayerTag: image.dependencyLayerTag,
|
|
58013
|
+
...input.executionContract ? { executionContract: { ...input.executionContract } } : {},
|
|
57705
58014
|
inputDigest,
|
|
57706
58015
|
runtime: image.runtime,
|
|
57707
58016
|
policy: { ...DEFAULT_RUN_POLICY, ...input.policy },
|
|
@@ -57717,6 +58026,7 @@ function createSubmitRunService(options) {
|
|
|
57717
58026
|
function ownedAdmission(admission) {
|
|
57718
58027
|
return {
|
|
57719
58028
|
...admission,
|
|
58029
|
+
...admission.executionContract ? { executionContract: { ...admission.executionContract } } : {},
|
|
57720
58030
|
policy: { ...admission.policy, egressAllowlist: [...admission.policy.egressAllowlist] },
|
|
57721
58031
|
limits: { ...admission.limits }
|
|
57722
58032
|
};
|
|
@@ -57738,12 +58048,12 @@ var LEGAL_TRANSITIONS = {
|
|
|
57738
58048
|
cancelled: []
|
|
57739
58049
|
};
|
|
57740
58050
|
function createRunStateMachine(store) {
|
|
57741
|
-
async function transition(
|
|
57742
|
-
const run = await store.getRun(
|
|
58051
|
+
async function transition(runId3, from, to, attemptId = null) {
|
|
58052
|
+
const run = await store.getRun(runId3);
|
|
57743
58053
|
if (!run)
|
|
57744
58054
|
return { ok: false, reason: "NO_SUCH_RUN" };
|
|
57745
58055
|
if (attemptId) {
|
|
57746
|
-
const attempt = await findAttempt(store,
|
|
58056
|
+
const attempt = await findAttempt(store, runId3, attemptId);
|
|
57747
58057
|
if (!attempt)
|
|
57748
58058
|
return { ok: false, reason: "NO_SUCH_ATTEMPT" };
|
|
57749
58059
|
}
|
|
@@ -57755,11 +58065,11 @@ function createRunStateMachine(store) {
|
|
|
57755
58065
|
if (!LEGAL_TRANSITIONS[from].includes(to)) {
|
|
57756
58066
|
return { ok: false, reason: "INVALID_TRANSITION" };
|
|
57757
58067
|
}
|
|
57758
|
-
const next2 = await store.setRunStatus(
|
|
58068
|
+
const next2 = await store.setRunStatus(runId3, to);
|
|
57759
58069
|
if (!next2)
|
|
57760
58070
|
return { ok: false, reason: "NO_SUCH_RUN" };
|
|
57761
58071
|
const record3 = {
|
|
57762
|
-
runId:
|
|
58072
|
+
runId: runId3,
|
|
57763
58073
|
attemptId,
|
|
57764
58074
|
from,
|
|
57765
58075
|
to,
|
|
@@ -57796,8 +58106,8 @@ function createRunStateMachine(store) {
|
|
|
57796
58106
|
});
|
|
57797
58107
|
return claimed;
|
|
57798
58108
|
},
|
|
57799
|
-
async start(
|
|
57800
|
-
return transition(
|
|
58109
|
+
async start(runId3, attemptId) {
|
|
58110
|
+
return transition(runId3, "leased", "running", attemptId);
|
|
57801
58111
|
},
|
|
57802
58112
|
async terminate(input) {
|
|
57803
58113
|
const run = await store.getRun(input.runId);
|
|
@@ -57817,26 +58127,26 @@ function createRunStateMachine(store) {
|
|
|
57817
58127
|
await store.markAttemptTerminal(input.runId, input.attemptId);
|
|
57818
58128
|
return { ok: true };
|
|
57819
58129
|
},
|
|
57820
|
-
async cancel(
|
|
57821
|
-
const run = await store.getRun(
|
|
58130
|
+
async cancel(runId3) {
|
|
58131
|
+
const run = await store.getRun(runId3);
|
|
57822
58132
|
if (!run)
|
|
57823
58133
|
return { ok: false, reason: "NO_SUCH_RUN" };
|
|
57824
58134
|
if (isTerminalStatus(run.status))
|
|
57825
58135
|
return { ok: false, reason: "RUN_TERMINAL" };
|
|
57826
|
-
const result = await transition(
|
|
58136
|
+
const result = await transition(runId3, run.status, "cancelled", run.currentAttemptId);
|
|
57827
58137
|
if (!result.ok)
|
|
57828
58138
|
return result;
|
|
57829
58139
|
return { ok: true };
|
|
57830
58140
|
},
|
|
57831
58141
|
transition,
|
|
57832
|
-
async getStatus(
|
|
57833
|
-
const run = await store.getRun(
|
|
58142
|
+
async getStatus(runId3) {
|
|
58143
|
+
const run = await store.getRun(runId3);
|
|
57834
58144
|
return run ? run.status : null;
|
|
57835
58145
|
}
|
|
57836
58146
|
};
|
|
57837
58147
|
}
|
|
57838
|
-
async function findAttempt(store,
|
|
57839
|
-
const attempts = await store.listAttempts(
|
|
58148
|
+
async function findAttempt(store, runId3, attemptId) {
|
|
58149
|
+
const attempts = await store.listAttempts(runId3);
|
|
57840
58150
|
return attempts.find((attempt) => attempt.attemptId === attemptId) ?? null;
|
|
57841
58151
|
}
|
|
57842
58152
|
|
|
@@ -57857,6 +58167,7 @@ function createReceiptService(store) {
|
|
|
57857
58167
|
runtimeImageDigest: input.admission.runtimeImageDigest,
|
|
57858
58168
|
bundleDigest: input.admission.bundleDigest,
|
|
57859
58169
|
dependencyLayerTag: input.admission.dependencyLayerTag,
|
|
58170
|
+
...input.admission.executionContract ? { executionContract: { ...input.admission.executionContract } } : {},
|
|
57860
58171
|
policy: input.admission.policy,
|
|
57861
58172
|
limits: input.admission.limits,
|
|
57862
58173
|
exitCode: null,
|
|
@@ -57885,7 +58196,7 @@ function createReceiptService(store) {
|
|
|
57885
58196
|
await store.finalizeRun(input.runId, input.status, input.attemptId);
|
|
57886
58197
|
return { receipt: written, runId: input.runId, status: input.status };
|
|
57887
58198
|
},
|
|
57888
|
-
get: (
|
|
58199
|
+
get: (runId3, attemptId) => store.getReceipt(runId3, attemptId)
|
|
57889
58200
|
};
|
|
57890
58201
|
}
|
|
57891
58202
|
|
|
@@ -61124,11 +61435,11 @@ function observedTask(states, taskArn) {
|
|
|
61124
61435
|
const state2 = states[0];
|
|
61125
61436
|
return state2.lastStatus === "STOPPED" || LIVE_TASK_STATUSES.has(state2.lastStatus) ? state2 : undefined;
|
|
61126
61437
|
}
|
|
61127
|
-
function clientTokenFor(
|
|
61128
|
-
return createHash24("sha256").update(`${
|
|
61438
|
+
function clientTokenFor(runId3, attemptId) {
|
|
61439
|
+
return createHash24("sha256").update(`${runId3}\x00${attemptId}`).digest("hex").slice(0, CLIENT_TOKEN_BYTES * 2);
|
|
61129
61440
|
}
|
|
61130
|
-
function startedByFor(
|
|
61131
|
-
return `skills-exec/${
|
|
61441
|
+
function startedByFor(runId3, attemptNumber) {
|
|
61442
|
+
return `skills-exec/${runId3}/a${attemptNumber}`;
|
|
61132
61443
|
}
|
|
61133
61444
|
function requestDigestFor(admission, attemptId) {
|
|
61134
61445
|
return createHash24("sha256").update(canonicalJson2({ admission, attemptId })).digest("hex");
|
|
@@ -61173,29 +61484,29 @@ class EcsDispatcher {
|
|
|
61173
61484
|
return { accepted: false, detail: `run already ${outcome.status}` };
|
|
61174
61485
|
}
|
|
61175
61486
|
}
|
|
61176
|
-
async cancel(
|
|
61177
|
-
const run = await this.store.getRun(
|
|
61487
|
+
async cancel(runId3) {
|
|
61488
|
+
const run = await this.store.getRun(runId3);
|
|
61178
61489
|
if (!run)
|
|
61179
61490
|
return { accepted: false, detail: "no such run" };
|
|
61180
61491
|
if (run.status === "succeeded" || run.status === "failed") {
|
|
61181
61492
|
return { accepted: false, detail: `run already ${run.status}` };
|
|
61182
61493
|
}
|
|
61183
|
-
const attempts = await this.store.listAttempts(
|
|
61494
|
+
const attempts = await this.store.listAttempts(runId3);
|
|
61184
61495
|
const current = attempts[attempts.length - 1];
|
|
61185
61496
|
if (!current) {
|
|
61186
61497
|
if (run.status === "cancelled")
|
|
61187
61498
|
return { accepted: true, detail: "already cancelled (no attempt launched)" };
|
|
61188
|
-
const cancelled = await this.stateMachine.cancel(
|
|
61499
|
+
const cancelled = await this.stateMachine.cancel(runId3);
|
|
61189
61500
|
return cancelled.ok ? { accepted: true, detail: "cancelled (no attempt launched)" } : { accepted: false, detail: cancelled.reason };
|
|
61190
61501
|
}
|
|
61191
|
-
if (!this.cancellationReceipt(await this.receipts.get(
|
|
61502
|
+
if (!this.cancellationReceipt(await this.receipts.get(runId3, current.attemptId), run.admission, current)) {
|
|
61192
61503
|
return { accepted: false, detail: "matching cancellation receipt is unavailable or contradictory" };
|
|
61193
61504
|
}
|
|
61194
61505
|
const observation = await this.reconcile(run.admission, current);
|
|
61195
61506
|
if (observation.kind !== "already-launched" && observation.kind !== "previous-terminal") {
|
|
61196
61507
|
return { accepted: false, detail: "task state unknown; cancellation is not yet confirmed" };
|
|
61197
61508
|
}
|
|
61198
|
-
const taskArn = observation.kind === "already-launched" ? observation.taskId : (await this.store.listAttempts(
|
|
61509
|
+
const taskArn = observation.kind === "already-launched" ? observation.taskId : (await this.store.listAttempts(runId3)).find((row) => row.attemptId === current.attemptId)?.taskId ?? null;
|
|
61199
61510
|
if (!taskArn)
|
|
61200
61511
|
return { accepted: false, detail: "stopped task identity is not confirmed" };
|
|
61201
61512
|
if (observation.kind === "already-launched") {
|
|
@@ -61208,7 +61519,7 @@ class EcsDispatcher {
|
|
|
61208
61519
|
}
|
|
61209
61520
|
}
|
|
61210
61521
|
if (run.status !== "cancelled") {
|
|
61211
|
-
const cancelled = await this.stateMachine.cancel(
|
|
61522
|
+
const cancelled = await this.stateMachine.cancel(runId3);
|
|
61212
61523
|
if (!cancelled.ok)
|
|
61213
61524
|
return { accepted: false, detail: cancelled.reason };
|
|
61214
61525
|
}
|
|
@@ -61219,22 +61530,22 @@ class EcsDispatcher {
|
|
|
61219
61530
|
}
|
|
61220
61531
|
return { accepted: true, target: taskArn, detail: "cancelled after confirmed task stop and receipt" };
|
|
61221
61532
|
}
|
|
61222
|
-
async launchAttempt(
|
|
61223
|
-
const run = await this.store.getRun(
|
|
61533
|
+
async launchAttempt(runId3) {
|
|
61534
|
+
const run = await this.store.getRun(runId3);
|
|
61224
61535
|
if (!run)
|
|
61225
61536
|
return { kind: "no-admission" };
|
|
61226
61537
|
if (run.status === "succeeded" || run.status === "failed" || run.status === "cancelled") {
|
|
61227
61538
|
return { kind: "run-terminal", status: run.status };
|
|
61228
61539
|
}
|
|
61229
|
-
const attempts = await this.store.listAttempts(
|
|
61540
|
+
const attempts = await this.store.listAttempts(runId3);
|
|
61230
61541
|
const previous = attempts[attempts.length - 1];
|
|
61231
61542
|
if (previous) {
|
|
61232
61543
|
return this.reconcile(run.admission, previous);
|
|
61233
61544
|
}
|
|
61234
61545
|
const attemptNumber = 1;
|
|
61235
|
-
const attempt = await this.store.createAttempt({ runId:
|
|
61546
|
+
const attempt = await this.store.createAttempt({ runId: runId3, attemptNumber });
|
|
61236
61547
|
const claimed = await this.stateMachine.claim({
|
|
61237
|
-
runId:
|
|
61548
|
+
runId: runId3,
|
|
61238
61549
|
attemptId: attempt.attemptId,
|
|
61239
61550
|
workerId: this.workerId,
|
|
61240
61551
|
expectedLeaseGeneration: 0
|
|
@@ -61242,11 +61553,11 @@ class EcsDispatcher {
|
|
|
61242
61553
|
if (!claimed.ok) {
|
|
61243
61554
|
return { kind: "claim-refused", attemptId: attempt.attemptId, reason: claimed.reason };
|
|
61244
61555
|
}
|
|
61245
|
-
const clientToken = clientTokenFor(
|
|
61246
|
-
const startedBy = startedByFor(
|
|
61556
|
+
const clientToken = clientTokenFor(runId3, attempt.attemptId);
|
|
61557
|
+
const startedBy = startedByFor(runId3, attemptNumber);
|
|
61247
61558
|
const requestDigest = requestDigestFor(run.admission, attempt.attemptId);
|
|
61248
61559
|
const intent = await this.store.recordLaunchIntent({
|
|
61249
|
-
runId:
|
|
61560
|
+
runId: runId3,
|
|
61250
61561
|
attemptId: attempt.attemptId,
|
|
61251
61562
|
clientToken,
|
|
61252
61563
|
requestDigest,
|
|
@@ -61264,11 +61575,11 @@ class EcsDispatcher {
|
|
|
61264
61575
|
try {
|
|
61265
61576
|
result = await this.client.runTask(this.runTaskInput(run.admission, intent.attempt, clientToken, startedBy, requestDigest));
|
|
61266
61577
|
} catch {
|
|
61267
|
-
await this.store.recordLaunchState({ runId:
|
|
61578
|
+
await this.store.recordLaunchState({ runId: runId3, attemptId: attempt.attemptId, launchState: "ambiguous" });
|
|
61268
61579
|
const reconciled = await this.reconcile(run.admission, intent.attempt);
|
|
61269
61580
|
return reconciled;
|
|
61270
61581
|
}
|
|
61271
|
-
await this.store.recordLaunchState({ runId:
|
|
61582
|
+
await this.store.recordLaunchState({ runId: runId3, attemptId: attempt.attemptId, launchState: "launched", taskId: result.taskArn });
|
|
61272
61583
|
return { kind: "launched", attemptId: attempt.attemptId, taskId: result.taskArn };
|
|
61273
61584
|
}
|
|
61274
61585
|
async reconcile(admission, attempt) {
|
|
@@ -61330,7 +61641,7 @@ class EcsDispatcher {
|
|
|
61330
61641
|
}
|
|
61331
61642
|
cancellationReceipt(receipt, admission, attempt) {
|
|
61332
61643
|
try {
|
|
61333
|
-
return !!receipt && receipt.runId === admission.runId && receipt.attemptId === attempt.attemptId && !!attempt.clientToken && receipt.clientToken === attempt.clientToken && !!attempt.requestDigest && receipt.requestDigest === attempt.requestDigest && !!attempt.startedBy && receipt.startedBy === attempt.startedBy && receipt.bundleDigest === admission.bundleDigest && receipt.runtimeImageDigest === admission.runtimeImageDigest && receipt.dependencyLayerTag === admission.dependencyLayerTag && canonicalJson2(receipt.policy) === canonicalJson2(admission.policy) && canonicalJson2(receipt.limits) === canonicalJson2(admission.limits) && (receipt.taskId === null || receipt.taskId === attempt.taskId) && (receipt.status === null && receipt.completedAt === null || receipt.status === "cancelled" && typeof receipt.completedAt === "string" && Number.isFinite(Date.parse(receipt.completedAt)));
|
|
61644
|
+
return !!receipt && receipt.runId === admission.runId && receipt.attemptId === attempt.attemptId && !!attempt.clientToken && receipt.clientToken === attempt.clientToken && !!attempt.requestDigest && receipt.requestDigest === attempt.requestDigest && !!attempt.startedBy && receipt.startedBy === attempt.startedBy && receipt.bundleDigest === admission.bundleDigest && receipt.runtimeImageDigest === admission.runtimeImageDigest && receipt.dependencyLayerTag === admission.dependencyLayerTag && canonicalJson2(receipt.executionContract ?? null) === canonicalJson2(admission.executionContract ?? null) && canonicalJson2(receipt.policy) === canonicalJson2(admission.policy) && canonicalJson2(receipt.limits) === canonicalJson2(admission.limits) && (receipt.taskId === null || receipt.taskId === attempt.taskId) && (receipt.status === null && receipt.completedAt === null || receipt.status === "cancelled" && typeof receipt.completedAt === "string" && Number.isFinite(Date.parse(receipt.completedAt)));
|
|
61334
61645
|
} catch {
|
|
61335
61646
|
return false;
|
|
61336
61647
|
}
|
|
@@ -61427,7 +61738,174 @@ function validText(value) {
|
|
|
61427
61738
|
}
|
|
61428
61739
|
|
|
61429
61740
|
// src/server/runtime-policy.ts
|
|
61430
|
-
import { createHash as
|
|
61741
|
+
import { createHash as createHash26, createHmac as createHmac3, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
61742
|
+
|
|
61743
|
+
// src/server/runtime-pure-contract.ts
|
|
61744
|
+
import { createHash as createHash25 } from "crypto";
|
|
61745
|
+
var PURE_DESCRIPTOR = Object.freeze({
|
|
61746
|
+
id: "regex-test.v1",
|
|
61747
|
+
runtime: "bun",
|
|
61748
|
+
adapter: "skills-input-json.v1",
|
|
61749
|
+
input: Object.freeze({ patternBytes: 512, textBytes: 4096, totalBytes: 8192, flags: "dgimsuvy" }),
|
|
61750
|
+
output: "regex-matches.v1",
|
|
61751
|
+
secrets: "none",
|
|
61752
|
+
egress: "deny",
|
|
61753
|
+
maxDurationMs: 5000,
|
|
61754
|
+
maxMemoryMb: 512,
|
|
61755
|
+
maxCpuUnits: 256,
|
|
61756
|
+
stdoutBytes: 16384,
|
|
61757
|
+
stderrBytes: 8192,
|
|
61758
|
+
artifactsBytes: 0
|
|
61759
|
+
});
|
|
61760
|
+
var PURE_DESCRIPTOR_DIGEST = digestInput(PURE_DESCRIPTOR);
|
|
61761
|
+
var PURE_LIMITS = Object.freeze({
|
|
61762
|
+
maxDurationMs: 5000,
|
|
61763
|
+
maxMemoryMb: 512,
|
|
61764
|
+
maxCpuUnits: 256,
|
|
61765
|
+
maxArtifactsBytes: 0,
|
|
61766
|
+
maxConcurrency: 1
|
|
61767
|
+
});
|
|
61768
|
+
var hash2 = (bytes) => createHash25("sha256").update(bytes).digest("hex");
|
|
61769
|
+
var object5 = (v2) => {
|
|
61770
|
+
if (!v2 || typeof v2 !== "object" || Array.isArray(v2))
|
|
61771
|
+
throw Error("Pure contract requires an object");
|
|
61772
|
+
return v2;
|
|
61773
|
+
};
|
|
61774
|
+
function keys2(v2, allowed) {
|
|
61775
|
+
if (Object.keys(v2).some((k4) => !allowed.includes(k4)))
|
|
61776
|
+
throw Error("Unsupported pure contract field");
|
|
61777
|
+
}
|
|
61778
|
+
function uniqueJson(text2) {
|
|
61779
|
+
const result = JSON.parse(text2);
|
|
61780
|
+
let i4 = 0;
|
|
61781
|
+
const ws = () => {
|
|
61782
|
+
while (/\s/.test(text2[i4] ?? "") && i4 < text2.length)
|
|
61783
|
+
i4++;
|
|
61784
|
+
};
|
|
61785
|
+
const string = () => {
|
|
61786
|
+
const start = i4++;
|
|
61787
|
+
while (i4 < text2.length) {
|
|
61788
|
+
const c4 = text2[i4++];
|
|
61789
|
+
if (c4 === "\\")
|
|
61790
|
+
i4++;
|
|
61791
|
+
else if (c4 === '"')
|
|
61792
|
+
break;
|
|
61793
|
+
}
|
|
61794
|
+
return JSON.parse(text2.slice(start, i4));
|
|
61795
|
+
};
|
|
61796
|
+
const value = (depth) => {
|
|
61797
|
+
if (depth > 32)
|
|
61798
|
+
throw Error("JSON nesting limit exceeded");
|
|
61799
|
+
ws();
|
|
61800
|
+
if (text2[i4] === '"') {
|
|
61801
|
+
string();
|
|
61802
|
+
return;
|
|
61803
|
+
}
|
|
61804
|
+
if (text2[i4] === "{") {
|
|
61805
|
+
i4++;
|
|
61806
|
+
ws();
|
|
61807
|
+
const seen = new Set;
|
|
61808
|
+
if (text2[i4] === "}") {
|
|
61809
|
+
i4++;
|
|
61810
|
+
return;
|
|
61811
|
+
}
|
|
61812
|
+
for (;; ) {
|
|
61813
|
+
ws();
|
|
61814
|
+
const key = string();
|
|
61815
|
+
if (seen.has(key))
|
|
61816
|
+
throw Error("Duplicate JSON key");
|
|
61817
|
+
seen.add(key);
|
|
61818
|
+
ws();
|
|
61819
|
+
i4++;
|
|
61820
|
+
value(depth + 1);
|
|
61821
|
+
ws();
|
|
61822
|
+
if (text2[i4++] === "}")
|
|
61823
|
+
return;
|
|
61824
|
+
}
|
|
61825
|
+
}
|
|
61826
|
+
if (text2[i4] === "[") {
|
|
61827
|
+
i4++;
|
|
61828
|
+
ws();
|
|
61829
|
+
if (text2[i4] === "]") {
|
|
61830
|
+
i4++;
|
|
61831
|
+
return;
|
|
61832
|
+
}
|
|
61833
|
+
for (;; ) {
|
|
61834
|
+
value(depth + 1);
|
|
61835
|
+
ws();
|
|
61836
|
+
if (text2[i4++] === "]")
|
|
61837
|
+
return;
|
|
61838
|
+
}
|
|
61839
|
+
}
|
|
61840
|
+
while (i4 < text2.length && !/[\s,\]}]/.test(text2[i4]))
|
|
61841
|
+
i4++;
|
|
61842
|
+
};
|
|
61843
|
+
value(0);
|
|
61844
|
+
return result;
|
|
61845
|
+
}
|
|
61846
|
+
function pureInput(value) {
|
|
61847
|
+
const v2 = object5(value);
|
|
61848
|
+
keys2(v2, ["pattern", "text", "flags"]);
|
|
61849
|
+
if (typeof v2.pattern !== "string" || Buffer.byteLength(v2.pattern) > 512 || typeof v2.text !== "string" || Buffer.byteLength(v2.text) > 4096 || typeof v2.flags !== "string" || !/^[dgimsuvy]*$/.test(v2.flags) || new Set(v2.flags).size !== v2.flags.length || v2.flags.includes("u") && v2.flags.includes("v") || Buffer.byteLength(JSON.stringify(v2)) > 8192)
|
|
61850
|
+
throw Error("Invalid pure regex input");
|
|
61851
|
+
return { pattern: v2.pattern, text: v2.text, flags: v2.flags };
|
|
61852
|
+
}
|
|
61853
|
+
function assertPureContract(value) {
|
|
61854
|
+
const v2 = object5(value);
|
|
61855
|
+
keys2(v2, ["id", "descriptorDigest", "entrypoint", "entrypointDigest"]);
|
|
61856
|
+
if (v2.id !== PURE_DESCRIPTOR.id || v2.descriptorDigest !== PURE_DESCRIPTOR_DIGEST || typeof v2.entrypoint !== "string" || !/^src\/[a-zA-Z0-9_/-]+\.[cm]?[jt]s$/.test(v2.entrypoint) || v2.entrypoint.includes("//") || typeof v2.entrypointDigest !== "string" || !/^[a-f0-9]{64}$/.test(v2.entrypointDigest))
|
|
61857
|
+
throw Error("Unsupported pure execution contract");
|
|
61858
|
+
}
|
|
61859
|
+
function assertPureAdmission(a4) {
|
|
61860
|
+
assertPureContract(a4.executionContract);
|
|
61861
|
+
if (a4.runtime !== "bun" || a4.dependencyLayerTag !== null || a4.policy.egress !== "deny" || a4.policy.egressAllowlist.length !== 0 || a4.policy.networkByteCap !== 0 || Object.entries(PURE_LIMITS).some(([k4, v2]) => a4.limits[k4] !== v2))
|
|
61862
|
+
throw Error("Pure admission isolation policy mismatch");
|
|
61863
|
+
return a4.executionContract;
|
|
61864
|
+
}
|
|
61865
|
+
async function inspectPureBundle(bytes, contract) {
|
|
61866
|
+
assertPureContract(contract);
|
|
61867
|
+
const { entries } = await inspectSkillBundle(bytes, { limits: {
|
|
61868
|
+
compressedBytes: 1e6,
|
|
61869
|
+
decompressedBytes: 4000000,
|
|
61870
|
+
fileBytes: 1e6,
|
|
61871
|
+
entries: 100,
|
|
61872
|
+
timeoutMs: 5000
|
|
61873
|
+
} });
|
|
61874
|
+
const entry = entries.find((e5) => e5.path === contract.entrypoint);
|
|
61875
|
+
if (!entry || hash2(entry.bytes) !== contract.entrypointDigest)
|
|
61876
|
+
throw Error("Pure entrypoint integrity failed");
|
|
61877
|
+
if (entries.some((e5) => e5.path.split("/").includes("node_modules")))
|
|
61878
|
+
throw Error("Pure bundle cannot supply dependencies");
|
|
61879
|
+
const read2 = (name) => {
|
|
61880
|
+
const entry2 = entries.find((e5) => e5.path === name);
|
|
61881
|
+
if (!entry2)
|
|
61882
|
+
throw Error("Pure bundle manifest missing");
|
|
61883
|
+
return object5(uniqueJson(Buffer.from(entry2.bytes).toString("utf8")));
|
|
61884
|
+
};
|
|
61885
|
+
const manifest = read2("skill.json"), runtime = object5(manifest.runtime), pkg = read2("package.json");
|
|
61886
|
+
if (manifest.kind !== "executable" || runtime.runtime !== "bun" || runtime.entrypoint !== contract.entrypoint || !Array.isArray(runtime.env) || runtime.env.length !== 0 || runtime.system_deps !== undefined && (!Array.isArray(runtime.system_deps) || runtime.system_deps.length !== 0) || runtime.needs_network === true || manifest.needs_network === true)
|
|
61887
|
+
throw Error("Pure bundle requires a credential-free self-contained Bun entrypoint");
|
|
61888
|
+
for (const key of ["dependencies", "optionalDependencies", "peerDependencies", "devDependencies", "scripts"])
|
|
61889
|
+
if (pkg[key] !== undefined && Object.keys(object5(pkg[key])).length)
|
|
61890
|
+
throw Error("Pure bundle cannot install dependencies or lifecycle scripts");
|
|
61891
|
+
return entries;
|
|
61892
|
+
}
|
|
61893
|
+
function validatePureOutput(stdout, input) {
|
|
61894
|
+
if (Buffer.byteLength(stdout) > PURE_DESCRIPTOR.stdoutBytes)
|
|
61895
|
+
throw Error("Pure output limit exceeded");
|
|
61896
|
+
const v2 = object5(uniqueJson(stdout));
|
|
61897
|
+
keys2(v2, ["pattern", "flags", "matches"]);
|
|
61898
|
+
if (v2.pattern !== input.pattern || v2.flags !== input.flags || !Array.isArray(v2.matches))
|
|
61899
|
+
throw Error("Pure output does not match admitted input");
|
|
61900
|
+
for (const value of v2.matches) {
|
|
61901
|
+
const m2 = object5(value);
|
|
61902
|
+
keys2(m2, ["match", "groups", "namedGroups", "index"]);
|
|
61903
|
+
if (typeof m2.match !== "string" || !Number.isInteger(m2.index) || m2.index < 0 || m2.index > input.text.length || input.text.slice(m2.index, m2.index + m2.match.length) !== m2.match || !Array.isArray(m2.groups) || m2.groups.some((x2) => x2 !== null && typeof x2 !== "string") || m2.namedGroups !== null && Object.values(object5(m2.namedGroups)).some((x2) => typeof x2 !== "string"))
|
|
61904
|
+
throw Error("Invalid pure match output");
|
|
61905
|
+
}
|
|
61906
|
+
}
|
|
61907
|
+
|
|
61908
|
+
// src/server/runtime-policy.ts
|
|
61431
61909
|
var RUNTIME_MAX_INPUT_BYTES = 1e5;
|
|
61432
61910
|
var RUNTIME_MAX_BUNDLE_BYTES = 1e6;
|
|
61433
61911
|
var RUNTIME_MAX_ARTIFACT_BYTES = 2000000;
|
|
@@ -61462,8 +61940,25 @@ function readRuntimeConfig(env = process.env) {
|
|
|
61462
61940
|
for (const key of ["subnets", "securityGroups"])
|
|
61463
61941
|
if (!Array.isArray(c4[key]) || !c4[key].length || c4[key].some((v2) => typeof v2 !== "string" || !v2))
|
|
61464
61942
|
throw Error(`Runtime configuration missing ${key}`);
|
|
61465
|
-
if (!Array.isArray(c4.reviewedBundles) || !c4.reviewedBundles.length
|
|
61466
|
-
throw Error("Runtime requires exact reviewed
|
|
61943
|
+
if (!Array.isArray(c4.reviewedBundles) || !c4.reviewedBundles.length)
|
|
61944
|
+
throw Error("Runtime requires exact reviewed versions and bundle digests");
|
|
61945
|
+
const seen = new Set;
|
|
61946
|
+
for (const b4 of c4.reviewedBundles) {
|
|
61947
|
+
if (!b4 || typeof b4.slug !== "string" || !/^[a-z0-9-]+$/.test(b4.slug) || typeof b4.version !== "string" || !/^\d+\.\d+\.\d+$/.test(b4.version) || !/^[a-f0-9]{64}$/.test(b4.sha256))
|
|
61948
|
+
throw Error("Runtime requires exact reviewed versions and bundle digests");
|
|
61949
|
+
if (b4.executionContract) {
|
|
61950
|
+
if (b4.slug === "pdf-generate")
|
|
61951
|
+
throw Error("pdf-generate is reserved for the legacy PDF contract");
|
|
61952
|
+
assertPureContract(b4.executionContract);
|
|
61953
|
+
if (typeof b4.tenantId !== "string" || !b4.tenantId.trim() || b4.imageDigest !== c4.imageDigest)
|
|
61954
|
+
throw Error("Pure runtime review must bind tenant and configured image");
|
|
61955
|
+
} else if (b4.slug !== "pdf-generate")
|
|
61956
|
+
throw Error("Missing reviewed execution contract");
|
|
61957
|
+
const key = JSON.stringify([b4.executionContract ? b4.tenantId : null, b4.slug, b4.version, b4.sha256]);
|
|
61958
|
+
if (seen.has(key))
|
|
61959
|
+
throw Error("Ambiguous runtime review");
|
|
61960
|
+
seen.add(key);
|
|
61961
|
+
}
|
|
61467
61962
|
return c4;
|
|
61468
61963
|
}
|
|
61469
61964
|
function runtimeInput(value) {
|
|
@@ -61499,7 +61994,7 @@ function verifyRuntimeToken(token, key, admission, attempt, now = Date.now()) {
|
|
|
61499
61994
|
const actual = Buffer.from(token);
|
|
61500
61995
|
return actual.length === expected.length && timingSafeEqual2(actual, expected);
|
|
61501
61996
|
}
|
|
61502
|
-
var hashBytes = (bytes) =>
|
|
61997
|
+
var hashBytes = (bytes) => createHash26("sha256").update(bytes).digest("hex");
|
|
61503
61998
|
|
|
61504
61999
|
// src/server/runtime-api.ts
|
|
61505
62000
|
async function createRuntimeService(options) {
|
|
@@ -61575,7 +62070,7 @@ async function body3(request2, max) {
|
|
|
61575
62070
|
}
|
|
61576
62071
|
chunks.push(v2.value);
|
|
61577
62072
|
}
|
|
61578
|
-
return
|
|
62073
|
+
return uniqueJson(Buffer.concat(chunks).toString("utf8"));
|
|
61579
62074
|
}
|
|
61580
62075
|
var scope = (p2, action) => p2.scopes.includes("*") || p2.scopes.includes("runs:*") || p2.scopes.includes(action) || action === "runs:read" && p2.scopes.includes("skills:read");
|
|
61581
62076
|
function publicJob(job) {
|
|
@@ -61589,6 +62084,7 @@ function publicJob(job) {
|
|
|
61589
62084
|
bundleDigest: a4.bundleDigest,
|
|
61590
62085
|
inputDigest: a4.inputDigest,
|
|
61591
62086
|
runtimeImageDigest: a4.runtimeImageDigest,
|
|
62087
|
+
executionContract: a4.executionContract ?? { id: "pdf.v1" },
|
|
61592
62088
|
status: job.execution.status,
|
|
61593
62089
|
createdAt: a4.createdAt,
|
|
61594
62090
|
updatedAt: job.execution.updatedAt,
|
|
@@ -61599,6 +62095,33 @@ function publicJob(job) {
|
|
|
61599
62095
|
artifacts: (job.result?.artifacts ?? []).map(({ base64, ...meta }) => meta)
|
|
61600
62096
|
};
|
|
61601
62097
|
}
|
|
62098
|
+
function findReview(service, tenant, slug, version2, sha2562) {
|
|
62099
|
+
return service.config.reviewedBundles.find((b4) => b4.slug === slug && b4.version === version2 && b4.sha256 === sha2562 && (!b4.executionContract || b4.tenantId === tenant && b4.imageDigest === service.config.imageDigest));
|
|
62100
|
+
}
|
|
62101
|
+
function sameContract(admission, contract, image) {
|
|
62102
|
+
return digestInput(admission.executionContract ?? null) === digestInput(contract ?? null) && (!contract || admission.runtimeImageDigest === image);
|
|
62103
|
+
}
|
|
62104
|
+
|
|
62105
|
+
class RuntimeBundleError extends Error {
|
|
62106
|
+
code;
|
|
62107
|
+
status;
|
|
62108
|
+
constructor(code, status) {
|
|
62109
|
+
super(code);
|
|
62110
|
+
this.code = code;
|
|
62111
|
+
this.status = status;
|
|
62112
|
+
}
|
|
62113
|
+
}
|
|
62114
|
+
async function reviewedBytes(service, principal, digest4) {
|
|
62115
|
+
const bundle2 = await service.productStore.getSkillBundle(principal, digest4);
|
|
62116
|
+
if (!bundle2)
|
|
62117
|
+
throw new RuntimeBundleError("BUNDLE_UNAVAILABLE", 503);
|
|
62118
|
+
if (bundle2.byteSize > RUNTIME_MAX_BUNDLE_BYTES)
|
|
62119
|
+
throw new RuntimeBundleError("BUNDLE_LIMIT", 413);
|
|
62120
|
+
const bytes = await service.artifacts.readBundle(bundle2);
|
|
62121
|
+
if (!bytes || bytes.length > RUNTIME_MAX_BUNDLE_BYTES || hashBytes(bytes) !== digest4)
|
|
62122
|
+
throw new RuntimeBundleError("BUNDLE_INTEGRITY_FAILED", 503);
|
|
62123
|
+
return bytes;
|
|
62124
|
+
}
|
|
61602
62125
|
async function handleRuntimeApiRequest(request2, principal, service) {
|
|
61603
62126
|
const segments = path(request2);
|
|
61604
62127
|
if (segments[0] !== "api" || segments[1] !== "v1" || segments[2] !== "executions")
|
|
@@ -61611,9 +62134,50 @@ async function handleRuntimeApiRequest(request2, principal, service) {
|
|
|
61611
62134
|
if (!id2 || segments.length > 6 || !/^[a-z0-9_-]+$/.test(id2))
|
|
61612
62135
|
return refusal2("INVALID_EXECUTION_PATH", 400);
|
|
61613
62136
|
try {
|
|
62137
|
+
if (request2.method === "GET" && sub === "eligibility" && segments.length === 5) {
|
|
62138
|
+
const query = new URL(request2.url).searchParams;
|
|
62139
|
+
const versionName = query.get("version");
|
|
62140
|
+
if (!versionName || !/^\d+\.\d+\.\d+$/.test(versionName))
|
|
62141
|
+
return refusal2("REVIEWED_VERSION_REQUIRED", 400);
|
|
62142
|
+
const skill = await service.productStore.getSkill(principal, id2);
|
|
62143
|
+
if (skill?.lifecycle === "archived")
|
|
62144
|
+
return refusal2("SKILL_ARCHIVED", 410);
|
|
62145
|
+
const version2 = skill && !skill.tombstonedAt && await service.productStore.getSkillVersion(principal, id2, versionName);
|
|
62146
|
+
if (!version2)
|
|
62147
|
+
return refusal2("SKILL_VERSION_NOT_FOUND", 404);
|
|
62148
|
+
const pinned = query.get("bundleDigest");
|
|
62149
|
+
if (pinned && pinned.replace(/^sha256:/, "") !== version2.bundleSha256)
|
|
62150
|
+
return refusal2("SELECTION_DIGEST_MISMATCH", 409);
|
|
62151
|
+
const review = findReview(service, principal.orgId, id2, versionName, version2.bundleSha256);
|
|
62152
|
+
if (!review)
|
|
62153
|
+
return json3({
|
|
62154
|
+
contractVersion: 1,
|
|
62155
|
+
eligible: false,
|
|
62156
|
+
reason: "BUNDLE_NOT_REVIEWED_FOR_CLOUD",
|
|
62157
|
+
skill: id2,
|
|
62158
|
+
version: versionName,
|
|
62159
|
+
bundleDigest: version2.bundleSha256
|
|
62160
|
+
});
|
|
62161
|
+
if (review.executionContract) {
|
|
62162
|
+
const bytes = await reviewedBytes(service, principal, version2.bundleSha256);
|
|
62163
|
+
await inspectPureBundle(bytes, review.executionContract);
|
|
62164
|
+
}
|
|
62165
|
+
return json3({
|
|
62166
|
+
contractVersion: 1,
|
|
62167
|
+
eligible: true,
|
|
62168
|
+
skill: id2,
|
|
62169
|
+
version: versionName,
|
|
62170
|
+
bundleDigest: version2.bundleSha256,
|
|
62171
|
+
runtimeImageDigest: service.config.imageDigest,
|
|
62172
|
+
executionContract: review.executionContract ?? { id: "pdf.v1" },
|
|
62173
|
+
secrets: "none",
|
|
62174
|
+
egress: "deny",
|
|
62175
|
+
...review.executionContract ? { descriptor: PURE_DESCRIPTOR, limits: PURE_LIMITS } : {}
|
|
62176
|
+
});
|
|
62177
|
+
}
|
|
61614
62178
|
if (request2.method === "POST" && !sub) {
|
|
61615
62179
|
const raw = await body3(request2, 150000);
|
|
61616
|
-
if (
|
|
62180
|
+
if (typeof raw.version !== "string" || !/^\d+\.\d+\.\d+$/.test(raw.version))
|
|
61617
62181
|
return refusal2("REVIEWED_VERSION_REQUIRED", 400);
|
|
61618
62182
|
if (Object.keys(raw).some((k4) => ![
|
|
61619
62183
|
"version",
|
|
@@ -61625,31 +62189,32 @@ async function handleRuntimeApiRequest(request2, principal, service) {
|
|
|
61625
62189
|
return refusal2("UNSUPPORTED_EXECUTION_FIELD", 400);
|
|
61626
62190
|
if (raw.workspaceId !== undefined && raw.workspaceId !== principal.orgId)
|
|
61627
62191
|
return refusal2("SELECTION_WORKSPACE_MISMATCH", 409);
|
|
61628
|
-
const input = runtimeInput(raw.input);
|
|
61629
62192
|
const key = request2.headers.get("idempotency-key") ?? raw.idempotencyKey;
|
|
61630
62193
|
if (typeof key !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(key))
|
|
61631
62194
|
return refusal2("IDEMPOTENCY_KEY_REQUIRED", 400);
|
|
61632
62195
|
const skill = await service.productStore.getSkill(principal, id2);
|
|
61633
62196
|
if (!skill || skill.tombstonedAt)
|
|
61634
62197
|
return refusal2("SKILL_NOT_FOUND", 404);
|
|
62198
|
+
if (skill.lifecycle === "archived")
|
|
62199
|
+
return refusal2("SKILL_ARCHIVED", 410);
|
|
61635
62200
|
const version2 = await service.productStore.getSkillVersion(principal, id2, raw.version);
|
|
61636
62201
|
if (!version2)
|
|
61637
62202
|
return refusal2("SKILL_VERSION_NOT_FOUND", 404);
|
|
61638
62203
|
if (raw.bundleDigest !== undefined && (typeof raw.bundleDigest !== "string" || raw.bundleDigest.replace(/^sha256:/, "") !== version2.bundleSha256))
|
|
61639
62204
|
return refusal2("SELECTION_DIGEST_MISMATCH", 409);
|
|
61640
|
-
|
|
62205
|
+
const review = findReview(service, principal.orgId, id2, raw.version, version2.bundleSha256);
|
|
62206
|
+
if (!review)
|
|
61641
62207
|
return refusal2("BUNDLE_NOT_REVIEWED_FOR_CLOUD", 403);
|
|
62208
|
+
const contract = review.executionContract;
|
|
62209
|
+
if (contract && (raw.workspaceId !== principal.orgId || typeof raw.bundleDigest !== "string"))
|
|
62210
|
+
return refusal2("PURE_SELECTION_REQUIRED", 400);
|
|
62211
|
+
const input = contract ? pureInput(raw.input) : runtimeInput(raw.input);
|
|
61642
62212
|
const existing = await service.store.getRunByKey(principal.orgId, key);
|
|
61643
|
-
if (existing && (existing.admission.skillId !== id2 || existing.admission.skillVersion !== raw.version || existing.admission.bundleDigest !== version2.bundleSha256 || existing.admission.inputDigest !== digestInput(input)))
|
|
62213
|
+
if (existing && (existing.admission.skillId !== id2 || existing.admission.skillVersion !== raw.version || existing.admission.bundleDigest !== version2.bundleSha256 || existing.admission.inputDigest !== digestInput(input) || !sameContract(existing.admission, contract, service.config.imageDigest)))
|
|
61644
62214
|
return refusal2("IDEMPOTENCY_CONFLICT", 409);
|
|
61645
|
-
const
|
|
61646
|
-
if (
|
|
61647
|
-
|
|
61648
|
-
if (bundle2.byteSize > RUNTIME_MAX_BUNDLE_BYTES)
|
|
61649
|
-
return refusal2("BUNDLE_LIMIT", 413);
|
|
61650
|
-
const bytes = await service.artifacts.readBundle(bundle2);
|
|
61651
|
-
if (!bytes || hashBytes(bytes) !== version2.bundleSha256)
|
|
61652
|
-
return refusal2("BUNDLE_INTEGRITY_FAILED", 503);
|
|
62215
|
+
const bytes = await reviewedBytes(service, principal, version2.bundleSha256);
|
|
62216
|
+
if (contract)
|
|
62217
|
+
await inspectPureBundle(bytes, contract);
|
|
61653
62218
|
const admission = createSubmitRunService({
|
|
61654
62219
|
store: service.store,
|
|
61655
62220
|
imageProfiles: createImageProfileRegistry({
|
|
@@ -61671,12 +62236,13 @@ async function handleRuntimeApiRequest(request2, principal, service) {
|
|
|
61671
62236
|
input,
|
|
61672
62237
|
idempotencyKey: key,
|
|
61673
62238
|
runtime: "bun",
|
|
61674
|
-
|
|
62239
|
+
...contract ? { executionContract: contract } : {},
|
|
62240
|
+
limits: contract ? PURE_LIMITS : {
|
|
61675
62241
|
maxDurationMs: RUNTIME_TIMEOUT_MS,
|
|
61676
62242
|
maxArtifactsBytes: RUNTIME_MAX_ARTIFACT_BYTES
|
|
61677
62243
|
}
|
|
61678
62244
|
});
|
|
61679
|
-
if (run.tenantId !== principal.orgId || run.skillId !== id2 || run.skillVersion !== raw.version || run.bundleDigest !== version2.bundleSha256 || run.inputDigest !== digestInput(input))
|
|
62245
|
+
if (run.tenantId !== principal.orgId || run.skillId !== id2 || run.skillVersion !== raw.version || run.bundleDigest !== version2.bundleSha256 || run.inputDigest !== digestInput(input) || !sameContract(run, contract, service.config.imageDigest))
|
|
61680
62246
|
return refusal2("IDEMPOTENCY_CONFLICT", 409);
|
|
61681
62247
|
await service.store.mutate(run.runId, (j4) => {
|
|
61682
62248
|
if (!j4.input) {
|
|
@@ -61768,6 +62334,8 @@ async function handleRuntimeApiRequest(request2, principal, service) {
|
|
|
61768
62334
|
}
|
|
61769
62335
|
return refusal2("EXECUTION_ROUTE_NOT_FOUND", 404);
|
|
61770
62336
|
} catch (error) {
|
|
62337
|
+
if (error instanceof RuntimeBundleError)
|
|
62338
|
+
return refusal2(error.code, error.status);
|
|
61771
62339
|
if (error.message === "Runtime concurrency limit reached")
|
|
61772
62340
|
return refusal2("RUNTIME_CONCURRENCY_LIMIT", 429);
|
|
61773
62341
|
return refusal2("EXECUTION_REQUEST_FAILED", 400, error.message.startsWith("PDF ") ? error.message : "Execution request could not be completed");
|
|
@@ -61798,7 +62366,7 @@ async function handleRuntimeWorkerRequest(request2, service) {
|
|
|
61798
62366
|
}
|
|
61799
62367
|
if (request2.method === "POST" && s2[4] === "complete") {
|
|
61800
62368
|
try {
|
|
61801
|
-
const result = validatedResult(await body3(request2, 3000000));
|
|
62369
|
+
const result = validatedResult(await body3(request2, 3000000), job);
|
|
61802
62370
|
await service.store.mutate(s2[3], (j4) => {
|
|
61803
62371
|
if (j4.execution.status === "cancelled")
|
|
61804
62372
|
throw Error("Execution cancelled");
|
|
@@ -61835,12 +62403,28 @@ async function handleRuntimeWorkerRequest(request2, service) {
|
|
|
61835
62403
|
}
|
|
61836
62404
|
return refusal2("METHOD_NOT_ALLOWED", 405);
|
|
61837
62405
|
}
|
|
61838
|
-
function validatedResult(value) {
|
|
62406
|
+
function validatedResult(value, job) {
|
|
61839
62407
|
if (!value || typeof value !== "object")
|
|
61840
62408
|
throw Error("Invalid result");
|
|
61841
62409
|
const v2 = value;
|
|
61842
62410
|
if (!Number.isInteger(v2.exitCode) || v2.exitCode < 0 || v2.exitCode > 255 || typeof v2.stdout !== "string" || typeof v2.stderr !== "string" || Buffer.byteLength(v2.stdout) > RUNTIME_MAX_LOG_BYTES || Buffer.byteLength(v2.stderr) > RUNTIME_MAX_LOG_BYTES || !Array.isArray(v2.artifacts) || v2.artifacts.length > 2)
|
|
61843
62411
|
throw Error("Invalid result");
|
|
62412
|
+
if (job.execution.admission.executionContract) {
|
|
62413
|
+
assertPureAdmission(job.execution.admission);
|
|
62414
|
+
if (v2.artifacts.length || Buffer.byteLength(v2.stdout) > PURE_DESCRIPTOR.stdoutBytes || Buffer.byteLength(v2.stderr) > PURE_DESCRIPTOR.stderrBytes)
|
|
62415
|
+
throw Error("Invalid pure result limits");
|
|
62416
|
+
if (v2.exitCode === 0)
|
|
62417
|
+
validatePureOutput(v2.stdout, pureInput(job.input));
|
|
62418
|
+
return {
|
|
62419
|
+
exitCode: v2.exitCode,
|
|
62420
|
+
stdout: v2.stdout,
|
|
62421
|
+
stderr: v2.stderr,
|
|
62422
|
+
artifacts: [],
|
|
62423
|
+
...typeof v2.error === "string" ? { error: v2.error.slice(0, 500) } : {}
|
|
62424
|
+
};
|
|
62425
|
+
}
|
|
62426
|
+
if (job.execution.admission.skillId !== "pdf-generate")
|
|
62427
|
+
throw Error("Missing execution contract");
|
|
61844
62428
|
let total = 0;
|
|
61845
62429
|
const seen = new Set;
|
|
61846
62430
|
for (const a4 of v2.artifacts) {
|
|
@@ -62048,8 +62632,22 @@ class MemorySkillsStore {
|
|
|
62048
62632
|
async ensureBootstrapApiKey(token, principal) {
|
|
62049
62633
|
this.addApiKey(token, principal);
|
|
62050
62634
|
}
|
|
62051
|
-
async authenticateApiKeyHash(
|
|
62052
|
-
return this.apiKeys.get(
|
|
62635
|
+
async authenticateApiKeyHash(hash3) {
|
|
62636
|
+
return this.apiKeys.get(hash3) ?? null;
|
|
62637
|
+
}
|
|
62638
|
+
async updateApiKeyScopes(actor, keyId, expectedScopes, addScopes) {
|
|
62639
|
+
for (const [hash3, target] of this.apiKeys) {
|
|
62640
|
+
if (target.apiKeyId !== keyId || target.orgId !== actor.orgId)
|
|
62641
|
+
continue;
|
|
62642
|
+
if (target.scopes.length !== expectedScopes.length || target.scopes.some((scope2, index) => scope2 !== expectedScopes[index])) {
|
|
62643
|
+
return { kind: "stale", scopes: [...target.scopes] };
|
|
62644
|
+
}
|
|
62645
|
+
const scopes = [...target.scopes, ...addScopes.filter((scope2) => !target.scopes.includes(scope2))];
|
|
62646
|
+
const updated = { ...target, scopes };
|
|
62647
|
+
this.apiKeys.set(hash3, updated);
|
|
62648
|
+
return { kind: "updated", scopes };
|
|
62649
|
+
}
|
|
62650
|
+
return { kind: "not_found" };
|
|
62053
62651
|
}
|
|
62054
62652
|
async createRun(input) {
|
|
62055
62653
|
const idemKey = input.idempotencyKey?.trim();
|
|
@@ -62061,7 +62659,7 @@ class MemorySkillsStore {
|
|
|
62061
62659
|
}
|
|
62062
62660
|
const now = nowIso();
|
|
62063
62661
|
const run = {
|
|
62064
|
-
id:
|
|
62662
|
+
id: runId2(),
|
|
62065
62663
|
orgId: input.principal.orgId,
|
|
62066
62664
|
userId: input.principal.userId,
|
|
62067
62665
|
skill: input.slug,
|
|
@@ -62085,8 +62683,8 @@ class MemorySkillsStore {
|
|
|
62085
62683
|
async listRuns(principal, limit) {
|
|
62086
62684
|
return Array.from(this.runs.values()).filter((run) => run.orgId === principal.orgId).sort((a4, b4) => b4.createdAt.localeCompare(a4.createdAt)).slice(0, normalizeLimit(limit));
|
|
62087
62685
|
}
|
|
62088
|
-
async getRun(principal,
|
|
62089
|
-
const run = this.runs.get(
|
|
62686
|
+
async getRun(principal, runId3) {
|
|
62687
|
+
const run = this.runs.get(runId3);
|
|
62090
62688
|
return run && run.orgId === principal.orgId ? run : null;
|
|
62091
62689
|
}
|
|
62092
62690
|
async claimNextRun(_input) {
|
|
@@ -62095,28 +62693,28 @@ class MemorySkillsStore {
|
|
|
62095
62693
|
return null;
|
|
62096
62694
|
return this.patchRun(run.id, { status: "running", startedAt: run.startedAt ?? nowIso(), leaseGeneration: run.leaseGeneration + 1 });
|
|
62097
62695
|
}
|
|
62098
|
-
async updateRun(
|
|
62099
|
-
return this.patchRun(
|
|
62696
|
+
async updateRun(runId3, patch) {
|
|
62697
|
+
return this.patchRun(runId3, patch);
|
|
62100
62698
|
}
|
|
62101
|
-
async transitionRun(
|
|
62102
|
-
const run = this.runs.get(
|
|
62699
|
+
async transitionRun(runId3, patch, expectedGeneration) {
|
|
62700
|
+
const run = this.runs.get(runId3);
|
|
62103
62701
|
if (!run)
|
|
62104
62702
|
return null;
|
|
62105
62703
|
if (run.leaseGeneration !== expectedGeneration) {
|
|
62106
|
-
throw new StaleLeaseGenerationError(
|
|
62704
|
+
throw new StaleLeaseGenerationError(runId3, expectedGeneration, run.leaseGeneration, run.status);
|
|
62107
62705
|
}
|
|
62108
|
-
return this.patchRun(
|
|
62706
|
+
return this.patchRun(runId3, patch);
|
|
62109
62707
|
}
|
|
62110
|
-
async appendLog(
|
|
62111
|
-
const entries = this.logs.get(
|
|
62112
|
-
const log = { runId:
|
|
62708
|
+
async appendLog(runId3, orgId, level, message) {
|
|
62709
|
+
const entries = this.logs.get(runId3) ?? [];
|
|
62710
|
+
const log = { runId: runId3, sequence: entries.length + 1, level, message, createdAt: nowIso() };
|
|
62113
62711
|
entries.push(log);
|
|
62114
|
-
this.logs.set(
|
|
62712
|
+
this.logs.set(runId3, entries);
|
|
62115
62713
|
return log;
|
|
62116
62714
|
}
|
|
62117
|
-
async listLogs(principal,
|
|
62118
|
-
const run = await this.getRun(principal,
|
|
62119
|
-
return run ? [...this.logs.get(
|
|
62715
|
+
async listLogs(principal, runId3) {
|
|
62716
|
+
const run = await this.getRun(principal, runId3);
|
|
62717
|
+
return run ? [...this.logs.get(runId3) ?? []] : [];
|
|
62120
62718
|
}
|
|
62121
62719
|
async addArtifact(artifact) {
|
|
62122
62720
|
const next2 = { ...artifact, createdAt: nowIso() };
|
|
@@ -62125,12 +62723,12 @@ class MemorySkillsStore {
|
|
|
62125
62723
|
this.artifacts.set(artifact.runId, artifacts);
|
|
62126
62724
|
return next2;
|
|
62127
62725
|
}
|
|
62128
|
-
async listArtifacts(principal,
|
|
62129
|
-
const run = await this.getRun(principal,
|
|
62130
|
-
return run ? [...this.artifacts.get(
|
|
62726
|
+
async listArtifacts(principal, runId3) {
|
|
62727
|
+
const run = await this.getRun(principal, runId3);
|
|
62728
|
+
return run ? [...this.artifacts.get(runId3) ?? []] : [];
|
|
62131
62729
|
}
|
|
62132
|
-
async getArtifact(principal,
|
|
62133
|
-
const artifacts = await this.listArtifacts(principal,
|
|
62730
|
+
async getArtifact(principal, runId3, id2) {
|
|
62731
|
+
const artifacts = await this.listArtifacts(principal, runId3);
|
|
62134
62732
|
return artifacts.find((artifact) => artifact.id === id2) ?? null;
|
|
62135
62733
|
}
|
|
62136
62734
|
async publishSkill(input) {
|
|
@@ -62180,7 +62778,11 @@ class MemorySkillsStore {
|
|
|
62180
62778
|
createdAt: previous?.createdAt ?? now,
|
|
62181
62779
|
updatedAt: now,
|
|
62182
62780
|
revisionId: revisionIdOfRecord(recordFieldsOf(input, carriedBundle, carriedSkillMd)),
|
|
62183
|
-
revisionNumber: (previous?.revisionNumber ?? 0) + 1
|
|
62781
|
+
revisionNumber: (previous?.revisionNumber ?? 0) + 1,
|
|
62782
|
+
lifecycle: previous?.lifecycle ?? "active",
|
|
62783
|
+
...previous?.archivedAt ? { archivedAt: previous.archivedAt } : {},
|
|
62784
|
+
...previous?.archiveReason ? { archiveReason: previous.archiveReason } : {},
|
|
62785
|
+
...previous?.replacementSlug ? { replacementSlug: previous.replacementSlug } : {}
|
|
62184
62786
|
};
|
|
62185
62787
|
this.skills.set(key, record3);
|
|
62186
62788
|
if (input.version && versionSha && !this.versions.has(versionKey(input.principal.orgId, input.slug, input.version))) {
|
|
@@ -62216,6 +62818,33 @@ class MemorySkillsStore {
|
|
|
62216
62818
|
const skill = this.skills.get(skillKey(principal.orgId, slug));
|
|
62217
62819
|
return skill && skill.orgId === principal.orgId ? skill : null;
|
|
62218
62820
|
}
|
|
62821
|
+
async setSkillLifecycle(principal, slug, patch, expectedRevisionId) {
|
|
62822
|
+
const current = await this.getSkill(principal, slug);
|
|
62823
|
+
if (!current || current.tombstonedAt)
|
|
62824
|
+
return null;
|
|
62825
|
+
if (expectedRevisionId !== current.revisionId)
|
|
62826
|
+
throw new SkillRevisionConflictError(slug, expectedRevisionId, current.revisionId);
|
|
62827
|
+
if (patch.lifecycle === "archived" && current.lifecycle !== "archived") {
|
|
62828
|
+
const profiles = await this.selectionStore.profilesReferencingSkill(principal, slug);
|
|
62829
|
+
if (profiles.length)
|
|
62830
|
+
throw new SkillLifecycleConflictError(slug, profiles);
|
|
62831
|
+
}
|
|
62832
|
+
const next2 = {
|
|
62833
|
+
...current,
|
|
62834
|
+
lifecycle: patch.lifecycle,
|
|
62835
|
+
updatedAt: nowIso(),
|
|
62836
|
+
revisionId: revisionIdOfRecord({ ...current, lifecycle: patch.lifecycle, archiveReason: patch.reason, replacementSlug: patch.replacementSlug }),
|
|
62837
|
+
revisionNumber: current.revisionNumber + 1,
|
|
62838
|
+
...patch.lifecycle === "archived" ? { archivedAt: current.archivedAt ?? nowIso(), ...patch.reason ? { archiveReason: patch.reason } : {}, ...patch.replacementSlug ? { replacementSlug: patch.replacementSlug } : {} } : {}
|
|
62839
|
+
};
|
|
62840
|
+
if (patch.lifecycle === "active") {
|
|
62841
|
+
delete next2.archivedAt;
|
|
62842
|
+
delete next2.archiveReason;
|
|
62843
|
+
delete next2.replacementSlug;
|
|
62844
|
+
}
|
|
62845
|
+
this.skills.set(skillKey(principal.orgId, slug), next2);
|
|
62846
|
+
return next2;
|
|
62847
|
+
}
|
|
62219
62848
|
async updateSkill(principal, slug, patch, expectedRevisionId) {
|
|
62220
62849
|
const current = await this.getSkill(principal, slug);
|
|
62221
62850
|
if (!current || current.tombstonedAt)
|
|
@@ -62286,7 +62915,7 @@ class MemorySkillsStore {
|
|
|
62286
62915
|
await this.purgeExpiredTombstones(principal);
|
|
62287
62916
|
const tags = new Set;
|
|
62288
62917
|
for (const skill of this.skills.values()) {
|
|
62289
|
-
if (skill.orgId !== principal.orgId)
|
|
62918
|
+
if (skill.orgId !== principal.orgId || skill.lifecycle !== "active")
|
|
62290
62919
|
continue;
|
|
62291
62920
|
for (const tag of skill.tags) {
|
|
62292
62921
|
if (tag.trim())
|
|
@@ -62297,13 +62926,13 @@ class MemorySkillsStore {
|
|
|
62297
62926
|
}
|
|
62298
62927
|
async listSkillsByTag(principal, tag) {
|
|
62299
62928
|
await this.purgeExpiredTombstones(principal);
|
|
62300
|
-
return Array.from(this.skills.values()).filter((skill) => skill.orgId === principal.orgId && !skill.tombstonedAt && skill.tags.includes(tag)).sort((a4, b4) => a4.slug.localeCompare(b4.slug));
|
|
62929
|
+
return Array.from(this.skills.values()).filter((skill) => skill.orgId === principal.orgId && !skill.tombstonedAt && skill.lifecycle === "active" && skill.tags.includes(tag)).sort((a4, b4) => a4.slug.localeCompare(b4.slug));
|
|
62301
62930
|
}
|
|
62302
62931
|
async listPinsByTag(principal, tag) {
|
|
62303
62932
|
await this.purgeExpiredTombstones(principal);
|
|
62304
62933
|
const taggedSlugs = new Set;
|
|
62305
62934
|
for (const skill of this.skills.values()) {
|
|
62306
|
-
if (skill.orgId === principal.orgId && !skill.tombstonedAt && skill.tags.includes(tag))
|
|
62935
|
+
if (skill.orgId === principal.orgId && !skill.tombstonedAt && skill.lifecycle === "active" && skill.tags.includes(tag))
|
|
62307
62936
|
taggedSlugs.add(skill.slug);
|
|
62308
62937
|
}
|
|
62309
62938
|
return Array.from(this.pins.values()).filter((pin) => pin.orgId === principal.orgId && pin.principal === principal.apiKeyId && taggedSlugs.has(pin.slug)).sort((a4, b4) => a4.slug.localeCompare(b4.slug));
|
|
@@ -62316,12 +62945,12 @@ class MemorySkillsStore {
|
|
|
62316
62945
|
if (!referenced)
|
|
62317
62946
|
this.bundles.delete(skillKey(orgId, sha2562));
|
|
62318
62947
|
}
|
|
62319
|
-
patchRun(
|
|
62320
|
-
const run = this.runs.get(
|
|
62948
|
+
patchRun(runId3, patch) {
|
|
62949
|
+
const run = this.runs.get(runId3);
|
|
62321
62950
|
if (!run)
|
|
62322
62951
|
return null;
|
|
62323
62952
|
const next2 = { ...run, ...patch };
|
|
62324
|
-
this.runs.set(
|
|
62953
|
+
this.runs.set(runId3, next2);
|
|
62325
62954
|
return next2;
|
|
62326
62955
|
}
|
|
62327
62956
|
}
|
|
@@ -62394,7 +63023,7 @@ class PostgresSkillsStore {
|
|
|
62394
63023
|
ON CONFLICT (key_hash) DO NOTHING
|
|
62395
63024
|
`;
|
|
62396
63025
|
}
|
|
62397
|
-
async authenticateApiKeyHash(
|
|
63026
|
+
async authenticateApiKeyHash(hash3) {
|
|
62398
63027
|
const rows = await this.sql`
|
|
62399
63028
|
SELECT k.id AS api_key_id, k.scopes_json, o.id AS org_id, o.slug AS org_slug, o.name AS org_name,
|
|
62400
63029
|
u.id AS user_id, u.email, m.role
|
|
@@ -62402,7 +63031,7 @@ class PostgresSkillsStore {
|
|
|
62402
63031
|
JOIN organizations o ON o.id = k.org_id
|
|
62403
63032
|
JOIN users u ON u.id = k.user_id
|
|
62404
63033
|
LEFT JOIN organization_members m ON m.org_id = k.org_id AND m.user_id = k.user_id
|
|
62405
|
-
WHERE k.key_hash = ${
|
|
63034
|
+
WHERE k.key_hash = ${hash3} AND k.revoked_at IS NULL
|
|
62406
63035
|
LIMIT 1
|
|
62407
63036
|
`;
|
|
62408
63037
|
const row = rows[0];
|
|
@@ -62420,6 +63049,38 @@ class PostgresSkillsStore {
|
|
|
62420
63049
|
scopes: parseJsonArray(row.scopes_json)
|
|
62421
63050
|
};
|
|
62422
63051
|
}
|
|
63052
|
+
async updateApiKeyScopes(actor, keyId, expectedScopes, addScopes) {
|
|
63053
|
+
return this.sql.begin(async (tx) => {
|
|
63054
|
+
const rows = await tx`
|
|
63055
|
+
SELECT scopes_json FROM api_keys
|
|
63056
|
+
WHERE id = ${keyId} AND org_id = ${actor.orgId} AND revoked_at IS NULL
|
|
63057
|
+
LIMIT 1
|
|
63058
|
+
`;
|
|
63059
|
+
const row = rows[0];
|
|
63060
|
+
if (!row)
|
|
63061
|
+
return { kind: "not_found" };
|
|
63062
|
+
const current = parseJsonArray(row.scopes_json);
|
|
63063
|
+
if (current.length !== expectedScopes.length || current.some((scope2, index) => scope2 !== expectedScopes[index])) {
|
|
63064
|
+
return { kind: "stale", scopes: current };
|
|
63065
|
+
}
|
|
63066
|
+
const scopes = [...current, ...addScopes.filter((scope2) => !current.includes(scope2))];
|
|
63067
|
+
const updated = await tx`
|
|
63068
|
+
UPDATE api_keys SET scopes_json = ${JSON.stringify(scopes)}::jsonb
|
|
63069
|
+
WHERE id = ${keyId} AND org_id = ${actor.orgId} AND revoked_at IS NULL
|
|
63070
|
+
AND scopes_json = ${JSON.stringify(expectedScopes)}::jsonb
|
|
63071
|
+
RETURNING scopes_json
|
|
63072
|
+
`;
|
|
63073
|
+
if (!updated[0]) {
|
|
63074
|
+
const latest = await tx`SELECT scopes_json FROM api_keys WHERE id = ${keyId} AND org_id = ${actor.orgId} AND revoked_at IS NULL LIMIT 1`;
|
|
63075
|
+
return { kind: "stale", scopes: latest[0] ? parseJsonArray(latest[0].scopes_json) : current };
|
|
63076
|
+
}
|
|
63077
|
+
await tx`
|
|
63078
|
+
INSERT INTO skills_audit_events (org_id, user_id, api_key_id, action, target_type, target_id, metadata_json)
|
|
63079
|
+
VALUES (${actor.orgId}, ${actor.userId}, ${actor.apiKeyId}, ${"api_key_scopes_added"}, ${"api_key"}, ${keyId}, ${JSON.stringify({ added: addScopes, scopes })}::jsonb)
|
|
63080
|
+
`;
|
|
63081
|
+
return { kind: "updated", scopes };
|
|
63082
|
+
});
|
|
63083
|
+
}
|
|
62423
63084
|
async withContext(orgId, worker, fn) {
|
|
62424
63085
|
return this.sql.begin(async (tx) => {
|
|
62425
63086
|
await tx`SELECT set_config('app.skills_org_id', ${orgId ?? ""}, true)`;
|
|
@@ -62438,7 +63099,7 @@ class PostgresSkillsStore {
|
|
|
62438
63099
|
if (existing[0])
|
|
62439
63100
|
return rowToRun(existing[0]);
|
|
62440
63101
|
}
|
|
62441
|
-
const id2 =
|
|
63102
|
+
const id2 = runId2();
|
|
62442
63103
|
const rows = await tx`
|
|
62443
63104
|
INSERT INTO skills_runs (id, org_id, user_id, skill_slug, requested_slug, status, input_json, args_json, idempotency_key, correlation_id)
|
|
62444
63105
|
VALUES (${id2}, ${input.principal.orgId}, ${input.principal.userId}, ${input.slug}, ${input.slug}, ${"queued"}, ${JSON.stringify(input.input)}::jsonb, ${JSON.stringify(input.args)}::jsonb, ${input.idempotencyKey ?? null}, ${randomUUID9()})
|
|
@@ -62456,10 +63117,10 @@ class PostgresSkillsStore {
|
|
|
62456
63117
|
return rows.map(rowToRun);
|
|
62457
63118
|
});
|
|
62458
63119
|
}
|
|
62459
|
-
async getRun(principal,
|
|
63120
|
+
async getRun(principal, runId3) {
|
|
62460
63121
|
return this.withContext(principal.orgId, false, async (tx) => {
|
|
62461
63122
|
const rows = await tx`
|
|
62462
|
-
SELECT * FROM skills_runs WHERE id = ${
|
|
63123
|
+
SELECT * FROM skills_runs WHERE id = ${runId3} AND org_id = ${principal.orgId} LIMIT 1
|
|
62463
63124
|
`;
|
|
62464
63125
|
return rows[0] ? rowToRun(rows[0]) : null;
|
|
62465
63126
|
});
|
|
@@ -62482,9 +63143,9 @@ class PostgresSkillsStore {
|
|
|
62482
63143
|
return rows[0] ? rowToRun(rows[0]) : null;
|
|
62483
63144
|
});
|
|
62484
63145
|
}
|
|
62485
|
-
async updateRun(
|
|
63146
|
+
async updateRun(runId3, patch) {
|
|
62486
63147
|
return this.withContext(null, true, async (tx) => {
|
|
62487
|
-
const current = await tx`SELECT * FROM skills_runs WHERE id = ${
|
|
63148
|
+
const current = await tx`SELECT * FROM skills_runs WHERE id = ${runId3} LIMIT 1`;
|
|
62488
63149
|
if (!current[0])
|
|
62489
63150
|
return null;
|
|
62490
63151
|
const run = { ...rowToRun(current[0]), ...patch };
|
|
@@ -62497,20 +63158,20 @@ class PostgresSkillsStore {
|
|
|
62497
63158
|
error_message = ${run.errorMessage ?? null},
|
|
62498
63159
|
started_at = ${run.startedAt ?? null},
|
|
62499
63160
|
completed_at = ${run.completedAt ?? null}
|
|
62500
|
-
WHERE id = ${
|
|
63161
|
+
WHERE id = ${runId3}
|
|
62501
63162
|
RETURNING *
|
|
62502
63163
|
`;
|
|
62503
63164
|
return rows[0] ? rowToRun(rows[0]) : null;
|
|
62504
63165
|
});
|
|
62505
63166
|
}
|
|
62506
|
-
async transitionRun(
|
|
63167
|
+
async transitionRun(runId3, patch, expectedGeneration) {
|
|
62507
63168
|
return this.withContext(null, true, async (tx) => {
|
|
62508
|
-
const current = await tx`SELECT * FROM skills_runs WHERE id = ${
|
|
63169
|
+
const current = await tx`SELECT * FROM skills_runs WHERE id = ${runId3} LIMIT 1`;
|
|
62509
63170
|
if (!current[0])
|
|
62510
63171
|
return null;
|
|
62511
63172
|
const stored = rowToRun(current[0]);
|
|
62512
63173
|
if (stored.leaseGeneration !== expectedGeneration) {
|
|
62513
|
-
throw new StaleLeaseGenerationError(
|
|
63174
|
+
throw new StaleLeaseGenerationError(runId3, expectedGeneration, stored.leaseGeneration, stored.status);
|
|
62514
63175
|
}
|
|
62515
63176
|
const run = { ...stored, ...patch };
|
|
62516
63177
|
const rows = await tx`
|
|
@@ -62523,13 +63184,13 @@ class PostgresSkillsStore {
|
|
|
62523
63184
|
started_at = ${run.startedAt ?? null},
|
|
62524
63185
|
completed_at = ${run.completedAt ?? null},
|
|
62525
63186
|
lease_generation = ${run.leaseGeneration}
|
|
62526
|
-
WHERE id = ${
|
|
63187
|
+
WHERE id = ${runId3} AND lease_generation = ${expectedGeneration}
|
|
62527
63188
|
RETURNING *
|
|
62528
63189
|
`;
|
|
62529
63190
|
return rows[0] ? rowToRun(rows[0]) : null;
|
|
62530
63191
|
});
|
|
62531
63192
|
}
|
|
62532
|
-
async appendLog(
|
|
63193
|
+
async appendLog(runId3, orgId, level, message) {
|
|
62533
63194
|
let lastError;
|
|
62534
63195
|
for (let attempt = 0;attempt < LOG_SEQUENCE_ATTEMPTS; attempt += 1) {
|
|
62535
63196
|
try {
|
|
@@ -62537,9 +63198,9 @@ class PostgresSkillsStore {
|
|
|
62537
63198
|
return await tx`
|
|
62538
63199
|
INSERT INTO skills_run_logs (run_id, org_id, sequence, level, message)
|
|
62539
63200
|
VALUES (
|
|
62540
|
-
${
|
|
63201
|
+
${runId3},
|
|
62541
63202
|
${orgId},
|
|
62542
|
-
(SELECT COALESCE(MAX(sequence), 0) + 1 FROM skills_run_logs WHERE run_id = ${
|
|
63203
|
+
(SELECT COALESCE(MAX(sequence), 0) + 1 FROM skills_run_logs WHERE run_id = ${runId3}),
|
|
62543
63204
|
${level},
|
|
62544
63205
|
${message}
|
|
62545
63206
|
)
|
|
@@ -62555,12 +63216,12 @@ class PostgresSkillsStore {
|
|
|
62555
63216
|
}
|
|
62556
63217
|
throw lastError;
|
|
62557
63218
|
}
|
|
62558
|
-
async listLogs(principal,
|
|
63219
|
+
async listLogs(principal, runId3) {
|
|
62559
63220
|
return this.withContext(principal.orgId, false, async (tx) => {
|
|
62560
63221
|
const rows = await tx`
|
|
62561
63222
|
SELECT l.* FROM skills_run_logs l
|
|
62562
63223
|
JOIN skills_runs r ON r.id = l.run_id AND r.org_id = ${principal.orgId}
|
|
62563
|
-
WHERE l.run_id = ${
|
|
63224
|
+
WHERE l.run_id = ${runId3}
|
|
62564
63225
|
ORDER BY l.sequence ASC
|
|
62565
63226
|
`;
|
|
62566
63227
|
return rows.map(rowToLog);
|
|
@@ -62576,23 +63237,23 @@ class PostgresSkillsStore {
|
|
|
62576
63237
|
return rowToArtifact(rows[0]);
|
|
62577
63238
|
});
|
|
62578
63239
|
}
|
|
62579
|
-
async listArtifacts(principal,
|
|
63240
|
+
async listArtifacts(principal, runId3) {
|
|
62580
63241
|
return this.withContext(principal.orgId, false, async (tx) => {
|
|
62581
63242
|
const rows = await tx`
|
|
62582
63243
|
SELECT a.* FROM skills_artifacts a
|
|
62583
63244
|
JOIN skills_runs r ON r.id = a.run_id AND r.org_id = ${principal.orgId}
|
|
62584
|
-
WHERE a.run_id = ${
|
|
63245
|
+
WHERE a.run_id = ${runId3}
|
|
62585
63246
|
ORDER BY a.created_at ASC
|
|
62586
63247
|
`;
|
|
62587
63248
|
return rows.map(rowToArtifact);
|
|
62588
63249
|
});
|
|
62589
63250
|
}
|
|
62590
|
-
async getArtifact(principal,
|
|
63251
|
+
async getArtifact(principal, runId3, artifactId2) {
|
|
62591
63252
|
return this.withContext(principal.orgId, false, async (tx) => {
|
|
62592
63253
|
const rows = await tx`
|
|
62593
63254
|
SELECT a.* FROM skills_artifacts a
|
|
62594
63255
|
JOIN skills_runs r ON r.id = a.run_id AND r.org_id = ${principal.orgId}
|
|
62595
|
-
WHERE a.run_id = ${
|
|
63256
|
+
WHERE a.run_id = ${runId3} AND a.id = ${artifactId2}
|
|
62596
63257
|
LIMIT 1
|
|
62597
63258
|
`;
|
|
62598
63259
|
return rows[0] ? rowToArtifact(rows[0]) : null;
|
|
@@ -62740,6 +63401,42 @@ class PostgresSkillsStore {
|
|
|
62740
63401
|
const rows = await this.sql`SELECT * FROM skills_registry WHERE org_id = ${principal.orgId} AND slug = ${slug} LIMIT 1`;
|
|
62741
63402
|
return rows[0] ? rowToSkill(rows[0]) : null;
|
|
62742
63403
|
}
|
|
63404
|
+
async setSkillLifecycle(principal, slug, patch, expectedRevisionId) {
|
|
63405
|
+
return this.sql.begin(async (tx) => {
|
|
63406
|
+
const currentRows = await tx`SELECT * FROM skills_registry WHERE org_id=${principal.orgId} AND slug=${slug} LIMIT 1 FOR UPDATE`;
|
|
63407
|
+
const current = currentRows[0] ? rowToSkill(currentRows[0]) : null;
|
|
63408
|
+
if (!current || current.tombstonedAt)
|
|
63409
|
+
return null;
|
|
63410
|
+
if (expectedRevisionId !== current.revisionId)
|
|
63411
|
+
throw new SkillRevisionConflictError(slug, expectedRevisionId, current.revisionId);
|
|
63412
|
+
if (patch.lifecycle === "archived" && current.lifecycle !== "archived") {
|
|
63413
|
+
const profiles = await tx`SELECT profile_id,selections_json FROM skills_profiles WHERE org_id=${principal.orgId}`;
|
|
63414
|
+
const referencing = profiles.filter((profile2) => {
|
|
63415
|
+
const selections2 = typeof profile2.selections_json === "string" ? JSON.parse(profile2.selections_json) : profile2.selections_json;
|
|
63416
|
+
return Array.isArray(selections2) && selections2.some((selection2) => selection2?.slug === slug);
|
|
63417
|
+
}).map((profile2) => String(profile2.profile_id)).sort();
|
|
63418
|
+
if (referencing.length)
|
|
63419
|
+
throw new SkillLifecycleConflictError(slug, referencing);
|
|
63420
|
+
}
|
|
63421
|
+
const next2 = {
|
|
63422
|
+
...current,
|
|
63423
|
+
lifecycle: patch.lifecycle,
|
|
63424
|
+
updatedAt: nowIso(),
|
|
63425
|
+
revisionId: revisionIdOfRecord({ ...current, lifecycle: patch.lifecycle, archiveReason: patch.reason, replacementSlug: patch.replacementSlug }),
|
|
63426
|
+
revisionNumber: current.revisionNumber + 1,
|
|
63427
|
+
...patch.lifecycle === "archived" ? { archivedAt: current.archivedAt ?? nowIso(), ...patch.reason ? { archiveReason: patch.reason } : {}, ...patch.replacementSlug ? { replacementSlug: patch.replacementSlug } : {} } : {}
|
|
63428
|
+
};
|
|
63429
|
+
if (patch.lifecycle === "active") {
|
|
63430
|
+
delete next2.archivedAt;
|
|
63431
|
+
delete next2.archiveReason;
|
|
63432
|
+
delete next2.replacementSlug;
|
|
63433
|
+
}
|
|
63434
|
+
const rows = await tx`UPDATE skills_registry SET lifecycle=${next2.lifecycle}, archived_at=${next2.archivedAt ?? null}, archive_reason=${next2.archiveReason ?? null}, replacement_slug=${next2.replacementSlug ?? null}, revision_id=${next2.revisionId}, revision_number=revision_number+1, updated_at=${next2.updatedAt} WHERE org_id=${principal.orgId} AND slug=${slug} AND tombstoned_at IS NULL AND revision_id=${current.revisionId} RETURNING *`;
|
|
63435
|
+
if (!rows[0])
|
|
63436
|
+
throw new SkillRevisionConflictError(slug, expectedRevisionId, (await this.getSkill(principal, slug))?.revisionId ?? null);
|
|
63437
|
+
return rowToSkill(rows[0]);
|
|
63438
|
+
});
|
|
63439
|
+
}
|
|
62743
63440
|
async updateSkill(principal, slug, patch, expectedRevisionId) {
|
|
62744
63441
|
const current = await this.getSkill(principal, slug);
|
|
62745
63442
|
if (!current || current.tombstonedAt)
|
|
@@ -62875,7 +63572,7 @@ class PostgresSkillsStore {
|
|
|
62875
63572
|
async listTags(principal) {
|
|
62876
63573
|
await this.purgeExpiredTombstones(principal);
|
|
62877
63574
|
const rows = await this.sql`
|
|
62878
|
-
SELECT DISTINCT tag FROM skills_tags WHERE org_id = ${principal.orgId} ORDER BY tag ASC
|
|
63575
|
+
SELECT DISTINCT t.tag FROM skills_tags t JOIN skills_registry s ON s.org_id = t.org_id AND s.slug = t.slug WHERE t.org_id = ${principal.orgId} AND s.lifecycle = 'active' ORDER BY t.tag ASC
|
|
62879
63576
|
`;
|
|
62880
63577
|
return rows.map((row) => String(row.tag));
|
|
62881
63578
|
}
|
|
@@ -62884,7 +63581,7 @@ class PostgresSkillsStore {
|
|
|
62884
63581
|
const rows = await this.sql`
|
|
62885
63582
|
SELECT s.* FROM skills_registry s
|
|
62886
63583
|
JOIN skills_tags t ON t.org_id = s.org_id AND t.slug = s.slug
|
|
62887
|
-
WHERE t.org_id = ${principal.orgId} AND t.tag = ${tag} AND s.tombstoned_at IS NULL
|
|
63584
|
+
WHERE t.org_id = ${principal.orgId} AND t.tag = ${tag} AND s.tombstoned_at IS NULL AND s.lifecycle = 'active'
|
|
62888
63585
|
ORDER BY s.slug ASC
|
|
62889
63586
|
`;
|
|
62890
63587
|
return rows.map(rowToSkill);
|
|
@@ -62896,7 +63593,7 @@ class PostgresSkillsStore {
|
|
|
62896
63593
|
JOIN skills_tags t ON t.org_id = p.org_id AND t.slug = p.slug
|
|
62897
63594
|
JOIN skills_registry s ON s.org_id = p.org_id AND s.slug = p.slug
|
|
62898
63595
|
WHERE p.org_id = ${principal.orgId} AND p.principal = ${principal.apiKeyId}
|
|
62899
|
-
AND t.tag = ${tag} AND s.tombstoned_at IS NULL
|
|
63596
|
+
AND t.tag = ${tag} AND s.tombstoned_at IS NULL AND s.lifecycle = 'active'
|
|
62900
63597
|
ORDER BY p.slug ASC
|
|
62901
63598
|
`;
|
|
62902
63599
|
return rows.map(rowToPin);
|
|
@@ -62993,7 +63690,7 @@ async function createSkillsFetchHandler(options = {}) {
|
|
|
62993
63690
|
if (request2.method === "GET" && url.pathname === "/api/v1/capabilities") {
|
|
62994
63691
|
const response = await handleProfileApi(store, principal, request2, ["capabilities"], config);
|
|
62995
63692
|
const payload = await response.json();
|
|
62996
|
-
return json4({ ...payload, cloudExecution: Boolean(runtime), capabilities: [...payload.capabilities, ...runtime ? ["skills.cloud-execution"] : []] });
|
|
63693
|
+
return json4({ ...payload, cloudExecution: Boolean(runtime), cloudExecutionContracts: runtime ? ["pdf.v1", "regex-test.v1"] : [], capabilities: [...payload.capabilities, ...runtime ? ["skills.cloud-execution"] : []] });
|
|
62997
63694
|
}
|
|
62998
63695
|
if (request2.method === "GET" && url.pathname === "/api/auth/whoami") {
|
|
62999
63696
|
return json4(identityPayload(principal));
|
|
@@ -63017,6 +63714,9 @@ async function createSkillsFetchHandler(options = {}) {
|
|
|
63017
63714
|
attemptedBundleSha256: error.attemptedSha256
|
|
63018
63715
|
}, { status: 409 });
|
|
63019
63716
|
}
|
|
63717
|
+
if (error instanceof SkillLifecycleConflictError) {
|
|
63718
|
+
return json4({ error: error.message, code: "SKILL_ARCHIVE_PROFILE_CONFLICT", profiles: error.profiles }, { status: 409 });
|
|
63719
|
+
}
|
|
63020
63720
|
if (error instanceof SkillRevisionConflictError) {
|
|
63021
63721
|
return json4({
|
|
63022
63722
|
error: error.message,
|
|
@@ -63055,6 +63755,32 @@ async function handleApiV1(store, governanceStore, principal, request2, parts, c
|
|
|
63055
63755
|
if (parts.some(segmentEscapesPath)) {
|
|
63056
63756
|
return json4({ error: "invalid path segment", code: "INVALID_PATH" }, { status: 400 });
|
|
63057
63757
|
}
|
|
63758
|
+
if (parts.length === 4 && resource === "admin" && id2 === "keys" && subresource && childId === "scopes" && request2.method === "PATCH") {
|
|
63759
|
+
const adminScope = principal.scopes.includes("*") || principal.scopes.includes("skills:keys.admin");
|
|
63760
|
+
if (!adminScope || !["owner", "admin"].includes(principal.role)) {
|
|
63761
|
+
return json4({ error: "Key scope administration requires an owner/admin key with skills:keys.admin", code: "KEY_ADMIN_REQUIRED" }, { status: 403 });
|
|
63762
|
+
}
|
|
63763
|
+
if (!/^[A-Za-z0-9_-]+$/.test(subresource)) {
|
|
63764
|
+
return json4({ error: "Key id must be url-safe (letters, digits, '_' or '-')", code: "INVALID_KEY_ID" }, { status: 400 });
|
|
63765
|
+
}
|
|
63766
|
+
const body4 = await readJson(request2, config.requestBodyLimitBytes);
|
|
63767
|
+
const expected = body4.expected_scopes;
|
|
63768
|
+
const additions = body4.add_scopes;
|
|
63769
|
+
if (!Array.isArray(expected) || !expected.every((scope2) => typeof scope2 === "string") || !Array.isArray(additions) || !additions.every((scope2) => typeof scope2 === "string")) {
|
|
63770
|
+
return json4({ error: "expected_scopes and add_scopes must be arrays of scope strings", code: "INVALID_KEY_SCOPE_UPDATE" }, { status: 400 });
|
|
63771
|
+
}
|
|
63772
|
+
if (additions.length !== 1 || additions[0] !== "skills:publish") {
|
|
63773
|
+
return json4({ error: "Only adding skills:publish to an existing Skills key is supported", code: "INVALID_KEY_SCOPE_UPDATE" }, { status: 400 });
|
|
63774
|
+
}
|
|
63775
|
+
const update = await store.updateApiKeyScopes?.(principal, subresource, expected, additions);
|
|
63776
|
+
if (!update)
|
|
63777
|
+
return json4({ error: "Key scope administration is unavailable", code: "KEY_SCOPE_UPDATE_UNAVAILABLE" }, { status: 501 });
|
|
63778
|
+
if (update.kind === "not_found")
|
|
63779
|
+
return json4({ error: "API key not found", code: "API_KEY_NOT_FOUND" }, { status: 404 });
|
|
63780
|
+
if (update.kind === "stale")
|
|
63781
|
+
return json4({ error: "API key scopes changed; re-read and retry with expected_scopes", code: "API_KEY_SCOPE_CONFLICT", keyId: subresource, scopes: update.scopes }, { status: 409 });
|
|
63782
|
+
return json4({ keyId: subresource, orgId: principal.orgId, scopes: update.scopes, updated: true });
|
|
63783
|
+
}
|
|
63058
63784
|
if (!permitsSkillsRoute(principal, request2.method, resource ?? "")) {
|
|
63059
63785
|
return json4({ error: "The API key does not allow this operation", code: "INSUFFICIENT_SCOPE" }, { status: 403 });
|
|
63060
63786
|
}
|
|
@@ -63088,6 +63814,13 @@ async function handleApiV1(store, governanceStore, principal, request2, parts, c
|
|
|
63088
63814
|
if (request2.method === "GET" && id2 && subresource === "versions" && !childId) {
|
|
63089
63815
|
return json4(await listSkillVersionsPayload(store, principal, id2));
|
|
63090
63816
|
}
|
|
63817
|
+
if (request2.method === "PATCH" && id2 && subresource === "lifecycle" && !childId) {
|
|
63818
|
+
if (!["owner", "admin"].includes(principal.role))
|
|
63819
|
+
return json4({ error: "Only organization owners or admins may change skill lifecycle", code: "LIFECYCLE_ROLE_REQUIRED" }, { status: 403 });
|
|
63820
|
+
const expectedRevisionId = parseIfMatch(request2.headers.get("if-match"));
|
|
63821
|
+
const updated = await store.setSkillLifecycle(principal, id2, skillLifecyclePatch(await readJson(request2, config.requestBodyLimitBytes)), expectedRevisionId);
|
|
63822
|
+
return updated ? json4(publishedPayload(updated), { headers: { ETag: revisionEtag(updated.revisionId) } }) : json4({ error: "published skill not found", code: "SKILL_NOT_FOUND" }, { status: 404 });
|
|
63823
|
+
}
|
|
63091
63824
|
if (request2.method === "GET" && id2 && subresource === "versions" && childId && !parts[4]) {
|
|
63092
63825
|
const version2 = await readSkillVersion(store, principal, id2, childId);
|
|
63093
63826
|
const current = (await store.getSkill(principal, id2))?.bundleSha256;
|
|
@@ -63246,8 +63979,11 @@ async function handleApiV1(store, governanceStore, principal, request2, parts, c
|
|
|
63246
63979
|
return json4({ error: "not found", code: "NOT_FOUND" }, { status: 404 });
|
|
63247
63980
|
}
|
|
63248
63981
|
function identityPayload(principal) {
|
|
63982
|
+
const safeKeyId = /^[A-Za-z0-9_-]{1,256}$/.test(principal.apiKeyId) ? principal.apiKeyId : undefined;
|
|
63983
|
+
const safeScopes = principal.scopes.length <= 32 && principal.scopes.every((scope2) => scope2.length <= 128 && /^(?:\*|[a-z][a-z0-9_-]*:(?:\*|[a-z][a-z0-9_-]*))$/.test(scope2)) ? [...new Set(principal.scopes)] : undefined;
|
|
63249
63984
|
return {
|
|
63250
63985
|
user: { id: principal.userId, email: principal.email, role: principal.role },
|
|
63986
|
+
...safeKeyId && safeScopes ? { apiKey: { id: safeKeyId, scopes: safeScopes } } : {},
|
|
63251
63987
|
organization: { id: principal.orgId, slug: principal.orgSlug, name: principal.orgName }
|
|
63252
63988
|
};
|
|
63253
63989
|
}
|
|
@@ -63503,14 +64239,14 @@ var util;
|
|
|
63503
64239
|
return obj[e5];
|
|
63504
64240
|
});
|
|
63505
64241
|
};
|
|
63506
|
-
util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (
|
|
63507
|
-
const
|
|
63508
|
-
for (const key in
|
|
63509
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
63510
|
-
|
|
64242
|
+
util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object6) => {
|
|
64243
|
+
const keys3 = [];
|
|
64244
|
+
for (const key in object6) {
|
|
64245
|
+
if (Object.prototype.hasOwnProperty.call(object6, key)) {
|
|
64246
|
+
keys3.push(key);
|
|
63511
64247
|
}
|
|
63512
64248
|
}
|
|
63513
|
-
return
|
|
64249
|
+
return keys3;
|
|
63514
64250
|
};
|
|
63515
64251
|
util2.find = (arr, checker) => {
|
|
63516
64252
|
for (const item of arr) {
|
|
@@ -65760,8 +66496,8 @@ class ZodObject extends ZodType {
|
|
|
65760
66496
|
if (this._cached !== null)
|
|
65761
66497
|
return this._cached;
|
|
65762
66498
|
const shape = this._def.shape();
|
|
65763
|
-
const
|
|
65764
|
-
this._cached = { shape, keys:
|
|
66499
|
+
const keys3 = util.objectKeys(shape);
|
|
66500
|
+
this._cached = { shape, keys: keys3 };
|
|
65765
66501
|
return this._cached;
|
|
65766
66502
|
}
|
|
65767
66503
|
_parse(input) {
|
|
@@ -67435,8 +68171,8 @@ function createRunService({ store, governance }) {
|
|
|
67435
68171
|
return run;
|
|
67436
68172
|
},
|
|
67437
68173
|
leaseNext: (workerId) => store.claimNextRun({ workerId }),
|
|
67438
|
-
transition: (
|
|
67439
|
-
get: (principal,
|
|
68174
|
+
transition: (runId3, patch) => store.updateRun(runId3, patch),
|
|
68175
|
+
get: (principal, runId3) => store.getRun(principal, runId3)
|
|
67440
68176
|
};
|
|
67441
68177
|
}
|
|
67442
68178
|
async function settleRun(store, options, run, actualCents) {
|
|
@@ -67481,7 +68217,7 @@ var localRunExecutor = {
|
|
|
67481
68217
|
// src/sdk/storage.ts
|
|
67482
68218
|
var artifactStorageSeam = ArtifactStorage;
|
|
67483
68219
|
// src/sdk/outputs.ts
|
|
67484
|
-
import { createHash as
|
|
68220
|
+
import { createHash as createHash27 } from "crypto";
|
|
67485
68221
|
class DatabaseOnlyObjectStore {
|
|
67486
68222
|
usesS3 = false;
|
|
67487
68223
|
async materialize(run, artifact, body4) {
|
|
@@ -67491,8 +68227,8 @@ class DatabaseOnlyObjectStore {
|
|
|
67491
68227
|
return null;
|
|
67492
68228
|
}
|
|
67493
68229
|
async deleteBundle() {}
|
|
67494
|
-
quarantineKeyFor(tenantId,
|
|
67495
|
-
return `${tenantId}/${
|
|
68230
|
+
quarantineKeyFor(tenantId, runId3, artifactId2) {
|
|
68231
|
+
return `${tenantId}/${runId3}/quarantine/${artifactId2}`;
|
|
67496
68232
|
}
|
|
67497
68233
|
}
|
|
67498
68234
|
function redactRunOutput(value, patterns = DEFAULT_OUTPUT_GOVERNANCE.redactPatterns) {
|
|
@@ -67528,7 +68264,7 @@ function createGovernedArtifactWriter(options) {
|
|
|
67528
68264
|
const stamped = {
|
|
67529
68265
|
...meta,
|
|
67530
68266
|
byteSize: outputBytes,
|
|
67531
|
-
sha256:
|
|
68267
|
+
sha256: createHash27("sha256").update(persistedBytes).digest("hex"),
|
|
67532
68268
|
visibility: options.visibility ?? config.defaultVisibility,
|
|
67533
68269
|
...config.artifactTtlSeconds !== undefined ? { expiresAt: expiresAtFor(createdAt, config.artifactTtlSeconds) } : {}
|
|
67534
68270
|
};
|
|
@@ -67674,13 +68410,13 @@ function createSpendService(options) {
|
|
|
67674
68410
|
exhaust("monthly", `org spend for ${monthPrefix} is ${monthly} cents and this run estimates ${estimated}; monthly ceiling is ${ceilings.monthlyTotalCents} cents`);
|
|
67675
68411
|
}
|
|
67676
68412
|
},
|
|
67677
|
-
async reserve(tenantId,
|
|
68413
|
+
async reserve(tenantId, runId3, estimatedCents) {
|
|
67678
68414
|
assertReservationCents(estimatedCents, "estimatedCents");
|
|
67679
|
-
return store.createReservation({ orgId: tenantId, runId:
|
|
68415
|
+
return store.createReservation({ orgId: tenantId, runId: runId3, estimatedCents });
|
|
67680
68416
|
},
|
|
67681
|
-
async reconcile(tenantId,
|
|
68417
|
+
async reconcile(tenantId, runId3, actualCents) {
|
|
67682
68418
|
assertReservationCents(actualCents, "actualCents");
|
|
67683
|
-
const reservations = await store.reservationsForRun(tenantId,
|
|
68419
|
+
const reservations = await store.reservationsForRun(tenantId, runId3);
|
|
67684
68420
|
const open = reservations.find((reservation) => reservation.status === "reserved");
|
|
67685
68421
|
if (!open)
|
|
67686
68422
|
return null;
|
|
@@ -67773,7 +68509,7 @@ __export(exports_external2, {
|
|
|
67773
68509
|
overwrite: () => _overwrite,
|
|
67774
68510
|
output: () => output,
|
|
67775
68511
|
optional: () => optional,
|
|
67776
|
-
object: () =>
|
|
68512
|
+
object: () => object6,
|
|
67777
68513
|
number: () => number2,
|
|
67778
68514
|
nullish: () => nullish2,
|
|
67779
68515
|
nullable: () => nullable,
|
|
@@ -67824,7 +68560,7 @@ __export(exports_external2, {
|
|
|
67824
68560
|
httpUrl: () => httpUrl,
|
|
67825
68561
|
hostname: () => hostname2,
|
|
67826
68562
|
hex: () => hex2,
|
|
67827
|
-
hash: () =>
|
|
68563
|
+
hash: () => hash3,
|
|
67828
68564
|
guid: () => guid2,
|
|
67829
68565
|
gte: () => _gte,
|
|
67830
68566
|
gt: () => _gt,
|
|
@@ -68403,9 +69139,9 @@ function floatSafeRemainder2(val, step) {
|
|
|
68403
69139
|
return ratio - roundedRatio;
|
|
68404
69140
|
}
|
|
68405
69141
|
var EVALUATING = /* @__PURE__ */ Symbol("evaluating");
|
|
68406
|
-
function defineLazy(
|
|
69142
|
+
function defineLazy(object6, key, getter) {
|
|
68407
69143
|
let value = undefined;
|
|
68408
|
-
Object.defineProperty(
|
|
69144
|
+
Object.defineProperty(object6, key, {
|
|
68409
69145
|
get() {
|
|
68410
69146
|
if (value === EVALUATING) {
|
|
68411
69147
|
return;
|
|
@@ -68417,7 +69153,7 @@ function defineLazy(object5, key, getter) {
|
|
|
68417
69153
|
return value;
|
|
68418
69154
|
},
|
|
68419
69155
|
set(v2) {
|
|
68420
|
-
Object.defineProperty(
|
|
69156
|
+
Object.defineProperty(object6, key, {
|
|
68421
69157
|
value: v2
|
|
68422
69158
|
});
|
|
68423
69159
|
},
|
|
@@ -68452,12 +69188,12 @@ function getElementAtPath(obj, path2) {
|
|
|
68452
69188
|
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
68453
69189
|
}
|
|
68454
69190
|
function promiseAllObject(promisesObj) {
|
|
68455
|
-
const
|
|
68456
|
-
const promises =
|
|
69191
|
+
const keys3 = Object.keys(promisesObj);
|
|
69192
|
+
const promises = keys3.map((key) => promisesObj[key]);
|
|
68457
69193
|
return Promise.all(promises).then((results) => {
|
|
68458
69194
|
const resolvedObj = {};
|
|
68459
|
-
for (let i4 = 0;i4 <
|
|
68460
|
-
resolvedObj[
|
|
69195
|
+
for (let i4 = 0;i4 < keys3.length; i4++) {
|
|
69196
|
+
resolvedObj[keys3[i4]] = results[i4];
|
|
68461
69197
|
}
|
|
68462
69198
|
return resolvedObj;
|
|
68463
69199
|
});
|
|
@@ -71087,10 +71823,10 @@ function handlePropertyResult(result, final, key, input, optin, optout) {
|
|
|
71087
71823
|
}
|
|
71088
71824
|
var NO_SYMBOL_KEYS = [];
|
|
71089
71825
|
function normalizeDef(def) {
|
|
71090
|
-
const
|
|
71826
|
+
const keys3 = Object.keys(def.shape);
|
|
71091
71827
|
const ownSymbols = Object.getOwnPropertySymbols(def.shape);
|
|
71092
71828
|
const symbolKeys = ownSymbols.length ? ownSymbols : NO_SYMBOL_KEYS;
|
|
71093
|
-
const allKeys = symbolKeys.length ? [...
|
|
71829
|
+
const allKeys = symbolKeys.length ? [...keys3, ...symbolKeys] : keys3;
|
|
71094
71830
|
for (const k4 of allKeys) {
|
|
71095
71831
|
if (!def.shape?.[k4]?._zod?.traits?.has("$ZodType")) {
|
|
71096
71832
|
throw new Error(`Invalid element at key "${String(k4)}": expected a Zod schema`);
|
|
@@ -71101,8 +71837,8 @@ function normalizeDef(def) {
|
|
|
71101
71837
|
...def,
|
|
71102
71838
|
allKeys,
|
|
71103
71839
|
symbolKeys,
|
|
71104
|
-
keySet: new Set(
|
|
71105
|
-
numKeys:
|
|
71840
|
+
keySet: new Set(keys3),
|
|
71841
|
+
numKeys: keys3.length,
|
|
71106
71842
|
optionalKeys: new Set(okeys)
|
|
71107
71843
|
};
|
|
71108
71844
|
}
|
|
@@ -71619,19 +72355,19 @@ function handleIntersectionResults(result, left, right) {
|
|
|
71619
72355
|
let unrecIssue;
|
|
71620
72356
|
const keyIssues = new Map;
|
|
71621
72357
|
const collect = (iss, side) => {
|
|
71622
|
-
let
|
|
72358
|
+
let keys3;
|
|
71623
72359
|
if (iss.code === "unrecognized_keys" && !iss.path?.length) {
|
|
71624
72360
|
unrecIssue ?? (unrecIssue = iss);
|
|
71625
|
-
|
|
72361
|
+
keys3 = iss.keys;
|
|
71626
72362
|
} else if (iss.code === "invalid_key" && iss.origin === "record" && iss.path?.length === 1) {
|
|
71627
72363
|
const k4 = String(iss.path[0]);
|
|
71628
72364
|
if (!keyIssues.has(k4))
|
|
71629
72365
|
keyIssues.set(k4, iss);
|
|
71630
|
-
|
|
72366
|
+
keys3 = [k4];
|
|
71631
72367
|
} else {
|
|
71632
72368
|
return false;
|
|
71633
72369
|
}
|
|
71634
|
-
for (const k4 of
|
|
72370
|
+
for (const k4 of keys3) {
|
|
71635
72371
|
if (!unrecKeys.has(k4))
|
|
71636
72372
|
unrecKeys.set(k4, {});
|
|
71637
72373
|
unrecKeys.get(k4)[side] = true;
|
|
@@ -80835,13 +81571,13 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
80835
81571
|
const def = schema._zod.def;
|
|
80836
81572
|
doc.write(`if (typeof ${accessor} !== "object" || ${accessor} === null || Array.isArray(${accessor})) return INVALID;`);
|
|
80837
81573
|
const shape = def.shape;
|
|
80838
|
-
const
|
|
81574
|
+
const keys3 = Object.keys(shape);
|
|
80839
81575
|
const symbolKeys = Object.getOwnPropertySymbols(shape);
|
|
80840
|
-
const allKeys = symbolKeys.length ? [...
|
|
81576
|
+
const allKeys = symbolKeys.length ? [...keys3, ...symbolKeys] : keys3;
|
|
80841
81577
|
const keyExpr = (k4) => typeof k4 === "symbol" ? addConstant(ctx, k4) : esc(k4);
|
|
80842
81578
|
const propKey = (k4) => typeof k4 === "symbol" ? `[${keyExpr(k4)}]` : esc(k4);
|
|
80843
81579
|
const propShape = shape;
|
|
80844
|
-
if (
|
|
81580
|
+
if (keys3.includes("__proto__")) {
|
|
80845
81581
|
throw new ZodCompileUnsupportedError('object shape key "__proto__"');
|
|
80846
81582
|
}
|
|
80847
81583
|
const propOutputs = new Map;
|
|
@@ -80883,7 +81619,7 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
80883
81619
|
if (catchall) {
|
|
80884
81620
|
const catchallType = catchall._zod.def.type;
|
|
80885
81621
|
if (catchallType === "never") {
|
|
80886
|
-
const condition =
|
|
81622
|
+
const condition = keys3.map((k4) => `k !== ${esc(k4)}`).join(" && ") || "true";
|
|
80887
81623
|
doc.write(`for (const k in ${accessor}) {`);
|
|
80888
81624
|
doc.indented((d4) => {
|
|
80889
81625
|
d4.write(`if (${condition}) return INVALID;`);
|
|
@@ -80899,7 +81635,7 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
80899
81635
|
const hasConditionalKeys = allKeys.some((k4) => mayOutputUndefined(propShape[k4]) || dropsWhenAbsent(propShape[k4]));
|
|
80900
81636
|
if (!buildsValue) {
|
|
80901
81637
|
if (unknownKeysMode === "schema") {
|
|
80902
|
-
const knownSet =
|
|
81638
|
+
const knownSet = keys3.length > 0 ? addConstant(ctx, new Set(keys3)) : null;
|
|
80903
81639
|
doc.write(`for (const k in ${accessor}) {`);
|
|
80904
81640
|
doc.indented((d4) => {
|
|
80905
81641
|
d4.write(`if (k === "__proto__") continue;`);
|
|
@@ -80931,7 +81667,7 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
80931
81667
|
}
|
|
80932
81668
|
}
|
|
80933
81669
|
if (unknownKeysMode !== "none") {
|
|
80934
|
-
const knownSet =
|
|
81670
|
+
const knownSet = keys3.length > 0 ? addConstant(ctx, new Set(keys3)) : null;
|
|
80935
81671
|
doc.write(`for (const k in ${accessor}) {`);
|
|
80936
81672
|
doc.indented((d4) => {
|
|
80937
81673
|
d4.write(`if (k === "__proto__") continue;`);
|
|
@@ -82794,8 +83530,8 @@ function compactTypeUnion(schema) {
|
|
|
82794
83530
|
if (!option || typeof option !== "object")
|
|
82795
83531
|
return;
|
|
82796
83532
|
compactTypeUnion(option);
|
|
82797
|
-
const
|
|
82798
|
-
if (
|
|
83533
|
+
const keys3 = Object.keys(option);
|
|
83534
|
+
if (keys3.length !== 1 || keys3[0] !== "type")
|
|
82799
83535
|
return;
|
|
82800
83536
|
const type = option.type;
|
|
82801
83537
|
for (const member of Array.isArray(type) ? type : [type]) {
|
|
@@ -82829,8 +83565,8 @@ function foldObjects(members2) {
|
|
|
82829
83565
|
}
|
|
82830
83566
|
const properties = {};
|
|
82831
83567
|
const required2 = new Set;
|
|
82832
|
-
for (const
|
|
82833
|
-
for (const key in
|
|
83568
|
+
for (const object6 of objects) {
|
|
83569
|
+
for (const key in object6.properties) {
|
|
82834
83570
|
if (Object.prototype.hasOwnProperty.call(properties, key))
|
|
82835
83571
|
continue;
|
|
82836
83572
|
const parts = [];
|
|
@@ -82844,18 +83580,18 @@ function foldObjects(members2) {
|
|
|
82844
83580
|
const merged = parts.length === 1 ? parts[0] : foldObjects(parts) ?? { allOf: parts };
|
|
82845
83581
|
assignProp(properties, key, merged);
|
|
82846
83582
|
}
|
|
82847
|
-
for (const key of
|
|
83583
|
+
for (const key of object6.required ?? [])
|
|
82848
83584
|
required2.add(key);
|
|
82849
83585
|
}
|
|
82850
83586
|
const folded = { type: "object", properties };
|
|
82851
83587
|
if (required2.size)
|
|
82852
83588
|
folded.required = [...required2];
|
|
82853
|
-
if (objects.every((
|
|
83589
|
+
if (objects.every((object6) => object6.additionalProperties === false)) {
|
|
82854
83590
|
folded.additionalProperties = false;
|
|
82855
83591
|
} else {
|
|
82856
83592
|
const constraints = [];
|
|
82857
|
-
for (const
|
|
82858
|
-
const constraint = undeclaredConstraint(
|
|
83593
|
+
for (const object6 of objects) {
|
|
83594
|
+
const constraint = undeclaredConstraint(object6);
|
|
82859
83595
|
if (constraint && !constraints.some((seen) => JSON.stringify(seen) === JSON.stringify(constraint)))
|
|
82860
83596
|
constraints.push(constraint);
|
|
82861
83597
|
}
|
|
@@ -83867,7 +84603,7 @@ __export(exports_schemas2, {
|
|
|
83867
84603
|
pipe: () => pipe,
|
|
83868
84604
|
partialRecord: () => partialRecord,
|
|
83869
84605
|
optional: () => optional,
|
|
83870
|
-
object: () =>
|
|
84606
|
+
object: () => object6,
|
|
83871
84607
|
number: () => number2,
|
|
83872
84608
|
nullish: () => nullish2,
|
|
83873
84609
|
nullable: () => nullable,
|
|
@@ -83899,7 +84635,7 @@ __export(exports_schemas2, {
|
|
|
83899
84635
|
httpUrl: () => httpUrl,
|
|
83900
84636
|
hostname: () => hostname2,
|
|
83901
84637
|
hex: () => hex2,
|
|
83902
|
-
hash: () =>
|
|
84638
|
+
hash: () => hash3,
|
|
83903
84639
|
guid: () => guid2,
|
|
83904
84640
|
function: () => _function,
|
|
83905
84641
|
float64: () => float64,
|
|
@@ -84613,7 +85349,7 @@ function hostname2(_params) {
|
|
|
84613
85349
|
function hex2(_params) {
|
|
84614
85350
|
return _stringFormat(ZodCustomStringFormat, "hex", exports_regexes.hex, _params);
|
|
84615
85351
|
}
|
|
84616
|
-
function
|
|
85352
|
+
function hash3(alg, params) {
|
|
84617
85353
|
const enc = params?.enc ?? "hex";
|
|
84618
85354
|
const format = `${alg}_${enc}`;
|
|
84619
85355
|
const regex = exports_regexes[format];
|
|
@@ -84913,7 +85649,7 @@ var ZodObject2 = /* @__PURE__ */ $constructor("ZodObject", (inst, def) => {
|
|
|
84913
85649
|
return exports_util.required(ZodNonOptional, this, args[0]);
|
|
84914
85650
|
}
|
|
84915
85651
|
});
|
|
84916
|
-
function
|
|
85652
|
+
function object6(shape, params) {
|
|
84917
85653
|
const def = {
|
|
84918
85654
|
type: "object",
|
|
84919
85655
|
shape: shape ?? {},
|
|
@@ -85106,11 +85842,11 @@ var ZodEnum2 = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
|
|
|
85106
85842
|
inst._zod.processJSONSchema = (ctx, json5, params) => enumProcessor(inst, ctx, json5, params);
|
|
85107
85843
|
inst.enum = def.entries;
|
|
85108
85844
|
inst.options = Object.values(def.entries);
|
|
85109
|
-
const
|
|
85845
|
+
const keys3 = new Set(Object.keys(def.entries));
|
|
85110
85846
|
inst.extract = (values, params) => {
|
|
85111
85847
|
const newEntries = {};
|
|
85112
85848
|
for (const value of values) {
|
|
85113
|
-
if (
|
|
85849
|
+
if (keys3.has(value)) {
|
|
85114
85850
|
newEntries[value] = def.entries[value];
|
|
85115
85851
|
} else
|
|
85116
85852
|
throw new Error(`Key ${value} not found in enum`);
|
|
@@ -85125,7 +85861,7 @@ var ZodEnum2 = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
|
|
|
85125
85861
|
inst.exclude = (values, params) => {
|
|
85126
85862
|
const newEntries = { ...def.entries };
|
|
85127
85863
|
for (const value of values) {
|
|
85128
|
-
if (
|
|
85864
|
+
if (keys3.has(value)) {
|
|
85129
85865
|
delete newEntries[value];
|
|
85130
85866
|
} else
|
|
85131
85867
|
throw new Error(`Key ${value} not found in enum`);
|
|
@@ -86112,10 +86848,10 @@ function visit2(schema, fnOrHandlers) {
|
|
|
86112
86848
|
switch (kind) {
|
|
86113
86849
|
case "object": {
|
|
86114
86850
|
const oldShape = def.shape;
|
|
86115
|
-
const
|
|
86851
|
+
const keys3 = Object.keys(oldShape);
|
|
86116
86852
|
let changed = false;
|
|
86117
86853
|
const newShape = {};
|
|
86118
|
-
for (const k4 of
|
|
86854
|
+
for (const k4 of keys3) {
|
|
86119
86855
|
const mapped = run(oldShape[k4]);
|
|
86120
86856
|
if (mapped !== oldShape[k4])
|
|
86121
86857
|
changed = true;
|
|
@@ -86295,7 +87031,7 @@ function date4(params) {
|
|
|
86295
87031
|
return _coercedDate(ZodDate2, params);
|
|
86296
87032
|
}
|
|
86297
87033
|
// src/lib/canonical-json.ts
|
|
86298
|
-
import { createHash as
|
|
87034
|
+
import { createHash as createHash28 } from "crypto";
|
|
86299
87035
|
function canonicalJson3(value, maximumBytes = 2 * 1024 * 1024) {
|
|
86300
87036
|
return canonicalJsonAtDepth(value, maximumBytes, 64);
|
|
86301
87037
|
}
|
|
@@ -86339,7 +87075,7 @@ function canonicalJsonAtDepth(value, maximumBytes, maximumDepth) {
|
|
|
86339
87075
|
return visit3(value, 0);
|
|
86340
87076
|
}
|
|
86341
87077
|
function canonicalJsonSha256(value, maximumBytes) {
|
|
86342
|
-
return
|
|
87078
|
+
return createHash28("sha256").update(canonicalJson3(value, maximumBytes)).digest("hex");
|
|
86343
87079
|
}
|
|
86344
87080
|
|
|
86345
87081
|
// src/lib/remote-recurring-contract.ts
|
|
@@ -86597,9 +87333,9 @@ function requireResult(value) {
|
|
|
86597
87333
|
if (!value)
|
|
86598
87334
|
throw new RemoteRecurringReadError;
|
|
86599
87335
|
}
|
|
86600
|
-
function termsIntegrity(terms,
|
|
87336
|
+
function termsIntegrity(terms, hash4, identity3, own2) {
|
|
86601
87337
|
requireResult(terms.organizationId === identity3.organization.id && (!own2 || terms.approvedByUserId === identity3.user.id && terms.approvedMembershipId === identity3.user.membershipId));
|
|
86602
|
-
requireResult(canonicalJsonSha256(terms) ===
|
|
87338
|
+
requireResult(canonicalJsonSha256(terms) === hash4);
|
|
86603
87339
|
requireResult(canonicalJsonSha256({ skill: terms.skill, input: terms.input, args: terms.args, runtime: terms.runtime }) === terms.payloadSha256);
|
|
86604
87340
|
requireResult(Date.parse(terms.expiresAt) > Date.parse(terms.startsAt));
|
|
86605
87341
|
}
|
|
@@ -86734,7 +87470,7 @@ var inputFailure = () => {
|
|
|
86734
87470
|
function invitationInput(action, input2) {
|
|
86735
87471
|
if (!record4(input2))
|
|
86736
87472
|
return inputFailure();
|
|
86737
|
-
const
|
|
87473
|
+
const keys3 = {
|
|
86738
87474
|
list: ["after"],
|
|
86739
87475
|
get: ["invitationId"],
|
|
86740
87476
|
issue: ["email", "role", "idempotencyKey", "confirm"],
|
|
@@ -86742,7 +87478,7 @@ function invitationInput(action, input2) {
|
|
|
86742
87478
|
revoke: ["invitationId", "expectedGeneration", "confirm"],
|
|
86743
87479
|
accept: ["invitationId", "token", "confirm"]
|
|
86744
87480
|
};
|
|
86745
|
-
if (Object.keys(input2).some((key) => !
|
|
87481
|
+
if (Object.keys(input2).some((key) => !keys3[action].includes(key)) || action !== "list" && keys3[action].some((key) => !Object.hasOwn(input2, key)))
|
|
86746
87482
|
return inputFailure();
|
|
86747
87483
|
const value = { ...input2 };
|
|
86748
87484
|
if (action === "list") {
|
|
@@ -87119,15 +87855,15 @@ function runQuoteReceipt(value) {
|
|
|
87119
87855
|
return value;
|
|
87120
87856
|
}
|
|
87121
87857
|
function parseRemoteRunQuote(value) {
|
|
87122
|
-
const quote2 =
|
|
87858
|
+
const quote2 = object7(value);
|
|
87123
87859
|
runQuoteReceipt(quote2.quoteReceipt);
|
|
87124
|
-
const pricing =
|
|
87860
|
+
const pricing = object7(quote2.pricing);
|
|
87125
87861
|
if (typeof quote2.skill !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(quote2.skill))
|
|
87126
87862
|
throw new Error("Invalid quoted skill");
|
|
87127
87863
|
const costCents = creditCount(pricing.costCredits ?? pricing.costCents);
|
|
87128
87864
|
if (pricing.costCredits !== undefined && pricing.costCents !== undefined && pricing.costCents !== costCents)
|
|
87129
87865
|
throw new Error("Inconsistent quoted credit count");
|
|
87130
|
-
if (quote2.availability &&
|
|
87866
|
+
if (quote2.availability && object7(quote2.availability).status !== "available")
|
|
87131
87867
|
throw new Error("This skill is unavailable for remote execution");
|
|
87132
87868
|
return { ...quote2, skill: quote2.skill, pricing: { ...pricing, costCredits: costCents, costCents, formattedCost: `${costCents} credits` } };
|
|
87133
87869
|
}
|
|
@@ -87136,7 +87872,7 @@ function parseRemoteCreditPacks(value) {
|
|
|
87136
87872
|
throw new Error("Invalid credit pack response");
|
|
87137
87873
|
const ids = new Set;
|
|
87138
87874
|
return value.map((value2) => {
|
|
87139
|
-
const row =
|
|
87875
|
+
const row = object7(value2);
|
|
87140
87876
|
const counts = [row.credits, row.creditsCents, row.amountCents].filter((value3) => value3 !== undefined).map(creditCount);
|
|
87141
87877
|
if (!counts.length || counts[0] === 0 || counts.some((count) => count !== counts[0]))
|
|
87142
87878
|
throw new Error("Inconsistent credit pack counts");
|
|
@@ -87151,7 +87887,7 @@ function parseRemoteCreditPacks(value) {
|
|
|
87151
87887
|
});
|
|
87152
87888
|
}
|
|
87153
87889
|
function parseRemoteBillingStatus(value) {
|
|
87154
|
-
const row =
|
|
87890
|
+
const row = object7(value);
|
|
87155
87891
|
const counts = [row.creditBalance, row.balanceCents].filter((value2) => value2 !== undefined).map(creditCount);
|
|
87156
87892
|
if (!counts.length || counts.some((count) => count !== counts[0]))
|
|
87157
87893
|
throw new Error("Inconsistent credit balance");
|
|
@@ -87163,7 +87899,7 @@ function parseRemoteBillingStatus(value) {
|
|
|
87163
87899
|
};
|
|
87164
87900
|
}
|
|
87165
87901
|
function parseRemoteCheckout(value) {
|
|
87166
|
-
const row =
|
|
87902
|
+
const row = object7(value);
|
|
87167
87903
|
if (typeof row.url !== "string")
|
|
87168
87904
|
throw new Error("Invalid checkout URL");
|
|
87169
87905
|
const url2 = new URL(row.url);
|
|
@@ -87171,7 +87907,7 @@ function parseRemoteCheckout(value) {
|
|
|
87171
87907
|
throw new Error("Invalid checkout URL");
|
|
87172
87908
|
return { url: row.url };
|
|
87173
87909
|
}
|
|
87174
|
-
function
|
|
87910
|
+
function object7(value) {
|
|
87175
87911
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
87176
87912
|
throw new Error("Invalid Skills server response");
|
|
87177
87913
|
return value;
|
|
@@ -87375,6 +88111,18 @@ class RemoteRequestError extends Error {
|
|
|
87375
88111
|
this.name = "RemoteRequestError";
|
|
87376
88112
|
}
|
|
87377
88113
|
}
|
|
88114
|
+
var KNOWN_SKILL_LIFECYCLE_CODES = new Set(["SKILL_ARCHIVE_PROFILE_CONFLICT", "LIFECYCLE_ROLE_REQUIRED"]);
|
|
88115
|
+
|
|
88116
|
+
class RemoteSkillLifecycleError extends RemoteRequestError {
|
|
88117
|
+
code;
|
|
88118
|
+
constructor(path2, status, code) {
|
|
88119
|
+
super(path2, status);
|
|
88120
|
+
this.name = "RemoteSkillLifecycleError";
|
|
88121
|
+
const safeCode = code !== undefined && KNOWN_SKILL_LIFECYCLE_CODES.has(code) ? code : undefined;
|
|
88122
|
+
this.code = safeCode;
|
|
88123
|
+
this.message = `Skill lifecycle update was refused (HTTP ${status}${safeCode ? `, code ${safeCode}` : ""})`;
|
|
88124
|
+
}
|
|
88125
|
+
}
|
|
87378
88126
|
|
|
87379
88127
|
class RemoteCreditCheckoutError extends RemoteRequestError {
|
|
87380
88128
|
code;
|
|
@@ -87555,6 +88303,39 @@ class RemoteSkillsClient {
|
|
|
87555
88303
|
})();
|
|
87556
88304
|
return this.capabilities;
|
|
87557
88305
|
}
|
|
88306
|
+
async addSkillPublishScope(keyId, expectedScopes, expectedOrgId) {
|
|
88307
|
+
if (!/^[A-Za-z0-9_-]+$/.test(keyId))
|
|
88308
|
+
throw new Error("Invalid API key id");
|
|
88309
|
+
if (!/^[A-Za-z0-9_-]{1,256}$/.test(expectedOrgId))
|
|
88310
|
+
throw new Error("Invalid expected organization id");
|
|
88311
|
+
if (!Array.isArray(expectedScopes) || expectedScopes.length > 32 || expectedScopes.some((scope2) => typeof scope2 !== "string" || scope2.length > 128 || !/^(?:\*|[a-z][a-z0-9_-]*:(?:\*|[a-z][a-z0-9_-]*))$/.test(scope2)) || new Set(expectedScopes).size !== expectedScopes.length)
|
|
88312
|
+
throw new Error("Invalid expected API key scopes");
|
|
88313
|
+
const path2 = `/api/v1/admin/keys/${encodeURIComponent(keyId)}/scopes`;
|
|
88314
|
+
const response = await this.request(path2, { method: "PATCH", body: JSON.stringify({ expected_scopes: expectedScopes, add_scopes: ["skills:publish"] }) });
|
|
88315
|
+
if (!response.ok)
|
|
88316
|
+
throw new RemoteRequestError(path2, response.status);
|
|
88317
|
+
let value;
|
|
88318
|
+
try {
|
|
88319
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 64 * 1024)));
|
|
88320
|
+
} catch {
|
|
88321
|
+
throw new Error("Invalid API key scope update response");
|
|
88322
|
+
}
|
|
88323
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
88324
|
+
throw new Error("Invalid API key scope update response");
|
|
88325
|
+
const body4 = value;
|
|
88326
|
+
const returnedId = body4.keyId;
|
|
88327
|
+
const orgId = body4.orgId;
|
|
88328
|
+
const scopes = body4.scopes;
|
|
88329
|
+
if (returnedId !== keyId || typeof orgId !== "string" || !orgId || expectedOrgId !== undefined && orgId !== expectedOrgId || !Array.isArray(scopes) || scopes.some((scope2) => typeof scope2 !== "string") || body4.updated !== true) {
|
|
88330
|
+
throw new Error("Invalid API key scope update response");
|
|
88331
|
+
}
|
|
88332
|
+
const returnedScopes = scopes;
|
|
88333
|
+
const expectedResult = expectedScopes.includes("skills:publish") ? expectedScopes : [...expectedScopes, "skills:publish"];
|
|
88334
|
+
if (returnedScopes.length !== expectedResult.length || expectedResult.some((scope2, index) => returnedScopes[index] !== scope2)) {
|
|
88335
|
+
throw new Error("API key scope update response did not preserve the expected scopes");
|
|
88336
|
+
}
|
|
88337
|
+
return { keyId, orgId, scopes: [...returnedScopes], updated: true };
|
|
88338
|
+
}
|
|
87558
88339
|
async submitQuotedRun(slug2, input2 = {}, args = [], approval = {}) {
|
|
87559
88340
|
runQuoteReceipt(approval.quoteReceipt);
|
|
87560
88341
|
({ input: input2, args, approval } = JSON.parse(JSON.stringify({ input: input2, args, approval })));
|
|
@@ -87928,14 +88709,14 @@ class RemoteSkillsClient {
|
|
|
87928
88709
|
async createBillingPortal() {
|
|
87929
88710
|
return this.checkoutResponse("/api/v1/billing/portal");
|
|
87930
88711
|
}
|
|
87931
|
-
async cancelRun(
|
|
87932
|
-
return this.controlRun(
|
|
88712
|
+
async cancelRun(runId3) {
|
|
88713
|
+
return this.controlRun(runId3, "cancel");
|
|
87933
88714
|
}
|
|
87934
|
-
async resumeRun(
|
|
87935
|
-
return this.controlRun(
|
|
88715
|
+
async resumeRun(runId3) {
|
|
88716
|
+
return this.controlRun(runId3, "resume");
|
|
87936
88717
|
}
|
|
87937
|
-
async controlRun(
|
|
87938
|
-
const response = await this.requestNewRoute(`/api/v1/runs/${encodeURIComponent(
|
|
88718
|
+
async controlRun(runId3, action) {
|
|
88719
|
+
const response = await this.requestNewRoute(`/api/v1/runs/${encodeURIComponent(runId3)}/${action}`, { method: "POST", body: "{}" });
|
|
87939
88720
|
return normalizeRemoteSkillRunContract(await response.json());
|
|
87940
88721
|
}
|
|
87941
88722
|
async checkoutResponse(path2) {
|
|
@@ -87947,8 +88728,8 @@ class RemoteSkillsClient {
|
|
|
87947
88728
|
throw new Error("Invalid Skills server list response");
|
|
87948
88729
|
return rows;
|
|
87949
88730
|
}
|
|
87950
|
-
async getRun(
|
|
87951
|
-
const path2 = `/api/v1/runs/${encodeURIComponent(
|
|
88731
|
+
async getRun(runId3) {
|
|
88732
|
+
const path2 = `/api/v1/runs/${encodeURIComponent(runId3)}`;
|
|
87952
88733
|
const res = await this.request(path2);
|
|
87953
88734
|
if (res.status === 404)
|
|
87954
88735
|
return null;
|
|
@@ -87956,30 +88737,30 @@ class RemoteSkillsClient {
|
|
|
87956
88737
|
throw new RemoteRequestError(path2, res.status, res.statusText);
|
|
87957
88738
|
return normalizeRemoteSkillRunContract(await res.json());
|
|
87958
88739
|
}
|
|
87959
|
-
async getRunLogs(
|
|
87960
|
-
return this.arrayResponse(`/api/v1/runs/${encodeURIComponent(
|
|
88740
|
+
async getRunLogs(runId3) {
|
|
88741
|
+
return this.arrayResponse(`/api/v1/runs/${encodeURIComponent(runId3)}/logs`);
|
|
87961
88742
|
}
|
|
87962
88743
|
async listRuns(limit = 20) {
|
|
87963
88744
|
if (!Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
87964
88745
|
throw new Error("Run limit must be an integer from 1 to 100");
|
|
87965
88746
|
return this.arrayResponse(`/api/v1/runs?limit=${limit}`);
|
|
87966
88747
|
}
|
|
87967
|
-
async getRunArtifacts(
|
|
87968
|
-
return this.arrayResponse(`/api/v1/runs/${encodeURIComponent(
|
|
88748
|
+
async getRunArtifacts(runId3) {
|
|
88749
|
+
return this.arrayResponse(`/api/v1/runs/${encodeURIComponent(runId3)}/artifacts`);
|
|
87969
88750
|
}
|
|
87970
|
-
async downloadRunArtifact(
|
|
87971
|
-
return this.request(`/api/v1/runs/${encodeURIComponent(
|
|
88751
|
+
async downloadRunArtifact(runId3, artifactId2) {
|
|
88752
|
+
return this.request(`/api/v1/runs/${encodeURIComponent(runId3)}/artifacts/${encodeURIComponent(artifactId2)}/download`, {
|
|
87972
88753
|
method: "GET"
|
|
87973
88754
|
});
|
|
87974
88755
|
}
|
|
87975
|
-
async getVerifiedRunArtifact(
|
|
87976
|
-
const artifacts = await this.getRunArtifacts(
|
|
88756
|
+
async getVerifiedRunArtifact(runId3, artifactId2, maximumBytes = MAX_REMOTE_FILE_BYTES) {
|
|
88757
|
+
const artifacts = await this.getRunArtifacts(runId3);
|
|
87977
88758
|
const artifact = artifacts.find((row) => row.id === artifactId2);
|
|
87978
88759
|
if (!artifact)
|
|
87979
88760
|
throw new Error("Run artifact not found");
|
|
87980
88761
|
if (!Number.isSafeInteger(artifact.byteSize) || artifact.byteSize < 0 || artifact.byteSize > maximumBytes || typeof artifact.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(artifact.sha256))
|
|
87981
88762
|
throw new Error("The server does not provide valid artifact integrity metadata");
|
|
87982
|
-
const response = await this.downloadRunArtifact(
|
|
88763
|
+
const response = await this.downloadRunArtifact(runId3, artifactId2);
|
|
87983
88764
|
if (!response.ok)
|
|
87984
88765
|
throw new RemoteRequestError("artifact download", response.status, response.statusText);
|
|
87985
88766
|
const bytes = await readBoundedResponse(response, artifact.byteSize);
|
|
@@ -88027,9 +88808,9 @@ class RemoteSkillsClient {
|
|
|
88027
88808
|
}
|
|
88028
88809
|
return run;
|
|
88029
88810
|
}
|
|
88030
|
-
async uploadRunFiles(
|
|
88811
|
+
async uploadRunFiles(runId3, files) {
|
|
88031
88812
|
const descriptors = describeRemoteFiles(files);
|
|
88032
|
-
const response = await this.requestNewRoute(`/api/v1/runs/${encodeURIComponent(
|
|
88813
|
+
const response = await this.requestNewRoute(`/api/v1/runs/${encodeURIComponent(runId3)}/uploads`, { method: "POST", body: JSON.stringify({ files: descriptors }) });
|
|
88033
88814
|
const payload = await response.json();
|
|
88034
88815
|
if (!Array.isArray(payload.files) || payload.files.length !== files.length || new Set(payload.files.map((file2) => file2.name)).size !== files.length)
|
|
88035
88816
|
throw new Error("Invalid input upload response");
|
|
@@ -88072,6 +88853,19 @@ class RemoteSkillsClient {
|
|
|
88072
88853
|
async deleteSkill(slug2) {
|
|
88073
88854
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}`, { method: "DELETE" });
|
|
88074
88855
|
}
|
|
88856
|
+
async setSkillLifecycle(slug2, lifecycle, options) {
|
|
88857
|
+
const path2 = `/api/v1/skills/${encodeURIComponent(slug2)}/lifecycle`;
|
|
88858
|
+
const response = await this.request(path2, {
|
|
88859
|
+
method: "PATCH",
|
|
88860
|
+
headers: { "If-Match": options.expectedRevisionId },
|
|
88861
|
+
body: JSON.stringify({ lifecycle, ...options.reason ? { reason: options.reason } : {}, ...options.replacementSlug ? { replacementSlug: options.replacementSlug } : {} })
|
|
88862
|
+
});
|
|
88863
|
+
if (!response.ok) {
|
|
88864
|
+
const code = await readResponseCode(response);
|
|
88865
|
+
throw new RemoteSkillLifecycleError(path2, response.status, code);
|
|
88866
|
+
}
|
|
88867
|
+
return response.json();
|
|
88868
|
+
}
|
|
88075
88869
|
async downloadSkillBundle(slug2) {
|
|
88076
88870
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}/bundle`, { method: "GET" });
|
|
88077
88871
|
}
|
|
@@ -88224,9 +89018,13 @@ function normalizeSkillSummaryList(payload) {
|
|
|
88224
89018
|
return payload.map(normalizeSkillSummary);
|
|
88225
89019
|
}
|
|
88226
89020
|
async function responseBodyCarriesCode(response, codes) {
|
|
89021
|
+
const code = await readResponseCode(response);
|
|
89022
|
+
return code !== undefined && codes.includes(code);
|
|
89023
|
+
}
|
|
89024
|
+
async function readResponseCode(response) {
|
|
88227
89025
|
const reader = response.body?.getReader();
|
|
88228
89026
|
if (!reader)
|
|
88229
|
-
return
|
|
89027
|
+
return;
|
|
88230
89028
|
const maximum = 8 * 1024;
|
|
88231
89029
|
let deadline;
|
|
88232
89030
|
const expired = new Promise((_, reject) => {
|
|
@@ -88241,7 +89039,7 @@ async function responseBodyCarriesCode(response, codes) {
|
|
|
88241
89039
|
break;
|
|
88242
89040
|
size += next2.value.byteLength;
|
|
88243
89041
|
if (size > maximum)
|
|
88244
|
-
return
|
|
89042
|
+
return;
|
|
88245
89043
|
chunks.push(next2.value);
|
|
88246
89044
|
}
|
|
88247
89045
|
const bytes = new Uint8Array(size);
|
|
@@ -88252,11 +89050,11 @@ async function responseBodyCarriesCode(response, codes) {
|
|
|
88252
89050
|
}
|
|
88253
89051
|
const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
88254
89052
|
if (!payload || typeof payload !== "object" || Array.isArray(payload) || !Object.hasOwn(payload, "code"))
|
|
88255
|
-
return
|
|
89053
|
+
return;
|
|
88256
89054
|
const code = payload.code;
|
|
88257
|
-
return typeof code === "string" &&
|
|
89055
|
+
return typeof code === "string" && /^[A-Z][A-Z0-9_:-]{0,127}$/.test(code) ? code : undefined;
|
|
88258
89056
|
} catch {
|
|
88259
|
-
return
|
|
89057
|
+
return;
|
|
88260
89058
|
} finally {
|
|
88261
89059
|
clearTimeout(deadline);
|
|
88262
89060
|
reader.cancel().catch(() => {});
|
|
@@ -88327,8 +89125,8 @@ function invitationEmailIds(invitationId, challengeId) {
|
|
|
88327
89125
|
return { invitationId, challengeId };
|
|
88328
89126
|
}
|
|
88329
89127
|
function invitationEmailInput(action, input2) {
|
|
88330
|
-
const
|
|
88331
|
-
if (!record8(input2) || Object.keys(input2).length !==
|
|
89128
|
+
const keys3 = ["invitationId", "token", "challengeId", "confirm", ...action === "accept" ? ["code"] : []];
|
|
89129
|
+
if (!record8(input2) || Object.keys(input2).length !== keys3.length || keys3.some((key) => !Object.hasOwn(input2, key)))
|
|
88332
89130
|
throw new InvitationEmailInputError;
|
|
88333
89131
|
const value = { ...input2 };
|
|
88334
89132
|
const ids = invitationEmailIds(value.invitationId, value.challengeId);
|
|
@@ -88378,7 +89176,7 @@ async function requestInvitationEmail(origin, action, input2) {
|
|
|
88378
89176
|
}
|
|
88379
89177
|
|
|
88380
89178
|
// src/lib/remote-private-publications.ts
|
|
88381
|
-
import { createHash as
|
|
89179
|
+
import { createHash as createHash29 } from "crypto";
|
|
88382
89180
|
var PRIVATE_PUBLICATION_MAX_BYTES = 16 * 1024 * 1024;
|
|
88383
89181
|
var failures2 = {
|
|
88384
89182
|
INVALID_REQUEST: [400, "The publication request is invalid."],
|
|
@@ -88421,17 +89219,17 @@ var invalid7 = () => {
|
|
|
88421
89219
|
throw new PrivatePublicationError("INVALID_PUBLICATION_RESPONSE", "The server returned an invalid publication result.");
|
|
88422
89220
|
};
|
|
88423
89221
|
var publicationUuid = (v2) => typeof v2 === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v2);
|
|
88424
|
-
var
|
|
89222
|
+
var hash4 = (v2) => typeof v2 === "string" && /^[a-f0-9]{64}$/.test(v2);
|
|
88425
89223
|
var record9 = (v2) => !!v2 && typeof v2 === "object" && !Array.isArray(v2);
|
|
88426
|
-
var exact = (v2,
|
|
89224
|
+
var exact = (v2, keys3) => record9(v2) && Object.keys(v2).sort().join(",") === keys3.sort().join(",");
|
|
88427
89225
|
var date5 = (v2) => typeof v2 === "string" && /^\d{4}-\d\d-\d\dT[0-9:.]+(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(v2) && Number.isFinite(Date.parse(v2));
|
|
88428
|
-
var publicationSha256 = (bytes) =>
|
|
89226
|
+
var publicationSha256 = (bytes) => createHash29("sha256").update(bytes).digest("hex");
|
|
88429
89227
|
function checkedPublicationDeclaration(value) {
|
|
88430
|
-
if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !
|
|
89228
|
+
if (!exact(value, ["idempotencyKey", "version", "expectedCurrentVersionId", "manifestText", "archiveSha256", "archiveByteSize"]) || !publicationUuid(value.idempotencyKey) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || typeof value.manifestText !== "string" || Buffer.byteLength(value.manifestText) > 16384 || !hash4(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES)
|
|
88431
89229
|
return bad();
|
|
88432
89230
|
try {
|
|
88433
89231
|
const manifest = JSON.parse(value.manifestText);
|
|
88434
|
-
if (!record9(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !
|
|
89232
|
+
if (!record9(manifest) || manifest.version !== value.version || validatePortableManifestContract(manifest, { strict: true }).length || !hash4(manifest.provenance?.content_hash))
|
|
88435
89233
|
return bad();
|
|
88436
89234
|
} catch {
|
|
88437
89235
|
return bad();
|
|
@@ -88439,7 +89237,7 @@ function checkedPublicationDeclaration(value) {
|
|
|
88439
89237
|
return Object.freeze({ ...value });
|
|
88440
89238
|
}
|
|
88441
89239
|
function checkedPublicationView(value, skillId, intentId, declaration) {
|
|
88442
|
-
if (!exact(value, ["id", "skillId", "version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize", "state", "expiresAt", "createdAt", "versionId"]) || !publicationUuid(value.id) || value.skillId !== skillId || intentId !== undefined && value.id !== intentId || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || !
|
|
89240
|
+
if (!exact(value, ["id", "skillId", "version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize", "state", "expiresAt", "createdAt", "versionId"]) || !publicationUuid(value.id) || value.skillId !== skillId || intentId !== undefined && value.id !== intentId || typeof value.version !== "string" || !value.version || value.version.length > 128 || /[\p{Cc}\p{Cs}]/u.test(value.version) || !(value.expectedCurrentVersionId === null || publicationUuid(value.expectedCurrentVersionId)) || !hash4(value.archiveSha256) || !Number.isSafeInteger(value.archiveByteSize) || Number(value.archiveByteSize) < 1 || Number(value.archiveByteSize) > PRIVATE_PUBLICATION_MAX_BYTES || typeof value.state !== "string" || !["awaiting_upload", "queued", "verifying", "needs_attention", "committed", "rejected", "cancelled", "expired"].includes(value.state) || !date5(value.expiresAt) || !date5(value.createdAt) || !(value.versionId === null || publicationUuid(value.versionId)) || value.state === "committed" && value.versionId === null)
|
|
88443
89241
|
return invalid7();
|
|
88444
89242
|
if (declaration && ["version", "expectedCurrentVersionId", "archiveSha256", "archiveByteSize"].some((k4) => value[k4] !== declaration[k4]))
|
|
88445
89243
|
return invalid7();
|
|
@@ -89165,8 +89963,8 @@ function record10(value) {
|
|
|
89165
89963
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
89166
89964
|
}
|
|
89167
89965
|
function exact2(value, names) {
|
|
89168
|
-
const
|
|
89169
|
-
return
|
|
89966
|
+
const keys3 = Object.keys(value).sort(), expected = [...names].sort();
|
|
89967
|
+
return keys3.length === expected.length && keys3.every((key, index) => key === expected[index]);
|
|
89170
89968
|
}
|
|
89171
89969
|
function snapshot(value, maximum) {
|
|
89172
89970
|
let nodes4 = 0, bytes = 0;
|
|
@@ -89203,13 +90001,13 @@ function snapshot(value, maximum) {
|
|
|
89203
90001
|
const array3 = Array.isArray(input2), proto = Object.getPrototypeOf(input2);
|
|
89204
90002
|
if (array3 ? proto !== Array.prototype : proto !== Object.prototype && proto !== null)
|
|
89205
90003
|
throw Error();
|
|
89206
|
-
const
|
|
89207
|
-
if (
|
|
90004
|
+
const keys3 = Reflect.ownKeys(input2);
|
|
90005
|
+
if (keys3.length > SKILL_OPERATION_LIMITS.nodes + 1 || keys3.some((key) => typeof key !== "string"))
|
|
89208
90006
|
throw Error();
|
|
89209
90007
|
ancestors2.add(input2);
|
|
89210
90008
|
try {
|
|
89211
90009
|
if (array3) {
|
|
89212
|
-
if (input2.length > SKILL_OPERATION_LIMITS.nodes ||
|
|
90010
|
+
if (input2.length > SKILL_OPERATION_LIMITS.nodes || keys3.length !== input2.length + 1)
|
|
89213
90011
|
throw Error();
|
|
89214
90012
|
const result2 = [];
|
|
89215
90013
|
append("[");
|
|
@@ -89226,7 +90024,7 @@ function snapshot(value, maximum) {
|
|
|
89226
90024
|
}
|
|
89227
90025
|
const result = Object.create(null);
|
|
89228
90026
|
append("{");
|
|
89229
|
-
for (const [i4, key] of
|
|
90027
|
+
for (const [i4, key] of keys3.sort().entries()) {
|
|
89230
90028
|
const descriptor = Object.getOwnPropertyDescriptor(input2, key);
|
|
89231
90029
|
if (!("value" in descriptor) || !descriptor.enumerable || /[\ud800-\udfff]/u.test(key) || key.length > maximum - bytes)
|
|
89232
90030
|
throw Error();
|
|
@@ -89513,6 +90311,7 @@ export {
|
|
|
89513
90311
|
DEFAULT_OUTPUT_GOVERNANCE,
|
|
89514
90312
|
DEFAULT_IMAGE_PROFILES,
|
|
89515
90313
|
ContentHashInputError,
|
|
90314
|
+
CloudExecutionClient,
|
|
89516
90315
|
CONTENT_HASH_LIMITS,
|
|
89517
90316
|
CLIENT_TOKEN_BYTES,
|
|
89518
90317
|
ArtifactStorage
|