@hasna/loops 0.3.31 → 0.3.33
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 +80 -5
- package/dist/daemon/index.js +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2245,9 +2245,10 @@ class Store {
|
|
|
2245
2245
|
|
|
2246
2246
|
// src/cli/index.ts
|
|
2247
2247
|
import { createHash as createHash2, randomUUID } from "crypto";
|
|
2248
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync2, realpathSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2248
|
+
import { closeSync, existsSync as existsSync4, mkdirSync as mkdirSync5, mkdtempSync, openSync as openSync2, readFileSync as readFileSync2, realpathSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
2249
2249
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
2250
2250
|
import { join as join4, resolve as resolve2 } from "path";
|
|
2251
|
+
import { tmpdir } from "os";
|
|
2251
2252
|
import { Database as Database2 } from "bun:sqlite";
|
|
2252
2253
|
import { Command } from "commander";
|
|
2253
2254
|
|
|
@@ -5253,7 +5254,7 @@ function buildScriptInventoryReport(store, opts = {}) {
|
|
|
5253
5254
|
// package.json
|
|
5254
5255
|
var package_default = {
|
|
5255
5256
|
name: "@hasna/loops",
|
|
5256
|
-
version: "0.3.
|
|
5257
|
+
version: "0.3.33",
|
|
5257
5258
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
5258
5259
|
type: "module",
|
|
5259
5260
|
main: "dist/index.js",
|
|
@@ -6222,6 +6223,35 @@ function runLocalCommand(command, args, opts = {}) {
|
|
|
6222
6223
|
error: result.error ? String(result.error.message || result.error) : ""
|
|
6223
6224
|
};
|
|
6224
6225
|
}
|
|
6226
|
+
function runLocalCommandWithStdoutFile(command, args, opts = {}) {
|
|
6227
|
+
const tempDir = mkdtempSync(join4(tmpdir(), "loops-command-output-"));
|
|
6228
|
+
const stdoutPath = join4(tempDir, "stdout");
|
|
6229
|
+
const stdoutFd = openSync2(stdoutPath, "w");
|
|
6230
|
+
let result;
|
|
6231
|
+
try {
|
|
6232
|
+
result = spawnSync5(command, args, {
|
|
6233
|
+
input: opts.input,
|
|
6234
|
+
encoding: "utf8",
|
|
6235
|
+
timeout: opts.timeoutMs ?? 30000,
|
|
6236
|
+
maxBuffer: opts.maxBuffer ?? 8 * 1024 * 1024,
|
|
6237
|
+
env: process.env,
|
|
6238
|
+
stdio: ["pipe", stdoutFd, "pipe"]
|
|
6239
|
+
});
|
|
6240
|
+
} finally {
|
|
6241
|
+
closeSync(stdoutFd);
|
|
6242
|
+
}
|
|
6243
|
+
try {
|
|
6244
|
+
return {
|
|
6245
|
+
ok: result.status === 0,
|
|
6246
|
+
status: result.status,
|
|
6247
|
+
stdout: readFileSync2(stdoutPath, "utf8"),
|
|
6248
|
+
stderr: typeof result.stderr === "string" ? result.stderr : result.stderr?.toString() || "",
|
|
6249
|
+
error: result.error ? String(result.error.message || result.error) : ""
|
|
6250
|
+
};
|
|
6251
|
+
} finally {
|
|
6252
|
+
rmSync2(tempDir, { recursive: true, force: true });
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6225
6255
|
function ensureTodosTaskList(project, slug, name, description) {
|
|
6226
6256
|
runLocalCommand("todos", ["--project", project, "task-lists", "--add", name, "--slug", slug, "-d", description]);
|
|
6227
6257
|
const list = runLocalCommand("todos", ["--project", project, "--json", "task-lists"]);
|
|
@@ -6898,10 +6928,29 @@ function taskDrainEvent(task) {
|
|
|
6898
6928
|
}
|
|
6899
6929
|
};
|
|
6900
6930
|
}
|
|
6931
|
+
function compactDrainResult(result) {
|
|
6932
|
+
const value = result.value;
|
|
6933
|
+
const event = objectField(value.event);
|
|
6934
|
+
const loop = objectField(value.loop);
|
|
6935
|
+
const workflow = objectField(value.workflow);
|
|
6936
|
+
const throttle = objectField(value.throttle);
|
|
6937
|
+
return {
|
|
6938
|
+
kind: result.kind,
|
|
6939
|
+
taskId: event?.subject,
|
|
6940
|
+
eventId: event?.id,
|
|
6941
|
+
idempotencyKey: stringField(value.idempotencyKey),
|
|
6942
|
+
reason: stringField(value.reason) ?? throttle?.reason,
|
|
6943
|
+
loopId: stringField(loop?.id),
|
|
6944
|
+
loopName: stringField(loop?.name),
|
|
6945
|
+
workflowId: stringField(workflow?.id),
|
|
6946
|
+
workflowName: stringField(workflow?.name),
|
|
6947
|
+
queuedAtSource: value.queuedAtSource
|
|
6948
|
+
};
|
|
6949
|
+
}
|
|
6901
6950
|
function loadReadyTodosTasks(opts, scanLimit) {
|
|
6902
6951
|
const todosProject = opts.todosProject ?? defaultLoopsProject();
|
|
6903
6952
|
const args = ["--project", todosProject, "--json", "ready", "--limit", String(scanLimit)];
|
|
6904
|
-
const result =
|
|
6953
|
+
const result = runLocalCommandWithStdoutFile("todos", args, { timeoutMs: 60000, maxBuffer: 64 * 1024 * 1024 });
|
|
6905
6954
|
if (!result.ok)
|
|
6906
6955
|
throw new Error(result.stderr || result.error || "todos ready failed");
|
|
6907
6956
|
let parsed;
|
|
@@ -7105,7 +7154,7 @@ eventsHandle.command("todos-task").description("create a one-shot worker/verifie
|
|
|
7105
7154
|
print(result.value, result.human);
|
|
7106
7155
|
});
|
|
7107
7156
|
var eventsDrain = events.command("drain").description("drain durable source queues into bounded OpenLoops workflows");
|
|
7108
|
-
eventsDrain.command("todos-task").description("drain ready todos tasks into bounded worker/verifier workflow loops").option("--todos-project <path>", "todos storage project path", defaultLoopsProject()).option("--todos-project-id <id>", "filter todos ready output to one todos project id").option("--task-list <id-or-slug>", "filter ready tasks to one task-list id, slug, or name").option("--project-path-prefix <path>", "filter ready tasks to a project/repo path prefix").option("--tags <tags>", "require all comma-separated tags before routing").option("--tag <tags>", "alias for --tags").option("--limit <n>", "maximum filtered ready-task candidates to consider", "50").option("--scan-limit <n>", "maximum raw todos ready rows to fetch before filters; defaults to 500 when filters are used").option("--max-dispatch <n>", "maximum new workflow loops to create in this drain run", "1").option("--evidence-dir <path>", "write a JSON drain report to this directory").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("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated task worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing workflow loops").option("--dry-run", "preview selected tasks and generated workflow loops without storing anything").action((opts) => {
|
|
7157
|
+
eventsDrain.command("todos-task").description("drain ready todos tasks into bounded worker/verifier workflow loops").option("--todos-project <path>", "todos storage project path", defaultLoopsProject()).option("--todos-project-id <id>", "filter todos ready output to one todos project id").option("--task-list <id-or-slug>", "filter ready tasks to one task-list id, slug, or name").option("--project-path-prefix <path>", "filter ready tasks to a project/repo path prefix").option("--tags <tags>", "require all comma-separated tags before routing").option("--tag <tags>", "alias for --tags").option("--limit <n>", "maximum filtered ready-task candidates to consider", "50").option("--scan-limit <n>", "maximum raw todos ready rows to fetch before filters; defaults to 500 when filters are used").option("--max-dispatch <n>", "maximum new workflow loops to create in this drain run", "1").option("--evidence-dir <path>", "write a JSON drain report to this directory").option("--compact", "print compact JSON to stdout while preserving the full evidence file").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("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated task worktrees", "openloops").option("--name-prefix <prefix>", "workflow/loop name prefix", "event:todos-task").option("--preflight", "check generated workflow steps before storing workflow loops").option("--dry-run", "preview selected tasks and generated workflow loops without storing anything").action((opts) => {
|
|
7109
7158
|
const maxDispatch = positiveInteger(opts.maxDispatch ?? "1", "--max-dispatch") ?? 1;
|
|
7110
7159
|
const todosProject = opts.todosProject ?? defaultLoopsProject();
|
|
7111
7160
|
const requiredTags = splitList(opts.tags ?? opts.tag) ?? [];
|
|
@@ -7161,7 +7210,33 @@ eventsDrain.command("todos-task").description("drain ready todos tasks into boun
|
|
|
7161
7210
|
results: results.map((result) => ({ kind: result.kind, ...result.value }))
|
|
7162
7211
|
};
|
|
7163
7212
|
const evidencePath = writeRouteEvidence("todos-task-drain", report, opts.evidenceDir);
|
|
7164
|
-
|
|
7213
|
+
const output = opts.compact ? {
|
|
7214
|
+
drainedAt: report.drainedAt,
|
|
7215
|
+
todosProject: report.todosProject,
|
|
7216
|
+
todosProjectId: report.todosProjectId,
|
|
7217
|
+
taskList: report.taskList,
|
|
7218
|
+
taskListId: report.taskListId,
|
|
7219
|
+
projectPathPrefix: report.projectPathPrefix,
|
|
7220
|
+
tags: report.tags,
|
|
7221
|
+
limit: report.limit,
|
|
7222
|
+
scanLimit: report.scanLimit,
|
|
7223
|
+
filtersApplied: report.filtersApplied,
|
|
7224
|
+
scanned: report.scanned,
|
|
7225
|
+
candidates: report.candidates,
|
|
7226
|
+
filteredCandidates: report.filteredCandidates,
|
|
7227
|
+
scanExhausted: report.scanExhausted,
|
|
7228
|
+
considered: report.considered,
|
|
7229
|
+
created: report.created,
|
|
7230
|
+
deduped: report.deduped,
|
|
7231
|
+
throttled: report.throttled,
|
|
7232
|
+
skipped: report.skipped,
|
|
7233
|
+
maxDispatch: report.maxDispatch,
|
|
7234
|
+
source: report.source,
|
|
7235
|
+
dryRun: report.dryRun,
|
|
7236
|
+
evidencePath,
|
|
7237
|
+
results: results.map(compactDrainResult)
|
|
7238
|
+
} : { ...report, evidencePath };
|
|
7239
|
+
print(output, `drained todos ready queue: considered=${report.considered} created=${report.created} deduped=${report.deduped} throttled=${report.throttled} skipped=${report.skipped}`);
|
|
7165
7240
|
});
|
|
7166
7241
|
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("--project-group <name>", "optional project group for concurrency limits").option("--max-active <n>", "skip creating a workflow when this many active routed workflows already exist globally").option("--max-active-per-project <n>", "skip creating a workflow when this many active routed workflows already exist for the project").option("--max-active-per-project-group <n>", "skip creating a workflow when this many active routed workflows already exist for the project group").option("--worktree-mode <mode>", "worktree isolation mode: auto, required, off, or main", "auto").option("--worktree-root <path>", "base directory for OpenLoops-managed git worktrees").option("--worktree-branch-prefix <prefix>", "branch prefix for generated event worktrees", "openloops").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) => {
|
|
7167
7242
|
const event = await readEventEnvelopeFromStdin();
|
package/dist/daemon/index.js
CHANGED
|
@@ -4574,7 +4574,7 @@ function enableStartup(result) {
|
|
|
4574
4574
|
// package.json
|
|
4575
4575
|
var package_default = {
|
|
4576
4576
|
name: "@hasna/loops",
|
|
4577
|
-
version: "0.3.
|
|
4577
|
+
version: "0.3.33",
|
|
4578
4578
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
4579
4579
|
type: "module",
|
|
4580
4580
|
main: "dist/index.js",
|
package/package.json
CHANGED