cueclaw 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-DIUXV4XR.js +1953 -0
- package/dist/chunk-BVQG3WYO.js +29 -0
- package/dist/chunk-DVQFSFIZ.js +14 -0
- package/dist/{chunk-GMHDL4CG.js → chunk-F3EQAFH4.js} +59 -5
- package/dist/{chunk-K4PGB2DU.js → chunk-G43R5ASK.js} +46 -2
- package/dist/chunk-KRNAXOQ4.js +46 -0
- package/dist/{service-BHFOM6E2.js → chunk-PZZ6FBGB.js} +25 -7
- package/dist/chunk-QBOYMF4A.js +42 -0
- package/dist/chunk-RSKXBXSJ.js +247 -0
- package/dist/{chunk-D77G7ABJ.js → chunk-SEYPA5M2.js} +36 -318
- package/dist/{daemon-TWVEMRCU.js → chunk-W274JWEK.js} +3 -82
- package/dist/chunk-WE5J7GMR.js +409 -0
- package/dist/chunk-ZCK3IFLC.js +59 -0
- package/dist/cli.js +300 -46
- package/dist/config-6NWFKNLW.js +21 -0
- package/dist/daemon-R3LU23PR.js +95 -0
- package/dist/env-2ZIW4OD7.js +16 -0
- package/dist/executor-LS3Y4DO5.js +14 -0
- package/dist/logger-HD23RPWS.js +12 -0
- package/dist/planner-UU4T5IEN.js +28 -0
- package/dist/router-KEZ3YQXC.js +14 -0
- package/dist/service-VTUYSAAZ.js +15 -0
- package/dist/{setup-QZUEJUIN.js → setup-NWBKTQCO.js} +11 -45
- package/dist/{telegram-BTTWEETO.js → telegram-P6DBJ7WZ.js} +2 -2
- package/dist/{whatsapp-36XWDSJ5.js → whatsapp-HFMOFSFI.js} +1 -1
- package/package.json +4 -1
- package/dist/app-UJ4M3TPW.js +0 -448
- package/dist/chunk-E7BP6DMO.js +0 -10
- package/dist/chunk-JRHM3Z4C.js +0 -158
- package/dist/config-HMHM7UAZ.js +0 -12
- package/dist/router-36O66FDW.js +0 -10
|
@@ -4,316 +4,21 @@ import {
|
|
|
4
4
|
updateStepRunStatus,
|
|
5
5
|
updateWorkflowPhase,
|
|
6
6
|
updateWorkflowRunStatus
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-G43R5ASK.js";
|
|
8
8
|
import {
|
|
9
|
-
ConfigError,
|
|
10
|
-
ExecutorError,
|
|
11
|
-
PlannerError,
|
|
12
9
|
cueclawHome,
|
|
13
10
|
loadConfig
|
|
14
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RSKXBXSJ.js";
|
|
12
|
+
import {
|
|
13
|
+
ConfigError,
|
|
14
|
+
ExecutorError
|
|
15
|
+
} from "./chunk-BVQG3WYO.js";
|
|
15
16
|
import {
|
|
16
17
|
logger
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
|
|
19
|
-
// src/planner.ts
|
|
20
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
21
|
-
import { z } from "zod/v4";
|
|
22
|
-
import { nanoid } from "nanoid";
|
|
23
|
-
|
|
24
|
-
// src/workflow.ts
|
|
25
|
-
function validateDAG(steps) {
|
|
26
|
-
const errors = [];
|
|
27
|
-
const stepIds = new Set(steps.map((s) => s.id));
|
|
28
|
-
for (const step of steps) {
|
|
29
|
-
for (const dep of step.depends_on) {
|
|
30
|
-
if (!stepIds.has(dep)) {
|
|
31
|
-
errors.push(`Step "${step.id}" depends on unknown step "${dep}"`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
for (const step of steps) {
|
|
36
|
-
const refs = extractStepRefs(step.inputs);
|
|
37
|
-
for (const ref of refs) {
|
|
38
|
-
if (!stepIds.has(ref)) {
|
|
39
|
-
errors.push(`Step "${step.id}" references unknown step "${ref}" in inputs`);
|
|
40
|
-
}
|
|
41
|
-
if (stepIds.has(ref) && !step.depends_on.includes(ref)) {
|
|
42
|
-
errors.push(`Step "${step.id}" references "$steps.${ref}.output" but does not list "${ref}" in depends_on`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (hasCycle(steps)) {
|
|
47
|
-
errors.push("DAG contains a cycle");
|
|
48
|
-
}
|
|
49
|
-
return errors;
|
|
50
|
-
}
|
|
51
|
-
function extractStepRefs(obj) {
|
|
52
|
-
const refs = [];
|
|
53
|
-
const pattern = /\$steps\.([a-z0-9-]+)\.output/g;
|
|
54
|
-
function walk(value) {
|
|
55
|
-
if (typeof value === "string") {
|
|
56
|
-
let match;
|
|
57
|
-
while ((match = pattern.exec(value)) !== null) {
|
|
58
|
-
refs.push(match[1]);
|
|
59
|
-
}
|
|
60
|
-
pattern.lastIndex = 0;
|
|
61
|
-
} else if (Array.isArray(value)) {
|
|
62
|
-
for (const item of value) walk(item);
|
|
63
|
-
} else if (value !== null && typeof value === "object") {
|
|
64
|
-
for (const v of Object.values(value)) walk(v);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
walk(obj);
|
|
68
|
-
return refs;
|
|
69
|
-
}
|
|
70
|
-
function hasCycle(steps) {
|
|
71
|
-
const inDegree = /* @__PURE__ */ new Map();
|
|
72
|
-
const adjacency = /* @__PURE__ */ new Map();
|
|
73
|
-
for (const step of steps) {
|
|
74
|
-
inDegree.set(step.id, 0);
|
|
75
|
-
adjacency.set(step.id, []);
|
|
76
|
-
}
|
|
77
|
-
for (const step of steps) {
|
|
78
|
-
for (const dep of step.depends_on) {
|
|
79
|
-
if (adjacency.has(dep)) {
|
|
80
|
-
adjacency.get(dep).push(step.id);
|
|
81
|
-
inDegree.set(step.id, (inDegree.get(step.id) ?? 0) + 1);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
const queue = [];
|
|
86
|
-
for (const [id, deg] of inDegree) {
|
|
87
|
-
if (deg === 0) queue.push(id);
|
|
88
|
-
}
|
|
89
|
-
let processed = 0;
|
|
90
|
-
while (queue.length > 0) {
|
|
91
|
-
const current = queue.shift();
|
|
92
|
-
processed++;
|
|
93
|
-
for (const neighbor of adjacency.get(current) ?? []) {
|
|
94
|
-
const newDeg = (inDegree.get(neighbor) ?? 1) - 1;
|
|
95
|
-
inDegree.set(neighbor, newDeg);
|
|
96
|
-
if (newDeg === 0) queue.push(neighbor);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return processed !== steps.length;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// src/planner.ts
|
|
103
|
-
var PlanStepSchema = z.object({
|
|
104
|
-
id: z.string(),
|
|
105
|
-
description: z.string(),
|
|
106
|
-
expected_output: z.string().optional(),
|
|
107
|
-
agent: z.literal("claude"),
|
|
108
|
-
inputs: z.record(z.string(), z.any()),
|
|
109
|
-
depends_on: z.array(z.string())
|
|
110
|
-
});
|
|
111
|
-
var TriggerConfigSchema = z.union([
|
|
112
|
-
z.object({
|
|
113
|
-
type: z.literal("poll"),
|
|
114
|
-
interval_seconds: z.number(),
|
|
115
|
-
check_script: z.string(),
|
|
116
|
-
diff_mode: z.enum(["new_items", "any_change"])
|
|
117
|
-
}),
|
|
118
|
-
z.object({
|
|
119
|
-
type: z.literal("cron"),
|
|
120
|
-
expression: z.string(),
|
|
121
|
-
timezone: z.string().optional()
|
|
122
|
-
}),
|
|
123
|
-
z.object({ type: z.literal("manual") })
|
|
124
|
-
]);
|
|
125
|
-
var FailurePolicySchema = z.object({
|
|
126
|
-
on_step_failure: z.enum(["stop", "skip_dependents", "ask_user"]),
|
|
127
|
-
max_retries: z.number(),
|
|
128
|
-
retry_delay_ms: z.number()
|
|
129
|
-
});
|
|
130
|
-
var PlannerOutputSchema = z.object({
|
|
131
|
-
name: z.string(),
|
|
132
|
-
description: z.string(),
|
|
133
|
-
trigger: TriggerConfigSchema,
|
|
134
|
-
steps: z.array(PlanStepSchema).min(1),
|
|
135
|
-
failure_policy: FailurePolicySchema
|
|
136
|
-
});
|
|
137
|
-
var plannerOutputJsonSchema = {
|
|
138
|
-
type: "object",
|
|
139
|
-
required: ["name", "description", "trigger", "steps", "failure_policy"],
|
|
140
|
-
properties: {
|
|
141
|
-
name: { type: "string", description: "Short workflow name" },
|
|
142
|
-
description: { type: "string", description: "User's original natural language description" },
|
|
143
|
-
trigger: {
|
|
144
|
-
type: "object",
|
|
145
|
-
description: "Trigger configuration",
|
|
146
|
-
properties: {
|
|
147
|
-
type: { type: "string", enum: ["poll", "cron", "manual"] },
|
|
148
|
-
interval_seconds: { type: "number" },
|
|
149
|
-
check_script: { type: "string" },
|
|
150
|
-
diff_mode: { type: "string", enum: ["new_items", "any_change"] },
|
|
151
|
-
expression: { type: "string" },
|
|
152
|
-
timezone: { type: "string" }
|
|
153
|
-
},
|
|
154
|
-
required: ["type"]
|
|
155
|
-
},
|
|
156
|
-
steps: {
|
|
157
|
-
type: "array",
|
|
158
|
-
items: {
|
|
159
|
-
type: "object",
|
|
160
|
-
required: ["id", "description", "agent", "inputs", "depends_on"],
|
|
161
|
-
properties: {
|
|
162
|
-
id: { type: "string", description: "kebab-case step ID" },
|
|
163
|
-
description: { type: "string", description: "Detailed step description for the agent" },
|
|
164
|
-
expected_output: { type: "string" },
|
|
165
|
-
agent: { type: "string", enum: ["claude"] },
|
|
166
|
-
inputs: { type: "object", description: "Step inputs, may use $steps.{id}.output or $trigger_data" },
|
|
167
|
-
depends_on: { type: "array", items: { type: "string" }, description: "Step IDs this step depends on" }
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
minItems: 1
|
|
171
|
-
},
|
|
172
|
-
failure_policy: {
|
|
173
|
-
type: "object",
|
|
174
|
-
required: ["on_step_failure", "max_retries", "retry_delay_ms"],
|
|
175
|
-
properties: {
|
|
176
|
-
on_step_failure: { type: "string", enum: ["stop", "skip_dependents", "ask_user"] },
|
|
177
|
-
max_retries: { type: "number" },
|
|
178
|
-
retry_delay_ms: { type: "number" }
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
var plannerTool = {
|
|
184
|
-
name: "create_workflow",
|
|
185
|
-
description: "Create a workflow definition from the user description. Generate steps as a DAG with proper dependencies.",
|
|
186
|
-
input_schema: plannerOutputJsonSchema
|
|
187
|
-
};
|
|
188
|
-
function buildPlannerSystemPrompt(config) {
|
|
189
|
-
const identity = config.identity?.name ? `
|
|
190
|
-
User identity: ${config.identity.name}` : "";
|
|
191
|
-
return `You are CueClaw Planner. Convert user's natural language into a structured Workflow.
|
|
192
|
-
|
|
193
|
-
## Execution Environment
|
|
194
|
-
|
|
195
|
-
All steps are executed by the Claude Agent SDK. The agent has Bash and can use
|
|
196
|
-
any CLI tool installed locally. It auto-loads .claude/skills/.
|
|
197
|
-
You don't need to verify tool availability \u2014 the agent detects at runtime.
|
|
198
|
-
|
|
199
|
-
## Rules
|
|
200
|
-
|
|
201
|
-
1. Every step's agent field is "claude" (Claude Agent SDK execution)
|
|
202
|
-
2. Step description uses natural language \u2014 the agent decides which tools to use
|
|
203
|
-
3. depends_on must reference defined step IDs, forming a valid DAG (no cycles)
|
|
204
|
-
4. Step IDs use kebab-case: "fetch-issues", "create-branch"
|
|
205
|
-
5. Use $steps.{id}.output in inputs to reference prior step results
|
|
206
|
-
6. Use $trigger_data in inputs to reference the trigger's output data
|
|
207
|
-
7. Do NOT generate position, phase, schema_version, status, or timestamp fields (framework auto-fills). You MUST generate step id fields.
|
|
208
|
-
8. Trigger check_script can use any shell commands
|
|
209
|
-
9. Step description should be detailed enough for the agent to execute independently \u2014 not just a one-line summary
|
|
210
|
-
10. Input keys use snake_case (e.g., issue_number, repo_path)
|
|
211
|
-
11. Steps must NOT include a status field \u2014 the framework sets all steps to pending automatically
|
|
212
|
-
12. Step output is a plain text string \u2014 downstream agents parse structure themselves
|
|
213
|
-
|
|
214
|
-
## User Identity
|
|
215
|
-
${identity}`;
|
|
216
|
-
}
|
|
217
|
-
async function generatePlan(userDescription, config) {
|
|
218
|
-
const anthropic = new Anthropic({ apiKey: config.claude.api_key });
|
|
219
|
-
const MAX_RETRIES = 2;
|
|
220
|
-
let retryContext = "";
|
|
221
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
222
|
-
const prompt = retryContext ? `${userDescription}
|
|
223
|
-
|
|
224
|
-
${retryContext}` : userDescription;
|
|
225
|
-
logger.debug({ attempt, hasRetryContext: !!retryContext }, "Planner attempt");
|
|
226
|
-
const response = await anthropic.messages.create({
|
|
227
|
-
model: config.claude.planner.model,
|
|
228
|
-
max_tokens: 4096,
|
|
229
|
-
system: buildPlannerSystemPrompt(config),
|
|
230
|
-
messages: [{ role: "user", content: prompt }],
|
|
231
|
-
tools: [plannerTool],
|
|
232
|
-
tool_choice: { type: "tool", name: "create_workflow" }
|
|
233
|
-
});
|
|
234
|
-
const toolBlock = response.content.find((b) => b.type === "tool_use");
|
|
235
|
-
if (!toolBlock || toolBlock.type !== "tool_use") {
|
|
236
|
-
throw new PlannerError("Unexpected planner response format: no tool_use block");
|
|
237
|
-
}
|
|
238
|
-
const parseResult = PlannerOutputSchema.safeParse(toolBlock.input);
|
|
239
|
-
if (!parseResult.success) {
|
|
240
|
-
const errMsg = parseResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
241
|
-
if (attempt < MAX_RETRIES) {
|
|
242
|
-
retryContext = `[System] Previous plan had validation issues:
|
|
243
|
-
${errMsg}
|
|
244
|
-
Please fix and try again.`;
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
throw new PlannerError(`Invalid plan after ${MAX_RETRIES + 1} attempts: ${errMsg}`);
|
|
248
|
-
}
|
|
249
|
-
const dagErrors = validateDAG(parseResult.data.steps);
|
|
250
|
-
if (dagErrors.length > 0) {
|
|
251
|
-
if (attempt < MAX_RETRIES) {
|
|
252
|
-
retryContext = `[System] DAG dependency issues:
|
|
253
|
-
${dagErrors.join("\n")}
|
|
254
|
-
Please fix the step dependencies.`;
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
throw new PlannerError(`DAG validation failed after ${MAX_RETRIES + 1} attempts: ${dagErrors.join(", ")}`);
|
|
258
|
-
}
|
|
259
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
260
|
-
return {
|
|
261
|
-
...parseResult.data,
|
|
262
|
-
schema_version: "1.0",
|
|
263
|
-
id: `wf_${nanoid()}`,
|
|
264
|
-
phase: "awaiting_confirmation",
|
|
265
|
-
created_at: now,
|
|
266
|
-
updated_at: now
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
throw new PlannerError("Failed to generate valid plan after retries");
|
|
270
|
-
}
|
|
271
|
-
async function modifyPlan(originalWorkflow, modificationDescription, config) {
|
|
272
|
-
const plannerOutput = {
|
|
273
|
-
name: originalWorkflow.name,
|
|
274
|
-
description: originalWorkflow.description,
|
|
275
|
-
trigger: originalWorkflow.trigger,
|
|
276
|
-
steps: originalWorkflow.steps,
|
|
277
|
-
failure_policy: originalWorkflow.failure_policy
|
|
278
|
-
};
|
|
279
|
-
const combinedPrompt = `Here is the current workflow plan:
|
|
280
|
-
\`\`\`json
|
|
281
|
-
${JSON.stringify(plannerOutput, null, 2)}
|
|
282
|
-
\`\`\`
|
|
283
|
-
|
|
284
|
-
The user wants to modify it as follows:
|
|
285
|
-
${modificationDescription}
|
|
286
|
-
|
|
287
|
-
Preserve unmodified steps' IDs, descriptions, and dependencies \u2014 only change what the user specified.
|
|
288
|
-
Return the complete modified workflow using the create_workflow tool.`;
|
|
289
|
-
const result = await generatePlan(combinedPrompt, config);
|
|
290
|
-
return {
|
|
291
|
-
...result,
|
|
292
|
-
id: originalWorkflow.id,
|
|
293
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
function confirmPlan(workflow) {
|
|
297
|
-
if (workflow.phase !== "awaiting_confirmation") {
|
|
298
|
-
throw new PlannerError(`Cannot confirm workflow in phase "${workflow.phase}"`);
|
|
299
|
-
}
|
|
300
|
-
const nextPhase = workflow.trigger.type === "manual" ? "executing" : "active";
|
|
301
|
-
return {
|
|
302
|
-
...workflow,
|
|
303
|
-
phase: nextPhase,
|
|
304
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
function rejectPlan(workflow) {
|
|
308
|
-
return {
|
|
309
|
-
...workflow,
|
|
310
|
-
phase: "planning",
|
|
311
|
-
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
312
|
-
};
|
|
313
|
-
}
|
|
18
|
+
} from "./chunk-QBOYMF4A.js";
|
|
314
19
|
|
|
315
20
|
// src/executor.ts
|
|
316
|
-
import { nanoid as
|
|
21
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
317
22
|
|
|
318
23
|
// src/hooks.ts
|
|
319
24
|
function checkBashSafety(command) {
|
|
@@ -402,7 +107,7 @@ import {
|
|
|
402
107
|
mkdirSync
|
|
403
108
|
} from "fs";
|
|
404
109
|
import { join as join2 } from "path";
|
|
405
|
-
import { nanoid
|
|
110
|
+
import { nanoid } from "nanoid";
|
|
406
111
|
var IpcWatcher = class {
|
|
407
112
|
constructor(workflowId, _stepId, ipcDir, onMessage, pollInterval = 500) {
|
|
408
113
|
this.workflowId = workflowId;
|
|
@@ -451,7 +156,7 @@ var IpcWatcher = class {
|
|
|
451
156
|
sendToContainer(msg) {
|
|
452
157
|
const inputDir = join2(this.ipcDir, "input");
|
|
453
158
|
mkdirSync(inputDir, { recursive: true });
|
|
454
|
-
const filename = `${Date.now()}-${
|
|
159
|
+
const filename = `${Date.now()}-${nanoid(6)}.json`;
|
|
455
160
|
const tempPath = join2(inputDir, `.${filename}.tmp`);
|
|
456
161
|
const finalPath = join2(inputDir, filename);
|
|
457
162
|
writeFileSync2(tempPath, JSON.stringify(msg));
|
|
@@ -744,8 +449,17 @@ function runAgent(opts) {
|
|
|
744
449
|
}
|
|
745
450
|
let aborted = false;
|
|
746
451
|
const resultPromise = (async () => {
|
|
452
|
+
const authToken = config.claude.executor.api_key ?? config.claude.api_key;
|
|
453
|
+
const baseUrl = config.claude.executor.base_url ?? config.claude.base_url;
|
|
454
|
+
const prevAuthToken = process.env["ANTHROPIC_AUTH_TOKEN"];
|
|
455
|
+
const prevBaseUrl = process.env["ANTHROPIC_BASE_URL"];
|
|
456
|
+
process.env["ANTHROPIC_AUTH_TOKEN"] = authToken;
|
|
457
|
+
if (baseUrl !== "https://api.anthropic.com") {
|
|
458
|
+
process.env["ANTHROPIC_BASE_URL"] = baseUrl;
|
|
459
|
+
}
|
|
747
460
|
try {
|
|
748
461
|
const { query } = await import("@anthropic-ai/claude-agent-sdk");
|
|
462
|
+
const permMode = config.claude.executor.skip_permissions ? "dangerously-skip-permissions" : "default";
|
|
749
463
|
const q = query({
|
|
750
464
|
prompt: opts.prompt,
|
|
751
465
|
options: {
|
|
@@ -763,7 +477,7 @@ function runAgent(opts) {
|
|
|
763
477
|
"WebFetch"
|
|
764
478
|
],
|
|
765
479
|
settingSources: ["project"],
|
|
766
|
-
permissionMode:
|
|
480
|
+
permissionMode: permMode
|
|
767
481
|
}
|
|
768
482
|
});
|
|
769
483
|
let sessionId;
|
|
@@ -802,6 +516,11 @@ function runAgent(opts) {
|
|
|
802
516
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
803
517
|
logger.error({ err, stepId: opts.stepId }, "Agent execution failed");
|
|
804
518
|
return { status: "failed", error: errorMsg };
|
|
519
|
+
} finally {
|
|
520
|
+
if (prevAuthToken !== void 0) process.env["ANTHROPIC_AUTH_TOKEN"] = prevAuthToken;
|
|
521
|
+
else delete process.env["ANTHROPIC_AUTH_TOKEN"];
|
|
522
|
+
if (prevBaseUrl !== void 0) process.env["ANTHROPIC_BASE_URL"] = prevBaseUrl;
|
|
523
|
+
else delete process.env["ANTHROPIC_BASE_URL"];
|
|
805
524
|
}
|
|
806
525
|
})();
|
|
807
526
|
return {
|
|
@@ -813,11 +532,11 @@ function runAgent(opts) {
|
|
|
813
532
|
}
|
|
814
533
|
|
|
815
534
|
// src/session.ts
|
|
816
|
-
import { nanoid as
|
|
535
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
817
536
|
function createSession(db, stepRunId, sdkSessionId) {
|
|
818
537
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
819
538
|
const session = {
|
|
820
|
-
id: `sess_${
|
|
539
|
+
id: `sess_${nanoid2()}`,
|
|
821
540
|
step_run_id: stepRunId,
|
|
822
541
|
sdk_session_id: sdkSessionId,
|
|
823
542
|
created_at: now,
|
|
@@ -881,7 +600,7 @@ function hasSkipMarker(inputs) {
|
|
|
881
600
|
return false;
|
|
882
601
|
}
|
|
883
602
|
async function executeStepOnce(step, resolvedInputs, runId, db, cwd, onProgress) {
|
|
884
|
-
const stepRunId = `sr_${
|
|
603
|
+
const stepRunId = `sr_${nanoid3()}`;
|
|
885
604
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
886
605
|
const stepRun = {
|
|
887
606
|
id: stepRunId,
|
|
@@ -927,7 +646,7 @@ async function executeStepWithRetry(step, resolvedInputs, runId, db, cwd, policy
|
|
|
927
646
|
}
|
|
928
647
|
async function executeWorkflow(opts) {
|
|
929
648
|
const { workflow, triggerData, db, cwd, onStepFailure, onProgress, signal } = opts;
|
|
930
|
-
const runId = `run_${
|
|
649
|
+
const runId = `run_${nanoid3()}`;
|
|
931
650
|
const run = {
|
|
932
651
|
id: runId,
|
|
933
652
|
workflow_id: workflow.id,
|
|
@@ -946,6 +665,7 @@ async function executeWorkflow(opts) {
|
|
|
946
665
|
if (signal?.aborted) {
|
|
947
666
|
for (const id of remaining) {
|
|
948
667
|
completed.set(id, { status: "skipped", error: "Aborted" });
|
|
668
|
+
onProgress?.(id, { status: "skipped" });
|
|
949
669
|
}
|
|
950
670
|
remaining.clear();
|
|
951
671
|
runFailed = true;
|
|
@@ -967,7 +687,7 @@ async function executeWorkflow(opts) {
|
|
|
967
687
|
if (depsFailed && workflow.failure_policy.on_step_failure !== "ask_user") {
|
|
968
688
|
remaining.delete(id);
|
|
969
689
|
completed.set(id, { status: "skipped" });
|
|
970
|
-
const skipRunId = `sr_${
|
|
690
|
+
const skipRunId = `sr_${nanoid3()}`;
|
|
971
691
|
insertStepRun(db, { id: skipRunId, run_id: runId, step_id: id, status: "skipped" });
|
|
972
692
|
continue;
|
|
973
693
|
}
|
|
@@ -975,7 +695,7 @@ async function executeWorkflow(opts) {
|
|
|
975
695
|
if (hasSkipMarker(resolvedInputs)) {
|
|
976
696
|
remaining.delete(id);
|
|
977
697
|
completed.set(id, { status: "skipped" });
|
|
978
|
-
const skipRunId = `sr_${
|
|
698
|
+
const skipRunId = `sr_${nanoid3()}`;
|
|
979
699
|
insertStepRun(db, { id: skipRunId, run_id: runId, step_id: id, status: "skipped" });
|
|
980
700
|
continue;
|
|
981
701
|
}
|
|
@@ -1005,7 +725,7 @@ async function executeWorkflow(opts) {
|
|
|
1005
725
|
if (policy === "stop") {
|
|
1006
726
|
for (const remainingId of remaining) {
|
|
1007
727
|
completed.set(remainingId, { status: "skipped" });
|
|
1008
|
-
const skipRunId = `sr_${
|
|
728
|
+
const skipRunId = `sr_${nanoid3()}`;
|
|
1009
729
|
insertStepRun(db, { id: skipRunId, run_id: runId, step_id: remainingId, status: "skipped" });
|
|
1010
730
|
}
|
|
1011
731
|
remaining.clear();
|
|
@@ -1017,7 +737,7 @@ async function executeWorkflow(opts) {
|
|
|
1017
737
|
if (decision === "stop") {
|
|
1018
738
|
for (const remainingId of remaining) {
|
|
1019
739
|
completed.set(remainingId, { status: "skipped" });
|
|
1020
|
-
const skipRunId = `sr_${
|
|
740
|
+
const skipRunId = `sr_${nanoid3()}`;
|
|
1021
741
|
insertStepRun(db, { id: skipRunId, run_id: runId, step_id: remainingId, status: "skipped" });
|
|
1022
742
|
}
|
|
1023
743
|
remaining.clear();
|
|
@@ -1043,9 +763,7 @@ async function executeWorkflow(opts) {
|
|
|
1043
763
|
}
|
|
1044
764
|
|
|
1045
765
|
export {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
confirmPlan,
|
|
1049
|
-
rejectPlan,
|
|
766
|
+
resolveValue,
|
|
767
|
+
resolveInputs,
|
|
1050
768
|
executeWorkflow
|
|
1051
769
|
};
|
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MessageRouter
|
|
3
|
-
} from "./chunk-GMHDL4CG.js";
|
|
4
1
|
import {
|
|
5
2
|
executeWorkflow
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
initDb
|
|
9
|
-
} from "./chunk-K4PGB2DU.js";
|
|
10
|
-
import {
|
|
11
|
-
loadConfig
|
|
12
|
-
} from "./chunk-JRHM3Z4C.js";
|
|
3
|
+
} from "./chunk-SEYPA5M2.js";
|
|
13
4
|
import {
|
|
14
5
|
logger
|
|
15
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QBOYMF4A.js";
|
|
16
7
|
|
|
17
8
|
// src/trigger-loop.ts
|
|
18
9
|
import { CronExpressionParser } from "cron-parser";
|
|
@@ -233,76 +224,6 @@ var TriggerLoop = class {
|
|
|
233
224
|
}
|
|
234
225
|
};
|
|
235
226
|
|
|
236
|
-
// src/daemon.ts
|
|
237
|
-
async function startDaemon() {
|
|
238
|
-
const config = loadConfig();
|
|
239
|
-
const db = initDb();
|
|
240
|
-
const cwd = process.cwd();
|
|
241
|
-
const router = new MessageRouter(db, config, cwd);
|
|
242
|
-
if (config.whatsapp?.enabled) {
|
|
243
|
-
try {
|
|
244
|
-
const { WhatsAppChannel } = await import("./whatsapp-36XWDSJ5.js");
|
|
245
|
-
const wa = new WhatsAppChannel(
|
|
246
|
-
config.whatsapp.auth_dir ?? `${process.env["HOME"]}/.cueclaw/auth/whatsapp`,
|
|
247
|
-
config.whatsapp.allowed_jids ?? [],
|
|
248
|
-
(jid, msg) => router.handleInbound("whatsapp", jid, msg)
|
|
249
|
-
);
|
|
250
|
-
router.registerChannel(wa);
|
|
251
|
-
await wa.connect();
|
|
252
|
-
logger.info("WhatsApp channel started");
|
|
253
|
-
} catch (err) {
|
|
254
|
-
logger.error({ err }, "Failed to start WhatsApp channel");
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
if (config.telegram?.enabled) {
|
|
258
|
-
try {
|
|
259
|
-
const { TelegramChannel } = await import("./telegram-BTTWEETO.js");
|
|
260
|
-
const tg = new TelegramChannel(
|
|
261
|
-
config.telegram.token,
|
|
262
|
-
config.telegram.allowed_users ?? [],
|
|
263
|
-
(jid, msg) => router.handleInbound("telegram", jid, msg)
|
|
264
|
-
);
|
|
265
|
-
router.registerChannel(tg);
|
|
266
|
-
await tg.connect();
|
|
267
|
-
logger.info("Telegram channel started");
|
|
268
|
-
} catch (err) {
|
|
269
|
-
logger.error({ err }, "Failed to start Telegram channel");
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
await recoverRunningWorkflows(db, router);
|
|
273
|
-
const maxConcurrent = 5;
|
|
274
|
-
const triggerLoop = new TriggerLoop(db, router, cwd, maxConcurrent);
|
|
275
|
-
triggerLoop.start();
|
|
276
|
-
router.start();
|
|
277
|
-
logger.info("Daemon started");
|
|
278
|
-
const shutdown = () => {
|
|
279
|
-
logger.info("Shutting down daemon...");
|
|
280
|
-
triggerLoop.stop();
|
|
281
|
-
router.stop();
|
|
282
|
-
db.close();
|
|
283
|
-
process.exit(0);
|
|
284
|
-
};
|
|
285
|
-
process.on("SIGTERM", shutdown);
|
|
286
|
-
process.on("SIGINT", shutdown);
|
|
287
|
-
}
|
|
288
|
-
async function recoverRunningWorkflows(db, router) {
|
|
289
|
-
const interruptedRuns = db.prepare(
|
|
290
|
-
"SELECT id, workflow_id FROM workflow_runs WHERE status = 'running'"
|
|
291
|
-
).all();
|
|
292
|
-
if (interruptedRuns.length === 0) return;
|
|
293
|
-
logger.warn({ count: interruptedRuns.length }, "Recovering interrupted workflow runs");
|
|
294
|
-
for (const run of interruptedRuns) {
|
|
295
|
-
db.prepare(
|
|
296
|
-
"UPDATE workflow_runs SET status = 'failed', error = ?, completed_at = ? WHERE id = ?"
|
|
297
|
-
).run("Daemon restarted during execution", (/* @__PURE__ */ new Date()).toISOString(), run.id);
|
|
298
|
-
db.prepare(
|
|
299
|
-
"UPDATE step_runs SET status = 'failed', error = ? WHERE run_id = ? AND status = 'running'"
|
|
300
|
-
).run("Daemon restarted during execution", run.id);
|
|
301
|
-
router.broadcastNotification(
|
|
302
|
-
`Workflow ${run.workflow_id} was interrupted by daemon restart (run ${run.id})`
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
227
|
export {
|
|
307
|
-
|
|
228
|
+
TriggerLoop
|
|
308
229
|
};
|