@lchase/yak 0.3.0 → 0.4.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/index.js +46 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -768,6 +768,10 @@ async function readJournal(runDir) {
|
|
|
768
768
|
const contents = await readFile4(journalPath(runDir), "utf8").catch(() => "");
|
|
769
769
|
return contents.split("\n").filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
|
|
770
770
|
}
|
|
771
|
+
function runTagFromJournal(events) {
|
|
772
|
+
const started = events.find((e) => e.t === "run.started");
|
|
773
|
+
return started?.t === "run.started" ? started.tag : void 0;
|
|
774
|
+
}
|
|
771
775
|
function completedStepsFromJournal(events) {
|
|
772
776
|
const started = /* @__PURE__ */ new Map();
|
|
773
777
|
const completed = /* @__PURE__ */ new Map();
|
|
@@ -2322,6 +2326,9 @@ function resolveDirs(runsDir, cwd, cacheDir) {
|
|
|
2322
2326
|
function worktreePathFor(runsDir, runId) {
|
|
2323
2327
|
return path15.join(path15.dirname(runsDir), ".yak", "worktrees", runId);
|
|
2324
2328
|
}
|
|
2329
|
+
function worktreeBranchFor(runId) {
|
|
2330
|
+
return `yak/${runId}`;
|
|
2331
|
+
}
|
|
2325
2332
|
function generateRunId() {
|
|
2326
2333
|
const iso = (/* @__PURE__ */ new Date()).toISOString();
|
|
2327
2334
|
const [datePart, timePart] = iso.split("T");
|
|
@@ -2353,7 +2360,8 @@ async function executeWorkflowFile(workflowPath, opts = {}) {
|
|
|
2353
2360
|
workflow: workflow.name,
|
|
2354
2361
|
inputHash: inputWritten?.hash ?? sha256(JSON.stringify(workflow)),
|
|
2355
2362
|
adapter,
|
|
2356
|
-
isolation
|
|
2363
|
+
isolation,
|
|
2364
|
+
...opts.tag !== void 0 ? { tag: opts.tag } : {}
|
|
2357
2365
|
});
|
|
2358
2366
|
if (inputWritten) {
|
|
2359
2367
|
await appendJournalEvent(runDir, runId, {
|
|
@@ -2363,11 +2371,16 @@ async function executeWorkflowFile(workflowPath, opts = {}) {
|
|
|
2363
2371
|
bytes: inputWritten.bytes
|
|
2364
2372
|
});
|
|
2365
2373
|
}
|
|
2374
|
+
await opts.onStart?.({
|
|
2375
|
+
runId,
|
|
2376
|
+
runDir,
|
|
2377
|
+
...isolation === "worktree" ? { worktreeBranch: worktreeBranchFor(runId) } : {}
|
|
2378
|
+
});
|
|
2366
2379
|
let stepCwd = cwd;
|
|
2367
2380
|
if (isolation === "worktree") {
|
|
2368
2381
|
const worktreePath = worktreePathFor(runsDir, runId);
|
|
2369
2382
|
try {
|
|
2370
|
-
await createWorktree(cwd,
|
|
2383
|
+
await createWorktree(cwd, worktreeBranchFor(runId), "HEAD", worktreePath);
|
|
2371
2384
|
} catch {
|
|
2372
2385
|
await appendJournalEvent(runDir, runId, { t: "run.finished", status: "failed" });
|
|
2373
2386
|
return { runId, runDir, status: "failed" };
|
|
@@ -2520,7 +2533,8 @@ async function pendingCommand(opts = {}) {
|
|
|
2520
2533
|
const last = events.at(-1);
|
|
2521
2534
|
if (!last || last.t !== "run.finished" || last.status !== "suspended") continue;
|
|
2522
2535
|
anyPending = true;
|
|
2523
|
-
|
|
2536
|
+
const tag = runTagFromJournal(events);
|
|
2537
|
+
console.log(`run ${runId} suspended${tag !== void 0 ? ` (tag: ${tag})` : ""}:`);
|
|
2524
2538
|
const openIds = openRequestStepIds(events);
|
|
2525
2539
|
if (openIds.length === 0) {
|
|
2526
2540
|
console.log(" (no open request found \u2014 loop budget suspend from before M4, unresumable)");
|
|
@@ -2751,7 +2765,16 @@ async function runCommand(workflowPath, opts = {}) {
|
|
|
2751
2765
|
result = await executeWorkflowFile(path18.resolve(workflowPath), {
|
|
2752
2766
|
adapter: opts.adapter,
|
|
2753
2767
|
isolation: opts.isolation,
|
|
2754
|
-
input: opts.input
|
|
2768
|
+
input: opts.input,
|
|
2769
|
+
tag: opts.tag,
|
|
2770
|
+
// yak#23: emit the run id (and worktree branch) to stderr the moment
|
|
2771
|
+
// `run.started` is journalled, so an out-of-process launcher can
|
|
2772
|
+
// capture it without waiting for a terminal state. stdout is
|
|
2773
|
+
// untouched — the terminal-state line there is unchanged.
|
|
2774
|
+
onStart: ({ runId, worktreeBranch }) => {
|
|
2775
|
+
console.error(`run ${runId} started`);
|
|
2776
|
+
if (worktreeBranch) console.error(` worktree branch ${worktreeBranch}`);
|
|
2777
|
+
}
|
|
2755
2778
|
});
|
|
2756
2779
|
while (opts.interactive && result.status === "suspended") {
|
|
2757
2780
|
await answerOpenRequestsInteractively(result.runId, result.runDir);
|
|
@@ -2796,7 +2819,8 @@ async function statusCommand(runId, opts = {}) {
|
|
|
2796
2819
|
const events = await readJournal(runDir);
|
|
2797
2820
|
const statuses = stepStatusesFromJournal(workflow.steps, events);
|
|
2798
2821
|
const failuresByStep = new Map(failedStepsFromJournal(events).map((f) => [f.stepId, f]));
|
|
2799
|
-
|
|
2822
|
+
const tag = runTagFromJournal(events);
|
|
2823
|
+
console.log(`run ${resolvedRunId}${tag !== void 0 ? ` (tag: ${tag})` : ""}:`);
|
|
2800
2824
|
for (const { stepId, status } of statuses) {
|
|
2801
2825
|
console.log(` ${stepId}: ${status}`);
|
|
2802
2826
|
const step = workflow.steps.find((s) => s.id === stepId);
|
|
@@ -2904,6 +2928,19 @@ function parseInputPairs(pairs) {
|
|
|
2904
2928
|
return out;
|
|
2905
2929
|
}
|
|
2906
2930
|
|
|
2931
|
+
// src/cli/version.ts
|
|
2932
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
2933
|
+
function readVersion() {
|
|
2934
|
+
for (const rel of ["../package.json", "../../package.json"]) {
|
|
2935
|
+
try {
|
|
2936
|
+
const manifest = JSON.parse(readFileSync2(new URL(rel, import.meta.url), "utf8"));
|
|
2937
|
+
if (manifest.name === "@lchase/yak" && manifest.version) return manifest.version;
|
|
2938
|
+
} catch {
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
return "0.0.0";
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2907
2944
|
// src/cli/index.ts
|
|
2908
2945
|
function requireAdapterId(value) {
|
|
2909
2946
|
if (value !== "mock" && value !== "claude-code") {
|
|
@@ -2918,17 +2955,18 @@ function requireIsolation(value) {
|
|
|
2918
2955
|
return value;
|
|
2919
2956
|
}
|
|
2920
2957
|
var program = new Command();
|
|
2921
|
-
program.name("yak").description("An agentic workflow engine").version(
|
|
2958
|
+
program.name("yak").description("An agentic workflow engine").version(readVersion());
|
|
2922
2959
|
program.command("run").description("Run a workflow YAML file").argument("<workflow>", "path to the workflow YAML file").option("--adapter <adapter>", "agent adapter to use", "claude-code").option("--interactive", "prompt inline for gate answers instead of exiting to resume later", false).option("--isolation <mode>", 'run inside a fresh git worktree ("worktree") or the plain cwd ("none")', "none").option(
|
|
2923
2960
|
"--input <pair...>",
|
|
2924
2961
|
"workflow input as key=value, repeatable \u2014 validated against the workflow inputSchema"
|
|
2925
|
-
).action(
|
|
2962
|
+
).option("--tag <string>", "opaque correlation tag, stored on the run and echoed by yak pending/status").action(
|
|
2926
2963
|
async (workflow, options) => {
|
|
2927
2964
|
process.exitCode = await runCommand(workflow, {
|
|
2928
2965
|
adapter: requireAdapterId(options.adapter),
|
|
2929
2966
|
interactive: options.interactive,
|
|
2930
2967
|
isolation: requireIsolation(options.isolation),
|
|
2931
|
-
input: parseInputPairs(options.input)
|
|
2968
|
+
input: parseInputPairs(options.input),
|
|
2969
|
+
tag: options.tag
|
|
2932
2970
|
});
|
|
2933
2971
|
}
|
|
2934
2972
|
);
|