agentflow-core 0.1.2 → 0.1.3
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/chunk-DGLK6IBP.js +402 -0
- package/dist/cli.cjs +542 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +142 -0
- package/dist/index.cjs +174 -0
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +5 -225
- package/package.json +6 -3
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/cli.ts
|
|
5
|
+
var import_path = require("path");
|
|
6
|
+
|
|
7
|
+
// src/runner.ts
|
|
8
|
+
var import_node_child_process = require("child_process");
|
|
9
|
+
var import_node_fs = require("fs");
|
|
10
|
+
var import_node_path = require("path");
|
|
11
|
+
|
|
12
|
+
// src/graph-builder.ts
|
|
13
|
+
var import_crypto = require("crypto");
|
|
14
|
+
function deepFreeze(obj) {
|
|
15
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
16
|
+
if (obj instanceof Map) {
|
|
17
|
+
Object.freeze(obj);
|
|
18
|
+
for (const value of obj.values()) {
|
|
19
|
+
deepFreeze(value);
|
|
20
|
+
}
|
|
21
|
+
return obj;
|
|
22
|
+
}
|
|
23
|
+
Object.freeze(obj);
|
|
24
|
+
const record = obj;
|
|
25
|
+
for (const key of Object.keys(record)) {
|
|
26
|
+
const value = record[key];
|
|
27
|
+
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
28
|
+
deepFreeze(value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return obj;
|
|
32
|
+
}
|
|
33
|
+
function createCounterIdGenerator() {
|
|
34
|
+
let counter = 0;
|
|
35
|
+
return () => {
|
|
36
|
+
counter++;
|
|
37
|
+
return `node_${String(counter).padStart(3, "0")}`;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function createGraphBuilder(config) {
|
|
41
|
+
const generateId = config?.idGenerator ?? createCounterIdGenerator();
|
|
42
|
+
const agentId = config?.agentId ?? "unknown";
|
|
43
|
+
const trigger = config?.trigger ?? "manual";
|
|
44
|
+
const spanId = (0, import_crypto.randomUUID)();
|
|
45
|
+
const traceId = config?.traceId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_TRACE_ID : void 0) ?? (0, import_crypto.randomUUID)();
|
|
46
|
+
const parentSpanId = config?.parentSpanId ?? (typeof process !== "undefined" ? process.env?.AGENTFLOW_PARENT_SPAN_ID : void 0) ?? null;
|
|
47
|
+
const graphId = generateId();
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
50
|
+
const edges = [];
|
|
51
|
+
const events = [];
|
|
52
|
+
const parentStack = [];
|
|
53
|
+
let rootNodeId = null;
|
|
54
|
+
let built = false;
|
|
55
|
+
function assertNotBuilt() {
|
|
56
|
+
if (built) {
|
|
57
|
+
throw new Error("GraphBuilder: cannot mutate after build() has been called");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function getNode(nodeId) {
|
|
61
|
+
const node = nodes.get(nodeId);
|
|
62
|
+
if (!node) {
|
|
63
|
+
throw new Error(`GraphBuilder: node "${nodeId}" does not exist`);
|
|
64
|
+
}
|
|
65
|
+
return node;
|
|
66
|
+
}
|
|
67
|
+
function recordEvent(nodeId, eventType, data = {}) {
|
|
68
|
+
events.push({
|
|
69
|
+
timestamp: Date.now(),
|
|
70
|
+
eventType,
|
|
71
|
+
nodeId,
|
|
72
|
+
data
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function buildGraph() {
|
|
76
|
+
if (rootNodeId === null) {
|
|
77
|
+
throw new Error("GraphBuilder: cannot build a graph with no nodes");
|
|
78
|
+
}
|
|
79
|
+
let graphStatus = "completed";
|
|
80
|
+
for (const node of nodes.values()) {
|
|
81
|
+
if (node.status === "failed" || node.status === "timeout" || node.status === "hung") {
|
|
82
|
+
graphStatus = "failed";
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
if (node.status === "running") {
|
|
86
|
+
graphStatus = "running";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const endTime = graphStatus === "running" ? null : Date.now();
|
|
90
|
+
const frozenNodes = new Map(
|
|
91
|
+
[...nodes.entries()].map(([id, mNode]) => [
|
|
92
|
+
id,
|
|
93
|
+
{
|
|
94
|
+
id: mNode.id,
|
|
95
|
+
type: mNode.type,
|
|
96
|
+
name: mNode.name,
|
|
97
|
+
startTime: mNode.startTime,
|
|
98
|
+
endTime: mNode.endTime,
|
|
99
|
+
status: mNode.status,
|
|
100
|
+
parentId: mNode.parentId,
|
|
101
|
+
children: [...mNode.children],
|
|
102
|
+
metadata: { ...mNode.metadata },
|
|
103
|
+
state: { ...mNode.state }
|
|
104
|
+
}
|
|
105
|
+
])
|
|
106
|
+
);
|
|
107
|
+
const graph = {
|
|
108
|
+
id: graphId,
|
|
109
|
+
rootNodeId,
|
|
110
|
+
nodes: frozenNodes,
|
|
111
|
+
edges: [...edges],
|
|
112
|
+
startTime,
|
|
113
|
+
endTime,
|
|
114
|
+
status: graphStatus,
|
|
115
|
+
trigger,
|
|
116
|
+
agentId,
|
|
117
|
+
events: [...events],
|
|
118
|
+
traceId,
|
|
119
|
+
spanId,
|
|
120
|
+
parentSpanId
|
|
121
|
+
};
|
|
122
|
+
return deepFreeze(graph);
|
|
123
|
+
}
|
|
124
|
+
const builder = {
|
|
125
|
+
get graphId() {
|
|
126
|
+
return graphId;
|
|
127
|
+
},
|
|
128
|
+
get traceContext() {
|
|
129
|
+
return { traceId, spanId };
|
|
130
|
+
},
|
|
131
|
+
startNode(opts) {
|
|
132
|
+
assertNotBuilt();
|
|
133
|
+
const id = generateId();
|
|
134
|
+
const parentId = opts.parentId ?? parentStack[parentStack.length - 1] ?? null;
|
|
135
|
+
if (parentId !== null && !nodes.has(parentId)) {
|
|
136
|
+
throw new Error(`GraphBuilder: parent node "${parentId}" does not exist`);
|
|
137
|
+
}
|
|
138
|
+
const node = {
|
|
139
|
+
id,
|
|
140
|
+
type: opts.type,
|
|
141
|
+
name: opts.name,
|
|
142
|
+
startTime: Date.now(),
|
|
143
|
+
endTime: null,
|
|
144
|
+
status: "running",
|
|
145
|
+
parentId,
|
|
146
|
+
children: [],
|
|
147
|
+
metadata: opts.metadata ? { ...opts.metadata } : {},
|
|
148
|
+
state: {}
|
|
149
|
+
};
|
|
150
|
+
nodes.set(id, node);
|
|
151
|
+
if (parentId !== null) {
|
|
152
|
+
const parent = nodes.get(parentId);
|
|
153
|
+
if (parent) {
|
|
154
|
+
parent.children.push(id);
|
|
155
|
+
}
|
|
156
|
+
edges.push({ from: parentId, to: id, type: "spawned" });
|
|
157
|
+
}
|
|
158
|
+
if (rootNodeId === null) {
|
|
159
|
+
rootNodeId = id;
|
|
160
|
+
}
|
|
161
|
+
recordEvent(id, "agent_start", { type: opts.type, name: opts.name });
|
|
162
|
+
return id;
|
|
163
|
+
},
|
|
164
|
+
endNode(nodeId, status = "completed") {
|
|
165
|
+
assertNotBuilt();
|
|
166
|
+
const node = getNode(nodeId);
|
|
167
|
+
if (node.endTime !== null) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`GraphBuilder: node "${nodeId}" has already ended (status: ${node.status})`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
node.endTime = Date.now();
|
|
173
|
+
node.status = status;
|
|
174
|
+
recordEvent(nodeId, "agent_end", { status });
|
|
175
|
+
},
|
|
176
|
+
failNode(nodeId, error) {
|
|
177
|
+
assertNotBuilt();
|
|
178
|
+
const node = getNode(nodeId);
|
|
179
|
+
if (node.endTime !== null) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`GraphBuilder: node "${nodeId}" has already ended (status: ${node.status})`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
185
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
186
|
+
node.endTime = Date.now();
|
|
187
|
+
node.status = "failed";
|
|
188
|
+
node.metadata.error = errorMessage;
|
|
189
|
+
if (errorStack) {
|
|
190
|
+
node.metadata.errorStack = errorStack;
|
|
191
|
+
}
|
|
192
|
+
recordEvent(nodeId, "tool_error", { error: errorMessage });
|
|
193
|
+
},
|
|
194
|
+
addEdge(from, to, type) {
|
|
195
|
+
assertNotBuilt();
|
|
196
|
+
getNode(from);
|
|
197
|
+
getNode(to);
|
|
198
|
+
edges.push({ from, to, type });
|
|
199
|
+
recordEvent(from, "custom", { to, type, action: "edge_add" });
|
|
200
|
+
},
|
|
201
|
+
pushEvent(event) {
|
|
202
|
+
assertNotBuilt();
|
|
203
|
+
getNode(event.nodeId);
|
|
204
|
+
events.push({
|
|
205
|
+
...event,
|
|
206
|
+
timestamp: Date.now()
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
updateState(nodeId, state) {
|
|
210
|
+
assertNotBuilt();
|
|
211
|
+
const node = getNode(nodeId);
|
|
212
|
+
Object.assign(node.state, state);
|
|
213
|
+
recordEvent(nodeId, "custom", { action: "state_update", ...state });
|
|
214
|
+
},
|
|
215
|
+
withParent(parentId, fn) {
|
|
216
|
+
assertNotBuilt();
|
|
217
|
+
getNode(parentId);
|
|
218
|
+
parentStack.push(parentId);
|
|
219
|
+
try {
|
|
220
|
+
return fn();
|
|
221
|
+
} finally {
|
|
222
|
+
parentStack.pop();
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
getSnapshot() {
|
|
226
|
+
return buildGraph();
|
|
227
|
+
},
|
|
228
|
+
build() {
|
|
229
|
+
assertNotBuilt();
|
|
230
|
+
const graph = buildGraph();
|
|
231
|
+
built = true;
|
|
232
|
+
return graph;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
return builder;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/runner.ts
|
|
239
|
+
function globToRegex(pattern) {
|
|
240
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
241
|
+
return new RegExp(`^${escaped}$`);
|
|
242
|
+
}
|
|
243
|
+
function snapshotDir(dir, patterns) {
|
|
244
|
+
const result = /* @__PURE__ */ new Map();
|
|
245
|
+
if (!(0, import_node_fs.existsSync)(dir)) return result;
|
|
246
|
+
for (const entry of (0, import_node_fs.readdirSync)(dir)) {
|
|
247
|
+
if (!patterns.some((re) => re.test(entry))) continue;
|
|
248
|
+
const full = (0, import_node_path.join)(dir, entry);
|
|
249
|
+
try {
|
|
250
|
+
const stat = (0, import_node_fs.statSync)(full);
|
|
251
|
+
if (stat.isFile()) {
|
|
252
|
+
result.set(full, stat.mtimeMs);
|
|
253
|
+
}
|
|
254
|
+
} catch {
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
function agentIdFromFilename(filePath) {
|
|
260
|
+
const base = (0, import_node_path.basename)(filePath, ".json");
|
|
261
|
+
const cleaned = base.replace(/-state$/, "");
|
|
262
|
+
return `alfred-${cleaned}`;
|
|
263
|
+
}
|
|
264
|
+
function deriveAgentId(command) {
|
|
265
|
+
return "orchestrator";
|
|
266
|
+
}
|
|
267
|
+
function fileTimestamp() {
|
|
268
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-").replace(/\.\d+Z$/, "");
|
|
269
|
+
}
|
|
270
|
+
function graphToJson(graph) {
|
|
271
|
+
const nodesObj = {};
|
|
272
|
+
for (const [id, node] of graph.nodes) {
|
|
273
|
+
nodesObj[id] = node;
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
id: graph.id,
|
|
277
|
+
rootNodeId: graph.rootNodeId,
|
|
278
|
+
nodes: nodesObj,
|
|
279
|
+
edges: graph.edges,
|
|
280
|
+
startTime: graph.startTime,
|
|
281
|
+
endTime: graph.endTime,
|
|
282
|
+
status: graph.status,
|
|
283
|
+
trigger: graph.trigger,
|
|
284
|
+
agentId: graph.agentId,
|
|
285
|
+
events: graph.events,
|
|
286
|
+
traceId: graph.traceId,
|
|
287
|
+
spanId: graph.spanId,
|
|
288
|
+
parentSpanId: graph.parentSpanId
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
async function runTraced(config) {
|
|
292
|
+
const {
|
|
293
|
+
command,
|
|
294
|
+
agentId = deriveAgentId(command),
|
|
295
|
+
trigger = "cli",
|
|
296
|
+
tracesDir = "./traces",
|
|
297
|
+
watchDirs = [],
|
|
298
|
+
watchPatterns = ["*.json"]
|
|
299
|
+
} = config;
|
|
300
|
+
if (command.length === 0) {
|
|
301
|
+
throw new Error("runTraced: command must not be empty");
|
|
302
|
+
}
|
|
303
|
+
const resolvedTracesDir = (0, import_node_path.resolve)(tracesDir);
|
|
304
|
+
const patterns = watchPatterns.map(globToRegex);
|
|
305
|
+
const orchestrator = createGraphBuilder({ agentId, trigger });
|
|
306
|
+
const { traceId, spanId } = orchestrator.traceContext;
|
|
307
|
+
const beforeSnapshots = /* @__PURE__ */ new Map();
|
|
308
|
+
for (const dir of watchDirs) {
|
|
309
|
+
beforeSnapshots.set(dir, snapshotDir(dir, patterns));
|
|
310
|
+
}
|
|
311
|
+
const rootId = orchestrator.startNode({ type: "agent", name: agentId });
|
|
312
|
+
const dispatchId = orchestrator.startNode({
|
|
313
|
+
type: "tool",
|
|
314
|
+
name: "dispatch-command",
|
|
315
|
+
parentId: rootId
|
|
316
|
+
});
|
|
317
|
+
orchestrator.updateState(dispatchId, { command: command.join(" ") });
|
|
318
|
+
const monitorId = orchestrator.startNode({
|
|
319
|
+
type: "tool",
|
|
320
|
+
name: "state-monitor",
|
|
321
|
+
parentId: rootId
|
|
322
|
+
});
|
|
323
|
+
orchestrator.updateState(monitorId, {
|
|
324
|
+
watchDirs,
|
|
325
|
+
watchPatterns
|
|
326
|
+
});
|
|
327
|
+
const startMs = Date.now();
|
|
328
|
+
const execCmd = command[0] ?? "";
|
|
329
|
+
const execArgs = command.slice(1);
|
|
330
|
+
process.env.AGENTFLOW_TRACE_ID = traceId;
|
|
331
|
+
process.env.AGENTFLOW_PARENT_SPAN_ID = spanId;
|
|
332
|
+
const result = (0, import_node_child_process.spawnSync)(execCmd, execArgs, { stdio: "inherit" });
|
|
333
|
+
delete process.env.AGENTFLOW_TRACE_ID;
|
|
334
|
+
delete process.env.AGENTFLOW_PARENT_SPAN_ID;
|
|
335
|
+
const exitCode = result.status ?? 1;
|
|
336
|
+
const duration = (Date.now() - startMs) / 1e3;
|
|
337
|
+
const stateChanges = [];
|
|
338
|
+
for (const dir of watchDirs) {
|
|
339
|
+
const before = beforeSnapshots.get(dir) ?? /* @__PURE__ */ new Map();
|
|
340
|
+
const after = snapshotDir(dir, patterns);
|
|
341
|
+
for (const [filePath, mtime] of after) {
|
|
342
|
+
const prevMtime = before.get(filePath);
|
|
343
|
+
if (prevMtime === void 0 || mtime > prevMtime) {
|
|
344
|
+
stateChanges.push(filePath);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
orchestrator.updateState(monitorId, { stateChanges });
|
|
349
|
+
orchestrator.endNode(monitorId);
|
|
350
|
+
if (exitCode === 0) {
|
|
351
|
+
orchestrator.endNode(dispatchId);
|
|
352
|
+
} else {
|
|
353
|
+
orchestrator.failNode(dispatchId, `Command exited with code ${exitCode}`);
|
|
354
|
+
}
|
|
355
|
+
orchestrator.updateState(rootId, {
|
|
356
|
+
exitCode,
|
|
357
|
+
duration,
|
|
358
|
+
stateChanges
|
|
359
|
+
});
|
|
360
|
+
if (exitCode === 0) {
|
|
361
|
+
orchestrator.endNode(rootId);
|
|
362
|
+
} else {
|
|
363
|
+
orchestrator.failNode(rootId, `Command exited with code ${exitCode}`);
|
|
364
|
+
}
|
|
365
|
+
const orchestratorGraph = orchestrator.build();
|
|
366
|
+
const allGraphs = [orchestratorGraph];
|
|
367
|
+
for (const filePath of stateChanges) {
|
|
368
|
+
const childAgentId = agentIdFromFilename(filePath);
|
|
369
|
+
const childBuilder = createGraphBuilder({
|
|
370
|
+
agentId: childAgentId,
|
|
371
|
+
trigger: "state-change",
|
|
372
|
+
traceId,
|
|
373
|
+
parentSpanId: spanId
|
|
374
|
+
});
|
|
375
|
+
const childRootId = childBuilder.startNode({
|
|
376
|
+
type: "agent",
|
|
377
|
+
name: childAgentId
|
|
378
|
+
});
|
|
379
|
+
childBuilder.updateState(childRootId, {
|
|
380
|
+
stateFile: filePath,
|
|
381
|
+
detectedBy: "runner-state-monitor"
|
|
382
|
+
});
|
|
383
|
+
childBuilder.endNode(childRootId);
|
|
384
|
+
allGraphs.push(childBuilder.build());
|
|
385
|
+
}
|
|
386
|
+
if (!(0, import_node_fs.existsSync)(resolvedTracesDir)) {
|
|
387
|
+
(0, import_node_fs.mkdirSync)(resolvedTracesDir, { recursive: true });
|
|
388
|
+
}
|
|
389
|
+
const ts = fileTimestamp();
|
|
390
|
+
const tracePaths = [];
|
|
391
|
+
for (const graph of allGraphs) {
|
|
392
|
+
const filename = `${graph.agentId}-${ts}.json`;
|
|
393
|
+
const outPath = (0, import_node_path.join)(resolvedTracesDir, filename);
|
|
394
|
+
(0, import_node_fs.writeFileSync)(outPath, JSON.stringify(graphToJson(graph), null, 2), "utf-8");
|
|
395
|
+
tracePaths.push(outPath);
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
exitCode,
|
|
399
|
+
traceId,
|
|
400
|
+
spanId,
|
|
401
|
+
tracePaths,
|
|
402
|
+
stateChanges,
|
|
403
|
+
duration
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/cli.ts
|
|
408
|
+
function parseArgs(argv) {
|
|
409
|
+
const result = {
|
|
410
|
+
tracesDir: "./traces",
|
|
411
|
+
watchDirs: [],
|
|
412
|
+
watchPatterns: ["*.json"],
|
|
413
|
+
trigger: "cli",
|
|
414
|
+
command: []
|
|
415
|
+
};
|
|
416
|
+
const dashDashIdx = argv.indexOf("--");
|
|
417
|
+
const flagArgs = dashDashIdx === -1 ? argv : argv.slice(0, dashDashIdx);
|
|
418
|
+
const commandArgs = dashDashIdx === -1 ? [] : argv.slice(dashDashIdx + 1);
|
|
419
|
+
let i = 0;
|
|
420
|
+
while (i < flagArgs.length) {
|
|
421
|
+
const arg = flagArgs[i];
|
|
422
|
+
switch (arg) {
|
|
423
|
+
case "run":
|
|
424
|
+
i++;
|
|
425
|
+
break;
|
|
426
|
+
case "--traces-dir":
|
|
427
|
+
i++;
|
|
428
|
+
result.tracesDir = flagArgs[i] ?? result.tracesDir;
|
|
429
|
+
i++;
|
|
430
|
+
break;
|
|
431
|
+
case "--watch-dir":
|
|
432
|
+
i++;
|
|
433
|
+
if (flagArgs[i]) {
|
|
434
|
+
result.watchDirs.push(flagArgs[i]);
|
|
435
|
+
}
|
|
436
|
+
i++;
|
|
437
|
+
break;
|
|
438
|
+
case "--watch-pattern":
|
|
439
|
+
i++;
|
|
440
|
+
if (flagArgs[i]) {
|
|
441
|
+
if (result.watchPatterns.length === 1 && result.watchPatterns[0] === "*.json") {
|
|
442
|
+
result.watchPatterns = [];
|
|
443
|
+
}
|
|
444
|
+
result.watchPatterns.push(flagArgs[i]);
|
|
445
|
+
}
|
|
446
|
+
i++;
|
|
447
|
+
break;
|
|
448
|
+
case "--agent-id":
|
|
449
|
+
i++;
|
|
450
|
+
result.agentId = flagArgs[i];
|
|
451
|
+
i++;
|
|
452
|
+
break;
|
|
453
|
+
case "--trigger":
|
|
454
|
+
i++;
|
|
455
|
+
result.trigger = flagArgs[i] ?? result.trigger;
|
|
456
|
+
i++;
|
|
457
|
+
break;
|
|
458
|
+
default:
|
|
459
|
+
i++;
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
result.command = commandArgs;
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
function printUsage() {
|
|
467
|
+
console.log(`
|
|
468
|
+
AgentFlow CLI \u2014 Wrap any command with automatic execution tracing.
|
|
469
|
+
|
|
470
|
+
Usage:
|
|
471
|
+
agentflow run [options] -- <command>
|
|
472
|
+
|
|
473
|
+
Options:
|
|
474
|
+
--traces-dir <path> Directory to save trace files (default: ./traces)
|
|
475
|
+
--watch-dir <path> Directory to watch for state changes (repeatable)
|
|
476
|
+
--watch-pattern <glob> File pattern to watch (default: *.json)
|
|
477
|
+
--agent-id <name> Agent ID for the orchestrator trace
|
|
478
|
+
--trigger <name> Trigger label (default: cli)
|
|
479
|
+
--help Show this help message
|
|
480
|
+
|
|
481
|
+
Examples:
|
|
482
|
+
agentflow run -- python -m alfred process
|
|
483
|
+
agentflow run --watch-dir /home/trader/.alfred/data -- python -m alfred process
|
|
484
|
+
agentflow run --traces-dir ./my-traces --agent-id alfred -- node worker.js
|
|
485
|
+
`.trim());
|
|
486
|
+
}
|
|
487
|
+
async function main() {
|
|
488
|
+
const argv = process.argv.slice(2);
|
|
489
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
490
|
+
printUsage();
|
|
491
|
+
process.exit(0);
|
|
492
|
+
}
|
|
493
|
+
const parsed = parseArgs(argv);
|
|
494
|
+
if (parsed.command.length === 0) {
|
|
495
|
+
console.error("Error: No command specified. Use -- to separate agentflow flags from the command.");
|
|
496
|
+
console.error("Example: agentflow run -- python -m alfred process");
|
|
497
|
+
process.exit(1);
|
|
498
|
+
}
|
|
499
|
+
const commandStr = parsed.command.join(" ");
|
|
500
|
+
const watchSummary = parsed.watchDirs.length > 0 ? parsed.watchDirs.map((d) => `${d} (${parsed.watchPatterns.join(", ")})`).join(", ") : "(none)";
|
|
501
|
+
console.log(`
|
|
502
|
+
\u{1F50D} AgentFlow: Tracing command: ${commandStr}`);
|
|
503
|
+
console.log(`\u{1F4C1} Traces: ${parsed.tracesDir}`);
|
|
504
|
+
console.log(`\u{1F441}\uFE0F Watching: ${watchSummary}`);
|
|
505
|
+
console.log("");
|
|
506
|
+
const config = {
|
|
507
|
+
command: parsed.command,
|
|
508
|
+
tracesDir: parsed.tracesDir,
|
|
509
|
+
watchDirs: parsed.watchDirs,
|
|
510
|
+
watchPatterns: parsed.watchPatterns,
|
|
511
|
+
trigger: parsed.trigger
|
|
512
|
+
};
|
|
513
|
+
if (parsed.agentId) {
|
|
514
|
+
config.agentId = parsed.agentId;
|
|
515
|
+
}
|
|
516
|
+
try {
|
|
517
|
+
const result = await runTraced(config);
|
|
518
|
+
console.log("");
|
|
519
|
+
console.log(`\u2705 Command completed (exit code ${result.exitCode}, ${result.duration.toFixed(1)}s)`);
|
|
520
|
+
if (result.tracePaths.length > 0) {
|
|
521
|
+
console.log("\u{1F4DD} Traces saved:");
|
|
522
|
+
const orchPath = result.tracePaths[0];
|
|
523
|
+
const orchName = (0, import_path.basename)(orchPath, ".json").split("-")[0] ?? "orchestrator";
|
|
524
|
+
console.log(` ${orchName.padEnd(14)} \u2192 ${orchPath}`);
|
|
525
|
+
for (let i = 1; i < result.tracePaths.length; i++) {
|
|
526
|
+
const tPath = result.tracePaths[i];
|
|
527
|
+
const name = (0, import_path.basename)(tPath, ".json").replace(/-\d{4}-.*$/, "");
|
|
528
|
+
const isLast = i === result.tracePaths.length - 1;
|
|
529
|
+
const prefix = isLast ? "\u2514\u2500" : "\u251C\u2500";
|
|
530
|
+
console.log(` ${prefix} ${name.padEnd(12)} \u2192 ${tPath} (state changed)`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
console.log(`\u{1F517} Trace ID: ${result.traceId}`);
|
|
534
|
+
console.log("");
|
|
535
|
+
process.exit(result.exitCode);
|
|
536
|
+
} catch (err) {
|
|
537
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
538
|
+
console.error(`\u274C AgentFlow error: ${message}`);
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
main();
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runTraced
|
|
4
|
+
} from "./chunk-DGLK6IBP.js";
|
|
5
|
+
|
|
6
|
+
// src/cli.ts
|
|
7
|
+
import { basename } from "path";
|
|
8
|
+
function parseArgs(argv) {
|
|
9
|
+
const result = {
|
|
10
|
+
tracesDir: "./traces",
|
|
11
|
+
watchDirs: [],
|
|
12
|
+
watchPatterns: ["*.json"],
|
|
13
|
+
trigger: "cli",
|
|
14
|
+
command: []
|
|
15
|
+
};
|
|
16
|
+
const dashDashIdx = argv.indexOf("--");
|
|
17
|
+
const flagArgs = dashDashIdx === -1 ? argv : argv.slice(0, dashDashIdx);
|
|
18
|
+
const commandArgs = dashDashIdx === -1 ? [] : argv.slice(dashDashIdx + 1);
|
|
19
|
+
let i = 0;
|
|
20
|
+
while (i < flagArgs.length) {
|
|
21
|
+
const arg = flagArgs[i];
|
|
22
|
+
switch (arg) {
|
|
23
|
+
case "run":
|
|
24
|
+
i++;
|
|
25
|
+
break;
|
|
26
|
+
case "--traces-dir":
|
|
27
|
+
i++;
|
|
28
|
+
result.tracesDir = flagArgs[i] ?? result.tracesDir;
|
|
29
|
+
i++;
|
|
30
|
+
break;
|
|
31
|
+
case "--watch-dir":
|
|
32
|
+
i++;
|
|
33
|
+
if (flagArgs[i]) {
|
|
34
|
+
result.watchDirs.push(flagArgs[i]);
|
|
35
|
+
}
|
|
36
|
+
i++;
|
|
37
|
+
break;
|
|
38
|
+
case "--watch-pattern":
|
|
39
|
+
i++;
|
|
40
|
+
if (flagArgs[i]) {
|
|
41
|
+
if (result.watchPatterns.length === 1 && result.watchPatterns[0] === "*.json") {
|
|
42
|
+
result.watchPatterns = [];
|
|
43
|
+
}
|
|
44
|
+
result.watchPatterns.push(flagArgs[i]);
|
|
45
|
+
}
|
|
46
|
+
i++;
|
|
47
|
+
break;
|
|
48
|
+
case "--agent-id":
|
|
49
|
+
i++;
|
|
50
|
+
result.agentId = flagArgs[i];
|
|
51
|
+
i++;
|
|
52
|
+
break;
|
|
53
|
+
case "--trigger":
|
|
54
|
+
i++;
|
|
55
|
+
result.trigger = flagArgs[i] ?? result.trigger;
|
|
56
|
+
i++;
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
i++;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
result.command = commandArgs;
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function printUsage() {
|
|
67
|
+
console.log(`
|
|
68
|
+
AgentFlow CLI \u2014 Wrap any command with automatic execution tracing.
|
|
69
|
+
|
|
70
|
+
Usage:
|
|
71
|
+
agentflow run [options] -- <command>
|
|
72
|
+
|
|
73
|
+
Options:
|
|
74
|
+
--traces-dir <path> Directory to save trace files (default: ./traces)
|
|
75
|
+
--watch-dir <path> Directory to watch for state changes (repeatable)
|
|
76
|
+
--watch-pattern <glob> File pattern to watch (default: *.json)
|
|
77
|
+
--agent-id <name> Agent ID for the orchestrator trace
|
|
78
|
+
--trigger <name> Trigger label (default: cli)
|
|
79
|
+
--help Show this help message
|
|
80
|
+
|
|
81
|
+
Examples:
|
|
82
|
+
agentflow run -- python -m alfred process
|
|
83
|
+
agentflow run --watch-dir /home/trader/.alfred/data -- python -m alfred process
|
|
84
|
+
agentflow run --traces-dir ./my-traces --agent-id alfred -- node worker.js
|
|
85
|
+
`.trim());
|
|
86
|
+
}
|
|
87
|
+
async function main() {
|
|
88
|
+
const argv = process.argv.slice(2);
|
|
89
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
90
|
+
printUsage();
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
const parsed = parseArgs(argv);
|
|
94
|
+
if (parsed.command.length === 0) {
|
|
95
|
+
console.error("Error: No command specified. Use -- to separate agentflow flags from the command.");
|
|
96
|
+
console.error("Example: agentflow run -- python -m alfred process");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const commandStr = parsed.command.join(" ");
|
|
100
|
+
const watchSummary = parsed.watchDirs.length > 0 ? parsed.watchDirs.map((d) => `${d} (${parsed.watchPatterns.join(", ")})`).join(", ") : "(none)";
|
|
101
|
+
console.log(`
|
|
102
|
+
\u{1F50D} AgentFlow: Tracing command: ${commandStr}`);
|
|
103
|
+
console.log(`\u{1F4C1} Traces: ${parsed.tracesDir}`);
|
|
104
|
+
console.log(`\u{1F441}\uFE0F Watching: ${watchSummary}`);
|
|
105
|
+
console.log("");
|
|
106
|
+
const config = {
|
|
107
|
+
command: parsed.command,
|
|
108
|
+
tracesDir: parsed.tracesDir,
|
|
109
|
+
watchDirs: parsed.watchDirs,
|
|
110
|
+
watchPatterns: parsed.watchPatterns,
|
|
111
|
+
trigger: parsed.trigger
|
|
112
|
+
};
|
|
113
|
+
if (parsed.agentId) {
|
|
114
|
+
config.agentId = parsed.agentId;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const result = await runTraced(config);
|
|
118
|
+
console.log("");
|
|
119
|
+
console.log(`\u2705 Command completed (exit code ${result.exitCode}, ${result.duration.toFixed(1)}s)`);
|
|
120
|
+
if (result.tracePaths.length > 0) {
|
|
121
|
+
console.log("\u{1F4DD} Traces saved:");
|
|
122
|
+
const orchPath = result.tracePaths[0];
|
|
123
|
+
const orchName = basename(orchPath, ".json").split("-")[0] ?? "orchestrator";
|
|
124
|
+
console.log(` ${orchName.padEnd(14)} \u2192 ${orchPath}`);
|
|
125
|
+
for (let i = 1; i < result.tracePaths.length; i++) {
|
|
126
|
+
const tPath = result.tracePaths[i];
|
|
127
|
+
const name = basename(tPath, ".json").replace(/-\d{4}-.*$/, "");
|
|
128
|
+
const isLast = i === result.tracePaths.length - 1;
|
|
129
|
+
const prefix = isLast ? "\u2514\u2500" : "\u251C\u2500";
|
|
130
|
+
console.log(` ${prefix} ${name.padEnd(12)} \u2192 ${tPath} (state changed)`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
console.log(`\u{1F517} Trace ID: ${result.traceId}`);
|
|
134
|
+
console.log("");
|
|
135
|
+
process.exit(result.exitCode);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
138
|
+
console.error(`\u274C AgentFlow error: ${message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
main();
|