cueclaw 0.0.1 → 0.0.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.
@@ -0,0 +1,10 @@
1
+ // src/logger.ts
2
+ import pino from "pino";
3
+ var logger = pino({
4
+ level: process.env["LOG_LEVEL"] ?? "info",
5
+ transport: process.env["NODE_ENV"] === "production" ? void 0 : { target: "pino-pretty", options: { colorize: true } }
6
+ });
7
+
8
+ export {
9
+ logger
10
+ };
@@ -0,0 +1,250 @@
1
+ import {
2
+ confirmPlan,
3
+ executeWorkflow,
4
+ generatePlan,
5
+ modifyPlan,
6
+ rejectPlan
7
+ } from "./chunk-D77G7ABJ.js";
8
+ import {
9
+ getWorkflow,
10
+ listWorkflows,
11
+ updateWorkflowPhase
12
+ } from "./chunk-K4PGB2DU.js";
13
+ import {
14
+ logger
15
+ } from "./chunk-E7BP6DMO.js";
16
+
17
+ // src/router.ts
18
+ var CONFIRMATION_TIMEOUT = 10 * 6e4;
19
+ var RATE_LIMIT_WINDOW = 6e4;
20
+ var RATE_LIMIT_MAX = 10;
21
+ var CLEANUP_INTERVAL = 5 * 6e4;
22
+ var MessageRouter = class {
23
+ constructor(db, config, cwd) {
24
+ this.db = db;
25
+ this.config = config;
26
+ this.cwd = cwd;
27
+ }
28
+ channels = /* @__PURE__ */ new Map();
29
+ pendingConfirmations = /* @__PURE__ */ new Map();
30
+ messageTimestamps = /* @__PURE__ */ new Map();
31
+ cleanupTimer = null;
32
+ registerChannel(channel) {
33
+ this.channels.set(channel.name, channel);
34
+ }
35
+ start() {
36
+ this.cleanupTimer = setInterval(() => this.cleanupRateLimits(), CLEANUP_INTERVAL);
37
+ }
38
+ stop() {
39
+ if (this.cleanupTimer) {
40
+ clearInterval(this.cleanupTimer);
41
+ this.cleanupTimer = null;
42
+ }
43
+ }
44
+ /** Broadcast a notification to all connected channels (used by MCP handler) */
45
+ broadcastNotification(message) {
46
+ for (const channel of this.channels.values()) {
47
+ if (channel.isConnected()) {
48
+ channel.sendMessage("broadcast", message).catch((err) => {
49
+ logger.error({ err, channel: channel.name }, "Failed to broadcast notification");
50
+ });
51
+ }
52
+ }
53
+ }
54
+ async handleInbound(channelName, chatJid, message) {
55
+ const channel = this.channels.get(channelName);
56
+ if (!channel) {
57
+ logger.warn({ channelName }, "Message from unknown channel");
58
+ return;
59
+ }
60
+ const text = typeof message === "string" ? message : message.text;
61
+ if (this.isRateLimited(chatJid)) {
62
+ await channel.sendMessage(chatJid, "Rate limited, please wait before sending more messages.");
63
+ return;
64
+ }
65
+ if (text.startsWith("/") || text.startsWith("!")) {
66
+ await this.handleCommand(channel, chatJid, text);
67
+ } else if (this.hasPendingConfirmation(chatJid)) {
68
+ await this.handleConfirmation(channel, chatJid, text);
69
+ } else {
70
+ await this.handleNewWorkflow(channel, chatJid, text);
71
+ }
72
+ }
73
+ async handleCommand(channel, chatJid, text) {
74
+ const parts = text.slice(1).split(/\s+/);
75
+ const command = parts[0]?.toLowerCase();
76
+ const args = parts.slice(1).join(" ");
77
+ switch (command) {
78
+ case "new":
79
+ if (args) {
80
+ await this.handleNewWorkflow(channel, chatJid, args);
81
+ } else {
82
+ await channel.sendMessage(chatJid, "Send a workflow description to create a new workflow.");
83
+ }
84
+ break;
85
+ case "list": {
86
+ const workflows = listWorkflows(this.db);
87
+ if (workflows.length === 0) {
88
+ await channel.sendMessage(chatJid, "No workflows found.");
89
+ } else {
90
+ const lines = workflows.map(
91
+ (wf) => `${wf.id.slice(0, 8)} ${wf.phase.padEnd(12)} ${wf.name}`
92
+ );
93
+ await channel.sendMessage(chatJid, `Workflows:
94
+ ${lines.join("\n")}`);
95
+ }
96
+ break;
97
+ }
98
+ case "status": {
99
+ if (!args) {
100
+ await channel.sendMessage(chatJid, "Usage: /status <workflow-id>");
101
+ break;
102
+ }
103
+ const wf = getWorkflow(this.db, args);
104
+ if (!wf) {
105
+ await channel.sendMessage(chatJid, `Workflow not found: ${args}`);
106
+ } else {
107
+ const steps = wf.steps.map((s, i) => `${i + 1}. ${s.description}`).join("\n");
108
+ await channel.sendMessage(chatJid, `Workflow: ${wf.name}
109
+ Phase: ${wf.phase}
110
+
111
+ Steps:
112
+ ${steps}`);
113
+ }
114
+ break;
115
+ }
116
+ case "cancel": {
117
+ if (!args) {
118
+ await channel.sendMessage(chatJid, "Usage: /cancel <workflow-id>");
119
+ break;
120
+ }
121
+ const wf = getWorkflow(this.db, args);
122
+ if (!wf) {
123
+ await channel.sendMessage(chatJid, `Workflow not found: ${args}`);
124
+ } else {
125
+ const rejected = rejectPlan(wf);
126
+ updateWorkflowPhase(this.db, wf.id, rejected.phase);
127
+ await channel.sendMessage(chatJid, `Workflow cancelled: ${wf.name}`);
128
+ }
129
+ break;
130
+ }
131
+ case "help":
132
+ await channel.sendMessage(chatJid, [
133
+ "Commands:",
134
+ "/new <description> \u2014 Create a new workflow",
135
+ "/list \u2014 List all workflows",
136
+ "/status <id> \u2014 View workflow status",
137
+ "/cancel <id> \u2014 Cancel a workflow",
138
+ "/help \u2014 Show this help"
139
+ ].join("\n"));
140
+ break;
141
+ default:
142
+ await channel.sendMessage(chatJid, `Unknown command: ${command}. Type /help for available commands.`);
143
+ }
144
+ }
145
+ async handleNewWorkflow(channel, chatJid, text) {
146
+ await channel.sendMessage(chatJid, "Generating execution plan...");
147
+ channel.setTyping?.(chatJid, true);
148
+ try {
149
+ const workflow = await generatePlan(text, this.config);
150
+ channel.setTyping?.(chatJid, false);
151
+ this.pendingConfirmations.set(chatJid, {
152
+ workflowId: workflow.id,
153
+ workflow,
154
+ expiresAt: Date.now() + CONFIRMATION_TIMEOUT
155
+ });
156
+ await channel.sendConfirmation(chatJid, workflow);
157
+ } catch (err) {
158
+ channel.setTyping?.(chatJid, false);
159
+ const msg = err instanceof Error ? err.message : String(err);
160
+ await channel.sendMessage(chatJid, `Failed to generate plan: ${msg}`);
161
+ logger.error({ err, chatJid }, "Plan generation failed");
162
+ }
163
+ }
164
+ async handleConfirmation(channel, chatJid, text) {
165
+ const pending = this.pendingConfirmations.get(chatJid);
166
+ if (!pending) return;
167
+ const lower = text.toLowerCase().trim();
168
+ if (["yes", "y", "confirm", "1"].includes(lower)) {
169
+ this.pendingConfirmations.delete(chatJid);
170
+ const confirmed = confirmPlan(pending.workflow);
171
+ await channel.sendMessage(chatJid, `Workflow activated: ${confirmed.name} (${confirmed.id})`);
172
+ channel.setTyping?.(chatJid, true);
173
+ try {
174
+ const result = await executeWorkflow({
175
+ workflow: confirmed,
176
+ triggerData: null,
177
+ db: this.db,
178
+ cwd: this.cwd,
179
+ onProgress: async (stepId, msg) => {
180
+ if (typeof msg === "object" && msg?.type === "step_complete") {
181
+ await channel.sendMessage(chatJid, `Step completed: ${stepId}`);
182
+ }
183
+ }
184
+ });
185
+ channel.setTyping?.(chatJid, false);
186
+ await channel.sendMessage(chatJid, `Workflow complete! Status: ${result.status}`);
187
+ } catch (err) {
188
+ channel.setTyping?.(chatJid, false);
189
+ const msg = err instanceof Error ? err.message : String(err);
190
+ await channel.sendMessage(chatJid, `Workflow failed: ${msg}`);
191
+ }
192
+ } else if (["no", "n", "cancel", "3"].includes(lower)) {
193
+ this.pendingConfirmations.delete(chatJid);
194
+ rejectPlan(pending.workflow);
195
+ await channel.sendMessage(chatJid, "Plan cancelled.");
196
+ } else if (["modify", "m", "2"].includes(lower)) {
197
+ await channel.sendMessage(chatJid, "Describe your modifications:");
198
+ } else {
199
+ this.pendingConfirmations.delete(chatJid);
200
+ await channel.sendMessage(chatJid, "Modifying plan...");
201
+ channel.setTyping?.(chatJid, true);
202
+ try {
203
+ const modified = await modifyPlan(pending.workflow, text, this.config);
204
+ channel.setTyping?.(chatJid, false);
205
+ this.pendingConfirmations.set(chatJid, {
206
+ workflowId: modified.id,
207
+ workflow: modified,
208
+ expiresAt: Date.now() + CONFIRMATION_TIMEOUT
209
+ });
210
+ await channel.sendConfirmation(chatJid, modified);
211
+ } catch (err) {
212
+ channel.setTyping?.(chatJid, false);
213
+ const msg = err instanceof Error ? err.message : String(err);
214
+ await channel.sendMessage(chatJid, `Modification failed: ${msg}`);
215
+ }
216
+ }
217
+ }
218
+ hasPendingConfirmation(chatJid) {
219
+ const pending = this.pendingConfirmations.get(chatJid);
220
+ if (!pending) return false;
221
+ if (Date.now() > pending.expiresAt) {
222
+ this.pendingConfirmations.delete(chatJid);
223
+ return false;
224
+ }
225
+ return true;
226
+ }
227
+ isRateLimited(chatJid) {
228
+ const now = Date.now();
229
+ const timestamps = this.messageTimestamps.get(chatJid) ?? [];
230
+ const recent = timestamps.filter((t) => now - t < RATE_LIMIT_WINDOW);
231
+ recent.push(now);
232
+ this.messageTimestamps.set(chatJid, recent);
233
+ return recent.length > RATE_LIMIT_MAX;
234
+ }
235
+ cleanupRateLimits() {
236
+ const now = Date.now();
237
+ for (const [jid, timestamps] of this.messageTimestamps) {
238
+ const recent = timestamps.filter((t) => now - t < RATE_LIMIT_WINDOW);
239
+ if (recent.length === 0) {
240
+ this.messageTimestamps.delete(jid);
241
+ } else {
242
+ this.messageTimestamps.set(jid, recent);
243
+ }
244
+ }
245
+ }
246
+ };
247
+
248
+ export {
249
+ MessageRouter
250
+ };
@@ -0,0 +1,158 @@
1
+ // src/config.ts
2
+ import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { parse as parseYaml } from "yaml";
6
+ import { z } from "zod/v4";
7
+
8
+ // src/types.ts
9
+ var CueclawError = class extends Error {
10
+ constructor(message, code) {
11
+ super(message);
12
+ this.code = code;
13
+ this.name = "CueclawError";
14
+ }
15
+ };
16
+ var PlannerError = class extends CueclawError {
17
+ constructor(message) {
18
+ super(message, "PLANNER_ERROR");
19
+ }
20
+ };
21
+ var ExecutorError = class extends CueclawError {
22
+ constructor(message) {
23
+ super(message, "EXECUTOR_ERROR");
24
+ }
25
+ };
26
+ var ConfigError = class extends CueclawError {
27
+ constructor(message) {
28
+ super(message, "CONFIG_ERROR");
29
+ }
30
+ };
31
+
32
+ // src/config.ts
33
+ var ConfigSchema = z.object({
34
+ claude: z.object({
35
+ api_key: z.string(),
36
+ base_url: z.string().url().default("https://api.anthropic.com"),
37
+ planner: z.object({ model: z.string().default("claude-sonnet-4-6") }).default({ model: "claude-sonnet-4-6" }),
38
+ executor: z.object({ model: z.string().default("claude-sonnet-4-6") }).default({ model: "claude-sonnet-4-6" })
39
+ }),
40
+ identity: z.object({ name: z.string() }).optional(),
41
+ whatsapp: z.object({
42
+ enabled: z.boolean().default(false),
43
+ auth_dir: z.string().default("~/.cueclaw/auth/whatsapp"),
44
+ allowed_jids: z.array(z.string()).default([])
45
+ }).optional(),
46
+ telegram: z.object({
47
+ enabled: z.boolean().default(false),
48
+ token: z.string().optional(),
49
+ allowed_users: z.array(z.string()).default([])
50
+ }).optional(),
51
+ logging: z.object({
52
+ level: z.enum(["debug", "info", "warn", "error"]).default("info"),
53
+ dir: z.string().default("~/.cueclaw/logs")
54
+ }).optional(),
55
+ container: z.object({
56
+ enabled: z.boolean().default(false),
57
+ image: z.string().default("cueclaw-agent:latest"),
58
+ timeout: z.number().default(18e5),
59
+ max_output_size: z.number().default(10485760),
60
+ idle_timeout: z.number().default(18e5),
61
+ network: z.enum(["none", "host", "bridge"]).default("none")
62
+ }).optional()
63
+ });
64
+ function cueclawHome() {
65
+ return join(homedir(), ".cueclaw");
66
+ }
67
+ function ensureCueclawHome() {
68
+ const home = cueclawHome();
69
+ const dirs = [home, join(home, "db"), join(home, "logs"), join(home, "auth")];
70
+ for (const dir of dirs) {
71
+ mkdirSync(dir, { recursive: true });
72
+ }
73
+ }
74
+ function interpolateEnvVars(value) {
75
+ return value.replace(/\$\{(\w+)\}/g, (_match, name) => {
76
+ return process.env[name] ?? "";
77
+ });
78
+ }
79
+ function interpolateObject(obj) {
80
+ if (typeof obj === "string") return interpolateEnvVars(obj);
81
+ if (Array.isArray(obj)) return obj.map(interpolateObject);
82
+ if (obj !== null && typeof obj === "object") {
83
+ const result = {};
84
+ for (const [key, value] of Object.entries(obj)) {
85
+ result[key] = interpolateObject(value);
86
+ }
87
+ return result;
88
+ }
89
+ return obj;
90
+ }
91
+ function loadYamlFile(path) {
92
+ if (!existsSync(path)) return null;
93
+ const content = readFileSync(path, "utf-8");
94
+ const parsed = parseYaml(content);
95
+ return parsed && typeof parsed === "object" ? parsed : null;
96
+ }
97
+ function deepMerge(target, source) {
98
+ const result = { ...target };
99
+ for (const [key, value] of Object.entries(source)) {
100
+ if (value !== null && typeof value === "object" && !Array.isArray(value) && result[key] !== null && typeof result[key] === "object" && !Array.isArray(result[key])) {
101
+ result[key] = deepMerge(result[key], value);
102
+ } else {
103
+ result[key] = value;
104
+ }
105
+ }
106
+ return result;
107
+ }
108
+ function loadConfig() {
109
+ const globalPath = join(cueclawHome(), "config.yaml");
110
+ const localPath = join(process.cwd(), ".cueclaw", "config.yaml");
111
+ let merged = {};
112
+ const globalConfig = loadYamlFile(globalPath);
113
+ if (globalConfig) merged = deepMerge(merged, globalConfig);
114
+ const localConfig = loadYamlFile(localPath);
115
+ if (localConfig) merged = deepMerge(merged, localConfig);
116
+ if (process.env["ANTHROPIC_API_KEY"]) {
117
+ merged.claude = merged.claude ?? {};
118
+ merged.claude.api_key = process.env["ANTHROPIC_API_KEY"];
119
+ }
120
+ merged = interpolateObject(merged);
121
+ const result = ConfigSchema.safeParse(merged);
122
+ if (!result.success) {
123
+ const issues = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
124
+ throw new ConfigError(`Invalid configuration:
125
+ ${issues}`);
126
+ }
127
+ return result.data;
128
+ }
129
+ var DEFAULT_CONFIG = `# CueClaw Configuration
130
+ # See docs/config.md for all options
131
+
132
+ claude:
133
+ api_key: \${ANTHROPIC_API_KEY}
134
+ planner:
135
+ model: claude-sonnet-4-6
136
+ executor:
137
+ model: claude-sonnet-4-6
138
+
139
+ logging:
140
+ level: info
141
+ `;
142
+ function createDefaultConfig() {
143
+ const configPath = join(cueclawHome(), "config.yaml");
144
+ if (!existsSync(configPath)) {
145
+ ensureCueclawHome();
146
+ writeFileSync(configPath, DEFAULT_CONFIG, "utf-8");
147
+ }
148
+ }
149
+
150
+ export {
151
+ PlannerError,
152
+ ExecutorError,
153
+ ConfigError,
154
+ cueclawHome,
155
+ ensureCueclawHome,
156
+ loadConfig,
157
+ createDefaultConfig
158
+ };
@@ -0,0 +1,140 @@
1
+ import {
2
+ cueclawHome
3
+ } from "./chunk-JRHM3Z4C.js";
4
+
5
+ // src/db.ts
6
+ import Database from "better-sqlite3";
7
+ import { join } from "path";
8
+ import { mkdirSync } from "fs";
9
+ var SCHEMA = `
10
+ CREATE TABLE IF NOT EXISTS workflows (
11
+ id TEXT PRIMARY KEY,
12
+ name TEXT NOT NULL,
13
+ description TEXT NOT NULL,
14
+ trigger_json TEXT NOT NULL,
15
+ steps_json TEXT NOT NULL,
16
+ failure_policy_json TEXT NOT NULL,
17
+ phase TEXT NOT NULL DEFAULT 'planning',
18
+ schema_version TEXT NOT NULL DEFAULT '1.0',
19
+ metadata_json TEXT,
20
+ created_at TEXT NOT NULL,
21
+ updated_at TEXT NOT NULL
22
+ );
23
+
24
+ CREATE TABLE IF NOT EXISTS workflow_runs (
25
+ id TEXT PRIMARY KEY,
26
+ workflow_id TEXT NOT NULL REFERENCES workflows(id),
27
+ trigger_data TEXT,
28
+ status TEXT NOT NULL DEFAULT 'running',
29
+ started_at TEXT NOT NULL,
30
+ completed_at TEXT,
31
+ error TEXT,
32
+ duration_ms INTEGER
33
+ );
34
+
35
+ CREATE TABLE IF NOT EXISTS step_runs (
36
+ id TEXT PRIMARY KEY,
37
+ run_id TEXT NOT NULL REFERENCES workflow_runs(id),
38
+ step_id TEXT NOT NULL,
39
+ status TEXT NOT NULL DEFAULT 'pending',
40
+ output_json TEXT,
41
+ error TEXT,
42
+ started_at TEXT,
43
+ completed_at TEXT,
44
+ duration_ms INTEGER
45
+ );
46
+
47
+ CREATE TABLE IF NOT EXISTS sessions (
48
+ id TEXT PRIMARY KEY,
49
+ step_run_id TEXT NOT NULL REFERENCES step_runs(id),
50
+ sdk_session_id TEXT,
51
+ created_at TEXT NOT NULL,
52
+ last_used_at TEXT NOT NULL,
53
+ is_active INTEGER NOT NULL DEFAULT 1
54
+ );
55
+
56
+ CREATE TABLE IF NOT EXISTS trigger_state (
57
+ workflow_id TEXT PRIMARY KEY REFERENCES workflows(id),
58
+ last_result TEXT,
59
+ last_check_at TEXT,
60
+ last_fire_at TEXT,
61
+ last_error TEXT
62
+ );
63
+
64
+ CREATE INDEX IF NOT EXISTS idx_workflows_phase ON workflows(phase);
65
+ CREATE INDEX IF NOT EXISTS idx_workflow_runs_status ON workflow_runs(status);
66
+ CREATE INDEX IF NOT EXISTS idx_workflow_runs_workflow_id ON workflow_runs(workflow_id);
67
+ CREATE INDEX IF NOT EXISTS idx_step_runs_run_id ON step_runs(run_id);
68
+ CREATE INDEX IF NOT EXISTS idx_step_runs_step_run ON step_runs(step_id, run_id);
69
+ CREATE INDEX IF NOT EXISTS idx_sessions_step_run_id ON sessions(step_run_id);
70
+ `;
71
+ function initDb(dbPath) {
72
+ const path = dbPath ?? join(cueclawHome(), "db", "cueclaw.db");
73
+ mkdirSync(join(path, ".."), { recursive: true });
74
+ const db = new Database(path);
75
+ db.pragma("journal_mode = WAL");
76
+ db.pragma("foreign_keys = ON");
77
+ runMigrations(db);
78
+ return db;
79
+ }
80
+ function runMigrations(db) {
81
+ db.exec(SCHEMA);
82
+ }
83
+ function rowToWorkflow(row) {
84
+ return {
85
+ id: row.id,
86
+ name: row.name,
87
+ description: row.description,
88
+ trigger: JSON.parse(row.trigger_json),
89
+ steps: JSON.parse(row.steps_json),
90
+ failure_policy: JSON.parse(row.failure_policy_json),
91
+ phase: row.phase,
92
+ schema_version: row.schema_version,
93
+ metadata: row.metadata_json ? JSON.parse(row.metadata_json) : void 0,
94
+ created_at: row.created_at,
95
+ updated_at: row.updated_at
96
+ };
97
+ }
98
+ function getWorkflow(db, id) {
99
+ const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get(id);
100
+ return row ? rowToWorkflow(row) : void 0;
101
+ }
102
+ function listWorkflows(db, phase) {
103
+ const query = phase ? db.prepare("SELECT * FROM workflows WHERE phase = ? ORDER BY updated_at DESC") : db.prepare("SELECT * FROM workflows ORDER BY updated_at DESC");
104
+ const rows = phase ? query.all(phase) : query.all();
105
+ return rows.map(rowToWorkflow);
106
+ }
107
+ function updateWorkflowPhase(db, id, phase) {
108
+ db.prepare("UPDATE workflows SET phase = ?, updated_at = ? WHERE id = ?").run(phase, (/* @__PURE__ */ new Date()).toISOString(), id);
109
+ }
110
+ function insertWorkflowRun(db, run) {
111
+ db.prepare(`
112
+ INSERT INTO workflow_runs (id, workflow_id, trigger_data, status, started_at, completed_at, error, duration_ms)
113
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
114
+ `).run(run.id, run.workflow_id, run.trigger_data, run.status, run.started_at, run.completed_at ?? null, run.error ?? null, run.duration_ms ?? null);
115
+ }
116
+ function updateWorkflowRunStatus(db, id, status, error) {
117
+ const completedAt = status !== "running" ? (/* @__PURE__ */ new Date()).toISOString() : null;
118
+ db.prepare("UPDATE workflow_runs SET status = ?, completed_at = ?, error = ? WHERE id = ?").run(status, completedAt, error ?? null, id);
119
+ }
120
+ function insertStepRun(db, stepRun) {
121
+ db.prepare(`
122
+ INSERT INTO step_runs (id, run_id, step_id, status, output_json, error, started_at, completed_at, duration_ms)
123
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
124
+ `).run(stepRun.id, stepRun.run_id, stepRun.step_id, stepRun.status, stepRun.output_json ?? null, stepRun.error ?? null, stepRun.started_at ?? null, stepRun.completed_at ?? null, stepRun.duration_ms ?? null);
125
+ }
126
+ function updateStepRunStatus(db, id, status, output, error) {
127
+ const completedAt = status === "succeeded" || status === "failed" || status === "skipped" ? (/* @__PURE__ */ new Date()).toISOString() : null;
128
+ db.prepare("UPDATE step_runs SET status = ?, output_json = ?, error = ?, completed_at = ? WHERE id = ?").run(status, output ?? null, error ?? null, completedAt, id);
129
+ }
130
+
131
+ export {
132
+ initDb,
133
+ getWorkflow,
134
+ listWorkflows,
135
+ updateWorkflowPhase,
136
+ insertWorkflowRun,
137
+ updateWorkflowRunStatus,
138
+ insertStepRun,
139
+ updateStepRunStatus
140
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node