@hasna/loops 0.3.22 → 0.3.23
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 +50 -21
- package/dist/daemon/index.js +1 -1
- package/docs/USAGE.md +6 -4
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -5143,7 +5143,7 @@ function buildScriptInventoryReport(store, opts = {}) {
|
|
|
5143
5143
|
// package.json
|
|
5144
5144
|
var package_default = {
|
|
5145
5145
|
name: "@hasna/loops",
|
|
5146
|
-
version: "0.3.
|
|
5146
|
+
version: "0.3.23",
|
|
5147
5147
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
5148
5148
|
type: "module",
|
|
5149
5149
|
main: "dist/index.js",
|
|
@@ -5662,6 +5662,20 @@ function preflightStoredWorkflow(workflow, context, opts) {
|
|
|
5662
5662
|
preflightFailed(error, context);
|
|
5663
5663
|
}
|
|
5664
5664
|
}
|
|
5665
|
+
function workflowSpecForPreflight(body, id = "validation") {
|
|
5666
|
+
const now = new Date().toISOString();
|
|
5667
|
+
return {
|
|
5668
|
+
id,
|
|
5669
|
+
name: body.name,
|
|
5670
|
+
description: body.description,
|
|
5671
|
+
version: body.version ?? 1,
|
|
5672
|
+
status: "active",
|
|
5673
|
+
goal: body.goal,
|
|
5674
|
+
steps: body.steps,
|
|
5675
|
+
createdAt: now,
|
|
5676
|
+
updatedAt: now
|
|
5677
|
+
};
|
|
5678
|
+
}
|
|
5665
5679
|
function printTextOutput(value) {
|
|
5666
5680
|
for (const line of textOutputBlocks(value, { indent: " " }))
|
|
5667
5681
|
console.log(line);
|
|
@@ -6211,7 +6225,7 @@ templates.command("create-workflow <id>").description("render and store a templa
|
|
|
6211
6225
|
}
|
|
6212
6226
|
});
|
|
6213
6227
|
var eventsHandle = events.command("handle").description("handle a Hasna event envelope");
|
|
6214
|
-
eventsHandle.command("todos-task").description("create a one-shot worker/verifier workflow loop for a todos task event").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--project-path <path>", "fallback project/repo working directory").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--dry-run", "print the workflow and loop input without storing anything").action(async (opts) => {
|
|
6228
|
+
eventsHandle.command("todos-task").description("create a one-shot worker/verifier workflow loop for a todos task event").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--project-path <path>", "fallback project/repo working directory").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing the workflow loop").option("--dry-run", "print the workflow and loop input without storing anything").action(async (opts) => {
|
|
6215
6229
|
const event = await readEventEnvelopeFromStdin();
|
|
6216
6230
|
const data = eventData(event);
|
|
6217
6231
|
const metadata = eventMetadata(event);
|
|
@@ -6280,7 +6294,12 @@ eventsHandle.command("todos-task").description("create a one-shot worker/verifie
|
|
|
6280
6294
|
leaseMs: 90 * 60000
|
|
6281
6295
|
};
|
|
6282
6296
|
if (opts.dryRun) {
|
|
6283
|
-
|
|
6297
|
+
const preflight = opts.preflight ? preflightStoredWorkflow(workflowSpecForPreflight(workflowBody, "event-preflight"), {
|
|
6298
|
+
name: workflowBody.name,
|
|
6299
|
+
type: "todos-task-event-workflow",
|
|
6300
|
+
event: event.id
|
|
6301
|
+
}, {}) : undefined;
|
|
6302
|
+
print({ deduped: false, idempotencyKey, event, workflow: workflowBody, loop: loopInput, preflight }, `dry-run ${loopName}`);
|
|
6284
6303
|
return;
|
|
6285
6304
|
}
|
|
6286
6305
|
const store = new Store;
|
|
@@ -6299,17 +6318,23 @@ eventsHandle.command("todos-task").description("create a one-shot worker/verifie
|
|
|
6299
6318
|
return;
|
|
6300
6319
|
}
|
|
6301
6320
|
const existingWorkflow = store.findWorkflowByName(workflowBody.name);
|
|
6321
|
+
const workflowPreflightSpec = existingWorkflow ?? workflowSpecForPreflight(workflowBody, "event-preflight");
|
|
6322
|
+
const preflight = opts.preflight ? preflightStoredWorkflow(workflowPreflightSpec, {
|
|
6323
|
+
name: workflowBody.name,
|
|
6324
|
+
type: "todos-task-event-workflow",
|
|
6325
|
+
event: event.id
|
|
6326
|
+
}, {}) : undefined;
|
|
6302
6327
|
const workflow = existingWorkflow ?? store.createWorkflow(workflowBody);
|
|
6303
6328
|
const loop = store.createLoop({
|
|
6304
6329
|
...loopInput,
|
|
6305
6330
|
target: { type: "workflow", workflowId: workflow.id }
|
|
6306
6331
|
});
|
|
6307
|
-
print({ deduped: false, idempotencyKey, event, workflow: publicWorkflow(workflow), loop: publicLoop(loop) }, `created ${loop.id} (${loop.name}) workflow=${workflow.name} event=${event.id} idempotency=${idempotencyKey}`);
|
|
6332
|
+
print({ deduped: false, idempotencyKey, event, workflow: publicWorkflow(workflow), loop: publicLoop(loop), preflight }, `created ${loop.id} (${loop.name}) workflow=${workflow.name} event=${event.id} idempotency=${idempotencyKey}`);
|
|
6308
6333
|
} finally {
|
|
6309
6334
|
store.close();
|
|
6310
6335
|
}
|
|
6311
6336
|
});
|
|
6312
|
-
eventsHandle.command("generic").description("create a one-shot worker/verifier workflow loop for any Hasna event").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--project-path <path>", "fallback project/repo working directory").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:generic").option("--dry-run", "print the workflow and loop input without storing anything").action(async (opts) => {
|
|
6337
|
+
eventsHandle.command("generic").description("create a one-shot worker/verifier workflow loop for any Hasna event").option("--provider <provider>", "agent provider", "codewith").option("--auth-profile <profile>", "provider-native auth profile; currently supported for codewith").option("--auth-profile-pool <profiles>", "comma-separated provider-native auth profile pool").option("--worker-auth-profile <profile>", "provider-native auth profile for worker step").option("--verifier-auth-profile <profile>", "provider-native auth profile for verifier step").option("--account <profile>", "OpenAccounts profile name").option("--account-pool <profiles>", "comma-separated OpenAccounts profile pool").option("--worker-account <profile>", "OpenAccounts profile for worker step").option("--verifier-account <profile>", "OpenAccounts profile for verifier step").option("--account-tool <tool>", "OpenAccounts tool id").option("--model <model>", "provider model").option("--variant <variant>", "provider-specific model variant or reasoning effort").option("--agent <agent>", "provider-specific agent").option("--permission-mode <mode>", "provider permission mode: default, plan, auto, or bypass", "bypass").option("--sandbox <mode>", "provider sandbox").option("--project-path <path>", "fallback project/repo working directory").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:generic").option("--preflight", "check generated workflow steps before storing the workflow loop").option("--dry-run", "print the workflow and loop input without storing anything").action(async (opts) => {
|
|
6313
6338
|
const event = await readEventEnvelopeFromStdin();
|
|
6314
6339
|
const data = eventData(event);
|
|
6315
6340
|
const projectPath = opts.projectPath ?? taskEventField(data, ["working_dir", "workingDir", "project_path", "projectPath", "cwd", "repo_path", "repoPath"]) ?? process.cwd();
|
|
@@ -6359,7 +6384,12 @@ eventsHandle.command("generic").description("create a one-shot worker/verifier w
|
|
|
6359
6384
|
leaseMs: 90 * 60000
|
|
6360
6385
|
};
|
|
6361
6386
|
if (opts.dryRun) {
|
|
6362
|
-
|
|
6387
|
+
const preflight = opts.preflight ? preflightStoredWorkflow(workflowSpecForPreflight(workflowBody, "event-preflight"), {
|
|
6388
|
+
name: workflowBody.name,
|
|
6389
|
+
type: "generic-event-workflow",
|
|
6390
|
+
event: event.id
|
|
6391
|
+
}, {}) : undefined;
|
|
6392
|
+
print({ event, workflow: workflowBody, loop: loopInput, preflight }, `dry-run ${loopName}`);
|
|
6363
6393
|
return;
|
|
6364
6394
|
}
|
|
6365
6395
|
const store = new Store;
|
|
@@ -6371,12 +6401,18 @@ eventsHandle.command("generic").description("create a one-shot worker/verifier w
|
|
|
6371
6401
|
return;
|
|
6372
6402
|
}
|
|
6373
6403
|
const existingWorkflow = store.findWorkflowByName(workflowBody.name);
|
|
6404
|
+
const workflowPreflightSpec = existingWorkflow ?? workflowSpecForPreflight(workflowBody, "event-preflight");
|
|
6405
|
+
const preflight = opts.preflight ? preflightStoredWorkflow(workflowPreflightSpec, {
|
|
6406
|
+
name: workflowBody.name,
|
|
6407
|
+
type: "generic-event-workflow",
|
|
6408
|
+
event: event.id
|
|
6409
|
+
}, {}) : undefined;
|
|
6374
6410
|
const workflow = existingWorkflow ?? store.createWorkflow(workflowBody);
|
|
6375
6411
|
const loop = store.createLoop({
|
|
6376
6412
|
...loopInput,
|
|
6377
6413
|
target: { type: "workflow", workflowId: workflow.id }
|
|
6378
6414
|
});
|
|
6379
|
-
print({ deduped: false, event, workflow: publicWorkflow(workflow), loop: publicLoop(loop) }, `created ${loop.id} (${loop.name}) workflow=${workflow.name}`);
|
|
6415
|
+
print({ deduped: false, event, workflow: publicWorkflow(workflow), loop: publicLoop(loop), preflight }, `created ${loop.id} (${loop.name}) workflow=${workflow.name}`);
|
|
6380
6416
|
} finally {
|
|
6381
6417
|
store.close();
|
|
6382
6418
|
}
|
|
@@ -6441,27 +6477,20 @@ machines.command("show <id>").description("resolve a machine assignment").action
|
|
|
6441
6477
|
});
|
|
6442
6478
|
workflows.command("validate <file>").description("validate a workflow JSON file without storing or running it").option("--name <name>", "override workflow name from the file").option("--preflight", "also check account env and target executables").action((file, opts) => {
|
|
6443
6479
|
const body = workflowBodyFromJson(JSON.parse(readFileSync2(file, "utf8")), opts.name);
|
|
6444
|
-
const
|
|
6445
|
-
const workflow = {
|
|
6446
|
-
id: "validation",
|
|
6447
|
-
name: body.name,
|
|
6448
|
-
description: body.description,
|
|
6449
|
-
version: body.version ?? 1,
|
|
6450
|
-
status: "active",
|
|
6451
|
-
goal: body.goal,
|
|
6452
|
-
steps: body.steps,
|
|
6453
|
-
createdAt: now,
|
|
6454
|
-
updatedAt: now
|
|
6455
|
-
};
|
|
6480
|
+
const workflow = workflowSpecForPreflight(body);
|
|
6456
6481
|
const preflight = opts.preflight ? preflightWorkflow(workflow) : undefined;
|
|
6457
6482
|
print({ valid: true, workflow: publicWorkflow(workflow), preflight }, `valid workflow ${workflow.name} steps=${workflow.steps.length}`);
|
|
6458
6483
|
});
|
|
6459
|
-
workflows.command("create <file>").description("validate and store a workflow JSON file").option("--name <name>", "override workflow name from the file").action((file, opts) => {
|
|
6484
|
+
workflows.command("create <file>").description("validate and store a workflow JSON file").option("--name <name>", "override workflow name from the file").option("--preflight", "also check account env and target executables before storing").action((file, opts) => {
|
|
6460
6485
|
const store = new Store;
|
|
6461
6486
|
try {
|
|
6462
6487
|
const body = workflowBodyFromJson(JSON.parse(readFileSync2(file, "utf8")), opts.name);
|
|
6488
|
+
const preflight = opts.preflight ? preflightStoredWorkflow(workflowSpecForPreflight(body, "creation-preflight"), { name: body.name, type: "workflow" }, {}) : undefined;
|
|
6463
6489
|
const workflow = store.createWorkflow(body);
|
|
6464
|
-
|
|
6490
|
+
if (preflight !== undefined)
|
|
6491
|
+
print({ workflow: publicWorkflow(workflow), preflight }, `created workflow ${workflow.id} (${workflow.name}) steps=${workflow.steps.length}`);
|
|
6492
|
+
else
|
|
6493
|
+
print(publicWorkflow(workflow), `created workflow ${workflow.id} (${workflow.name}) steps=${workflow.steps.length}`);
|
|
6465
6494
|
} finally {
|
|
6466
6495
|
store.close();
|
|
6467
6496
|
}
|
package/dist/daemon/index.js
CHANGED
|
@@ -4470,7 +4470,7 @@ function enableStartup(result) {
|
|
|
4470
4470
|
// package.json
|
|
4471
4471
|
var package_default = {
|
|
4472
4472
|
name: "@hasna/loops",
|
|
4473
|
-
version: "0.3.
|
|
4473
|
+
version: "0.3.23",
|
|
4474
4474
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
4475
4475
|
type: "module",
|
|
4476
4476
|
main: "dist/index.js",
|
package/docs/USAGE.md
CHANGED
|
@@ -59,10 +59,12 @@ loops create command repo-status \
|
|
|
59
59
|
--preflight
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
`--preflight` is available on `loops create command`, `loops create agent`,
|
|
63
|
-
`loops create workflow
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
`--preflight` is available on `loops create command`, `loops create agent`,
|
|
63
|
+
`loops create workflow`, `loops workflows create`, and event-router commands such
|
|
64
|
+
as `loops events handle todos-task` and `loops events handle generic`. It checks
|
|
65
|
+
target executables and configured account profiles before loop or workflow rows
|
|
66
|
+
are stored, so a missing command, provider binary, OpenAccounts profile, native
|
|
67
|
+
Codewith auth profile, or workflow step dependency fails without creating a
|
|
66
68
|
scheduled loop. Use `--json` with `--preflight` to capture stable machine-readable
|
|
67
69
|
preflight evidence.
|
|
68
70
|
|
package/package.json
CHANGED