auto-mode 0.13.0 → 0.15.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/{classify-edit-D8GC6b91.js → classify-edit-LpoeenGt.js} +4 -1
- package/dist/cli.js +134 -118
- package/dist/index.d.ts +14 -2
- package/dist/index.js +1 -1
- package/dist/{render-verdict-De8aTmg-.js → render-verdict-Bj2tGqBI.js} +474 -228
- package/mods/auto-mode/hooks/testing.d.ts +76 -24
- package/mods/auto-mode/hooks/types.ts +1 -1
- package/package.json +2 -2
- package/policy/classifier.md +4 -1
- package/policy/decision.md +2 -0
- package/policy/rules.md +18 -1
- package/mods/auto-mode/hooks/build-prompt-context.claude-check.ts +0 -93
- package/mods/auto-mode/hooks/register.claude-check.ts +0 -1226
|
@@ -17717,6 +17717,9 @@ function getRE2(pattern) {
|
|
|
17717
17717
|
compiledRE2.set(pattern, compiled);
|
|
17718
17718
|
return compiled;
|
|
17719
17719
|
}
|
|
17720
|
+
//#endregion
|
|
17721
|
+
//#region src/bypass/classify-edit.ts
|
|
17722
|
+
const MAX_SCANNED_BYTES = 262144;
|
|
17720
17723
|
function classifyEdit(action, scope) {
|
|
17721
17724
|
const fields = getEditFields(action.toolName);
|
|
17722
17725
|
if (fields === null) return {
|
|
@@ -17747,7 +17750,7 @@ function classifyEdit(action, scope) {
|
|
|
17747
17750
|
kind: "jev",
|
|
17748
17751
|
reason: text.reason
|
|
17749
17752
|
};
|
|
17750
|
-
if (Buffer.byteLength(text, "utf8") >
|
|
17753
|
+
if (Buffer.byteLength(text, "utf8") > MAX_SCANNED_BYTES) return {
|
|
17751
17754
|
kind: "jev",
|
|
17752
17755
|
reason: "content larger than the secret scan reads"
|
|
17753
17756
|
};
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as
|
|
2
|
+
import { D as strictObject, E as number, O as string, _ as findCheckout, a as loadSessionScope, b as DEFAULT_SCOPE_SOURCES, d as loadCheckoutRemotes, f as loadPolicy, g as resolveStateDir, h as splitCommandWords, i as resolveSessionScopePath, m as splitShellCommand, n as parseActionRequest, o as EMPTY_SESSION_SCOPE, p as toRepositorySlug, r as classifyAction, t as renderVerdict, u as loadRepositoryContext, w as readHostEnvironment, x as loadConfig, y as DEFAULT_DENIAL_BUDGET } from "./render-verdict-Bj2tGqBI.js";
|
|
3
3
|
import { appendFile, mkdir, open, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, isAbsolute, join, normalize } from "node:path";
|
|
5
|
-
import { homedir } from "node:os";
|
|
6
5
|
import { spawn } from "node:child_process";
|
|
7
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
7
|
import { text } from "node:stream/consumers";
|
|
@@ -120,10 +119,10 @@ function formatDenialsLeft(left) {
|
|
|
120
119
|
}
|
|
121
120
|
//#endregion
|
|
122
121
|
//#region src/budget/resolve-denial-state-path.ts
|
|
123
|
-
function resolveDenialStatePath(request) {
|
|
122
|
+
function resolveDenialStatePath(request, stateDir) {
|
|
124
123
|
const agentID = request.decisionContext?.agentID ?? "";
|
|
125
124
|
const key = createHash("sha256").update(`${request.sessionID}\0${agentID}`).digest("hex").slice(0, 32);
|
|
126
|
-
return join(
|
|
125
|
+
return join(stateDir, "denials", `${key}.json`);
|
|
127
126
|
}
|
|
128
127
|
//#endregion
|
|
129
128
|
//#region src/budget/write-denial-state.ts
|
|
@@ -150,8 +149,8 @@ function buildJevOnlyConfig(config) {
|
|
|
150
149
|
}
|
|
151
150
|
//#endregion
|
|
152
151
|
//#region src/diagnostics/write-action-diagnostic.ts
|
|
153
|
-
async function writeActionDiagnostic(payload, entry) {
|
|
154
|
-
const path =
|
|
152
|
+
async function writeActionDiagnostic(payload, entry, host, warnings) {
|
|
153
|
+
const path = host.env["AUTO_MODE_DIAGNOSTICS_PATH"] ?? join(resolveStateDir(host), "actions.jsonl");
|
|
155
154
|
if (path === "") return;
|
|
156
155
|
const record = {
|
|
157
156
|
schemaVersion: 3,
|
|
@@ -173,23 +172,25 @@ async function writeActionDiagnostic(payload, entry) {
|
|
|
173
172
|
});
|
|
174
173
|
await appendFile(path, `${JSON.stringify(record)}\n`, { mode: 384 });
|
|
175
174
|
} catch {
|
|
176
|
-
|
|
175
|
+
warnings.write("auto-mode: diagnostics unavailable\n");
|
|
177
176
|
}
|
|
178
177
|
}
|
|
179
178
|
function toHash(value) {
|
|
180
179
|
return createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
181
180
|
}
|
|
182
181
|
//#endregion
|
|
183
|
-
//#region src/request/
|
|
184
|
-
const
|
|
182
|
+
//#region src/request/scope-record-request-schema.ts
|
|
183
|
+
const scopeRecordRequestSchema = strictObject({
|
|
185
184
|
sessionID: string().min(1),
|
|
186
185
|
cwd: string().min(1),
|
|
187
186
|
startedAt: number().int().positive(),
|
|
188
187
|
command: string().min(1),
|
|
189
188
|
resultText: string()
|
|
190
189
|
});
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/request/parse-scope-record-request.ts
|
|
191
192
|
function parseScopeRecordRequest(body) {
|
|
192
|
-
const parsed =
|
|
193
|
+
const parsed = scopeRecordRequestSchema.safeParse(body);
|
|
193
194
|
return parsed.success ? parsed.data : null;
|
|
194
195
|
}
|
|
195
196
|
//#endregion
|
|
@@ -365,13 +366,17 @@ async function readBranchCreatedAt(commonDir, branch) {
|
|
|
365
366
|
}
|
|
366
367
|
//#endregion
|
|
367
368
|
//#region src/scope/write-session-scope.ts
|
|
368
|
-
|
|
369
|
+
const SYSTEM_CLOCK = {
|
|
370
|
+
now: Date.now,
|
|
371
|
+
wait: (ms) => setTimeout(ms)
|
|
372
|
+
};
|
|
373
|
+
async function writeSessionScope(path, added, clock = SYSTEM_CLOCK) {
|
|
369
374
|
await mkdir(dirname(path), {
|
|
370
375
|
recursive: true,
|
|
371
376
|
mode: 448
|
|
372
377
|
});
|
|
373
378
|
const lock = `${path}.lock`;
|
|
374
|
-
await claimLock(lock);
|
|
379
|
+
await claimLock(lock, clock);
|
|
375
380
|
try {
|
|
376
381
|
const merged = mergeSessionScope([await loadSessionScope(path), added]);
|
|
377
382
|
const staged = `${path}.${randomUUID()}.tmp`;
|
|
@@ -384,8 +389,8 @@ async function writeSessionScope(path, added) {
|
|
|
384
389
|
const STALE_LOCK_MS = 1e4;
|
|
385
390
|
const LOCK_WAIT_MS = 3e3;
|
|
386
391
|
const LOCK_POLL_MS = 10;
|
|
387
|
-
async function claimLock(lock) {
|
|
388
|
-
const deadline =
|
|
392
|
+
async function claimLock(lock, clock) {
|
|
393
|
+
const deadline = clock.now() + LOCK_WAIT_MS;
|
|
389
394
|
for (;;) {
|
|
390
395
|
try {
|
|
391
396
|
await (await open(lock, "wx", 384)).close();
|
|
@@ -394,9 +399,9 @@ async function claimLock(lock) {
|
|
|
394
399
|
if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) throw error;
|
|
395
400
|
}
|
|
396
401
|
const held = await stat(lock).catch(() => null);
|
|
397
|
-
if (held !== null &&
|
|
398
|
-
else if (
|
|
399
|
-
else await
|
|
402
|
+
if (held !== null && clock.now() - held.mtimeMs > STALE_LOCK_MS) await rm(lock, { force: true });
|
|
403
|
+
else if (clock.now() > deadline) throw new Error("session scope lock unavailable");
|
|
404
|
+
else await clock.wait(LOCK_POLL_MS);
|
|
400
405
|
}
|
|
401
406
|
}
|
|
402
407
|
//#endregion
|
|
@@ -407,12 +412,12 @@ async function updateSessionScope(request, options) {
|
|
|
407
412
|
if (age < -6e4 || age > MAX_CALL_AGE_MS) return EMPTY_SESSION_SCOPE;
|
|
408
413
|
const events = collectScopeEvents(request.command, request.cwd, options.home);
|
|
409
414
|
const scope = mergeSessionScope(await Promise.all(events.map((event) => tryVerifyScopeEvent(event, request, options))));
|
|
410
|
-
if (scope.worktrees.length + scope.branches.length + scope.pullRequests.length > 0) await writeSessionScope(resolveSessionScopePath(options.stateDir, request.sessionID), scope);
|
|
415
|
+
if (scope.worktrees.length + scope.branches.length + scope.pullRequests.length > 0) await writeSessionScope(resolveSessionScopePath(options.stateDir, request.sessionID), scope, options.lockClock);
|
|
411
416
|
return scope;
|
|
412
417
|
}
|
|
413
418
|
async function tryVerifyScopeEvent(event, request, options) {
|
|
414
419
|
try {
|
|
415
|
-
return await verifyScopeEvent(event, request, options);
|
|
420
|
+
return await (options.verifyEvent === void 0 ? verifyScopeEvent(event, request, options) : options.verifyEvent(event, request));
|
|
416
421
|
} catch {
|
|
417
422
|
return EMPTY_SESSION_SCOPE;
|
|
418
423
|
}
|
|
@@ -422,9 +427,9 @@ const FILE_CLOCK_SLACK_MS = 50;
|
|
|
422
427
|
async function verifyScopeEvent(event, request, options) {
|
|
423
428
|
if (event.kind === "worktree") {
|
|
424
429
|
const link = await stat(join(event.path, ".git"));
|
|
425
|
-
const checkout = await findCheckout(event.path);
|
|
430
|
+
const checkout = await findCheckout(event.path, options.env);
|
|
426
431
|
if (!link.isFile() || link.mtimeMs < request.startedAt - FILE_CLOCK_SLACK_MS || checkout?.worktree !== event.path) return EMPTY_SESSION_SCOPE;
|
|
427
|
-
const branch = (await loadRepositoryContext(event.path))?.branch ?? null;
|
|
432
|
+
const branch = (await loadRepositoryContext(event.path, options.env))?.branch ?? null;
|
|
428
433
|
if (branch === null) return {
|
|
429
434
|
...EMPTY_SESSION_SCOPE,
|
|
430
435
|
worktrees: [event.path]
|
|
@@ -439,7 +444,7 @@ async function verifyScopeEvent(event, request, options) {
|
|
|
439
444
|
}] : []
|
|
440
445
|
};
|
|
441
446
|
}
|
|
442
|
-
const checkout = await findCheckout(event.directory);
|
|
447
|
+
const checkout = await findCheckout(event.directory, options.env);
|
|
443
448
|
if (checkout === null) return EMPTY_SESSION_SCOPE;
|
|
444
449
|
if (event.kind === "branch") return await isBranchCreatedSince(checkout.commonDir, event.name, request.startedAt) ? {
|
|
445
450
|
...EMPTY_SESSION_SCOPE,
|
|
@@ -470,7 +475,7 @@ async function isBranchCreatedSince(commonDir, branch, since) {
|
|
|
470
475
|
return ref !== null && ref.mtimeMs >= since - FILE_CLOCK_SLACK_MS;
|
|
471
476
|
}
|
|
472
477
|
//#endregion
|
|
473
|
-
//#region src/cli.ts
|
|
478
|
+
//#region src/run-cli.ts
|
|
474
479
|
const USAGE = `auto-mode — a permission classifier for the auto-mode Claude Code mod
|
|
475
480
|
|
|
476
481
|
Usage:
|
|
@@ -486,44 +491,85 @@ Options:
|
|
|
486
491
|
--jev-only With run: require Jev and cap its API timeout at 5 seconds
|
|
487
492
|
--evaluation-deadline <unix-ms> With --jev-only: share the helper and API deadline
|
|
488
493
|
`;
|
|
489
|
-
function
|
|
490
|
-
|
|
494
|
+
async function runCLI(argv, io) {
|
|
495
|
+
const args = parseArgs({
|
|
496
|
+
args: [...argv],
|
|
497
|
+
allowPositionals: true,
|
|
498
|
+
options: {
|
|
499
|
+
classifier: { type: "string" },
|
|
500
|
+
rules: { type: "string" },
|
|
501
|
+
explain: { type: "boolean" },
|
|
502
|
+
"local-only": { type: "boolean" },
|
|
503
|
+
"jev-only": { type: "boolean" },
|
|
504
|
+
"evaluation-deadline": { type: "string" },
|
|
505
|
+
help: {
|
|
506
|
+
type: "boolean",
|
|
507
|
+
short: "h"
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
const [command] = args.positionals;
|
|
512
|
+
if (args.values.help === true || command === void 0) {
|
|
513
|
+
io.stdout.write(USAGE);
|
|
514
|
+
return 0;
|
|
515
|
+
}
|
|
516
|
+
if (command === "run") try {
|
|
517
|
+
return await run(io, args.values.explain === true, args.values["local-only"] === true, args.values["jev-only"] === true, args.values["evaluation-deadline"]);
|
|
518
|
+
} catch {
|
|
519
|
+
io.stderr.write("auto-mode: configuration unreadable; no verdict\n");
|
|
520
|
+
return 0;
|
|
521
|
+
}
|
|
522
|
+
if (command === "record") return runRecord(io);
|
|
523
|
+
if (command === "print-prompt") {
|
|
524
|
+
const config = await loadConfig(void 0, io.host);
|
|
525
|
+
printWarnings(io.stderr, config.warnings);
|
|
526
|
+
const framework = config.provider.protocol === "system-one" ? "decision.md" : "classifier.md";
|
|
527
|
+
const prompt = await loadPolicy({
|
|
528
|
+
classifierPath: args.values.classifier ?? config.classifierPath,
|
|
529
|
+
rulesPath: args.values.rules ?? config.rulesPath
|
|
530
|
+
}, framework);
|
|
531
|
+
io.stdout.write(`${prompt}\n`);
|
|
532
|
+
return 0;
|
|
533
|
+
}
|
|
534
|
+
io.stderr.write(`auto-mode: unknown command '${command}'\n\n${USAGE}`);
|
|
535
|
+
return 2;
|
|
491
536
|
}
|
|
492
|
-
async function run(explain, localOnly, jevOnly, deadline) {
|
|
493
|
-
const raw = await
|
|
537
|
+
async function run(io, explain, localOnly, jevOnly, deadline) {
|
|
538
|
+
const raw = await io.stdin();
|
|
494
539
|
let body;
|
|
495
540
|
try {
|
|
496
541
|
body = JSON.parse(raw);
|
|
497
542
|
} catch {
|
|
498
|
-
return printNote(explain, "stdin is not JSON, so no verdict");
|
|
543
|
+
return printNote(io.stderr, explain, "stdin is not JSON, so no verdict");
|
|
499
544
|
}
|
|
500
545
|
const request = parseActionRequest(body);
|
|
501
|
-
if (request === null) return printNote(explain, "not an action request this classifier judges, so no verdict");
|
|
546
|
+
if (request === null) return printNote(io.stderr, explain, "not an action request this classifier judges, so no verdict");
|
|
547
|
+
const host = io.host;
|
|
502
548
|
const invocationID = randomUUID();
|
|
503
549
|
await writeActionDiagnostic(request, {
|
|
504
550
|
invocationID,
|
|
505
551
|
status: "started"
|
|
506
|
-
});
|
|
552
|
+
}, host, io.stderr);
|
|
507
553
|
let loaded;
|
|
508
554
|
try {
|
|
509
|
-
loaded = await loadConfig();
|
|
555
|
+
loaded = await loadConfig(void 0, host);
|
|
510
556
|
} catch {
|
|
511
557
|
await writeActionDiagnostic(request, {
|
|
512
558
|
invocationID,
|
|
513
559
|
status: "failure",
|
|
514
560
|
verdict: "defer"
|
|
515
|
-
});
|
|
516
|
-
return printNote(true, "configuration unreadable; no verdict");
|
|
561
|
+
}, host, io.stderr);
|
|
562
|
+
return printNote(io.stderr, true, "configuration unreadable; no verdict");
|
|
517
563
|
}
|
|
518
|
-
printWarnings(loaded.warnings);
|
|
564
|
+
printWarnings(io.stderr, loaded.warnings);
|
|
519
565
|
const config = jevOnly ? buildJevOnlyConfig(loaded) : loaded;
|
|
520
566
|
if (config === null) {
|
|
521
567
|
await writeActionDiagnostic(request, {
|
|
522
568
|
invocationID,
|
|
523
569
|
status: "failure",
|
|
524
570
|
verdict: "defer"
|
|
525
|
-
});
|
|
526
|
-
return printNote(true, "Jev-only evaluation requires system-one; no verdict");
|
|
571
|
+
}, host, io.stderr);
|
|
572
|
+
return printNote(io.stderr, true, "Jev-only evaluation requires system-one; no verdict");
|
|
527
573
|
}
|
|
528
574
|
const deadlineAt = deadline === void 0 ? void 0 : Number(deadline);
|
|
529
575
|
if (deadlineAt !== void 0 && (!jevOnly || !Number.isSafeInteger(deadlineAt) || deadlineAt <= 0)) {
|
|
@@ -531,26 +577,24 @@ async function run(explain, localOnly, jevOnly, deadline) {
|
|
|
531
577
|
invocationID,
|
|
532
578
|
status: "failure",
|
|
533
579
|
verdict: "defer"
|
|
534
|
-
});
|
|
535
|
-
return printNote(true, "invalid Jev evaluation deadline; no verdict");
|
|
580
|
+
}, host, io.stderr);
|
|
581
|
+
return printNote(io.stderr, true, "invalid Jev evaluation deadline; no verdict");
|
|
536
582
|
}
|
|
537
583
|
const controller = new AbortController();
|
|
538
|
-
const
|
|
584
|
+
const unsubscribe = jevOnly ? io.subscribeToStopSignals(() => {
|
|
539
585
|
controller.abort();
|
|
540
|
-
};
|
|
541
|
-
if (jevOnly) {
|
|
542
|
-
process.on("SIGTERM", stopEvaluation);
|
|
543
|
-
process.on("SIGINT", stopEvaluation);
|
|
544
|
-
}
|
|
586
|
+
}) : null;
|
|
545
587
|
try {
|
|
546
|
-
const statePath = resolveDenialStatePath(request);
|
|
588
|
+
const statePath = resolveDenialStatePath(request, resolveStateDir(host));
|
|
547
589
|
const retryKey = buildRetryKey(request);
|
|
548
590
|
const before = await loadDenialState(statePath);
|
|
549
591
|
const retry = findRetryDeny(before, retryKey);
|
|
550
592
|
const outcome = retry === null ? await classifyAction(request, config, {
|
|
551
593
|
localOnly,
|
|
552
594
|
deadlineAt,
|
|
553
|
-
signal: controller.signal
|
|
595
|
+
signal: controller.signal,
|
|
596
|
+
host,
|
|
597
|
+
stderr: io.stderr
|
|
554
598
|
}) : {
|
|
555
599
|
verdict: retry,
|
|
556
600
|
decidingStage: "retry",
|
|
@@ -559,8 +603,8 @@ async function run(explain, localOnly, jevOnly, deadline) {
|
|
|
559
603
|
};
|
|
560
604
|
const latest = retry === null ? await loadDenialState(statePath) : before;
|
|
561
605
|
const plan = planDenialBudget(latest, outcome.verdict, retryKey, config.denialBudget ?? DEFAULT_DENIAL_BUDGET);
|
|
562
|
-
if (plan.state !== latest) await tryWriteDenialState(statePath, plan.state);
|
|
563
|
-
if (plan.verdict !== null)
|
|
606
|
+
if (plan.state !== latest) await tryWriteDenialState(io.stderr, statePath, plan.state);
|
|
607
|
+
if (plan.verdict !== null) printVerdict(io.stdout, plan.verdict);
|
|
564
608
|
await writeActionDiagnostic(request, {
|
|
565
609
|
invocationID,
|
|
566
610
|
status: outcome.status,
|
|
@@ -572,16 +616,32 @@ async function run(explain, localOnly, jevOnly, deadline) {
|
|
|
572
616
|
},
|
|
573
617
|
escalation: plan.escalation,
|
|
574
618
|
...outcome.diagnostics === void 0 ? {} : { diagnostics: outcome.diagnostics }
|
|
575
|
-
});
|
|
619
|
+
}, host, io.stderr);
|
|
576
620
|
const note = plan.escalation ? "denial budget exhausted; the user decides this action" : outcome.note;
|
|
577
|
-
return printNote(explain || outcome.unavailable === true, note);
|
|
621
|
+
return printNote(io.stderr, explain || outcome.unavailable === true, note);
|
|
578
622
|
} finally {
|
|
579
|
-
|
|
580
|
-
|
|
623
|
+
unsubscribe?.();
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function printNote(stderr, explain, message) {
|
|
627
|
+
if (explain) stderr.write(`auto-mode: ${message}\n`);
|
|
628
|
+
return 0;
|
|
629
|
+
}
|
|
630
|
+
function printWarnings(stderr, warnings = []) {
|
|
631
|
+
for (const warning of warnings) stderr.write(`auto-mode: ${warning}\n`);
|
|
632
|
+
}
|
|
633
|
+
async function tryWriteDenialState(stderr, path, state) {
|
|
634
|
+
try {
|
|
635
|
+
await writeDenialState(path, state);
|
|
636
|
+
} catch {
|
|
637
|
+
stderr.write("auto-mode: denial counts unavailable\n");
|
|
581
638
|
}
|
|
582
639
|
}
|
|
583
|
-
|
|
584
|
-
|
|
640
|
+
function printVerdict(stdout, verdict) {
|
|
641
|
+
stdout.write(renderVerdict(verdict));
|
|
642
|
+
}
|
|
643
|
+
async function runRecord(io) {
|
|
644
|
+
const raw = await io.stdin();
|
|
585
645
|
let body;
|
|
586
646
|
try {
|
|
587
647
|
body = JSON.parse(raw);
|
|
@@ -591,78 +651,34 @@ async function runRecord() {
|
|
|
591
651
|
const request = parseScopeRecordRequest(body);
|
|
592
652
|
if (request === null) return 0;
|
|
593
653
|
try {
|
|
594
|
-
const config = await loadConfig();
|
|
654
|
+
const config = await loadConfig(void 0, io.host);
|
|
595
655
|
if (Object.values(config.scopeSources ?? DEFAULT_SCOPE_SOURCES).some((source) => source.kind === "session")) await updateSessionScope(request, {
|
|
596
656
|
now: Date.now(),
|
|
597
|
-
stateDir: resolveStateDir(),
|
|
598
|
-
home:
|
|
657
|
+
stateDir: resolveStateDir(io.host),
|
|
658
|
+
home: io.host.home,
|
|
659
|
+
env: io.host.env,
|
|
599
660
|
readPullRequest
|
|
600
661
|
});
|
|
601
662
|
} catch {
|
|
602
|
-
|
|
663
|
+
io.stderr.write("auto-mode: session scope unavailable\n");
|
|
603
664
|
}
|
|
604
665
|
return 0;
|
|
605
666
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
if (explain) process.stderr.write(`auto-mode: ${message}\n`);
|
|
621
|
-
return 0;
|
|
622
|
-
}
|
|
623
|
-
async function main(argv) {
|
|
624
|
-
const args = parseArgs({
|
|
625
|
-
args: [...argv],
|
|
626
|
-
allowPositionals: true,
|
|
627
|
-
options: {
|
|
628
|
-
classifier: { type: "string" },
|
|
629
|
-
rules: { type: "string" },
|
|
630
|
-
explain: { type: "boolean" },
|
|
631
|
-
"local-only": { type: "boolean" },
|
|
632
|
-
"jev-only": { type: "boolean" },
|
|
633
|
-
"evaluation-deadline": { type: "string" },
|
|
634
|
-
help: {
|
|
635
|
-
type: "boolean",
|
|
636
|
-
short: "h"
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
});
|
|
640
|
-
const [command] = args.positionals;
|
|
641
|
-
if (args.values.help === true || command === void 0) {
|
|
642
|
-
process.stdout.write(USAGE);
|
|
643
|
-
return 0;
|
|
644
|
-
}
|
|
645
|
-
if (command === "run") try {
|
|
646
|
-
return await run(args.values.explain === true, args.values["local-only"] === true, args.values["jev-only"] === true, args.values["evaluation-deadline"]);
|
|
647
|
-
} catch {
|
|
648
|
-
process.stderr.write("auto-mode: configuration unreadable; no verdict\n");
|
|
649
|
-
return 0;
|
|
650
|
-
}
|
|
651
|
-
if (command === "record") return runRecord();
|
|
652
|
-
if (command === "print-prompt") {
|
|
653
|
-
const config = await loadConfig();
|
|
654
|
-
printWarnings(config.warnings);
|
|
655
|
-
const framework = config.provider.protocol === "system-one" ? "decision.md" : "classifier.md";
|
|
656
|
-
const prompt = await loadPolicy({
|
|
657
|
-
classifierPath: args.values.classifier ?? config.classifierPath,
|
|
658
|
-
rulesPath: args.values.rules ?? config.rulesPath
|
|
659
|
-
}, framework);
|
|
660
|
-
process.stdout.write(`${prompt}\n`);
|
|
661
|
-
return 0;
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/cli.ts
|
|
669
|
+
process.exitCode = await runCLI(process.argv.slice(2), {
|
|
670
|
+
stdin: () => text(process.stdin),
|
|
671
|
+
stdout: process.stdout,
|
|
672
|
+
stderr: process.stderr,
|
|
673
|
+
host: readHostEnvironment(),
|
|
674
|
+
subscribeToStopSignals: (onStop) => {
|
|
675
|
+
process.on("SIGTERM", onStop);
|
|
676
|
+
process.on("SIGINT", onStop);
|
|
677
|
+
return () => {
|
|
678
|
+
process.off("SIGTERM", onStop);
|
|
679
|
+
process.off("SIGINT", onStop);
|
|
680
|
+
};
|
|
662
681
|
}
|
|
663
|
-
|
|
664
|
-
return 2;
|
|
665
|
-
}
|
|
666
|
-
process.exitCode = await main(process.argv.slice(2));
|
|
682
|
+
});
|
|
667
683
|
//#endregion
|
|
668
684
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -80,10 +80,21 @@ interface DecisionDiagnostics {
|
|
|
80
80
|
}
|
|
81
81
|
//#endregion
|
|
82
82
|
//#region src/config/types.d.ts
|
|
83
|
+
interface OutputStream {
|
|
84
|
+
readonly write: (text: string) => unknown;
|
|
85
|
+
}
|
|
86
|
+
interface HostEnvironment {
|
|
87
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
88
|
+
readonly home: string;
|
|
89
|
+
readonly scratchPaths?: readonly string[] | undefined;
|
|
90
|
+
}
|
|
83
91
|
interface EvaluationOptions {
|
|
84
92
|
readonly deadlineAt?: number | undefined;
|
|
85
93
|
readonly signal?: Readonly<AbortSignal> | undefined;
|
|
86
94
|
readonly taskScope?: TaskScopeSummary | undefined;
|
|
95
|
+
readonly host?: Readonly<HostEnvironment> | undefined;
|
|
96
|
+
readonly now?: (() => number) | undefined;
|
|
97
|
+
readonly timeout?: ((ms: number) => AbortSignal) | undefined;
|
|
87
98
|
}
|
|
88
99
|
//#endregion
|
|
89
100
|
//#region src/config/presets.d.ts
|
|
@@ -119,8 +130,8 @@ interface Config {
|
|
|
119
130
|
readonly warnings?: readonly string[];
|
|
120
131
|
}
|
|
121
132
|
export declare const DEFAULT_CONFIG: Config;
|
|
122
|
-
export declare function resolveConfigPath(): string;
|
|
123
|
-
export declare function loadConfig(
|
|
133
|
+
export declare function resolveConfigPath(host?: Readonly<HostEnvironment>): string;
|
|
134
|
+
export declare function loadConfig(configPath?: string, host?: Readonly<HostEnvironment>): Promise<Config>;
|
|
124
135
|
export declare function resolveApiKey(provider: ProviderConfig, options?: EvaluationOptions): Promise<string | null>;
|
|
125
136
|
//#endregion
|
|
126
137
|
//#region src/classify-action.d.ts
|
|
@@ -135,6 +146,7 @@ interface ActionOutcome {
|
|
|
135
146
|
}
|
|
136
147
|
interface ClassifyOptions extends EvaluationOptions {
|
|
137
148
|
readonly localOnly?: boolean;
|
|
149
|
+
readonly stderr?: Readonly<OutputStream> | undefined;
|
|
138
150
|
}
|
|
139
151
|
export declare function classifyAction(request: ActionRequest, config: Config, options?: ClassifyOptions): Promise<ActionOutcome>;
|
|
140
152
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as resolveConfigPath, S as resolveApiKey,
|
|
1
|
+
import { C as resolveConfigPath, S as resolveApiKey, T as PRESETS, c as classifyWithModel, f as loadPolicy, l as parseModelVerdict, n as parseActionRequest, r as classifyAction, s as classifyLocally, t as renderVerdict, v as DEFAULT_CONFIG, x as loadConfig } from "./render-verdict-Bj2tGqBI.js";
|
|
2
2
|
export { DEFAULT_CONFIG, PRESETS, classifyAction, classifyLocally, classifyWithModel, loadConfig, loadPolicy, parseActionRequest, parseModelVerdict, renderVerdict, resolveApiKey, resolveConfigPath };
|