@hasna/loops 0.3.10 → 0.3.12
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/cli/index.js +15 -23
- package/dist/daemon/index.js +15 -23
- package/dist/index.js +14 -22
- package/dist/lib/store.d.ts +8 -0
- package/dist/lib/store.js +13 -21
- package/dist/sdk/index.js +14 -22
- package/docs/USAGE.md +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -860,31 +860,23 @@ class Store {
|
|
|
860
860
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_loop_run ON goal_runs(loop_run_id);
|
|
861
861
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_workflow_run ON goal_runs(workflow_run_id);
|
|
862
862
|
`);
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
this.db.query("ALTER TABLE loop_runs ADD COLUMN goal_run_id TEXT").run();
|
|
871
|
-
} catch {}
|
|
872
|
-
try {
|
|
873
|
-
this.db.query("ALTER TABLE workflow_specs ADD COLUMN goal_json TEXT").run();
|
|
874
|
-
} catch {}
|
|
875
|
-
try {
|
|
876
|
-
this.db.query("ALTER TABLE workflow_runs ADD COLUMN goal_run_id TEXT").run();
|
|
877
|
-
} catch {}
|
|
878
|
-
try {
|
|
879
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN pid INTEGER").run();
|
|
880
|
-
} catch {}
|
|
881
|
-
try {
|
|
882
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN goal_run_id TEXT").run();
|
|
883
|
-
} catch {}
|
|
863
|
+
this.addColumnIfMissing("loops", "machine_json", "TEXT");
|
|
864
|
+
this.addColumnIfMissing("loops", "goal_json", "TEXT");
|
|
865
|
+
this.addColumnIfMissing("loop_runs", "goal_run_id", "TEXT");
|
|
866
|
+
this.addColumnIfMissing("workflow_specs", "goal_json", "TEXT");
|
|
867
|
+
this.addColumnIfMissing("workflow_runs", "goal_run_id", "TEXT");
|
|
868
|
+
this.addColumnIfMissing("workflow_step_runs", "pid", "INTEGER");
|
|
869
|
+
this.addColumnIfMissing("workflow_step_runs", "goal_run_id", "TEXT");
|
|
884
870
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0001_initial_and_workflows", nowIso());
|
|
885
871
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0002_loop_machines", nowIso());
|
|
886
872
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0003_goals", nowIso());
|
|
887
873
|
}
|
|
874
|
+
addColumnIfMissing(table, column, definition) {
|
|
875
|
+
const columns = this.db.query(`PRAGMA table_info(${table})`).all();
|
|
876
|
+
if (columns.some((c) => c.name === column))
|
|
877
|
+
return;
|
|
878
|
+
this.db.query(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).run();
|
|
879
|
+
}
|
|
888
880
|
assertDaemonLeaseFence(opts = {}, now = nowIso()) {
|
|
889
881
|
if (!opts.daemonLeaseId)
|
|
890
882
|
return;
|
|
@@ -2502,7 +2494,7 @@ function agentArgs(target) {
|
|
|
2502
2494
|
args.push(...target.extraArgs ?? []);
|
|
2503
2495
|
return args;
|
|
2504
2496
|
case "cursor":
|
|
2505
|
-
args.push("-p");
|
|
2497
|
+
args.push("agent", "-p");
|
|
2506
2498
|
if (target.model)
|
|
2507
2499
|
args.push("--model", target.model);
|
|
2508
2500
|
if (target.agent)
|
|
@@ -4338,7 +4330,7 @@ function runDoctor(store) {
|
|
|
4338
4330
|
// package.json
|
|
4339
4331
|
var package_default = {
|
|
4340
4332
|
name: "@hasna/loops",
|
|
4341
|
-
version: "0.3.
|
|
4333
|
+
version: "0.3.12",
|
|
4342
4334
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
4343
4335
|
type: "module",
|
|
4344
4336
|
main: "dist/index.js",
|
package/dist/daemon/index.js
CHANGED
|
@@ -860,31 +860,23 @@ class Store {
|
|
|
860
860
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_loop_run ON goal_runs(loop_run_id);
|
|
861
861
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_workflow_run ON goal_runs(workflow_run_id);
|
|
862
862
|
`);
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
this.db.query("ALTER TABLE loop_runs ADD COLUMN goal_run_id TEXT").run();
|
|
871
|
-
} catch {}
|
|
872
|
-
try {
|
|
873
|
-
this.db.query("ALTER TABLE workflow_specs ADD COLUMN goal_json TEXT").run();
|
|
874
|
-
} catch {}
|
|
875
|
-
try {
|
|
876
|
-
this.db.query("ALTER TABLE workflow_runs ADD COLUMN goal_run_id TEXT").run();
|
|
877
|
-
} catch {}
|
|
878
|
-
try {
|
|
879
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN pid INTEGER").run();
|
|
880
|
-
} catch {}
|
|
881
|
-
try {
|
|
882
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN goal_run_id TEXT").run();
|
|
883
|
-
} catch {}
|
|
863
|
+
this.addColumnIfMissing("loops", "machine_json", "TEXT");
|
|
864
|
+
this.addColumnIfMissing("loops", "goal_json", "TEXT");
|
|
865
|
+
this.addColumnIfMissing("loop_runs", "goal_run_id", "TEXT");
|
|
866
|
+
this.addColumnIfMissing("workflow_specs", "goal_json", "TEXT");
|
|
867
|
+
this.addColumnIfMissing("workflow_runs", "goal_run_id", "TEXT");
|
|
868
|
+
this.addColumnIfMissing("workflow_step_runs", "pid", "INTEGER");
|
|
869
|
+
this.addColumnIfMissing("workflow_step_runs", "goal_run_id", "TEXT");
|
|
884
870
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0001_initial_and_workflows", nowIso());
|
|
885
871
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0002_loop_machines", nowIso());
|
|
886
872
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0003_goals", nowIso());
|
|
887
873
|
}
|
|
874
|
+
addColumnIfMissing(table, column, definition) {
|
|
875
|
+
const columns = this.db.query(`PRAGMA table_info(${table})`).all();
|
|
876
|
+
if (columns.some((c) => c.name === column))
|
|
877
|
+
return;
|
|
878
|
+
this.db.query(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).run();
|
|
879
|
+
}
|
|
888
880
|
assertDaemonLeaseFence(opts = {}, now = nowIso()) {
|
|
889
881
|
if (!opts.daemonLeaseId)
|
|
890
882
|
return;
|
|
@@ -2397,7 +2389,7 @@ function agentArgs(target) {
|
|
|
2397
2389
|
args.push(...target.extraArgs ?? []);
|
|
2398
2390
|
return args;
|
|
2399
2391
|
case "cursor":
|
|
2400
|
-
args.push("-p");
|
|
2392
|
+
args.push("agent", "-p");
|
|
2401
2393
|
if (target.model)
|
|
2402
2394
|
args.push("--model", target.model);
|
|
2403
2395
|
if (target.agent)
|
|
@@ -4138,7 +4130,7 @@ function enableStartup(result) {
|
|
|
4138
4130
|
// package.json
|
|
4139
4131
|
var package_default = {
|
|
4140
4132
|
name: "@hasna/loops",
|
|
4141
|
-
version: "0.3.
|
|
4133
|
+
version: "0.3.12",
|
|
4142
4134
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
4143
4135
|
type: "module",
|
|
4144
4136
|
main: "dist/index.js",
|
package/dist/index.js
CHANGED
|
@@ -858,31 +858,23 @@ class Store {
|
|
|
858
858
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_loop_run ON goal_runs(loop_run_id);
|
|
859
859
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_workflow_run ON goal_runs(workflow_run_id);
|
|
860
860
|
`);
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
this.db.query("ALTER TABLE loop_runs ADD COLUMN goal_run_id TEXT").run();
|
|
869
|
-
} catch {}
|
|
870
|
-
try {
|
|
871
|
-
this.db.query("ALTER TABLE workflow_specs ADD COLUMN goal_json TEXT").run();
|
|
872
|
-
} catch {}
|
|
873
|
-
try {
|
|
874
|
-
this.db.query("ALTER TABLE workflow_runs ADD COLUMN goal_run_id TEXT").run();
|
|
875
|
-
} catch {}
|
|
876
|
-
try {
|
|
877
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN pid INTEGER").run();
|
|
878
|
-
} catch {}
|
|
879
|
-
try {
|
|
880
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN goal_run_id TEXT").run();
|
|
881
|
-
} catch {}
|
|
861
|
+
this.addColumnIfMissing("loops", "machine_json", "TEXT");
|
|
862
|
+
this.addColumnIfMissing("loops", "goal_json", "TEXT");
|
|
863
|
+
this.addColumnIfMissing("loop_runs", "goal_run_id", "TEXT");
|
|
864
|
+
this.addColumnIfMissing("workflow_specs", "goal_json", "TEXT");
|
|
865
|
+
this.addColumnIfMissing("workflow_runs", "goal_run_id", "TEXT");
|
|
866
|
+
this.addColumnIfMissing("workflow_step_runs", "pid", "INTEGER");
|
|
867
|
+
this.addColumnIfMissing("workflow_step_runs", "goal_run_id", "TEXT");
|
|
882
868
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0001_initial_and_workflows", nowIso());
|
|
883
869
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0002_loop_machines", nowIso());
|
|
884
870
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0003_goals", nowIso());
|
|
885
871
|
}
|
|
872
|
+
addColumnIfMissing(table, column, definition) {
|
|
873
|
+
const columns = this.db.query(`PRAGMA table_info(${table})`).all();
|
|
874
|
+
if (columns.some((c) => c.name === column))
|
|
875
|
+
return;
|
|
876
|
+
this.db.query(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).run();
|
|
877
|
+
}
|
|
886
878
|
assertDaemonLeaseFence(opts = {}, now = nowIso()) {
|
|
887
879
|
if (!opts.daemonLeaseId)
|
|
888
880
|
return;
|
|
@@ -2387,7 +2379,7 @@ function agentArgs(target) {
|
|
|
2387
2379
|
args.push(...target.extraArgs ?? []);
|
|
2388
2380
|
return args;
|
|
2389
2381
|
case "cursor":
|
|
2390
|
-
args.push("-p");
|
|
2382
|
+
args.push("agent", "-p");
|
|
2391
2383
|
if (target.model)
|
|
2392
2384
|
args.push("--model", target.model);
|
|
2393
2385
|
if (target.agent)
|
package/dist/lib/store.d.ts
CHANGED
|
@@ -58,6 +58,14 @@ export declare class Store {
|
|
|
58
58
|
private db;
|
|
59
59
|
constructor(path?: string);
|
|
60
60
|
private migrate;
|
|
61
|
+
/**
|
|
62
|
+
* Add a column only if it does not already exist. Idempotent — avoids the
|
|
63
|
+
* "duplicate column name" error that SQLite logs (via libsqlite3, before any
|
|
64
|
+
* JS try/catch) when re-running an additive migration on a database that has
|
|
65
|
+
* already been upgraded. Table/column/definition come from hardcoded literals
|
|
66
|
+
* in {@link migrate}, never user input, so interpolation here is safe.
|
|
67
|
+
*/
|
|
68
|
+
private addColumnIfMissing;
|
|
61
69
|
private assertDaemonLeaseFence;
|
|
62
70
|
createLoop(input: CreateLoopInput, from?: Date): Loop;
|
|
63
71
|
getLoop(id: string): Loop | undefined;
|
package/dist/lib/store.js
CHANGED
|
@@ -858,31 +858,23 @@ class Store {
|
|
|
858
858
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_loop_run ON goal_runs(loop_run_id);
|
|
859
859
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_workflow_run ON goal_runs(workflow_run_id);
|
|
860
860
|
`);
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
this.db.query("ALTER TABLE loop_runs ADD COLUMN goal_run_id TEXT").run();
|
|
869
|
-
} catch {}
|
|
870
|
-
try {
|
|
871
|
-
this.db.query("ALTER TABLE workflow_specs ADD COLUMN goal_json TEXT").run();
|
|
872
|
-
} catch {}
|
|
873
|
-
try {
|
|
874
|
-
this.db.query("ALTER TABLE workflow_runs ADD COLUMN goal_run_id TEXT").run();
|
|
875
|
-
} catch {}
|
|
876
|
-
try {
|
|
877
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN pid INTEGER").run();
|
|
878
|
-
} catch {}
|
|
879
|
-
try {
|
|
880
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN goal_run_id TEXT").run();
|
|
881
|
-
} catch {}
|
|
861
|
+
this.addColumnIfMissing("loops", "machine_json", "TEXT");
|
|
862
|
+
this.addColumnIfMissing("loops", "goal_json", "TEXT");
|
|
863
|
+
this.addColumnIfMissing("loop_runs", "goal_run_id", "TEXT");
|
|
864
|
+
this.addColumnIfMissing("workflow_specs", "goal_json", "TEXT");
|
|
865
|
+
this.addColumnIfMissing("workflow_runs", "goal_run_id", "TEXT");
|
|
866
|
+
this.addColumnIfMissing("workflow_step_runs", "pid", "INTEGER");
|
|
867
|
+
this.addColumnIfMissing("workflow_step_runs", "goal_run_id", "TEXT");
|
|
882
868
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0001_initial_and_workflows", nowIso());
|
|
883
869
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0002_loop_machines", nowIso());
|
|
884
870
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0003_goals", nowIso());
|
|
885
871
|
}
|
|
872
|
+
addColumnIfMissing(table, column, definition) {
|
|
873
|
+
const columns = this.db.query(`PRAGMA table_info(${table})`).all();
|
|
874
|
+
if (columns.some((c) => c.name === column))
|
|
875
|
+
return;
|
|
876
|
+
this.db.query(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).run();
|
|
877
|
+
}
|
|
886
878
|
assertDaemonLeaseFence(opts = {}, now = nowIso()) {
|
|
887
879
|
if (!opts.daemonLeaseId)
|
|
888
880
|
return;
|
package/dist/sdk/index.js
CHANGED
|
@@ -858,31 +858,23 @@ class Store {
|
|
|
858
858
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_loop_run ON goal_runs(loop_run_id);
|
|
859
859
|
CREATE INDEX IF NOT EXISTS idx_goal_runs_workflow_run ON goal_runs(workflow_run_id);
|
|
860
860
|
`);
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
this.db.query("ALTER TABLE loop_runs ADD COLUMN goal_run_id TEXT").run();
|
|
869
|
-
} catch {}
|
|
870
|
-
try {
|
|
871
|
-
this.db.query("ALTER TABLE workflow_specs ADD COLUMN goal_json TEXT").run();
|
|
872
|
-
} catch {}
|
|
873
|
-
try {
|
|
874
|
-
this.db.query("ALTER TABLE workflow_runs ADD COLUMN goal_run_id TEXT").run();
|
|
875
|
-
} catch {}
|
|
876
|
-
try {
|
|
877
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN pid INTEGER").run();
|
|
878
|
-
} catch {}
|
|
879
|
-
try {
|
|
880
|
-
this.db.query("ALTER TABLE workflow_step_runs ADD COLUMN goal_run_id TEXT").run();
|
|
881
|
-
} catch {}
|
|
861
|
+
this.addColumnIfMissing("loops", "machine_json", "TEXT");
|
|
862
|
+
this.addColumnIfMissing("loops", "goal_json", "TEXT");
|
|
863
|
+
this.addColumnIfMissing("loop_runs", "goal_run_id", "TEXT");
|
|
864
|
+
this.addColumnIfMissing("workflow_specs", "goal_json", "TEXT");
|
|
865
|
+
this.addColumnIfMissing("workflow_runs", "goal_run_id", "TEXT");
|
|
866
|
+
this.addColumnIfMissing("workflow_step_runs", "pid", "INTEGER");
|
|
867
|
+
this.addColumnIfMissing("workflow_step_runs", "goal_run_id", "TEXT");
|
|
882
868
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0001_initial_and_workflows", nowIso());
|
|
883
869
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0002_loop_machines", nowIso());
|
|
884
870
|
this.db.query("INSERT OR IGNORE INTO schema_migrations (id, applied_at) VALUES (?, ?)").run("0003_goals", nowIso());
|
|
885
871
|
}
|
|
872
|
+
addColumnIfMissing(table, column, definition) {
|
|
873
|
+
const columns = this.db.query(`PRAGMA table_info(${table})`).all();
|
|
874
|
+
if (columns.some((c) => c.name === column))
|
|
875
|
+
return;
|
|
876
|
+
this.db.query(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).run();
|
|
877
|
+
}
|
|
886
878
|
assertDaemonLeaseFence(opts = {}, now = nowIso()) {
|
|
887
879
|
if (!opts.daemonLeaseId)
|
|
888
880
|
return;
|
|
@@ -2387,7 +2379,7 @@ function agentArgs(target) {
|
|
|
2387
2379
|
args.push(...target.extraArgs ?? []);
|
|
2388
2380
|
return args;
|
|
2389
2381
|
case "cursor":
|
|
2390
|
-
args.push("-p");
|
|
2382
|
+
args.push("agent", "-p");
|
|
2391
2383
|
if (target.model)
|
|
2392
2384
|
args.push("--model", target.model);
|
|
2393
2385
|
if (target.agent)
|
package/docs/USAGE.md
CHANGED
|
@@ -240,7 +240,7 @@ The adapters intentionally use provider command surfaces instead of pretending e
|
|
|
240
240
|
- Claude uses `claude -p --output-format json` and safe-mode/local setting sources by default.
|
|
241
241
|
- Codewith uses `codewith --ask-for-approval never exec --json --ephemeral --skip-git-repo-check`.
|
|
242
242
|
- AI Copilot and OpenCode use `run --format json --pure`.
|
|
243
|
-
- Cursor is CLI-first for now via `cursor-agent -p`; treat output as less stable until a stronger public SDK contract is selected.
|
|
243
|
+
- Cursor is CLI-first for now via `cursor-agent agent -p`; treat output as less stable until a stronger public SDK contract is selected.
|
|
244
244
|
- Codex uses `codex exec --json --ephemeral --ask-for-approval never`.
|
|
245
245
|
- Agent prompts are sent through child stdin instead of argv so prompt bodies do not appear in process listings.
|
|
246
246
|
- When `--account` or a step `account` is set, OpenLoops resolves `accounts env <profile> --tool <tool>` before spawning the target, strips inherited tool home/API-key variables, and applies the selected profile only to that process. Missing account profiles fail before the provider binary receives the prompt.
|
package/package.json
CHANGED