auto-mode 0.14.0 → 0.16.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/README.md CHANGED
@@ -58,10 +58,10 @@ assessment.
58
58
 
59
59
  ## Decisions and failures
60
60
 
61
- Jev evaluates each named rule with a typed choice. auto-mode returns a denial for a confident block,
62
- approval when every rule confidently allows the action, and a denial for uncertain decisions. Every
63
- denial names its rule, the harm, and what clears it, and the agent continues on another path. The
64
- confidence threshold is configurable and needs evaluation against your actions.
61
+ Jev evaluates each named rule with a typed choice between allow and block. auto-mode allows the
62
+ action unless some rule's block probability reaches the threshold, 0.2 by default. Every denial
63
+ names the rule with the highest block probability, the harm, and what clears it, and the agent
64
+ continues on another path. The threshold is configurable and needs evaluation against your actions.
65
65
 
66
66
  A missing key, failed API call, malformed response, or oversized request follows
67
67
  `decision.onFailure`. The default `defer` writes no verdict and keeps the prompt; `deny` fails
@@ -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") > 262144) return {
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 string, E as strictObject, T as number, _ as splitCommandWords, a as resolveSessionScopePath, b as DEFAULT_SCOPE_SOURCES, c as findCheckout, f as loadRepositoryContext, g as splitShellCommand, h as toRepositorySlug, i as resolveStateDir, m as loadPolicy, n as parseActionRequest, o as loadSessionScope, p as loadCheckoutRemotes, r as classifyAction, s as EMPTY_SESSION_SCOPE, t as renderVerdict, x as loadConfig, y as DEFAULT_DENIAL_BUDGET } from "./render-verdict-QLyj-g7d.js";
2
+ import { C as resolveStateDir, D as DEFAULT_SCOPE_SOURCES, E as DEFAULT_DENIAL_BUDGET, F as string, N as number, O as loadConfig, P as strictObject, a as loadSessionScope, b as splitCommandWords, f as loadRepositoryContext, g as loadPolicy, i as resolveSessionScopePath, j as readHostEnvironment, n as parseActionRequest, o as EMPTY_SESSION_SCOPE, p as loadCheckoutRemotes, r as classifyAction, t as renderVerdict, v as toRepositorySlug, w as findCheckout, y as splitShellCommand } from "./render-verdict-u3_ftSEp.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(resolveStateDir(), "denials", `${key}.json`);
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 = process.env["AUTO_MODE_DIAGNOSTICS_PATH"] ?? join(resolveStateDir(), "actions.jsonl");
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,29 +172,32 @@ async function writeActionDiagnostic(payload, entry) {
173
172
  });
174
173
  await appendFile(path, `${JSON.stringify(record)}\n`, { mode: 384 });
