tines 0.0.243 → 0.0.245
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/dist/index.js +183 -116
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4243,8 +4243,8 @@ function createApiClient(options) {
|
|
|
4243
4243
|
getPublicationResult: (candidateId) => get(
|
|
4244
4244
|
`/api/v1/publications/${encodeURIComponent(candidateId)}/result`
|
|
4245
4245
|
),
|
|
4246
|
-
listPublications: (workflow2) => get(
|
|
4247
|
-
`/api/v1/publications${query({ workflow: workflow2 })}`
|
|
4246
|
+
listPublications: (workflow2, page) => get(
|
|
4247
|
+
`/api/v1/publications${query({ workflow: workflow2, ...page })}`
|
|
4248
4248
|
),
|
|
4249
4249
|
withdrawPublication: (snapshotId) => request(
|
|
4250
4250
|
"POST",
|
|
@@ -4453,11 +4453,12 @@ async function* listPages(fetchPage, opts = {}) {
|
|
|
4453
4453
|
const maxItems = opts.maxItems ?? MAX_ALL_PAGES_ITEMS;
|
|
4454
4454
|
const seen = /* @__PURE__ */ new Set();
|
|
4455
4455
|
const seenCursors = /* @__PURE__ */ new Set();
|
|
4456
|
+
const identify = opts.identify ?? ((item) => item.id);
|
|
4456
4457
|
let cursor;
|
|
4457
4458
|
for (; ; ) {
|
|
4458
4459
|
const res = await fetchPage({ limit: pageSize, cursor });
|
|
4459
|
-
const fresh = res.items.filter((item) => !seen.has(item
|
|
4460
|
-
for (const item of fresh) seen.add(item
|
|
4460
|
+
const fresh = res.items.filter((item) => !seen.has(identify(item)));
|
|
4461
|
+
for (const item of fresh) seen.add(identify(item));
|
|
4461
4462
|
if (seen.size > maxItems) {
|
|
4462
4463
|
throw new Error(
|
|
4463
4464
|
`list has more than ${maxItems} items \u2014 narrow it with filters, or page manually with --cursor`
|
|
@@ -5549,21 +5550,35 @@ var PACKAGE_CHOICES_MAX_BYTES = 512 * 1024;
|
|
|
5549
5550
|
var PUBLIC_WORKFLOW_MAX_BYTES = 1048576;
|
|
5550
5551
|
var PUBLICATION_CANDIDATE_TTL_MS = 15 * 60 * 1e3;
|
|
5551
5552
|
var SNAPSHOT_ID = /^[A-Za-z0-9_-]{20,100}$/;
|
|
5552
|
-
function
|
|
5553
|
-
if (SNAPSHOT_ID.test(value)) return value;
|
|
5553
|
+
function parsePublicSnapshotUrl(value) {
|
|
5554
5554
|
let url;
|
|
5555
5555
|
try {
|
|
5556
5556
|
url = new URL(value);
|
|
5557
5557
|
} catch {
|
|
5558
|
-
throw new Error("Expected a
|
|
5558
|
+
throw new Error("Expected a canonical public URL");
|
|
5559
5559
|
}
|
|
5560
|
-
if (!["http:", "https:"].includes(url.protocol)
|
|
5561
|
-
throw new Error("Invalid public publication URL");
|
|
5562
|
-
if (
|
|
5560
|
+
if (!["http:", "https:"].includes(url.protocol))
|
|
5561
|
+
throw new Error("Invalid public publication URL: must use HTTP or HTTPS");
|
|
5562
|
+
if (url.username || url.password)
|
|
5563
|
+
throw new Error("Invalid public publication URL: must not contain credentials");
|
|
5564
|
+
if (url.hash) throw new Error("Invalid public publication URL: must not contain a fragment");
|
|
5565
|
+
if (url.search)
|
|
5566
|
+
throw new Error("Invalid public publication URL: must not contain query parameters");
|
|
5567
|
+
const match = /^\/p\/([A-Za-z0-9_-]{20,100})(?:\/download)?$/.exec(url.pathname) ?? /^\/api\/v1\/publications\/public\/([A-Za-z0-9_-]{20,100})\/download$/.exec(url.pathname);
|
|
5568
|
+
if (!match) throw new Error("Invalid public publication URL: expected a canonical path");
|
|
5569
|
+
const origin = url.origin;
|
|
5570
|
+
return {
|
|
5571
|
+
snapshotId: match[1],
|
|
5572
|
+
publicUrl: `${origin}/p/${match[1]}`,
|
|
5573
|
+
downloadUrl: `${origin}/api/v1/publications/public/${match[1]}/download`
|
|
5574
|
+
};
|
|
5575
|
+
}
|
|
5576
|
+
function parsePublicSnapshotReference(value, configuredOrigin) {
|
|
5577
|
+
if (SNAPSHOT_ID.test(value)) return value;
|
|
5578
|
+
const parsed = parsePublicSnapshotUrl(value);
|
|
5579
|
+
if (configuredOrigin && new URL(parsed.publicUrl).origin !== new URL(configuredOrigin).origin)
|
|
5563
5580
|
throw new Error("external_source_requires_download");
|
|
5564
|
-
|
|
5565
|
-
if (!match || url.search) throw new Error("Invalid public publication URL");
|
|
5566
|
-
return match[1];
|
|
5581
|
+
return parsed.snapshotId;
|
|
5567
5582
|
}
|
|
5568
5583
|
|
|
5569
5584
|
// ../shared/src/usage.ts
|
|
@@ -6125,10 +6140,14 @@ more results: rerun with --all-pages, or resume with --cursor ${res.next_cursor}
|
|
|
6125
6140
|
);
|
|
6126
6141
|
}
|
|
6127
6142
|
}
|
|
6128
|
-
async function fetchList(opts, fetchPage) {
|
|
6143
|
+
async function fetchList(opts, fetchPage, identify) {
|
|
6129
6144
|
if (opts.allPages) {
|
|
6130
6145
|
return {
|
|
6131
|
-
items: await listAll(fetchPage, {
|
|
6146
|
+
items: await listAll(fetchPage, {
|
|
6147
|
+
pageSize: opts.limit,
|
|
6148
|
+
maxItems: opts.maxItems,
|
|
6149
|
+
identify: identify ?? ((item) => item.id)
|
|
6150
|
+
}),
|
|
6132
6151
|
next_cursor: null
|
|
6133
6152
|
};
|
|
6134
6153
|
}
|
|
@@ -9655,6 +9674,11 @@ async function discoverCodex(daemonVersion) {
|
|
|
9655
9674
|
};
|
|
9656
9675
|
const timer = setTimeout(() => fail("Codex model discovery timed out"), DEADLINE_MS);
|
|
9657
9676
|
child.on("error", (error) => fail(error.message));
|
|
9677
|
+
child.stdin.on("error", (error) => fail(`Codex app-server stdin: ${error.message}`));
|
|
9678
|
+
child.on(
|
|
9679
|
+
"close",
|
|
9680
|
+
(code, signal) => fail(`Codex app-server exited (${signal ?? `code ${code}`}) before listing models`)
|
|
9681
|
+
);
|
|
9658
9682
|
child.stdout.on("data", (chunk) => {
|
|
9659
9683
|
bytes2 += chunk.length;
|
|
9660
9684
|
if (bytes2 > MAX_STDOUT) return fail("Codex model discovery exceeded 1 MiB");
|
|
@@ -12937,7 +12961,7 @@ async function askToInstall(plan) {
|
|
|
12937
12961
|
rl.close();
|
|
12938
12962
|
}
|
|
12939
12963
|
}
|
|
12940
|
-
async function recoverOrInstall(api,
|
|
12964
|
+
async function recoverOrInstall(api, rawOrLoader, plan) {
|
|
12941
12965
|
const checked = (receipt) => {
|
|
12942
12966
|
if (receipt.id !== plan.plan_id || receipt.document_digest !== plan.document_digest || receipt.plan_digest !== plan.plan_digest)
|
|
12943
12967
|
throw new Error("recovered workflow package receipt does not match the saved plan");
|
|
@@ -12948,6 +12972,7 @@ async function recoverOrInstall(api, raw, plan) {
|
|
|
12948
12972
|
} catch (error) {
|
|
12949
12973
|
if (!(error instanceof ApiError) || error.status !== 404) throw error;
|
|
12950
12974
|
}
|
|
12975
|
+
const raw = typeof rawOrLoader === "function" ? await rawOrLoader() : rawOrLoader;
|
|
12951
12976
|
const request = {
|
|
12952
12977
|
document_json: raw,
|
|
12953
12978
|
plan_token: plan.plan_token,
|
|
@@ -12971,7 +12996,6 @@ import { lookup as dnsLookup } from "node:dns/promises";
|
|
|
12971
12996
|
import { request as httpRequest } from "node:http";
|
|
12972
12997
|
import { request as httpsRequest } from "node:https";
|
|
12973
12998
|
import { isIP } from "node:net";
|
|
12974
|
-
var SNAPSHOT_PATH = /^\/p\/[A-Za-z0-9_-]{20,100}(?:\/download)?$/;
|
|
12975
12999
|
var MAX_REDIRECTS = 3;
|
|
12976
13000
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
12977
13001
|
function mappedIpv4(address) {
|
|
@@ -13038,40 +13062,51 @@ function isPublicAddress(address) {
|
|
|
13038
13062
|
}
|
|
13039
13063
|
return false;
|
|
13040
13064
|
}
|
|
13041
|
-
function validateHop(url
|
|
13065
|
+
function validateHop(url) {
|
|
13042
13066
|
if (!["http:", "https:"].includes(url.protocol))
|
|
13043
13067
|
throw new Error("publication URL must use HTTPS");
|
|
13044
13068
|
if (url.username || url.password) throw new Error("publication URL must not contain credentials");
|
|
13045
13069
|
if (url.hash) throw new Error("publication URL must not contain a fragment");
|
|
13046
13070
|
if (url.search) throw new Error("publication URL must not contain query parameters");
|
|
13047
|
-
if (initial && !SNAPSHOT_PATH.test(url.pathname))
|
|
13048
|
-
throw new Error("expected a canonical public snapshot or download URL");
|
|
13049
13071
|
}
|
|
13050
|
-
async function
|
|
13072
|
+
async function pinnedAddresses(url, lookup) {
|
|
13051
13073
|
const hostname4 = url.hostname.replace(/^\[|\]$/g, "");
|
|
13052
13074
|
const literalFamily = isIP(hostname4);
|
|
13053
13075
|
const addresses = literalFamily ? [{ address: hostname4, family: literalFamily }] : await lookup(hostname4, { all: true, verbatim: true });
|
|
13054
|
-
if (!addresses.length
|
|
13055
|
-
|
|
13076
|
+
if (!addresses.length || addresses.some(({ address, family }) => isIP(address) !== family || ![4, 6].includes(family)))
|
|
13077
|
+
throw new Error(`publication host ${url.hostname} did not resolve to valid addresses`);
|
|
13078
|
+
const literalLoopback = Boolean(literalFamily && isLoopback(hostname4));
|
|
13079
|
+
const loopbackDevelopment = url.protocol === "http:" && (url.hostname === "localhost" || literalLoopback) && addresses.every(({ address }) => isLoopback(address));
|
|
13056
13080
|
if (url.protocol === "http:" && !loopbackDevelopment)
|
|
13057
13081
|
throw new Error(
|
|
13058
13082
|
"publication URL must use HTTPS (HTTP is allowed only for loopback development)"
|
|
13059
13083
|
);
|
|
13060
13084
|
if (!loopbackDevelopment && addresses.some(({ address }) => !isPublicAddress(address)))
|
|
13061
13085
|
throw new Error("publication URL resolves to a private or reserved address");
|
|
13062
|
-
return addresses
|
|
13086
|
+
return addresses;
|
|
13087
|
+
}
|
|
13088
|
+
function pinnedLookup(addresses) {
|
|
13089
|
+
return (_hostname, opts, callback) => {
|
|
13090
|
+
const family = typeof opts.family === "number" ? opts.family : 0;
|
|
13091
|
+
const matching = family === 4 || family === 6 ? addresses.filter((item) => item.family === family) : addresses;
|
|
13092
|
+
if (!matching.length)
|
|
13093
|
+
return callback(new Error(`publication host has no IPv${family} address`));
|
|
13094
|
+
if (opts.all) return callback(null, matching);
|
|
13095
|
+
return callback(null, matching[0].address, matching[0].family);
|
|
13096
|
+
};
|
|
13063
13097
|
}
|
|
13064
13098
|
async function readHop(url, options) {
|
|
13065
|
-
const pinned = await
|
|
13099
|
+
const pinned = await pinnedAddresses(url, options.lookup);
|
|
13066
13100
|
return new Promise((resolve2, reject) => {
|
|
13067
13101
|
const request = (url.protocol === "https:" ? httpsRequest : httpRequest)(
|
|
13068
13102
|
url,
|
|
13069
13103
|
{
|
|
13070
13104
|
method: "GET",
|
|
13071
13105
|
headers: { accept: "application/json" },
|
|
13072
|
-
lookup: (
|
|
13106
|
+
lookup: pinnedLookup(pinned)
|
|
13073
13107
|
},
|
|
13074
13108
|
(response) => {
|
|
13109
|
+
let responseFailure;
|
|
13075
13110
|
const status = response.statusCode ?? 0;
|
|
13076
13111
|
if (status >= 300 && status < 400) {
|
|
13077
13112
|
response.resume();
|
|
@@ -13101,10 +13136,16 @@ async function readHop(url, options) {
|
|
|
13101
13136
|
const chunks = [];
|
|
13102
13137
|
response.on("data", (chunk) => {
|
|
13103
13138
|
size += chunk.length;
|
|
13104
|
-
if (size > options.maxBytes)
|
|
13105
|
-
|
|
13106
|
-
|
|
13139
|
+
if (size > options.maxBytes) {
|
|
13140
|
+
responseFailure = new Error("publication download is too large");
|
|
13141
|
+
response.destroy(responseFailure);
|
|
13142
|
+
} else chunks.push(chunk);
|
|
13107
13143
|
});
|
|
13144
|
+
response.on(
|
|
13145
|
+
"aborted",
|
|
13146
|
+
() => reject(responseFailure ?? new Error("publication download was aborted"))
|
|
13147
|
+
);
|
|
13148
|
+
response.on("error", reject);
|
|
13108
13149
|
response.on("end", () => {
|
|
13109
13150
|
try {
|
|
13110
13151
|
resolve2({
|
|
@@ -13128,17 +13169,11 @@ async function readHop(url, options) {
|
|
|
13128
13169
|
});
|
|
13129
13170
|
}
|
|
13130
13171
|
async function fetchPublicWorkflowPackage(value, options = {}) {
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
|
|
13134
|
-
} catch {
|
|
13135
|
-
throw new Error("expected a canonical public snapshot or download URL");
|
|
13136
|
-
}
|
|
13137
|
-
validateHop(url, true);
|
|
13138
|
-
if (!url.pathname.endsWith("/download")) url.pathname += "/download";
|
|
13139
|
-
const sourceUrl = url.toString();
|
|
13172
|
+
const parsed = parsePublicSnapshotUrl(value);
|
|
13173
|
+
let url = new URL(parsed.downloadUrl);
|
|
13174
|
+
const sourceUrl = parsed.downloadUrl;
|
|
13140
13175
|
for (let redirects = 0; ; redirects += 1) {
|
|
13141
|
-
validateHop(url
|
|
13176
|
+
validateHop(url);
|
|
13142
13177
|
const result = await readHop(url, {
|
|
13143
13178
|
lookup: options.lookup ?? dnsLookup,
|
|
13144
13179
|
timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
@@ -13170,6 +13205,44 @@ function readPublicationProof(path2) {
|
|
|
13170
13205
|
die("invalid workflow publication proof file");
|
|
13171
13206
|
return saved;
|
|
13172
13207
|
}
|
|
13208
|
+
function withWorkflowExportSelectors(cmd) {
|
|
13209
|
+
return cmd.option("--project <id-or-name>", "source project for selected project-bound configuration").option("--schedule <id>", "include this source schedule ID (repeatable)", collect2, []).option("--tier <state-ref=tier>", "include a state tier preference (repeatable)", collect2, []).option("--project-routing", "make every --tier selector project scoped").option("--inputs <file>", "JSON object containing input declarations and text uses");
|
|
13210
|
+
}
|
|
13211
|
+
function hasWorkflowExportSelectors(opts) {
|
|
13212
|
+
return Boolean(
|
|
13213
|
+
opts.project || opts.schedule.length || opts.tier.length || opts.projectRouting || opts.inputs
|
|
13214
|
+
);
|
|
13215
|
+
}
|
|
13216
|
+
async function resolveWorkflowExportSelection(api, workflowRef, opts) {
|
|
13217
|
+
const all = await listAll((page) => api.listWorkflows(page));
|
|
13218
|
+
const workflow2 = pickWorkflow(all, workflowRef);
|
|
13219
|
+
const sourceProject = opts.project ? await resolveProject(api, opts.project) : void 0;
|
|
13220
|
+
if ((opts.schedule.length || opts.projectRouting) && !sourceProject)
|
|
13221
|
+
die("--project is required with --schedule or --project-routing");
|
|
13222
|
+
const tiers = [];
|
|
13223
|
+
for (const selector of opts.tier) {
|
|
13224
|
+
const separator = selector.lastIndexOf("=");
|
|
13225
|
+
if (separator < 1) die(`invalid --tier "${selector}"; expected <state-ref>=<tier>`);
|
|
13226
|
+
const tier = selector.slice(separator + 1);
|
|
13227
|
+
if (!["smartest", "balanced", "cheapest"].includes(tier))
|
|
13228
|
+
die(`invalid tier "${tier}"; expected smartest, balanced, or cheapest`);
|
|
13229
|
+
tiers.push({
|
|
13230
|
+
state_id: await resolveExportState(api, selector.slice(0, separator)),
|
|
13231
|
+
tier,
|
|
13232
|
+
project_scoped: Boolean(opts.projectRouting)
|
|
13233
|
+
});
|
|
13234
|
+
}
|
|
13235
|
+
const authoring = opts.inputs ? readStrictObject(opts.inputs, "inputs file") : void 0;
|
|
13236
|
+
return {
|
|
13237
|
+
workflow: workflow2,
|
|
13238
|
+
options: {
|
|
13239
|
+
source_project_id: sourceProject?.id,
|
|
13240
|
+
schedule_ids: opts.schedule,
|
|
13241
|
+
tiers,
|
|
13242
|
+
authoring
|
|
13243
|
+
}
|
|
13244
|
+
};
|
|
13245
|
+
}
|
|
13173
13246
|
async function confirmPublicationAction(question) {
|
|
13174
13247
|
const rl = createInterface5({ input: process.stdin, output: process.stderr });
|
|
13175
13248
|
try {
|
|
@@ -13519,14 +13592,18 @@ function registerPackageCommands(workflows) {
|
|
|
13519
13592
|
if (!result.valid) process.exitCode = 1;
|
|
13520
13593
|
});
|
|
13521
13594
|
withCommon(
|
|
13522
|
-
|
|
13523
|
-
"
|
|
13524
|
-
|
|
13525
|
-
|
|
13595
|
+
withWorkflowExportSelectors(
|
|
13596
|
+
workflows.command("publish [workflow]").description("Prepare or commit one exact immutable public workflow snapshot").option(
|
|
13597
|
+
"--from <file>",
|
|
13598
|
+
"prepare from an existing workflow package instead of an owned workflow"
|
|
13599
|
+
).option("--proof-out <file>", "save the complete prepared proof with mode 0600").option("--proof <file>", "commit a previously saved exact proof").option("--display-name <name>", "public attribution name (never an email)").option("--license <license>", "reuse license (currently MIT)", "MIT").option("--license-year <year>", "MIT copyright year", String((/* @__PURE__ */ new Date()).getFullYear())).option("--confirm <review-digest>", "exact digest of the reviewed proof").option("--sharing-rights", "confirm rights to every bundled declaration and file").option("--recover", "reconcile this same candidate after a lost response").option("--repo <local-id>", "confirm one bundled repository ID (repeatable)", collect2, [])
|
|
13600
|
+
)
|
|
13526
13601
|
).action(
|
|
13527
13602
|
async (workflow2, opts) => {
|
|
13528
13603
|
const apiBase = normalizeUrl(resolveUrl(opts));
|
|
13529
13604
|
const api = client(opts);
|
|
13605
|
+
if ((opts.from || opts.proof) && hasWorkflowExportSelectors(opts))
|
|
13606
|
+
die("workflow selectors cannot be combined with --from or --proof");
|
|
13530
13607
|
if (opts.proof) {
|
|
13531
13608
|
if (workflow2 || opts.from || opts.proofOut || opts.displayName)
|
|
13532
13609
|
die(
|
|
@@ -13540,11 +13617,13 @@ function registerPackageCommands(workflows) {
|
|
|
13540
13617
|
die(`confirmation digest does not match reviewed proof ${proof2.review_digest}`);
|
|
13541
13618
|
if (!process.stdin.isTTY && (opts.confirm !== proof2.review_digest || !opts.sharingRights))
|
|
13542
13619
|
die(`non-interactive publish requires --confirm ${proof2.review_digest} --sharing-rights`);
|
|
13543
|
-
if (process.stdin.isTTY &&
|
|
13620
|
+
if (process.stdin.isTTY && opts.confirm !== proof2.review_digest) {
|
|
13544
13621
|
process.stderr.write(`${JSON.stringify(proof2, null, 2)}
|
|
13545
13622
|
`);
|
|
13546
13623
|
if (!await confirmPublicationAction("Publish these exact immutable bytes?"))
|
|
13547
13624
|
die("publication declined");
|
|
13625
|
+
}
|
|
13626
|
+
if (process.stdin.isTTY && !opts.sharingRights) {
|
|
13548
13627
|
if (!await confirmPublicationAction("Do you have sharing rights for every bundled item?"))
|
|
13549
13628
|
die("publication declined");
|
|
13550
13629
|
}
|
|
@@ -13573,11 +13652,16 @@ function registerPackageCommands(workflows) {
|
|
|
13573
13652
|
if (opts.license !== "MIT") die("--license currently supports only MIT");
|
|
13574
13653
|
const year = Number(opts.licenseYear);
|
|
13575
13654
|
if (!Number.isSafeInteger(year)) die("--license-year must be an integer");
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
|
|
13579
|
-
|
|
13580
|
-
|
|
13655
|
+
let source;
|
|
13656
|
+
if (opts.from) source = { kind: "file", document_json: readPackageSource(opts.from) };
|
|
13657
|
+
else {
|
|
13658
|
+
const selected = await resolveWorkflowExportSelection(api, workflow2, opts);
|
|
13659
|
+
source = {
|
|
13660
|
+
kind: "owned_workflow",
|
|
13661
|
+
workflow_id: selected.workflow.id,
|
|
13662
|
+
options: selected.options
|
|
13663
|
+
};
|
|
13664
|
+
}
|
|
13581
13665
|
const proof = await api.preparePublication({
|
|
13582
13666
|
prepare_request_id: crypto.randomUUID(),
|
|
13583
13667
|
source,
|
|
@@ -13591,22 +13675,26 @@ function registerPackageCommands(workflows) {
|
|
|
13591
13675
|
}
|
|
13592
13676
|
}
|
|
13593
13677
|
);
|
|
13594
|
-
|
|
13678
|
+
withList(
|
|
13595
13679
|
workflows.command("publications").description("List your public workflow snapshots").option("--workflow <id>", "filter by owned source workflow ID")
|
|
13596
13680
|
).action(async (opts) => {
|
|
13597
|
-
const result = await
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13681
|
+
const result = await fetchList(
|
|
13682
|
+
opts,
|
|
13683
|
+
(page) => client(opts).listPublications(opts.workflow, page),
|
|
13684
|
+
(item) => item.candidate_id
|
|
13685
|
+
);
|
|
13686
|
+
printList(result, opts, (items) => {
|
|
13687
|
+
if (!items.length) return console.log("no public snapshots");
|
|
13601
13688
|
table([
|
|
13602
13689
|
["NAME", "STATUS", "PUBLISHED", "SNAPSHOT"],
|
|
13603
|
-
...
|
|
13690
|
+
...items.map((item) => [
|
|
13604
13691
|
item.metadata.display_name,
|
|
13605
13692
|
item.owner_state === "published" && item.host_state === "active" ? "hosted" : "unavailable",
|
|
13606
13693
|
new Date(item.published_at).toISOString(),
|
|
13607
13694
|
item.snapshot_id
|
|
13608
13695
|
])
|
|
13609
13696
|
]);
|
|
13697
|
+
});
|
|
13610
13698
|
});
|
|
13611
13699
|
withCommon(
|
|
13612
13700
|
workflows.command("unpublish <public-url-or-id>").description("Withdraw a hosted public snapshot").option("-y, --yes", "skip the explicit withdrawal prompt")
|
|
@@ -13633,44 +13721,16 @@ function registerPackageCommands(workflows) {
|
|
|
13633
13721
|
else console.log(`restored ${result.receipt.public_url}`);
|
|
13634
13722
|
});
|
|
13635
13723
|
withCommon(
|
|
13636
|
-
|
|
13637
|
-
"
|
|
13638
|
-
|
|
13639
|
-
|
|
13640
|
-
|
|
13641
|
-
|
|
13642
|
-
|
|
13643
|
-
|
|
13644
|
-
const api = client(opts);
|
|
13645
|
-
const all = await listAll((page) => api.listWorkflows(page));
|
|
13646
|
-
const workflow2 = pickWorkflow(all, ref);
|
|
13647
|
-
const sourceProject = opts.project ? await resolveProject(api, opts.project) : void 0;
|
|
13648
|
-
if ((opts.schedule.length || opts.projectRouting) && !sourceProject)
|
|
13649
|
-
die("--project is required with --schedule or --project-routing");
|
|
13650
|
-
const tiers = [];
|
|
13651
|
-
for (const selector of opts.tier) {
|
|
13652
|
-
const separator = selector.lastIndexOf("=");
|
|
13653
|
-
if (separator < 1) die(`invalid --tier "${selector}"; expected <state-ref>=<tier>`);
|
|
13654
|
-
const tier = selector.slice(separator + 1);
|
|
13655
|
-
if (!["smartest", "balanced", "cheapest"].includes(tier))
|
|
13656
|
-
die(`invalid tier "${tier}"; expected smartest, balanced, or cheapest`);
|
|
13657
|
-
tiers.push({
|
|
13658
|
-
state_id: await resolveExportState(api, selector.slice(0, separator)),
|
|
13659
|
-
tier,
|
|
13660
|
-
project_scoped: Boolean(opts.projectRouting)
|
|
13661
|
-
});
|
|
13662
|
-
}
|
|
13663
|
-
const authoring = opts.inputs ? readStrictObject(opts.inputs, "inputs file") : void 0;
|
|
13664
|
-
const document = await api.exportWorkflowPackage(workflow2.id, {
|
|
13665
|
-
source_project_id: sourceProject?.id,
|
|
13666
|
-
schedule_ids: opts.schedule,
|
|
13667
|
-
tiers,
|
|
13668
|
-
authoring
|
|
13669
|
-
});
|
|
13670
|
-
process.stdout.write(`${canonicalWorkflowPackage(document)}
|
|
13724
|
+
withWorkflowExportSelectors(
|
|
13725
|
+
workflows.command("export <workflow-id-or-unambiguous-name>").description("Export a canonical workflow package JSON document")
|
|
13726
|
+
)
|
|
13727
|
+
).action(async (ref, opts) => {
|
|
13728
|
+
const api = client(opts);
|
|
13729
|
+
const selected = await resolveWorkflowExportSelection(api, ref, opts);
|
|
13730
|
+
const document = await api.exportWorkflowPackage(selected.workflow.id, selected.options);
|
|
13731
|
+
process.stdout.write(`${canonicalWorkflowPackage(document)}
|
|
13671
13732
|
`);
|
|
13672
|
-
|
|
13673
|
-
);
|
|
13733
|
+
});
|
|
13674
13734
|
withCommon(
|
|
13675
13735
|
workflows.command("validate <file>").description("Validate a workflow package file (use - for stdin)").option("--public", "apply the stricter text-only public-hosting policy")
|
|
13676
13736
|
).action(async (path2, opts) => {
|
|
@@ -13739,39 +13799,45 @@ function registerPackageCommands(workflows) {
|
|
|
13739
13799
|
);
|
|
13740
13800
|
const apiBase = normalizeUrl(resolveUrl(opts));
|
|
13741
13801
|
const api = client(opts);
|
|
13742
|
-
|
|
13743
|
-
let raw;
|
|
13802
|
+
let loadSource;
|
|
13744
13803
|
let plan;
|
|
13745
13804
|
let planPath = opts.plan;
|
|
13746
13805
|
if (planPath) {
|
|
13747
13806
|
const saved = readWorkflowPackagePlan(planPath);
|
|
13748
13807
|
if (normalizeUrl(saved.api_base) !== apiBase)
|
|
13749
13808
|
die(`plan belongs to ${saved.api_base}, not ${apiBase}`);
|
|
13750
|
-
if (saved.remote_source) {
|
|
13751
|
-
if (source.kind !== "remote")
|
|
13752
|
-
die("saved remote plan must be installed from its public source URL");
|
|
13753
|
-
const fetched = await fetchPublicWorkflowPackage(path2);
|
|
13754
|
-
if (fetched.sourceUrl !== saved.remote_source.url || workflowPackageBytesSha256(fetched.raw) !== saved.remote_source.bytes_sha256)
|
|
13755
|
-
die("public source changed since preview; create and review a fresh plan");
|
|
13756
|
-
raw = fetched.raw;
|
|
13757
|
-
} else if (saved.plan.source?.kind === "hosted_publication") {
|
|
13758
|
-
if (source.kind !== "hosted" || source.snapshotId !== saved.plan.source.snapshot_id)
|
|
13759
|
-
die("saved hosted plan belongs to a different public snapshot");
|
|
13760
|
-
await api.getPublicSnapshotStatus(source.snapshotId);
|
|
13761
|
-
raw = canonicalWorkflowPackage(saved.plan.document);
|
|
13762
|
-
} else {
|
|
13763
|
-
if (source.kind !== "file") die("saved file plan must be installed from its file");
|
|
13764
|
-
raw = readPackageSource(path2);
|
|
13765
|
-
}
|
|
13766
|
-
const { document } = await packageDocument(raw);
|
|
13767
|
-
if (saved.document_digest !== document.digest || saved.plan.document_digest !== document.digest || canonicalWorkflowPackage(saved.plan.document) !== canonicalWorkflowPackage(document))
|
|
13768
|
-
die(
|
|
13769
|
-
`package digest ${document.digest} does not match saved plan ${saved.document_digest}`
|
|
13770
|
-
);
|
|
13771
13809
|
plan = saved.plan;
|
|
13810
|
+
loadSource = async () => {
|
|
13811
|
+
const source = publicationSource(path2, apiBase);
|
|
13812
|
+
let raw;
|
|
13813
|
+
if (saved.remote_source) {
|
|
13814
|
+
if (source.kind !== "remote")
|
|
13815
|
+
die("saved remote plan must be installed from its public source URL");
|
|
13816
|
+
const fetched = await fetchPublicWorkflowPackage(path2);
|
|
13817
|
+
if (fetched.sourceUrl !== saved.remote_source.url || workflowPackageBytesSha256(fetched.raw) !== saved.remote_source.bytes_sha256)
|
|
13818
|
+
die("public source changed since preview; create and review a fresh plan");
|
|
13819
|
+
raw = fetched.raw;
|
|
13820
|
+
} else if (saved.plan.source?.kind === "hosted_publication") {
|
|
13821
|
+
if (source.kind !== "hosted" || source.snapshotId !== saved.plan.source.snapshot_id)
|
|
13822
|
+
die("saved hosted plan belongs to a different public snapshot");
|
|
13823
|
+
await api.getPublicSnapshotStatus(source.snapshotId);
|
|
13824
|
+
raw = canonicalWorkflowPackage(saved.plan.document);
|
|
13825
|
+
} else {
|
|
13826
|
+
if (source.kind !== "file") die("saved file plan must be installed from its file");
|
|
13827
|
+
raw = readPackageSource(path2);
|
|
13828
|
+
}
|
|
13829
|
+
const { document } = await packageDocument(raw);
|
|
13830
|
+
if (saved.document_digest !== document.digest || saved.plan.document_digest !== document.digest || canonicalWorkflowPackage(saved.plan.document) !== canonicalWorkflowPackage(document))
|
|
13831
|
+
die(
|
|
13832
|
+
`package digest ${document.digest} does not match saved plan ${saved.document_digest}`
|
|
13833
|
+
);
|
|
13834
|
+
return raw;
|
|
13835
|
+
};
|
|
13772
13836
|
} else {
|
|
13837
|
+
const source = publicationSource(path2, apiBase);
|
|
13773
13838
|
const choices = opts.choices ? readStrictObject(opts.choices, "choices file") : void 0;
|
|
13774
13839
|
let remoteSource;
|
|
13840
|
+
let raw;
|
|
13775
13841
|
if (source.kind === "hosted") {
|
|
13776
13842
|
plan = await api.prepareHostedWorkflowPackage(source.snapshotId, choices);
|
|
13777
13843
|
raw = canonicalWorkflowPackage(plan.document);
|
|
@@ -13791,6 +13857,7 @@ function registerPackageCommands(workflows) {
|
|
|
13791
13857
|
planPath = source.kind === "file" ? `${path2}.plan.json` : "workflow-publication.plan.json";
|
|
13792
13858
|
saveWorkflowPackagePlan(planPath, apiBase, plan, remoteSource);
|
|
13793
13859
|
console.error(`saved retryable signed plan to ${planPath}`);
|
|
13860
|
+
loadSource = async () => raw;
|
|
13794
13861
|
}
|
|
13795
13862
|
if (opts.confirm !== void 0 && opts.confirm !== plan.plan_digest)
|
|
13796
13863
|
die(`confirmation digest does not match reviewed plan ${plan.plan_digest}`);
|
|
@@ -13802,7 +13869,7 @@ function registerPackageCommands(workflows) {
|
|
|
13802
13869
|
process.stderr.write(`${formatWorkflowPackageReview(plan)}
|
|
13803
13870
|
`);
|
|
13804
13871
|
}
|
|
13805
|
-
const receipt = await recoverOrInstall(api,
|
|
13872
|
+
const receipt = await recoverOrInstall(api, loadSource, plan);
|
|
13806
13873
|
if (opts.json) printJson(receipt);
|
|
13807
13874
|
else {
|
|
13808
13875
|
console.log(`installed workflow package (${receipt.id})`);
|