@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,1116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { n as __exportAll } from "./chunk-CH6r78ws.mjs";
|
|
4
|
+
import { t as assertWorkflowProjectRoot } from "./project-config-D1qsQlO7.mjs";
|
|
5
|
+
import { V as SHA256HashSchema, g as collectCredentialRequirementEntries, p as TriggerUploadDataSchema } from "./schedule-BXx3uXwr.mjs";
|
|
6
|
+
import { c as FlowGraphSchema, s as WorkflowManifestSchema } from "./_manifest-JSRE3H8k.mjs";
|
|
7
|
+
import { t as AgentVersionManifestSchema } from "./agent-manifest-CDnbkR2f.mjs";
|
|
8
|
+
import { t as TaskBuildManifestSchema } from "./schemas-CDib1RhE.mjs";
|
|
9
|
+
import "./task-B2sZMaZu.mjs";
|
|
10
|
+
import { n as FileMetadataSchema } from "./file-metadata-D1vm-XY2.mjs";
|
|
11
|
+
import { t as computeProjectSnapshotHash } from "./task-target-deploy-CA6elFpF-BEr4gkol.mjs";
|
|
12
|
+
import { a as MANIFEST_FILE_NAME, d as getMetadataRoot, f as getTriggersDir, m as getWorkflowManifestPath, p as getWorkflowArtifactPaths, t as BUILD_DIR_NAME } from "./layout-CbMtQ2tm.mjs";
|
|
13
|
+
import { n as resolveProjectBuildOutputDir } from "./utils-CywxCDM7.mjs";
|
|
14
|
+
import { t as TriggerBuildManifestSchema } from "./trigger-manifest-CY7brZeg.mjs";
|
|
15
|
+
import { t as batchUpload } from "./upload-CkU--iDC.mjs";
|
|
16
|
+
import { createReadStream } from "node:fs";
|
|
17
|
+
import * as fs from "node:fs/promises";
|
|
18
|
+
import { stat } from "node:fs/promises";
|
|
19
|
+
import * as path$1 from "node:path";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
//#region ../../packages/workflow-deploy/dist/index.mjs
|
|
23
|
+
var dist_exports = /* @__PURE__ */ __exportAll({
|
|
24
|
+
collectCredentialFingerprintMapFromProjectDist: () => collectCredentialFingerprintMapFromProjectDist,
|
|
25
|
+
collectSchemaFingerprintsFromAgentManifest: () => collectSchemaFingerprintsFromAgentManifest,
|
|
26
|
+
deployFromDir: () => deployFromDir,
|
|
27
|
+
readAgentManifestsFromOutDir: () => readAgentManifestsFromOutDir,
|
|
28
|
+
readManifestsFromOutDir: () => readManifestsFromOutDir,
|
|
29
|
+
readWorkflowsFromDisk: () => readWorkflowsFromDisk,
|
|
30
|
+
uploadTestBundle: () => uploadTestBundle
|
|
31
|
+
});
|
|
32
|
+
/** Matches `AGENT_ARTIFACT_DIR_PREFIX` in `@keystroke/workflow-builder` artifact layout. */
|
|
33
|
+
const AGENT_ARTIFACT_DIR_PREFIX$1 = "agent_";
|
|
34
|
+
/**
|
|
35
|
+
* Reads all workflow manifests from the dist output directory.
|
|
36
|
+
* Optionally filters by workflow ref: tries authored id first, then name.
|
|
37
|
+
*
|
|
38
|
+
* When ref is provided:
|
|
39
|
+
* - First match: manifest.id === ref
|
|
40
|
+
* - Fallback: manifest.name === ref
|
|
41
|
+
*
|
|
42
|
+
* Returns [] when the dist directory does not exist or is unreadable.
|
|
43
|
+
*/
|
|
44
|
+
async function readManifestsFromOutDir(workflowsDir, ref) {
|
|
45
|
+
const distDir = await resolveProjectBuildOutputDir(workflowsDir);
|
|
46
|
+
const entries = await fs.readdir(distDir, { withFileTypes: true }).catch(() => null);
|
|
47
|
+
if (!entries) return [];
|
|
48
|
+
const byId = [];
|
|
49
|
+
const byName = [];
|
|
50
|
+
const all = [];
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
if (!entry.isDirectory()) continue;
|
|
53
|
+
const manifestPath = getWorkflowManifestPath(distDir, entry.name);
|
|
54
|
+
try {
|
|
55
|
+
const raw = await fs.readFile(manifestPath, "utf-8");
|
|
56
|
+
const parsed = WorkflowManifestSchema.safeParse(JSON.parse(raw));
|
|
57
|
+
if (!parsed.success) continue;
|
|
58
|
+
if (ref === void 0) {
|
|
59
|
+
all.push({
|
|
60
|
+
manifest: parsed.data,
|
|
61
|
+
manifestPath
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (parsed.data.id === ref) byId.push({
|
|
66
|
+
manifest: parsed.data,
|
|
67
|
+
manifestPath
|
|
68
|
+
});
|
|
69
|
+
else if (parsed.data.name === ref) byName.push({
|
|
70
|
+
manifest: parsed.data,
|
|
71
|
+
manifestPath
|
|
72
|
+
});
|
|
73
|
+
} catch {}
|
|
74
|
+
}
|
|
75
|
+
if (ref === void 0) return all;
|
|
76
|
+
if (byId.length > 0) return byId;
|
|
77
|
+
return byName;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reads all built agent version manifests from the dist output directory.
|
|
81
|
+
* Agent artifacts live under `dist/agent_<authoredAgentId>_<hash>/manifest.json`.
|
|
82
|
+
*
|
|
83
|
+
* Returns [] when the dist directory does not exist or is unreadable.
|
|
84
|
+
*/
|
|
85
|
+
async function readAgentManifestsFromOutDir(workflowsDir) {
|
|
86
|
+
const distDir = await resolveProjectBuildOutputDir(workflowsDir);
|
|
87
|
+
const entries = await fs.readdir(distDir, { withFileTypes: true }).catch(() => null);
|
|
88
|
+
if (!entries) return [];
|
|
89
|
+
const results = [];
|
|
90
|
+
for (const entry of entries) {
|
|
91
|
+
if (!entry.isDirectory() || !entry.name.startsWith(AGENT_ARTIFACT_DIR_PREFIX$1)) continue;
|
|
92
|
+
const manifestPath = path.join(distDir, entry.name, MANIFEST_FILE_NAME);
|
|
93
|
+
try {
|
|
94
|
+
const raw = await fs.readFile(manifestPath, "utf-8");
|
|
95
|
+
const parsed = AgentVersionManifestSchema.safeParse(JSON.parse(raw));
|
|
96
|
+
if (!parsed.success) continue;
|
|
97
|
+
results.push({
|
|
98
|
+
manifest: parsed.data,
|
|
99
|
+
manifestPath
|
|
100
|
+
});
|
|
101
|
+
} catch {}
|
|
102
|
+
}
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Collects `resolvedCredentialSetId` → `schemaFingerprint` from a built agent
|
|
107
|
+
* manifest (top-level credential sets, MCP servers, and messaging gateways).
|
|
108
|
+
*/
|
|
109
|
+
function collectSchemaFingerprintsFromAgentManifest(manifest) {
|
|
110
|
+
const map = /* @__PURE__ */ new Map();
|
|
111
|
+
const put = (resolvedId, fingerprint) => {
|
|
112
|
+
if (fingerprint && !map.has(resolvedId)) map.set(resolvedId, fingerprint);
|
|
113
|
+
};
|
|
114
|
+
for (const credentialSet of manifest.credentialSets ?? []) put(credentialSet.resolvedId, credentialSet.schemaFingerprint);
|
|
115
|
+
for (const server of manifest.mcpServers ?? []) for (const credentialSet of server.credentialSets) put(credentialSet.resolvedId, credentialSet.schemaFingerprint);
|
|
116
|
+
for (const gateway of manifest.messaging ?? []) put(gateway.credentialSet.resolvedId, gateway.credentialSet.schemaFingerprint);
|
|
117
|
+
return map;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Builds `resolvedCredentialSetId` → `schemaFingerprint` from all workflow
|
|
121
|
+
* manifests and agent version manifests under a project's build output dir.
|
|
122
|
+
*/
|
|
123
|
+
async function collectCredentialFingerprintMapFromProjectDist(projectRoot) {
|
|
124
|
+
const map = /* @__PURE__ */ new Map();
|
|
125
|
+
for (const { manifest } of await readManifestsFromOutDir(projectRoot)) for (const entry of collectCredentialRequirementEntries(manifest.credentials)) if (entry.schemaFingerprint && !map.has(entry.credentialSetId)) map.set(entry.credentialSetId, entry.schemaFingerprint);
|
|
126
|
+
for (const { manifest } of await readAgentManifestsFromOutDir(projectRoot)) for (const [resolvedId, fp] of collectSchemaFingerprintsFromAgentManifest(manifest)) if (!map.has(resolvedId)) map.set(resolvedId, fp);
|
|
127
|
+
return map;
|
|
128
|
+
}
|
|
129
|
+
function splitChangedArtifacts(options) {
|
|
130
|
+
if (options.force) return {
|
|
131
|
+
toUpload: options.prepared,
|
|
132
|
+
skippedResults: []
|
|
133
|
+
};
|
|
134
|
+
const toUpload = [];
|
|
135
|
+
const skippedResults = [];
|
|
136
|
+
for (const item of options.prepared) {
|
|
137
|
+
const name = options.getName(item);
|
|
138
|
+
if (options.existingArtifacts[name]?.bundleHash === options.getHash(item)) {
|
|
139
|
+
skippedResults.push(options.toSkippedResult(name));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
toUpload.push(item);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
toUpload,
|
|
146
|
+
skippedResults
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/** Separates unchanged workflows (skipped) from those needing upload. */
|
|
150
|
+
function filterUnchangedWorkflows(prepared, existingArtifacts, force) {
|
|
151
|
+
const { toUpload, skippedResults } = splitChangedArtifacts({
|
|
152
|
+
prepared,
|
|
153
|
+
existingArtifacts,
|
|
154
|
+
force,
|
|
155
|
+
getName: (workflow) => workflow.name,
|
|
156
|
+
getHash: (workflow) => workflow.bundleHash,
|
|
157
|
+
toSkippedResult: (name) => ({
|
|
158
|
+
name,
|
|
159
|
+
status: "skipped"
|
|
160
|
+
})
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
workflowsToUpload: toUpload,
|
|
164
|
+
skippedResults
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** Separates unchanged agents (skipped) from those needing upload. */
|
|
168
|
+
function filterUnchangedAgents(prepared, existingArtifacts, force) {
|
|
169
|
+
const { toUpload, skippedResults } = splitChangedArtifacts({
|
|
170
|
+
prepared,
|
|
171
|
+
existingArtifacts,
|
|
172
|
+
force,
|
|
173
|
+
getName: (agent) => agent.agentId,
|
|
174
|
+
getHash: (agent) => agent.sandboxPackageHash,
|
|
175
|
+
toSkippedResult: (name) => ({
|
|
176
|
+
name,
|
|
177
|
+
status: "skipped"
|
|
178
|
+
})
|
|
179
|
+
});
|
|
180
|
+
return {
|
|
181
|
+
agentsToUpload: toUpload,
|
|
182
|
+
skippedResults
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** Separates unchanged tasks (skipped) from those needing upload. */
|
|
186
|
+
function filterUnchangedTasks(prepared, existingArtifacts, force) {
|
|
187
|
+
const { toUpload, skippedResults } = splitChangedArtifacts({
|
|
188
|
+
prepared,
|
|
189
|
+
existingArtifacts,
|
|
190
|
+
force,
|
|
191
|
+
getName: (task) => task.taskId,
|
|
192
|
+
getHash: (task) => task.bundleHash,
|
|
193
|
+
toSkippedResult: (name) => ({
|
|
194
|
+
name,
|
|
195
|
+
status: "skipped"
|
|
196
|
+
})
|
|
197
|
+
});
|
|
198
|
+
return {
|
|
199
|
+
tasksToUpload: toUpload,
|
|
200
|
+
skippedResults
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const EMPTY_RESULT = {
|
|
204
|
+
workflowArtifacts: {},
|
|
205
|
+
agentArtifacts: {},
|
|
206
|
+
taskArtifacts: {},
|
|
207
|
+
workflowFilter: {
|
|
208
|
+
workflowsToUpload: [],
|
|
209
|
+
skippedResults: []
|
|
210
|
+
},
|
|
211
|
+
agentFilter: {
|
|
212
|
+
agentsToUpload: [],
|
|
213
|
+
skippedResults: []
|
|
214
|
+
},
|
|
215
|
+
taskFilter: {
|
|
216
|
+
tasksToUpload: [],
|
|
217
|
+
skippedResults: []
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
async function hashCheckAndFilter(options) {
|
|
221
|
+
const { prepared, preparedAgents, preparedTasks, client, organizationId, projectId, force, onDeployProgressEvent } = options;
|
|
222
|
+
if (prepared.length === 0 && preparedAgents.length === 0 && preparedTasks.length === 0) return EMPTY_RESULT;
|
|
223
|
+
const hashCheckStartTime = Date.now();
|
|
224
|
+
onDeployProgressEvent?.({
|
|
225
|
+
phase: "hash-check",
|
|
226
|
+
status: "start",
|
|
227
|
+
workflowCount: prepared.length,
|
|
228
|
+
agentCount: preparedAgents.length,
|
|
229
|
+
taskCount: preparedTasks.length
|
|
230
|
+
});
|
|
231
|
+
const hashLookup = await client.bundles.getHashes({
|
|
232
|
+
organizationId,
|
|
233
|
+
projectId,
|
|
234
|
+
...prepared.length > 0 ? { workflowNames: prepared.map((workflow) => workflow.name) } : {},
|
|
235
|
+
...preparedAgents.length > 0 ? { agentIds: preparedAgents.map((agent) => agent.agentId) } : {},
|
|
236
|
+
...preparedTasks.length > 0 ? { tasks: preparedTasks.map((task) => ({
|
|
237
|
+
taskId: task.taskId,
|
|
238
|
+
bundleHash: task.bundleHash
|
|
239
|
+
})) } : {}
|
|
240
|
+
});
|
|
241
|
+
onDeployProgressEvent?.({
|
|
242
|
+
phase: "hash-check",
|
|
243
|
+
status: "complete",
|
|
244
|
+
elapsedMs: Date.now() - hashCheckStartTime
|
|
245
|
+
});
|
|
246
|
+
const filterStartTime = Date.now();
|
|
247
|
+
const workflowFilter = filterUnchangedWorkflows(prepared, hashLookup.workflows, force);
|
|
248
|
+
const agentFilter = filterUnchangedAgents(preparedAgents, hashLookup.agents, force);
|
|
249
|
+
const taskFilter = filterUnchangedTasks(preparedTasks, hashLookup.tasks || {}, force);
|
|
250
|
+
onDeployProgressEvent?.({
|
|
251
|
+
phase: "filter",
|
|
252
|
+
status: "complete",
|
|
253
|
+
workflowsToUpload: workflowFilter.workflowsToUpload.length,
|
|
254
|
+
workflowsSkipped: workflowFilter.skippedResults.length,
|
|
255
|
+
agentsToUpload: agentFilter.agentsToUpload.length,
|
|
256
|
+
agentsSkipped: agentFilter.skippedResults.length,
|
|
257
|
+
tasksToUpload: taskFilter.tasksToUpload.length,
|
|
258
|
+
tasksSkipped: taskFilter.skippedResults.length,
|
|
259
|
+
force,
|
|
260
|
+
elapsedMs: Date.now() - filterStartTime
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
workflowArtifacts: hashLookup.workflows,
|
|
264
|
+
agentArtifacts: hashLookup.agents,
|
|
265
|
+
taskArtifacts: hashLookup.tasks || {},
|
|
266
|
+
workflowFilter,
|
|
267
|
+
agentFilter,
|
|
268
|
+
taskFilter
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function finalizeDeployResult(options) {
|
|
272
|
+
const result = {
|
|
273
|
+
success: options.workflows.every((workflow) => workflow.status !== "failed") && options.agents.every((agent) => agent.status !== "failed") && (options.tasks || []).every((task) => task.status !== "failed"),
|
|
274
|
+
workflows: options.workflows,
|
|
275
|
+
agents: options.agents,
|
|
276
|
+
tasks: options.tasks || [],
|
|
277
|
+
totalTime: Date.now() - options.startTime
|
|
278
|
+
};
|
|
279
|
+
options.onDeployProgressEvent?.({
|
|
280
|
+
phase: "complete",
|
|
281
|
+
status: "complete",
|
|
282
|
+
success: result.success,
|
|
283
|
+
workflowsTotal: result.workflows.length,
|
|
284
|
+
agentsTotal: result.agents.length,
|
|
285
|
+
tasksTotal: result.tasks.length,
|
|
286
|
+
elapsedMs: result.totalTime
|
|
287
|
+
});
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
async function runUploadPhase(options) {
|
|
291
|
+
if (options.count === 0) return [];
|
|
292
|
+
const uploadStartTime = Date.now();
|
|
293
|
+
options.onDeployProgressEvent?.({
|
|
294
|
+
phase: options.phase,
|
|
295
|
+
status: "start",
|
|
296
|
+
count: options.count,
|
|
297
|
+
concurrency: options.concurrency
|
|
298
|
+
});
|
|
299
|
+
const results = await options.upload();
|
|
300
|
+
let succeeded = 0;
|
|
301
|
+
let failed = 0;
|
|
302
|
+
for (const result of results) {
|
|
303
|
+
if (result.kind === "success") {
|
|
304
|
+
succeeded += 1;
|
|
305
|
+
options.onDeployProgressEvent?.({
|
|
306
|
+
phase: options.phase,
|
|
307
|
+
status: "item",
|
|
308
|
+
name: result.name,
|
|
309
|
+
result: "success"
|
|
310
|
+
});
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
failed += 1;
|
|
314
|
+
options.onDeployProgressEvent?.({
|
|
315
|
+
phase: options.phase,
|
|
316
|
+
status: "item",
|
|
317
|
+
name: result.name,
|
|
318
|
+
result: "failure",
|
|
319
|
+
error: result.error
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
options.onDeployProgressEvent?.({
|
|
323
|
+
phase: options.phase,
|
|
324
|
+
status: "complete",
|
|
325
|
+
succeeded,
|
|
326
|
+
failed,
|
|
327
|
+
elapsedMs: Date.now() - uploadStartTime
|
|
328
|
+
});
|
|
329
|
+
return results;
|
|
330
|
+
}
|
|
331
|
+
function toFailedUploadResults(uploadResults) {
|
|
332
|
+
return uploadResults.filter((result) => result.kind === "failure").map((result) => ({
|
|
333
|
+
name: result.name,
|
|
334
|
+
status: "failed",
|
|
335
|
+
error: result.error
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
/** Builds a bundle registration item from a prepared workflow and its upload storage path. */
|
|
339
|
+
function buildBundleRegistrationItem(wf, storagePath, existingArtifacts, force) {
|
|
340
|
+
return {
|
|
341
|
+
workflowName: wf.name,
|
|
342
|
+
storagePath,
|
|
343
|
+
manifest: { ...wf.manifestData },
|
|
344
|
+
flowJson: wf.flowData,
|
|
345
|
+
bundleSize: wf.bundleSize,
|
|
346
|
+
triggers: wf.triggers,
|
|
347
|
+
bundleHash: wf.bundleHash,
|
|
348
|
+
skipVersionBump: !force && existingArtifacts[wf.name]?.bundleHash === wf.bundleHash
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/** Builds an agent registration item from a prepared agent and its upload storage path. */
|
|
352
|
+
function buildAgentRegistrationItem(agent, storagePath, existingArtifacts, force) {
|
|
353
|
+
const manifest = {
|
|
354
|
+
...agent.manifestData,
|
|
355
|
+
sandbox: {
|
|
356
|
+
...agent.manifestData.sandbox ?? {},
|
|
357
|
+
bootstrapArtifacts: [{
|
|
358
|
+
storagePath,
|
|
359
|
+
sha256: agent.sandboxPackageHash,
|
|
360
|
+
target: "/home/user",
|
|
361
|
+
kind: "archive"
|
|
362
|
+
}],
|
|
363
|
+
sandboxBundleStoragePath: storagePath
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
return {
|
|
367
|
+
agentId: agent.agentId,
|
|
368
|
+
agentName: agent.agentName,
|
|
369
|
+
storagePath,
|
|
370
|
+
manifest,
|
|
371
|
+
bundleSize: agent.sandboxPackageSize,
|
|
372
|
+
bundleHash: agent.sandboxPackageHash,
|
|
373
|
+
skipVersionBump: !force && existingArtifacts[agent.agentId]?.bundleHash === agent.sandboxPackageHash
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/** Builds a task registration item from a prepared task and its upload storage path. */
|
|
377
|
+
function buildTaskRegistrationItem(task, storagePath, existingArtifacts, force) {
|
|
378
|
+
return {
|
|
379
|
+
taskId: task.taskId,
|
|
380
|
+
taskName: task.name,
|
|
381
|
+
storagePath,
|
|
382
|
+
manifest: { ...task.manifestData },
|
|
383
|
+
bundleSize: task.bundleSize,
|
|
384
|
+
bundleHash: task.bundleHash,
|
|
385
|
+
triggers: task.triggers,
|
|
386
|
+
skipVersionBump: !force && existingArtifacts[task.taskId]?.bundleHash === task.bundleHash
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
async function syncProjectFileMetadata(client, organizationId, projectId, fileMetadata) {
|
|
390
|
+
return client.bundles.syncFileMetadata({
|
|
391
|
+
fileMetadata,
|
|
392
|
+
organizationId,
|
|
393
|
+
projectId,
|
|
394
|
+
syncMode: "replace"
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
function resolveArtifactStoragePath(uploadedStoragePath, existingArtifact, artifactLabel, artifactName) {
|
|
398
|
+
const storagePath = uploadedStoragePath ?? existingArtifact?.storagePath;
|
|
399
|
+
if (!storagePath) throw new Error(`Missing current deployment storage path for unchanged ${artifactLabel} "${artifactName}"`);
|
|
400
|
+
return storagePath;
|
|
401
|
+
}
|
|
402
|
+
function mapDeployResultStatus(result) {
|
|
403
|
+
if (result.status === "failed") return {
|
|
404
|
+
name: result.name,
|
|
405
|
+
status: "failed",
|
|
406
|
+
error: result.error
|
|
407
|
+
};
|
|
408
|
+
if (result.status === "skipped") return {
|
|
409
|
+
name: result.name,
|
|
410
|
+
status: "skipped"
|
|
411
|
+
};
|
|
412
|
+
return {
|
|
413
|
+
name: result.name,
|
|
414
|
+
status: "deployed",
|
|
415
|
+
authoredWorkflowId: result.authoredWorkflowId
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function mapAgentResultStatus(result) {
|
|
419
|
+
if (result.status === "failed") return {
|
|
420
|
+
name: result.name,
|
|
421
|
+
status: "failed",
|
|
422
|
+
error: result.error
|
|
423
|
+
};
|
|
424
|
+
if (result.status === "skipped") return {
|
|
425
|
+
name: result.name,
|
|
426
|
+
status: "skipped"
|
|
427
|
+
};
|
|
428
|
+
return {
|
|
429
|
+
name: result.name,
|
|
430
|
+
status: "deployed",
|
|
431
|
+
agentId: result.agentId
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function mapTaskResultStatus(result) {
|
|
435
|
+
if (result.status === "failed") return {
|
|
436
|
+
name: result.name,
|
|
437
|
+
status: "failed",
|
|
438
|
+
error: result.error
|
|
439
|
+
};
|
|
440
|
+
if (result.status === "skipped") return {
|
|
441
|
+
name: result.name,
|
|
442
|
+
status: "skipped"
|
|
443
|
+
};
|
|
444
|
+
return {
|
|
445
|
+
name: result.name,
|
|
446
|
+
status: "deployed",
|
|
447
|
+
taskId: result.taskId
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
async function registerDeployment(options) {
|
|
451
|
+
const { prepared, preparedAgents, preparedTasks, successfulWorkflowUploads, successfulAgentUploads, successfulTaskUploads, workflowArtifacts, agentArtifacts, taskArtifacts, organizationId, projectId, projectRoot, client, force, deploymentScope = "project", onDeployProgressEvent } = options;
|
|
452
|
+
if (prepared.length === 0 && preparedAgents.length === 0 && preparedTasks.length === 0) return {
|
|
453
|
+
workflows: [],
|
|
454
|
+
agents: [],
|
|
455
|
+
tasks: []
|
|
456
|
+
};
|
|
457
|
+
const workflowUploadStorageByName = new Map(successfulWorkflowUploads.map((upload) => [upload.name, upload.storagePath]));
|
|
458
|
+
const agentUploadStorageById = new Map(successfulAgentUploads.map((upload) => [upload.name, upload.storagePath]));
|
|
459
|
+
const taskUploadStorageById = new Map(successfulTaskUploads.map((upload) => [upload.name, upload.storagePath]));
|
|
460
|
+
const deploymentStartTime = Date.now();
|
|
461
|
+
if (prepared.length > 0) onDeployProgressEvent?.({
|
|
462
|
+
phase: "register-workflows",
|
|
463
|
+
status: "start",
|
|
464
|
+
count: prepared.length
|
|
465
|
+
});
|
|
466
|
+
if (preparedAgents.length > 0) onDeployProgressEvent?.({
|
|
467
|
+
phase: "register-agents",
|
|
468
|
+
status: "start",
|
|
469
|
+
count: preparedAgents.length
|
|
470
|
+
});
|
|
471
|
+
if (preparedTasks.length > 0) onDeployProgressEvent?.({
|
|
472
|
+
phase: "register-tasks",
|
|
473
|
+
status: "start",
|
|
474
|
+
count: preparedTasks.length
|
|
475
|
+
});
|
|
476
|
+
const deploymentWorkflows = prepared.map((workflow) => {
|
|
477
|
+
return buildBundleRegistrationItem(workflow, resolveArtifactStoragePath(workflowUploadStorageByName.get(workflow.name), workflowArtifacts[workflow.name], "workflow", workflow.name), workflowArtifacts, force);
|
|
478
|
+
});
|
|
479
|
+
const deploymentAgents = preparedAgents.map((agent) => {
|
|
480
|
+
return buildAgentRegistrationItem(agent, resolveArtifactStoragePath(agentUploadStorageById.get(agent.agentId), agentArtifacts[agent.agentId], "agent", agent.agentName), agentArtifacts, force);
|
|
481
|
+
});
|
|
482
|
+
const deploymentTasks = preparedTasks.map((task) => {
|
|
483
|
+
return buildTaskRegistrationItem(task, resolveArtifactStoragePath(taskUploadStorageById.get(task.taskId), taskArtifacts[task.taskId], "task", task.name), taskArtifacts, force);
|
|
484
|
+
});
|
|
485
|
+
const snapshotHash = computeProjectSnapshotHash({
|
|
486
|
+
workflows: deploymentWorkflows,
|
|
487
|
+
agents: deploymentAgents
|
|
488
|
+
});
|
|
489
|
+
let deployResult;
|
|
490
|
+
try {
|
|
491
|
+
deployResult = await client.public.projects.deploy({
|
|
492
|
+
organizationId,
|
|
493
|
+
projectId,
|
|
494
|
+
projectRoot,
|
|
495
|
+
scope: deploymentScope,
|
|
496
|
+
snapshotHash,
|
|
497
|
+
force,
|
|
498
|
+
workflows: deploymentWorkflows,
|
|
499
|
+
agents: deploymentAgents,
|
|
500
|
+
tasks: deploymentTasks
|
|
501
|
+
});
|
|
502
|
+
} finally {
|
|
503
|
+
releasePreparedTaskCallbackBundleCode(preparedTasks);
|
|
504
|
+
}
|
|
505
|
+
const deployedWorkflowResults = deployResult.workflows;
|
|
506
|
+
const deployedAgentResults = deployResult.agents;
|
|
507
|
+
const deployedTaskResults = deployResult.tasks ?? [];
|
|
508
|
+
if (prepared.length > 0) onDeployProgressEvent?.({
|
|
509
|
+
phase: "register-workflows",
|
|
510
|
+
status: "complete",
|
|
511
|
+
succeeded: deployedWorkflowResults.filter((item) => item.status === "deployed").length,
|
|
512
|
+
failed: deployedWorkflowResults.filter((item) => item.status === "failed").length,
|
|
513
|
+
elapsedMs: Date.now() - deploymentStartTime
|
|
514
|
+
});
|
|
515
|
+
if (preparedAgents.length > 0) onDeployProgressEvent?.({
|
|
516
|
+
phase: "register-agents",
|
|
517
|
+
status: "complete",
|
|
518
|
+
succeeded: deployedAgentResults.filter((item) => item.status === "deployed").length,
|
|
519
|
+
failed: deployedAgentResults.filter((item) => item.status === "failed").length,
|
|
520
|
+
elapsedMs: Date.now() - deploymentStartTime
|
|
521
|
+
});
|
|
522
|
+
if (preparedTasks.length > 0) onDeployProgressEvent?.({
|
|
523
|
+
phase: "register-tasks",
|
|
524
|
+
status: "complete",
|
|
525
|
+
succeeded: deployedTaskResults.filter((item) => item.status === "deployed").length,
|
|
526
|
+
failed: deployedTaskResults.filter((item) => item.status === "failed").length,
|
|
527
|
+
elapsedMs: Date.now() - deploymentStartTime
|
|
528
|
+
});
|
|
529
|
+
return {
|
|
530
|
+
workflows: deployedWorkflowResults.map(mapDeployResultStatus),
|
|
531
|
+
agents: deployedAgentResults.map(mapAgentResultStatus),
|
|
532
|
+
tasks: deployedTaskResults.map(mapTaskResultStatus)
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function releasePreparedTaskCallbackBundleCode(preparedTasks) {
|
|
536
|
+
for (const task of preparedTasks) for (const trigger of task.triggers) {
|
|
537
|
+
if (trigger.callbackBundle) trigger.callbackBundle.code = "";
|
|
538
|
+
if (trigger.transformCallbackBundle) trigger.transformCallbackBundle.code = "";
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function syncMetadata(options) {
|
|
542
|
+
const { client, organizationId, projectId, fileMetadata, onDeployProgressEvent } = options;
|
|
543
|
+
const metadataSyncStartTime = Date.now();
|
|
544
|
+
onDeployProgressEvent?.({
|
|
545
|
+
phase: "sync-file-metadata",
|
|
546
|
+
status: "start",
|
|
547
|
+
fileCount: fileMetadata.entries.length
|
|
548
|
+
});
|
|
549
|
+
await syncProjectFileMetadata(client, organizationId, projectId, fileMetadata.entries);
|
|
550
|
+
onDeployProgressEvent?.({
|
|
551
|
+
phase: "sync-file-metadata",
|
|
552
|
+
status: "complete",
|
|
553
|
+
fileCount: fileMetadata.entries.length,
|
|
554
|
+
elapsedMs: Date.now() - metadataSyncStartTime
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
const AGENT_SANDBOX_PACKAGE_RELATIVE_PATH = `${BUILD_DIR_NAME}/sandbox-bundle.tar.gz`;
|
|
558
|
+
async function hashFile(filePath) {
|
|
559
|
+
const fileStat = await stat(filePath);
|
|
560
|
+
const hash = createHash("sha256");
|
|
561
|
+
await new Promise((resolve, reject) => {
|
|
562
|
+
const stream = createReadStream(filePath);
|
|
563
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
564
|
+
stream.on("error", reject);
|
|
565
|
+
stream.on("end", resolve);
|
|
566
|
+
});
|
|
567
|
+
return {
|
|
568
|
+
hash: hash.digest("hex"),
|
|
569
|
+
size: fileStat.size
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
/** Top-level build output folder for task bundles; not workflow-shaped (see readTasksFromDisk). */
|
|
573
|
+
const TASKS_OUTPUT_DIR_NAME = "tasks";
|
|
574
|
+
async function readCallbackBundle(baseDir, relativePath) {
|
|
575
|
+
const bundlePath = path$1.join(baseDir, relativePath);
|
|
576
|
+
const code = await fs.readFile(bundlePath, "utf-8");
|
|
577
|
+
return {
|
|
578
|
+
code,
|
|
579
|
+
hash: createHash("sha256").update(code).digest("hex"),
|
|
580
|
+
size: Buffer.byteLength(code)
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
async function readTriggersFromBuildOutput(buildDir, options) {
|
|
584
|
+
const triggersDir = getTriggersDir(buildDir);
|
|
585
|
+
try {
|
|
586
|
+
await fs.access(triggersDir);
|
|
587
|
+
} catch {
|
|
588
|
+
return [];
|
|
589
|
+
}
|
|
590
|
+
const entries = await fs.readdir(triggersDir, { withFileTypes: true });
|
|
591
|
+
const triggers = [];
|
|
592
|
+
for (const entry of entries) {
|
|
593
|
+
if (!entry.isDirectory()) continue;
|
|
594
|
+
const triggerDir = path$1.join(triggersDir, entry.name);
|
|
595
|
+
const manifestPath = path$1.join(triggerDir, MANIFEST_FILE_NAME);
|
|
596
|
+
try {
|
|
597
|
+
const raw = await fs.readFile(manifestPath, "utf-8");
|
|
598
|
+
const manifest = TriggerBuildManifestSchema.parse(JSON.parse(raw));
|
|
599
|
+
const callbackExports = manifest.functions && Object.keys(manifest.functions).length > 0 ? Object.fromEntries(Object.entries(manifest.functions).map(([key, value]) => [key, value.export])) : void 0;
|
|
600
|
+
const callbackBundlePath = manifest.functions ? Object.values(manifest.functions)[0]?.js : void 0;
|
|
601
|
+
const transformCallbackExports = manifest.attachmentFunctions && Object.keys(manifest.attachmentFunctions).length > 0 ? Object.fromEntries(Object.entries(manifest.attachmentFunctions).map(([key, value]) => [key, value.export])) : void 0;
|
|
602
|
+
const transformCallbackBundlePath = manifest.attachmentFunctions ? Object.values(manifest.attachmentFunctions)[0]?.js : void 0;
|
|
603
|
+
const trigger = TriggerUploadDataSchema.parse({
|
|
604
|
+
id: manifest.id,
|
|
605
|
+
type: manifest.type,
|
|
606
|
+
...manifest.triggerSource ? { triggerSource: manifest.triggerSource } : {},
|
|
607
|
+
enabled: manifest.enabled,
|
|
608
|
+
...manifest.path ? { path: manifest.path } : {},
|
|
609
|
+
...manifest.method ? { method: manifest.method } : {},
|
|
610
|
+
...manifest.schedule ? { schedule: manifest.schedule } : {},
|
|
611
|
+
...manifest.timezone ? { timezone: manifest.timezone } : {},
|
|
612
|
+
...manifest.config ? { config: manifest.config } : {},
|
|
613
|
+
...manifest.requiredCredentials ? { requiredCredentials: manifest.requiredCredentials } : {},
|
|
614
|
+
...callbackExports ? { callbackExports } : {},
|
|
615
|
+
...callbackBundlePath ? { callbackBundle: await readCallbackBundle(triggerDir, callbackBundlePath) } : {},
|
|
616
|
+
...transformCallbackExports ? { transformCallbackExports } : {},
|
|
617
|
+
...transformCallbackBundlePath ? { transformCallbackBundle: await readCallbackBundle(triggerDir, transformCallbackBundlePath) } : {}
|
|
618
|
+
});
|
|
619
|
+
triggers.push(trigger);
|
|
620
|
+
} catch (error) {
|
|
621
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
622
|
+
options?.onWarning?.(`Skipping trigger artifact ${entry.name}: ${message}`);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
return triggers.sort((left, right) => left.id.localeCompare(right.id));
|
|
626
|
+
}
|
|
627
|
+
/** Reads all workflow artifacts from the build output directory. */
|
|
628
|
+
async function readWorkflowsFromDisk(outDir, options) {
|
|
629
|
+
const filterSet = options?.filter ? new Set(options.filter) : void 0;
|
|
630
|
+
let entries;
|
|
631
|
+
try {
|
|
632
|
+
entries = await fs.readdir(outDir);
|
|
633
|
+
} catch {
|
|
634
|
+
return [];
|
|
635
|
+
}
|
|
636
|
+
const workflows = [];
|
|
637
|
+
for (const entryName of entries) {
|
|
638
|
+
if (entryName.startsWith("agent_")) continue;
|
|
639
|
+
if (entryName === "metadata") continue;
|
|
640
|
+
if (entryName === TASKS_OUTPUT_DIR_NAME) continue;
|
|
641
|
+
if (!(await fs.stat(path$1.join(outDir, entryName)).catch(() => null))?.isDirectory()) continue;
|
|
642
|
+
const artifactPaths = getWorkflowArtifactPaths(outDir, entryName);
|
|
643
|
+
try {
|
|
644
|
+
const manifestRaw = await fs.readFile(artifactPaths.manifestPath, "utf-8");
|
|
645
|
+
const manifestData = WorkflowManifestSchema.parse(JSON.parse(manifestRaw));
|
|
646
|
+
if (filterSet && !filterSet.has(manifestData.name) && !filterSet.has(manifestData.id)) continue;
|
|
647
|
+
const flowRaw = await fs.readFile(artifactPaths.flowPath, "utf-8");
|
|
648
|
+
const flowData = FlowGraphSchema.parse(JSON.parse(flowRaw));
|
|
649
|
+
const bundleInfo = await hashFile(artifactPaths.bundlePath);
|
|
650
|
+
const bundleHash = SHA256HashSchema.parse(bundleInfo.hash);
|
|
651
|
+
const triggers = await readTriggersFromBuildOutput(artifactPaths.buildDir, options?.onWarning ? { onWarning: (message) => options.onWarning?.(`[${manifestData.name}] ${message}`) } : void 0);
|
|
652
|
+
workflows.push({
|
|
653
|
+
workflowId: entryName,
|
|
654
|
+
name: manifestData.name,
|
|
655
|
+
exportName: manifestData.exportName,
|
|
656
|
+
dependencyPaths: [],
|
|
657
|
+
bundlePath: artifactPaths.bundlePath,
|
|
658
|
+
bundleHash,
|
|
659
|
+
bundleSize: bundleInfo.size,
|
|
660
|
+
manifestData,
|
|
661
|
+
flowData,
|
|
662
|
+
triggers
|
|
663
|
+
});
|
|
664
|
+
} catch (error) {
|
|
665
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
666
|
+
options?.onWarning?.(`Skipping workflow directory ${entryName}: ${message}`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return workflows.sort((left, right) => left.name.localeCompare(right.name));
|
|
670
|
+
}
|
|
671
|
+
/** Reads all task artifacts from the build output directory. */
|
|
672
|
+
async function readTasksFromDisk(outDir, options) {
|
|
673
|
+
const filterSet = options?.filter ? new Set(options.filter) : void 0;
|
|
674
|
+
const tasksDir = path$1.join(outDir, TASKS_OUTPUT_DIR_NAME);
|
|
675
|
+
let entries;
|
|
676
|
+
try {
|
|
677
|
+
entries = await fs.readdir(tasksDir);
|
|
678
|
+
} catch {
|
|
679
|
+
return [];
|
|
680
|
+
}
|
|
681
|
+
const tasks = [];
|
|
682
|
+
for (const entryName of entries) {
|
|
683
|
+
if (!(await fs.stat(path$1.join(tasksDir, entryName)).catch(() => null))?.isDirectory()) continue;
|
|
684
|
+
const taskDir = path$1.join(tasksDir, entryName);
|
|
685
|
+
const manifestPath = path$1.join(taskDir, "manifest.json");
|
|
686
|
+
const bundlePath = path$1.join(taskDir, "bundle.js");
|
|
687
|
+
try {
|
|
688
|
+
const manifestRaw = await fs.readFile(manifestPath, "utf-8");
|
|
689
|
+
const manifestData = TaskBuildManifestSchema.parse(JSON.parse(manifestRaw));
|
|
690
|
+
if (filterSet && !filterSet.has(manifestData.name) && !filterSet.has(manifestData.id)) continue;
|
|
691
|
+
const bundleInfo = await hashFile(bundlePath);
|
|
692
|
+
const bundleHash = SHA256HashSchema.parse(bundleInfo.hash);
|
|
693
|
+
const builtTriggers = await readTriggersFromBuildOutput(path$1.join(taskDir, "build"), options);
|
|
694
|
+
const triggers = builtTriggers.length > 0 ? builtTriggers : manifestData.triggers.map((trigger, index) => {
|
|
695
|
+
const runtime = trigger.runtime ?? {};
|
|
696
|
+
const triggerName = trigger.name ?? `trigger_${index}`;
|
|
697
|
+
const triggerId = createHash("sha256").update(`${entryName}::${triggerName}`).digest("hex").slice(0, 16);
|
|
698
|
+
const webhookSource = runtime.source && typeof runtime.source === "object" ? runtime.source : null;
|
|
699
|
+
const sourceType = webhookSource?.type === "app" || webhookSource?.type === "custom" ? webhookSource.type : void 0;
|
|
700
|
+
return {
|
|
701
|
+
id: triggerId,
|
|
702
|
+
type: trigger.triggerType ?? trigger.type,
|
|
703
|
+
...sourceType ? { triggerSource: sourceType } : {},
|
|
704
|
+
enabled: trigger.enabled ?? true,
|
|
705
|
+
...runtime.schedule ? { schedule: runtime.schedule } : {},
|
|
706
|
+
...runtime.timezone ? { timezone: runtime.timezone } : {},
|
|
707
|
+
...webhookSource?.path ? { path: webhookSource.path } : {},
|
|
708
|
+
...webhookSource?.method ? { method: webhookSource.method } : {},
|
|
709
|
+
...trigger.payload !== void 0 ? { config: { payload: trigger.payload } } : {}
|
|
710
|
+
};
|
|
711
|
+
});
|
|
712
|
+
tasks.push({
|
|
713
|
+
taskId: entryName,
|
|
714
|
+
name: manifestData.name,
|
|
715
|
+
bundlePath,
|
|
716
|
+
bundleHash,
|
|
717
|
+
bundleSize: bundleInfo.size,
|
|
718
|
+
manifestData,
|
|
719
|
+
triggers
|
|
720
|
+
});
|
|
721
|
+
} catch (error) {
|
|
722
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
723
|
+
options?.onWarning?.(`Skipping task directory ${entryName}: ${message}`);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return tasks.sort((left, right) => left.name.localeCompare(right.name));
|
|
727
|
+
}
|
|
728
|
+
async function collectJsonFiles(directory) {
|
|
729
|
+
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
730
|
+
return (await Promise.all(entries.map(async (entry) => {
|
|
731
|
+
const entryPath = path$1.join(directory, entry.name);
|
|
732
|
+
if (entry.isDirectory()) return collectJsonFiles(entryPath);
|
|
733
|
+
if (entry.isFile() && entry.name.endsWith(".json")) return [entryPath];
|
|
734
|
+
return [];
|
|
735
|
+
}))).flat();
|
|
736
|
+
}
|
|
737
|
+
async function readFileMetadataArtifacts(outDir, options) {
|
|
738
|
+
const metadataDir = getMetadataRoot(outDir);
|
|
739
|
+
try {
|
|
740
|
+
await fs.access(metadataDir);
|
|
741
|
+
} catch {
|
|
742
|
+
throw new Error(`Expected file metadata output at ${metadataDir}. Rebuild workflows before deploying.`);
|
|
743
|
+
}
|
|
744
|
+
const jsonFiles = await collectJsonFiles(metadataDir);
|
|
745
|
+
return { entries: (await Promise.all(jsonFiles.map(async (filePath) => {
|
|
746
|
+
const raw = await fs.readFile(filePath, "utf-8").catch((error) => {
|
|
747
|
+
throw new Error(`Failed to read file metadata at ${filePath}: ${formatArtifactError(error instanceof Error ? error : String(error))}`);
|
|
748
|
+
});
|
|
749
|
+
try {
|
|
750
|
+
return FileMetadataSchema.parse(JSON.parse(raw));
|
|
751
|
+
} catch (error) {
|
|
752
|
+
throw new Error(`Failed to parse file metadata at ${filePath}: ${formatArtifactError(error instanceof Error ? error : String(error))}`);
|
|
753
|
+
}
|
|
754
|
+
})).catch((error) => {
|
|
755
|
+
options?.onWarning?.(error instanceof Error ? error.message : String(error));
|
|
756
|
+
throw error;
|
|
757
|
+
})).sort((left, right) => left.filePath.localeCompare(right.filePath)) };
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Discovers agent artifacts from the build output directory.
|
|
761
|
+
* Scans outDir/ for agent_* directories containing manifest.json and the
|
|
762
|
+
* packaged sandbox bootstrap archive.
|
|
763
|
+
*/
|
|
764
|
+
async function discoverAgentArtifacts(outDir, options) {
|
|
765
|
+
const { strict = false, onWarning } = options ?? {};
|
|
766
|
+
const filterSet = options?.filter ? new Set(options.filter) : void 0;
|
|
767
|
+
let dirEntries;
|
|
768
|
+
try {
|
|
769
|
+
dirEntries = await fs.readdir(outDir);
|
|
770
|
+
} catch {
|
|
771
|
+
return [];
|
|
772
|
+
}
|
|
773
|
+
const agents = [];
|
|
774
|
+
for (const entryName of dirEntries) {
|
|
775
|
+
if (!entryName.startsWith("agent_")) continue;
|
|
776
|
+
const artifactDir = path$1.join(outDir, entryName);
|
|
777
|
+
const manifestPath = getWorkflowManifestPath(outDir, entryName);
|
|
778
|
+
try {
|
|
779
|
+
const manifestRaw = await fs.readFile(manifestPath, "utf-8").catch((error) => {
|
|
780
|
+
throw new Error(`Failed to read agent manifest at ${manifestPath}: ${formatArtifactError(error instanceof Error ? error : String(error))}`);
|
|
781
|
+
});
|
|
782
|
+
let manifestData;
|
|
783
|
+
try {
|
|
784
|
+
manifestData = AgentVersionManifestSchema.parse(JSON.parse(manifestRaw));
|
|
785
|
+
} catch (error) {
|
|
786
|
+
throw new Error(`Failed to parse agent manifest at ${manifestPath}: ${formatArtifactError(error instanceof Error ? error : String(error))}`);
|
|
787
|
+
}
|
|
788
|
+
if (filterSet && !filterSet.has(manifestData.agentId) && !filterSet.has(manifestData.authoredAgentId) && !filterSet.has(manifestData.agentName)) continue;
|
|
789
|
+
const sandboxPackagePath = path$1.join(artifactDir, AGENT_SANDBOX_PACKAGE_RELATIVE_PATH);
|
|
790
|
+
await fs.access(sandboxPackagePath).catch((error) => {
|
|
791
|
+
throw new Error(`Failed to read agent sandbox package at ${sandboxPackagePath}: ${formatArtifactError(error instanceof Error ? error : String(error))}`);
|
|
792
|
+
});
|
|
793
|
+
const sandboxPackageInfo = await hashFile(sandboxPackagePath);
|
|
794
|
+
const sandboxPackageHash = SHA256HashSchema.parse(sandboxPackageInfo.hash);
|
|
795
|
+
agents.push({
|
|
796
|
+
agentId: manifestData.agentId,
|
|
797
|
+
agentName: manifestData.agentName,
|
|
798
|
+
sandboxPackagePath,
|
|
799
|
+
sandboxPackageHash,
|
|
800
|
+
sandboxPackageSize: sandboxPackageInfo.size,
|
|
801
|
+
manifestData,
|
|
802
|
+
sandboxArtifacts: []
|
|
803
|
+
});
|
|
804
|
+
} catch (error) {
|
|
805
|
+
if (strict) throw error;
|
|
806
|
+
onWarning?.(`Skipping agent artifact ${entryName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return agents.sort((left, right) => left.agentName.localeCompare(right.agentName));
|
|
810
|
+
}
|
|
811
|
+
function formatArtifactError(error) {
|
|
812
|
+
return error instanceof Error ? error.message : String(error);
|
|
813
|
+
}
|
|
814
|
+
/** Uploads a single test bundle, returning the storage path. Throws on failure. */
|
|
815
|
+
async function uploadTestBundle(client, workflow, _localMode) {
|
|
816
|
+
const { results } = await client.bundles.upload([{
|
|
817
|
+
workflowName: workflow.name,
|
|
818
|
+
bundle: workflow.bundle,
|
|
819
|
+
bundlePath: workflow.bundlePath,
|
|
820
|
+
bundleHash: workflow.bundleHash,
|
|
821
|
+
bundleSize: workflow.bundleSize,
|
|
822
|
+
bundleFileName: path$1.basename(workflow.bundlePath)
|
|
823
|
+
}], "test");
|
|
824
|
+
const result = results[0];
|
|
825
|
+
if (!result || result.kind !== "success") {
|
|
826
|
+
const error = result?.kind === "failure" ? result.error : "Unknown upload error";
|
|
827
|
+
throw new Error(`Upload failed for "${workflow.name}": ${error}`);
|
|
828
|
+
}
|
|
829
|
+
return { storagePath: result.storagePath };
|
|
830
|
+
}
|
|
831
|
+
/** Uploads bundles to storage (cloud) or resolves local paths. Returns one result per workflow. */
|
|
832
|
+
async function uploadWorkflowBundles(client, workflows, _localMode, concurrency) {
|
|
833
|
+
if (workflows.length === 0) return [];
|
|
834
|
+
const { results } = await client.bundles.upload(workflows.map((workflow) => ({
|
|
835
|
+
workflowName: workflow.name,
|
|
836
|
+
bundleHash: workflow.bundleHash,
|
|
837
|
+
bundleSize: workflow.bundleSize,
|
|
838
|
+
bundle: workflow.bundle,
|
|
839
|
+
bundlePath: workflow.bundlePath,
|
|
840
|
+
bundleFileName: path$1.basename(workflow.bundlePath)
|
|
841
|
+
})), "deploy", { concurrency });
|
|
842
|
+
return results;
|
|
843
|
+
}
|
|
844
|
+
/** Uploads agent bundles to storage (cloud) or resolves local paths. Returns one result per agent. */
|
|
845
|
+
async function uploadAgentBundles(client, agents, _localMode, concurrency) {
|
|
846
|
+
if (agents.length === 0) return [];
|
|
847
|
+
const { agents: agentUrls } = await client.bundles.getPresignedUrl({ agents: agents.map((agent) => ({
|
|
848
|
+
agentId: agent.agentId,
|
|
849
|
+
agentName: agent.agentName,
|
|
850
|
+
bundleHash: agent.sandboxPackageHash,
|
|
851
|
+
bundleSize: agent.sandboxPackageSize,
|
|
852
|
+
bundleFileName: path$1.basename(agent.sandboxPackagePath)
|
|
853
|
+
})) });
|
|
854
|
+
const { results } = await batchUpload(agents.map((agent) => ({
|
|
855
|
+
name: agent.agentId,
|
|
856
|
+
bundle: agent.sandboxPackage,
|
|
857
|
+
bundlePath: agent.sandboxPackagePath
|
|
858
|
+
})), agentUrls, { concurrency });
|
|
859
|
+
return results;
|
|
860
|
+
}
|
|
861
|
+
/** Uploads task bundles to storage (cloud) or resolves local paths. Returns one result per task. */
|
|
862
|
+
async function uploadTaskBundles(client, tasks, _localMode, concurrency) {
|
|
863
|
+
if (tasks.length === 0) return [];
|
|
864
|
+
const { results } = await client.bundles.upload(tasks.map((task) => ({
|
|
865
|
+
workflowName: task.taskId,
|
|
866
|
+
bundleHash: task.bundleHash,
|
|
867
|
+
bundleSize: task.bundleSize,
|
|
868
|
+
bundle: task.bundle,
|
|
869
|
+
bundlePath: task.bundlePath,
|
|
870
|
+
bundleFileName: path$1.basename(task.bundlePath)
|
|
871
|
+
})), "deploy", { concurrency });
|
|
872
|
+
return results;
|
|
873
|
+
}
|
|
874
|
+
const CONCURRENT_DEPLOY_RETRY_DELAYS_MS = [
|
|
875
|
+
2e3,
|
|
876
|
+
5e3,
|
|
877
|
+
1e4
|
|
878
|
+
];
|
|
879
|
+
function isConcurrentVersionConflict(error) {
|
|
880
|
+
if (!error) return false;
|
|
881
|
+
return error.includes("duplicate key value violates unique constraint") && /Key \((step_id|workflow_id|authored_workflow_id), version\)=.*already exists\./.test(error);
|
|
882
|
+
}
|
|
883
|
+
function shouldRetryConcurrentDeployFailure(result) {
|
|
884
|
+
if (result.success) return false;
|
|
885
|
+
const failedWorkflows = result.workflows.filter((workflow) => workflow.status === "failed");
|
|
886
|
+
if (failedWorkflows.length === 0) return false;
|
|
887
|
+
if (result.agents.some((agent) => agent.status === "failed")) return false;
|
|
888
|
+
if (result.tasks?.some((task) => task.status === "failed")) return false;
|
|
889
|
+
return failedWorkflows.every((workflow) => isConcurrentVersionConflict(workflow.error));
|
|
890
|
+
}
|
|
891
|
+
/** Deploys workflows and agents from a build output directory. */
|
|
892
|
+
async function deployFromDir(options) {
|
|
893
|
+
const { outDir, workflowsDir, client, isLocalMode: localMode = false, dryRun = false, concurrency = 5, force = false, filter, artifactFilter, artifactKinds, deploymentScope = "project", onDeployProgressEvent } = options;
|
|
894
|
+
const artifactKindSet = new Set(artifactKinds ?? [
|
|
895
|
+
"workflow",
|
|
896
|
+
"agent",
|
|
897
|
+
"task"
|
|
898
|
+
]);
|
|
899
|
+
const startTime = Date.now();
|
|
900
|
+
const projectRoot = path$1.resolve(workflowsDir);
|
|
901
|
+
const { organizationId, projectId } = await assertWorkflowProjectRoot(projectRoot);
|
|
902
|
+
const prepareStartTime = Date.now();
|
|
903
|
+
onDeployProgressEvent?.({
|
|
904
|
+
phase: "prepare",
|
|
905
|
+
status: "start",
|
|
906
|
+
workflowCount: 0
|
|
907
|
+
});
|
|
908
|
+
const warningHandler = (warning) => onDeployProgressEvent?.({
|
|
909
|
+
phase: "prepare",
|
|
910
|
+
status: "warning",
|
|
911
|
+
warning
|
|
912
|
+
});
|
|
913
|
+
const workflowFilter = artifactFilter?.workflows ?? filter;
|
|
914
|
+
const agentFilter = artifactFilter?.agents;
|
|
915
|
+
const taskFilter = artifactFilter?.tasks ?? filter;
|
|
916
|
+
const prepared = artifactKindSet.has("workflow") ? await readWorkflowsFromDisk(outDir, {
|
|
917
|
+
onWarning: warningHandler,
|
|
918
|
+
filter: workflowFilter
|
|
919
|
+
}) : [];
|
|
920
|
+
const preparedAgents = artifactKindSet.has("agent") ? await discoverAgentArtifacts(outDir, {
|
|
921
|
+
onWarning: warningHandler,
|
|
922
|
+
filter: agentFilter
|
|
923
|
+
}) : [];
|
|
924
|
+
const preparedTasks = artifactKindSet.has("task") ? await readTasksFromDisk(outDir, {
|
|
925
|
+
onWarning: warningHandler,
|
|
926
|
+
filter: taskFilter
|
|
927
|
+
}) : [];
|
|
928
|
+
const fileMetadata = await readFileMetadataArtifacts(outDir, { onWarning: warningHandler });
|
|
929
|
+
onDeployProgressEvent?.({
|
|
930
|
+
phase: "prepare",
|
|
931
|
+
status: "complete",
|
|
932
|
+
workflowCount: prepared.length,
|
|
933
|
+
agentCount: preparedAgents.length,
|
|
934
|
+
taskCount: preparedTasks.length,
|
|
935
|
+
elapsedMs: Date.now() - prepareStartTime
|
|
936
|
+
});
|
|
937
|
+
if (filter && prepared.length === 0 && preparedTasks.length === 0) {
|
|
938
|
+
onDeployProgressEvent?.({
|
|
939
|
+
phase: "filter",
|
|
940
|
+
status: "complete",
|
|
941
|
+
workflowsToUpload: 0,
|
|
942
|
+
workflowsSkipped: 0,
|
|
943
|
+
agentsToUpload: 0,
|
|
944
|
+
agentsSkipped: 0,
|
|
945
|
+
tasksToUpload: 0,
|
|
946
|
+
tasksSkipped: 0,
|
|
947
|
+
force,
|
|
948
|
+
elapsedMs: 0
|
|
949
|
+
});
|
|
950
|
+
return finalizeDeployResult({
|
|
951
|
+
workflows: [],
|
|
952
|
+
agents: [],
|
|
953
|
+
tasks: [],
|
|
954
|
+
startTime,
|
|
955
|
+
onDeployProgressEvent
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
if (dryRun) return finalizeDeployResult({
|
|
959
|
+
workflows: prepared.map((wf) => ({
|
|
960
|
+
name: wf.name,
|
|
961
|
+
status: "skipped"
|
|
962
|
+
})),
|
|
963
|
+
agents: preparedAgents.map((agent) => ({
|
|
964
|
+
name: agent.agentName,
|
|
965
|
+
status: "skipped"
|
|
966
|
+
})),
|
|
967
|
+
tasks: preparedTasks.map((task) => ({
|
|
968
|
+
name: task.name,
|
|
969
|
+
status: "skipped"
|
|
970
|
+
})),
|
|
971
|
+
startTime,
|
|
972
|
+
onDeployProgressEvent
|
|
973
|
+
});
|
|
974
|
+
let result = await executeDeploy({
|
|
975
|
+
prepared,
|
|
976
|
+
preparedAgents,
|
|
977
|
+
preparedTasks,
|
|
978
|
+
fileMetadata,
|
|
979
|
+
organizationId,
|
|
980
|
+
projectId,
|
|
981
|
+
projectRoot,
|
|
982
|
+
client,
|
|
983
|
+
localMode,
|
|
984
|
+
concurrency,
|
|
985
|
+
force,
|
|
986
|
+
deploymentScope,
|
|
987
|
+
startTime,
|
|
988
|
+
onDeployProgressEvent
|
|
989
|
+
});
|
|
990
|
+
for (const delayMs of CONCURRENT_DEPLOY_RETRY_DELAYS_MS) {
|
|
991
|
+
if (!shouldRetryConcurrentDeployFailure(result)) break;
|
|
992
|
+
await sleep(delayMs);
|
|
993
|
+
result = await executeDeploy({
|
|
994
|
+
prepared,
|
|
995
|
+
preparedAgents,
|
|
996
|
+
preparedTasks,
|
|
997
|
+
fileMetadata,
|
|
998
|
+
organizationId,
|
|
999
|
+
projectId,
|
|
1000
|
+
projectRoot,
|
|
1001
|
+
client,
|
|
1002
|
+
localMode,
|
|
1003
|
+
concurrency,
|
|
1004
|
+
force,
|
|
1005
|
+
deploymentScope,
|
|
1006
|
+
startTime,
|
|
1007
|
+
onDeployProgressEvent
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
return result;
|
|
1011
|
+
}
|
|
1012
|
+
async function executeDeploy(options) {
|
|
1013
|
+
const { prepared, preparedAgents, preparedTasks, fileMetadata, organizationId, projectId, projectRoot, client, localMode, concurrency, force, deploymentScope, startTime, onDeployProgressEvent } = options;
|
|
1014
|
+
const { workflowArtifacts, agentArtifacts, taskArtifacts, workflowFilter, agentFilter, taskFilter } = await hashCheckAndFilter({
|
|
1015
|
+
prepared,
|
|
1016
|
+
preparedAgents,
|
|
1017
|
+
preparedTasks,
|
|
1018
|
+
client,
|
|
1019
|
+
organizationId,
|
|
1020
|
+
projectId,
|
|
1021
|
+
force,
|
|
1022
|
+
onDeployProgressEvent
|
|
1023
|
+
});
|
|
1024
|
+
const workflowUploadResults = await runUploadPhase({
|
|
1025
|
+
phase: "upload-workflows",
|
|
1026
|
+
count: workflowFilter.workflowsToUpload.length,
|
|
1027
|
+
concurrency,
|
|
1028
|
+
upload: () => uploadWorkflowBundles(client, workflowFilter.workflowsToUpload, localMode, concurrency),
|
|
1029
|
+
onDeployProgressEvent
|
|
1030
|
+
});
|
|
1031
|
+
const agentUploadResults = await runUploadPhase({
|
|
1032
|
+
phase: "upload-agents",
|
|
1033
|
+
count: agentFilter.agentsToUpload.length,
|
|
1034
|
+
concurrency,
|
|
1035
|
+
upload: () => uploadAgentBundles(client, agentFilter.agentsToUpload, localMode, concurrency),
|
|
1036
|
+
onDeployProgressEvent
|
|
1037
|
+
});
|
|
1038
|
+
const taskUploadResults = await runUploadPhase({
|
|
1039
|
+
phase: "upload-tasks",
|
|
1040
|
+
count: taskFilter.tasksToUpload.length,
|
|
1041
|
+
concurrency,
|
|
1042
|
+
upload: () => uploadTaskBundles(client, taskFilter.tasksToUpload, localMode, concurrency),
|
|
1043
|
+
onDeployProgressEvent
|
|
1044
|
+
});
|
|
1045
|
+
const workflowFailedUploads = toFailedUploadResults(workflowUploadResults);
|
|
1046
|
+
const agentFailedUploads = toFailedUploadResults(agentUploadResults);
|
|
1047
|
+
const taskFailedUploads = toFailedUploadResults(taskUploadResults);
|
|
1048
|
+
const successfulWorkflowUploads = workflowUploadResults.filter((r) => r.kind === "success");
|
|
1049
|
+
const successfulAgentUploads = agentUploadResults.filter((r) => r.kind === "success");
|
|
1050
|
+
const successfulTaskUploads = taskUploadResults.filter((r) => r.kind === "success");
|
|
1051
|
+
if (workflowFailedUploads.length > 0 || agentFailedUploads.length > 0 || taskFailedUploads.length > 0) return finalizeDeployResult({
|
|
1052
|
+
workflows: [
|
|
1053
|
+
...workflowFilter.skippedResults,
|
|
1054
|
+
...workflowFailedUploads,
|
|
1055
|
+
...successfulWorkflowUploads.map((u) => ({
|
|
1056
|
+
name: u.name,
|
|
1057
|
+
status: "skipped"
|
|
1058
|
+
}))
|
|
1059
|
+
],
|
|
1060
|
+
agents: [
|
|
1061
|
+
...agentFilter.skippedResults,
|
|
1062
|
+
...agentFailedUploads,
|
|
1063
|
+
...successfulAgentUploads.map((u) => ({
|
|
1064
|
+
name: u.name,
|
|
1065
|
+
status: "skipped"
|
|
1066
|
+
}))
|
|
1067
|
+
],
|
|
1068
|
+
tasks: [
|
|
1069
|
+
...taskFilter.skippedResults,
|
|
1070
|
+
...taskFailedUploads,
|
|
1071
|
+
...successfulTaskUploads.map((u) => ({
|
|
1072
|
+
name: u.name,
|
|
1073
|
+
status: "skipped"
|
|
1074
|
+
}))
|
|
1075
|
+
],
|
|
1076
|
+
startTime,
|
|
1077
|
+
onDeployProgressEvent
|
|
1078
|
+
});
|
|
1079
|
+
const registration = await registerDeployment({
|
|
1080
|
+
prepared,
|
|
1081
|
+
preparedAgents,
|
|
1082
|
+
preparedTasks,
|
|
1083
|
+
successfulWorkflowUploads,
|
|
1084
|
+
successfulAgentUploads,
|
|
1085
|
+
successfulTaskUploads,
|
|
1086
|
+
workflowArtifacts,
|
|
1087
|
+
agentArtifacts,
|
|
1088
|
+
taskArtifacts,
|
|
1089
|
+
projectId,
|
|
1090
|
+
organizationId,
|
|
1091
|
+
projectRoot,
|
|
1092
|
+
client,
|
|
1093
|
+
force,
|
|
1094
|
+
deploymentScope,
|
|
1095
|
+
onDeployProgressEvent
|
|
1096
|
+
});
|
|
1097
|
+
if (!(registration.workflows.some((wf) => wf.status === "failed") || registration.agents.some((a) => a.status === "failed") || registration.tasks.some((t) => t.status === "failed"))) await syncMetadata({
|
|
1098
|
+
client,
|
|
1099
|
+
organizationId,
|
|
1100
|
+
projectId,
|
|
1101
|
+
fileMetadata,
|
|
1102
|
+
onDeployProgressEvent
|
|
1103
|
+
});
|
|
1104
|
+
return finalizeDeployResult({
|
|
1105
|
+
workflows: registration.workflows,
|
|
1106
|
+
agents: registration.agents,
|
|
1107
|
+
tasks: registration.tasks,
|
|
1108
|
+
startTime,
|
|
1109
|
+
onDeployProgressEvent
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
async function sleep(ms) {
|
|
1113
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
1114
|
+
}
|
|
1115
|
+
//#endregion
|
|
1116
|
+
export { readManifestsFromOutDir as a, readAgentManifestsFromOutDir as i, deployFromDir as n, readWorkflowsFromDisk as o, dist_exports as r, uploadTestBundle as s, collectCredentialFingerprintMapFromProjectDist as t };
|