175
174
  } catch {
176
- process.stderr.write("auto-mode: diagnostics unavailable\n");
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/parse-scope-record-request.ts
184
- const recordSchema = strictObject({
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 = recordSchema.safeParse(body);
193
+ const parsed = scopeRecordRequestSchema.safeParse(body);
193
194
  return parsed.success ? parsed.data : null;
194
195
  }
195
196
  //#endregion
196
197
  //#region src/scope/read-pull-request.ts
197
198
  const LOOKUP_TIMEOUT_MS = 5e3;
198
- function readPullRequest(repository, number) {
199
+ function readPullRequest(repository, number, options = {}) {
200
+ const [file, ...prefix] = options.command ?? ["gh"];
199
201
  const args = [
200
202
  "pr",
201
203
  "view",
@@ -206,7 +208,8 @@ function readPullRequest(repository, number) {
206
208
  "headRefName,createdAt"
207
209
  ];
208
210
  return new Promise((resolve) => {
209
- const child = spawn("gh", [
211
+ const child = spawn(file, [
212
+ ...prefix,
210
213
  ...args,
211
214
  "--jq",
212
215
  "[.headRefName, .createdAt] | @tsv"
@@ -216,7 +219,7 @@ function readPullRequest(repository, number) {
216
219
  "pipe",
217
220
  "ignore"
218
221
  ],
219
- timeout: LOOKUP_TIMEOUT_MS
222
+ timeout: options.timeoutMs ?? LOOKUP_TIMEOUT_MS
220
223
  });
221
224
  let output = "";
222
225
  child.stdout.setEncoding("utf8");
@@ -365,13 +368,17 @@ async function readBranchCreatedAt(commonDir, branch) {
365
368
  }
366
369
  //#endregion
367
370
  //#region src/scope/write-session-scope.ts
368
- async function writeSessionScope(path, added) {
371
+ const SYSTEM_CLOCK = {
372
+ now: Date.now,
373
+ wait: (ms) => setTimeout(ms)
374
+ };
375
+ async function writeSessionScope(path, added, clock = SYSTEM_CLOCK) {
369
376
  await mkdir(dirname(path), {
370
377
  recursive: true,
371
378
  mode: 448
372
379
  });
373
380
  const lock = `${path}.lock`;
374
- await claimLock(lock);
381
+ await claimLock(lock, clock);
375
382
  try {
376
383
  const merged = mergeSessionScope([await loadSessionScope(path), added]);
377
384
  const staged = `${path}.${randomUUID()}.tmp`;
@@ -384,8 +391,8 @@ async function writeSessionScope(path, added) {
384
391
  const STALE_LOCK_MS = 1e4;
385
392
  const LOCK_WAIT_MS = 3e3;
386
393
  const LOCK_POLL_MS = 10;
387
- async function claimLock(lock) {
388
- const deadline = Date.now() + LOCK_WAIT_MS;
394
+ async function claimLock(lock, clock) {
395
+ const deadline = clock.now() + LOCK_WAIT_MS;
389
396
  for (;;) {
390
397
  try {
391
398
  await (await open(lock, "wx", 384)).close();
@@ -394,9 +401,9 @@ async function claimLock(lock) {
394
401
  if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) throw error;
395
402
  }
396
403
  const held = await stat(lock).catch(() => null);
397
- if (held !== null && Date.now() - held.mtimeMs > STALE_LOCK_MS) await rm(lock, { force: true });
398
- else if (Date.now() > deadline) throw new Error("session scope lock unavailable");
399
- else await setTimeout(LOCK_POLL_MS);
404
+ if (held !== null && clock.now() - held.mtimeMs > STALE_LOCK_MS) await rm(lock, { force: true });
405
+ else if (clock.now() > deadline) throw new Error("session scope lock unavailable");
406
+ else await clock.wait(LOCK_POLL_MS);
400
407
  }
401
408
  }
402
409
  //#endregion
@@ -407,12 +414,12 @@ async function updateSessionScope(request, options) {
407
414
  if (age < -6e4 || age > MAX_CALL_AGE_MS) return EMPTY_SESSION_SCOPE;
408
415
  const events = collectScopeEvents(request.command, request.cwd, options.home);
409
416
  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);
417
+ if (scope.worktrees.length + scope.branches.length + scope.pullRequests.length > 0) await writeSessionScope(resolveSessionScopePath(options.stateDir, request.sessionID), scope, options.lockClock);
411
418
  return scope;
412
419
  }
413
420
  async function tryVerifyScopeEvent(event, request, options) {
414
421
  try {
415
- return await verifyScopeEvent(event, request, options);
422
+ return await (options.verifyEvent === void 0 ? verifyScopeEvent(event, request, options) : options.verifyEvent(event, request));
416
423
  } catch {
417
424
  return EMPTY_SESSION_SCOPE;
418
425
  }
@@ -422,9 +429,9 @@ const FILE_CLOCK_SLACK_MS = 50;
422
429
  async function verifyScopeEvent(event, request, options) {
423
430
  if (event.kind === "worktree") {
424
431
  const link = await stat(join(event.path, ".git"));
425
- const checkout = await findCheckout(event.path);
432
+ const checkout = await findCheckout(event.path, options.env);
426
433
  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;
434
+ const branch = (await loadRepositoryContext(event.path, options.env))?.branch ?? null;
428
435
  if (branch === null) return {
429
436
  ...EMPTY_SESSION_SCOPE,
430
437
  worktrees: [event.path]
@@ -439,7 +446,7 @@ async function verifyScopeEvent(event, request, options) {
439
446
  }] : []
440
447
  };
441
448
  }
442
- const checkout = await findCheckout(event.directory);
449
+ const checkout = await findCheckout(event.directory, options.env);
443
450
  if (checkout === null) return EMPTY_SESSION_SCOPE;
444
451
  if (event.kind === "branch") return await isBranchCreatedSince(checkout.commonDir, event.name, request.startedAt) ? {
445
452
  ...EMPTY_SESSION_SCOPE,
@@ -470,7 +477,7 @@ async function isBranchCreatedSince(commonDir, branch, since) {
470
477
  return ref !== null && ref.mtimeMs >= since - FILE_CLOCK_SLACK_MS;
471
478
  }
472
479
  //#endregion
473
- //#region src/cli.ts
480
+ //#region src/run-cli.ts
474
481
  const USAGE = `auto-mode — a permission classifier for the auto-mode Claude Code mod
475
482
 
476
483
  Usage:
@@ -486,44 +493,85 @@ Options:
486
493
  --jev-only With run: require Jev and cap its API timeout at 5 seconds
487
494
  --evaluation-deadline <unix-ms> With --jev-only: share the helper and API deadline
488
495
  `;
489
- function readStdin() {
490
- return text(process.stdin);
496
+ async function runCLI(argv, io) {
497
+ const args = parseArgs({
498
+ args: [...argv],
499
+ allowPositionals: true,
500
+ options: {
501
+ classifier: { type: "string" },
502
+ rules: { type: "string" },
503
+ explain: { type: "boolean" },
504
+ "local-only": { type: "boolean" },
505
+ "jev-only": { type: "boolean" },
506
+ "evaluation-deadline": { type: "string" },
507
+ help: {
508
+ type: "boolean",
509
+ short: "h"
510
+ }
511
+ }
512
+ });
513
+ const [command] = args.positionals;
514
+ if (args.values.help === true || command === void 0) {
515
+ io.stdout.write(USAGE);
516
+ return 0;
517
+ }
518
+ if (command === "run") try {
519
+ return await run(io, args.values.explain === true, args.values["local-only"] === true, args.values["jev-only"] === true, args.values["evaluation-deadline"]);
520
+ } catch {
521
+ io.stderr.write("auto-mode: configuration unreadable; no verdict\n");
522
+ return 0;
523
+ }
524
+ if (command === "record") return runRecord(io);
525
+ if (command === "print-prompt") {
526
+ const config = await loadConfig(void 0, io.host);
527
+ printWarnings(io.stderr, config.warnings);
528
+ const framework = config.provider.protocol === "system-one" ? "decision.md" : "classifier.md";
529
+ const prompt = await loadPolicy({
530
+ classifierPath: args.values.classifier ?? config.classifierPath,
531
+ rulesPath: args.values.rules ?? config.rulesPath
532
+ }, framework);
533
+ io.stdout.write(`${prompt}\n`);
534
+ return 0;
535
+ }
536
+ io.stderr.write(`auto-mode: unknown command '${command}'\n\n${USAGE}`);
537
+ return 2;
491
538
  }
492
- async function run(explain, localOnly, jevOnly, deadline) {
493
- const raw = await readStdin();
539
+ async function run(io, explain, localOnly, jevOnly, deadline) {
540
+ const raw = await io.stdin();
494
541
  let body;
495
542
  try {
496
543
  body = JSON.parse(raw);
497
544
  } catch {
498
- return printNote(explain, "stdin is not JSON, so no verdict");
545
+ return printNote(io.stderr, explain, "stdin is not JSON, so no verdict");
499
546
  }
500
547
  const request = parseActionRequest(body);
501
- if (request === null) return printNote(explain, "not an action request this classifier judges, so no verdict");
548
+ if (request === null) return printNote(io.stderr, explain, "not an action request this classifier judges, so no verdict");
549
+ const host = io.host;
502
550
  const invocationID = randomUUID();
503
551
  await writeActionDiagnostic(request, {
504
552
  invocationID,
505
553
  status: "started"
506
- });
554
+ }, host, io.stderr);
507
555
  let loaded;
508
556
  try {
509
- loaded = await loadConfig();
557
+ loaded = await loadConfig(void 0, host);
510
558
  } catch {
511
559
  await writeActionDiagnostic(request, {
512
560
  invocationID,
513
561
  status: "failure",
514
562
  verdict: "defer"
515
- });
516
- return printNote(true, "configuration unreadable; no verdict");
563
+ }, host, io.stderr);
564
+ return printNote(io.stderr, true, "configuration unreadable; no verdict");
517
565
  }
518
- printWarnings(loaded.warnings);
566
+ printWarnings(io.stderr, loaded.warnings);
519
567
  const config = jevOnly ? buildJevOnlyConfig(loaded) : loaded;
520
568
  if (config === null) {
521
569
  await writeActionDiagnostic(request, {
522
570
  invocationID,
523
571
  status: "failure",
524
572
  verdict: "defer"
525
- });
526
- return printNote(true, "Jev-only evaluation requires system-one; no verdict");
573
+ }, host, io.stderr);
574
+ return printNote(io.stderr, true, "Jev-only evaluation requires system-one; no verdict");
527
575
  }
528
576
  const deadlineAt = deadline === void 0 ? void 0 : Number(deadline);
529
577
  if (deadlineAt !== void 0 && (!jevOnly || !Number.isSafeInteger(deadlineAt) || deadlineAt <= 0)) {
@@ -531,26 +579,24 @@ async function run(explain, localOnly, jevOnly, deadline) {
531
579
  invocationID,
532
580
  status: "failure",
533
581
  verdict: "defer"
534
- });
535
- return printNote(true, "invalid Jev evaluation deadline; no verdict");
582
+ }, host, io.stderr);
583
+ return printNote(io.stderr, true, "invalid Jev evaluation deadline; no verdict");
536
584
  }
537
585
  const controller = new AbortController();
538
- const stopEvaluation = () => {
586
+ const unsubscribe = jevOnly ? io.subscribeToStopSignals(() => {
539
587
  controller.abort();
540
- };
541
- if (jevOnly) {
542
- process.on("SIGTERM", stopEvaluation);
543
- process.on("SIGINT", stopEvaluation);
544
- }
588
+ }) : null;
545
589
  try {
546
- const statePath = resolveDenialStatePath(request);
590
+ const statePath = resolveDenialStatePath(request, resolveStateDir(host));
547
591
  const retryKey = buildRetryKey(request);
548
592
  const before = await loadDenialState(statePath);
549
593
  const retry = findRetryDeny(before, retryKey);
550
594
  const outcome = retry === null ? await classifyAction(request, config, {
551
595
  localOnly,
552
596
  deadlineAt,
553
- signal: controller.signal
597
+ signal: controller.signal,
598
+ host,
599
+ stderr: io.stderr
554
600
  }) : {
555
601
  verdict: retry,
556
602
  decidingStage: "retry",
@@ -559,8 +605,8 @@ async function run(explain, localOnly, jevOnly, deadline) {
559
605
  };
560
606
  const latest = retry === null ? await loadDenialState(statePath) : before;
561
607
  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) writeVerdict(plan.verdict);
608
+ if (plan.state !== latest) await tryWriteDenialState(io.stderr, statePath, plan.state);
609
+ if (plan.verdict !== null) printVerdict(io.stdout, plan.verdict);
564
610
  await writeActionDiagnostic(request, {
565
611
  invocationID,
566
612
  status: outcome.status,
@@ -572,16 +618,32 @@ async function run(explain, localOnly, jevOnly, deadline) {
572
618
  },
573
619
  escalation: plan.escalation,
574
620
  ...outcome.diagnostics === void 0 ? {} : { diagnostics: outcome.diagnostics }
575
- });
621
+ }, host, io.stderr);
576
622
  const note = plan.escalation ? "denial budget exhausted; the user decides this action" : outcome.note;
577
- return printNote(explain || outcome.unavailable === true, note);
623
+ return printNote(io.stderr, explain || outcome.unavailable === true, note);
578
624
  } finally {
579
- process.off("SIGTERM", stopEvaluation);
580
- process.off("SIGINT", stopEvaluation);
625
+ unsubscribe?.();
626
+ }
627
+ }
628
+ function printNote(stderr, explain, message) {
629
+ if (explain) stderr.write(`auto-mode: ${message}\n`);
630
+ return 0;
631
+ }
632
+ function printWarnings(stderr, warnings = []) {
633
+ for (const warning of warnings) stderr.write(`auto-mode: ${warning}\n`);
634
+ }
635
+ async function tryWriteDenialState(stderr, path, state) {
636
+ try {
637
+ await writeDenialState(path, state);
638
+ } catch {
639
+ stderr.write("auto-mode: denial counts unavailable\n");
581
640
  }
582
641
  }
583
- async function runRecord() {
584
- const raw = await readStdin();
642
+ function printVerdict(stdout, verdict) {
643
+ stdout.write(renderVerdict(verdict));
644
+ }
645
+ async function runRecord(io) {
646
+ const raw = await io.stdin();
585
647
  let body;
586
648
  try {
587
649
  body = JSON.parse(raw);
@@ -591,78 +653,34 @@ async function runRecord() {
591
653
  const request = parseScopeRecordRequest(body);
592
654
  if (request === null) return 0;
593
655
  try {
594
- const config = await loadConfig();
656
+ const config = await loadConfig(void 0, io.host);
595
657
  if (Object.values(config.scopeSources ?? DEFAULT_SCOPE_SOURCES).some((source) => source.kind === "session")) await updateSessionScope(request, {
596
658
  now: Date.now(),
597
- stateDir: resolveStateDir(),
598
- home: homedir(),
659
+ stateDir: resolveStateDir(io.host),
660
+ home: io.host.home,
661
+ env: io.host.env,
599
662
  readPullRequest
600
663
  });
601
664
  } catch {
602
- process.stderr.write("auto-mode: session scope unavailable\n");
665
+ io.stderr.write("auto-mode: session scope unavailable\n");
603
666
  }
604
667
  return 0;
605
668
  }
606
- async function tryWriteDenialState(path, state) {
607
- try {
608
- await writeDenialState(path, state);
609
- } catch {
610
- process.stderr.write("auto-mode: denial counts unavailable\n");
611
- }
612
- }
613
- function writeVerdict(verdict) {
614
- process.stdout.write(renderVerdict(verdict));
615
- }
616
- function printWarnings(warnings = []) {
617
- for (const warning of warnings) process.stderr.write(`auto-mode: ${warning}\n`);
618
- }
619
- function printNote(explain, message) {
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;
669
+ //#endregion
670
+ //#region src/cli.ts
671
+ process.exitCode = await runCLI(process.argv.slice(2), {
672
+ stdin: () => text(process.stdin),
673
+ stdout: process.stdout,
674
+ stderr: process.stderr,
675
+ host: readHostEnvironment(),
676
+ subscribeToStopSignals: (onStop) => {
677
+ process.on("SIGTERM", onStop);
678
+ process.on("SIGINT", onStop);
679
+ return () => {
680
+ process.off("SIGTERM", onStop);
681
+ process.off("SIGINT", onStop);
682
+ };
662
683
  }
663
- process.stderr.write(`auto-mode: unknown command '${command}'\n\n${USAGE}`);
664
- return 2;
665
- }
666
- process.exitCode = await main(process.argv.slice(2));
684
+ });
667
685
  //#endregion
668
686
  export {};