@storewright/cli 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/VERSION +1 -0
- package/bin/storewright.mjs +62 -0
- package/contracts/action-registry.json +175 -0
- package/contracts/capability-registry.json +63 -0
- package/contracts/workflow-manifest.json +207 -0
- package/lib/cli/storewright-cli.mjs +259 -0
- package/lib/internal/launch-envelope.mjs +223 -0
- package/lib/internal/multi-agent-contracts.mjs +137 -0
- package/lib/internal/operation-ledger.mjs +190 -0
- package/lib/internal/pricing/default-preview-pricing.mjs +181 -0
- package/lib/internal/run-state-helpers.mjs +313 -0
- package/lib/internal/shopify-operation-adapter.mjs +456 -0
- package/package.json +38 -0
- package/schemas/action-registry.schema.json +11 -0
- package/schemas/agent-report.schema.json +14 -0
- package/schemas/approval-grant.schema.json +16 -0
- package/schemas/base-theme-report.schema.json +25 -0
- package/schemas/brand-identity.schema.json +142 -0
- package/schemas/capability-registry.schema.json +11 -0
- package/schemas/competitor-audit.schema.json +38 -0
- package/schemas/design-direction.schema.json +64 -0
- package/schemas/external-operation.schema.json +34 -0
- package/schemas/intake-blocked-report.schema.json +76 -0
- package/schemas/launch-envelope.schema.json +25 -0
- package/schemas/launch-readiness.schema.json +73 -0
- package/schemas/media-file-inspection-report.schema.json +223 -0
- package/schemas/media-manifest.schema.json +84 -0
- package/schemas/merchandising-brief.schema.json +27 -0
- package/schemas/normalized-product-catalog.schema.json +42 -0
- package/schemas/product-content-generation-input.schema.json +40 -0
- package/schemas/product-content-generation-output.schema.json +43 -0
- package/schemas/raw-product-candidates.schema.json +32 -0
- package/schemas/shopify-access-preflight-report.schema.json +213 -0
- package/schemas/shopify-content-sync-report.schema.json +190 -0
- package/schemas/shopify-media-map.schema.json +87 -0
- package/schemas/shopify-media-upload-report.schema.json +96 -0
- package/schemas/shopify-operation-request.schema.json +81 -0
- package/schemas/shopify-preflight-report.schema.json +187 -0
- package/schemas/store-blueprint.schema.json +112 -0
- package/schemas/store-content-generation-output.schema.json +102 -0
- package/schemas/store-intake.schema.json +205 -0
- package/schemas/store-ops-plan.schema.json +82 -0
- package/schemas/storefront-preview-review.schema.json +227 -0
- package/schemas/supplier-access-report.schema.json +36 -0
- package/schemas/supplier-extraction-report.schema.json +185 -0
- package/schemas/theme-build-report.schema.json +43 -0
- package/schemas/theme-code-change-summary.schema.json +65 -0
- package/schemas/theme-plan.schema.json +26 -0
- package/schemas/theme-push-report.schema.json +151 -0
- package/schemas/theme-workspace-validation-report.schema.json +61 -0
- package/schemas/workflow-manifest.schema.json +29 -0
- package/scripts/audit-run-state.mjs +472 -0
- package/scripts/execute-shopify-operation.mjs +190 -0
- package/scripts/generate-image-assets-openai.mjs +342 -0
- package/scripts/generate-media-assets.mjs +121 -0
- package/scripts/init-run-state.mjs +69 -0
- package/scripts/inspect-media-files.mjs +334 -0
- package/scripts/prepare-launch-envelope.mjs +47 -0
- package/scripts/shopify-access-preflight.mjs +432 -0
- package/scripts/upload-shopify-media.mjs +831 -0
- package/scripts/validate-agent-report.mjs +46 -0
- package/scripts/validate-artifact.mjs +196 -0
- package/scripts/validate-launch-envelope.mjs +50 -0
- package/scripts/validate-registries.mjs +50 -0
- package/scripts/validate-workflow-manifest.mjs +38 -0
- package/scripts/version.mjs +192 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
|
|
8
|
+
const defaultDimensionThreshold = 300;
|
|
9
|
+
|
|
10
|
+
function parseArgs(argv) {
|
|
11
|
+
const args = {};
|
|
12
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
13
|
+
const arg = argv[index];
|
|
14
|
+
if (arg === "--run-dir") {
|
|
15
|
+
args.runDir = argv[index + 1];
|
|
16
|
+
index += 1;
|
|
17
|
+
} else if (arg === "--supplier-extraction-report" || arg === "--supplier-extraction") {
|
|
18
|
+
args.supplierExtractionPath = argv[index + 1];
|
|
19
|
+
index += 1;
|
|
20
|
+
} else if (arg === "--media-manifest" || arg === "--manifest") {
|
|
21
|
+
args.mediaManifestPath = argv[index + 1];
|
|
22
|
+
index += 1;
|
|
23
|
+
} else if (arg === "--output-report") {
|
|
24
|
+
args.outputReportPath = argv[index + 1];
|
|
25
|
+
index += 1;
|
|
26
|
+
} else if (arg === "--dimension-threshold") {
|
|
27
|
+
args.dimensionThreshold = Number.parseInt(argv[index + 1], 10);
|
|
28
|
+
index += 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return args;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveRunPath(runDir, filePath) {
|
|
35
|
+
if (!filePath || isAbsolute(filePath)) return filePath;
|
|
36
|
+
return join(runDir, filePath);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function readJsonIfPresent(filePath) {
|
|
40
|
+
if (!filePath) return null;
|
|
41
|
+
return JSON.parse(await readFile(filePath, "utf8"));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function sourceArtifactName(filePath, fallback) {
|
|
45
|
+
return filePath?.split("/").at(-1) ?? fallback;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function collectSupplierAssets(report, sourceArtifact) {
|
|
49
|
+
const assets = [];
|
|
50
|
+
for (const item of report?.supplierExtraction?.items ?? []) {
|
|
51
|
+
const evidence = item.localMediaEvidence ?? [];
|
|
52
|
+
if (evidence.length > 0) {
|
|
53
|
+
evidence.forEach((media, index) => {
|
|
54
|
+
assets.push({
|
|
55
|
+
assetRef: `${item.candidateId ?? "candidate"}:${index + 1}`,
|
|
56
|
+
candidateId: item.candidateId,
|
|
57
|
+
sourceArtifact,
|
|
58
|
+
sourceUrl: media.sourceUrl,
|
|
59
|
+
localPath: media.localPath,
|
|
60
|
+
declaredBytes: media.bytes
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const [index, localPath] of (item.localMediaPaths ?? []).entries()) {
|
|
67
|
+
assets.push({
|
|
68
|
+
assetRef: `${item.candidateId ?? "candidate"}:${index + 1}`,
|
|
69
|
+
candidateId: item.candidateId,
|
|
70
|
+
sourceArtifact,
|
|
71
|
+
sourceUrl: item.imageSignals?.[index],
|
|
72
|
+
localPath
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return assets;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function collectManifestAssets(manifest, sourceArtifact) {
|
|
80
|
+
return (manifest?.media?.assets ?? []).map((asset, index) => ({
|
|
81
|
+
assetRef: asset.assetId ?? `manifest:${index + 1}`,
|
|
82
|
+
candidateId: asset.candidateId,
|
|
83
|
+
sourceArtifact,
|
|
84
|
+
sourceUrl: asset.provenance?.sourceUrl,
|
|
85
|
+
localPath: asset.localPath,
|
|
86
|
+
declaredBytes: asset.provenance?.bytes
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function sha256(bytes) {
|
|
91
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readUInt24LE(buffer, offset) {
|
|
95
|
+
return buffer[offset] + (buffer[offset + 1] << 8) + (buffer[offset + 2] << 16);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function inspectPng(buffer) {
|
|
99
|
+
const signature = "89504e470d0a1a0a";
|
|
100
|
+
if (buffer.length < 24 || buffer.subarray(0, 8).toString("hex") !== signature) return null;
|
|
101
|
+
return {
|
|
102
|
+
detectedMimeType: "image/png",
|
|
103
|
+
width: buffer.readUInt32BE(16),
|
|
104
|
+
height: buffer.readUInt32BE(20)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function inspectJpeg(buffer) {
|
|
109
|
+
if (buffer.length < 4 || buffer[0] !== 0xff || buffer[1] !== 0xd8) return null;
|
|
110
|
+
let offset = 2;
|
|
111
|
+
while (offset + 9 < buffer.length) {
|
|
112
|
+
if (buffer[offset] !== 0xff) {
|
|
113
|
+
offset += 1;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const marker = buffer[offset + 1];
|
|
117
|
+
const length = buffer.readUInt16BE(offset + 2);
|
|
118
|
+
const isStartOfFrame = marker >= 0xc0 && marker <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marker);
|
|
119
|
+
if (isStartOfFrame) {
|
|
120
|
+
return {
|
|
121
|
+
detectedMimeType: "image/jpeg",
|
|
122
|
+
height: buffer.readUInt16BE(offset + 5),
|
|
123
|
+
width: buffer.readUInt16BE(offset + 7)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (length < 2) break;
|
|
127
|
+
offset += 2 + length;
|
|
128
|
+
}
|
|
129
|
+
return { detectedMimeType: "image/jpeg" };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function inspectWebp(buffer) {
|
|
133
|
+
if (
|
|
134
|
+
buffer.length < 30 ||
|
|
135
|
+
buffer.subarray(0, 4).toString("ascii") !== "RIFF" ||
|
|
136
|
+
buffer.subarray(8, 12).toString("ascii") !== "WEBP"
|
|
137
|
+
) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const chunk = buffer.subarray(12, 16).toString("ascii");
|
|
142
|
+
if (chunk === "VP8X" && buffer.length >= 30) {
|
|
143
|
+
return {
|
|
144
|
+
detectedMimeType: "image/webp",
|
|
145
|
+
width: readUInt24LE(buffer, 24) + 1,
|
|
146
|
+
height: readUInt24LE(buffer, 27) + 1
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (chunk === "VP8 " && buffer.length >= 30 && buffer[23] === 0x9d && buffer[24] === 0x01 && buffer[25] === 0x2a) {
|
|
150
|
+
return {
|
|
151
|
+
detectedMimeType: "image/webp",
|
|
152
|
+
width: buffer.readUInt16LE(26) & 0x3fff,
|
|
153
|
+
height: buffer.readUInt16LE(28) & 0x3fff
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (chunk === "VP8L" && buffer.length >= 25 && buffer[20] === 0x2f) {
|
|
157
|
+
const bits = buffer.readUInt32LE(21);
|
|
158
|
+
return {
|
|
159
|
+
detectedMimeType: "image/webp",
|
|
160
|
+
width: (bits & 0x3fff) + 1,
|
|
161
|
+
height: ((bits >> 14) & 0x3fff) + 1
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { detectedMimeType: "image/webp" };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function inspectImageHeader(buffer) {
|
|
168
|
+
return inspectPng(buffer) ?? inspectJpeg(buffer) ?? inspectWebp(buffer) ?? {};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function isPlatformOrShopAssetUrl(sourceUrl) {
|
|
172
|
+
if (!sourceUrl) return false;
|
|
173
|
+
return [
|
|
174
|
+
/gtms\d*\.alicdn\.com/i,
|
|
175
|
+
/\/tps\//i,
|
|
176
|
+
/-tps-\d+-\d+/i,
|
|
177
|
+
/\/s\.gif(?:$|\?)/i,
|
|
178
|
+
/avatar/i,
|
|
179
|
+
/shopmanager/i,
|
|
180
|
+
/logo/i,
|
|
181
|
+
/\/tfs\//i
|
|
182
|
+
].some((pattern) => pattern.test(sourceUrl));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function addFinding(asset, code, evidence) {
|
|
186
|
+
asset.findingCodes.push(code);
|
|
187
|
+
asset.findings.push({ code, evidence });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function inspectAsset(asset, { runDir, dimensionThreshold }) {
|
|
191
|
+
const inspected = {
|
|
192
|
+
assetRef: asset.assetRef,
|
|
193
|
+
...(asset.candidateId ? { candidateId: asset.candidateId } : {}),
|
|
194
|
+
...(asset.sourceArtifact ? { sourceArtifact: asset.sourceArtifact } : {}),
|
|
195
|
+
...(asset.sourceUrl ? { sourceUrl: asset.sourceUrl } : {}),
|
|
196
|
+
localPath: asset.localPath,
|
|
197
|
+
fileExists: false,
|
|
198
|
+
findingCodes: [],
|
|
199
|
+
findings: []
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
if (!asset.sourceUrl) {
|
|
203
|
+
addFinding(inspected, "missing-source-url", "No sourceUrl was present in the input artifact.");
|
|
204
|
+
} else if (isPlatformOrShopAssetUrl(asset.sourceUrl)) {
|
|
205
|
+
addFinding(inspected, "platform-or-shop-asset-url", asset.sourceUrl);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const absolutePath = resolveRunPath(runDir, asset.localPath);
|
|
209
|
+
if (!absolutePath || !existsSync(absolutePath)) {
|
|
210
|
+
addFinding(inspected, "missing-local-file", asset.localPath ?? "");
|
|
211
|
+
return inspected;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const bytes = await readFile(absolutePath);
|
|
215
|
+
const header = inspectImageHeader(bytes);
|
|
216
|
+
inspected.fileExists = true;
|
|
217
|
+
inspected.bytes = bytes.length;
|
|
218
|
+
inspected.sha256 = sha256(bytes);
|
|
219
|
+
if (header.detectedMimeType) inspected.detectedMimeType = header.detectedMimeType;
|
|
220
|
+
if (Number.isFinite(header.width)) inspected.width = header.width;
|
|
221
|
+
if (Number.isFinite(header.height)) inspected.height = header.height;
|
|
222
|
+
|
|
223
|
+
if (!header.detectedMimeType) {
|
|
224
|
+
addFinding(inspected, "unsupported-image-header", asset.localPath);
|
|
225
|
+
}
|
|
226
|
+
if (
|
|
227
|
+
Number.isFinite(header.width) &&
|
|
228
|
+
Number.isFinite(header.height) &&
|
|
229
|
+
(header.width < dimensionThreshold || header.height < dimensionThreshold)
|
|
230
|
+
) {
|
|
231
|
+
addFinding(inspected, "below-dimension-threshold", `${header.width}x${header.height} < ${dimensionThreshold}x${dimensionThreshold}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return inspected;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function applyDuplicateFindings(assets) {
|
|
238
|
+
const byHash = new Map();
|
|
239
|
+
for (const asset of assets) {
|
|
240
|
+
if (!asset.sha256) continue;
|
|
241
|
+
const group = byHash.get(asset.sha256) ?? [];
|
|
242
|
+
group.push(asset);
|
|
243
|
+
byHash.set(asset.sha256, group);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const duplicateHashGroups = [];
|
|
247
|
+
for (const [hash, group] of byHash) {
|
|
248
|
+
const candidateIds = [...new Set(group.map((asset) => asset.candidateId).filter(Boolean))];
|
|
249
|
+
if (group.length < 2 || candidateIds.length < 2) continue;
|
|
250
|
+
const groupId = `duplicate-${duplicateHashGroups.length + 1}`;
|
|
251
|
+
duplicateHashGroups.push({
|
|
252
|
+
groupId,
|
|
253
|
+
sha256: hash,
|
|
254
|
+
assetRefs: group.map((asset) => asset.assetRef),
|
|
255
|
+
candidateIds
|
|
256
|
+
});
|
|
257
|
+
for (const asset of group) {
|
|
258
|
+
asset.duplicateHashGroupId = groupId;
|
|
259
|
+
addFinding(asset, "duplicate-across-candidates", groupId);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return duplicateHashGroups;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function inspectMediaFiles({
|
|
267
|
+
runDir,
|
|
268
|
+
supplierExtractionPath,
|
|
269
|
+
mediaManifestPath,
|
|
270
|
+
outputReportPath,
|
|
271
|
+
dimensionThreshold = defaultDimensionThreshold
|
|
272
|
+
}) {
|
|
273
|
+
if (!runDir) throw new Error("Missing --run-dir argument");
|
|
274
|
+
if (!supplierExtractionPath && !mediaManifestPath) {
|
|
275
|
+
throw new Error("Missing media input artifact: pass --supplier-extraction-report or --media-manifest");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const supplierExtraction = await readJsonIfPresent(supplierExtractionPath);
|
|
279
|
+
const mediaManifest = await readJsonIfPresent(mediaManifestPath);
|
|
280
|
+
const sourceInputArtifacts = [
|
|
281
|
+
...(supplierExtractionPath ? [supplierExtractionPath] : []),
|
|
282
|
+
...(mediaManifestPath ? [mediaManifestPath] : [])
|
|
283
|
+
];
|
|
284
|
+
const runId = supplierExtraction?.runId ?? mediaManifest?.runId ?? "unknown";
|
|
285
|
+
const inputAssets = [
|
|
286
|
+
...collectSupplierAssets(supplierExtraction, sourceArtifactName(supplierExtractionPath, "supplier-extraction-report.json")),
|
|
287
|
+
...collectManifestAssets(mediaManifest, sourceArtifactName(mediaManifestPath, "media-manifest.json"))
|
|
288
|
+
];
|
|
289
|
+
const assets = [];
|
|
290
|
+
for (const asset of inputAssets) {
|
|
291
|
+
assets.push(await inspectAsset(asset, { runDir, dimensionThreshold }));
|
|
292
|
+
}
|
|
293
|
+
const duplicateHashGroups = applyDuplicateFindings(assets);
|
|
294
|
+
|
|
295
|
+
const report = {
|
|
296
|
+
schemaVersion: "1.0.0",
|
|
297
|
+
runId,
|
|
298
|
+
authoring: {
|
|
299
|
+
mode: "worker-authored",
|
|
300
|
+
createdBy: "media-file-inspection-adapter",
|
|
301
|
+
sourceInputArtifacts
|
|
302
|
+
},
|
|
303
|
+
mediaFileInspection: {
|
|
304
|
+
dimensionThreshold,
|
|
305
|
+
summary: {
|
|
306
|
+
assetCount: assets.length,
|
|
307
|
+
missingFileCount: assets.filter((asset) => asset.findingCodes.includes("missing-local-file")).length,
|
|
308
|
+
duplicateHashGroupCount: duplicateHashGroups.length,
|
|
309
|
+
findingCount: assets.reduce((total, asset) => total + asset.findingCodes.length, 0)
|
|
310
|
+
},
|
|
311
|
+
duplicateHashGroups,
|
|
312
|
+
assets
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const resolvedOutputPath = outputReportPath ?? join(runDir, "media-file-inspection-report.json");
|
|
317
|
+
await mkdir(dirname(resolvedOutputPath), { recursive: true });
|
|
318
|
+
await writeFile(resolvedOutputPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
319
|
+
return { outputReportPath: resolvedOutputPath, assetCount: assets.length };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function main() {
|
|
323
|
+
try {
|
|
324
|
+
const result = await inspectMediaFiles(parseArgs(process.argv.slice(2)));
|
|
325
|
+
console.log(JSON.stringify(result));
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error(error.message);
|
|
328
|
+
process.exitCode = 1;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
333
|
+
await main();
|
|
334
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { createLaunchEnvelope } from "../lib/internal/launch-envelope.mjs";
|
|
7
|
+
|
|
8
|
+
function parseArgs(argv) {
|
|
9
|
+
const options = {};
|
|
10
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
11
|
+
const arg = argv[index];
|
|
12
|
+
const value = argv[index + 1];
|
|
13
|
+
if (arg === "--input") {
|
|
14
|
+
options.inputPath = value;
|
|
15
|
+
index += 1;
|
|
16
|
+
} else if (arg === "--run-dir") {
|
|
17
|
+
options.runDir = value;
|
|
18
|
+
index += 1;
|
|
19
|
+
} else if (arg === "--package-root") {
|
|
20
|
+
options.packageRoot = value;
|
|
21
|
+
index += 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function prepareLaunchEnvelope({ packageRoot = join(import.meta.dirname, ".."), runDir, inputPath }) {
|
|
28
|
+
if (!inputPath) {
|
|
29
|
+
throw new Error("Missing --input launch envelope request JSON");
|
|
30
|
+
}
|
|
31
|
+
const request = JSON.parse(await readFile(inputPath, "utf8"));
|
|
32
|
+
return createLaunchEnvelope({ packageRoot, ...request, runDir: runDir ?? request.runDir });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function main() {
|
|
36
|
+
try {
|
|
37
|
+
const result = await prepareLaunchEnvelope(parseArgs(process.argv.slice(2)));
|
|
38
|
+
console.log(JSON.stringify(result, null, 2));
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(error.message);
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
46
|
+
await main();
|
|
47
|
+
}
|