@keystrokehq/cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS-blurb.md +123 -0
- package/LICENSE +42 -0
- package/README.md +177 -0
- package/THIRD_PARTY_NOTICES.md +16 -0
- package/bin/keystroke.mjs +107 -0
- package/dist/_manifest-JSRE3H8k.mjs +385 -0
- package/dist/agent-bundle-package-DWV6B_5q-BtV7Xycc.mjs +2344 -0
- package/dist/agent-manifest-CDnbkR2f.mjs +245 -0
- package/dist/agents-CZJGxVqV.mjs +228 -0
- package/dist/api-keys-D2lgguuY.mjs +40 -0
- package/dist/auth-DN2VusyU.mjs +59 -0
- package/dist/auth.handler-CT1BQUvu.mjs +340 -0
- package/dist/browser-qwFrUH82.mjs +24 -0
- package/dist/build-agents-BmM_AsSd-BGi9wtzt.mjs +514 -0
- package/dist/build-metadata-BWS7uhd_-DR8gJjTX.mjs +1422 -0
- package/dist/build-progress-DgYKb4hB.mjs +183 -0
- package/dist/build-tasks-CdihpudT-D5r5HUHe.mjs +91 -0
- package/dist/build-workflows-CfxBnIWh-CdYPv8w2.mjs +370 -0
- package/dist/build.handler-4799CjWH.mjs +36 -0
- package/dist/chunk-CH6r78ws.mjs +37 -0
- package/dist/clear-cache.handler-B9tqSoSM.mjs +11 -0
- package/dist/clear.handler-BTIXXPTJ.mjs +42 -0
- package/dist/clear.handler-BydlX-zE.mjs +11 -0
- package/dist/commander-DfTVqQ-3.mjs +133 -0
- package/dist/concurrency-gXn9Rw8x-DNl2YtrS.mjs +20 -0
- package/dist/connect-BUXkeH0F.mjs +43 -0
- package/dist/connect.handler-CYel9cy6.mjs +430 -0
- package/dist/constants-CPpPdSNg.mjs +8 -0
- package/dist/context-T7HZuB97.mjs +138 -0
- package/dist/credential-env-map-CI8yWHVy.mjs +28 -0
- package/dist/credential-schema-mismatch-BKo5PjcQ.mjs +76 -0
- package/dist/credentials-CvmjU0lK.mjs +171 -0
- package/dist/credentials-OfVHOtG3.mjs +151216 -0
- package/dist/current-deployment-workflow-poHt27i3.mjs +94 -0
- package/dist/current.handler-B8zKzfPp.mjs +21 -0
- package/dist/delete.handler-bAu1iXVQ.mjs +17 -0
- package/dist/deploy-7Jjls436.mjs +26 -0
- package/dist/deploy-BOPIpRWm.mjs +74 -0
- package/dist/deploy-progress-BmGUNFKg.mjs +70 -0
- package/dist/deploy.handler-BAzgiNhd.mjs +370 -0
- package/dist/detect-env-access-CwkOYeYM-D_BCZqV6.mjs +209 -0
- package/dist/diff-utils-NEfcjqxt.mjs +185 -0
- package/dist/diff.handler-Du7SY8K4.mjs +47 -0
- package/dist/dist-BkJUoBiG.mjs +1116 -0
- package/dist/dist-CUK7yBM0.mjs +308 -0
- package/dist/env-91KwMKov.mjs +140 -0
- package/dist/env.handler-BAzBuMzQ.mjs +277 -0
- package/dist/error-boundary-VL-JLfIa.mjs +34 -0
- package/dist/file-metadata-D1vm-XY2.mjs +191 -0
- package/dist/get-intrinsic-zLxwtrLK.mjs +658 -0
- package/dist/import-module-CV84H5fZ-B_CBCmb4.mjs +1747 -0
- package/dist/init-DpMCotSK.mjs +45 -0
- package/dist/init.handler-CPRnif52.mjs +585 -0
- package/dist/inspect.handler-DT_cD036.mjs +146 -0
- package/dist/integration-catalog-Bt-L3GjF.mjs +104 -0
- package/dist/integrations-DlatPK4W.mjs +79 -0
- package/dist/keystroke.d.mts +3 -0
- package/dist/keystroke.mjs +707 -0
- package/dist/layout-CbMtQ2tm.mjs +67 -0
- package/dist/list-enrichment-y-cwizLr.mjs +189 -0
- package/dist/list.handler-BTWvCyjA.mjs +52 -0
- package/dist/list.handler-CWF_Dj15.mjs +24 -0
- package/dist/list.handler-CZ6G2x_G.mjs +75 -0
- package/dist/list.handler-DWaQkJaR.mjs +51 -0
- package/dist/list.handler-DqbFcBW7.mjs +180 -0
- package/dist/list.handler-lq3ZGAn4.mjs +104 -0
- package/dist/logs-BEg9L5l8.mjs +28 -0
- package/dist/logs.handler-6hoMBzqw.mjs +35 -0
- package/dist/logs.handler-BD_dXiL1.mjs +231 -0
- package/dist/metadata-layout-GUYIUo0i-_aG2zjue.mjs +5877 -0
- package/dist/normalize-path-CojS-CgQ-DLCOvnD1.mjs +20 -0
- package/dist/options-CeaTcFxP.mjs +43 -0
- package/dist/org-xLzBtt2_.mjs +41 -0
- package/dist/output-DM4b7KgY.mjs +72 -0
- package/dist/oxc-B3KI3rf_-n9d1hKNq.mjs +119 -0
- package/dist/paused.handler-BMFm9Cff.mjs +94 -0
- package/dist/project-config-D1qsQlO7.mjs +107 -0
- package/dist/projects-CHkRE9rS.mjs +1574 -0
- package/dist/projects-Cjb7sovS.mjs +30 -0
- package/dist/read-credential-keys-77a91T8M-KA0Iw0Z1.mjs +9 -0
- package/dist/register.handler-BPCdor1_.mjs +86 -0
- package/dist/requirements.handler-DPXdSks3.mjs +201 -0
- package/dist/resolve-project-DDJ29sCF.mjs +35 -0
- package/dist/rolldown-runtime-twds-ZHy-BWWzu8VG.mjs +15 -0
- package/dist/run-polling-CAgFRdK3.mjs +20 -0
- package/dist/runs-D9hNLb9A.mjs +259 -0
- package/dist/schedule-BXx3uXwr.mjs +1142 -0
- package/dist/schema-17qMfNyI.mjs +18 -0
- package/dist/schema-display-CgmeKigW.mjs +130 -0
- package/dist/schemas-CDib1RhE.mjs +125 -0
- package/dist/skills-sync.handler-DIy8GR16.mjs +34 -0
- package/dist/skills.command-CrjI2dN9.mjs +35 -0
- package/dist/skills.handler-Bz8bJKql.mjs +9 -0
- package/dist/source-analysis-Cj-ADyu--BJQcFPCG.mjs +144 -0
- package/dist/spinner-progress-DMVwgqO9.mjs +173 -0
- package/dist/src-C0X6u_Mw.mjs +1340 -0
- package/dist/src-eHwu-Gfw.mjs +369 -0
- package/dist/status.handler-BO4nwvWn.mjs +101 -0
- package/dist/switch.handler-D_9213Vf.mjs +51 -0
- package/dist/sync-BL_Mo5st.mjs +39 -0
- package/dist/sync-keystroke-agent-skills-Kx_H7UTd.mjs +70 -0
- package/dist/sync.handler-BUFPdzWz.mjs +82 -0
- package/dist/task-B2sZMaZu.mjs +8 -0
- package/dist/task-target-build-CBeCKbu2.mjs +432 -0
- package/dist/task-target-deploy-C5X-USeR.mjs +4 -0
- package/dist/task-target-deploy-CA6elFpF-BEr4gkol.mjs +271 -0
- package/dist/task-target-deploy-runner.d.mts +3 -0
- package/dist/task-target-deploy-runner.mjs +202 -0
- package/dist/test-BHTgR3UA.mjs +698 -0
- package/dist/test.handler-BcPQ8b74.mjs +13 -0
- package/dist/trigger-artifacts-DQPbQNqC-B4yeeFBY.mjs +239 -0
- package/dist/trigger-manifest-CY7brZeg.mjs +30 -0
- package/dist/try-deploy.handler-DqybNhXx.mjs +490 -0
- package/dist/upload-CkU--iDC.mjs +207 -0
- package/dist/upload.handler-DCtiznQp.mjs +441 -0
- package/dist/utils-CywxCDM7.mjs +14 -0
- package/dist/validate.handler-DOcTaJL0.mjs +280 -0
- package/dist/workflow-build-DBQaBfnn.mjs +1819 -0
- package/dist/workflow-bundler-BPiqVscj-X1PFFAuP.mjs +167 -0
- package/dist/workflows-g9z87AJJ.mjs +799 -0
- package/dist/writer-BG8poUm3-BbXlU2kI.mjs +426 -0
- package/package.json +87 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { n as __exportAll } from "./chunk-CH6r78ws.mjs";
|
|
4
|
+
import { C as CliExitError, h as toErrorMessage, t as ui, w as InputValidationError } from "./keystroke.mjs";
|
|
5
|
+
import { d as trackProject } from "./dist-CUK7yBM0.mjs";
|
|
6
|
+
import { t as assertWorkflowProjectRoot } from "./project-config-D1qsQlO7.mjs";
|
|
7
|
+
import { a as readManifestsFromOutDir, o as readWorkflowsFromDisk, s as uploadTestBundle } from "./dist-BkJUoBiG.mjs";
|
|
8
|
+
import { t as requireWorkflowsDir } from "./resolve-project-DDJ29sCF.mjs";
|
|
9
|
+
import { a as runWorkflowBuild, t as WorkflowNotFoundError } from "./workflow-build-DBQaBfnn.mjs";
|
|
10
|
+
import { a as validateRequiredFields, n as formatValidationError, r as isUnknownSchema, t as formatMissingInputError } from "./schema-display-CgmeKigW.mjs";
|
|
11
|
+
import { r as isLocalMode } from "./env-91KwMKov.mjs";
|
|
12
|
+
import { i as requireClient, t as assertProjectConfigMatchesAuthenticatedOrg } from "./context-T7HZuB97.mjs";
|
|
13
|
+
import { t as createBuildProgress } from "./build-progress-DgYKb4hB.mjs";
|
|
14
|
+
import { t as withErrorBoundary } from "./error-boundary-VL-JLfIa.mjs";
|
|
15
|
+
import { n as sleep, t as TERMINAL_STATUSES } from "./run-polling-CAgFRdK3.mjs";
|
|
16
|
+
import * as fs from "node:fs/promises";
|
|
17
|
+
import * as path$1 from "node:path";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
//#region src/lib/format.ts
|
|
20
|
+
function formatBytes(bytes) {
|
|
21
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
22
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
23
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region ../../packages/utils/src/json-schema-validation.ts
|
|
27
|
+
const SCHEMA_CACHE_LIMIT = 256;
|
|
28
|
+
const schemaCache = /* @__PURE__ */ new Map();
|
|
29
|
+
function isObjectRecord(value) {
|
|
30
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
function toStableJson(value) {
|
|
33
|
+
if (Array.isArray(value)) return `[${value.map((item) => toStableJson(item)).join(",")}]`;
|
|
34
|
+
if (isObjectRecord(value)) return `{${Object.keys(value).sort((a, b) => a.localeCompare(b)).map((key) => `${JSON.stringify(key)}:${toStableJson(value[key])}`).join(",")}}`;
|
|
35
|
+
return JSON.stringify(value);
|
|
36
|
+
}
|
|
37
|
+
function normalizeIssues(error) {
|
|
38
|
+
return error.issues.map((issue) => ({
|
|
39
|
+
path: issue.path.length > 0 ? issue.path.map((segment) => String(segment)) : [""],
|
|
40
|
+
message: issue.message
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
function getOrCompileSchema(jsonSchema) {
|
|
44
|
+
const cacheKey = toStableJson(jsonSchema);
|
|
45
|
+
const cached = schemaCache.get(cacheKey);
|
|
46
|
+
if (cached) {
|
|
47
|
+
schemaCache.delete(cacheKey);
|
|
48
|
+
schemaCache.set(cacheKey, cached);
|
|
49
|
+
return {
|
|
50
|
+
kind: "compiled",
|
|
51
|
+
schema: cached
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const compiled = z.fromJSONSchema(jsonSchema);
|
|
56
|
+
schemaCache.set(cacheKey, compiled);
|
|
57
|
+
if (schemaCache.size > SCHEMA_CACHE_LIMIT) {
|
|
58
|
+
const oldestKey = schemaCache.keys().next().value;
|
|
59
|
+
if (oldestKey) schemaCache.delete(oldestKey);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
kind: "compiled",
|
|
63
|
+
schema: compiled
|
|
64
|
+
};
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return {
|
|
67
|
+
kind: "unsupported_schema",
|
|
68
|
+
reason: "from_json_schema_failed",
|
|
69
|
+
message: error instanceof Error ? error.message : String(error)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function compileJsonSchema(jsonSchema) {
|
|
74
|
+
if (!jsonSchema || typeof jsonSchema !== "object") return {
|
|
75
|
+
kind: "unknown_schema",
|
|
76
|
+
message: "Schema is null or undefined."
|
|
77
|
+
};
|
|
78
|
+
if (jsonSchema.type === "unknown") return {
|
|
79
|
+
kind: "unknown_schema",
|
|
80
|
+
message: "Schema is the unknown sentinel (`{ type: \"unknown\" }`)."
|
|
81
|
+
};
|
|
82
|
+
return getOrCompileSchema(jsonSchema);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validates an arbitrary payload against a JSON Schema via z.fromJSONSchema().
|
|
86
|
+
*/
|
|
87
|
+
function validateJsonSchemaPayload(params) {
|
|
88
|
+
const compiled = compileJsonSchema(params.jsonSchema);
|
|
89
|
+
if (compiled.kind === "unknown_schema" || compiled.kind === "unsupported_schema") return compiled;
|
|
90
|
+
if (compiled.kind !== "compiled") throw new Error(`Unhandled schema compilation result: ${JSON.stringify(compiled)}`);
|
|
91
|
+
const parsed = compiled.schema.safeParse(params.payload);
|
|
92
|
+
if (parsed.success) return {
|
|
93
|
+
kind: "valid",
|
|
94
|
+
validatedData: parsed.data
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
kind: "invalid_payload",
|
|
98
|
+
issues: normalizeIssues(parsed.error)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/commands/workflows/_shared/input.ts
|
|
103
|
+
async function resolveInput(options) {
|
|
104
|
+
if (options.input && options.inputFile) {
|
|
105
|
+
ui.error("Cannot specify both --input and --input-file");
|
|
106
|
+
throw new InputValidationError("Cannot specify both --input and --input-file");
|
|
107
|
+
}
|
|
108
|
+
if (options.input) return parseJsonObjectInput(options.input, "--input flag");
|
|
109
|
+
if (options.inputFile) {
|
|
110
|
+
const filePath = path$1.resolve(options.inputFile);
|
|
111
|
+
try {
|
|
112
|
+
return parseJsonObjectInput(await fs.readFile(filePath, "utf-8"), `input file ${filePath}`);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
const msg = `Failed to read input file: ${toErrorMessage(error)}`;
|
|
115
|
+
ui.error(msg);
|
|
116
|
+
throw new InputValidationError(msg);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
function parseJsonObjectInput(raw, source) {
|
|
122
|
+
let parsed;
|
|
123
|
+
try {
|
|
124
|
+
parsed = JSON.parse(raw);
|
|
125
|
+
} catch {
|
|
126
|
+
ui.error(`Invalid JSON in ${source}`);
|
|
127
|
+
throw new InputValidationError(`Invalid JSON in ${source}`);
|
|
128
|
+
}
|
|
129
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
130
|
+
const msg = `Invalid input from ${source}: expected a JSON object (e.g. {"key": "value"}), got ${Array.isArray(parsed) ? "array" : parsed === null ? "null" : typeof parsed}`;
|
|
131
|
+
ui.error(msg);
|
|
132
|
+
throw new InputValidationError(msg);
|
|
133
|
+
}
|
|
134
|
+
return parsed;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Validate workflow input in two layers:
|
|
138
|
+
* 1. Required fields check (reliable)
|
|
139
|
+
* 2. Full schema parse via shared JSON schema validator (best effort)
|
|
140
|
+
*/
|
|
141
|
+
function validateInputOrExit(workflowName, input, inputSchema) {
|
|
142
|
+
const { valid, missingRequired } = validateRequiredFields(input, inputSchema);
|
|
143
|
+
if (!valid) {
|
|
144
|
+
const msg = formatMissingInputError(workflowName, missingRequired, inputSchema);
|
|
145
|
+
ui.error(msg);
|
|
146
|
+
throw new InputValidationError(msg);
|
|
147
|
+
}
|
|
148
|
+
const result = validateJsonSchemaPayload({
|
|
149
|
+
jsonSchema: inputSchema,
|
|
150
|
+
payload: input
|
|
151
|
+
});
|
|
152
|
+
if (result.kind === "valid") return;
|
|
153
|
+
if (result.kind === "invalid_payload") {
|
|
154
|
+
const msg = formatValidationError(workflowName, result.issues, inputSchema);
|
|
155
|
+
ui.error(msg);
|
|
156
|
+
throw new InputValidationError(msg);
|
|
157
|
+
}
|
|
158
|
+
if (result.kind === "unsupported_schema") {
|
|
159
|
+
ui.warn(`Schema import failed for workflow "${workflowName}" (${result.message}). Required fields were checked, but full validation was skipped.`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (result.kind === "unknown_schema") {
|
|
163
|
+
ui.warn(`Schema for workflow "${workflowName}" is unknown. Required fields were checked, but full validation was skipped.`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
throw new Error(`Unhandled validation result: ${JSON.stringify(result)}`);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Read input schema from an existing dist manifest for fast pre-build validation.
|
|
170
|
+
*/
|
|
171
|
+
async function tryReadExistingInputSchema(workflowsDir, workflowName) {
|
|
172
|
+
const match = (await readManifestsFromOutDir(workflowsDir, workflowName))[0];
|
|
173
|
+
if (!match) return null;
|
|
174
|
+
const schema = match.manifest.workflowSchemas.input;
|
|
175
|
+
if (schema && typeof schema === "object" && !Array.isArray(schema)) return schema;
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/commands/workflows/_shared/run-helpers.ts
|
|
180
|
+
/**
|
|
181
|
+
* Resolve input and validate against existing schema if available.
|
|
182
|
+
* Returns input and workflowsDir for downstream use.
|
|
183
|
+
*/
|
|
184
|
+
async function resolveAndValidateInputForRun(options) {
|
|
185
|
+
const input = await resolveInput(options);
|
|
186
|
+
const workflowsDir = await requireWorkflowsDir(options.path);
|
|
187
|
+
trackProject(workflowsDir);
|
|
188
|
+
const existingSchema = await tryReadExistingInputSchema(workflowsDir, options.workflow);
|
|
189
|
+
if (existingSchema && !isUnknownSchema(existingSchema)) validateInputOrExit(options.workflow, input, existingSchema);
|
|
190
|
+
return {
|
|
191
|
+
input,
|
|
192
|
+
workflowsDir
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Build workflow(s) for run using the standard build pipeline, then read
|
|
197
|
+
* prepared artifacts from disk filtered by workflow ref.
|
|
198
|
+
*/
|
|
199
|
+
async function buildWorkflowsForRun(options) {
|
|
200
|
+
const progress = createBuildProgress(options.workflow);
|
|
201
|
+
try {
|
|
202
|
+
const { result, outDir } = await runWorkflowBuild({
|
|
203
|
+
workflowsDir: options.workflowsDir,
|
|
204
|
+
workflowRef: options.workflow,
|
|
205
|
+
verbose: options.verbose,
|
|
206
|
+
onProgressEvent: progress.handleEvent
|
|
207
|
+
});
|
|
208
|
+
const filtered = (await readWorkflowsFromDisk(outDir)).filter((wf) => wf.manifestData.id === options.workflow || wf.name === options.workflow);
|
|
209
|
+
if (filtered.length === 0) {
|
|
210
|
+
const availableNames = result.artifacts.map((a) => a.manifest.name);
|
|
211
|
+
throw new WorkflowNotFoundError(options.workflow, availableNames);
|
|
212
|
+
}
|
|
213
|
+
return filtered;
|
|
214
|
+
} finally {
|
|
215
|
+
progress.stop();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Validate input against the first build's input schema if available.
|
|
220
|
+
*/
|
|
221
|
+
function validateInputWithFirstBuild(options, input, builds) {
|
|
222
|
+
const inputSchema = builds[0]?.manifestData.workflowSchemas.input;
|
|
223
|
+
if (inputSchema && typeof inputSchema === "object" && !isUnknownSchema(inputSchema)) validateInputOrExit(options.workflow, input, inputSchema);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Render success message after build.
|
|
227
|
+
*/
|
|
228
|
+
function renderBuildSuccessMessage(builds) {
|
|
229
|
+
const first = builds[0];
|
|
230
|
+
if (!first) return;
|
|
231
|
+
if (builds.length > 1) ui.success(`Built ${builds.length} workflow(s) with name "${first.name}"`);
|
|
232
|
+
else ui.success(`Built ${first.name} (${formatBytes(first.bundleSize)})`);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Render completion success message (single or multi).
|
|
236
|
+
*/
|
|
237
|
+
function renderRunCompletionSuccessMessage(builds, elapsedSeconds) {
|
|
238
|
+
if (builds.length === 0) return;
|
|
239
|
+
if (builds.length > 1) ui.success(`All ${builds.length} workflow(s) completed in ${elapsedSeconds}s`);
|
|
240
|
+
else ui.success(`Workflow completed in ${elapsedSeconds}s`);
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/commands/workflows/try-deploy.handler/render.ts
|
|
244
|
+
function createRuntimeRenderState() {
|
|
245
|
+
return {
|
|
246
|
+
seenEventIds: /* @__PURE__ */ new Set(),
|
|
247
|
+
seenLogIds: /* @__PURE__ */ new Set(),
|
|
248
|
+
stepStates: /* @__PURE__ */ new Map(),
|
|
249
|
+
lastStatus: void 0
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function renderRunSnapshot(snapshot, state, verbose) {
|
|
253
|
+
if (snapshot.run.status !== state.lastStatus) {
|
|
254
|
+
ui.hint(`Run status: ${snapshot.run.status}`);
|
|
255
|
+
state.lastStatus = snapshot.run.status;
|
|
256
|
+
}
|
|
257
|
+
for (const log of snapshot.logs) {
|
|
258
|
+
if (state.seenLogIds.has(log.id)) continue;
|
|
259
|
+
state.seenLogIds.add(log.id);
|
|
260
|
+
renderRunLog(log, verbose);
|
|
261
|
+
}
|
|
262
|
+
for (const event of snapshot.events) {
|
|
263
|
+
if (state.seenEventIds.has(event.id)) continue;
|
|
264
|
+
state.seenEventIds.add(event.id);
|
|
265
|
+
renderRuntimeEvent(event, state.stepStates, verbose);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function renderRunLog(log, verbose) {
|
|
269
|
+
const message = `${`log[${log.source}:${log.level}]`} ${log.message}`;
|
|
270
|
+
ui.text(message);
|
|
271
|
+
if (verbose && log.metadata) ui.text(` metadata: ${JSON.stringify(log.metadata, null, 2)}`);
|
|
272
|
+
}
|
|
273
|
+
function renderRuntimeEvent(event, stepStates, verbose) {
|
|
274
|
+
const eventData = toObject(event.eventData);
|
|
275
|
+
if (event.eventType === "stream_chunk") {
|
|
276
|
+
const chunkCandidate = eventData?.chunk ?? eventData?.content;
|
|
277
|
+
if (typeof chunkCandidate === "string" && chunkCandidate.length > 0) {
|
|
278
|
+
ui.text(`stream> ${chunkCandidate}`);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (event.eventType.startsWith("step_")) {
|
|
283
|
+
const stepName = typeof eventData?.stepName === "string" && eventData.stepName || stepStates.get(event.correlationId)?.name || event.correlationId;
|
|
284
|
+
const current = stepStates.get(event.correlationId) ?? {
|
|
285
|
+
name: stepName,
|
|
286
|
+
status: "pending",
|
|
287
|
+
attempts: 0
|
|
288
|
+
};
|
|
289
|
+
if (event.eventType === "step_created") {
|
|
290
|
+
const next = {
|
|
291
|
+
...current,
|
|
292
|
+
name: stepName,
|
|
293
|
+
status: "pending"
|
|
294
|
+
};
|
|
295
|
+
stepStates.set(event.correlationId, next);
|
|
296
|
+
ui.hint(`Step created: ${stepName}`);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (event.eventType === "step_started") {
|
|
300
|
+
const attempts = Number(eventData?.attempt) || current.attempts + 1;
|
|
301
|
+
const next = {
|
|
302
|
+
...current,
|
|
303
|
+
name: stepName,
|
|
304
|
+
attempts,
|
|
305
|
+
status: "running"
|
|
306
|
+
};
|
|
307
|
+
stepStates.set(event.correlationId, next);
|
|
308
|
+
ui.hint(`Step running: ${stepName} (attempt ${attempts})`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (event.eventType === "step_retrying") {
|
|
312
|
+
const attempts = Number(eventData?.attempt) || current.attempts + 1;
|
|
313
|
+
const next = {
|
|
314
|
+
...current,
|
|
315
|
+
name: stepName,
|
|
316
|
+
attempts,
|
|
317
|
+
status: "retrying"
|
|
318
|
+
};
|
|
319
|
+
stepStates.set(event.correlationId, next);
|
|
320
|
+
ui.warn(`Step retrying: ${stepName} (attempt ${attempts})`);
|
|
321
|
+
if (verbose && typeof eventData?.error === "string" && eventData.error.length > 0) ui.hint(` reason: ${eventData.error}`);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (event.eventType === "step_completed") {
|
|
325
|
+
const next = {
|
|
326
|
+
...current,
|
|
327
|
+
name: stepName,
|
|
328
|
+
status: "completed"
|
|
329
|
+
};
|
|
330
|
+
stepStates.set(event.correlationId, next);
|
|
331
|
+
ui.success(`Step completed: ${stepName}`);
|
|
332
|
+
if (verbose && "result" in (eventData ?? {})) ui.text(` result: ${JSON.stringify(eventData?.result, null, 2)}`);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (event.eventType === "step_failed") {
|
|
336
|
+
const next = {
|
|
337
|
+
...current,
|
|
338
|
+
name: stepName,
|
|
339
|
+
status: "failed"
|
|
340
|
+
};
|
|
341
|
+
stepStates.set(event.correlationId, next);
|
|
342
|
+
const error = typeof eventData?.error === "string" && eventData.error.length > 0 ? eventData.error : "Unknown step error";
|
|
343
|
+
ui.error(`Step failed: ${stepName} - ${error}`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (verbose) {
|
|
348
|
+
const label = event.eventType.replaceAll("_", " ");
|
|
349
|
+
ui.hint(`Event: ${label} (${event.correlationId})`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function toObject(value) {
|
|
353
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
354
|
+
return value;
|
|
355
|
+
}
|
|
356
|
+
//#endregion
|
|
357
|
+
//#region src/commands/workflows/try-deploy.handler/poll.ts
|
|
358
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
359
|
+
async function pollForCompletion(client, runId, timeoutSeconds, verbose) {
|
|
360
|
+
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
361
|
+
const renderState = createRuntimeRenderState();
|
|
362
|
+
const runtimeState = {
|
|
363
|
+
hasWarnedEventsUnavailable: false,
|
|
364
|
+
hasWarnedLogsUnavailable: false
|
|
365
|
+
};
|
|
366
|
+
while (Date.now() < deadline) {
|
|
367
|
+
const snapshot = await getRunSnapshot(client, runId, runtimeState, verbose);
|
|
368
|
+
renderRunSnapshot(snapshot, renderState, verbose);
|
|
369
|
+
if (TERMINAL_STATUSES.has(snapshot.run.status)) return snapshot;
|
|
370
|
+
await sleep(POLL_INTERVAL_MS);
|
|
371
|
+
}
|
|
372
|
+
const snapshot = await getRunSnapshot(client, runId, runtimeState, verbose);
|
|
373
|
+
renderRunSnapshot(snapshot, renderState, verbose);
|
|
374
|
+
return snapshot;
|
|
375
|
+
}
|
|
376
|
+
async function getRunSnapshot(client, runId, runtimeState, verbose) {
|
|
377
|
+
const runResponse = await client.runs.getRun(runId);
|
|
378
|
+
let events = [];
|
|
379
|
+
let logs = [];
|
|
380
|
+
try {
|
|
381
|
+
events = (await client.runs.listEvents(runId)).events;
|
|
382
|
+
} catch (error) {
|
|
383
|
+
if (verbose && !runtimeState.hasWarnedEventsUnavailable) {
|
|
384
|
+
const message = toErrorMessage(error);
|
|
385
|
+
ui.hint("Event streaming is temporarily unavailable; continuing with status polling only.");
|
|
386
|
+
ui.hint(` reason: ${message}`);
|
|
387
|
+
runtimeState.hasWarnedEventsUnavailable = true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
logs = (await client.runs.listLogs(runId)).logs;
|
|
392
|
+
} catch (error) {
|
|
393
|
+
if (verbose && !runtimeState.hasWarnedLogsUnavailable) {
|
|
394
|
+
const message = toErrorMessage(error);
|
|
395
|
+
ui.hint("Log polling is temporarily unavailable; continuing with status/event polling.");
|
|
396
|
+
ui.hint(` reason: ${message}`);
|
|
397
|
+
runtimeState.hasWarnedLogsUnavailable = true;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
run: runResponse.run,
|
|
402
|
+
events,
|
|
403
|
+
logs
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region src/commands/workflows/try-deploy.handler/index.ts
|
|
408
|
+
var try_deploy_handler_exports = /* @__PURE__ */ __exportAll({ handleWorkflowsTryDeploy: () => handleWorkflowsTryDeploy });
|
|
409
|
+
async function handleWorkflowsTryDeploy(options, ctx) {
|
|
410
|
+
return withErrorBoundary("Try-deploy", async () => {
|
|
411
|
+
const client = requireClient(ctx);
|
|
412
|
+
const startTime = Date.now();
|
|
413
|
+
const local = isLocalMode();
|
|
414
|
+
const { input, workflowsDir } = await resolveAndValidateInputForRun(options);
|
|
415
|
+
const storagePaths = [];
|
|
416
|
+
let runError;
|
|
417
|
+
try {
|
|
418
|
+
const builds = await buildWorkflowsForRun({
|
|
419
|
+
...options,
|
|
420
|
+
workflowsDir
|
|
421
|
+
});
|
|
422
|
+
renderBuildSuccessMessage(builds);
|
|
423
|
+
validateInputWithFirstBuild(options, input, builds);
|
|
424
|
+
const projectConfig = await assertWorkflowProjectRoot(workflowsDir);
|
|
425
|
+
await assertProjectConfigMatchesAuthenticatedOrg(client, projectConfig);
|
|
426
|
+
for (const [i, build] of builds.entries()) {
|
|
427
|
+
if (builds.length > 1) ui.header(`Running "${build.name}" (${i + 1}/${builds.length})...`);
|
|
428
|
+
let storagePath;
|
|
429
|
+
if (local) {
|
|
430
|
+
storagePath = build.bundlePath;
|
|
431
|
+
if (i === 0) ui.hint("Local mode: using bundle from build output (skipping upload)");
|
|
432
|
+
} else {
|
|
433
|
+
ui.header("Uploading test bundle...");
|
|
434
|
+
storagePath = (await uploadTestBundle(client, build, false)).storagePath;
|
|
435
|
+
storagePaths.push(storagePath);
|
|
436
|
+
ui.success("Upload complete");
|
|
437
|
+
}
|
|
438
|
+
ui.header("Executing workflow...");
|
|
439
|
+
const { runId } = await client.workflows.testById({
|
|
440
|
+
workflowName: build.name,
|
|
441
|
+
storagePath,
|
|
442
|
+
exportName: build.exportName,
|
|
443
|
+
manifest: build.manifestData,
|
|
444
|
+
flowJson: build.flowData,
|
|
445
|
+
triggers: build.triggers,
|
|
446
|
+
bundleHash: build.bundleHash,
|
|
447
|
+
bundleSize: build.bundleSize,
|
|
448
|
+
args: [input],
|
|
449
|
+
organizationId: projectConfig.organizationId,
|
|
450
|
+
projectId: projectConfig.projectId
|
|
451
|
+
});
|
|
452
|
+
ui.hint(`Run ID: ${runId}`);
|
|
453
|
+
const snapshot = await pollForCompletion(client, runId, options.timeout, options.verbose);
|
|
454
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
455
|
+
if (snapshot.run.status === "completed") {
|
|
456
|
+
if (snapshot.run.output !== void 0 && snapshot.run.output !== null) {
|
|
457
|
+
ui.br();
|
|
458
|
+
ui.header("Output:");
|
|
459
|
+
ui.text(JSON.stringify(snapshot.run.output, null, 2));
|
|
460
|
+
}
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (snapshot.run.status === "failed") {
|
|
464
|
+
ui.br();
|
|
465
|
+
ui.error(`Workflow failed after ${elapsed}s`);
|
|
466
|
+
if (snapshot.run.error) ui.error(toErrorMessage(snapshot.run.error));
|
|
467
|
+
runError = snapshot.run.error ?? /* @__PURE__ */ new Error("Workflow failed");
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
ui.br();
|
|
471
|
+
ui.warn(`Workflow still ${snapshot.run.status} after ${elapsed}s (timeout: ${options.timeout}s)`);
|
|
472
|
+
ui.hint(`Check status: keystroke runs get ${runId}`);
|
|
473
|
+
runError = /* @__PURE__ */ new Error(`Workflow still ${snapshot.run.status} after timeout`);
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
if (!runError) {
|
|
477
|
+
ui.br();
|
|
478
|
+
renderRunCompletionSuccessMessage(builds, ((Date.now() - startTime) / 1e3).toFixed(1));
|
|
479
|
+
}
|
|
480
|
+
} finally {
|
|
481
|
+
if (!local) for (const sp of storagePaths) try {
|
|
482
|
+
await client.workflows.deleteTestBundle({ storagePath: sp });
|
|
483
|
+
if (options.verbose) ui.hint("Test bundle cleaned up");
|
|
484
|
+
} catch {}
|
|
485
|
+
}
|
|
486
|
+
if (runError) throw new CliExitError("Workflow execution failed");
|
|
487
|
+
}, { json: ctx.jsonMode });
|
|
488
|
+
}
|
|
489
|
+
//#endregion
|
|
490
|
+
export { validateInputOrExit as a, resolveInput as i, try_deploy_handler_exports as n, pollForCompletion as r, handleWorkflowsTryDeploy as t };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { n as __exportAll } from "./chunk-CH6r78ws.mjs";
|
|
4
|
+
import { createReadStream, promises } from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import http from "node:http";
|
|
8
|
+
import https from "node:https";
|
|
9
|
+
//#region ../../packages/workflow-sdk/src/utils/pool.ts
|
|
10
|
+
/**
|
|
11
|
+
* Executes async operations with a concurrency limit.
|
|
12
|
+
* Processes items in parallel, but never more than `limit` at a time.
|
|
13
|
+
*/
|
|
14
|
+
async function pool(items, limit, fn) {
|
|
15
|
+
if (items.length === 0) return [];
|
|
16
|
+
const effectiveLimit = Math.floor(limit);
|
|
17
|
+
if (effectiveLimit <= 0) throw new Error(`pool() limit must be a positive integer, got ${limit}`);
|
|
18
|
+
const results = [];
|
|
19
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
const operation = fn(item).then((result) => {
|
|
22
|
+
results.push(result);
|
|
23
|
+
inFlight.delete(operation);
|
|
24
|
+
});
|
|
25
|
+
inFlight.add(operation);
|
|
26
|
+
if (inFlight.size >= effectiveLimit) await Promise.race(inFlight);
|
|
27
|
+
}
|
|
28
|
+
await Promise.all(inFlight);
|
|
29
|
+
return results;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region ../../packages/workflow-sdk/src/utils/upload.ts
|
|
33
|
+
/**
|
|
34
|
+
* Presigned URL upload helpers with retry logic.
|
|
35
|
+
*
|
|
36
|
+
* Uses raw fetch (not ky) because presigned URLs bypass the API —
|
|
37
|
+
* no auth headers or base URL prefix needed.
|
|
38
|
+
*/
|
|
39
|
+
var upload_exports = /* @__PURE__ */ __exportAll({
|
|
40
|
+
batchUpload: () => batchUpload,
|
|
41
|
+
uploadToPresignedUrl: () => uploadToPresignedUrl
|
|
42
|
+
});
|
|
43
|
+
/** HTTP status codes that are safe to retry (server errors + rate limiting) */
|
|
44
|
+
const RETRYABLE_STATUS_CODES = new Set([
|
|
45
|
+
408,
|
|
46
|
+
429,
|
|
47
|
+
500,
|
|
48
|
+
502,
|
|
49
|
+
503,
|
|
50
|
+
504
|
|
51
|
+
]);
|
|
52
|
+
const DEFAULT_UPLOAD_RETRY = {
|
|
53
|
+
maxAttempts: 3,
|
|
54
|
+
baseDelayMs: 1e3,
|
|
55
|
+
maxDelayMs: 1e4
|
|
56
|
+
};
|
|
57
|
+
function resolveLocalUploadTarget(url) {
|
|
58
|
+
if (path.isAbsolute(url)) return url;
|
|
59
|
+
if (url.startsWith("file://")) return fileURLToPath(url);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function isRetryableStatus(status) {
|
|
63
|
+
return RETRYABLE_STATUS_CODES.has(status);
|
|
64
|
+
}
|
|
65
|
+
function isRetryableError(error) {
|
|
66
|
+
if (error instanceof TypeError) return true;
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const DEFAULT_UPLOAD_CONCURRENCY = 5;
|
|
70
|
+
/**
|
|
71
|
+
* Uploads multiple bundles to presigned URLs in parallel with retry.
|
|
72
|
+
*
|
|
73
|
+
* This is the shared implementation behind `client.bundles.upload()`.
|
|
74
|
+
* The resource call resolves presigned URLs, then passes URL map + items
|
|
75
|
+
* here for the actual uploads.
|
|
76
|
+
*/
|
|
77
|
+
async function batchUpload(items, urls, options) {
|
|
78
|
+
const concurrency = options?.concurrency ?? DEFAULT_UPLOAD_CONCURRENCY;
|
|
79
|
+
const retryConfig = options?.retryConfig;
|
|
80
|
+
return { results: await pool(items, concurrency, async (item) => {
|
|
81
|
+
const urlInfo = urls[item.name];
|
|
82
|
+
if (!urlInfo) return {
|
|
83
|
+
kind: "failure",
|
|
84
|
+
name: item.name,
|
|
85
|
+
error: `No presigned URL returned for ${item.name}`
|
|
86
|
+
};
|
|
87
|
+
try {
|
|
88
|
+
await uploadToPresignedUrl(urlInfo.url, resolveUploadBody(item), retryConfig, urlInfo.contentType);
|
|
89
|
+
return {
|
|
90
|
+
kind: "success",
|
|
91
|
+
name: item.name,
|
|
92
|
+
storagePath: urlInfo.key
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return {
|
|
96
|
+
kind: "failure",
|
|
97
|
+
name: item.name,
|
|
98
|
+
error: `Upload failed: ${error instanceof Error ? error.message : String(error)}`
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}) };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Uploads a bundle to a presigned URL with exponential backoff retry.
|
|
105
|
+
* Retries on transient failures (5xx, 408, 429, network errors).
|
|
106
|
+
* Non-retryable errors (4xx except 408/429) throw immediately.
|
|
107
|
+
*/
|
|
108
|
+
async function uploadToPresignedUrl(url, bundle, config = DEFAULT_UPLOAD_RETRY, contentType) {
|
|
109
|
+
const localUploadTarget = resolveLocalUploadTarget(url);
|
|
110
|
+
if (localUploadTarget) {
|
|
111
|
+
await promises.mkdir(path.dirname(localUploadTarget), { recursive: true });
|
|
112
|
+
if ("path" in bundle) await promises.copyFile(bundle.path, localUploadTarget);
|
|
113
|
+
else await promises.writeFile(localUploadTarget, bundle);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const headers = await resolveUploadHeaders(bundle, contentType);
|
|
117
|
+
let lastError;
|
|
118
|
+
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
119
|
+
try {
|
|
120
|
+
const response = await uploadRemoteBundle(url, bundle, headers);
|
|
121
|
+
if (response.ok) return;
|
|
122
|
+
const errorText = await response.text().catch(() => "");
|
|
123
|
+
const message = `Upload failed (${response.status}): ${errorText || response.statusText}`;
|
|
124
|
+
lastError = new Error(message);
|
|
125
|
+
if (!isRetryableStatus(response.status)) throw lastError;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (!isRetryableError(error)) throw error;
|
|
128
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
129
|
+
}
|
|
130
|
+
if (attempt === config.maxAttempts) throw lastError ?? /* @__PURE__ */ new Error("Upload failed after retries");
|
|
131
|
+
const delay = Math.min(config.baseDelayMs * 2 ** (attempt - 1), config.maxDelayMs);
|
|
132
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function resolveUploadHeaders(bundle, contentType) {
|
|
136
|
+
const contentLength = "path" in bundle ? (await promises.stat(bundle.path)).size : bundle.byteLength;
|
|
137
|
+
return {
|
|
138
|
+
...contentType ? { "Content-Type": contentType } : {},
|
|
139
|
+
"Content-Length": String(contentLength)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async function uploadRemoteBundle(url, bundle, headers) {
|
|
143
|
+
if ("path" in bundle) {
|
|
144
|
+
const bunFileBody = resolveBunFileBody(bundle.path);
|
|
145
|
+
if (bunFileBody) return fetch(url, {
|
|
146
|
+
method: "PUT",
|
|
147
|
+
body: bunFileBody,
|
|
148
|
+
headers
|
|
149
|
+
});
|
|
150
|
+
return uploadFileStreamToPresignedUrl(url, bundle.path, headers);
|
|
151
|
+
}
|
|
152
|
+
return await fetch(url, {
|
|
153
|
+
method: "PUT",
|
|
154
|
+
body: bundle,
|
|
155
|
+
headers
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function resolveBunFileBody(filePath) {
|
|
159
|
+
const bun = globalThis.Bun;
|
|
160
|
+
return bun?.file ? bun.file(filePath) : void 0;
|
|
161
|
+
}
|
|
162
|
+
function uploadFileStreamToPresignedUrl(url, filePath, headers) {
|
|
163
|
+
return new Promise((resolve, reject) => {
|
|
164
|
+
const parsedUrl = new URL(url);
|
|
165
|
+
const client = parsedUrl.protocol === "http:" ? http : https;
|
|
166
|
+
let settled = false;
|
|
167
|
+
const settle = (fn) => {
|
|
168
|
+
if (settled) return;
|
|
169
|
+
settled = true;
|
|
170
|
+
fn();
|
|
171
|
+
};
|
|
172
|
+
const request = client.request(parsedUrl, {
|
|
173
|
+
method: "PUT",
|
|
174
|
+
headers
|
|
175
|
+
}, (response) => {
|
|
176
|
+
const chunks = [];
|
|
177
|
+
response.on("data", (chunk) => {
|
|
178
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
179
|
+
});
|
|
180
|
+
response.on("end", () => {
|
|
181
|
+
const body = Buffer.concat(chunks).toString("utf8");
|
|
182
|
+
settle(() => resolve({
|
|
183
|
+
ok: response.statusCode !== void 0 && response.statusCode >= 200 && response.statusCode < 300,
|
|
184
|
+
status: response.statusCode ?? 0,
|
|
185
|
+
statusText: response.statusMessage ?? "",
|
|
186
|
+
text: async () => body
|
|
187
|
+
}));
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
request.on("error", (error) => {
|
|
191
|
+
settle(() => reject(new TypeError(error.message)));
|
|
192
|
+
});
|
|
193
|
+
const stream = createReadStream(filePath);
|
|
194
|
+
stream.on("error", (error) => {
|
|
195
|
+
request.destroy(error);
|
|
196
|
+
settle(() => reject(error));
|
|
197
|
+
});
|
|
198
|
+
stream.pipe(request);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
function resolveUploadBody(item) {
|
|
202
|
+
if (item.bundle) return item.bundle;
|
|
203
|
+
if (item.bundlePath) return { path: item.bundlePath };
|
|
204
|
+
throw new Error(`Upload item "${item.name}" did not include a bundle or bundlePath`);
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
export { upload_exports as n, batchUpload as t };
|