@terminals-tech/sdk 1.0.0-rc.1 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/WebContainerManager-SHXC5VKI.js +22 -0
- package/dist/browser-http-client-ZQLDWZMU.js +317 -0
- package/dist/cache-VKYSQRXX.js +45 -0
- package/dist/capabilities-MIPUMBLL.js +96 -0
- package/dist/chunk-2ESYSVXG.js +48 -0
- package/dist/chunk-2WTYE4SW.js +190 -0
- package/dist/chunk-3CEM77QZ.js +474 -0
- package/dist/chunk-3LFMIVJM.js +40 -0
- package/dist/chunk-ABCK4FWN.js +136 -0
- package/dist/chunk-AFDUOYHD.js +2060 -0
- package/dist/chunk-BCOQMFKT.js +265 -0
- package/dist/chunk-BKB3MD5Y.js +723 -0
- package/dist/chunk-BYXBJQAS.js +0 -0
- package/dist/chunk-DKFJIILR.js +9798 -0
- package/dist/chunk-EXI3LJVJ.js +51 -0
- package/dist/chunk-FOXUEYWK.js +42 -0
- package/dist/chunk-GJWAJAX3.js +173 -0
- package/dist/chunk-H3POJCFA.js +333 -0
- package/dist/chunk-KHR7ZYCX.js +4034 -0
- package/dist/chunk-NTMBOESX.js +152 -0
- package/dist/chunk-OSSRZOGC.js +190 -0
- package/dist/chunk-PUZ2S62E.js +977 -0
- package/dist/chunk-PWAHFID5.js +381 -0
- package/dist/chunk-Q2VI6ICE.js +188 -0
- package/dist/chunk-Q4B7YS7T.js +557 -0
- package/dist/chunk-QJFKEQHF.js +6460 -0
- package/dist/chunk-QWXPVB2L.js +320 -0
- package/dist/chunk-QWZRZKLZ.js +896 -0
- package/dist/chunk-SHPGIVDH.js +5521 -0
- package/dist/chunk-TSQ3BGLA.js +11945 -0
- package/dist/chunk-UJDUQNE2.js +79 -0
- package/dist/chunk-UKVUPKZP.js +296 -0
- package/dist/chunk-VZA2NUH3.js +118 -0
- package/dist/chunk-WGBCRNMB.js +1817 -0
- package/dist/chunk-WU4OTGJE.js +752 -0
- package/dist/chunk-XPJ63Y6T.js +70 -0
- package/dist/chunk-Y2EULKA2.js +172 -0
- package/dist/chunk-YJEZWCYV.js +94 -0
- package/dist/chunk-ZVO47SQV.js +150 -0
- package/dist/container-lite-KQX3NMPY.js +327 -0
- package/dist/core-H2UUDATO.js +146 -0
- package/dist/crypto-D4LMI2RN.js +45 -0
- package/dist/db-BWC2GGBN.js +50 -0
- package/dist/demo-VXMGMJNK.js +87 -0
- package/dist/diagnostics-6RQTBR6I.js +113 -0
- package/dist/dist-OPDCWARF.js +727 -0
- package/dist/dist-VXJEKX3T.js +2441 -0
- package/dist/dist-VYGJXGUS.js +1008 -0
- package/dist/embeddings-7QXTXUMC.js +15 -0
- package/dist/embeddings-MAEWWUHW.js +9 -0
- package/dist/graph-RKMNE2X5.js +36 -0
- package/dist/hvm-DRQK2MUT.js +126 -0
- package/dist/index.cjs +11706 -4004
- package/dist/index.d.cts +1299 -1339
- package/dist/index.d.ts +1299 -1339
- package/dist/index.js +189 -7938
- package/dist/mcp-NK34ZNM5.js +101 -0
- package/dist/mcp-client-service-browser-SGB2K3VZ.js +14 -0
- package/dist/neuro-state-XHRGIRVO.js +498 -0
- package/dist/nodes-DXKYDTVO.js +224 -0
- package/dist/package-EXUIU2RL.js +93 -0
- package/dist/package-VGL7HYTO.js +106 -0
- package/dist/package-XHMLOAQ4.js +98 -0
- package/dist/pg-events-QJAM2HIP.js +15 -0
- package/dist/pgliteService-IUGNNOVU.js +258 -0
- package/dist/policy-IRJCM6FS.js +13 -0
- package/dist/registry-BL3TDQDB.js +26 -0
- package/dist/registry-FW63E7FE.js +16 -0
- package/dist/registry-ZQ2IBLF6.js +9 -0
- package/dist/scheduler-H6Q53IMI.js +122 -0
- package/dist/secret-store-H7273UIT.js +18 -0
- package/dist/server-7DM74VFW.js +18 -0
- package/dist/skills-KLTTT2RM.js +6375 -0
- package/dist/stack-CHDAFU2S.js +103 -0
- package/dist/storage-L7MWNSPG.js +13 -0
- package/dist/supabaseService-6AYP2VY3.js +476 -0
- package/dist/topology-CIWWNVAN.js +13 -0
- package/dist/webcontainer-3LDJVIIL.js +281 -0
- package/package.json +2 -2
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getGlobalStore
|
|
3
|
+
} from "./chunk-VZA2NUH3.js";
|
|
4
|
+
import {
|
|
5
|
+
resolveSecretValue
|
|
6
|
+
} from "./chunk-QWXPVB2L.js";
|
|
7
|
+
import {
|
|
8
|
+
onMeshFeedback,
|
|
9
|
+
orchestratorToMesh
|
|
10
|
+
} from "./chunk-Q2VI6ICE.js";
|
|
11
|
+
import {
|
|
12
|
+
registerManifestProvider
|
|
13
|
+
} from "./chunk-3CEM77QZ.js";
|
|
14
|
+
import {
|
|
15
|
+
safeDbOperation
|
|
16
|
+
} from "./chunk-WGBCRNMB.js";
|
|
17
|
+
import {
|
|
18
|
+
getDB,
|
|
19
|
+
isDatabaseReady,
|
|
20
|
+
withDB
|
|
21
|
+
} from "./chunk-AFDUOYHD.js";
|
|
22
|
+
|
|
23
|
+
// ../../lib/terminals-tech/machines/core/stacksStore.ts
|
|
24
|
+
var globalStore = globalThis;
|
|
25
|
+
var memStacks = globalStore.__memStacks || /* @__PURE__ */ new Map();
|
|
26
|
+
globalStore.__memStacks = memStacks;
|
|
27
|
+
var pendingDeletes = globalStore.__pendingStackDeletes || /* @__PURE__ */ new Set();
|
|
28
|
+
globalStore.__pendingStackDeletes = pendingDeletes;
|
|
29
|
+
registerManifestProvider("stacksStore", loadStack);
|
|
30
|
+
async function loadStack(id) {
|
|
31
|
+
const { getDB: getDB2, safeDbOperation: safeDbOperation2 } = await import("./pgliteService-IUGNNOVU.js");
|
|
32
|
+
const dbResult = await safeDbOperation2(async () => {
|
|
33
|
+
const db = await getDB2();
|
|
34
|
+
await db.query(`ALTER TABLE stacks ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP`);
|
|
35
|
+
const r = await db.query(
|
|
36
|
+
`SELECT manifest FROM stacks WHERE id = $1 AND deleted_at IS NULL`,
|
|
37
|
+
[id]
|
|
38
|
+
);
|
|
39
|
+
if (!r.rows[0]) return null;
|
|
40
|
+
return r.rows[0].manifest;
|
|
41
|
+
}, null);
|
|
42
|
+
if (dbResult) return dbResult;
|
|
43
|
+
return memStacks.get(id)?.manifest || null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ../../lib/terminals-tech/machines/core/manifests.ts
|
|
47
|
+
function isNodeManifest(manifest) {
|
|
48
|
+
return "kind" in manifest && "entrypoints" in manifest;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ../../lib/terminals-tech/machines/core/validator.ts
|
|
52
|
+
function isAcyclic(nodes, edges) {
|
|
53
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
54
|
+
const out = /* @__PURE__ */ new Map();
|
|
55
|
+
nodes.forEach((n) => {
|
|
56
|
+
incoming.set(n.id, 0);
|
|
57
|
+
out.set(n.id, []);
|
|
58
|
+
});
|
|
59
|
+
edges.forEach((e) => {
|
|
60
|
+
incoming.set(e.to, (incoming.get(e.to) || 0) + 1);
|
|
61
|
+
const fromList = out.get(e.from);
|
|
62
|
+
if (fromList) fromList.push(e.to);
|
|
63
|
+
});
|
|
64
|
+
const queue = nodes.filter((n) => (incoming.get(n.id) || 0) === 0).map((n) => n.id);
|
|
65
|
+
let visited = 0;
|
|
66
|
+
while (queue.length) {
|
|
67
|
+
const id = queue.shift();
|
|
68
|
+
if (!id) break;
|
|
69
|
+
visited++;
|
|
70
|
+
for (const to of out.get(id) || []) {
|
|
71
|
+
incoming.set(to, (incoming.get(to) || 0) - 1);
|
|
72
|
+
if ((incoming.get(to) || 0) === 0) queue.push(to);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return visited === nodes.length;
|
|
76
|
+
}
|
|
77
|
+
function validateStack(manifest) {
|
|
78
|
+
const issues = [];
|
|
79
|
+
const ids = /* @__PURE__ */ new Set();
|
|
80
|
+
for (const n of manifest.nodes) {
|
|
81
|
+
if (ids.has(n.id))
|
|
82
|
+
issues.push({
|
|
83
|
+
code: "node.duplicateId",
|
|
84
|
+
message: `Duplicate node id: ${n.id}`,
|
|
85
|
+
details: { nodeId: n.id }
|
|
86
|
+
});
|
|
87
|
+
ids.add(n.id);
|
|
88
|
+
if (!n.nodeRef)
|
|
89
|
+
issues.push({
|
|
90
|
+
code: "node.nodeRef.required",
|
|
91
|
+
message: `Node ${n.id} missing nodeRef`,
|
|
92
|
+
details: { nodeId: n.id }
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
for (const e of manifest.edges || []) {
|
|
96
|
+
if (!ids.has(e.from))
|
|
97
|
+
issues.push({
|
|
98
|
+
code: "edge.from.missing",
|
|
99
|
+
message: `Edge from missing node: ${e.from}`,
|
|
100
|
+
details: { fromId: e.from }
|
|
101
|
+
});
|
|
102
|
+
if (!ids.has(e.to))
|
|
103
|
+
issues.push({
|
|
104
|
+
code: "edge.to.missing",
|
|
105
|
+
message: `Edge to missing node: ${e.to}`,
|
|
106
|
+
details: { toId: e.to }
|
|
107
|
+
});
|
|
108
|
+
if (e.from === e.to)
|
|
109
|
+
issues.push({
|
|
110
|
+
code: "edge.self",
|
|
111
|
+
message: `Self edge not allowed: ${e.from}`,
|
|
112
|
+
details: { fromId: e.from, toId: e.to }
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (!isAcyclic(manifest.nodes, manifest.edges || []))
|
|
116
|
+
issues.push({
|
|
117
|
+
code: "graph.cycle",
|
|
118
|
+
message: "Graph contains a cycle; v0 requires DAG"
|
|
119
|
+
});
|
|
120
|
+
if (manifest.runtimes?.default && manifest.runtimes.default.length === 0)
|
|
121
|
+
issues.push({
|
|
122
|
+
code: "runtimes.default.empty",
|
|
123
|
+
message: "Runtimes default cannot be empty"
|
|
124
|
+
});
|
|
125
|
+
if (manifest.runtimes?.overrides) {
|
|
126
|
+
for (const [nid, arr] of Object.entries(manifest.runtimes.overrides)) {
|
|
127
|
+
if (!ids.has(nid))
|
|
128
|
+
issues.push({
|
|
129
|
+
code: "runtimes.override.unknownNode",
|
|
130
|
+
message: `Runtime override for unknown node: ${nid}`,
|
|
131
|
+
details: { nodeId: nid }
|
|
132
|
+
});
|
|
133
|
+
if (!arr || arr.length === 0)
|
|
134
|
+
issues.push({
|
|
135
|
+
code: "runtimes.override.empty",
|
|
136
|
+
message: `Runtime override empty for node: ${nid}`,
|
|
137
|
+
details: { nodeId: nid }
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (manifest.secrets) {
|
|
142
|
+
for (const [name, scope] of Object.entries(manifest.secrets)) {
|
|
143
|
+
for (const nid of scope.scope)
|
|
144
|
+
if (!ids.has(nid))
|
|
145
|
+
issues.push({
|
|
146
|
+
code: "secrets.scope.unknownNode",
|
|
147
|
+
message: `Secret ${name} references unknown node ${nid}`,
|
|
148
|
+
details: { secretName: name, nodeId: nid }
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return issues.length === 0 ? { ok: true } : { ok: false, issues };
|
|
153
|
+
}
|
|
154
|
+
function toSimpleType(t) {
|
|
155
|
+
if (!t) return void 0;
|
|
156
|
+
if (typeof t === "string") return t;
|
|
157
|
+
if (Array.isArray(t)) {
|
|
158
|
+
const arr = t.filter((x) => typeof x === "string");
|
|
159
|
+
if (arr.length === 1) return arr[0];
|
|
160
|
+
}
|
|
161
|
+
return void 0;
|
|
162
|
+
}
|
|
163
|
+
function getRequiredInputs(manifest) {
|
|
164
|
+
const required = /* @__PURE__ */ new Set();
|
|
165
|
+
const schema = manifest.inputsSchema;
|
|
166
|
+
if (schema && Array.isArray(schema.required)) {
|
|
167
|
+
for (const k of schema.required) if (typeof k === "string") required.add(k);
|
|
168
|
+
}
|
|
169
|
+
return required;
|
|
170
|
+
}
|
|
171
|
+
function getPropertyType(schema, key) {
|
|
172
|
+
if (!schema || typeof schema !== "object") return void 0;
|
|
173
|
+
const props = schema.properties || {};
|
|
174
|
+
const prop = props ? props[key] : void 0;
|
|
175
|
+
if (!prop) return void 0;
|
|
176
|
+
return toSimpleType(prop.type);
|
|
177
|
+
}
|
|
178
|
+
async function linkAndValidateStack(manifest, resolver) {
|
|
179
|
+
const base = validateStack(manifest);
|
|
180
|
+
const issues = base.ok ? [] : base.issues.slice();
|
|
181
|
+
const nodeIdToManifest = /* @__PURE__ */ new Map();
|
|
182
|
+
for (const node of manifest.nodes) {
|
|
183
|
+
const m = await resolver(node.nodeRef);
|
|
184
|
+
if (!m) {
|
|
185
|
+
issues.push({
|
|
186
|
+
code: "link.nodeRef.unresolved",
|
|
187
|
+
message: `NodeRef '${node.nodeRef}' for node '${node.id}' could not be resolved.`,
|
|
188
|
+
details: { nodeId: node.id, nodeRef: node.nodeRef }
|
|
189
|
+
});
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
nodeIdToManifest.set(node.id, m);
|
|
193
|
+
}
|
|
194
|
+
if (issues.length > 0) return { ok: false, issues };
|
|
195
|
+
const inboundInputs = /* @__PURE__ */ new Map();
|
|
196
|
+
for (const e of manifest.edges || []) {
|
|
197
|
+
const toSet = inboundInputs.get(e.to) || /* @__PURE__ */ new Set();
|
|
198
|
+
const map = e.map || {};
|
|
199
|
+
for (const inKey of Object.values(map)) toSet.add(inKey);
|
|
200
|
+
inboundInputs.set(e.to, toSet);
|
|
201
|
+
}
|
|
202
|
+
for (const e of manifest.edges || []) {
|
|
203
|
+
const fromM = nodeIdToManifest.get(e.from);
|
|
204
|
+
const toM = nodeIdToManifest.get(e.to);
|
|
205
|
+
if (!fromM || !toM) continue;
|
|
206
|
+
const fromSchema = fromM.outputsSchema;
|
|
207
|
+
const toSchema = toM.inputsSchema;
|
|
208
|
+
const map = e.map || {};
|
|
209
|
+
for (const [outKey, inKey] of Object.entries(map)) {
|
|
210
|
+
const outType = getPropertyType(fromSchema, outKey);
|
|
211
|
+
const inType = getPropertyType(toSchema, inKey);
|
|
212
|
+
if (fromSchema && fromSchema.properties && !(outKey in fromSchema.properties)) {
|
|
213
|
+
issues.push({
|
|
214
|
+
code: "link.output.missing",
|
|
215
|
+
message: `Edge ${e.from}->${e.to} maps missing output '${outKey}' in '${fromM.id}'.`,
|
|
216
|
+
details: { fromId: e.from, toId: e.to, outKey, inKey }
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (toSchema && toSchema.properties && !(inKey in toSchema.properties)) {
|
|
220
|
+
issues.push({
|
|
221
|
+
code: "link.input.missing",
|
|
222
|
+
message: `Edge ${e.from}->${e.to} maps to missing input '${inKey}' in '${toM.id}'.`,
|
|
223
|
+
details: { fromId: e.from, toId: e.to, outKey, inKey }
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (outType && inType && outType !== inType) {
|
|
227
|
+
issues.push({
|
|
228
|
+
code: "link.typeMismatch",
|
|
229
|
+
message: `Type mismatch ${e.from}.${outKey} (${outType}) -> ${e.to}.${inKey} (${inType}).`,
|
|
230
|
+
details: { fromId: e.from, toId: e.to, outKey, inKey }
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
236
|
+
for (const n of manifest.nodes) nodeById.set(n.id, n);
|
|
237
|
+
for (const n of manifest.nodes) {
|
|
238
|
+
const m = nodeIdToManifest.get(n.id);
|
|
239
|
+
if (!m) continue;
|
|
240
|
+
const required = getRequiredInputs(m);
|
|
241
|
+
if (required.size === 0) continue;
|
|
242
|
+
const satisfiedByEdges = inboundInputs.get(n.id) || /* @__PURE__ */ new Set();
|
|
243
|
+
for (const key of required) {
|
|
244
|
+
const satisfiedByConfig = n.config && Object.prototype.hasOwnProperty.call(n.config, key);
|
|
245
|
+
const ok = satisfiedByConfig || satisfiedByEdges.has(key);
|
|
246
|
+
if (!ok) {
|
|
247
|
+
issues.push({
|
|
248
|
+
code: "link.requiredInput.unconnected",
|
|
249
|
+
message: `Node '${n.id}' (${m.id}) requires input '${key}' but it is neither in config nor provided by inbound edges.`,
|
|
250
|
+
details: { nodeId: n.id, inputKey: key }
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return issues.length === 0 ? { ok: true } : { ok: false, issues };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ../../lib/terminals-tech/machines/core/tracing.ts
|
|
259
|
+
var InMemoryTracer = class {
|
|
260
|
+
spans = [];
|
|
261
|
+
startSpan(name, attributes) {
|
|
262
|
+
const span = {
|
|
263
|
+
traceId: `${Date.now()}`,
|
|
264
|
+
spanId: Math.random().toString(36).slice(2),
|
|
265
|
+
name,
|
|
266
|
+
startTime: Date.now(),
|
|
267
|
+
attributes,
|
|
268
|
+
events: []
|
|
269
|
+
};
|
|
270
|
+
this.spans.push(span);
|
|
271
|
+
return span;
|
|
272
|
+
}
|
|
273
|
+
endSpan(span) {
|
|
274
|
+
span.endTime = Date.now();
|
|
275
|
+
}
|
|
276
|
+
addEvent(span, event) {
|
|
277
|
+
span.events?.push(event);
|
|
278
|
+
}
|
|
279
|
+
async export() {
|
|
280
|
+
return this.spans.slice();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// ../../lib/terminals-tech/machines/core/runStore.ts
|
|
285
|
+
var store = getGlobalStore("run_store");
|
|
286
|
+
function persistRunTrace(runId, spans) {
|
|
287
|
+
store.set(runId, { spans });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ../../lib/terminals-tech/machines/core/traceStore.ts
|
|
291
|
+
async function persistSpans(spans) {
|
|
292
|
+
const runId = spans[0]?.traceId;
|
|
293
|
+
if (runId) persistRunTrace(runId, spans);
|
|
294
|
+
await safeDbOperation(async () => {
|
|
295
|
+
const db = await getDB();
|
|
296
|
+
for (const s of spans) {
|
|
297
|
+
await db.query(
|
|
298
|
+
`INSERT INTO run_spans (trace_id, span_id, name, start_time, end_time, attributes)
|
|
299
|
+
VALUES ($1,$2,$3,$4,$5,$6)
|
|
300
|
+
ON CONFLICT (trace_id, span_id) DO UPDATE SET end_time = EXCLUDED.end_time, attributes = EXCLUDED.attributes`,
|
|
301
|
+
[
|
|
302
|
+
s.traceId,
|
|
303
|
+
s.spanId,
|
|
304
|
+
s.name,
|
|
305
|
+
new Date(s.startTime).toISOString(),
|
|
306
|
+
s.endTime ? new Date(s.endTime).toISOString() : null,
|
|
307
|
+
JSON.stringify(s.attributes || {})
|
|
308
|
+
]
|
|
309
|
+
);
|
|
310
|
+
for (const ev of s.events || []) {
|
|
311
|
+
await db.query(
|
|
312
|
+
`INSERT INTO run_span_events (trace_id, span_id, ts, event, attributes)
|
|
313
|
+
VALUES ($1,$2,$3,$4,$5)
|
|
314
|
+
ON CONFLICT DO NOTHING`,
|
|
315
|
+
[
|
|
316
|
+
s.traceId,
|
|
317
|
+
s.spanId,
|
|
318
|
+
new Date(ev.time).toISOString(),
|
|
319
|
+
ev.event,
|
|
320
|
+
JSON.stringify(ev.attributes || {})
|
|
321
|
+
]
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}, void 0);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ../../lib/terminals-tech/machines/core/runsMeta.ts
|
|
329
|
+
var memoryStore = /* @__PURE__ */ new Map();
|
|
330
|
+
async function persistRunMeta(meta) {
|
|
331
|
+
const list = memoryStore.get(meta.stackId) || [];
|
|
332
|
+
const merged = [meta, ...list.filter((m) => m.runId !== meta.runId)].slice(0, 50);
|
|
333
|
+
memoryStore.set(meta.stackId, merged);
|
|
334
|
+
if (!isDatabaseReady()) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
await withDB(async (db) => {
|
|
339
|
+
await db.query(
|
|
340
|
+
`INSERT INTO runs (run_id, stack_id, shape_hash, started_at, ended_at, status, metrics, feedback, replay_url, commit_sha, packages)
|
|
341
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
342
|
+
ON CONFLICT (run_id) DO UPDATE SET
|
|
343
|
+
ended_at = EXCLUDED.ended_at,
|
|
344
|
+
status = EXCLUDED.status,
|
|
345
|
+
metrics = EXCLUDED.metrics,
|
|
346
|
+
feedback = COALESCE(runs.feedback, '{}'::jsonb) || COALESCE(EXCLUDED.feedback, '{}'::jsonb),
|
|
347
|
+
replay_url = COALESCE(EXCLUDED.replay_url, runs.replay_url)`,
|
|
348
|
+
[
|
|
349
|
+
meta.runId,
|
|
350
|
+
meta.stackId,
|
|
351
|
+
meta.shapeHash || null,
|
|
352
|
+
meta.startedAt,
|
|
353
|
+
meta.endedAt,
|
|
354
|
+
meta.status,
|
|
355
|
+
JSON.stringify(meta.metrics || {}),
|
|
356
|
+
JSON.stringify(meta.feedback || {}),
|
|
357
|
+
meta.replayUrl || null,
|
|
358
|
+
meta.commit || null,
|
|
359
|
+
JSON.stringify(meta.packages || {})
|
|
360
|
+
]
|
|
361
|
+
);
|
|
362
|
+
});
|
|
363
|
+
} catch (err) {
|
|
364
|
+
console.warn("[runsMeta] PGLite persist failed:", err);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ../../lib/terminals-tech/machines/core/core.machine.orchestrator.ts
|
|
369
|
+
var Semaphore = class {
|
|
370
|
+
permits;
|
|
371
|
+
waiting = [];
|
|
372
|
+
constructor(permits) {
|
|
373
|
+
this.permits = permits;
|
|
374
|
+
}
|
|
375
|
+
async acquire() {
|
|
376
|
+
if (this.permits > 0) {
|
|
377
|
+
this.permits--;
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
return new Promise((resolve) => this.waiting.push(resolve));
|
|
381
|
+
}
|
|
382
|
+
release() {
|
|
383
|
+
if (this.waiting.length > 0) {
|
|
384
|
+
const next = this.waiting.shift();
|
|
385
|
+
next?.();
|
|
386
|
+
} else {
|
|
387
|
+
this.permits++;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
async function getPackageVersions() {
|
|
392
|
+
try {
|
|
393
|
+
const core = await import("./package-VGL7HYTO.js").catch(() => ({
|
|
394
|
+
version: "unavailable"
|
|
395
|
+
}));
|
|
396
|
+
const graph = await import("./package-EXUIU2RL.js").catch(() => ({
|
|
397
|
+
version: "unavailable"
|
|
398
|
+
}));
|
|
399
|
+
const embeddings = await import("./package-XHMLOAQ4.js").catch(() => ({
|
|
400
|
+
version: "unavailable"
|
|
401
|
+
}));
|
|
402
|
+
return {
|
|
403
|
+
core: core.version,
|
|
404
|
+
graph: graph.version,
|
|
405
|
+
embeddings: embeddings.version
|
|
406
|
+
};
|
|
407
|
+
} catch (err) {
|
|
408
|
+
console.error("[getPackageVersions] Failed to load package versions:", err);
|
|
409
|
+
return { core: "unknown", graph: "unknown", embeddings: "unknown" };
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
var OrchestratorV0 = class {
|
|
413
|
+
/**
|
|
414
|
+
* Creates a new orchestrator instance.
|
|
415
|
+
*
|
|
416
|
+
* @param catalogResolver - Function to resolve node/stack manifests by reference
|
|
417
|
+
* @param tracer - Distributed tracer for observability (default: InMemoryTracer)
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* ```typescript
|
|
421
|
+
* import { OrchestratorV0, resolveNode } from '@terminals-tech/machines';
|
|
422
|
+
*
|
|
423
|
+
* const orchestrator = new OrchestratorV0(resolveNode);
|
|
424
|
+
* ```
|
|
425
|
+
*/
|
|
426
|
+
constructor(catalogResolver, tracer = new InMemoryTracer()) {
|
|
427
|
+
this.catalogResolver = catalogResolver;
|
|
428
|
+
this.tracer = tracer;
|
|
429
|
+
this.meshFeedbackUnsub = onMeshFeedback((data) => {
|
|
430
|
+
this.handleMeshFeedback(data);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
// Adaptive concurrency based on mesh feedback (0.0 - 1.0 multiplier)
|
|
434
|
+
concurrencyMultiplier = 1;
|
|
435
|
+
// Mesh feedback unsubscriber
|
|
436
|
+
meshFeedbackUnsub = null;
|
|
437
|
+
// Track if we're under backpressure
|
|
438
|
+
underBackpressure = false;
|
|
439
|
+
// ============================================================================
|
|
440
|
+
// Neuro-Adaptive Execution State (Phase 3)
|
|
441
|
+
// ============================================================================
|
|
442
|
+
/** Whether execution is currently paused due to cognitive load */
|
|
443
|
+
isPaused = false;
|
|
444
|
+
/** Whether to check mood before each node batch (can be disabled for testing) */
|
|
445
|
+
moodCheckEnabled = true;
|
|
446
|
+
/** Resolver for waitForResume() promise */
|
|
447
|
+
resumeResolver = null;
|
|
448
|
+
/** Current run ID for pause/resume events */
|
|
449
|
+
currentRunId = null;
|
|
450
|
+
/** Coherence threshold below which to pause (0.0 - 1.0) */
|
|
451
|
+
pauseCoherenceThreshold = 0.3;
|
|
452
|
+
/**
|
|
453
|
+
* Handle feedback from mesh (L3 → L2)
|
|
454
|
+
* Adjusts concurrency and retry policy based on system health
|
|
455
|
+
*/
|
|
456
|
+
handleMeshFeedback(data) {
|
|
457
|
+
switch (data.type) {
|
|
458
|
+
case "health_warning":
|
|
459
|
+
console.log(`[OrchestratorV0] Health warning from mesh: ${data.warning}`);
|
|
460
|
+
this.underBackpressure = true;
|
|
461
|
+
this.concurrencyMultiplier = Math.max(0.25, this.concurrencyMultiplier * 0.75);
|
|
462
|
+
break;
|
|
463
|
+
case "metrics_update":
|
|
464
|
+
if (data.metrics) {
|
|
465
|
+
const { healthScore, errorRate } = data.metrics;
|
|
466
|
+
if (healthScore > 0.7 && errorRate < 0.1 && this.underBackpressure) {
|
|
467
|
+
this.concurrencyMultiplier = Math.min(1, this.concurrencyMultiplier * 1.1);
|
|
468
|
+
if (this.concurrencyMultiplier >= 0.95) {
|
|
469
|
+
this.underBackpressure = false;
|
|
470
|
+
this.concurrencyMultiplier = 1;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (healthScore < 0.3) {
|
|
474
|
+
this.underBackpressure = true;
|
|
475
|
+
this.concurrencyMultiplier = Math.max(0.1, this.concurrencyMultiplier * 0.5);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
break;
|
|
479
|
+
case "policy_update":
|
|
480
|
+
console.log("[OrchestratorV0] Received policy update from mesh");
|
|
481
|
+
break;
|
|
482
|
+
case "mood_recovery":
|
|
483
|
+
if (this.isPaused && data.recovery) {
|
|
484
|
+
const { coherence, healthScore } = data.recovery;
|
|
485
|
+
if (coherence > this.pauseCoherenceThreshold && healthScore > 0.3) {
|
|
486
|
+
console.log(
|
|
487
|
+
`[OrchestratorV0] Mood recovered (coherence: ${coherence.toFixed(2)}, health: ${healthScore.toFixed(2)}), resuming execution`
|
|
488
|
+
);
|
|
489
|
+
this.resume();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// ============================================================================
|
|
496
|
+
// Neuro-Adaptive Execution Methods (Phase 3)
|
|
497
|
+
// ============================================================================
|
|
498
|
+
/**
|
|
499
|
+
* Enable or disable mood checking before node batches.
|
|
500
|
+
* Useful for testing or when running trusted/internal stacks.
|
|
501
|
+
*/
|
|
502
|
+
setMoodCheckEnabled(enabled) {
|
|
503
|
+
this.moodCheckEnabled = enabled;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Check if the orchestrator is currently paused.
|
|
507
|
+
*/
|
|
508
|
+
getIsPaused() {
|
|
509
|
+
return this.isPaused;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Manually pause execution (e.g., from user request).
|
|
513
|
+
*/
|
|
514
|
+
pause(reason = "user_requested") {
|
|
515
|
+
if (this.isPaused) return;
|
|
516
|
+
this.isPaused = true;
|
|
517
|
+
if (this.currentRunId) {
|
|
518
|
+
orchestratorToMesh.onSystemPause(this.currentRunId, reason);
|
|
519
|
+
}
|
|
520
|
+
console.log(`[OrchestratorV0] Execution paused: ${reason}`);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Resume paused execution.
|
|
524
|
+
*/
|
|
525
|
+
resume() {
|
|
526
|
+
if (!this.isPaused) return;
|
|
527
|
+
this.isPaused = false;
|
|
528
|
+
if (this.currentRunId) {
|
|
529
|
+
orchestratorToMesh.onSystemResume(this.currentRunId);
|
|
530
|
+
}
|
|
531
|
+
console.log("[OrchestratorV0] Execution resumed");
|
|
532
|
+
if (this.resumeResolver) {
|
|
533
|
+
this.resumeResolver();
|
|
534
|
+
this.resumeResolver = null;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Wait for resume signal when paused.
|
|
539
|
+
* Returns immediately if not paused.
|
|
540
|
+
*/
|
|
541
|
+
waitForResume() {
|
|
542
|
+
if (!this.isPaused) {
|
|
543
|
+
return Promise.resolve();
|
|
544
|
+
}
|
|
545
|
+
return new Promise((resolve) => {
|
|
546
|
+
this.resumeResolver = resolve;
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Check current mood/coherence and pause if critical.
|
|
551
|
+
* Called before each batch of node executions.
|
|
552
|
+
*
|
|
553
|
+
* @returns true if execution can proceed, false if paused
|
|
554
|
+
*/
|
|
555
|
+
async checkMoodAndMaybePause(runId) {
|
|
556
|
+
if (!this.moodCheckEnabled) {
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
try {
|
|
560
|
+
const { neuroState } = await import("./neuro-state-XHRGIRVO.js");
|
|
561
|
+
const currentState = await new Promise((resolve) => {
|
|
562
|
+
let resolved = false;
|
|
563
|
+
let subscription = null;
|
|
564
|
+
const handleValue = (state) => {
|
|
565
|
+
if (resolved) return;
|
|
566
|
+
resolved = true;
|
|
567
|
+
resolve(state);
|
|
568
|
+
if (subscription) {
|
|
569
|
+
subscription.unsubscribe();
|
|
570
|
+
} else {
|
|
571
|
+
setTimeout(() => subscription?.unsubscribe(), 0);
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
subscription = neuroState.state$.subscribe(handleValue);
|
|
575
|
+
setTimeout(() => {
|
|
576
|
+
if (!resolved) {
|
|
577
|
+
resolved = true;
|
|
578
|
+
subscription?.unsubscribe();
|
|
579
|
+
resolve({
|
|
580
|
+
coherence: { overall: 1 },
|
|
581
|
+
convergence: { healthScore: 1 },
|
|
582
|
+
mood: "focused"
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
}, 50);
|
|
586
|
+
});
|
|
587
|
+
const { coherence, convergence, mood } = currentState;
|
|
588
|
+
if (mood === "overloaded" || coherence.overall < this.pauseCoherenceThreshold) {
|
|
589
|
+
console.log(
|
|
590
|
+
`[OrchestratorV0] Cognitive load critical (mood: ${mood}, coherence: ${coherence.overall.toFixed(2)}), pausing...`
|
|
591
|
+
);
|
|
592
|
+
this.pause("cognitive_load_critical");
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
if (convergence.healthScore < 0.3) {
|
|
596
|
+
console.log(
|
|
597
|
+
`[OrchestratorV0] Health degraded (score: ${convergence.healthScore.toFixed(2)}), pausing...`
|
|
598
|
+
);
|
|
599
|
+
this.pause("health_degraded");
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
return true;
|
|
603
|
+
} catch (err) {
|
|
604
|
+
console.warn("[OrchestratorV0] Mood check failed, continuing:", err);
|
|
605
|
+
return true;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Clean up mesh feedback subscription and release resources.
|
|
610
|
+
*
|
|
611
|
+
* Should be called when the orchestrator is no longer needed.
|
|
612
|
+
*/
|
|
613
|
+
dispose() {
|
|
614
|
+
if (this.meshFeedbackUnsub) {
|
|
615
|
+
this.meshFeedbackUnsub();
|
|
616
|
+
this.meshFeedbackUnsub = null;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Execute a stack workflow with DAG orchestration.
|
|
621
|
+
*
|
|
622
|
+
* Performs topological execution of all nodes in the stack, managing
|
|
623
|
+
* dependencies, parallelism, retries, and caching.
|
|
624
|
+
*
|
|
625
|
+
* @param stack - Stack manifest defining the workflow DAG
|
|
626
|
+
* @param opts - Execution options (concurrency, retry policy, etc.)
|
|
627
|
+
* @returns Promise resolving to run result with outputs or error
|
|
628
|
+
*
|
|
629
|
+
* @example
|
|
630
|
+
* ```typescript
|
|
631
|
+
* const stack: StackManifestV0 = {
|
|
632
|
+
* id: 'my-workflow',
|
|
633
|
+
* version: '1.0.0',
|
|
634
|
+
* nodes: [
|
|
635
|
+
* { id: 'step1', nodeRef: 'http-fetch', config: { url: '...' } },
|
|
636
|
+
* { id: 'step2', nodeRef: 'transform' }
|
|
637
|
+
* ],
|
|
638
|
+
* edges: [{ from: 'step1', to: 'step2', map: { data: 'input' } }]
|
|
639
|
+
* };
|
|
640
|
+
*
|
|
641
|
+
* const result = await orchestrator.run(stack, {
|
|
642
|
+
* maxConcurrency: 4,
|
|
643
|
+
* retryPolicy: { maxRetries: 2 }
|
|
644
|
+
* });
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
async run(stack, opts = {}) {
|
|
648
|
+
const startedAt = Date.now();
|
|
649
|
+
const runId = `${stack.id}:${startedAt}`;
|
|
650
|
+
const packageVersions = await getPackageVersions();
|
|
651
|
+
this.currentRunId = runId;
|
|
652
|
+
orchestratorToMesh.onRunStart(runId, stack.id);
|
|
653
|
+
try {
|
|
654
|
+
const validation = await linkAndValidateStack(stack, this.catalogResolver);
|
|
655
|
+
if (!validation.ok) {
|
|
656
|
+
const msg = validation.issues.map((i) => `${i.code}`).join(", ");
|
|
657
|
+
throw new Error(`Stack validation failed: ${msg}`);
|
|
658
|
+
}
|
|
659
|
+
const edges = (stack.edges || []).slice();
|
|
660
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
661
|
+
stack.nodes.forEach((n) => incoming.set(n.id, 0));
|
|
662
|
+
edges.forEach((e) => incoming.set(e.to, (incoming.get(e.to) || 0) + 1));
|
|
663
|
+
let ready = stack.nodes.filter((n) => (incoming.get(n.id) || 0) === 0);
|
|
664
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
665
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
666
|
+
const completed = /* @__PURE__ */ new Set();
|
|
667
|
+
const nodeAttempts = /* @__PURE__ */ new Map();
|
|
668
|
+
const baseConcurrency = opts.maxConcurrency || Infinity;
|
|
669
|
+
const effectiveConcurrency = baseConcurrency === Infinity ? Math.floor(1e3 * this.concurrencyMultiplier) : Math.max(1, Math.floor(baseConcurrency * this.concurrencyMultiplier));
|
|
670
|
+
const semaphore = new Semaphore(effectiveConcurrency);
|
|
671
|
+
if (this.concurrencyMultiplier < 1) {
|
|
672
|
+
console.log(
|
|
673
|
+
`[OrchestratorV0] Running with reduced concurrency: ${effectiveConcurrency} (multiplier: ${this.concurrencyMultiplier.toFixed(2)})`
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
const retryPolicy = {
|
|
677
|
+
maxRetries: opts.retryPolicy?.maxRetries ?? 2,
|
|
678
|
+
backoffMs: opts.retryPolicy?.backoffMs ?? 1e3,
|
|
679
|
+
backoffMultiplier: opts.retryPolicy?.backoffMultiplier ?? 2
|
|
680
|
+
};
|
|
681
|
+
let shapeHash;
|
|
682
|
+
try {
|
|
683
|
+
const { computeShapeHash: computeShapeHash2 } = await import("./topology-CIWWNVAN.js");
|
|
684
|
+
shapeHash = await computeShapeHash2(stack);
|
|
685
|
+
} catch (err) {
|
|
686
|
+
console.error("[OrchestratorV0] Failed to compute shape hash:", err);
|
|
687
|
+
}
|
|
688
|
+
const processNode = async (node) => {
|
|
689
|
+
await semaphore.acquire();
|
|
690
|
+
const manifest = await this.catalogResolver(node.nodeRef);
|
|
691
|
+
if (!manifest) throw new Error(`Node manifest not found: ${node.nodeRef}`);
|
|
692
|
+
const tier = this.pickTier(stack, node, manifest);
|
|
693
|
+
const baseInput = this.buildInputs(node, edges, outputs);
|
|
694
|
+
const input = {
|
|
695
|
+
...baseInput,
|
|
696
|
+
...opts.configOverrides?.[node.id] || {}
|
|
697
|
+
};
|
|
698
|
+
const modelId = input.model || node.config?.model || "default";
|
|
699
|
+
const span = this.tracer.startSpan(`node:${node.id}`, {
|
|
700
|
+
stackId: stack.id,
|
|
701
|
+
nodeRef: node.nodeRef,
|
|
702
|
+
runtimeTier: tier,
|
|
703
|
+
commit: process.env.GIT_COMMIT_SHA,
|
|
704
|
+
packages: packageVersions,
|
|
705
|
+
modelId,
|
|
706
|
+
policies: JSON.stringify(node.policies || manifest.policies || {})
|
|
707
|
+
});
|
|
708
|
+
let result = null;
|
|
709
|
+
let lastError = null;
|
|
710
|
+
const currentAttempt = (nodeAttempts.get(node.id) || 0) + 1;
|
|
711
|
+
nodeAttempts.set(node.id, currentAttempt);
|
|
712
|
+
try {
|
|
713
|
+
const { makeKey, getCachedOutput, setCachedOutput } = await import("./cache-VKYSQRXX.js");
|
|
714
|
+
const cacheKey = makeKey(shapeHash, node.id, input);
|
|
715
|
+
const cached = getCachedOutput(cacheKey);
|
|
716
|
+
if (cached) {
|
|
717
|
+
this.tracer.addEvent(span, {
|
|
718
|
+
time: Date.now(),
|
|
719
|
+
event: "cache.hit"
|
|
720
|
+
});
|
|
721
|
+
result = cached;
|
|
722
|
+
} else {
|
|
723
|
+
for (let attempt = 1; attempt <= retryPolicy.maxRetries + 1; attempt++) {
|
|
724
|
+
try {
|
|
725
|
+
result = await this.executeNode(stack.id, node, manifest, tier, input, opts);
|
|
726
|
+
lastError = null;
|
|
727
|
+
break;
|
|
728
|
+
} catch (execErr) {
|
|
729
|
+
lastError = execErr instanceof Error ? execErr : new Error(String(execErr));
|
|
730
|
+
this.tracer.addEvent(span, {
|
|
731
|
+
time: Date.now(),
|
|
732
|
+
event: "retry",
|
|
733
|
+
attributes: {
|
|
734
|
+
attempt,
|
|
735
|
+
maxRetries: retryPolicy.maxRetries,
|
|
736
|
+
error: lastError?.message
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
if (attempt <= retryPolicy.maxRetries) {
|
|
740
|
+
const backoffTime = retryPolicy.backoffMs * Math.pow(retryPolicy.backoffMultiplier, attempt - 1);
|
|
741
|
+
await new Promise((resolve) => setTimeout(resolve, backoffTime));
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (lastError) {
|
|
746
|
+
throw lastError;
|
|
747
|
+
}
|
|
748
|
+
if (result && Object.keys(result).length > 0) setCachedOutput(cacheKey, result);
|
|
749
|
+
}
|
|
750
|
+
this.tracer.addEvent(span, {
|
|
751
|
+
time: Date.now(),
|
|
752
|
+
event: "finished",
|
|
753
|
+
attributes: { ok: true, result }
|
|
754
|
+
});
|
|
755
|
+
} catch (err) {
|
|
756
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
757
|
+
this.tracer.addEvent(span, {
|
|
758
|
+
time: Date.now(),
|
|
759
|
+
event: "error",
|
|
760
|
+
attributes: { message: errorMessage }
|
|
761
|
+
});
|
|
762
|
+
throw err;
|
|
763
|
+
} finally {
|
|
764
|
+
this.tracer.endSpan(span);
|
|
765
|
+
semaphore.release();
|
|
766
|
+
}
|
|
767
|
+
outputs.set(node.id, result || {});
|
|
768
|
+
return node.id;
|
|
769
|
+
};
|
|
770
|
+
while (completed.size < stack.nodes.length) {
|
|
771
|
+
const canProceed = await this.checkMoodAndMaybePause(runId);
|
|
772
|
+
if (!canProceed) {
|
|
773
|
+
if (inFlight.size > 0) {
|
|
774
|
+
const finishedWhilePaused = await Promise.race(Array.from(inFlight.values()));
|
|
775
|
+
inFlight.delete(finishedWhilePaused);
|
|
776
|
+
completed.add(finishedWhilePaused);
|
|
777
|
+
orchestratorToMesh.onNodeComplete(
|
|
778
|
+
runId,
|
|
779
|
+
finishedWhilePaused,
|
|
780
|
+
true,
|
|
781
|
+
Date.now() - startedAt
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
await this.waitForResume();
|
|
785
|
+
}
|
|
786
|
+
for (const node of ready) {
|
|
787
|
+
if (!inFlight.has(node.id)) {
|
|
788
|
+
inFlight.set(node.id, processNode(node));
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (inFlight.size === 0) {
|
|
792
|
+
throw new Error(
|
|
793
|
+
"Orchestration failed: no nodes are ready or in-flight, but not all nodes have completed."
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
const finishedNodeId = await Promise.race(Array.from(inFlight.values()));
|
|
797
|
+
inFlight.delete(finishedNodeId);
|
|
798
|
+
completed.add(finishedNodeId);
|
|
799
|
+
orchestratorToMesh.onNodeComplete(runId, finishedNodeId, true, Date.now() - startedAt);
|
|
800
|
+
const newlyReady = [];
|
|
801
|
+
edges.filter((e) => e.from === finishedNodeId).forEach((e) => {
|
|
802
|
+
const to = e.to;
|
|
803
|
+
const newCount = (incoming.get(to) || 0) - 1;
|
|
804
|
+
incoming.set(to, newCount);
|
|
805
|
+
if (newCount === 0) {
|
|
806
|
+
const target = stack.nodes.find((n) => n.id === to);
|
|
807
|
+
if (target) newlyReady.push(target);
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
ready = newlyReady;
|
|
811
|
+
}
|
|
812
|
+
const spans = await this.tracer.export?.() || [];
|
|
813
|
+
for (const s of spans) {
|
|
814
|
+
s.traceId = runId;
|
|
815
|
+
}
|
|
816
|
+
try {
|
|
817
|
+
await persistSpans(spans);
|
|
818
|
+
} catch (err) {
|
|
819
|
+
console.error("[OrchestratorV0] Failed to persist spans:", err);
|
|
820
|
+
}
|
|
821
|
+
const endedAt = Date.now();
|
|
822
|
+
const totalDurationMs = endedAt - startedAt;
|
|
823
|
+
const nodeDurations = {};
|
|
824
|
+
for (const s of spans) {
|
|
825
|
+
const name = s.name;
|
|
826
|
+
if (name?.startsWith("node:") && s.startTime && s.endTime) {
|
|
827
|
+
const nodeId = name.slice("node:".length);
|
|
828
|
+
nodeDurations[nodeId] = (nodeDurations[nodeId] || 0) + (s.endTime - s.startTime);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
persistRunMeta({
|
|
832
|
+
runId,
|
|
833
|
+
stackId: stack.id,
|
|
834
|
+
shapeHash,
|
|
835
|
+
startedAt,
|
|
836
|
+
endedAt,
|
|
837
|
+
status: "succeeded",
|
|
838
|
+
commit: process.env.GIT_COMMIT_SHA,
|
|
839
|
+
packages: packageVersions,
|
|
840
|
+
metrics: { totalDurationMs, nodeDurations }
|
|
841
|
+
});
|
|
842
|
+
orchestratorToMesh.onRunComplete(runId, true, totalDurationMs);
|
|
843
|
+
this.currentRunId = null;
|
|
844
|
+
return { runId, succeeded: true, outputs };
|
|
845
|
+
} catch (e) {
|
|
846
|
+
const runError = e instanceof Error ? e : new Error(String(e));
|
|
847
|
+
try {
|
|
848
|
+
const spans = await this.tracer.export?.() || [];
|
|
849
|
+
for (const s of spans) {
|
|
850
|
+
s.traceId = runId;
|
|
851
|
+
}
|
|
852
|
+
await persistSpans(spans);
|
|
853
|
+
} catch (err) {
|
|
854
|
+
console.error("[OrchestratorV0] Failed to persist error spans:", err);
|
|
855
|
+
}
|
|
856
|
+
const failedAt = Date.now();
|
|
857
|
+
persistRunMeta({
|
|
858
|
+
runId,
|
|
859
|
+
stackId: stack.id,
|
|
860
|
+
startedAt,
|
|
861
|
+
endedAt: failedAt,
|
|
862
|
+
status: "failed",
|
|
863
|
+
commit: process.env.GIT_COMMIT_SHA,
|
|
864
|
+
packages: packageVersions,
|
|
865
|
+
// capture error message in feedback tags for now
|
|
866
|
+
feedback: { tags: [runError.message] }
|
|
867
|
+
});
|
|
868
|
+
orchestratorToMesh.onRunComplete(runId, false, failedAt - startedAt);
|
|
869
|
+
this.currentRunId = null;
|
|
870
|
+
return { runId, succeeded: false, error: runError };
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
pickTier(stack, node, manifest) {
|
|
874
|
+
if (node.runtimes?.includes("stack")) return "stack";
|
|
875
|
+
const typedManifest = manifest;
|
|
876
|
+
const preferred = node.runtimes || stack.runtimes?.overrides?.[node.id] || stack.runtimes?.default || (Array.isArray(typedManifest.runtimes) ? typedManifest.runtimes : typedManifest.runtimes?.default) || ["worker"];
|
|
877
|
+
if (!typedManifest.kind && typedManifest.nodes && preferred[0] === "worker") {
|
|
878
|
+
return "stack";
|
|
879
|
+
}
|
|
880
|
+
return preferred[0];
|
|
881
|
+
}
|
|
882
|
+
buildInputs(node, edges, outputs) {
|
|
883
|
+
const inbound = edges.filter((e) => e.to === node.id);
|
|
884
|
+
const input = { ...node.config || {} };
|
|
885
|
+
for (const edge of inbound) {
|
|
886
|
+
const from = outputs.get(edge.from) || {};
|
|
887
|
+
const map = edge.map || {};
|
|
888
|
+
for (const [outKey, inKey] of Object.entries(map)) {
|
|
889
|
+
input[inKey] = from[outKey];
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return input;
|
|
893
|
+
}
|
|
894
|
+
async executeNode(stackId, node, manifest, tier, input, options) {
|
|
895
|
+
void stackId;
|
|
896
|
+
void node;
|
|
897
|
+
if (typeof window === "undefined") {
|
|
898
|
+
const { ensureServerRunnersRegistered } = await import("./server-7DM74VFW.js");
|
|
899
|
+
await ensureServerRunnersRegistered();
|
|
900
|
+
}
|
|
901
|
+
const { runners } = await import("./registry-BL3TDQDB.js");
|
|
902
|
+
const tierRunner = runners.getRunner(tier);
|
|
903
|
+
if (tierRunner) {
|
|
904
|
+
return await tierRunner(manifest, input, {
|
|
905
|
+
stackId,
|
|
906
|
+
nodeId: node.id,
|
|
907
|
+
policies: node.policies || manifest.policies,
|
|
908
|
+
userId: options?.userId,
|
|
909
|
+
appletId: options?.appletId,
|
|
910
|
+
worldId: options?.worldId,
|
|
911
|
+
terminalId: options?.terminalId
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
if (tier === "worker") {
|
|
915
|
+
const fn = runners.getWorker(manifest.id);
|
|
916
|
+
const secretNames = node.secrets && node.secrets.length > 0 ? node.secrets : isNodeManifest(manifest) ? (manifest.secrets || []).map((s) => s.name) : [];
|
|
917
|
+
const secrets = {};
|
|
918
|
+
for (const name of secretNames) {
|
|
919
|
+
const scope = {
|
|
920
|
+
ownerId: options?.userId || "anonymous",
|
|
921
|
+
stackId,
|
|
922
|
+
appletId: options?.appletId,
|
|
923
|
+
worldId: options?.worldId,
|
|
924
|
+
terminalId: options?.terminalId,
|
|
925
|
+
nodeId: node.id
|
|
926
|
+
};
|
|
927
|
+
const value = await resolveSecretValue(name, scope);
|
|
928
|
+
if (value) secrets[name] = value;
|
|
929
|
+
}
|
|
930
|
+
if (fn)
|
|
931
|
+
return fn(
|
|
932
|
+
manifest,
|
|
933
|
+
{ ...input, __secrets: secrets },
|
|
934
|
+
{
|
|
935
|
+
stackId,
|
|
936
|
+
nodeId: node.id,
|
|
937
|
+
policies: node.policies || manifest.policies,
|
|
938
|
+
userId: options?.userId,
|
|
939
|
+
appletId: options?.appletId,
|
|
940
|
+
worldId: options?.worldId,
|
|
941
|
+
terminalId: options?.terminalId
|
|
942
|
+
}
|
|
943
|
+
);
|
|
944
|
+
return { ...input };
|
|
945
|
+
}
|
|
946
|
+
if (tier === "webcontainer") {
|
|
947
|
+
const { runInWebContainer } = await import("./webcontainer-3LDJVIIL.js");
|
|
948
|
+
return await runInWebContainer(manifest, input, {
|
|
949
|
+
stackId,
|
|
950
|
+
nodeId: node.id,
|
|
951
|
+
policies: node.policies || manifest.policies
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
if (tier === "wasm-hvm") {
|
|
955
|
+
const { runInHVM } = await import("./hvm-DRQK2MUT.js");
|
|
956
|
+
return await runInHVM(manifest, input, {
|
|
957
|
+
stackId,
|
|
958
|
+
nodeId: node.id,
|
|
959
|
+
policies: node.policies || manifest.policies
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
if (isNodeManifest(manifest) && manifest.kind === "mcp") {
|
|
963
|
+
const { runMcpNode } = await import("./mcp-NK34ZNM5.js");
|
|
964
|
+
return await runMcpNode(manifest, input, {
|
|
965
|
+
stackId,
|
|
966
|
+
nodeId: node.id,
|
|
967
|
+
policies: node.policies || manifest.policies
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
return {};
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
export {
|
|
975
|
+
loadStack,
|
|
976
|
+
OrchestratorV0
|
|
977
|
+
};
|