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
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAnthropicClient
|
|
3
|
+
} from "./chunk-DVQFSFIZ.js";
|
|
4
|
+
import {
|
|
5
|
+
getConfiguredSecretKeys
|
|
6
|
+
} from "./chunk-ZCK3IFLC.js";
|
|
7
|
+
import {
|
|
8
|
+
PlannerError
|
|
9
|
+
} from "./chunk-BVQG3WYO.js";
|
|
10
|
+
import {
|
|
11
|
+
logger
|
|
12
|
+
} from "./chunk-QBOYMF4A.js";
|
|
13
|
+
|
|
14
|
+
// src/planner.ts
|
|
15
|
+
import { z } from "zod/v4";
|
|
16
|
+
import { nanoid } from "nanoid";
|
|
17
|
+
|
|
18
|
+
// src/workflow.ts
|
|
19
|
+
function validateDAG(steps) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
const stepIds = new Set(steps.map((s) => s.id));
|
|
22
|
+
for (const step of steps) {
|
|
23
|
+
for (const dep of step.depends_on) {
|
|
24
|
+
if (!stepIds.has(dep)) {
|
|
25
|
+
errors.push(`Step "${step.id}" depends on unknown step "${dep}"`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const step of steps) {
|
|
30
|
+
const refs = extractStepRefs(step.inputs);
|
|
31
|
+
for (const ref of refs) {
|
|
32
|
+
if (!stepIds.has(ref)) {
|
|
33
|
+
errors.push(`Step "${step.id}" references unknown step "${ref}" in inputs`);
|
|
34
|
+
}
|
|
35
|
+
if (stepIds.has(ref) && !step.depends_on.includes(ref)) {
|
|
36
|
+
errors.push(`Step "${step.id}" references "$steps.${ref}.output" but does not list "${ref}" in depends_on`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (hasCycle(steps)) {
|
|
41
|
+
errors.push("DAG contains a cycle");
|
|
42
|
+
}
|
|
43
|
+
return errors;
|
|
44
|
+
}
|
|
45
|
+
function extractStepRefs(obj) {
|
|
46
|
+
const refs = [];
|
|
47
|
+
const pattern = /\$steps\.([a-z0-9-]+)\.output/g;
|
|
48
|
+
function walk(value) {
|
|
49
|
+
if (typeof value === "string") {
|
|
50
|
+
let match;
|
|
51
|
+
while ((match = pattern.exec(value)) !== null) {
|
|
52
|
+
refs.push(match[1]);
|
|
53
|
+
}
|
|
54
|
+
pattern.lastIndex = 0;
|
|
55
|
+
} else if (Array.isArray(value)) {
|
|
56
|
+
for (const item of value) walk(item);
|
|
57
|
+
} else if (value !== null && typeof value === "object") {
|
|
58
|
+
for (const v of Object.values(value)) walk(v);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
walk(obj);
|
|
62
|
+
return refs;
|
|
63
|
+
}
|
|
64
|
+
function hasCycle(steps) {
|
|
65
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
66
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
67
|
+
for (const step of steps) {
|
|
68
|
+
inDegree.set(step.id, 0);
|
|
69
|
+
adjacency.set(step.id, []);
|
|
70
|
+
}
|
|
71
|
+
for (const step of steps) {
|
|
72
|
+
for (const dep of step.depends_on) {
|
|
73
|
+
if (adjacency.has(dep)) {
|
|
74
|
+
adjacency.get(dep).push(step.id);
|
|
75
|
+
inDegree.set(step.id, (inDegree.get(step.id) ?? 0) + 1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const queue = [];
|
|
80
|
+
for (const [id, deg] of inDegree) {
|
|
81
|
+
if (deg === 0) queue.push(id);
|
|
82
|
+
}
|
|
83
|
+
let processed = 0;
|
|
84
|
+
while (queue.length > 0) {
|
|
85
|
+
const current = queue.shift();
|
|
86
|
+
processed++;
|
|
87
|
+
for (const neighbor of adjacency.get(current) ?? []) {
|
|
88
|
+
const newDeg = (inDegree.get(neighbor) ?? 1) - 1;
|
|
89
|
+
inDegree.set(neighbor, newDeg);
|
|
90
|
+
if (newDeg === 0) queue.push(neighbor);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return processed !== steps.length;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/planner.ts
|
|
97
|
+
var PlanStepSchema = z.object({
|
|
98
|
+
id: z.string(),
|
|
99
|
+
description: z.string(),
|
|
100
|
+
expected_output: z.string().optional(),
|
|
101
|
+
agent: z.literal("claude"),
|
|
102
|
+
inputs: z.record(z.string(), z.any()),
|
|
103
|
+
depends_on: z.array(z.string())
|
|
104
|
+
});
|
|
105
|
+
var TriggerConfigSchema = z.union([
|
|
106
|
+
z.object({
|
|
107
|
+
type: z.literal("poll"),
|
|
108
|
+
interval_seconds: z.number(),
|
|
109
|
+
check_script: z.string(),
|
|
110
|
+
diff_mode: z.enum(["new_items", "any_change"])
|
|
111
|
+
}),
|
|
112
|
+
z.object({
|
|
113
|
+
type: z.literal("cron"),
|
|
114
|
+
expression: z.string(),
|
|
115
|
+
timezone: z.string().optional()
|
|
116
|
+
}),
|
|
117
|
+
z.object({ type: z.literal("manual") })
|
|
118
|
+
]);
|
|
119
|
+
var FailurePolicySchema = z.object({
|
|
120
|
+
on_step_failure: z.enum(["stop", "skip_dependents", "ask_user"]),
|
|
121
|
+
max_retries: z.number(),
|
|
122
|
+
retry_delay_ms: z.number()
|
|
123
|
+
});
|
|
124
|
+
var PlannerOutputSchema = z.object({
|
|
125
|
+
name: z.string(),
|
|
126
|
+
description: z.string(),
|
|
127
|
+
trigger: TriggerConfigSchema,
|
|
128
|
+
steps: z.array(PlanStepSchema).min(1),
|
|
129
|
+
failure_policy: FailurePolicySchema
|
|
130
|
+
});
|
|
131
|
+
var plannerOutputJsonSchema = {
|
|
132
|
+
type: "object",
|
|
133
|
+
required: ["name", "description", "trigger", "steps", "failure_policy"],
|
|
134
|
+
properties: {
|
|
135
|
+
name: { type: "string", description: "Short workflow name" },
|
|
136
|
+
description: { type: "string", description: "User's original natural language description" },
|
|
137
|
+
trigger: {
|
|
138
|
+
type: "object",
|
|
139
|
+
description: "Trigger configuration",
|
|
140
|
+
properties: {
|
|
141
|
+
type: { type: "string", enum: ["poll", "cron", "manual"] },
|
|
142
|
+
interval_seconds: { type: "number" },
|
|
143
|
+
check_script: { type: "string" },
|
|
144
|
+
diff_mode: { type: "string", enum: ["new_items", "any_change"] },
|
|
145
|
+
expression: { type: "string" },
|
|
146
|
+
timezone: { type: "string" }
|
|
147
|
+
},
|
|
148
|
+
required: ["type"]
|
|
149
|
+
},
|
|
150
|
+
steps: {
|
|
151
|
+
type: "array",
|
|
152
|
+
items: {
|
|
153
|
+
type: "object",
|
|
154
|
+
required: ["id", "description", "agent", "inputs", "depends_on"],
|
|
155
|
+
properties: {
|
|
156
|
+
id: { type: "string", description: "kebab-case step ID" },
|
|
157
|
+
description: { type: "string", description: "Detailed step description for the agent" },
|
|
158
|
+
expected_output: { type: "string" },
|
|
159
|
+
agent: { type: "string", enum: ["claude"] },
|
|
160
|
+
inputs: { type: "object", description: "Step inputs, may use $steps.{id}.output or $trigger_data" },
|
|
161
|
+
depends_on: { type: "array", items: { type: "string" }, description: "Step IDs this step depends on" }
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
minItems: 1
|
|
165
|
+
},
|
|
166
|
+
failure_policy: {
|
|
167
|
+
type: "object",
|
|
168
|
+
required: ["on_step_failure", "max_retries", "retry_delay_ms"],
|
|
169
|
+
properties: {
|
|
170
|
+
on_step_failure: { type: "string", enum: ["stop", "skip_dependents", "ask_user"] },
|
|
171
|
+
max_retries: { type: "number" },
|
|
172
|
+
retry_delay_ms: { type: "number" }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var plannerTool = {
|
|
178
|
+
name: "create_workflow",
|
|
179
|
+
description: "Create a workflow definition from the user description. Generate steps as a DAG with proper dependencies.",
|
|
180
|
+
input_schema: plannerOutputJsonSchema
|
|
181
|
+
};
|
|
182
|
+
var askQuestionTool = {
|
|
183
|
+
name: "ask_question",
|
|
184
|
+
description: "Ask the user a clarifying question to gather more information before creating a workflow. Use this when the user description is ambiguous or missing key details such as trigger frequency, specific repositories, filters, output format, etc.",
|
|
185
|
+
input_schema: {
|
|
186
|
+
type: "object",
|
|
187
|
+
required: ["question"],
|
|
188
|
+
properties: {
|
|
189
|
+
question: { type: "string", description: "The question to ask the user" }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var setSecretTool = {
|
|
194
|
+
name: "set_secret",
|
|
195
|
+
description: "Store a secret/credential provided by the user (e.g., API token, webhook URL). The value will be saved to the environment for use by workflow steps. Only call this after the user explicitly provides the secret value.",
|
|
196
|
+
input_schema: {
|
|
197
|
+
type: "object",
|
|
198
|
+
required: ["key", "value"],
|
|
199
|
+
properties: {
|
|
200
|
+
key: { type: "string", description: "Environment variable name in UPPER_SNAKE_CASE (e.g., GITHUB_TOKEN)" },
|
|
201
|
+
value: { type: "string", description: "The secret value provided by the user" }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
function parsePlannerToolResponse(response) {
|
|
206
|
+
if (!response.content || !Array.isArray(response.content)) {
|
|
207
|
+
return { type: "error", error: "Unexpected API response: no content array" };
|
|
208
|
+
}
|
|
209
|
+
const askBlock = response.content.find((b) => b.type === "tool_use" && b.name === "ask_question");
|
|
210
|
+
if (askBlock && askBlock.type === "tool_use") {
|
|
211
|
+
const question = askBlock.input?.question;
|
|
212
|
+
return { type: "question", question: question ?? "Could you provide more details?" };
|
|
213
|
+
}
|
|
214
|
+
const secretBlock = response.content.find((b) => b.type === "tool_use" && b.name === "set_secret");
|
|
215
|
+
if (secretBlock && secretBlock.type === "tool_use") {
|
|
216
|
+
const input = secretBlock.input;
|
|
217
|
+
return { type: "set_secret", key: input.key, value: input.value };
|
|
218
|
+
}
|
|
219
|
+
const toolBlock = response.content.find((b) => b.type === "tool_use" && b.name === "create_workflow");
|
|
220
|
+
if (toolBlock && toolBlock.type === "tool_use") {
|
|
221
|
+
const parseResult = PlannerOutputSchema.safeParse(toolBlock.input);
|
|
222
|
+
if (!parseResult.success) {
|
|
223
|
+
const errMsg = parseResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
224
|
+
return { type: "error", error: `Invalid plan: ${errMsg}` };
|
|
225
|
+
}
|
|
226
|
+
const dagErrors = validateDAG(parseResult.data.steps);
|
|
227
|
+
if (dagErrors.length > 0) {
|
|
228
|
+
return { type: "error", error: `DAG validation failed: ${dagErrors.join(", ")}` };
|
|
229
|
+
}
|
|
230
|
+
return { type: "plan", plannerOutput: parseResult.data };
|
|
231
|
+
}
|
|
232
|
+
const textBlocks = response.content.filter((b) => b.type === "text");
|
|
233
|
+
if (textBlocks.length > 0) {
|
|
234
|
+
const text = textBlocks.map((b) => b.type === "text" ? b.text : "").join("\n");
|
|
235
|
+
return { type: "text", text };
|
|
236
|
+
}
|
|
237
|
+
return { type: "error", error: "Unexpected planner response format" };
|
|
238
|
+
}
|
|
239
|
+
function buildPlannerSystemPrompt(config) {
|
|
240
|
+
const identity = config.identity?.name ? `
|
|
241
|
+
User identity: ${config.identity.name}` : "";
|
|
242
|
+
return `You are CueClaw Planner. Convert user's natural language into a structured Workflow.
|
|
243
|
+
|
|
244
|
+
## Execution Environment
|
|
245
|
+
|
|
246
|
+
All steps are executed by the Claude Agent SDK. The agent has Bash and can use
|
|
247
|
+
any CLI tool installed locally. It auto-loads .claude/skills/.
|
|
248
|
+
You don't need to verify tool availability \u2014 the agent detects at runtime.
|
|
249
|
+
|
|
250
|
+
## Rules
|
|
251
|
+
|
|
252
|
+
1. Every step's agent field is "claude" (Claude Agent SDK execution)
|
|
253
|
+
2. Step description uses natural language \u2014 the agent decides which tools to use
|
|
254
|
+
3. depends_on must reference defined step IDs, forming a valid DAG (no cycles)
|
|
255
|
+
4. Step IDs use kebab-case: "fetch-issues", "create-branch"
|
|
256
|
+
5. Use $steps.{id}.output in inputs to reference prior step results
|
|
257
|
+
6. Use $trigger_data in inputs to reference the trigger's output data
|
|
258
|
+
7. Do NOT generate position, phase, schema_version, status, or timestamp fields (framework auto-fills). You MUST generate step id fields.
|
|
259
|
+
8. Trigger check_script can use any shell commands
|
|
260
|
+
9. Step description should be detailed enough for the agent to execute independently \u2014 not just a one-line summary
|
|
261
|
+
10. Input keys use snake_case (e.g., issue_number, repo_path)
|
|
262
|
+
11. Steps must NOT include a status field \u2014 the framework sets all steps to pending automatically
|
|
263
|
+
12. Step output is a plain text string \u2014 downstream agents parse structure themselves
|
|
264
|
+
|
|
265
|
+
## Available Credentials
|
|
266
|
+
|
|
267
|
+
${(() => {
|
|
268
|
+
const keys = getConfiguredSecretKeys();
|
|
269
|
+
if (keys.length > 0) {
|
|
270
|
+
return `The following credentials are configured: ${keys.join(", ")}
|
|
271
|
+
You can reference these in workflow steps \u2014 they are available as environment variables.`;
|
|
272
|
+
}
|
|
273
|
+
return "No credentials are currently configured.";
|
|
274
|
+
})()}
|
|
275
|
+
|
|
276
|
+
If a workflow needs credentials not listed above, use the set_secret tool to store them after the user provides the value. Never invent or guess secret values.
|
|
277
|
+
|
|
278
|
+
## User Identity
|
|
279
|
+
${identity}`;
|
|
280
|
+
}
|
|
281
|
+
async function generatePlan(userDescription, config) {
|
|
282
|
+
const anthropic = createAnthropicClient(config);
|
|
283
|
+
const MAX_RETRIES = 2;
|
|
284
|
+
let retryContext = "";
|
|
285
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
286
|
+
const prompt = retryContext ? `${userDescription}
|
|
287
|
+
|
|
288
|
+
${retryContext}` : userDescription;
|
|
289
|
+
logger.debug({ attempt, hasRetryContext: !!retryContext }, "Planner attempt");
|
|
290
|
+
let response;
|
|
291
|
+
try {
|
|
292
|
+
response = await anthropic.messages.create({
|
|
293
|
+
model: config.claude.planner.model,
|
|
294
|
+
max_tokens: 4096,
|
|
295
|
+
system: buildPlannerSystemPrompt(config),
|
|
296
|
+
messages: [{ role: "user", content: prompt }],
|
|
297
|
+
tools: [plannerTool],
|
|
298
|
+
tool_choice: { type: "tool", name: "create_workflow" }
|
|
299
|
+
});
|
|
300
|
+
} catch (err) {
|
|
301
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
302
|
+
throw new PlannerError(
|
|
303
|
+
`API request failed: ${detail}. Check your API key and base_url in ~/.cueclaw/config.yaml`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const rawResponse = response;
|
|
307
|
+
if (rawResponse.type === "error" || rawResponse.error) {
|
|
308
|
+
const errMsg = rawResponse.error?.message ?? JSON.stringify(rawResponse.error ?? rawResponse);
|
|
309
|
+
throw new PlannerError(`API error: ${errMsg}`);
|
|
310
|
+
}
|
|
311
|
+
if (!response.content || !Array.isArray(response.content)) {
|
|
312
|
+
logger.debug({ response: JSON.stringify(response).slice(0, 500) }, "Unexpected API response shape");
|
|
313
|
+
throw new PlannerError(
|
|
314
|
+
`Unexpected API response (no content array). This may indicate an issue with your API provider. Response: ${JSON.stringify(response).slice(0, 200)}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
const toolBlock = response.content.find((b) => b.type === "tool_use");
|
|
318
|
+
if (!toolBlock || toolBlock.type !== "tool_use") {
|
|
319
|
+
throw new PlannerError("Unexpected planner response format: no tool_use block");
|
|
320
|
+
}
|
|
321
|
+
const parseResult = PlannerOutputSchema.safeParse(toolBlock.input);
|
|
322
|
+
if (!parseResult.success) {
|
|
323
|
+
const errMsg = parseResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
324
|
+
if (attempt < MAX_RETRIES) {
|
|
325
|
+
retryContext = `[System] Previous plan had validation issues:
|
|
326
|
+
${errMsg}
|
|
327
|
+
Please fix and try again.`;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
throw new PlannerError(`Invalid plan after ${MAX_RETRIES + 1} attempts: ${errMsg}`);
|
|
331
|
+
}
|
|
332
|
+
const dagErrors = validateDAG(parseResult.data.steps);
|
|
333
|
+
if (dagErrors.length > 0) {
|
|
334
|
+
if (attempt < MAX_RETRIES) {
|
|
335
|
+
retryContext = `[System] DAG dependency issues:
|
|
336
|
+
${dagErrors.join("\n")}
|
|
337
|
+
Please fix the step dependencies.`;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
throw new PlannerError(`DAG validation failed after ${MAX_RETRIES + 1} attempts: ${dagErrors.join(", ")}`);
|
|
341
|
+
}
|
|
342
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
343
|
+
return {
|
|
344
|
+
...parseResult.data,
|
|
345
|
+
schema_version: "1.0",
|
|
346
|
+
id: `wf_${nanoid()}`,
|
|
347
|
+
phase: "awaiting_confirmation",
|
|
348
|
+
created_at: now,
|
|
349
|
+
updated_at: now
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
throw new PlannerError("Failed to generate valid plan after retries");
|
|
353
|
+
}
|
|
354
|
+
async function modifyPlan(originalWorkflow, modificationDescription, config) {
|
|
355
|
+
const plannerOutput = {
|
|
356
|
+
name: originalWorkflow.name,
|
|
357
|
+
description: originalWorkflow.description,
|
|
358
|
+
trigger: originalWorkflow.trigger,
|
|
359
|
+
steps: originalWorkflow.steps,
|
|
360
|
+
failure_policy: originalWorkflow.failure_policy
|
|
361
|
+
};
|
|
362
|
+
const combinedPrompt = `Here is the current workflow plan:
|
|
363
|
+
\`\`\`json
|
|
364
|
+
${JSON.stringify(plannerOutput, null, 2)}
|
|
365
|
+
\`\`\`
|
|
366
|
+
|
|
367
|
+
The user wants to modify it as follows:
|
|
368
|
+
${modificationDescription}
|
|
369
|
+
|
|
370
|
+
Preserve unmodified steps' IDs, descriptions, and dependencies \u2014 only change what the user specified.
|
|
371
|
+
Return the complete modified workflow using the create_workflow tool.`;
|
|
372
|
+
const result = await generatePlan(combinedPrompt, config);
|
|
373
|
+
return {
|
|
374
|
+
...result,
|
|
375
|
+
id: originalWorkflow.id,
|
|
376
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function confirmPlan(workflow) {
|
|
380
|
+
if (workflow.phase !== "awaiting_confirmation") {
|
|
381
|
+
throw new PlannerError(`Cannot confirm workflow in phase "${workflow.phase}"`);
|
|
382
|
+
}
|
|
383
|
+
const nextPhase = workflow.trigger.type === "manual" ? "executing" : "active";
|
|
384
|
+
return {
|
|
385
|
+
...workflow,
|
|
386
|
+
phase: nextPhase,
|
|
387
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function rejectPlan(workflow) {
|
|
391
|
+
return {
|
|
392
|
+
...workflow,
|
|
393
|
+
phase: "planning",
|
|
394
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export {
|
|
399
|
+
PlannerOutputSchema,
|
|
400
|
+
plannerTool,
|
|
401
|
+
askQuestionTool,
|
|
402
|
+
setSecretTool,
|
|
403
|
+
parsePlannerToolResponse,
|
|
404
|
+
buildPlannerSystemPrompt,
|
|
405
|
+
generatePlan,
|
|
406
|
+
modifyPlan,
|
|
407
|
+
confirmPlan,
|
|
408
|
+
rejectPlan
|
|
409
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/env.ts
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
3
|
+
import { resolve, dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { parse } from "dotenv";
|
|
6
|
+
var secrets = {};
|
|
7
|
+
var isDev = !import.meta.url.includes("/dist/");
|
|
8
|
+
function loadSecrets() {
|
|
9
|
+
if (!isDev) return;
|
|
10
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const envPath = resolve(thisDir, "..", ".env");
|
|
12
|
+
if (!existsSync(envPath)) return;
|
|
13
|
+
const content = readFileSync(envPath, "utf-8");
|
|
14
|
+
secrets = parse(content);
|
|
15
|
+
for (const [key, value] of Object.entries(secrets)) {
|
|
16
|
+
if (process.env[key] === void 0) {
|
|
17
|
+
process.env[key] = value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function getSecret(key) {
|
|
22
|
+
return secrets[key] ?? process.env[key];
|
|
23
|
+
}
|
|
24
|
+
function hasSecret(key) {
|
|
25
|
+
return getSecret(key) !== void 0;
|
|
26
|
+
}
|
|
27
|
+
function getConfiguredSecretKeys() {
|
|
28
|
+
const patterns = [/_TOKEN$/, /_API_KEY$/, /_SECRET$/, /_WEBHOOK/, /_PASSWORD$/];
|
|
29
|
+
return Object.keys(process.env).filter((k) => patterns.some((p) => p.test(k)) && process.env[k]).sort();
|
|
30
|
+
}
|
|
31
|
+
function writeEnvVar(key, value) {
|
|
32
|
+
if (!isDev) throw new Error("writeEnvVar should only be called in dev mode");
|
|
33
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const envPath = resolve(thisDir, "..", ".env");
|
|
35
|
+
let lines = [];
|
|
36
|
+
if (existsSync(envPath)) {
|
|
37
|
+
lines = readFileSync(envPath, "utf-8").split("\n");
|
|
38
|
+
}
|
|
39
|
+
const idx = lines.findIndex((l) => l.startsWith(`${key}=`));
|
|
40
|
+
const entry = `${key}=${value}`;
|
|
41
|
+
if (idx >= 0) {
|
|
42
|
+
lines[idx] = entry;
|
|
43
|
+
} else {
|
|
44
|
+
lines.push(entry);
|
|
45
|
+
}
|
|
46
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
47
|
+
writeFileSync(envPath, lines.join("\n") + "\n", "utf-8");
|
|
48
|
+
process.env[key] = value;
|
|
49
|
+
secrets[key] = value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
isDev,
|
|
54
|
+
loadSecrets,
|
|
55
|
+
getSecret,
|
|
56
|
+
hasSecret,
|
|
57
|
+
getConfiguredSecretKeys,
|
|
58
|
+
writeEnvVar
|
|
59
|
+
};
|