@epoch-agent/cli 0.2.0 → 0.3.2
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
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
writeProviderSecret
|
|
4
|
+
} from "./chunk-4SOT24BS.js";
|
|
5
5
|
// src/index.ts
|
|
6
6
|
import { Command } from "commander";
|
|
7
|
-
import { t as
|
|
7
|
+
import { t as t39 } from "@epoch-agent/infra";
|
|
8
8
|
// src/commands/agents.ts
|
|
9
9
|
import { AGENT_ROLE_DIAG_MODULE, findRole, roleSourceLabel } from "@epoch-agent/core";
|
|
10
10
|
import { t as t2 } from "@epoch-agent/infra";
|
|
@@ -331,9 +331,122 @@ function registerCompletionCommand(program2) {
|
|
|
331
331
|
process.stdout.write(renderCompletion(program2, shell));
|
|
332
332
|
});
|
|
333
333
|
}
|
|
334
|
+
// src/commands/compliance.ts
|
|
335
|
+
import {
|
|
336
|
+
ComplianceEngine,
|
|
337
|
+
DEFAULT_COMPLIANCE_SETTINGS,
|
|
338
|
+
loadConfig,
|
|
339
|
+
loadLexiconFiles,
|
|
340
|
+
resolveComplianceDir,
|
|
341
|
+
resolveComplianceDirs
|
|
342
|
+
} from "@epoch-agent/core";
|
|
343
|
+
import { t as t4 } from "@epoch-agent/infra";
|
|
344
|
+
// src/paths.ts
|
|
345
|
+
import { existsSync, mkdirSync } from "fs";
|
|
346
|
+
import { hasProviderCredentials } from "@epoch-agent/core";
|
|
347
|
+
import { configPath, envPath, resolveHomeDir } from "@epoch-agent/infra";
|
|
348
|
+
var EPOCH_HOME = resolveHomeDir();
|
|
349
|
+
var CONFIG_PATH = configPath(EPOCH_HOME);
|
|
350
|
+
var ENV_PATH = envPath(EPOCH_HOME);
|
|
351
|
+
function ensureEpochHome() {
|
|
352
|
+
if (!existsSync(EPOCH_HOME)) mkdirSync(EPOCH_HOME, { recursive: true });
|
|
353
|
+
}
|
|
354
|
+
function hasCredentials() {
|
|
355
|
+
return hasProviderCredentials(EPOCH_HOME);
|
|
356
|
+
}
|
|
357
|
+
// src/commands/compliance.ts
|
|
358
|
+
var log2 = (msg) => process.stdout.write(msg + "\n");
|
|
359
|
+
function buildEngine() {
|
|
360
|
+
const config = loadConfig(void 0, { homeDir: EPOCH_HOME });
|
|
361
|
+
const settings = {
|
|
362
|
+
enabled: config.compliance?.enabled ?? DEFAULT_COMPLIANCE_SETTINGS.enabled,
|
|
363
|
+
scope: { ...DEFAULT_COMPLIANCE_SETTINGS.scope, ...config.compliance?.scope },
|
|
364
|
+
skipCodeBlocks: config.compliance?.skipCodeBlocks ?? DEFAULT_COMPLIANCE_SETTINGS.skipCodeBlocks,
|
|
365
|
+
maxHoldChars: config.compliance?.maxHoldChars ?? DEFAULT_COMPLIANCE_SETTINGS.maxHoldChars,
|
|
366
|
+
actions: { ...DEFAULT_COMPLIANCE_SETTINGS.actions, ...config.compliance?.actions }
|
|
367
|
+
};
|
|
368
|
+
const userDir = resolveComplianceDir(config.homeDir);
|
|
369
|
+
const dirs = resolveComplianceDirs({ userDir, trusted: false });
|
|
370
|
+
const rules = [];
|
|
371
|
+
const exempt = [];
|
|
372
|
+
for (const spec of dirs.dirs) {
|
|
373
|
+
const loaded = loadLexiconFiles(spec.dir, spec.source);
|
|
374
|
+
rules.push(...loaded.rules);
|
|
375
|
+
exempt.push(...loaded.exempt);
|
|
376
|
+
}
|
|
377
|
+
return { engine: new ComplianceEngine({ settings, rules, exempt }), settings, dir: userDir };
|
|
378
|
+
}
|
|
379
|
+
async function readStdin() {
|
|
380
|
+
const chunks = [];
|
|
381
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
382
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
383
|
+
}
|
|
384
|
+
function checkText(text) {
|
|
385
|
+
const { engine, settings } = buildEngine();
|
|
386
|
+
if (!settings.enabled) {
|
|
387
|
+
log2(t4("compliance_cmd.disabled"));
|
|
388
|
+
return 0;
|
|
389
|
+
}
|
|
390
|
+
const gate = engine.inspect(text);
|
|
391
|
+
if (!gate) {
|
|
392
|
+
log2(t4("compliance_cmd.clean"));
|
|
393
|
+
return 0;
|
|
394
|
+
}
|
|
395
|
+
const { text: released, verdict } = gate;
|
|
396
|
+
log2(t4("compliance_cmd.verdict", { action: verdict.action, count: verdict.hits.length }));
|
|
397
|
+
for (const hit of verdict.hits) {
|
|
398
|
+
log2(
|
|
399
|
+
t4("compliance_cmd.hit", {
|
|
400
|
+
category: hit.category,
|
|
401
|
+
action: hit.action,
|
|
402
|
+
rule: hit.ruleId,
|
|
403
|
+
excerpt: text.slice(hit.start, hit.end)
|
|
404
|
+
})
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
if (verdict.action === "block") log2(t4("compliance_cmd.blocked"));
|
|
408
|
+
else log2(t4("compliance_cmd.released", { text: released }));
|
|
409
|
+
return verdict.action === "warn" ? 0 : 1;
|
|
410
|
+
}
|
|
411
|
+
function printStatus() {
|
|
412
|
+
const { engine, settings, dir } = buildEngine();
|
|
413
|
+
log2(t4("compliance_cmd.status_enabled", { enabled: String(settings.enabled) }));
|
|
414
|
+
log2(
|
|
415
|
+
t4("compliance_cmd.status_rules", {
|
|
416
|
+
active: engine.activeRuleCount,
|
|
417
|
+
builtin: engine.counts.builtin,
|
|
418
|
+
user: engine.counts.user
|
|
419
|
+
})
|
|
420
|
+
);
|
|
421
|
+
log2(t4("compliance_cmd.status_dir", { dir }));
|
|
422
|
+
log2(
|
|
423
|
+
t4("compliance_cmd.status_scope", {
|
|
424
|
+
output: String(settings.scope.output),
|
|
425
|
+
input: String(settings.scope.input),
|
|
426
|
+
code: String(settings.skipCodeBlocks)
|
|
427
|
+
})
|
|
428
|
+
);
|
|
429
|
+
for (const [category, action] of Object.entries(settings.actions)) {
|
|
430
|
+
log2(t4("compliance_cmd.status_action", { category, action }));
|
|
431
|
+
}
|
|
432
|
+
log2(t4("compliance_cmd.status_project_note"));
|
|
433
|
+
}
|
|
434
|
+
function registerComplianceCommand(program2) {
|
|
435
|
+
const cmd = program2.command("compliance").description(t4("compliance_cmd.cmd_root"));
|
|
436
|
+
cmd.command("check", { isDefault: true }).description(t4("compliance_cmd.cmd_check")).argument("[text]", t4("compliance_cmd.arg_text")).option("--stdin", t4("compliance_cmd.opt_stdin")).action(async (text, opts) => {
|
|
437
|
+
const input2 = opts.stdin ? await readStdin() : text;
|
|
438
|
+
if (!input2) {
|
|
439
|
+
log2(t4("compliance_cmd.need_text"));
|
|
440
|
+
process.exitCode = 2;
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
process.exitCode = checkText(input2);
|
|
444
|
+
});
|
|
445
|
+
cmd.command("status").description(t4("compliance_cmd.cmd_status")).action(() => printStatus());
|
|
446
|
+
}
|
|
334
447
|
// src/commands/config.ts
|
|
335
448
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
336
|
-
import { maskApiKey, t as
|
|
449
|
+
import { maskApiKey, t as t5 } from "@epoch-agent/infra";
|
|
337
450
|
import {
|
|
338
451
|
API_KEY_ENV_VARS,
|
|
339
452
|
isPermissionLevel,
|
|
@@ -354,35 +467,48 @@ import {
|
|
|
354
467
|
unsetSectionField,
|
|
355
468
|
writeConfigYaml
|
|
356
469
|
} from "@epoch-agent/infra";
|
|
357
|
-
// src/paths.ts
|
|
358
|
-
import { existsSync, mkdirSync } from "fs";
|
|
359
|
-
import { hasProviderCredentials } from "@epoch-agent/core";
|
|
360
|
-
import { configPath, envPath, resolveHomeDir } from "@epoch-agent/infra";
|
|
361
|
-
var EPOCH_HOME = resolveHomeDir();
|
|
362
|
-
var CONFIG_PATH = configPath(EPOCH_HOME);
|
|
363
|
-
var ENV_PATH = envPath(EPOCH_HOME);
|
|
364
|
-
function ensureEpochHome() {
|
|
365
|
-
if (!existsSync(EPOCH_HOME)) mkdirSync(EPOCH_HOME, { recursive: true });
|
|
366
|
-
}
|
|
367
|
-
function hasCredentials() {
|
|
368
|
-
return hasProviderCredentials(EPOCH_HOME);
|
|
369
|
-
}
|
|
370
470
|
// src/commands/config.ts
|
|
371
|
-
var
|
|
471
|
+
var log3 = (msg) => {
|
|
372
472
|
process.stdout.write(msg + "\n");
|
|
373
473
|
};
|
|
374
474
|
function isPositiveNumber(v) {
|
|
375
475
|
const n = Number(v);
|
|
376
476
|
return Number.isFinite(n) && n > 0;
|
|
377
477
|
}
|
|
478
|
+
function isPositiveInt(v) {
|
|
479
|
+
return isPositiveNumber(v) && Number.isInteger(Number(v));
|
|
480
|
+
}
|
|
378
481
|
var BUDGET_FIELDS = {
|
|
379
482
|
maxCostUsd: isPositiveNumber,
|
|
380
483
|
maxTokens: isPositiveNumber,
|
|
381
484
|
warnAtPercent: (v) => isPositiveNumber(v) && Number(v) <= 100,
|
|
382
485
|
onUnknownPricing: (v) => v === "block" || v === "warn"
|
|
383
486
|
};
|
|
487
|
+
var SCALAR_FIELDS = {
|
|
488
|
+
maxTurns: isPositiveInt,
|
|
489
|
+
contextLength: isPositiveInt
|
|
490
|
+
};
|
|
491
|
+
var SECTION_FIELDS = {
|
|
492
|
+
budget: BUDGET_FIELDS,
|
|
493
|
+
compression: {
|
|
494
|
+
enabled: (v) => v === "true" || v === "false",
|
|
495
|
+
threshold: (v) => Number.isFinite(Number(v)) && Number(v) > 0 && Number(v) <= 1
|
|
496
|
+
}
|
|
497
|
+
};
|
|
384
498
|
function knownKeys() {
|
|
385
|
-
|
|
499
|
+
const nested = Object.entries(SECTION_FIELDS).flatMap(
|
|
500
|
+
([section, fields]) => Object.keys(fields).map((field) => `${section}.${field}`)
|
|
501
|
+
);
|
|
502
|
+
return [
|
|
503
|
+
"provider",
|
|
504
|
+
"model",
|
|
505
|
+
"models.utility",
|
|
506
|
+
"fallback.model",
|
|
507
|
+
"permission",
|
|
508
|
+
"shell",
|
|
509
|
+
...Object.keys(SCALAR_FIELDS),
|
|
510
|
+
...nested
|
|
511
|
+
].join(", ");
|
|
386
512
|
}
|
|
387
513
|
function maskSecrets(yaml) {
|
|
388
514
|
return yaml.replace(/^(\s*(?:apiKey|api_key|token|secret)\s*:\s*)(.+)$/gim, (_m, head, raw) => {
|
|
@@ -392,111 +518,111 @@ function maskSecrets(yaml) {
|
|
|
392
518
|
});
|
|
393
519
|
}
|
|
394
520
|
function registerConfigCommand(program2) {
|
|
395
|
-
const cmd = program2.command("config").description(
|
|
396
|
-
cmd.command("show").description(
|
|
521
|
+
const cmd = program2.command("config").description(t5("cli.config.summary"));
|
|
522
|
+
cmd.command("show").description(t5("cli.config.show")).option("--raw", t5("cli.config.opt_raw")).action((opts) => {
|
|
397
523
|
if (!existsSync2(CONFIG_PATH)) {
|
|
398
|
-
|
|
524
|
+
log3(t5("cli.config.not_configured"));
|
|
399
525
|
} else {
|
|
400
526
|
const yaml = readFileSync(CONFIG_PATH, "utf-8").trim();
|
|
401
|
-
|
|
527
|
+
log3(opts.raw ? yaml : maskSecrets(yaml));
|
|
402
528
|
}
|
|
403
529
|
});
|
|
404
|
-
cmd.command("path").description(
|
|
405
|
-
cmd.command("get").description(
|
|
530
|
+
cmd.command("path").description(t5("cli.config.path")).action(() => log3(CONFIG_PATH));
|
|
531
|
+
cmd.command("get").description(t5("cli.config.get")).argument("<key>", t5("cli.config.keys")).action((key) => {
|
|
406
532
|
const value = readKey(
|
|
407
533
|
readConfigYaml(CONFIG_PATH),
|
|
408
534
|
key === "provider" ? "provider.type" : key
|
|
409
535
|
);
|
|
410
|
-
if (value === void 0) throw new CliError(
|
|
411
|
-
|
|
536
|
+
if (value === void 0) throw new CliError(t5("cli.config.unset_key", { key }));
|
|
537
|
+
log3(value);
|
|
412
538
|
});
|
|
413
|
-
cmd.command("set").description(
|
|
539
|
+
cmd.command("set").description(t5("cli.config.set")).argument("<key>", t5("cli.config.keys")).argument("<value>", t5("cli.config.value")).action((key, value) => {
|
|
414
540
|
ensureEpochHome();
|
|
415
541
|
writeConfigYaml(CONFIG_PATH, applySet(readConfigYaml(CONFIG_PATH), key, value));
|
|
416
|
-
|
|
542
|
+
log3(`\u2705 ${key} = ${value}`);
|
|
417
543
|
});
|
|
418
|
-
cmd.command("unset").description(
|
|
544
|
+
cmd.command("unset").description(t5("cli.config.unset")).argument("<key>", t5("cli.config.keys_unset")).action((key) => {
|
|
419
545
|
ensureEpochHome();
|
|
420
546
|
writeConfigYaml(CONFIG_PATH, applyUnset(readConfigYaml(CONFIG_PATH), key));
|
|
421
|
-
|
|
547
|
+
log3(`\u2705 ${t5("cli.config.removed", { key })}`);
|
|
422
548
|
});
|
|
423
549
|
registerSchemaCommand(cmd);
|
|
424
550
|
registerSecretCommand(cmd);
|
|
425
551
|
}
|
|
426
552
|
function registerSchemaCommand(cmd) {
|
|
427
|
-
cmd.command("schema").description(
|
|
553
|
+
cmd.command("schema").description(t5("cli.config.schema")).action(async () => {
|
|
428
554
|
const { resolveProjectRoot: resolveProjectRoot4 } = await import("@epoch-agent/core");
|
|
429
555
|
const root = resolveProjectRoot4(process.cwd());
|
|
430
556
|
const { writeProjectSchemas } = await import("./schema-file-DH6X4N4K.js");
|
|
431
557
|
const written = await writeProjectSchemas(root);
|
|
432
|
-
for (const entry of written)
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
558
|
+
for (const entry of written) log3(`\u2705 ${entry.path}`);
|
|
559
|
+
log3("");
|
|
560
|
+
log3(t5("cli.config.schema_paste_hint"));
|
|
561
|
+
log3(` { "$schema": "${written[0]?.reference ?? ""}", \u2026 }`);
|
|
436
562
|
});
|
|
437
563
|
}
|
|
438
564
|
function registerSecretCommand(cmd) {
|
|
439
|
-
cmd.command("secret").description(
|
|
565
|
+
cmd.command("secret").description(t5("cli.config.secret")).option("--export", t5("cli.config.opt_export")).action(async (opts) => {
|
|
440
566
|
const { ensureSecretsReady } = await import("@epoch-agent/runtime");
|
|
441
567
|
await ensureSecretsReady();
|
|
442
568
|
const { getSecretStore } = await import("@epoch-agent/infra");
|
|
443
569
|
const store = getSecretStore();
|
|
444
|
-
if (!store) throw new CliError(
|
|
570
|
+
if (!store) throw new CliError(t5("cli.config.secret_not_ready"));
|
|
445
571
|
if (opts.export) await exportSecrets(store);
|
|
446
572
|
else await printSecretStatus(store);
|
|
447
573
|
});
|
|
448
574
|
}
|
|
449
575
|
async function printSecretStatus(store) {
|
|
450
|
-
|
|
451
|
-
`${store.encrypted ? "\u2713" : "\u26A0"} ${
|
|
576
|
+
log3(
|
|
577
|
+
`${store.encrypted ? "\u2713" : "\u26A0"} ${t5("cli.config.secret_backend", {
|
|
452
578
|
backend: store.backend,
|
|
453
579
|
detail: store.detail
|
|
454
580
|
})}`
|
|
455
581
|
);
|
|
456
582
|
const names = (await store.list()).filter((n) => API_KEY_ENV_VARS.includes(n));
|
|
457
583
|
if (names.length === 0) {
|
|
458
|
-
|
|
584
|
+
log3(t5("cli.config.secret_empty"));
|
|
459
585
|
return;
|
|
460
586
|
}
|
|
461
|
-
for (const name of names)
|
|
587
|
+
for (const name of names) log3(` ${name}`);
|
|
462
588
|
}
|
|
463
589
|
async function exportSecrets(store) {
|
|
464
590
|
const { isNonInteractive: isNonInteractive9 } = await import("@epoch-agent/core");
|
|
465
591
|
if (isNonInteractive9()) {
|
|
466
|
-
throw new CliError(
|
|
592
|
+
throw new CliError(t5("cli.config.export_needs_tty"), 1, t5("cli.config.export_needs_tty_hint"));
|
|
467
593
|
}
|
|
468
594
|
const { confirm } = await import("@inquirer/prompts");
|
|
469
|
-
|
|
470
|
-
if (!await confirm({ message:
|
|
471
|
-
|
|
595
|
+
log3(t5("cli.config.export_about_to", { path: ENV_PATH }));
|
|
596
|
+
if (!await confirm({ message: t5("cli.config.export_confirm"), default: false })) {
|
|
597
|
+
log3(t5("cli.config.cancelled"));
|
|
472
598
|
return;
|
|
473
599
|
}
|
|
474
600
|
const again = await confirm({
|
|
475
|
-
message:
|
|
601
|
+
message: t5("cli.config.export_confirm_again", { path: ENV_PATH }),
|
|
476
602
|
default: false
|
|
477
603
|
});
|
|
478
604
|
if (!again) {
|
|
479
|
-
|
|
605
|
+
log3(t5("cli.config.cancelled"));
|
|
480
606
|
return;
|
|
481
607
|
}
|
|
482
608
|
ensureEpochHome();
|
|
483
|
-
const { exportSecretsToEnv } = await import("./env-file-
|
|
609
|
+
const { exportSecretsToEnv } = await import("./env-file-ERS45V7K.js");
|
|
484
610
|
const written = await exportSecretsToEnv(store, ENV_PATH, API_KEY_ENV_VARS);
|
|
485
611
|
if (written.length === 0) {
|
|
486
|
-
|
|
612
|
+
log3(t5("cli.config.export_nothing"));
|
|
487
613
|
return;
|
|
488
614
|
}
|
|
489
|
-
|
|
490
|
-
|
|
615
|
+
log3(`\u2705 ${t5("cli.config.export_done", { count: written.length, path: ENV_PATH })}`);
|
|
616
|
+
log3(t5("cli.config.export_done_note"));
|
|
491
617
|
}
|
|
492
618
|
function applySet(yaml, key, value) {
|
|
493
619
|
switch (key) {
|
|
494
620
|
case "provider":
|
|
495
621
|
if (!isProviderType(value)) {
|
|
496
622
|
throw new CliError(
|
|
497
|
-
|
|
623
|
+
t5("cli.config.unknown_provider", { value }),
|
|
498
624
|
1,
|
|
499
|
-
|
|
625
|
+
t5("cli.config.supported", { list: PROVIDER_TYPES.join(", ") })
|
|
500
626
|
);
|
|
501
627
|
}
|
|
502
628
|
return yaml.replace(/^(\s*)type:.*$/m, `$1type: ${value}`);
|
|
@@ -505,134 +631,146 @@ function applySet(yaml, key, value) {
|
|
|
505
631
|
case "permission":
|
|
506
632
|
if (!isPermissionLevel(value)) {
|
|
507
633
|
throw new CliError(
|
|
508
|
-
|
|
634
|
+
t5("cli.config.unknown_permission", { value }),
|
|
509
635
|
1,
|
|
510
|
-
|
|
636
|
+
t5("cli.config.supported", { list: PERMISSION_LEVELS.join(", ") })
|
|
511
637
|
);
|
|
512
638
|
}
|
|
513
639
|
return setScalar(yaml, "permission", value);
|
|
514
640
|
case "shell": {
|
|
515
641
|
if (!SHELL_KINDS.includes(value)) {
|
|
516
642
|
throw new CliError(
|
|
517
|
-
|
|
643
|
+
t5("cli.config.unknown_shell", { value }),
|
|
518
644
|
1,
|
|
519
|
-
|
|
645
|
+
t5("cli.config.supported", { list: SHELL_KINDS.join(", ") })
|
|
520
646
|
);
|
|
521
647
|
}
|
|
522
648
|
return setScalar(yaml, "shell", value);
|
|
523
649
|
}
|
|
524
|
-
case "models.utility":
|
|
650
|
+
case "models.utility":
|
|
651
|
+
case "fallback.model": {
|
|
525
652
|
if (!parseModelRef(value)) {
|
|
526
|
-
throw new CliError(
|
|
653
|
+
throw new CliError(t5("cli.config.utility_empty"), 1, t5("cli.config.utility_hint"));
|
|
527
654
|
}
|
|
528
|
-
|
|
655
|
+
const [section, field] = key.split(".");
|
|
656
|
+
return setSectionField(yaml, section, field, value);
|
|
529
657
|
}
|
|
530
658
|
default: {
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
659
|
+
const scalar = SCALAR_FIELDS[key];
|
|
660
|
+
if (scalar) {
|
|
661
|
+
if (!scalar(value)) throw new CliError(t5("cli.config.bad_value", { key, value }));
|
|
662
|
+
return setScalar(yaml, key, value);
|
|
663
|
+
}
|
|
664
|
+
const [section, field] = splitSection(key);
|
|
665
|
+
const validate = field === void 0 ? void 0 : SECTION_FIELDS[section]?.[field];
|
|
666
|
+
if (!validate || field === void 0)
|
|
534
667
|
throw new CliError(
|
|
535
|
-
|
|
668
|
+
t5("cli.config.unknown_key", { key }),
|
|
536
669
|
1,
|
|
537
|
-
|
|
670
|
+
t5("cli.config.supported", { list: knownKeys() })
|
|
538
671
|
);
|
|
539
|
-
if (!validate(value)) throw new CliError(
|
|
540
|
-
return setSectionField(yaml,
|
|
672
|
+
if (!validate(value)) throw new CliError(t5("cli.config.bad_value", { key, value }));
|
|
673
|
+
return setSectionField(yaml, section, field, value);
|
|
541
674
|
}
|
|
542
675
|
}
|
|
543
676
|
}
|
|
544
677
|
function applyUnset(yaml, key) {
|
|
545
678
|
if (key === "model" || key === "permission" || key === "shell") return unsetScalar(yaml, key);
|
|
679
|
+
if (SCALAR_FIELDS[key]) return unsetScalar(yaml, key);
|
|
546
680
|
if (key === "models.utility") return unsetSectionField(yaml, "models", "utility");
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
681
|
+
if (key === "fallback.model") return unsetSectionField(yaml, "fallback", "model");
|
|
682
|
+
const [section, field] = splitSection(key);
|
|
683
|
+
if (field !== void 0 && SECTION_FIELDS[section]?.[field]) {
|
|
684
|
+
return unsetSectionField(yaml, section, field);
|
|
685
|
+
}
|
|
686
|
+
throw new CliError(t5("cli.config.cannot_unset", { key }), 1, t5("cli.config.can_unset"));
|
|
550
687
|
}
|
|
551
|
-
function
|
|
552
|
-
|
|
688
|
+
function splitSection(key) {
|
|
689
|
+
const dot = key.indexOf(".");
|
|
690
|
+
return dot < 0 ? [key, void 0] : [key.slice(0, dot), key.slice(dot + 1)];
|
|
553
691
|
}
|
|
554
692
|
// src/commands/mcp.ts
|
|
555
|
-
import { t as
|
|
693
|
+
import { t as t6, uiDateLocale } from "@epoch-agent/infra";
|
|
556
694
|
import {
|
|
557
695
|
listMcpServers,
|
|
558
696
|
mcpLogin,
|
|
559
697
|
mcpLogout,
|
|
560
698
|
probeMcpServer
|
|
561
699
|
} from "@epoch-agent/runtime";
|
|
562
|
-
var
|
|
700
|
+
var log4 = (msg) => process.stdout.write(msg + "\n");
|
|
563
701
|
var warn = (msg) => process.stderr.write(msg + "\n");
|
|
564
702
|
function stateLabel(state) {
|
|
565
703
|
switch (state) {
|
|
566
704
|
case "not-applicable":
|
|
567
|
-
return
|
|
705
|
+
return t6("mcp.state_not_applicable");
|
|
568
706
|
case "bearer-header":
|
|
569
|
-
return
|
|
707
|
+
return t6("mcp.state_bearer");
|
|
570
708
|
case "authorized":
|
|
571
|
-
return
|
|
709
|
+
return t6("mcp.state_authorized");
|
|
572
710
|
case "refreshable":
|
|
573
|
-
return
|
|
711
|
+
return t6("mcp.state_refreshable");
|
|
574
712
|
case "logged-out":
|
|
575
|
-
return
|
|
713
|
+
return t6("mcp.state_logged_out");
|
|
576
714
|
}
|
|
577
715
|
}
|
|
578
716
|
function registerMcpCommand(program2) {
|
|
579
|
-
const cmd = program2.command("mcp").description(
|
|
580
|
-
cmd.command("list", { isDefault: true }).description(
|
|
581
|
-
cmd.command("status").description(
|
|
717
|
+
const cmd = program2.command("mcp").description(t6("mcp.cmd_root"));
|
|
718
|
+
cmd.command("list", { isDefault: true }).description(t6("mcp.cmd_list")).action(() => guardAsync(() => printList()));
|
|
719
|
+
cmd.command("status").description(t6("mcp.cmd_status")).argument("<name>", t6("mcp.arg_name")).action(
|
|
582
720
|
(name) => guardAsync(async () => {
|
|
583
|
-
|
|
721
|
+
log4(t6("mcp.connecting", { name }));
|
|
584
722
|
const result = await probeMcpServer(name, { homeDir: EPOCH_HOME });
|
|
585
723
|
if (!result.connected) {
|
|
586
724
|
warn(
|
|
587
|
-
result.error ?
|
|
725
|
+
result.error ? t6("mcp.connect_failed_reason", { reason: result.error }) : t6("mcp.connect_failed")
|
|
588
726
|
);
|
|
589
|
-
if (result.needsLogin) warn(
|
|
727
|
+
if (result.needsLogin) warn(t6("mcp.needs_login", { name }));
|
|
590
728
|
process.exit(1);
|
|
591
729
|
}
|
|
592
|
-
|
|
593
|
-
for (const tool of result.toolNames)
|
|
730
|
+
log4(`\u2713 ${t6("mcp.connected", { count: result.toolNames.length })}`);
|
|
731
|
+
for (const tool of result.toolNames) log4(` ${tool}`);
|
|
594
732
|
})
|
|
595
733
|
);
|
|
596
|
-
cmd.command("login").description(
|
|
734
|
+
cmd.command("login").description(t6("mcp.cmd_login")).argument("<name>", t6("mcp.arg_name")).option("--port <port>", t6("mcp.opt_port")).option("--scope <scope>", t6("mcp.opt_scope")).action(
|
|
597
735
|
(name, opts) => guardAsync(async () => {
|
|
598
736
|
const port = opts.port === void 0 ? void 0 : parsePort(opts.port);
|
|
599
737
|
const { refreshedOnly } = await mcpLogin(name, {
|
|
600
738
|
homeDir: EPOCH_HOME,
|
|
601
739
|
...port === void 0 ? {} : { port },
|
|
602
740
|
...opts.scope === void 0 ? {} : { scope: opts.scope },
|
|
603
|
-
print:
|
|
741
|
+
print: log4
|
|
604
742
|
});
|
|
605
|
-
|
|
606
|
-
refreshedOnly ? `\u2713 ${
|
|
743
|
+
log4(
|
|
744
|
+
refreshedOnly ? `\u2713 ${t6("mcp.refreshed", { name })}` : `\u2713 ${t6("mcp.authorized", { name })}`
|
|
607
745
|
);
|
|
608
746
|
})
|
|
609
747
|
);
|
|
610
|
-
cmd.command("logout").description(
|
|
748
|
+
cmd.command("logout").description(t6("mcp.cmd_logout")).argument("<name>", t6("mcp.arg_name")).action(
|
|
611
749
|
(name) => guardAsync(async () => {
|
|
612
750
|
const removed = await mcpLogout(name, { homeDir: EPOCH_HOME });
|
|
613
|
-
|
|
751
|
+
log4(removed ? `\u2713 ${t6("mcp.removed", { name })}` : t6("mcp.nothing_stored", { name }));
|
|
614
752
|
})
|
|
615
753
|
);
|
|
616
754
|
}
|
|
617
755
|
async function printList() {
|
|
618
756
|
const { servers, issues, configPath: configPath2 } = await listMcpServers({ homeDir: EPOCH_HOME });
|
|
619
757
|
for (const issue of issues)
|
|
620
|
-
warn(
|
|
621
|
-
|
|
758
|
+
warn(t6("mcp.config_issue", { path: issue.path, message: issue.message }));
|
|
759
|
+
log4(t6("mcp.list_head", { path: configPath2 }));
|
|
622
760
|
if (servers.length === 0) {
|
|
623
|
-
|
|
761
|
+
log4(t6("mcp.list_empty"));
|
|
624
762
|
return;
|
|
625
763
|
}
|
|
626
|
-
for (const s of servers)
|
|
764
|
+
for (const s of servers) log4(` ${describe(s)}`);
|
|
627
765
|
}
|
|
628
766
|
function describe(s) {
|
|
629
767
|
const target = s.config.transport === "stdio" ? s.config.command : s.config.url;
|
|
630
768
|
const parts = [
|
|
631
769
|
`${s.config.name} [${s.config.transport}] ${target ?? ""}`,
|
|
632
|
-
|
|
770
|
+
t6("mcp.auth_line", { state: stateLabel(s.auth.state) })
|
|
633
771
|
];
|
|
634
772
|
if (s.auth.expiresAt) {
|
|
635
|
-
parts[1] +=
|
|
773
|
+
parts[1] += t6("mcp.expires_at", {
|
|
636
774
|
when: new Date(s.auth.expiresAt).toLocaleString(uiDateLocale())
|
|
637
775
|
});
|
|
638
776
|
}
|
|
@@ -641,42 +779,42 @@ function describe(s) {
|
|
|
641
779
|
function parsePort(raw) {
|
|
642
780
|
const port = Number(raw);
|
|
643
781
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
644
|
-
throw new Error(
|
|
782
|
+
throw new Error(t6("mcp.bad_port", { raw }));
|
|
645
783
|
}
|
|
646
784
|
return port;
|
|
647
785
|
}
|
|
648
786
|
function guardAsync(fn) {
|
|
649
787
|
fn().catch((err2) => {
|
|
650
|
-
warn(
|
|
788
|
+
warn(t6("mcp.error", { message: err2 instanceof Error ? err2.message : String(err2) }));
|
|
651
789
|
process.exit(1);
|
|
652
790
|
});
|
|
653
791
|
}
|
|
654
792
|
// src/commands/model.ts
|
|
655
793
|
import { input, password, select } from "@inquirer/prompts";
|
|
656
794
|
import { isNonInteractive, readProviderKeyEnv } from "@epoch-agent/core";
|
|
657
|
-
import { t as
|
|
795
|
+
import { t as t7 } from "@epoch-agent/infra";
|
|
658
796
|
import {
|
|
659
797
|
apiKeyEnvVar,
|
|
660
798
|
PROVIDER_INFOS,
|
|
661
799
|
PROVIDER_TYPES as PROVIDER_TYPES2
|
|
662
800
|
} from "@epoch-agent/protocol";
|
|
663
801
|
import { providerLabel } from "@epoch-agent/runtime";
|
|
664
|
-
var
|
|
802
|
+
var log5 = (msg) => process.stdout.write(msg + "\n");
|
|
665
803
|
var CUSTOM = "__custom__";
|
|
666
804
|
function registerModelCommand(program2) {
|
|
667
|
-
program2.command("model").description(
|
|
805
|
+
program2.command("model").description(t7("model.cmd_root")).option("-r, --refresh", t7("model.opt_refresh")).action(async (opts) => {
|
|
668
806
|
ensureEpochHome();
|
|
669
807
|
if (opts.refresh) return refreshCache();
|
|
670
808
|
if (isNonInteractive()) {
|
|
671
|
-
throw new CliError(
|
|
809
|
+
throw new CliError(t7("model.needs_tty"), 1, t7("model.needs_tty_hint"));
|
|
672
810
|
}
|
|
673
811
|
await runWizard();
|
|
674
812
|
});
|
|
675
813
|
}
|
|
676
814
|
async function refreshCache() {
|
|
677
815
|
const { clearModelCache } = await import("@epoch-agent/core");
|
|
678
|
-
for (const
|
|
679
|
-
|
|
816
|
+
for (const t40 of PROVIDER_TYPES2) clearModelCache(EPOCH_HOME, t40);
|
|
817
|
+
log5(t7("model.cache_cleared"));
|
|
680
818
|
}
|
|
681
819
|
function readCurrentSelection() {
|
|
682
820
|
const yaml = readConfigYaml(CONFIG_PATH);
|
|
@@ -686,7 +824,7 @@ async function runWizard() {
|
|
|
686
824
|
await ensureSecrets();
|
|
687
825
|
const current = readCurrentSelection();
|
|
688
826
|
const provider = await select({
|
|
689
|
-
message:
|
|
827
|
+
message: t7("model.pick_provider"),
|
|
690
828
|
choices: providerChoices(current.provider),
|
|
691
829
|
pageSize: listPageSize(PROVIDER_INFOS.filter((p) => p.interactive).length),
|
|
692
830
|
...isKnownProvider(current.provider) ? { default: current.provider } : {}
|
|
@@ -695,42 +833,42 @@ async function runWizard() {
|
|
|
695
833
|
const keyForDiscover = await resolveApiKey(provider);
|
|
696
834
|
const { discoverModels } = await import("@epoch-agent/core");
|
|
697
835
|
const result = await discoverModels(EPOCH_HOME, provider, keyForDiscover, baseUrl);
|
|
698
|
-
|
|
836
|
+
log5(describeDiscovery(result.source, result.models.length, keyForDiscover !== ""));
|
|
699
837
|
const model = await pickModel(result.models, result.source, carriedModel(provider, current));
|
|
700
838
|
let yaml = readConfigYaml(CONFIG_PATH);
|
|
701
839
|
yaml = setSectionField(yaml, "provider", "type", provider);
|
|
702
840
|
if (baseUrl) yaml = setSectionField(yaml, "provider", "baseUrl", baseUrl);
|
|
703
841
|
yaml = setScalar(yaml, "model", model);
|
|
704
842
|
writeConfigYaml(CONFIG_PATH, yaml);
|
|
705
|
-
|
|
843
|
+
log5(`
|
|
706
844
|
\u2705 ${provider} / ${model}
|
|
707
|
-
${
|
|
845
|
+
${t7("model.ready")}`);
|
|
708
846
|
}
|
|
709
847
|
async function ensureSecrets() {
|
|
710
848
|
try {
|
|
711
849
|
const { ensureSecretsReady } = await import("@epoch-agent/runtime");
|
|
712
850
|
await ensureSecretsReady();
|
|
713
851
|
} catch (err2) {
|
|
714
|
-
|
|
852
|
+
log5(t7("model.secret_init_failed", { reason: describeError(err2) }));
|
|
715
853
|
}
|
|
716
854
|
}
|
|
717
855
|
async function resolveApiKey(provider) {
|
|
718
856
|
const envKey = apiKeyEnvVar(provider);
|
|
719
857
|
if (!envKey) return "";
|
|
720
858
|
const existing = readProviderKeyEnv(EPOCH_HOME)[envKey];
|
|
721
|
-
const message = existing ?
|
|
859
|
+
const message = existing ? t7("model.api_key_existing", { envKey }) : t7("model.api_key", { envKey });
|
|
722
860
|
const typed = (await password({ message, mask: "*" })).trim();
|
|
723
861
|
if (!typed) return existing ?? "";
|
|
724
862
|
await saveApiKey(envKey, typed);
|
|
725
863
|
return typed;
|
|
726
864
|
}
|
|
727
865
|
async function saveApiKey(envKey, apiKey) {
|
|
728
|
-
const result = await
|
|
729
|
-
|
|
866
|
+
const result = await writeProviderSecret(envKey, apiKey, ENV_PATH);
|
|
867
|
+
log5(result.encrypted ? t7("model.key_stored", { detail: result.detail }) : ` \u26A0 ${result.detail}`);
|
|
730
868
|
}
|
|
731
869
|
async function askBaseUrl() {
|
|
732
870
|
const answer = (await input({
|
|
733
|
-
message:
|
|
871
|
+
message: t7("model.ask_base_url"),
|
|
734
872
|
default: "http://localhost:8080/v1"
|
|
735
873
|
})).trim();
|
|
736
874
|
return answer || void 0;
|
|
@@ -739,17 +877,17 @@ async function pickModel(models, source, currentModel) {
|
|
|
739
877
|
const choices3 = modelChoices(models, source, currentModel);
|
|
740
878
|
if (choices3.length === 0) {
|
|
741
879
|
const fallback2 = currentModel ?? "gpt-4o-mini";
|
|
742
|
-
return await input({ message:
|
|
880
|
+
return await input({ message: t7("model.type_model"), default: fallback2 }) || fallback2;
|
|
743
881
|
}
|
|
744
882
|
const picked = await select({
|
|
745
|
-
message:
|
|
883
|
+
message: t7("model.pick_model"),
|
|
746
884
|
choices: choices3,
|
|
747
885
|
pageSize: listPageSize(choices3.length),
|
|
748
886
|
...currentModel && models.includes(currentModel) ? { default: currentModel } : {}
|
|
749
887
|
});
|
|
750
888
|
if (picked !== CUSTOM) return picked;
|
|
751
889
|
const fallback = currentModel ?? models[0] ?? "gpt-4o-mini";
|
|
752
|
-
return await input({ message:
|
|
890
|
+
return await input({ message: t7("model.type_model"), default: fallback }) || fallback;
|
|
753
891
|
}
|
|
754
892
|
function isKnownProvider(value) {
|
|
755
893
|
return PROVIDER_INFOS.some((p) => p.interactive && p.type === value);
|
|
@@ -761,7 +899,7 @@ function providerChoices(currentProvider) {
|
|
|
761
899
|
return PROVIDER_INFOS.filter((p) => p.interactive).map((p) => {
|
|
762
900
|
const label = providerLabel(p);
|
|
763
901
|
return {
|
|
764
|
-
name: p.type === currentProvider ?
|
|
902
|
+
name: p.type === currentProvider ? t7("model.current", { label }) : label,
|
|
765
903
|
value: p.type
|
|
766
904
|
};
|
|
767
905
|
});
|
|
@@ -769,16 +907,16 @@ function providerChoices(currentProvider) {
|
|
|
769
907
|
function modelChoices(models, source, currentModel) {
|
|
770
908
|
if (models.length === 0) return [];
|
|
771
909
|
const choices3 = models.map((m) => ({
|
|
772
|
-
name: m === currentModel ?
|
|
910
|
+
name: m === currentModel ? t7("model.current", { label: m }) : m,
|
|
773
911
|
value: m
|
|
774
912
|
}));
|
|
775
913
|
if (currentModel && !models.includes(currentModel)) {
|
|
776
914
|
choices3.unshift({
|
|
777
|
-
name:
|
|
915
|
+
name: t7("model.current_not_listed", { label: currentModel }),
|
|
778
916
|
value: currentModel
|
|
779
917
|
});
|
|
780
918
|
}
|
|
781
|
-
if (source !== "live") choices3.push({ name:
|
|
919
|
+
if (source !== "live") choices3.push({ name: t7("model.custom_entry"), value: CUSTOM });
|
|
782
920
|
return choices3;
|
|
783
921
|
}
|
|
784
922
|
function listPageSize(itemCount, rows = process.stdout.rows) {
|
|
@@ -786,10 +924,10 @@ function listPageSize(itemCount, rows = process.stdout.rows) {
|
|
|
786
924
|
return Math.min(itemCount, Math.max(3, height - 3));
|
|
787
925
|
}
|
|
788
926
|
function describeDiscovery(source, count, hasKey) {
|
|
789
|
-
if (source === "live") return
|
|
790
|
-
const why = hasKey ?
|
|
791
|
-
const what = source === "cache" ?
|
|
792
|
-
return
|
|
927
|
+
if (source === "live") return t7("model.from_api", { count });
|
|
928
|
+
const why = hasKey ? t7("model.why_fetch_failed") : t7("model.why_no_key");
|
|
929
|
+
const what = source === "cache" ? t7("model.fallback_cache") : t7("model.fallback_static");
|
|
930
|
+
return t7("model.fallback_line", { why, what });
|
|
793
931
|
}
|
|
794
932
|
function describeError(err2) {
|
|
795
933
|
return err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -818,98 +956,98 @@ import {
|
|
|
818
956
|
marketplacesPath,
|
|
819
957
|
pluginsDir,
|
|
820
958
|
pluginsStatePath,
|
|
821
|
-
t as
|
|
959
|
+
t as t8
|
|
822
960
|
} from "@epoch-agent/infra";
|
|
823
|
-
var
|
|
961
|
+
var log6 = (msg) => {
|
|
824
962
|
process.stdout.write(msg + "\n");
|
|
825
963
|
};
|
|
826
964
|
function locations() {
|
|
827
965
|
return { pluginsDir: pluginsDir(EPOCH_HOME), statePath: pluginsStatePath(EPOCH_HOME) };
|
|
828
966
|
}
|
|
829
967
|
function registerPluginCommand(program2) {
|
|
830
|
-
const cmd = program2.command("plugin").description(
|
|
968
|
+
const cmd = program2.command("plugin").description(t8("plugin.cmd_root"));
|
|
831
969
|
registerBasics(cmd);
|
|
832
970
|
registerLifecycle(cmd);
|
|
833
971
|
registerMarketplace(cmd);
|
|
834
972
|
}
|
|
835
973
|
function registerBasics(cmd) {
|
|
836
|
-
cmd.command("list", { isDefault: true }).alias("ls").description(
|
|
974
|
+
cmd.command("list", { isDefault: true }).alias("ls").description(t8("plugin.cmd_list")).option("--verbose", t8("plugin.opt_verbose")).action((opts) => {
|
|
837
975
|
printList2(opts.verbose === true);
|
|
838
976
|
});
|
|
839
|
-
cmd.command("install").alias("add").description(
|
|
977
|
+
cmd.command("install").alias("add").description(t8("plugin.cmd_install")).argument("<source>", t8("plugin.arg_source")).option("-y, --yes", t8("plugin.opt_yes_install")).action(async (source, opts) => {
|
|
840
978
|
await runInstall(source, opts.yes === true);
|
|
841
979
|
});
|
|
842
|
-
cmd.command("validate").description(
|
|
980
|
+
cmd.command("validate").description(t8("plugin.cmd_validate")).argument("[path]", t8("plugin.arg_dir")).action((path) => {
|
|
843
981
|
validateDir(path ?? process.cwd());
|
|
844
982
|
});
|
|
845
983
|
}
|
|
846
984
|
function registerLifecycle(cmd) {
|
|
847
|
-
cmd.command("uninstall").alias("rm").description(
|
|
985
|
+
cmd.command("uninstall").alias("rm").description(t8("plugin.cmd_uninstall")).argument("<name>", t8("plugin.arg_name")).action(async (name) => {
|
|
848
986
|
const outcome = await uninstallPlugin(name, { statePath: pluginsStatePath(EPOCH_HOME) });
|
|
849
987
|
if (!outcome.ok) throw new CliError(outcome.reason);
|
|
850
|
-
|
|
988
|
+
log6(`\u2713 ${outcome.message}`);
|
|
851
989
|
});
|
|
852
|
-
cmd.command("disable").description(
|
|
990
|
+
cmd.command("disable").description(t8("plugin.cmd_disable")).argument("<name>", t8("plugin.arg_name")).action(async (name) => {
|
|
853
991
|
await toggle(name, false);
|
|
854
992
|
});
|
|
855
|
-
cmd.command("enable").description(
|
|
993
|
+
cmd.command("enable").description(t8("plugin.cmd_enable")).argument("<name>", t8("plugin.arg_name")).action(async (name) => {
|
|
856
994
|
await toggle(name, true);
|
|
857
995
|
});
|
|
858
|
-
cmd.command("update").description(
|
|
996
|
+
cmd.command("update").description(t8("plugin.cmd_update")).argument("<name>", t8("plugin.arg_name")).option("-y, --yes", t8("plugin.opt_yes")).action(async (name, opts) => {
|
|
859
997
|
await runUpdate(name, opts.yes === true);
|
|
860
998
|
});
|
|
861
999
|
}
|
|
862
1000
|
function registerMarketplace(cmd) {
|
|
863
|
-
const market = cmd.command("marketplace").alias("mp").description(
|
|
864
|
-
market.command("add").description(
|
|
1001
|
+
const market = cmd.command("marketplace").alias("mp").description(t8("plugin.cmd_market"));
|
|
1002
|
+
market.command("add").description(t8("plugin.cmd_market_add")).argument("<source>", t8("plugin.arg_market_source")).action(async (source) => {
|
|
865
1003
|
const outcome = await addMarketplace(source, { statePath: marketplacesPath(EPOCH_HOME) });
|
|
866
1004
|
if (!outcome.ok) throw new CliError(outcome.reason);
|
|
867
1005
|
const count = outcome.record.catalog.plugins.length;
|
|
868
|
-
|
|
869
|
-
|
|
1006
|
+
log6(`\u2713 ${t8("plugin.market_added", { name: outcome.record.name, count })}`);
|
|
1007
|
+
log6(t8("plugin.market_added_hint"));
|
|
870
1008
|
});
|
|
871
|
-
market.command("list", { isDefault: true }).alias("ls").description(
|
|
872
|
-
market.command("remove").alias("rm").description(
|
|
1009
|
+
market.command("list", { isDefault: true }).alias("ls").description(t8("plugin.cmd_market_list")).action(printMarketplaces);
|
|
1010
|
+
market.command("remove").alias("rm").description(t8("plugin.cmd_market_remove")).argument("<name>", t8("plugin.arg_market_name")).action(async (name) => {
|
|
873
1011
|
const outcome = await removeMarketplace(name, { statePath: marketplacesPath(EPOCH_HOME) });
|
|
874
1012
|
if (!outcome.ok)
|
|
875
|
-
throw new CliError(outcome.reason ??
|
|
876
|
-
|
|
1013
|
+
throw new CliError(outcome.reason ?? t8("plugin.market_remove_failed", { name }));
|
|
1014
|
+
log6(`\u2713 ${t8("plugin.market_removed", { name })}`);
|
|
877
1015
|
});
|
|
878
|
-
market.command("update").description(
|
|
1016
|
+
market.command("update").description(t8("plugin.cmd_market_update")).argument("<name>", t8("plugin.arg_market_name")).action(async (name) => {
|
|
879
1017
|
const outcome = await updateMarketplace(name, { statePath: marketplacesPath(EPOCH_HOME) });
|
|
880
1018
|
if (!outcome.ok) throw new CliError(outcome.reason);
|
|
881
|
-
|
|
882
|
-
`\u2713 ${
|
|
1019
|
+
log6(
|
|
1020
|
+
`\u2713 ${t8("plugin.market_updated", {
|
|
883
1021
|
name,
|
|
884
1022
|
count: outcome.record.catalog.plugins.length
|
|
885
1023
|
})}`
|
|
886
1024
|
);
|
|
887
1025
|
});
|
|
888
|
-
cmd.command("search").description(
|
|
1026
|
+
cmd.command("search").description(t8("plugin.cmd_search")).argument("[keyword]", t8("plugin.arg_keyword")).action((keyword) => {
|
|
889
1027
|
printSearch(keyword ?? "");
|
|
890
1028
|
});
|
|
891
1029
|
}
|
|
892
1030
|
function printList2(verbose) {
|
|
893
1031
|
const artifacts = loadPlugins({ statePath: pluginsStatePath(EPOCH_HOME) });
|
|
894
1032
|
if (artifacts.loaded.length === 0 && artifacts.skipped.length === 0) {
|
|
895
|
-
|
|
896
|
-
|
|
1033
|
+
log6(t8("plugin.none_installed"));
|
|
1034
|
+
log6(t8("plugin.none_installed_hint"));
|
|
897
1035
|
return;
|
|
898
1036
|
}
|
|
899
|
-
|
|
1037
|
+
log6(`${t8("plugin.installed_head", { path: pluginsStatePath(EPOCH_HOME) })}
|
|
900
1038
|
`);
|
|
901
1039
|
for (const plugin of artifacts.loaded) {
|
|
902
|
-
|
|
1040
|
+
log6(loadedLine(plugin));
|
|
903
1041
|
if (!verbose) continue;
|
|
904
1042
|
for (const line of renderInventory(scanPluginDir(plugin.dir, plugin.name))) {
|
|
905
|
-
|
|
1043
|
+
log6(` ${line}`);
|
|
906
1044
|
}
|
|
907
|
-
|
|
1045
|
+
log6("");
|
|
908
1046
|
}
|
|
909
|
-
for (const skipped of artifacts.skipped)
|
|
910
|
-
if (artifacts.skipped.length > 0)
|
|
1047
|
+
for (const skipped of artifacts.skipped) log6(skippedLine(skipped));
|
|
1048
|
+
if (artifacts.skipped.length > 0) log6(`
|
|
911
1049
|
${markLegend()}`);
|
|
912
|
-
for (const detail of issueDetails(artifacts.issues))
|
|
1050
|
+
for (const detail of issueDetails(artifacts.issues)) log6(`
|
|
913
1051
|
\u26A0 ${detail}`);
|
|
914
1052
|
}
|
|
915
1053
|
function loadedLine(plugin) {
|
|
@@ -918,7 +1056,7 @@ function loadedLine(plugin) {
|
|
|
918
1056
|
function countsLine(counts) {
|
|
919
1057
|
const parts = [];
|
|
920
1058
|
const add = (n, key) => {
|
|
921
|
-
if (n > 0) parts.push(
|
|
1059
|
+
if (n > 0) parts.push(t8(key, { count: n }));
|
|
922
1060
|
};
|
|
923
1061
|
add(counts.commands, "plugin.n_commands");
|
|
924
1062
|
add(counts.roles, "plugin.n_roles");
|
|
@@ -926,84 +1064,84 @@ function countsLine(counts) {
|
|
|
926
1064
|
add(counts.hooks, "plugin.n_hooks");
|
|
927
1065
|
add(counts.denyRules, "plugin.n_deny");
|
|
928
1066
|
add(counts.mcpServers, "plugin.n_mcp");
|
|
929
|
-
return parts.length === 0 ?
|
|
1067
|
+
return parts.length === 0 ? t8("plugin.brings_nothing") : parts.join(" \xB7 ");
|
|
930
1068
|
}
|
|
931
1069
|
function markLegend() {
|
|
932
|
-
return
|
|
1070
|
+
return t8("plugin.mark_legend");
|
|
933
1071
|
}
|
|
934
1072
|
function skippedLine(skipped) {
|
|
935
1073
|
return ` ${skipped.disabled ? "\u25CB" : "\u2717"} ${skipped.name} ${skipped.reason}`;
|
|
936
1074
|
}
|
|
937
1075
|
async function runInstall(input2, yes) {
|
|
938
1076
|
if (!yes && isNonInteractive2()) {
|
|
939
|
-
throw new CliError(
|
|
1077
|
+
throw new CliError(t8("plugin.install_needs_tty"), 1, t8("plugin.install_needs_tty_hint"));
|
|
940
1078
|
}
|
|
941
1079
|
const ref = resolveMarketplaceRef(input2, marketplacesPath(EPOCH_HOME));
|
|
942
1080
|
if (ref && !ref.ok) throw new CliError(ref.reason);
|
|
943
1081
|
const source = ref?.ok ? ref.ref.source : input2;
|
|
944
|
-
if (ref?.ok)
|
|
1082
|
+
if (ref?.ok) log6(`${input2} \u2192 ${source}`);
|
|
945
1083
|
const outcome = await installPlugin(source, {
|
|
946
1084
|
...locations(),
|
|
947
1085
|
epochVersion: currentEpochVersion(),
|
|
948
|
-
onProgress:
|
|
1086
|
+
onProgress: log6,
|
|
949
1087
|
...ref?.ok ? { marketplace: ref.ref.marketplace } : {},
|
|
950
1088
|
...yes ? {} : { confirm: askInstall }
|
|
951
1089
|
});
|
|
952
1090
|
if (!outcome.ok) {
|
|
953
1091
|
if (outcome.cancelled) {
|
|
954
|
-
|
|
1092
|
+
log6(t8("plugin.nothing_installed"));
|
|
955
1093
|
return;
|
|
956
1094
|
}
|
|
957
|
-
throw new CliError(
|
|
1095
|
+
throw new CliError(t8("plugin.install_failed", { source, reason: outcome.reason }));
|
|
958
1096
|
}
|
|
959
|
-
|
|
1097
|
+
log6(
|
|
960
1098
|
`
|
|
961
|
-
\u2713 ${
|
|
1099
|
+
\u2713 ${t8("plugin.installed", {
|
|
962
1100
|
name: outcome.record.name,
|
|
963
1101
|
version: outcome.record.version,
|
|
964
1102
|
path: outcome.record.path
|
|
965
1103
|
})}`
|
|
966
1104
|
);
|
|
967
1105
|
if (outcome.record.linked) {
|
|
968
|
-
|
|
1106
|
+
log6(t8("plugin.installed_linked"));
|
|
969
1107
|
}
|
|
970
|
-
|
|
1108
|
+
log6(t8("plugin.installed_restart"));
|
|
971
1109
|
}
|
|
972
1110
|
async function runUpdate(name, yes) {
|
|
973
1111
|
if (!yes && isNonInteractive2()) {
|
|
974
|
-
throw new CliError(
|
|
1112
|
+
throw new CliError(t8("plugin.update_needs_tty"), 1, t8("plugin.update_needs_tty_hint"));
|
|
975
1113
|
}
|
|
976
1114
|
const outcome = await updatePlugin(name, {
|
|
977
1115
|
...locations(),
|
|
978
1116
|
epochVersion: currentEpochVersion(),
|
|
979
|
-
onProgress:
|
|
1117
|
+
onProgress: log6,
|
|
980
1118
|
...yes ? {} : { confirm: askInstall }
|
|
981
1119
|
});
|
|
982
1120
|
if (!outcome.ok) throw new CliError(outcome.reason);
|
|
983
|
-
|
|
984
|
-
`\u2713 ${"message" in outcome ? outcome.message :
|
|
1121
|
+
log6(
|
|
1122
|
+
`\u2713 ${"message" in outcome ? outcome.message : t8("plugin.updated_to", { version: outcome.record.version })}`
|
|
985
1123
|
);
|
|
986
1124
|
}
|
|
987
1125
|
async function askInstall(preview2) {
|
|
988
|
-
for (const line of renderPreview(preview2))
|
|
1126
|
+
for (const line of renderPreview(preview2)) log6(line);
|
|
989
1127
|
const { confirm } = await import("@inquirer/prompts");
|
|
990
|
-
return confirm({ message:
|
|
1128
|
+
return confirm({ message: t8("plugin.confirm_install"), default: false });
|
|
991
1129
|
}
|
|
992
1130
|
function renderPreview(preview2) {
|
|
993
1131
|
const { manifest, source, inventory } = preview2;
|
|
994
1132
|
const lines = [""];
|
|
995
|
-
lines.push(
|
|
1133
|
+
lines.push(t8("plugin.will_install", { name: manifest.name, version: manifest.version }));
|
|
996
1134
|
if (manifest.description) lines.push(` ${manifest.description}`);
|
|
997
|
-
lines.push(
|
|
998
|
-
if (source.sha256) lines.push(
|
|
999
|
-
if (manifest.author) lines.push(
|
|
1000
|
-
if (manifest.homepage) lines.push(
|
|
1135
|
+
lines.push(t8("plugin.preview_source", { raw: source.raw, type: source.type }));
|
|
1136
|
+
if (source.sha256) lines.push(t8("plugin.preview_sha", { sha: source.sha256 }));
|
|
1137
|
+
if (manifest.author) lines.push(t8("plugin.preview_author", { name: manifest.author.name }));
|
|
1138
|
+
if (manifest.homepage) lines.push(t8("plugin.preview_homepage", { url: manifest.homepage }));
|
|
1001
1139
|
if (preview2.versionUnmet) {
|
|
1002
1140
|
const { required, current } = preview2.versionUnmet;
|
|
1003
|
-
lines.push(` ${
|
|
1141
|
+
lines.push(` ${t8("plugin.preview_version_unmet", { required, current })}`);
|
|
1004
1142
|
}
|
|
1005
1143
|
lines.push("");
|
|
1006
|
-
lines.push(
|
|
1144
|
+
lines.push(t8("plugin.brings_head_indented"));
|
|
1007
1145
|
for (const line of renderInventory(inventory)) lines.push(` ${line}`);
|
|
1008
1146
|
lines.push("");
|
|
1009
1147
|
return lines;
|
|
@@ -1011,105 +1149,105 @@ function renderPreview(preview2) {
|
|
|
1011
1149
|
function renderInventory(inv) {
|
|
1012
1150
|
const lines = [];
|
|
1013
1151
|
if (inventoryTotal(inv) === 0) {
|
|
1014
|
-
lines.push(
|
|
1152
|
+
lines.push(t8("plugin.inventory_empty"));
|
|
1015
1153
|
}
|
|
1016
1154
|
if (inv.commands.length > 0)
|
|
1017
|
-
lines.push(
|
|
1155
|
+
lines.push(t8("plugin.inv_commands", { count: inv.commands.length, names: list(inv.commands) }));
|
|
1018
1156
|
if (inv.roles.length > 0)
|
|
1019
|
-
lines.push(
|
|
1157
|
+
lines.push(t8("plugin.inv_roles", { count: inv.roles.length, names: list(inv.roles) }));
|
|
1020
1158
|
if (inv.skills.length > 0)
|
|
1021
|
-
lines.push(
|
|
1159
|
+
lines.push(t8("plugin.inv_skills", { count: inv.skills.length, names: list(inv.skills) }));
|
|
1022
1160
|
if (inv.hooks.length > 0) {
|
|
1023
|
-
const tally = inv.hooks.map((h) => `${h.type} \xD7 ${h.count}`).join(
|
|
1024
|
-
lines.push(
|
|
1161
|
+
const tally = inv.hooks.map((h) => `${h.type} \xD7 ${h.count}`).join(t8("plugin.inv_join"));
|
|
1162
|
+
lines.push(t8("plugin.inv_hooks", { tally }));
|
|
1025
1163
|
}
|
|
1026
|
-
if (inv.denyRules > 0) lines.push(
|
|
1164
|
+
if (inv.denyRules > 0) lines.push(t8("plugin.inv_deny", { count: inv.denyRules }));
|
|
1027
1165
|
if (inv.mcpServers.length > 0) {
|
|
1028
1166
|
lines.push(
|
|
1029
|
-
|
|
1167
|
+
t8("plugin.preview_mcp", {
|
|
1030
1168
|
count: inv.mcpServers.length,
|
|
1031
1169
|
names: list(inv.mcpServers)
|
|
1032
1170
|
})
|
|
1033
1171
|
);
|
|
1034
1172
|
}
|
|
1035
1173
|
if (inv.ignoredBuckets.length > 0) {
|
|
1036
|
-
lines.push(
|
|
1174
|
+
lines.push(t8("plugin.inv_ignored", { buckets: inv.ignoredBuckets.join(" / ") }));
|
|
1037
1175
|
}
|
|
1038
1176
|
if (inv.jsTools) {
|
|
1039
|
-
lines.push(
|
|
1177
|
+
lines.push(t8("plugin.inv_js_tools"));
|
|
1040
1178
|
}
|
|
1041
1179
|
for (const detail of issueDetails(inv.issues)) lines.push(`\u26A0 ${detail}`);
|
|
1042
1180
|
return lines;
|
|
1043
1181
|
}
|
|
1044
1182
|
function list(names) {
|
|
1045
1183
|
const shown = names.slice(0, 8).join(", ");
|
|
1046
|
-
return names.length > 8 ?
|
|
1184
|
+
return names.length > 8 ? t8("plugin.list_more", { shown, n: names.length - 8 }) : shown;
|
|
1047
1185
|
}
|
|
1048
1186
|
function printMarketplaces() {
|
|
1049
1187
|
const state = readMarketplaces(marketplacesPath(EPOCH_HOME));
|
|
1050
|
-
for (const detail of issueDetails(state.issues))
|
|
1188
|
+
for (const detail of issueDetails(state.issues)) log6(`\u26A0 ${detail}`);
|
|
1051
1189
|
if (state.records.length === 0) {
|
|
1052
|
-
|
|
1053
|
-
|
|
1190
|
+
log6(t8("plugin.no_markets"));
|
|
1191
|
+
log6(t8("plugin.no_markets_hint"));
|
|
1054
1192
|
return;
|
|
1055
1193
|
}
|
|
1056
|
-
|
|
1194
|
+
log6(`${t8("plugin.markets_head", { path: marketplacesPath(EPOCH_HOME) })}
|
|
1057
1195
|
`);
|
|
1058
1196
|
for (const record of state.records) {
|
|
1059
1197
|
const owner = record.catalog.owner ? ` by ${record.catalog.owner.name}` : "";
|
|
1060
|
-
|
|
1061
|
-
` ${record.name} ${
|
|
1198
|
+
log6(
|
|
1199
|
+
` ${record.name} ${t8("plugin.market_plugin_count", {
|
|
1062
1200
|
count: record.catalog.plugins.length
|
|
1063
1201
|
})} \u2190 ${record.source}${owner}`
|
|
1064
1202
|
);
|
|
1065
|
-
if (record.catalog.description)
|
|
1203
|
+
if (record.catalog.description) log6(` ${record.catalog.description}`);
|
|
1066
1204
|
}
|
|
1067
1205
|
}
|
|
1068
1206
|
function printSearch(keyword) {
|
|
1069
1207
|
const statePath = marketplacesPath(EPOCH_HOME);
|
|
1070
1208
|
if (readMarketplaces(statePath).records.length === 0) {
|
|
1071
|
-
|
|
1209
|
+
log6(t8("plugin.search_no_markets"));
|
|
1072
1210
|
return;
|
|
1073
1211
|
}
|
|
1074
1212
|
const hits = searchMarketplaces(keyword, statePath);
|
|
1075
1213
|
if (hits.length === 0) {
|
|
1076
|
-
|
|
1214
|
+
log6(keyword ? t8("plugin.search_no_hit", { keyword }) : t8("plugin.search_all_empty"));
|
|
1077
1215
|
return;
|
|
1078
1216
|
}
|
|
1079
|
-
|
|
1217
|
+
log6(`${t8("plugin.search_count", { n: hits.length })}
|
|
1080
1218
|
`);
|
|
1081
1219
|
for (const hit of hits) {
|
|
1082
1220
|
const category = hit.entry.category ? ` [${hit.entry.category}]` : "";
|
|
1083
|
-
|
|
1084
|
-
if (hit.entry.description)
|
|
1221
|
+
log6(` ${hit.ref}${category}`);
|
|
1222
|
+
if (hit.entry.description) log6(` ${hit.entry.description}`);
|
|
1085
1223
|
}
|
|
1086
|
-
|
|
1087
|
-
${
|
|
1224
|
+
log6(`
|
|
1225
|
+
${t8("plugin.search_install_hint", { ref: hits[0]?.ref ?? t8("plugin.ref_placeholder") })}`);
|
|
1088
1226
|
}
|
|
1089
1227
|
async function toggle(name, enabled) {
|
|
1090
1228
|
const outcome = await setPluginEnabled(name, enabled, {
|
|
1091
1229
|
statePath: pluginsStatePath(EPOCH_HOME)
|
|
1092
1230
|
});
|
|
1093
1231
|
if (!outcome.ok) throw new CliError(outcome.reason);
|
|
1094
|
-
|
|
1232
|
+
log6(`\u2713 ${outcome.message}`);
|
|
1095
1233
|
}
|
|
1096
1234
|
function validateDir(dir) {
|
|
1097
1235
|
const read = readPluginManifest(dir);
|
|
1098
1236
|
if (!read.manifest) {
|
|
1099
1237
|
const detail = issueDetails(read.issues).map((d) => ` - ${d}`).join("\n");
|
|
1100
|
-
throw new CliError(`${
|
|
1238
|
+
throw new CliError(`${t8("plugin.validate_bad", { dir })}
|
|
1101
1239
|
${detail}`);
|
|
1102
1240
|
}
|
|
1103
1241
|
const manifest = read.manifest;
|
|
1104
|
-
|
|
1105
|
-
if (manifest.epochVersion)
|
|
1106
|
-
for (const detail of issueDetails(read.issues))
|
|
1107
|
-
|
|
1108
|
-
${
|
|
1242
|
+
log6(`\u2713 ${t8("plugin.validate_ok", { name: manifest.name, version: manifest.version })}`);
|
|
1243
|
+
if (manifest.epochVersion) log6(` epochVersion: ${manifest.epochVersion}`);
|
|
1244
|
+
for (const detail of issueDetails(read.issues)) log6(`\u26A0 ${detail}`);
|
|
1245
|
+
log6(`
|
|
1246
|
+
${t8("plugin.brings_head")}`);
|
|
1109
1247
|
const inventory = scanPluginDir(dir, manifest.name);
|
|
1110
|
-
for (const line of renderInventory(inventory))
|
|
1248
|
+
for (const line of renderInventory(inventory)) log6(` ${line}`);
|
|
1111
1249
|
if (inventory.issues.length > 0) {
|
|
1112
|
-
throw new CliError(
|
|
1250
|
+
throw new CliError(t8("plugin.inv_issues", { count: inventory.issues.length }));
|
|
1113
1251
|
}
|
|
1114
1252
|
}
|
|
1115
1253
|
// src/commands/run.ts
|
|
@@ -1117,7 +1255,7 @@ import { existsSync as existsSync6 } from "fs";
|
|
|
1117
1255
|
import { dirname as dirname3, join as join5 } from "path";
|
|
1118
1256
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1119
1257
|
import { isNonInteractive as isNonInteractive8, OPERATION_TYPES as OPERATION_TYPES3, PERMISSION_LEVELS as PERMISSION_LEVELS3 } from "@epoch-agent/core";
|
|
1120
|
-
import { t as
|
|
1258
|
+
import { t as t25 } from "@epoch-agent/infra";
|
|
1121
1259
|
import {
|
|
1122
1260
|
HEADLESS_INPUT_FORMATS as HEADLESS_INPUT_FORMATS2,
|
|
1123
1261
|
HEADLESS_OUTPUT_FORMATS as HEADLESS_OUTPUT_FORMATS2,
|
|
@@ -1125,13 +1263,13 @@ import {
|
|
|
1125
1263
|
} from "@epoch-agent/protocol";
|
|
1126
1264
|
// src/headless/session.ts
|
|
1127
1265
|
import { OPERATION_TYPES } from "@epoch-agent/core";
|
|
1128
|
-
import { t as
|
|
1266
|
+
import { t as t12 } from "@epoch-agent/infra";
|
|
1129
1267
|
import { buildRuntime } from "@epoch-agent/runtime";
|
|
1130
1268
|
// src/commands/run-flags.ts
|
|
1131
1269
|
import { existsSync as existsSync3 } from "fs";
|
|
1132
1270
|
import { delimiter, resolve } from "path";
|
|
1133
1271
|
import { isOperationType, resolveWorkspace } from "@epoch-agent/core";
|
|
1134
|
-
import { t as
|
|
1272
|
+
import { t as t9 } from "@epoch-agent/infra";
|
|
1135
1273
|
import {
|
|
1136
1274
|
HEADLESS_INPUT_FORMATS,
|
|
1137
1275
|
HEADLESS_OUTPUT_FORMATS,
|
|
@@ -1151,14 +1289,14 @@ function parseResumeArg(raw) {
|
|
|
1151
1289
|
}
|
|
1152
1290
|
function assertResumeFlagsExclusive(opts) {
|
|
1153
1291
|
if (opts.continue === true && opts.resume !== void 0) {
|
|
1154
|
-
throw new CliError(
|
|
1292
|
+
throw new CliError(t9("flags.resume_conflict"), 1, t9("flags.resume_conflict_hint"));
|
|
1155
1293
|
}
|
|
1156
1294
|
}
|
|
1157
1295
|
function applySettingsPath(path) {
|
|
1158
1296
|
if (!path) return;
|
|
1159
1297
|
const abs = resolve(path);
|
|
1160
1298
|
if (!existsSync3(abs)) {
|
|
1161
|
-
throw new CliError(
|
|
1299
|
+
throw new CliError(t9("flags.settings_missing", { path }), 1, t9("flags.resolved_to", { abs }));
|
|
1162
1300
|
}
|
|
1163
1301
|
process.env.EPOCH_SETTINGS = abs;
|
|
1164
1302
|
}
|
|
@@ -1169,9 +1307,9 @@ function applyAddDirs(dirs) {
|
|
|
1169
1307
|
const fatal = issues.filter((i) => i.kind !== "redundant");
|
|
1170
1308
|
if (fatal.length > 0) {
|
|
1171
1309
|
throw new CliError(
|
|
1172
|
-
|
|
1310
|
+
t9("flags.add_dir_unusable", { inputs: fatal.map((i) => i.input).join(", ") }),
|
|
1173
1311
|
1,
|
|
1174
|
-
fatal.map((i) => i.detail).join(
|
|
1312
|
+
fatal.map((i) => i.detail).join(t9("flags.detail_sep"))
|
|
1175
1313
|
);
|
|
1176
1314
|
}
|
|
1177
1315
|
process.env.EPOCH_ADD_DIR = abs.join(delimiter);
|
|
@@ -1203,17 +1341,17 @@ function resolveOutputFormat(opts) {
|
|
|
1203
1341
|
const explicit = opts.outputFormat;
|
|
1204
1342
|
if (explicit !== void 0 && !isHeadlessOutputFormat(explicit)) {
|
|
1205
1343
|
throw new CliError(
|
|
1206
|
-
|
|
1344
|
+
t9("flags.output_format_unknown", { value: explicit }),
|
|
1207
1345
|
1,
|
|
1208
|
-
|
|
1346
|
+
t9("flags.choices", { choices: HEADLESS_OUTPUT_FORMATS.join(" / ") })
|
|
1209
1347
|
);
|
|
1210
1348
|
}
|
|
1211
1349
|
if (explicit === void 0) return opts.json === true ? "json" : "text";
|
|
1212
1350
|
if (opts.json === true && explicit !== "json") {
|
|
1213
1351
|
throw new CliError(
|
|
1214
|
-
|
|
1352
|
+
t9("flags.json_conflict", { value: explicit }),
|
|
1215
1353
|
1,
|
|
1216
|
-
|
|
1354
|
+
t9("flags.json_conflict_hint")
|
|
1217
1355
|
);
|
|
1218
1356
|
}
|
|
1219
1357
|
return explicit;
|
|
@@ -1222,9 +1360,9 @@ function resolveHeadlessFormats(opts) {
|
|
|
1222
1360
|
const raw = opts.inputFormat;
|
|
1223
1361
|
if (raw !== void 0 && !isHeadlessInputFormat(raw)) {
|
|
1224
1362
|
throw new CliError(
|
|
1225
|
-
|
|
1363
|
+
t9("flags.input_format_unknown", { value: raw }),
|
|
1226
1364
|
1,
|
|
1227
|
-
|
|
1365
|
+
t9("flags.choices", { choices: HEADLESS_INPUT_FORMATS.join(" / ") })
|
|
1228
1366
|
);
|
|
1229
1367
|
}
|
|
1230
1368
|
const input2 = raw ?? "text";
|
|
@@ -1234,7 +1372,7 @@ function resolveHeadlessFormats(opts) {
|
|
|
1234
1372
|
}
|
|
1235
1373
|
const output = resolveOutputFormat(opts);
|
|
1236
1374
|
if (output !== "stream-json") {
|
|
1237
|
-
throw new CliError(
|
|
1375
|
+
throw new CliError(t9("flags.stream_pair", { output }), 1, t9("flags.stream_pair_hint"));
|
|
1238
1376
|
}
|
|
1239
1377
|
return { input: input2, output };
|
|
1240
1378
|
}
|
|
@@ -1245,9 +1383,9 @@ function resolveApproverProgram(raw) {
|
|
|
1245
1383
|
const abs = resolve(value);
|
|
1246
1384
|
if (!existsSync3(abs)) {
|
|
1247
1385
|
throw new CliError(
|
|
1248
|
-
|
|
1386
|
+
t9("flags.prompt_tool_missing", { value }),
|
|
1249
1387
|
1,
|
|
1250
|
-
|
|
1388
|
+
t9("flags.prompt_tool_missing_hint", { abs })
|
|
1251
1389
|
);
|
|
1252
1390
|
}
|
|
1253
1391
|
return `"${abs}"`;
|
|
@@ -1265,15 +1403,15 @@ function parsePositive(raw, flag, extra = () => true) {
|
|
|
1265
1403
|
const n = Number(raw);
|
|
1266
1404
|
if (!Number.isFinite(n) || n <= 0 || !extra(n)) {
|
|
1267
1405
|
throw new CliError(
|
|
1268
|
-
|
|
1406
|
+
t9("flags.not_a_number", { flag, raw }),
|
|
1269
1407
|
1,
|
|
1270
|
-
flag === "--max-turns" ?
|
|
1408
|
+
flag === "--max-turns" ? t9("flags.want_positive_int") : t9("flags.want_positive")
|
|
1271
1409
|
);
|
|
1272
1410
|
}
|
|
1273
1411
|
return n;
|
|
1274
1412
|
}
|
|
1275
1413
|
// src/headless/driver.ts
|
|
1276
|
-
import { t as
|
|
1414
|
+
import { t as t11 } from "@epoch-agent/infra";
|
|
1277
1415
|
// src/headless/emit.ts
|
|
1278
1416
|
import {
|
|
1279
1417
|
HEADLESS_PROTOCOL_VERSION
|
|
@@ -1390,14 +1528,14 @@ function initInfoOf(runtime, cwd) {
|
|
|
1390
1528
|
model: runtime.config.model,
|
|
1391
1529
|
...provider ? { provider } : {},
|
|
1392
1530
|
permissionLevel: runtime.permissions.level(),
|
|
1393
|
-
tools: runtime.tools.map((
|
|
1531
|
+
tools: runtime.tools.map((t40) => t40.name),
|
|
1394
1532
|
usageScope: runtime.usageScope,
|
|
1395
1533
|
diagnostics: runtime.diagnostics
|
|
1396
1534
|
};
|
|
1397
1535
|
}
|
|
1398
1536
|
// src/headless/read.ts
|
|
1399
1537
|
import { createInterface } from "readline";
|
|
1400
|
-
import { t as
|
|
1538
|
+
import { t as t10 } from "@epoch-agent/infra";
|
|
1401
1539
|
import {
|
|
1402
1540
|
APPROVAL_OUTCOMES,
|
|
1403
1541
|
HEADLESS_INPUT_EVENT_TYPES,
|
|
@@ -1413,17 +1551,17 @@ function parseInputLine(line) {
|
|
|
1413
1551
|
} catch (err2) {
|
|
1414
1552
|
return {
|
|
1415
1553
|
ok: false,
|
|
1416
|
-
message:
|
|
1554
|
+
message: t10("headless_input.bad_json", { why: describe2(err2), preview: preview(text) })
|
|
1417
1555
|
};
|
|
1418
1556
|
}
|
|
1419
1557
|
if (!isRecord(raw)) {
|
|
1420
|
-
return { ok: false, message:
|
|
1558
|
+
return { ok: false, message: t10("headless_input.not_object", { preview: preview(text) }) };
|
|
1421
1559
|
}
|
|
1422
1560
|
const type = raw["type"];
|
|
1423
1561
|
if (typeof type !== "string" || !HEADLESS_INPUT_EVENT_TYPES.has(type)) {
|
|
1424
1562
|
return {
|
|
1425
1563
|
ok: false,
|
|
1426
|
-
message:
|
|
1564
|
+
message: t10("headless_input.bad_type", {
|
|
1427
1565
|
value: JSON.stringify(type),
|
|
1428
1566
|
choices: [...HEADLESS_INPUT_EVENT_TYPES].join(" / ")
|
|
1429
1567
|
})
|
|
@@ -1437,15 +1575,15 @@ function parseInputLine(line) {
|
|
|
1437
1575
|
function parseUserMessage(raw) {
|
|
1438
1576
|
const content = raw["content"];
|
|
1439
1577
|
if (typeof content === "string") {
|
|
1440
|
-
if (!content.trim()) return { ok: false, message:
|
|
1578
|
+
if (!content.trim()) return { ok: false, message: t10("headless_input.msg_empty") };
|
|
1441
1579
|
return { ok: true, event: { type: "user-message", content } };
|
|
1442
1580
|
}
|
|
1443
1581
|
if (!Array.isArray(content) || content.length === 0) {
|
|
1444
|
-
return { ok: false, message:
|
|
1582
|
+
return { ok: false, message: t10("headless_input.msg_bad_content") };
|
|
1445
1583
|
}
|
|
1446
1584
|
const bad = content.findIndex((p) => !isRecord(p) || typeof p["type"] !== "string");
|
|
1447
1585
|
if (bad >= 0) {
|
|
1448
|
-
return { ok: false, message:
|
|
1586
|
+
return { ok: false, message: t10("headless_input.msg_bad_part", { index: bad }) };
|
|
1449
1587
|
}
|
|
1450
1588
|
return {
|
|
1451
1589
|
ok: true,
|
|
@@ -1455,13 +1593,13 @@ function parseUserMessage(raw) {
|
|
|
1455
1593
|
function parseApprovalResponse(raw) {
|
|
1456
1594
|
const requestId = raw["requestId"];
|
|
1457
1595
|
if (typeof requestId !== "string" || !requestId) {
|
|
1458
|
-
return { ok: false, message:
|
|
1596
|
+
return { ok: false, message: t10("headless_input.approval_no_id") };
|
|
1459
1597
|
}
|
|
1460
1598
|
const outcome = raw["outcome"];
|
|
1461
1599
|
if (typeof outcome !== "string" || !isApprovalOutcome(outcome)) {
|
|
1462
1600
|
return {
|
|
1463
1601
|
ok: false,
|
|
1464
|
-
message:
|
|
1602
|
+
message: t10("headless_input.approval_bad_outcome", {
|
|
1465
1603
|
value: JSON.stringify(outcome),
|
|
1466
1604
|
choices: APPROVAL_OUTCOMES.join(" / ")
|
|
1467
1605
|
})
|
|
@@ -1469,7 +1607,7 @@ function parseApprovalResponse(raw) {
|
|
|
1469
1607
|
}
|
|
1470
1608
|
const note = raw["note"];
|
|
1471
1609
|
if (note !== void 0 && typeof note !== "string") {
|
|
1472
|
-
return { ok: false, message:
|
|
1610
|
+
return { ok: false, message: t10("headless_input.approval_bad_note") };
|
|
1473
1611
|
}
|
|
1474
1612
|
return {
|
|
1475
1613
|
ok: true,
|
|
@@ -1484,19 +1622,19 @@ function parseApprovalResponse(raw) {
|
|
|
1484
1622
|
function parseQuestionResponse(raw) {
|
|
1485
1623
|
const requestId = raw["requestId"];
|
|
1486
1624
|
if (typeof requestId !== "string" || !requestId) {
|
|
1487
|
-
return { ok: false, message:
|
|
1625
|
+
return { ok: false, message: t10("headless_input.question_no_id") };
|
|
1488
1626
|
}
|
|
1489
1627
|
if (!isQuestionAnswerMap(raw["answers"])) {
|
|
1490
1628
|
return {
|
|
1491
1629
|
ok: false,
|
|
1492
|
-
message:
|
|
1630
|
+
message: t10("headless_input.question_bad_answers", {
|
|
1493
1631
|
json: '{"answers":{},"skipped":true}'
|
|
1494
1632
|
})
|
|
1495
1633
|
};
|
|
1496
1634
|
}
|
|
1497
1635
|
const skipped = raw["skipped"];
|
|
1498
1636
|
if (skipped !== void 0 && typeof skipped !== "boolean") {
|
|
1499
|
-
return { ok: false, message:
|
|
1637
|
+
return { ok: false, message: t10("headless_input.question_bad_skipped") };
|
|
1500
1638
|
}
|
|
1501
1639
|
return {
|
|
1502
1640
|
ok: true,
|
|
@@ -1539,7 +1677,7 @@ async function driveStreamJson(opts) {
|
|
|
1539
1677
|
const parsed = parseInputLine(line);
|
|
1540
1678
|
if (parsed === void 0) continue;
|
|
1541
1679
|
if (!parsed.ok) {
|
|
1542
|
-
warn4(
|
|
1680
|
+
warn4(t11("headless_driver.bad_input", { message: parsed.message }));
|
|
1543
1681
|
state.sawBadInput = true;
|
|
1544
1682
|
continue;
|
|
1545
1683
|
}
|
|
@@ -1574,7 +1712,7 @@ var DriverState = class {
|
|
|
1574
1712
|
}
|
|
1575
1713
|
if (event.type === "approval-response") {
|
|
1576
1714
|
if (!this.emitter.respondApproval(event.requestId, event.outcome, event.note)) {
|
|
1577
|
-
this.warn(
|
|
1715
|
+
this.warn(t11("headless_driver.unknown_request", { id: event.requestId }));
|
|
1578
1716
|
}
|
|
1579
1717
|
return false;
|
|
1580
1718
|
}
|
|
@@ -1584,7 +1722,7 @@ var DriverState = class {
|
|
|
1584
1722
|
...event.skipped ? { skipped: true } : {}
|
|
1585
1723
|
};
|
|
1586
1724
|
if (!this.emitter.respondQuestion(event.requestId, answer)) {
|
|
1587
|
-
this.warn(
|
|
1725
|
+
this.warn(t11("headless_driver.unknown_request", { id: event.requestId }));
|
|
1588
1726
|
}
|
|
1589
1727
|
return false;
|
|
1590
1728
|
}
|
|
@@ -1646,11 +1784,11 @@ var DriverState = class {
|
|
|
1646
1784
|
reapLostHost() {
|
|
1647
1785
|
const approvals = this.emitter.abandonApprovals();
|
|
1648
1786
|
if (approvals > 0) {
|
|
1649
|
-
this.warn(
|
|
1787
|
+
this.warn(t11("headless_driver.abandoned_approvals", { count: approvals }));
|
|
1650
1788
|
}
|
|
1651
1789
|
const questions = this.emitter.abandonQuestions();
|
|
1652
1790
|
if (questions > 0) {
|
|
1653
|
-
this.warn(
|
|
1791
|
+
this.warn(t11("headless_driver.abandoned_questions", { count: questions }));
|
|
1654
1792
|
}
|
|
1655
1793
|
this.queue.length = 0;
|
|
1656
1794
|
this.controller?.abort();
|
|
@@ -1675,9 +1813,9 @@ async function runStreamJsonSession(opts) {
|
|
|
1675
1813
|
const { policy: headless, invalid } = parseHeadlessFlags(opts);
|
|
1676
1814
|
if (invalid.length > 0) {
|
|
1677
1815
|
throw new CliError(
|
|
1678
|
-
|
|
1816
|
+
t12("run_once.err_bad_operation", { values: invalid.join(", ") }),
|
|
1679
1817
|
EXIT_CODES.FAILURE,
|
|
1680
|
-
|
|
1818
|
+
t12("run_once.err_bad_operation_hint", { choices: OPERATION_TYPES.join(" / ") })
|
|
1681
1819
|
);
|
|
1682
1820
|
}
|
|
1683
1821
|
const limits = parseCallLimits(opts);
|
|
@@ -1706,21 +1844,21 @@ function reportDeadRuntime(runtime) {
|
|
|
1706
1844
|
const detail = runtime.diagnostics.join("; ");
|
|
1707
1845
|
emitter.agentEvent({
|
|
1708
1846
|
type: "error",
|
|
1709
|
-
message: detail ?
|
|
1710
|
-
message:
|
|
1847
|
+
message: detail ? t12("headless_session.no_provider_detail", {
|
|
1848
|
+
message: t12("run_once.err_no_provider"),
|
|
1711
1849
|
detail
|
|
1712
|
-
}) :
|
|
1850
|
+
}) : t12("run_once.err_no_provider")
|
|
1713
1851
|
});
|
|
1714
1852
|
emitter.result({ ok: false, reason: "error", text: "", turns: 0 });
|
|
1715
1853
|
return EXIT_CODES.FAILURE;
|
|
1716
1854
|
}
|
|
1717
1855
|
function warnInertFlags(opts) {
|
|
1718
1856
|
if ((opts.allowTool ?? []).length > 0 || (opts.allowOperation ?? []).length > 0) {
|
|
1719
|
-
process.stderr.write(`${
|
|
1857
|
+
process.stderr.write(`${t12("headless_session.inert_allow")}
|
|
1720
1858
|
`);
|
|
1721
1859
|
}
|
|
1722
1860
|
if ((opts.image ?? []).length > 0) {
|
|
1723
|
-
process.stderr.write(`${
|
|
1861
|
+
process.stderr.write(`${t12("headless_session.inert_image")}
|
|
1724
1862
|
`);
|
|
1725
1863
|
}
|
|
1726
1864
|
}
|
|
@@ -1730,17 +1868,17 @@ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as rea
|
|
|
1730
1868
|
import { get } from "https";
|
|
1731
1869
|
import { dirname as dirname2, join as join3 } from "path";
|
|
1732
1870
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1733
|
-
import { t as
|
|
1871
|
+
import { t as t15 } from "@epoch-agent/infra";
|
|
1734
1872
|
// src/version.ts
|
|
1735
1873
|
import { readFileSync as readFileSync2 } from "fs";
|
|
1736
1874
|
import { dirname, join as join2 } from "path";
|
|
1737
1875
|
import { fileURLToPath } from "url";
|
|
1738
|
-
import { t as
|
|
1876
|
+
import { t as t14 } from "@epoch-agent/infra";
|
|
1739
1877
|
// src/installation.ts
|
|
1740
1878
|
import { execFileSync } from "child_process";
|
|
1741
1879
|
import { existsSync as existsSync4, realpathSync } from "fs";
|
|
1742
1880
|
import { join } from "path";
|
|
1743
|
-
import { normalizeForMatch, t as
|
|
1881
|
+
import { normalizeForMatch, t as t13 } from "@epoch-agent/infra";
|
|
1744
1882
|
var PATH_RULES = [
|
|
1745
1883
|
{
|
|
1746
1884
|
patterns: [
|
|
@@ -1754,7 +1892,7 @@ var PATH_RULES = [
|
|
|
1754
1892
|
build: () => ({
|
|
1755
1893
|
packageManager: "npx",
|
|
1756
1894
|
isGlobal: false,
|
|
1757
|
-
note:
|
|
1895
|
+
note: t13("installation.npx")
|
|
1758
1896
|
})
|
|
1759
1897
|
},
|
|
1760
1898
|
{
|
|
@@ -1763,7 +1901,7 @@ var PATH_RULES = [
|
|
|
1763
1901
|
packageManager: "volta",
|
|
1764
1902
|
isGlobal: true,
|
|
1765
1903
|
updateCommand: `volta install ${pkg}@latest`,
|
|
1766
|
-
note:
|
|
1904
|
+
note: t13("installation.volta")
|
|
1767
1905
|
})
|
|
1768
1906
|
},
|
|
1769
1907
|
{
|
|
@@ -1777,7 +1915,7 @@ var PATH_RULES = [
|
|
|
1777
1915
|
packageManager: "pnpm",
|
|
1778
1916
|
isGlobal: true,
|
|
1779
1917
|
updateCommand: `pnpm add -g ${pkg}@latest`,
|
|
1780
|
-
note:
|
|
1918
|
+
note: t13("installation.pnpm")
|
|
1781
1919
|
})
|
|
1782
1920
|
},
|
|
1783
1921
|
{
|
|
@@ -1791,7 +1929,7 @@ var PATH_RULES = [
|
|
|
1791
1929
|
packageManager: "yarn",
|
|
1792
1930
|
isGlobal: true,
|
|
1793
1931
|
updateCommand: `yarn global add ${pkg}@latest`,
|
|
1794
|
-
note:
|
|
1932
|
+
note: t13("installation.yarn")
|
|
1795
1933
|
})
|
|
1796
1934
|
},
|
|
1797
1935
|
{
|
|
@@ -1800,7 +1938,7 @@ var PATH_RULES = [
|
|
|
1800
1938
|
packageManager: "bun",
|
|
1801
1939
|
isGlobal: true,
|
|
1802
1940
|
updateCommand: `bun add -g ${pkg}@latest`,
|
|
1803
|
-
note:
|
|
1941
|
+
note: t13("installation.bun")
|
|
1804
1942
|
})
|
|
1805
1943
|
}
|
|
1806
1944
|
];
|
|
@@ -1820,7 +1958,7 @@ function detectHomebrew(realPath, formula) {
|
|
|
1820
1958
|
packageManager: "homebrew",
|
|
1821
1959
|
isGlobal: true,
|
|
1822
1960
|
updateCommand: `brew upgrade ${formula}`,
|
|
1823
|
-
note:
|
|
1961
|
+
note: t13("installation.homebrew")
|
|
1824
1962
|
};
|
|
1825
1963
|
}
|
|
1826
1964
|
} catch {
|
|
@@ -1845,12 +1983,12 @@ function detectLocalInstall(matchPath, cwd) {
|
|
|
1845
1983
|
return {
|
|
1846
1984
|
packageManager: manager,
|
|
1847
1985
|
isGlobal: false,
|
|
1848
|
-
note:
|
|
1986
|
+
note: t13("installation.local")
|
|
1849
1987
|
};
|
|
1850
1988
|
}
|
|
1851
1989
|
function getInstallationInfo(packageName, cliPath = process.argv[1]) {
|
|
1852
1990
|
if (!cliPath) {
|
|
1853
|
-
return { packageManager: "unknown", isGlobal: false, note:
|
|
1991
|
+
return { packageManager: "unknown", isGlobal: false, note: t13("installation.unknown") };
|
|
1854
1992
|
}
|
|
1855
1993
|
try {
|
|
1856
1994
|
const realPath = realpathSync(cliPath).replace(/\\/g, "/");
|
|
@@ -1863,7 +2001,7 @@ function getInstallationInfo(packageName, cliPath = process.argv[1]) {
|
|
|
1863
2001
|
return {
|
|
1864
2002
|
packageManager: "source",
|
|
1865
2003
|
isGlobal: false,
|
|
1866
|
-
note:
|
|
2004
|
+
note: t13("installation.source")
|
|
1867
2005
|
};
|
|
1868
2006
|
}
|
|
1869
2007
|
const brew = detectHomebrew(realPath, packageName.replace(/^@[^/]+\//, ""));
|
|
@@ -1874,10 +2012,10 @@ function getInstallationInfo(packageName, cliPath = process.argv[1]) {
|
|
|
1874
2012
|
packageManager: "npm",
|
|
1875
2013
|
isGlobal: true,
|
|
1876
2014
|
updateCommand: `npm install -g ${packageName}@latest`,
|
|
1877
|
-
note:
|
|
2015
|
+
note: t13("installation.npm")
|
|
1878
2016
|
};
|
|
1879
2017
|
} catch {
|
|
1880
|
-
return { packageManager: "unknown", isGlobal: false, note:
|
|
2018
|
+
return { packageManager: "unknown", isGlobal: false, note: t13("installation.unknown") };
|
|
1881
2019
|
}
|
|
1882
2020
|
}
|
|
1883
2021
|
// src/version.ts
|
|
@@ -1901,9 +2039,9 @@ function describeVersion(packageName) {
|
|
|
1901
2039
|
return [
|
|
1902
2040
|
VERSION,
|
|
1903
2041
|
`Node: ${process.version} (${process.platform}/${process.arch})`,
|
|
1904
|
-
|
|
2042
|
+
t14("version.install", {
|
|
1905
2043
|
manager: info.packageManager,
|
|
1906
|
-
global: info.isGlobal ?
|
|
2044
|
+
global: info.isGlobal ? t14("installation.global_suffix") : "",
|
|
1907
2045
|
note: info.note
|
|
1908
2046
|
})
|
|
1909
2047
|
].join("\n");
|
|
@@ -2047,7 +2185,7 @@ function readUpdateNotice(options = {}) {
|
|
|
2047
2185
|
name,
|
|
2048
2186
|
current: version,
|
|
2049
2187
|
latest: cache.latest,
|
|
2050
|
-
message:
|
|
2188
|
+
message: t15("update_check.available", { name, current: version, latest: cache.latest })
|
|
2051
2189
|
};
|
|
2052
2190
|
}
|
|
2053
2191
|
async function refreshUpdateCache(options = {}) {
|
|
@@ -2076,7 +2214,7 @@ async function refreshUpdateCache(options = {}) {
|
|
|
2076
2214
|
name,
|
|
2077
2215
|
current: version,
|
|
2078
2216
|
latest,
|
|
2079
|
-
message:
|
|
2217
|
+
message: t15("update_check.available", { name, current: version, latest })
|
|
2080
2218
|
};
|
|
2081
2219
|
}
|
|
2082
2220
|
function scheduleUpdateCheck(options = {}) {
|
|
@@ -2114,7 +2252,7 @@ import { execFile } from "child_process";
|
|
|
2114
2252
|
import { randomBytes } from "crypto";
|
|
2115
2253
|
import { mkdirSync as mkdirSync3 } from "fs";
|
|
2116
2254
|
import { basename, join as join4, resolve as resolve2 } from "path";
|
|
2117
|
-
import { t as
|
|
2255
|
+
import { t as t16, worktreesDir } from "@epoch-agent/infra";
|
|
2118
2256
|
var TIMEOUT_MS = 6e4;
|
|
2119
2257
|
function git(args, cwd) {
|
|
2120
2258
|
return new Promise((done) => {
|
|
@@ -2135,13 +2273,13 @@ function git(args, cwd) {
|
|
|
2135
2273
|
});
|
|
2136
2274
|
}
|
|
2137
2275
|
function firstLine(text) {
|
|
2138
|
-
return text.trim().split("\n")[0]?.trim() ??
|
|
2276
|
+
return text.trim().split("\n")[0]?.trim() ?? t16("git.unknown_reason");
|
|
2139
2277
|
}
|
|
2140
2278
|
async function createWorktree(cwd = process.cwd(), container = worktreesDir()) {
|
|
2141
2279
|
const repoRoot = await resolveRepoRoot(cwd);
|
|
2142
2280
|
const head = await git(["rev-parse", "HEAD"], repoRoot);
|
|
2143
2281
|
if (!head.ok) {
|
|
2144
|
-
throw new CliError(
|
|
2282
|
+
throw new CliError(t16("worktree.err_no_commit"), 1, t16("worktree.err_no_commit_hint"));
|
|
2145
2283
|
}
|
|
2146
2284
|
const baseRef = head.stdout.trim();
|
|
2147
2285
|
const shortId = randomBytes(3).toString("hex");
|
|
@@ -2150,20 +2288,20 @@ async function createWorktree(cwd = process.cwd(), container = worktreesDir()) {
|
|
|
2150
2288
|
mkdirSync3(container, { recursive: true });
|
|
2151
2289
|
const added = await git(["worktree", "add", "-b", branch, path, baseRef], repoRoot);
|
|
2152
2290
|
if (!added.ok) {
|
|
2153
|
-
throw new CliError(
|
|
2291
|
+
throw new CliError(t16("worktree.err_add_failed", { path }), 1, firstLine(added.stderr));
|
|
2154
2292
|
}
|
|
2155
2293
|
return { repoRoot, path, branch, baseRef };
|
|
2156
2294
|
}
|
|
2157
2295
|
async function resolveRepoRoot(cwd) {
|
|
2158
2296
|
const root = await git(["rev-parse", "--show-toplevel"], cwd);
|
|
2159
2297
|
if (root.missing) {
|
|
2160
|
-
throw new CliError(
|
|
2298
|
+
throw new CliError(t16("worktree.err_git_missing"), 1, t16("worktree.err_git_missing_hint"));
|
|
2161
2299
|
}
|
|
2162
2300
|
if (!root.ok) {
|
|
2163
2301
|
throw new CliError(
|
|
2164
|
-
|
|
2302
|
+
t16("worktree.err_not_a_repo"),
|
|
2165
2303
|
1,
|
|
2166
|
-
|
|
2304
|
+
t16("worktree.err_not_a_repo_hint", { cwd, why: firstLine(root.stderr) })
|
|
2167
2305
|
);
|
|
2168
2306
|
}
|
|
2169
2307
|
return resolve2(root.stdout.trim());
|
|
@@ -2181,16 +2319,16 @@ async function inspectWorktree(session) {
|
|
|
2181
2319
|
};
|
|
2182
2320
|
}
|
|
2183
2321
|
function worktreeKeepReason(state) {
|
|
2184
|
-
if (state.dirty === null || state.ahead === null) return
|
|
2185
|
-
if (state.dirty > 0) return
|
|
2186
|
-
if (state.ahead > 0) return
|
|
2322
|
+
if (state.dirty === null || state.ahead === null) return t16("worktree.keep_unknown");
|
|
2323
|
+
if (state.dirty > 0) return t16("worktree.keep_dirty", { count: state.dirty });
|
|
2324
|
+
if (state.ahead > 0) return t16("worktree.keep_ahead", { count: state.ahead });
|
|
2187
2325
|
return null;
|
|
2188
2326
|
}
|
|
2189
2327
|
function worktreeBanner(session) {
|
|
2190
|
-
const dir =
|
|
2191
|
-
const branch =
|
|
2328
|
+
const dir = t16("worktree.banner_dir", { path: session.path });
|
|
2329
|
+
const branch = t16("worktree.banner_branch", { branch: session.branch });
|
|
2192
2330
|
return `
|
|
2193
|
-
${
|
|
2331
|
+
${t16("worktree.banner")}
|
|
2194
2332
|
${dir}
|
|
2195
2333
|
${branch}
|
|
2196
2334
|
`;
|
|
@@ -2202,23 +2340,23 @@ async function finishWorktree(session, opts) {
|
|
|
2202
2340
|
if (forced !== null) {
|
|
2203
2341
|
print(
|
|
2204
2342
|
`
|
|
2205
|
-
${
|
|
2343
|
+
${t16("worktree.kept_forced", { reason: forced })}
|
|
2206
2344
|
${session.path}
|
|
2207
|
-
${
|
|
2345
|
+
${t16("worktree.banner_branch", { branch: session.branch })}`
|
|
2208
2346
|
);
|
|
2209
2347
|
return;
|
|
2210
2348
|
}
|
|
2211
2349
|
const remove = opts.interactive ? await (opts.ask ?? askRemove)(session) : false;
|
|
2212
2350
|
if (!remove) {
|
|
2213
2351
|
print(`
|
|
2214
|
-
${
|
|
2352
|
+
${t16("worktree.kept", { path: session.path, branch: session.branch })}`);
|
|
2215
2353
|
return;
|
|
2216
2354
|
}
|
|
2217
2355
|
const failure = await removeWorktree(session);
|
|
2218
2356
|
print(
|
|
2219
2357
|
failure === null ? `
|
|
2220
|
-
${
|
|
2221
|
-
${
|
|
2358
|
+
${t16("worktree.removed", { path: session.path, branch: session.branch })}` : `
|
|
2359
|
+
${t16("worktree.remove_failed", { path: session.path })}
|
|
2222
2360
|
${failure}`
|
|
2223
2361
|
);
|
|
2224
2362
|
}
|
|
@@ -2226,7 +2364,7 @@ async function removeWorktree(session) {
|
|
|
2226
2364
|
const removed = await git(["worktree", "remove", session.path], session.repoRoot);
|
|
2227
2365
|
if (!removed.ok) return firstLine(removed.stderr);
|
|
2228
2366
|
const branch = await git(["branch", "-D", session.branch], session.repoRoot);
|
|
2229
|
-
return branch.ok ? null :
|
|
2367
|
+
return branch.ok ? null : t16("worktree.branch_remove_failed", {
|
|
2230
2368
|
branch: session.branch,
|
|
2231
2369
|
why: firstLine(branch.stderr)
|
|
2232
2370
|
});
|
|
@@ -2235,7 +2373,7 @@ async function askRemove(session) {
|
|
|
2235
2373
|
const { confirm } = await import("@inquirer/prompts");
|
|
2236
2374
|
try {
|
|
2237
2375
|
return await confirm({
|
|
2238
|
-
message:
|
|
2376
|
+
message: t16("worktree.ask_remove", { path: session.path }),
|
|
2239
2377
|
default: false
|
|
2240
2378
|
});
|
|
2241
2379
|
} catch {
|
|
@@ -2244,7 +2382,7 @@ async function askRemove(session) {
|
|
|
2244
2382
|
}
|
|
2245
2383
|
// src/commands/run-once.ts
|
|
2246
2384
|
import { formatUsd, imageFromPath, OPERATION_TYPES as OPERATION_TYPES2 } from "@epoch-agent/core";
|
|
2247
|
-
import { t as
|
|
2385
|
+
import { t as t22 } from "@epoch-agent/infra";
|
|
2248
2386
|
import {
|
|
2249
2387
|
promptTokens
|
|
2250
2388
|
} from "@epoch-agent/protocol";
|
|
@@ -2253,38 +2391,38 @@ import { buildRuntime as buildRuntime2, listBackgroundTasks } from "@epoch-agent
|
|
|
2253
2391
|
import {
|
|
2254
2392
|
isNonInteractive as isNonInteractive3
|
|
2255
2393
|
} from "@epoch-agent/core";
|
|
2256
|
-
import { t as
|
|
2394
|
+
import { t as t17 } from "@epoch-agent/infra";
|
|
2257
2395
|
function typeLabel(type) {
|
|
2258
2396
|
switch (type) {
|
|
2259
2397
|
case "file_read":
|
|
2260
|
-
return
|
|
2398
|
+
return t17("tui.approval.type_file_read");
|
|
2261
2399
|
case "file_write":
|
|
2262
|
-
return
|
|
2400
|
+
return t17("tui.approval.type_file_write");
|
|
2263
2401
|
case "command":
|
|
2264
|
-
return
|
|
2402
|
+
return t17("tui.approval.type_command");
|
|
2265
2403
|
case "network":
|
|
2266
|
-
return
|
|
2404
|
+
return t17("tui.approval.type_network");
|
|
2267
2405
|
case "code_exec":
|
|
2268
|
-
return
|
|
2406
|
+
return t17("tui.approval.type_code_exec");
|
|
2269
2407
|
}
|
|
2270
2408
|
}
|
|
2271
2409
|
function formatApprovalRequest(req) {
|
|
2272
2410
|
if (req.plan) {
|
|
2273
2411
|
return [
|
|
2274
|
-
|
|
2275
|
-
|
|
2412
|
+
t17("approval.plan_head"),
|
|
2413
|
+
t17("approval.plan_previous_level", { level: req.plan.previousLevel }),
|
|
2276
2414
|
"",
|
|
2277
2415
|
...req.plan.markdown.split("\n").map((l) => ` ${l}`)
|
|
2278
2416
|
];
|
|
2279
2417
|
}
|
|
2280
2418
|
const lines = [
|
|
2281
|
-
|
|
2282
|
-
|
|
2419
|
+
t17("approval.asks", { tool: req.toolName, what: typeLabel(req.type) }),
|
|
2420
|
+
t17("approval.target", { target: req.target || t17("approval.target_none") })
|
|
2283
2421
|
];
|
|
2284
2422
|
if (req.detail && req.detail !== req.target) {
|
|
2285
|
-
lines.push(
|
|
2423
|
+
lines.push(t17("approval.detail", { detail: req.detail.slice(0, 300) }));
|
|
2286
2424
|
}
|
|
2287
|
-
if (req.reason) lines.push(
|
|
2425
|
+
if (req.reason) lines.push(t17("approval.reason", { reason: req.reason }));
|
|
2288
2426
|
return lines;
|
|
2289
2427
|
}
|
|
2290
2428
|
function createInteractiveApproval(opts = {}) {
|
|
@@ -2292,15 +2430,15 @@ function createInteractiveApproval(opts = {}) {
|
|
|
2292
2430
|
return async (req) => {
|
|
2293
2431
|
if (!interactive()) {
|
|
2294
2432
|
if (opts.permission?.()?.isPreauthorized(req.toolName, req.type)) {
|
|
2295
|
-
console.error(
|
|
2433
|
+
console.error(t17("approval.headless_allowed", { tool: req.toolName, target: req.target }));
|
|
2296
2434
|
return "allow-once";
|
|
2297
2435
|
}
|
|
2298
2436
|
console.error(
|
|
2299
|
-
`${
|
|
2437
|
+
`${t17("approval.headless_denied", {
|
|
2300
2438
|
tool: req.toolName,
|
|
2301
2439
|
target: req.target
|
|
2302
2440
|
})}
|
|
2303
|
-
${
|
|
2441
|
+
${t17("approval.headless_denied_hint", { tool: req.toolName })}`
|
|
2304
2442
|
);
|
|
2305
2443
|
return "deny";
|
|
2306
2444
|
}
|
|
@@ -2309,12 +2447,12 @@ ${t16("approval.headless_denied_hint", { tool: req.toolName })}`
|
|
|
2309
2447
|
const { select: select2 } = await import("@inquirer/prompts");
|
|
2310
2448
|
try {
|
|
2311
2449
|
return await select2({
|
|
2312
|
-
message:
|
|
2450
|
+
message: t17("approval.ask"),
|
|
2313
2451
|
choices: [
|
|
2314
|
-
{ name:
|
|
2315
|
-
{ name:
|
|
2316
|
-
{ name:
|
|
2317
|
-
{ name:
|
|
2452
|
+
{ name: t17("tui.approval.allow_once"), value: "allow-once" },
|
|
2453
|
+
{ name: t17("tui.approval.allow_session"), value: "allow-session" },
|
|
2454
|
+
{ name: t17("approval.allow_always_cli"), value: "allow-always" },
|
|
2455
|
+
{ name: t17("tui.approval.deny"), value: "deny" }
|
|
2318
2456
|
],
|
|
2319
2457
|
default: "allow-once"
|
|
2320
2458
|
});
|
|
@@ -2328,21 +2466,21 @@ async function askPlan(proposal) {
|
|
|
2328
2466
|
const choices3 = [
|
|
2329
2467
|
...proposal.canExecute ? [
|
|
2330
2468
|
{
|
|
2331
|
-
name:
|
|
2332
|
-
label:
|
|
2469
|
+
name: t17("approval.plan_execute_back", {
|
|
2470
|
+
label: t17("approval.plan_execute"),
|
|
2333
2471
|
level: proposal.previousLevel
|
|
2334
2472
|
}),
|
|
2335
2473
|
value: "plan-execute"
|
|
2336
2474
|
}
|
|
2337
2475
|
] : [],
|
|
2338
|
-
{ name:
|
|
2339
|
-
{ name:
|
|
2340
|
-
{ name:
|
|
2476
|
+
{ name: t17("approval.plan_readonly"), value: "plan-readonly" },
|
|
2477
|
+
{ name: t17("approval.plan_revise"), value: "plan-revise" },
|
|
2478
|
+
{ name: t17("approval.deny"), value: "deny" }
|
|
2341
2479
|
];
|
|
2342
2480
|
try {
|
|
2343
|
-
const outcome = await select2({ message:
|
|
2481
|
+
const outcome = await select2({ message: t17("approval.plan_ask"), choices: choices3 });
|
|
2344
2482
|
if (outcome !== "plan-revise") return outcome;
|
|
2345
|
-
const note = (await input2({ message:
|
|
2483
|
+
const note = (await input2({ message: t17("approval.plan_revise_ask") })).trim();
|
|
2346
2484
|
return note ? { outcome, note } : outcome;
|
|
2347
2485
|
} catch {
|
|
2348
2486
|
return "deny";
|
|
@@ -2355,7 +2493,7 @@ import {
|
|
|
2355
2493
|
matchPreauthorization,
|
|
2356
2494
|
mergeHeadlessPolicy
|
|
2357
2495
|
} from "@epoch-agent/core";
|
|
2358
|
-
import { t as
|
|
2496
|
+
import { t as t18 } from "@epoch-agent/infra";
|
|
2359
2497
|
import { APPROVAL_OUTCOMES as APPROVAL_OUTCOMES2, isApprovalOutcome as isApprovalOutcome2 } from "@epoch-agent/protocol";
|
|
2360
2498
|
var APPROVER_TIMEOUT_MS = 6e4;
|
|
2361
2499
|
var MAX_STDOUT_BYTES = 64 * 1024;
|
|
@@ -2365,22 +2503,22 @@ function clip(s) {
|
|
|
2365
2503
|
}
|
|
2366
2504
|
function parseApproverVerdict(stdout) {
|
|
2367
2505
|
const text = stdout.trim();
|
|
2368
|
-
if (!text) return { ok: false, why:
|
|
2506
|
+
if (!text) return { ok: false, why: t18("approver.no_output") };
|
|
2369
2507
|
let raw;
|
|
2370
2508
|
try {
|
|
2371
2509
|
raw = JSON.parse(text);
|
|
2372
2510
|
} catch {
|
|
2373
|
-
return { ok: false, why:
|
|
2511
|
+
return { ok: false, why: t18("approver.not_json", { text: clip(text) }) };
|
|
2374
2512
|
}
|
|
2375
2513
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
2376
|
-
return { ok: false, why:
|
|
2514
|
+
return { ok: false, why: t18("approver.not_object", { text: clip(text) }) };
|
|
2377
2515
|
}
|
|
2378
2516
|
const rec = raw;
|
|
2379
2517
|
const outcome = rec["outcome"];
|
|
2380
2518
|
if (typeof outcome !== "string" || !isApprovalOutcome2(outcome)) {
|
|
2381
2519
|
return {
|
|
2382
2520
|
ok: false,
|
|
2383
|
-
why:
|
|
2521
|
+
why: t18("approver.bad_outcome", {
|
|
2384
2522
|
value: clip(String(outcome)),
|
|
2385
2523
|
choices: APPROVAL_OUTCOMES2.join(" / ")
|
|
2386
2524
|
})
|
|
@@ -2416,7 +2554,7 @@ function askExternalApprover(program2, payload, opts = {}) {
|
|
|
2416
2554
|
});
|
|
2417
2555
|
const timer = setTimeout(() => {
|
|
2418
2556
|
child.kill("SIGKILL");
|
|
2419
|
-
settle({ ok: false, why:
|
|
2557
|
+
settle({ ok: false, why: t18("approver.timeout", { ms: timeoutMs }) });
|
|
2420
2558
|
}, timeoutMs);
|
|
2421
2559
|
const done = (v) => {
|
|
2422
2560
|
clearTimeout(timer);
|
|
@@ -2426,15 +2564,15 @@ function askExternalApprover(program2, payload, opts = {}) {
|
|
|
2426
2564
|
out2 += d.toString("utf-8");
|
|
2427
2565
|
if (out2.length > MAX_STDOUT_BYTES) {
|
|
2428
2566
|
child.kill("SIGKILL");
|
|
2429
|
-
done({ ok: false, why:
|
|
2567
|
+
done({ ok: false, why: t18("approver.too_much_output", { bytes: MAX_STDOUT_BYTES }) });
|
|
2430
2568
|
}
|
|
2431
2569
|
});
|
|
2432
2570
|
child.on("error", (err2) => {
|
|
2433
|
-
done({ ok: false, why:
|
|
2571
|
+
done({ ok: false, why: t18("approver.spawn_failed", { message: err2.message }) });
|
|
2434
2572
|
});
|
|
2435
2573
|
child.on("close", (code) => {
|
|
2436
2574
|
if (code !== 0) {
|
|
2437
|
-
done({ ok: false, why:
|
|
2575
|
+
done({ ok: false, why: t18("approver.bad_exit", { code: code ?? -1 }) });
|
|
2438
2576
|
return;
|
|
2439
2577
|
}
|
|
2440
2578
|
done(parseApproverVerdict(out2));
|
|
@@ -2468,7 +2606,7 @@ function createExternalApproval(opts) {
|
|
|
2468
2606
|
const hit = req.plan ? null : opts.preauth?.(req) ?? null;
|
|
2469
2607
|
if (hit) {
|
|
2470
2608
|
write(
|
|
2471
|
-
`${
|
|
2609
|
+
`${t18("approver.audit_preauth", {
|
|
2472
2610
|
tool: req.toolName,
|
|
2473
2611
|
target: req.target,
|
|
2474
2612
|
reason: hit
|
|
@@ -2480,7 +2618,7 @@ function createExternalApproval(opts) {
|
|
|
2480
2618
|
const verdict = await ask(payloadOf(req, cwd, opts.sessionId?.()));
|
|
2481
2619
|
if (!verdict.ok) {
|
|
2482
2620
|
write(
|
|
2483
|
-
`${
|
|
2621
|
+
`${t18("approver.audit_rejected", {
|
|
2484
2622
|
tool: req.toolName,
|
|
2485
2623
|
target: req.target,
|
|
2486
2624
|
why: verdict.why
|
|
@@ -2492,7 +2630,7 @@ function createExternalApproval(opts) {
|
|
|
2492
2630
|
const { outcome } = verdict.answer;
|
|
2493
2631
|
const vars = { tool: req.toolName, target: req.target, outcome };
|
|
2494
2632
|
write(
|
|
2495
|
-
`${outcome === "deny" ?
|
|
2633
|
+
`${outcome === "deny" ? t18("approver.audit_denied", vars) : t18("approver.audit_allowed", vars)}
|
|
2496
2634
|
`
|
|
2497
2635
|
);
|
|
2498
2636
|
return verdict.answer;
|
|
@@ -2500,35 +2638,35 @@ function createExternalApproval(opts) {
|
|
|
2500
2638
|
}
|
|
2501
2639
|
// src/headless/tasks.ts
|
|
2502
2640
|
import { describeBackgroundTask } from "@epoch-agent/core";
|
|
2503
|
-
import { t as
|
|
2641
|
+
import { t as t19 } from "@epoch-agent/infra";
|
|
2504
2642
|
function backgroundTaskSummary(all) {
|
|
2505
|
-
const tasks = all.filter((
|
|
2643
|
+
const tasks = all.filter((t40) => t40.kind === "command");
|
|
2506
2644
|
if (tasks.length === 0) return "";
|
|
2507
|
-
const running = tasks.filter((
|
|
2508
|
-
const head = running > 0 ?
|
|
2509
|
-
return [head, ...tasks.map((
|
|
2645
|
+
const running = tasks.filter((t40) => t40.status === "running").length;
|
|
2646
|
+
const head = running > 0 ? t19("background_tasks.head_running", { total: tasks.length, running }) : t19("background_tasks.head_done", { total: tasks.length });
|
|
2647
|
+
return [head, ...tasks.map((t40) => ` ${describeBackgroundTask(t40)}`)].join("\n");
|
|
2510
2648
|
}
|
|
2511
2649
|
// src/import-prompt.ts
|
|
2512
2650
|
import {
|
|
2513
2651
|
createImportGate,
|
|
2514
2652
|
ImportTrustStore,
|
|
2515
2653
|
isNonInteractive as isNonInteractive6,
|
|
2516
|
-
loadConfig as
|
|
2654
|
+
loadConfig as loadConfig3,
|
|
2517
2655
|
resolveProjectRoot as resolveProjectRoot2,
|
|
2518
2656
|
scanInstructions,
|
|
2519
2657
|
setImportGate,
|
|
2520
2658
|
TrustManager as TrustManager2
|
|
2521
2659
|
} from "@epoch-agent/core";
|
|
2522
|
-
import { t as
|
|
2660
|
+
import { t as t21, trustedImportsPath, trustPath as trustPath2 } from "@epoch-agent/infra";
|
|
2523
2661
|
// src/trust-prompt.ts
|
|
2524
2662
|
import {
|
|
2525
2663
|
findProjectInstructions,
|
|
2526
2664
|
isNonInteractive as isNonInteractive5,
|
|
2527
|
-
loadConfig,
|
|
2665
|
+
loadConfig as loadConfig2,
|
|
2528
2666
|
resolveProjectRoot,
|
|
2529
2667
|
TrustManager
|
|
2530
2668
|
} from "@epoch-agent/core";
|
|
2531
|
-
import { t as
|
|
2669
|
+
import { t as t20, trustPath } from "@epoch-agent/infra";
|
|
2532
2670
|
function trustPromptTarget(deps) {
|
|
2533
2671
|
if (!deps.gateEnabled) return null;
|
|
2534
2672
|
if (!deps.interactive) return null;
|
|
@@ -2541,29 +2679,29 @@ function trustPromptTarget(deps) {
|
|
|
2541
2679
|
function applyTrustChoice(store, root, choice) {
|
|
2542
2680
|
if (choice.kind === "trust") {
|
|
2543
2681
|
store.record(root, "trusted", choice.scope);
|
|
2544
|
-
const suffix = choice.scope === "directory-tree" ?
|
|
2545
|
-
return
|
|
2682
|
+
const suffix = choice.scope === "directory-tree" ? t20("trust_prompt.granted_tree_suffix") : "";
|
|
2683
|
+
return t20("trust_prompt.granted", { root, suffix });
|
|
2546
2684
|
}
|
|
2547
2685
|
if (choice.kind === "never") {
|
|
2548
2686
|
store.record(root, "untrusted", "directory");
|
|
2549
|
-
return
|
|
2687
|
+
return t20("trust_prompt.denied", { root });
|
|
2550
2688
|
}
|
|
2551
|
-
return
|
|
2689
|
+
return t20("trust_prompt.skipped");
|
|
2552
2690
|
}
|
|
2553
2691
|
function trustPromptMessage(target) {
|
|
2554
2692
|
return [
|
|
2555
2693
|
`
|
|
2556
|
-
${
|
|
2557
|
-
` ${
|
|
2694
|
+
${t20("trust_prompt.head", { path: target.instructionsPath })}`,
|
|
2695
|
+
` ${t20("trust_prompt.why")}`,
|
|
2558
2696
|
""
|
|
2559
2697
|
].join("\n");
|
|
2560
2698
|
}
|
|
2561
2699
|
function choices() {
|
|
2562
2700
|
return [
|
|
2563
|
-
{ name:
|
|
2564
|
-
{ name:
|
|
2565
|
-
{ name:
|
|
2566
|
-
{ name:
|
|
2701
|
+
{ name: t20("trust_prompt.choice_skip"), value: { kind: "skip" } },
|
|
2702
|
+
{ name: t20("trust_prompt.choice_dir"), value: { kind: "trust", scope: "directory" } },
|
|
2703
|
+
{ name: t20("trust_prompt.choice_tree"), value: { kind: "trust", scope: "directory-tree" } },
|
|
2704
|
+
{ name: t20("trust_prompt.choice_never"), value: { kind: "never" } }
|
|
2567
2705
|
];
|
|
2568
2706
|
}
|
|
2569
2707
|
function restoreStdinDefault() {
|
|
@@ -2572,7 +2710,7 @@ function restoreStdinDefault() {
|
|
|
2572
2710
|
stdin.resume();
|
|
2573
2711
|
}
|
|
2574
2712
|
async function maybePromptForTrust(opts = {}) {
|
|
2575
|
-
const config = opts.config ??
|
|
2713
|
+
const config = opts.config ?? loadConfig2();
|
|
2576
2714
|
const store = opts.store ?? new TrustManager(trustPath(config.homeDir));
|
|
2577
2715
|
const print = opts.print ?? ((msg) => process.stdout.write(msg + "\n"));
|
|
2578
2716
|
const target = trustPromptTarget({
|
|
@@ -2594,7 +2732,7 @@ async function maybePromptForTrust(opts = {}) {
|
|
|
2594
2732
|
print(applyTrustChoice(store, target.root, choice));
|
|
2595
2733
|
} catch (err2) {
|
|
2596
2734
|
print(
|
|
2597
|
-
|
|
2735
|
+
t20("trust_prompt.write_failed", {
|
|
2598
2736
|
message: err2 instanceof Error ? err2.message : String(err2)
|
|
2599
2737
|
})
|
|
2600
2738
|
);
|
|
@@ -2606,7 +2744,7 @@ async function askTrustChoice(_target) {
|
|
|
2606
2744
|
try {
|
|
2607
2745
|
const options = choices();
|
|
2608
2746
|
return await select2({
|
|
2609
|
-
message:
|
|
2747
|
+
message: t20("trust_prompt.ask"),
|
|
2610
2748
|
choices: [...options],
|
|
2611
2749
|
default: options[0]?.value
|
|
2612
2750
|
});
|
|
@@ -2639,38 +2777,38 @@ function externalImportTargets(deps) {
|
|
|
2639
2777
|
function importPromptMessage(target) {
|
|
2640
2778
|
return [
|
|
2641
2779
|
`
|
|
2642
|
-
${
|
|
2780
|
+
${t21("import_prompt.head", { from: target.from })}`,
|
|
2643
2781
|
` ${target.path}`,
|
|
2644
|
-
` ${
|
|
2645
|
-
` ${
|
|
2782
|
+
` ${t21("import_prompt.spec", { spec: target.spec })}`,
|
|
2783
|
+
` ${t21("import_prompt.why")}`,
|
|
2646
2784
|
""
|
|
2647
2785
|
].join("\n");
|
|
2648
2786
|
}
|
|
2649
2787
|
function applyImportChoice(store, target, choice) {
|
|
2650
2788
|
if (choice.kind === "once") {
|
|
2651
2789
|
store.allowOnce(target.path);
|
|
2652
|
-
return
|
|
2790
|
+
return t21("import_prompt.once", { path: target.path });
|
|
2653
2791
|
}
|
|
2654
2792
|
if (choice.kind === "remember") {
|
|
2655
2793
|
store.remember(target.path);
|
|
2656
|
-
return
|
|
2794
|
+
return t21("import_prompt.remembered", { path: target.path });
|
|
2657
2795
|
}
|
|
2658
2796
|
if (choice.kind === "never") {
|
|
2659
2797
|
store.denyAll();
|
|
2660
|
-
return
|
|
2798
|
+
return t21("import_prompt.never");
|
|
2661
2799
|
}
|
|
2662
|
-
return
|
|
2800
|
+
return t21("import_prompt.skipped", { path: target.path });
|
|
2663
2801
|
}
|
|
2664
2802
|
function choices2() {
|
|
2665
2803
|
return [
|
|
2666
|
-
{ name:
|
|
2667
|
-
{ name:
|
|
2668
|
-
{ name:
|
|
2669
|
-
{ name:
|
|
2804
|
+
{ name: t21("import_prompt.choice_skip"), value: { kind: "skip" } },
|
|
2805
|
+
{ name: t21("import_prompt.choice_once"), value: { kind: "once" } },
|
|
2806
|
+
{ name: t21("import_prompt.choice_remember"), value: { kind: "remember" } },
|
|
2807
|
+
{ name: t21("import_prompt.choice_never"), value: { kind: "never" } }
|
|
2670
2808
|
];
|
|
2671
2809
|
}
|
|
2672
2810
|
async function maybePromptForExternalImports(opts = {}) {
|
|
2673
|
-
const config = opts.config ??
|
|
2811
|
+
const config = opts.config ?? loadConfig3();
|
|
2674
2812
|
const store = opts.store ?? new ImportTrustStore(trustedImportsPath(config.homeDir));
|
|
2675
2813
|
const print = opts.print ?? ((msg) => process.stdout.write(msg + "\n"));
|
|
2676
2814
|
const targets = externalImportTargets({
|
|
@@ -2693,7 +2831,7 @@ async function maybePromptForExternalImports(opts = {}) {
|
|
|
2693
2831
|
print(applyImportChoice(store, target, choice));
|
|
2694
2832
|
} catch (err2) {
|
|
2695
2833
|
print(
|
|
2696
|
-
|
|
2834
|
+
t21("import_prompt.write_failed", {
|
|
2697
2835
|
message: err2 instanceof Error ? err2.message : String(err2)
|
|
2698
2836
|
})
|
|
2699
2837
|
);
|
|
@@ -2711,7 +2849,7 @@ async function askImportChoice(_target) {
|
|
|
2711
2849
|
try {
|
|
2712
2850
|
const options = choices2();
|
|
2713
2851
|
return await select2({
|
|
2714
|
-
message:
|
|
2852
|
+
message: t21("import_prompt.ask"),
|
|
2715
2853
|
choices: [...options],
|
|
2716
2854
|
default: options[0]?.value
|
|
2717
2855
|
});
|
|
@@ -2762,7 +2900,7 @@ function createStreamWriter(enabled, sink) {
|
|
|
2762
2900
|
};
|
|
2763
2901
|
}
|
|
2764
2902
|
// src/commands/run-once.ts
|
|
2765
|
-
async function
|
|
2903
|
+
async function readStdin2() {
|
|
2766
2904
|
if (process.stdin.isTTY) return "";
|
|
2767
2905
|
const chunks = [];
|
|
2768
2906
|
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
@@ -2788,7 +2926,7 @@ function composeContent(query, images) {
|
|
|
2788
2926
|
throw new CliError(
|
|
2789
2927
|
err2 instanceof Error ? err2.message : String(err2),
|
|
2790
2928
|
EXIT_CODES.FAILURE,
|
|
2791
|
-
|
|
2929
|
+
t22("run_once.err_image")
|
|
2792
2930
|
);
|
|
2793
2931
|
}
|
|
2794
2932
|
}
|
|
@@ -2797,9 +2935,9 @@ async function runOnce(query, opts, resumeId) {
|
|
|
2797
2935
|
const { policy: headless, invalid } = parseHeadlessFlags(opts);
|
|
2798
2936
|
if (invalid.length > 0) {
|
|
2799
2937
|
throw new CliError(
|
|
2800
|
-
|
|
2938
|
+
t22("run_once.err_bad_operation", { values: invalid.join(", ") }),
|
|
2801
2939
|
EXIT_CODES.FAILURE,
|
|
2802
|
-
|
|
2940
|
+
t22("run_once.err_bad_operation_hint", { choices: OPERATION_TYPES2.join(" / ") })
|
|
2803
2941
|
);
|
|
2804
2942
|
}
|
|
2805
2943
|
const format = resolveOutputFormat(opts);
|
|
@@ -2819,7 +2957,7 @@ async function runOnce(query, opts, resumeId) {
|
|
|
2819
2957
|
sessionId: () => rt?.sessionId,
|
|
2820
2958
|
preauth: (req) => preauthorizedBy(rt?.config.headless, headless, req)
|
|
2821
2959
|
}) : createInteractiveApproval({ permission: () => rt?.permission }),
|
|
2822
|
-
installSignalHandlers:
|
|
2960
|
+
installSignalHandlers: false,
|
|
2823
2961
|
...approver ? { interactive: true } : {},
|
|
2824
2962
|
...headless ? { headless } : {},
|
|
2825
2963
|
...resumeId ? { resumeId } : {}
|
|
@@ -2829,18 +2967,19 @@ async function runOnce(query, opts, resumeId) {
|
|
|
2829
2967
|
const detail = runtime.diagnostics.join("\n ");
|
|
2830
2968
|
await runtime.dispose();
|
|
2831
2969
|
throw new CliError(
|
|
2832
|
-
|
|
2970
|
+
t22("run_once.err_no_provider"),
|
|
2833
2971
|
EXIT_CODES.FAILURE,
|
|
2834
|
-
detail ? `${
|
|
2835
|
-
${detail}` :
|
|
2972
|
+
detail ? `${t22("run_once.err_no_provider_diag")}
|
|
2973
|
+
${detail}` : t22("run_once.err_no_provider_hint")
|
|
2836
2974
|
);
|
|
2837
2975
|
}
|
|
2976
|
+
installShutdown(runtime);
|
|
2838
2977
|
try {
|
|
2839
2978
|
if (format !== "json") reportDiagnostics(runtime.diagnostics, opts.verbose === true);
|
|
2840
2979
|
if (resumeId) {
|
|
2841
2980
|
const restored = runtime.session.getHistory().length;
|
|
2842
2981
|
process.stderr.write(
|
|
2843
|
-
`${
|
|
2982
|
+
`${t22("run_once.resumed", {
|
|
2844
2983
|
id: resumeId.slice(0, SHORT_ID_LEN),
|
|
2845
2984
|
count: restored
|
|
2846
2985
|
})}
|
|
@@ -2858,12 +2997,23 @@ async function runOnce(query, opts, resumeId) {
|
|
|
2858
2997
|
}
|
|
2859
2998
|
const limited = limitExitCode(outcome, limits);
|
|
2860
2999
|
if (limited !== void 0 && !process.exitCode) process.exitCode = limited;
|
|
2861
|
-
if (outcome.aborted && !process.exitCode) process.exitCode =
|
|
3000
|
+
if (outcome.aborted && !process.exitCode) process.exitCode = INTERRUPTED_EXIT_CODE;
|
|
2862
3001
|
} finally {
|
|
2863
3002
|
reportHeadlessAudit(runtime.permission);
|
|
2864
3003
|
await runtime.dispose();
|
|
2865
3004
|
}
|
|
2866
3005
|
}
|
|
3006
|
+
var INTERRUPTED_EXIT_CODE = 130;
|
|
3007
|
+
function installShutdown(runtime) {
|
|
3008
|
+
let shuttingDown = false;
|
|
3009
|
+
const onSignal = (code) => () => {
|
|
3010
|
+
if (shuttingDown) process.exit(EXIT_CODES.FAILURE);
|
|
3011
|
+
shuttingDown = true;
|
|
3012
|
+
void runtime.dispose().finally(() => process.exit(code));
|
|
3013
|
+
};
|
|
3014
|
+
process.on("SIGINT", onSignal(INTERRUPTED_EXIT_CODE));
|
|
3015
|
+
process.on("SIGTERM", onSignal(EXIT_CODES.SUCCESS));
|
|
3016
|
+
}
|
|
2867
3017
|
async function consume(runtime, query, opts, format, limits, emitter) {
|
|
2868
3018
|
const out2 = emptyOutcome(runtime.diagnostics);
|
|
2869
3019
|
const streaming = opts.stream !== false && format === "text";
|
|
@@ -2903,7 +3053,7 @@ function collect2(out2, ev, w) {
|
|
|
2903
3053
|
} else if (ev.type === "usage") {
|
|
2904
3054
|
out2.usage = ev.cumulative;
|
|
2905
3055
|
} else if (ev.type === "error") {
|
|
2906
|
-
out2.text =
|
|
3056
|
+
out2.text = t22("run_once.error_prefix", { message: ev.message });
|
|
2907
3057
|
process.exitCode = EXIT_CODES.FAILURE;
|
|
2908
3058
|
} else if (ev.type === "finish") {
|
|
2909
3059
|
out2.finishReason = ev.reason;
|
|
@@ -2938,14 +3088,14 @@ function emitOutcome(out2, opts, format, emitter, tasks = []) {
|
|
|
2938
3088
|
return;
|
|
2939
3089
|
}
|
|
2940
3090
|
if (out2.reasoning) {
|
|
2941
|
-
process.stderr.write(`${
|
|
3091
|
+
process.stderr.write(`${t22("run_once.reasoning_head")}
|
|
2942
3092
|
${out2.reasoning}
|
|
2943
3093
|
|
|
2944
3094
|
`);
|
|
2945
3095
|
}
|
|
2946
3096
|
if (opts.stream === false && out2.text) process.stdout.write(out2.text + "\n");
|
|
2947
3097
|
if (out2.aborted) process.stderr.write(`
|
|
2948
|
-
${
|
|
3098
|
+
${t22("run_once.aborted")}
|
|
2949
3099
|
`);
|
|
2950
3100
|
if (taskLines) process.stderr.write(taskLines + "\n");
|
|
2951
3101
|
const summary = formatSummary(out2.usage, out2.budgetExceeded);
|
|
@@ -2962,10 +3112,10 @@ function noteworthy(diagnostics, verbose) {
|
|
|
2962
3112
|
}
|
|
2963
3113
|
function formatSummary(usage, budgetExceeded) {
|
|
2964
3114
|
if (!usage) return "";
|
|
2965
|
-
const cached = usage.cacheHitTokens ?
|
|
3115
|
+
const cached = usage.cacheHitTokens ? t22("run_once.cache_hit", { count: usage.cacheHitTokens }) : "";
|
|
2966
3116
|
const tokens = `${promptTokens(usage)} in${cached} / ${usage.outputTokens} out`;
|
|
2967
|
-
const cost = usage.costUsd === void 0 ?
|
|
2968
|
-
const stopped = budgetExceeded ?
|
|
3117
|
+
const cost = usage.costUsd === void 0 ? t22("run_once.cost_unknown") : formatUsd(usage.costUsd);
|
|
3118
|
+
const stopped = budgetExceeded ? t22("run_once.budget_reached") : "";
|
|
2969
3119
|
return `\u2500 ${tokens} \xB7 ${cost}${stopped}`;
|
|
2970
3120
|
}
|
|
2971
3121
|
function reportHeadlessAudit(permission) {
|
|
@@ -2974,7 +3124,7 @@ function reportHeadlessAudit(permission) {
|
|
|
2974
3124
|
for (const e of audit) {
|
|
2975
3125
|
const vars = { tool: e.toolName, target: e.target, reason: e.reason };
|
|
2976
3126
|
process.stderr.write(
|
|
2977
|
-
`${e.outcome === "granted" ?
|
|
3127
|
+
`${e.outcome === "granted" ? t22("run_once.audit_granted", vars) : t22("run_once.audit_denied", vars)}
|
|
2978
3128
|
`
|
|
2979
3129
|
);
|
|
2980
3130
|
}
|
|
@@ -2984,7 +3134,7 @@ function reportHeadlessAudit(permission) {
|
|
|
2984
3134
|
}
|
|
2985
3135
|
// src/commands/run-overrides.ts
|
|
2986
3136
|
import { isPermissionLevel as isPermissionLevel2, PERMISSION_LEVELS as PERMISSION_LEVELS2 } from "@epoch-agent/core";
|
|
2987
|
-
import { t as
|
|
3137
|
+
import { t as t23 } from "@epoch-agent/infra";
|
|
2988
3138
|
import { isProviderType as isProviderType2, PROVIDER_TYPES as PROVIDER_TYPES3 } from "@epoch-agent/protocol";
|
|
2989
3139
|
function applyRunFlags(opts) {
|
|
2990
3140
|
applySettingsPath(opts.settings);
|
|
@@ -3003,9 +3153,9 @@ function applyOverrides(opts) {
|
|
|
3003
3153
|
if (opts.provider) {
|
|
3004
3154
|
if (!isProviderType2(opts.provider)) {
|
|
3005
3155
|
throw new CliError(
|
|
3006
|
-
|
|
3156
|
+
t23("flags.provider_unknown", { value: opts.provider }),
|
|
3007
3157
|
1,
|
|
3008
|
-
|
|
3158
|
+
t23("flags.choices", { choices: PROVIDER_TYPES3.join(", ") })
|
|
3009
3159
|
);
|
|
3010
3160
|
}
|
|
3011
3161
|
process.env.EPOCH_PROVIDER = opts.provider;
|
|
@@ -3013,9 +3163,9 @@ function applyOverrides(opts) {
|
|
|
3013
3163
|
if (opts.permission) {
|
|
3014
3164
|
if (!isPermissionLevel2(opts.permission)) {
|
|
3015
3165
|
throw new CliError(
|
|
3016
|
-
|
|
3166
|
+
t23("flags.permission_unknown", { value: opts.permission }),
|
|
3017
3167
|
1,
|
|
3018
|
-
|
|
3168
|
+
t23("flags.choices", { choices: PERMISSION_LEVELS2.join(", ") })
|
|
3019
3169
|
);
|
|
3020
3170
|
}
|
|
3021
3171
|
process.env.EPOCH_PERMISSION = opts.permission;
|
|
@@ -3026,9 +3176,9 @@ function assertModelName(value, flag) {
|
|
|
3026
3176
|
const spec = value.trim();
|
|
3027
3177
|
if (MODEL_ID.test(spec)) return value;
|
|
3028
3178
|
throw new CliError(
|
|
3029
|
-
|
|
3179
|
+
t23("flags.model_not_a_name", { flag, value }),
|
|
3030
3180
|
1,
|
|
3031
|
-
|
|
3181
|
+
t23("flags.model_not_a_name_hint", { value: spec })
|
|
3032
3182
|
);
|
|
3033
3183
|
}
|
|
3034
3184
|
function assertHttpUrl(value) {
|
|
@@ -3038,60 +3188,60 @@ function assertHttpUrl(value) {
|
|
|
3038
3188
|
} catch {
|
|
3039
3189
|
}
|
|
3040
3190
|
throw new CliError(
|
|
3041
|
-
|
|
3191
|
+
t23("flags.base_url_invalid", { value }),
|
|
3042
3192
|
1,
|
|
3043
|
-
|
|
3193
|
+
t23("flags.base_url_invalid_hint", { value: value.trim() })
|
|
3044
3194
|
);
|
|
3045
3195
|
}
|
|
3046
3196
|
// src/commands/sessions.ts
|
|
3047
3197
|
import { isNonInteractive as isNonInteractive7, resolveProjectRoot as resolveProjectRoot3, SessionManager } from "@epoch-agent/core";
|
|
3048
|
-
import { dbPath, t as
|
|
3049
|
-
var
|
|
3198
|
+
import { dbPath, t as t24, uiDateLocale as uiDateLocale2 } from "@epoch-agent/infra";
|
|
3199
|
+
var log7 = (msg) => process.stdout.write(msg + "\n");
|
|
3050
3200
|
var SHORT_ID_LEN2 = 8;
|
|
3051
3201
|
var PICK_LIMIT = 20;
|
|
3052
3202
|
function whenOf(startedAt) {
|
|
3053
|
-
return startedAt ? new Date(startedAt).toLocaleString(uiDateLocale2()) :
|
|
3203
|
+
return startedAt ? new Date(startedAt).toLocaleString(uiDateLocale2()) : t24("sessions.when_unknown");
|
|
3054
3204
|
}
|
|
3055
3205
|
function titleOf(s) {
|
|
3056
|
-
return s.title || s.preview ||
|
|
3206
|
+
return s.title || s.preview || t24("sessions.untitled");
|
|
3057
3207
|
}
|
|
3058
3208
|
function registerSessionsCommand(program2) {
|
|
3059
|
-
const cmd = program2.command("sessions").alias("session").description(
|
|
3060
|
-
cmd.command("list").description(
|
|
3209
|
+
const cmd = program2.command("sessions").alias("session").description(t24("sessions.cmd_root"));
|
|
3210
|
+
cmd.command("list").description(t24("sessions.cmd_list")).option("-n, --limit <n>", t24("sessions.opt_limit"), "10").action((opts) => {
|
|
3061
3211
|
withSessions((sm) => {
|
|
3062
3212
|
const r = sm.list({ limit: parseInt(opts.limit, 10) });
|
|
3063
3213
|
if (r.sessions.length === 0) {
|
|
3064
|
-
|
|
3214
|
+
log7(` ${t24("sessions.list_empty")}`);
|
|
3065
3215
|
return;
|
|
3066
3216
|
}
|
|
3067
3217
|
for (const s of r.sessions) {
|
|
3068
|
-
|
|
3218
|
+
log7(` ${s.id.slice(0, SHORT_ID_LEN2)} ${whenOf(s.startedAt)} ${titleOf(s)}`);
|
|
3069
3219
|
}
|
|
3070
3220
|
});
|
|
3071
3221
|
});
|
|
3072
|
-
cmd.command("resume").description(
|
|
3222
|
+
cmd.command("resume").description(t24("sessions.cmd_resume")).argument("[id]", t24("sessions.arg_id")).argument("[query]", t24("sessions.arg_query")).action(async (id, query) => {
|
|
3073
3223
|
const resolved = resolveSessionId(id);
|
|
3074
3224
|
if (!resolved.ok) {
|
|
3075
3225
|
throw new CliError(resolved.error, EXIT_CODES.FAILURE);
|
|
3076
3226
|
}
|
|
3077
|
-
await runOnce(query ||
|
|
3227
|
+
await runOnce(query || t24("sessions.default_query"), {}, resolved.id);
|
|
3078
3228
|
});
|
|
3079
3229
|
}
|
|
3080
3230
|
function resolveSessionId(input2) {
|
|
3081
3231
|
return withSessions((sm) => {
|
|
3082
3232
|
if (!input2) {
|
|
3083
3233
|
const latest = sm.getLatest()?.meta.id;
|
|
3084
|
-
return latest ? { ok: true, id: latest } : { ok: false, error:
|
|
3234
|
+
return latest ? { ok: true, id: latest } : { ok: false, error: t24("sessions.err_none") };
|
|
3085
3235
|
}
|
|
3086
3236
|
if (sm.get(input2)) return { ok: true, id: input2 };
|
|
3087
3237
|
const candidates = sm.list({ limit: 200 }).sessions.filter((s) => s.id.startsWith(input2)).map((s) => s.id);
|
|
3088
3238
|
if (candidates.length === 1) return { ok: true, id: candidates[0] };
|
|
3089
3239
|
if (candidates.length === 0) {
|
|
3090
|
-
return { ok: false, error:
|
|
3240
|
+
return { ok: false, error: t24("sessions.err_not_found", { input: input2 }) };
|
|
3091
3241
|
}
|
|
3092
3242
|
return {
|
|
3093
3243
|
ok: false,
|
|
3094
|
-
error:
|
|
3244
|
+
error: t24("sessions.err_ambiguous", { input: input2, count: candidates.length })
|
|
3095
3245
|
};
|
|
3096
3246
|
});
|
|
3097
3247
|
}
|
|
@@ -3102,9 +3252,9 @@ function latestSessionIdForProject(cwd = process.cwd()) {
|
|
|
3102
3252
|
async function pickSessionId() {
|
|
3103
3253
|
if (isNonInteractive7()) {
|
|
3104
3254
|
throw new CliError(
|
|
3105
|
-
|
|
3255
|
+
t24("sessions.err_needs_id"),
|
|
3106
3256
|
EXIT_CODES.FAILURE,
|
|
3107
|
-
|
|
3257
|
+
t24("sessions.err_needs_id_hint")
|
|
3108
3258
|
);
|
|
3109
3259
|
}
|
|
3110
3260
|
const rows = withSessions(
|
|
@@ -3112,22 +3262,22 @@ async function pickSessionId() {
|
|
|
3112
3262
|
id: s.id,
|
|
3113
3263
|
title: titleOf(s),
|
|
3114
3264
|
when: whenOf(s.startedAt),
|
|
3115
|
-
where: s.cwd ??
|
|
3265
|
+
where: s.cwd ?? t24("sessions.cwd_unrecorded"),
|
|
3116
3266
|
messageCount: s.messageCount
|
|
3117
3267
|
}))
|
|
3118
3268
|
);
|
|
3119
3269
|
if (rows.length === 0) {
|
|
3120
|
-
|
|
3270
|
+
log7(t24("sessions.pick_empty"));
|
|
3121
3271
|
return void 0;
|
|
3122
3272
|
}
|
|
3123
3273
|
const { select: select2 } = await import("@inquirer/prompts");
|
|
3124
3274
|
try {
|
|
3125
3275
|
return await select2({
|
|
3126
|
-
message:
|
|
3276
|
+
message: t24("sessions.pick_prompt"),
|
|
3127
3277
|
choices: rows.map((r) => ({
|
|
3128
3278
|
name: `${r.id.slice(0, SHORT_ID_LEN2)} ${r.when} ${r.title}`,
|
|
3129
3279
|
value: r.id,
|
|
3130
|
-
description: `${
|
|
3280
|
+
description: `${t24("tui.resume.message_count", { n: r.messageCount })} \xB7 ${r.where}`
|
|
3131
3281
|
})),
|
|
3132
3282
|
pageSize: 12
|
|
3133
3283
|
});
|
|
@@ -3144,27 +3294,27 @@ function withSessions(fn) {
|
|
|
3144
3294
|
}
|
|
3145
3295
|
}
|
|
3146
3296
|
// src/commands/run.ts
|
|
3147
|
-
var
|
|
3297
|
+
var log8 = (msg) => {
|
|
3148
3298
|
process.stdout.write(msg + "\n");
|
|
3149
3299
|
};
|
|
3150
3300
|
function registerRunCommand(program2) {
|
|
3151
|
-
program2.argument("[query]",
|
|
3301
|
+
program2.argument("[query]", t25("run.arg_query")).option("--allow-tool <name>", t25("run.opt_allow_tool"), collect, []).option(
|
|
3152
3302
|
"--allow-operation <type>",
|
|
3153
|
-
|
|
3303
|
+
t25("run.opt_allow_operation", { types: OPERATION_TYPES3.join(" / ") }),
|
|
3154
3304
|
collect,
|
|
3155
3305
|
[]
|
|
3156
|
-
).option("-i, --image <path>",
|
|
3306
|
+
).option("-i, --image <path>", t25("run.opt_image"), collect, []).option("-c, --continue", t25("run.opt_continue")).option("-r, --resume [id]", t25("run.opt_resume")).option("-m, --model <name>", t25("run.opt_model")).option("--fallback-model <name>", t25("run.opt_fallback_model")).option("-p, --provider <type>", t25("run.opt_provider", { types: PROVIDER_TYPES4.join(" / ") })).option("--base-url <url>", t25("run.opt_base_url")).option(
|
|
3157
3307
|
"--permission <level>",
|
|
3158
|
-
|
|
3159
|
-
).option("--settings <file>",
|
|
3308
|
+
t25("run.opt_permission", { levels: PERMISSION_LEVELS3.join(" / ") })
|
|
3309
|
+
).option("--settings <file>", t25("run.opt_settings")).option("--add-dir <dir>", t25("run.opt_add_dir"), collect, []).option("--agent <role>", t25("run.opt_agent")).option("--worktree", t25("run.opt_worktree")).option("--json", t25("run.opt_json")).option(
|
|
3160
3310
|
"--output-format <fmt>",
|
|
3161
|
-
|
|
3311
|
+
t25("run.opt_output_format", { formats: HEADLESS_OUTPUT_FORMATS2.join(" / ") })
|
|
3162
3312
|
).option(
|
|
3163
3313
|
"--input-format <fmt>",
|
|
3164
|
-
|
|
3165
|
-
).option("--permission-prompt-tool <program>",
|
|
3314
|
+
t25("run.opt_input_format", { formats: HEADLESS_INPUT_FORMATS2.join(" / ") })
|
|
3315
|
+
).option("--permission-prompt-tool <program>", t25("run.opt_permission_prompt_tool")).option("--max-turns <n>", t25("run.opt_max_turns")).option("--max-budget-usd <x>", t25("run.opt_max_budget")).option("--no-stream", t25("run.opt_no_stream")).option("-v, --verbose", t25("run.opt_verbose")).option("-V, --version", t25("run.opt_version")).action(async (query, opts) => {
|
|
3166
3316
|
if (opts.version === true) {
|
|
3167
|
-
|
|
3317
|
+
log8(describeVersion(selfPackage().name));
|
|
3168
3318
|
return;
|
|
3169
3319
|
}
|
|
3170
3320
|
applyRunFlags(opts);
|
|
@@ -3174,39 +3324,39 @@ function registerRunCommand(program2) {
|
|
|
3174
3324
|
if (formats.input === "stream-json") {
|
|
3175
3325
|
if (opts.continue === true || resumeArg.kind !== "off") {
|
|
3176
3326
|
throw new CliError(
|
|
3177
|
-
|
|
3327
|
+
t25("run.conflict_resume"),
|
|
3178
3328
|
EXIT_CODES.FAILURE,
|
|
3179
|
-
|
|
3329
|
+
t25("run.conflict_resume_hint")
|
|
3180
3330
|
);
|
|
3181
3331
|
}
|
|
3182
3332
|
if (opts.worktree === true) {
|
|
3183
3333
|
throw new CliError(
|
|
3184
|
-
|
|
3334
|
+
t25("run.conflict_worktree"),
|
|
3185
3335
|
EXIT_CODES.FAILURE,
|
|
3186
|
-
|
|
3336
|
+
t25("run.conflict_worktree_hint")
|
|
3187
3337
|
);
|
|
3188
3338
|
}
|
|
3189
3339
|
if (opts.permissionPromptTool !== void 0) {
|
|
3190
3340
|
throw new CliError(
|
|
3191
|
-
|
|
3341
|
+
t25("run.conflict_prompt_tool"),
|
|
3192
3342
|
EXIT_CODES.FAILURE,
|
|
3193
|
-
|
|
3343
|
+
t25("run.conflict_prompt_tool_hint")
|
|
3194
3344
|
);
|
|
3195
3345
|
}
|
|
3196
3346
|
if (!hasCredentials()) {
|
|
3197
3347
|
throw new CliError(
|
|
3198
|
-
|
|
3348
|
+
t25("run.no_credentials_stream"),
|
|
3199
3349
|
EXIT_CODES.FAILURE,
|
|
3200
|
-
|
|
3350
|
+
t25("run.no_credentials_hint")
|
|
3201
3351
|
);
|
|
3202
3352
|
}
|
|
3203
3353
|
process.exitCode = await runStreamJsonSession(opts);
|
|
3204
3354
|
return;
|
|
3205
3355
|
}
|
|
3206
3356
|
const carried = resumeArg.kind === "pick" ? resumeArg.query : void 0;
|
|
3207
|
-
const prompt = composePrompt(query ?? carried, await
|
|
3357
|
+
const prompt = composePrompt(query ?? carried, await readStdin2());
|
|
3208
3358
|
if (!hasCredentials()) {
|
|
3209
|
-
|
|
3359
|
+
log8(t25("run.welcome"));
|
|
3210
3360
|
return;
|
|
3211
3361
|
}
|
|
3212
3362
|
const resumeId = await resolveResumeTarget(opts, resumeArg);
|
|
@@ -3232,7 +3382,7 @@ async function resolveResumeTarget(opts, resumeArg) {
|
|
|
3232
3382
|
if (opts.continue === true) {
|
|
3233
3383
|
const latest = latestSessionIdForProject();
|
|
3234
3384
|
if (!latest) {
|
|
3235
|
-
process.stderr.write(`${
|
|
3385
|
+
process.stderr.write(`${t25("run.no_history")}
|
|
3236
3386
|
`);
|
|
3237
3387
|
}
|
|
3238
3388
|
return latest;
|
|
@@ -3243,7 +3393,7 @@ async function resolveResumeTarget(opts, resumeArg) {
|
|
|
3243
3393
|
throw new CliError(
|
|
3244
3394
|
resolved.error,
|
|
3245
3395
|
EXIT_CODES.FAILURE,
|
|
3246
|
-
|
|
3396
|
+
t25("run.resume_not_id_hint")
|
|
3247
3397
|
);
|
|
3248
3398
|
}
|
|
3249
3399
|
return resolved.id;
|
|
@@ -3262,7 +3412,7 @@ function resolveTuiEntry(baseDir, exists = existsSync6) {
|
|
|
3262
3412
|
async function launchTui(resumeId) {
|
|
3263
3413
|
const target = resolveTuiEntry(dirname3(fileURLToPath3(import.meta.url)));
|
|
3264
3414
|
if (!target) {
|
|
3265
|
-
process.stderr.write(`${
|
|
3415
|
+
process.stderr.write(`${t25("run.tui_entry_missing")}
|
|
3266
3416
|
`);
|
|
3267
3417
|
process.exitCode = EXIT_CODES.FAILURE;
|
|
3268
3418
|
return;
|
|
@@ -3277,7 +3427,7 @@ async function launchTui(resumeId) {
|
|
|
3277
3427
|
});
|
|
3278
3428
|
await new Promise((done) => {
|
|
3279
3429
|
child.on("error", (err2) => {
|
|
3280
|
-
process.stderr.write(`${
|
|
3430
|
+
process.stderr.write(`${t25("run.tui_spawn_failed", { message: err2.message })}
|
|
3281
3431
|
`);
|
|
3282
3432
|
process.exitCode = EXIT_CODES.FAILURE;
|
|
3283
3433
|
done();
|
|
@@ -3289,13 +3439,13 @@ async function launchTui(resumeId) {
|
|
|
3289
3439
|
});
|
|
3290
3440
|
}
|
|
3291
3441
|
// src/commands/schedule.ts
|
|
3292
|
-
import { t as
|
|
3442
|
+
import { t as t33 } from "@epoch-agent/infra";
|
|
3293
3443
|
// src/schedule/add.ts
|
|
3294
3444
|
import {
|
|
3295
3445
|
SCHEDULE_DEFAULTS,
|
|
3296
3446
|
validateSchedule
|
|
3297
3447
|
} from "@epoch-agent/core";
|
|
3298
|
-
import { automationWorkDir, t as
|
|
3448
|
+
import { automationWorkDir, t as t28 } from "@epoch-agent/infra";
|
|
3299
3449
|
import {
|
|
3300
3450
|
isOperationType as isOperationType2,
|
|
3301
3451
|
isPermissionLevel as isPermissionLevel3,
|
|
@@ -3304,7 +3454,7 @@ import {
|
|
|
3304
3454
|
SCHEDULE_INTERVAL_MINUTES
|
|
3305
3455
|
} from "@epoch-agent/protocol";
|
|
3306
3456
|
// src/schedule/labels.ts
|
|
3307
|
-
import { t as
|
|
3457
|
+
import { t as t26 } from "@epoch-agent/infra";
|
|
3308
3458
|
import {
|
|
3309
3459
|
collectPendingApprovals
|
|
3310
3460
|
} from "@epoch-agent/protocol";
|
|
@@ -3356,35 +3506,35 @@ var DRIFT_KEYS = {
|
|
|
3356
3506
|
"never-registered": "schedule.drift.never_registered"
|
|
3357
3507
|
};
|
|
3358
3508
|
function renderIssues(issues) {
|
|
3359
|
-
return issues.map((i) => ` \xB7 ${
|
|
3509
|
+
return issues.map((i) => ` \xB7 ${t26(ISSUE_KEYS[i.code], { detail: i.detail ?? "" })}`).join("\n");
|
|
3360
3510
|
}
|
|
3361
3511
|
function statusLabel(status) {
|
|
3362
3512
|
const mark = status === "ok" ? "\u2705" : status === "failed" || status === "denied" ? "\u274C" : "\u26A0\uFE0F";
|
|
3363
|
-
return `${mark} ${
|
|
3513
|
+
return `${mark} ${t26(STATUS_KEYS[status])}`;
|
|
3364
3514
|
}
|
|
3365
3515
|
function driftLabel(kind) {
|
|
3366
|
-
return
|
|
3516
|
+
return t26(DRIFT_KEYS[kind]);
|
|
3367
3517
|
}
|
|
3368
3518
|
function describeTrigger(trigger) {
|
|
3369
3519
|
if (trigger.kind === "interval") {
|
|
3370
|
-
return trigger.everyMinutes < 60 ?
|
|
3520
|
+
return trigger.everyMinutes < 60 ? t26("schedule.trigger.every_minutes", { n: trigger.everyMinutes }) : t26("schedule.trigger.every_hours", { n: trigger.everyMinutes / 60 });
|
|
3371
3521
|
}
|
|
3372
3522
|
if (trigger.kind === "once") {
|
|
3373
|
-
return
|
|
3523
|
+
return t26("schedule.trigger.once", { date: trigger.date, at: trigger.at });
|
|
3374
3524
|
}
|
|
3375
|
-
if (trigger.cycle === "daily") return
|
|
3525
|
+
if (trigger.cycle === "daily") return t26("schedule.trigger.daily", { at: trigger.at });
|
|
3376
3526
|
if (trigger.cycle === "weekly") {
|
|
3377
|
-
const days = trigger.weekdays.map((d) =>
|
|
3378
|
-
return
|
|
3527
|
+
const days = trigger.weekdays.map((d) => t26(WEEKDAY_KEYS[d])).join("/");
|
|
3528
|
+
return t26("schedule.trigger.weekly", { days, at: trigger.at });
|
|
3379
3529
|
}
|
|
3380
|
-
return
|
|
3530
|
+
return t26("schedule.trigger.monthly", { days: trigger.days.join("/"), at: trigger.at });
|
|
3381
3531
|
}
|
|
3382
3532
|
function permissionLabel(level) {
|
|
3383
|
-
if (level === "plan") return
|
|
3384
|
-
if (level === "acceptEdits") return
|
|
3385
|
-
if (level === "bypass") return
|
|
3386
|
-
if (level === "auto") return
|
|
3387
|
-
return
|
|
3533
|
+
if (level === "plan") return t26("schedule.permission.plan");
|
|
3534
|
+
if (level === "acceptEdits") return t26("schedule.permission.accept_edits");
|
|
3535
|
+
if (level === "bypass") return t26("schedule.permission.bypass");
|
|
3536
|
+
if (level === "auto") return t26("schedule.permission.auto");
|
|
3537
|
+
return t26("schedule.permission.default");
|
|
3388
3538
|
}
|
|
3389
3539
|
// src/schedule/shared.ts
|
|
3390
3540
|
import { realpathSync as realpathSync2 } from "fs";
|
|
@@ -3393,8 +3543,8 @@ import {
|
|
|
3393
3543
|
ScheduleRegistrar,
|
|
3394
3544
|
ScheduleStore
|
|
3395
3545
|
} from "@epoch-agent/core";
|
|
3396
|
-
import { dbPath as dbPath2, t as
|
|
3397
|
-
var
|
|
3546
|
+
import { dbPath as dbPath2, t as t27, uiDateLocale as uiDateLocale3 } from "@epoch-agent/infra";
|
|
3547
|
+
var log9 = (msg) => void process.stdout.write(`${msg}
|
|
3398
3548
|
`);
|
|
3399
3549
|
var warn2 = (msg) => void process.stderr.write(`${msg}
|
|
3400
3550
|
`);
|
|
@@ -3422,24 +3572,24 @@ function mustFind(store, idOrPrefix) {
|
|
|
3422
3572
|
if (hits.length === 1) return hits[0];
|
|
3423
3573
|
if (hits.length === 0) {
|
|
3424
3574
|
throw new CliError(
|
|
3425
|
-
|
|
3575
|
+
t27("schedule.err_not_found", { id: idOrPrefix }),
|
|
3426
3576
|
EXIT_CODES.FAILURE,
|
|
3427
|
-
|
|
3577
|
+
t27("schedule.err_not_found_hint")
|
|
3428
3578
|
);
|
|
3429
3579
|
}
|
|
3430
3580
|
throw new CliError(
|
|
3431
|
-
|
|
3581
|
+
t27("schedule.err_ambiguous", { id: idOrPrefix }),
|
|
3432
3582
|
EXIT_CODES.FAILURE,
|
|
3433
3583
|
hits.map((h) => ` ${h.id} ${h.name}`).join("\n")
|
|
3434
3584
|
);
|
|
3435
3585
|
}
|
|
3436
3586
|
function reportRegistration(outcome, fatal) {
|
|
3437
3587
|
if (outcome.ok) {
|
|
3438
|
-
if (outcome.warnings.includes("source")) warn2(
|
|
3588
|
+
if (outcome.warnings.includes("source")) warn2(t27("schedule.warn_source_entry"));
|
|
3439
3589
|
return;
|
|
3440
3590
|
}
|
|
3441
|
-
const message = outcome.reason === "unsupported-platform" ?
|
|
3442
|
-
const hint = outcome.reason === "refused" && outcome.refusal === "npx" ?
|
|
3591
|
+
const message = outcome.reason === "unsupported-platform" ? t27("schedule.err_platform_unsupported") : outcome.reason === "backend-error" ? t27("schedule.err_backend", { detail: outcome.detail }) : outcome.refusal === "npx" ? t27("schedule.err_npx") : t27("schedule.err_no_host_runner");
|
|
3592
|
+
const hint = outcome.reason === "refused" && outcome.refusal === "npx" ? t27("schedule.err_npx_hint") : outcome.reason === "refused" ? t27("schedule.err_no_host_runner_hint") : void 0;
|
|
3443
3593
|
if (!fatal) {
|
|
3444
3594
|
warn2(message);
|
|
3445
3595
|
if (hint) warn2(hint);
|
|
@@ -3477,7 +3627,7 @@ async function runAdd(opts) {
|
|
|
3477
3627
|
});
|
|
3478
3628
|
if (validation.issues.length > 0) {
|
|
3479
3629
|
throw new CliError(
|
|
3480
|
-
|
|
3630
|
+
t28("schedule.err_invalid"),
|
|
3481
3631
|
EXIT_CODES.FAILURE,
|
|
3482
3632
|
renderIssues(validation.issues)
|
|
3483
3633
|
);
|
|
@@ -3507,26 +3657,26 @@ async function fillInteractively(opts) {
|
|
|
3507
3657
|
if (!missing) return opts;
|
|
3508
3658
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
3509
3659
|
throw new CliError(
|
|
3510
|
-
|
|
3660
|
+
t28("schedule.err_missing_flags"),
|
|
3511
3661
|
EXIT_CODES.FAILURE,
|
|
3512
|
-
|
|
3662
|
+
t28("schedule.err_missing_flags_hint")
|
|
3513
3663
|
);
|
|
3514
3664
|
}
|
|
3515
3665
|
const { input: input2, select: select2 } = await import("@inquirer/prompts");
|
|
3516
3666
|
const next = { ...opts };
|
|
3517
|
-
next.name ||= await input2({ message:
|
|
3518
|
-
next.prompt ||= await input2({ message:
|
|
3667
|
+
next.name ||= await input2({ message: t28("schedule.ask_name") });
|
|
3668
|
+
next.prompt ||= await input2({ message: t28("schedule.ask_prompt") });
|
|
3519
3669
|
if (!hasTrigger) {
|
|
3520
|
-
const at = await input2({ message:
|
|
3670
|
+
const at = await input2({ message: t28("schedule.ask_at"), default: "09:00" });
|
|
3521
3671
|
next.daily = at;
|
|
3522
3672
|
}
|
|
3523
|
-
next.budget ||= await input2({ message:
|
|
3673
|
+
next.budget ||= await input2({ message: t28("schedule.ask_budget"), default: "0.50" });
|
|
3524
3674
|
next.permission ||= await select2({
|
|
3525
|
-
message:
|
|
3675
|
+
message: t28("schedule.ask_permission"),
|
|
3526
3676
|
choices: [
|
|
3527
|
-
{ name:
|
|
3528
|
-
{ name:
|
|
3529
|
-
{ name:
|
|
3677
|
+
{ name: t28("schedule.permission.default"), value: "default" },
|
|
3678
|
+
{ name: t28("schedule.permission.plan"), value: "plan" },
|
|
3679
|
+
{ name: t28("schedule.permission.accept_edits"), value: "acceptEdits" }
|
|
3530
3680
|
]
|
|
3531
3681
|
});
|
|
3532
3682
|
return next;
|
|
@@ -3535,8 +3685,8 @@ function parseTrigger(opts) {
|
|
|
3535
3685
|
const given = [opts.daily, opts.weekly, opts.monthly, opts.every, opts.once].filter(
|
|
3536
3686
|
(v) => v !== void 0
|
|
3537
3687
|
);
|
|
3538
|
-
if (given.length === 0) throw new CliError(
|
|
3539
|
-
if (given.length > 1) throw new CliError(
|
|
3688
|
+
if (given.length === 0) throw new CliError(t28("schedule.err_no_trigger"), EXIT_CODES.FAILURE);
|
|
3689
|
+
if (given.length > 1) throw new CliError(t28("schedule.err_many_triggers"), EXIT_CODES.FAILURE);
|
|
3540
3690
|
if (opts.daily !== void 0) return { kind: "cron", cycle: "daily", at: opts.daily };
|
|
3541
3691
|
if (opts.weekly !== void 0) {
|
|
3542
3692
|
return {
|
|
@@ -3560,14 +3710,14 @@ function parseTrigger(opts) {
|
|
|
3560
3710
|
return { kind: "interval", everyMinutes: parseEvery(opts.every) };
|
|
3561
3711
|
}
|
|
3562
3712
|
function requireAt(opts) {
|
|
3563
|
-
if (!opts.at) throw new CliError(
|
|
3713
|
+
if (!opts.at) throw new CliError(t28("schedule.err_at_required"), EXIT_CODES.FAILURE);
|
|
3564
3714
|
return opts.at;
|
|
3565
3715
|
}
|
|
3566
3716
|
function parseWeekday(raw) {
|
|
3567
3717
|
const idx = WEEKDAY_CODES.indexOf(raw.trim().toUpperCase());
|
|
3568
3718
|
if (idx < 0) {
|
|
3569
3719
|
throw new CliError(
|
|
3570
|
-
|
|
3720
|
+
t28("schedule.err_weekday", { value: raw }),
|
|
3571
3721
|
EXIT_CODES.FAILURE,
|
|
3572
3722
|
WEEKDAY_CODES.join(",")
|
|
3573
3723
|
);
|
|
@@ -3579,9 +3729,9 @@ function parseEvery(raw) {
|
|
|
3579
3729
|
const minutes = m ? Number(m[1]) * (m[2]?.toLowerCase() === "h" ? 60 : 1) : Number.NaN;
|
|
3580
3730
|
if (!SCHEDULE_INTERVAL_MINUTES.includes(minutes)) {
|
|
3581
3731
|
throw new CliError(
|
|
3582
|
-
|
|
3732
|
+
t28("schedule.err_every", { value: raw }),
|
|
3583
3733
|
EXIT_CODES.FAILURE,
|
|
3584
|
-
|
|
3734
|
+
t28("schedule.err_every_hint", {
|
|
3585
3735
|
values: SCHEDULE_INTERVAL_MINUTES.map((n) => n < 60 ? `${n}m` : `${n / 60}h`).join(" / ")
|
|
3586
3736
|
})
|
|
3587
3737
|
);
|
|
@@ -3592,7 +3742,7 @@ function parsePermission(raw) {
|
|
|
3592
3742
|
if (raw === void 0) return "default";
|
|
3593
3743
|
if (!isPermissionLevel3(raw)) {
|
|
3594
3744
|
throw new CliError(
|
|
3595
|
-
|
|
3745
|
+
t28("schedule.err_permission", { value: raw }),
|
|
3596
3746
|
EXIT_CODES.FAILURE,
|
|
3597
3747
|
PERMISSION_LEVELS4.join(" / ")
|
|
3598
3748
|
);
|
|
@@ -3604,7 +3754,7 @@ function parseOperations(raw) {
|
|
|
3604
3754
|
for (const value of raw ?? []) {
|
|
3605
3755
|
if (!isOperationType2(value)) {
|
|
3606
3756
|
throw new CliError(
|
|
3607
|
-
|
|
3757
|
+
t28("schedule.err_operation", { value }),
|
|
3608
3758
|
EXIT_CODES.FAILURE,
|
|
3609
3759
|
OPERATION_TYPES4.join(" / ")
|
|
3610
3760
|
);
|
|
@@ -3617,9 +3767,9 @@ function buildInput(opts, trigger, permission) {
|
|
|
3617
3767
|
const budget = Number(opts.budget);
|
|
3618
3768
|
if (!Number.isFinite(budget) || budget <= 0) {
|
|
3619
3769
|
throw new CliError(
|
|
3620
|
-
|
|
3770
|
+
t28("schedule.err_budget", { value: opts.budget ?? "" }),
|
|
3621
3771
|
EXIT_CODES.FAILURE,
|
|
3622
|
-
|
|
3772
|
+
t28("schedule.err_budget_hint")
|
|
3623
3773
|
);
|
|
3624
3774
|
}
|
|
3625
3775
|
return {
|
|
@@ -3655,52 +3805,52 @@ function applyDefaultAllowlist(store, def, opts) {
|
|
|
3655
3805
|
}) ?? def;
|
|
3656
3806
|
}
|
|
3657
3807
|
function printSummary(def) {
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3808
|
+
log9(t28("schedule.created", { id: def.id, name: def.name }));
|
|
3809
|
+
log9("");
|
|
3810
|
+
log9(t28("schedule.created_next", { id: def.id.slice(0, 12) }));
|
|
3661
3811
|
}
|
|
3662
3812
|
// src/schedule/doctor.ts
|
|
3663
3813
|
import { diagnoseSchedules, pruneSchedules, repairSchedules } from "@epoch-agent/core";
|
|
3664
|
-
import { t as
|
|
3814
|
+
import { t as t29 } from "@epoch-agent/infra";
|
|
3665
3815
|
async function runDoctor(opts) {
|
|
3666
3816
|
const ctx = await openSchedule();
|
|
3667
3817
|
try {
|
|
3668
3818
|
const deps = { store: ctx.store, backend: ctx.backend, registrar: ctx.registrar };
|
|
3669
3819
|
const report = await diagnoseSchedules(deps);
|
|
3670
3820
|
if (report.unsupported) {
|
|
3671
|
-
warn2(
|
|
3821
|
+
warn2(t29("schedule.doctor_unsupported"));
|
|
3672
3822
|
return;
|
|
3673
3823
|
}
|
|
3674
3824
|
if (report.drifts.length === 0) {
|
|
3675
|
-
|
|
3825
|
+
log9(t29("schedule.doctor_clean", { n: report.checked }));
|
|
3676
3826
|
return;
|
|
3677
3827
|
}
|
|
3678
|
-
|
|
3828
|
+
log9(t29("schedule.doctor_found", { n: report.drifts.length }));
|
|
3679
3829
|
for (const drift of report.drifts) {
|
|
3680
|
-
|
|
3830
|
+
log9(
|
|
3681
3831
|
` \xB7 ${driftLabel(drift.kind)} ${drift.name ?? drift.scheduleId ?? ""} [${drift.osTaskId || "\u2014"}]${drift.detail ? `
|
|
3682
3832
|
${drift.detail}` : ""}`
|
|
3683
3833
|
);
|
|
3684
3834
|
}
|
|
3685
3835
|
if (opts.repair !== true && opts.prune !== true) {
|
|
3686
|
-
|
|
3687
|
-
|
|
3836
|
+
log9("");
|
|
3837
|
+
log9(t29("schedule.doctor_hint"));
|
|
3688
3838
|
return;
|
|
3689
3839
|
}
|
|
3690
3840
|
if (opts.repair === true) {
|
|
3691
3841
|
for (const outcome of await repairSchedules(report.drifts, deps)) {
|
|
3692
3842
|
const label = outcome.drift.name ?? outcome.drift.scheduleId ?? "";
|
|
3693
|
-
if (outcome.ok)
|
|
3843
|
+
if (outcome.ok) log9(t29("schedule.doctor_repaired", { name: label }));
|
|
3694
3844
|
else
|
|
3695
|
-
warn2(
|
|
3845
|
+
warn2(t29("schedule.doctor_repair_failed", { name: label, detail: outcome.detail ?? "" }));
|
|
3696
3846
|
}
|
|
3697
3847
|
}
|
|
3698
3848
|
if (opts.prune === true) {
|
|
3699
3849
|
for (const outcome of await pruneSchedules(report.drifts, deps)) {
|
|
3700
|
-
if (outcome.ok)
|
|
3850
|
+
if (outcome.ok) log9(t29("schedule.doctor_pruned", { id: outcome.drift.osTaskId }));
|
|
3701
3851
|
else {
|
|
3702
3852
|
warn2(
|
|
3703
|
-
|
|
3853
|
+
t29("schedule.doctor_prune_failed", {
|
|
3704
3854
|
id: outcome.drift.osTaskId,
|
|
3705
3855
|
detail: outcome.detail ?? ""
|
|
3706
3856
|
})
|
|
@@ -3714,15 +3864,15 @@ async function runDoctor(opts) {
|
|
|
3714
3864
|
}
|
|
3715
3865
|
}
|
|
3716
3866
|
// src/schedule/manage.ts
|
|
3717
|
-
import { automationLogsDir, t as
|
|
3867
|
+
import { automationLogsDir, t as t30 } from "@epoch-agent/infra";
|
|
3718
3868
|
async function runSetEnabled(idOrPrefix, enabled) {
|
|
3719
3869
|
const ctx = await openSchedule();
|
|
3720
3870
|
try {
|
|
3721
3871
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3722
3872
|
const outcome = await ctx.registrar.setEnabled(def, enabled);
|
|
3723
3873
|
reportRegistration(outcome, false);
|
|
3724
|
-
|
|
3725
|
-
enabled ?
|
|
3874
|
+
log9(
|
|
3875
|
+
enabled ? t30("schedule.enabled_done", { name: def.name }) : t30("schedule.disabled_done", { name: def.name })
|
|
3726
3876
|
);
|
|
3727
3877
|
} finally {
|
|
3728
3878
|
ctx.close();
|
|
@@ -3734,14 +3884,14 @@ async function runRemove(idOrPrefix) {
|
|
|
3734
3884
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3735
3885
|
if (def.osTaskId) reportRegistration(await ctx.registrar.unregister(def), false);
|
|
3736
3886
|
ctx.store.remove(def.id);
|
|
3737
|
-
|
|
3738
|
-
|
|
3887
|
+
log9(t30("schedule.removed", { name: def.name }));
|
|
3888
|
+
log9(t30("schedule.removed_logs_kept", { dir: automationLogsDir(def.id, ctx.homeDir) }));
|
|
3739
3889
|
} finally {
|
|
3740
3890
|
ctx.close();
|
|
3741
3891
|
}
|
|
3742
3892
|
}
|
|
3743
3893
|
// src/schedule/run.ts
|
|
3744
|
-
import { t as
|
|
3894
|
+
import { t as t31 } from "@epoch-agent/infra";
|
|
3745
3895
|
import { unfixedRules } from "@epoch-agent/protocol";
|
|
3746
3896
|
import { fireSchedule } from "@epoch-agent/runtime";
|
|
3747
3897
|
async function runFire(scheduleId, homeDir) {
|
|
@@ -3754,9 +3904,9 @@ async function runFire(scheduleId, homeDir) {
|
|
|
3754
3904
|
ctx.close();
|
|
3755
3905
|
}
|
|
3756
3906
|
if (result.missing) {
|
|
3757
|
-
warn2(
|
|
3907
|
+
warn2(t31("schedule.fire_missing", { id: scheduleId }));
|
|
3758
3908
|
} else if (result.disabled) {
|
|
3759
|
-
warn2(
|
|
3909
|
+
warn2(t31("schedule.fire_disabled", { id: scheduleId }));
|
|
3760
3910
|
} else {
|
|
3761
3911
|
warn2(`${statusLabel(result.status)}${result.detail ? ` ${result.detail}` : ""}`);
|
|
3762
3912
|
}
|
|
@@ -3766,14 +3916,14 @@ async function runManual(idOrPrefix, opts) {
|
|
|
3766
3916
|
const ctx = await openSchedule();
|
|
3767
3917
|
try {
|
|
3768
3918
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3769
|
-
|
|
3919
|
+
log9(t31("schedule.run_start", { name: def.name }));
|
|
3770
3920
|
const result = await fireSchedule({
|
|
3771
3921
|
scheduleId: def.id,
|
|
3772
3922
|
homeDir: ctx.homeDir,
|
|
3773
3923
|
manual: true,
|
|
3774
3924
|
registrar: ctx.registrar
|
|
3775
3925
|
});
|
|
3776
|
-
|
|
3926
|
+
log9(`${statusLabel(result.status)}${result.detail ? ` ${result.detail}` : ""}`);
|
|
3777
3927
|
const pending = result.run?.pendingApprovals ?? [];
|
|
3778
3928
|
if (pending.length === 0) {
|
|
3779
3929
|
process.exitCode = result.exitCode;
|
|
@@ -3782,21 +3932,21 @@ async function runManual(idOrPrefix, opts) {
|
|
|
3782
3932
|
printPending(pending);
|
|
3783
3933
|
if (opts.fix === true) {
|
|
3784
3934
|
const added = applyFix(ctx.store, def.id, pending);
|
|
3785
|
-
|
|
3935
|
+
log9(added.length > 0 ? t31("schedule.fix_done", { n: added.length }) : t31("schedule.fix_none"));
|
|
3786
3936
|
return;
|
|
3787
3937
|
}
|
|
3788
|
-
|
|
3938
|
+
log9(t31("schedule.fix_hint", { id: def.id.slice(0, 12) }));
|
|
3789
3939
|
process.exitCode = result.exitCode;
|
|
3790
3940
|
} finally {
|
|
3791
3941
|
ctx.close();
|
|
3792
3942
|
}
|
|
3793
3943
|
}
|
|
3794
3944
|
function printPending(pending) {
|
|
3795
|
-
|
|
3796
|
-
|
|
3945
|
+
log9("");
|
|
3946
|
+
log9(t31("schedule.pending_header", { n: pending.length }));
|
|
3797
3947
|
for (const item of pending) {
|
|
3798
|
-
|
|
3799
|
-
if (item.suggestedRule)
|
|
3948
|
+
log9(` \xB7 ${t31("schedule.pending_line", { tool: item.toolName, target: item.target })}`);
|
|
3949
|
+
if (item.suggestedRule) log9(` \u2192 ${item.suggestedRule}`);
|
|
3800
3950
|
}
|
|
3801
3951
|
}
|
|
3802
3952
|
function applyFix(store, scheduleId, pending) {
|
|
@@ -3808,23 +3958,23 @@ function applyFix(store, scheduleId, pending) {
|
|
|
3808
3958
|
}
|
|
3809
3959
|
// src/schedule/view.ts
|
|
3810
3960
|
import { nextRunAt, readRecording } from "@epoch-agent/core";
|
|
3811
|
-
import { t as
|
|
3961
|
+
import { t as t32 } from "@epoch-agent/infra";
|
|
3812
3962
|
async function runList() {
|
|
3813
3963
|
const ctx = await openSchedule();
|
|
3814
3964
|
try {
|
|
3815
3965
|
const defs = ctx.store.list();
|
|
3816
3966
|
if (defs.length === 0) {
|
|
3817
|
-
|
|
3818
|
-
|
|
3967
|
+
log9(t32("schedule.list_empty"));
|
|
3968
|
+
log9(t32("schedule.list_empty_hint"));
|
|
3819
3969
|
return;
|
|
3820
3970
|
}
|
|
3821
|
-
|
|
3971
|
+
log9(t32("schedule.list_header"));
|
|
3822
3972
|
for (const def of defs) {
|
|
3823
|
-
const next = def.enabled ? formatWhen(nextRunAt(def.trigger, Date.now(), def.startDate, def.endDate) ?? void 0) :
|
|
3973
|
+
const next = def.enabled ? formatWhen(nextRunAt(def.trigger, Date.now(), def.startDate, def.endDate) ?? void 0) : t32("schedule.disabled_mark");
|
|
3824
3974
|
const danger = def.permission === "bypass" ? " \u{1F534}" : "";
|
|
3825
|
-
|
|
3975
|
+
log9(
|
|
3826
3976
|
` ${def.id.slice(0, 12)} ${def.name}${danger}
|
|
3827
|
-
${describeTrigger(def.trigger)} \xB7 ${
|
|
3977
|
+
${describeTrigger(def.trigger)} \xB7 ${t32("schedule.col_next")} ${next} \xB7 ${t32("schedule.col_last")} ${def.lastStatus ? statusLabel(def.lastStatus) : "\u2014"} \xB7 ${permissionLabel(def.permission)}`
|
|
3828
3978
|
);
|
|
3829
3979
|
}
|
|
3830
3980
|
} finally {
|
|
@@ -3835,45 +3985,45 @@ async function runShow(idOrPrefix) {
|
|
|
3835
3985
|
const ctx = await openSchedule();
|
|
3836
3986
|
try {
|
|
3837
3987
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3988
|
+
log9(`${def.name} (${def.id})`);
|
|
3989
|
+
log9(` ${t32("schedule.field_enabled")}: ${def.enabled ? t32("schedule.yes") : t32("schedule.no")}`);
|
|
3990
|
+
log9(` ${t32("schedule.field_trigger")}: ${describeTrigger(def.trigger)}`);
|
|
3841
3991
|
if (def.startDate ?? def.endDate) {
|
|
3842
|
-
|
|
3992
|
+
log9(` ${t32("schedule.field_window")}: ${def.startDate ?? "\u2014"} .. ${def.endDate ?? "\u2014"}`);
|
|
3843
3993
|
}
|
|
3844
|
-
|
|
3845
|
-
` ${
|
|
3994
|
+
log9(
|
|
3995
|
+
` ${t32("schedule.field_next")}: ${formatWhen(
|
|
3846
3996
|
nextRunAt(def.trigger, Date.now(), def.startDate, def.endDate) ?? void 0
|
|
3847
3997
|
)}`
|
|
3848
3998
|
);
|
|
3849
|
-
|
|
3850
|
-
if (def.model)
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
` ${
|
|
3999
|
+
log9(` ${t32("schedule.field_workdir")}: ${def.workDir}`);
|
|
4000
|
+
if (def.model) log9(` ${t32("schedule.field_model")}: ${def.model}`);
|
|
4001
|
+
log9(` ${t32("schedule.field_permission")}: ${permissionLabel(def.permission)}`);
|
|
4002
|
+
log9(
|
|
4003
|
+
` ${t32("schedule.field_limits")}: ` + t32("schedule.limits_value", {
|
|
3854
4004
|
turns: def.maxTurns,
|
|
3855
4005
|
budget: def.maxBudgetUsd.toFixed(2),
|
|
3856
4006
|
minutes: Math.round(def.timeoutMs / 6e4)
|
|
3857
4007
|
})
|
|
3858
4008
|
);
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
for (const line of def.prompt.split("\n"))
|
|
3862
|
-
|
|
4009
|
+
log9(` ${t32("schedule.field_backend")}: ${def.osBackend} ${def.osTaskId || "\u2014"}`);
|
|
4010
|
+
log9(` ${t32("schedule.field_prompt")}:`);
|
|
4011
|
+
for (const line of def.prompt.split("\n")) log9(` ${line}`);
|
|
4012
|
+
log9("");
|
|
3863
4013
|
printAllowlist(def);
|
|
3864
|
-
|
|
3865
|
-
|
|
4014
|
+
log9("");
|
|
4015
|
+
log9(t32("schedule.field_last_run", { when: formatWhen(def.lastRunAt) }));
|
|
3866
4016
|
} finally {
|
|
3867
4017
|
ctx.close();
|
|
3868
4018
|
}
|
|
3869
4019
|
}
|
|
3870
4020
|
function printAllowlist(def) {
|
|
3871
4021
|
if (def.permission === "bypass") {
|
|
3872
|
-
|
|
4022
|
+
log9(t32("schedule.allowlist_bypass"));
|
|
3873
4023
|
return;
|
|
3874
4024
|
}
|
|
3875
4025
|
if (def.permission === "plan") {
|
|
3876
|
-
|
|
4026
|
+
log9(t32("schedule.allowlist_readonly"));
|
|
3877
4027
|
return;
|
|
3878
4028
|
}
|
|
3879
4029
|
const lines = [
|
|
@@ -3881,9 +4031,9 @@ function printAllowlist(def) {
|
|
|
3881
4031
|
...def.allowOperations.map((v) => ` ${v}`),
|
|
3882
4032
|
...def.allowRules.map((v) => ` ${v}`)
|
|
3883
4033
|
];
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
4034
|
+
log9(t32("schedule.allowlist_header"));
|
|
4035
|
+
log9(lines.length > 0 ? lines.join("\n") : ` ${t32("schedule.allowlist_empty")}`);
|
|
4036
|
+
log9(t32("schedule.allowlist_scope_note"));
|
|
3887
4037
|
}
|
|
3888
4038
|
async function runLogs(idOrPrefix, limit, tail) {
|
|
3889
4039
|
const ctx = await openSchedule();
|
|
@@ -3891,7 +4041,7 @@ async function runLogs(idOrPrefix, limit, tail) {
|
|
|
3891
4041
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3892
4042
|
const runs = ctx.store.listRuns(def.id, limit);
|
|
3893
4043
|
if (runs.length === 0) {
|
|
3894
|
-
|
|
4044
|
+
log9(t32("schedule.logs_empty"));
|
|
3895
4045
|
return;
|
|
3896
4046
|
}
|
|
3897
4047
|
for (const run of runs) printRun(run);
|
|
@@ -3901,31 +4051,31 @@ async function runLogs(idOrPrefix, limit, tail) {
|
|
|
3901
4051
|
}
|
|
3902
4052
|
}
|
|
3903
4053
|
function printRun(run) {
|
|
3904
|
-
|
|
3905
|
-
` ${formatWhen(run.startedAt)} ${statusLabel(run.status)} ` +
|
|
4054
|
+
log9(
|
|
4055
|
+
` ${formatWhen(run.startedAt)} ${statusLabel(run.status)} ` + t32("schedule.run_meta", {
|
|
3906
4056
|
turns: run.turns,
|
|
3907
4057
|
cost: run.costUsd.toFixed(4),
|
|
3908
|
-
manual: run.manual ?
|
|
4058
|
+
manual: run.manual ? t32("schedule.run_manual") : ""
|
|
3909
4059
|
})
|
|
3910
4060
|
);
|
|
3911
|
-
if (run.reason)
|
|
4061
|
+
if (run.reason) log9(` ${run.reason}`);
|
|
3912
4062
|
for (const pending of run.pendingApprovals) {
|
|
3913
|
-
|
|
3914
|
-
` \u26A0 ${
|
|
4063
|
+
log9(
|
|
4064
|
+
` \u26A0 ${t32("schedule.pending_line", { tool: pending.toolName, target: pending.target })}`
|
|
3915
4065
|
);
|
|
3916
4066
|
if (pending.suggestedRule) {
|
|
3917
|
-
|
|
4067
|
+
log9(` ${t32("schedule.pending_fix", { rule: pending.suggestedRule })}`);
|
|
3918
4068
|
}
|
|
3919
4069
|
}
|
|
3920
4070
|
}
|
|
3921
4071
|
function printRecording(run) {
|
|
3922
4072
|
if (!run.logPath) return;
|
|
3923
|
-
|
|
3924
|
-
|
|
4073
|
+
log9("");
|
|
4074
|
+
log9(t32("schedule.logs_recording", { path: run.logPath }));
|
|
3925
4075
|
for (const envelope of readRecording(run.logPath)) {
|
|
3926
4076
|
const event = envelope.event;
|
|
3927
4077
|
if (event.type === "text-delta" && event.text) process.stdout.write(event.text);
|
|
3928
|
-
else if (event.type === "error" && event.message)
|
|
4078
|
+
else if (event.type === "error" && event.message) log9(`
|
|
3929
4079
|
[error] ${event.message}`);
|
|
3930
4080
|
}
|
|
3931
4081
|
process.stdout.write("\n");
|
|
@@ -3933,19 +4083,19 @@ function printRecording(run) {
|
|
|
3933
4083
|
// src/commands/schedule.ts
|
|
3934
4084
|
var DEFAULT_LOG_LIMIT = 10;
|
|
3935
4085
|
function registerScheduleCommand(program2) {
|
|
3936
|
-
const cmd = program2.command("schedule").description(
|
|
3937
|
-
cmd.command("add").description(
|
|
3938
|
-
cmd.command("list", { isDefault: true }).description(
|
|
3939
|
-
cmd.command("show").description(
|
|
3940
|
-
cmd.command("enable").description(
|
|
3941
|
-
cmd.command("disable").description(
|
|
3942
|
-
cmd.command("rm").alias("remove").description(
|
|
3943
|
-
cmd.command("run").description(
|
|
3944
|
-
cmd.command("logs").description(
|
|
4086
|
+
const cmd = program2.command("schedule").description(t33("cli.schedule.summary"));
|
|
4087
|
+
cmd.command("add").description(t33("cli.schedule.add")).option("--name <name>", t33("cli.schedule.opt_name")).option("--prompt <text>", t33("cli.schedule.opt_prompt")).option("--daily <HH:mm>", t33("cli.schedule.opt_daily")).option("--weekly <days>", t33("cli.schedule.opt_weekly")).option("--monthly <days>", t33("cli.schedule.opt_monthly")).option("--every <interval>", t33("cli.schedule.opt_every")).option("--once <YYYY-MM-DD>", t33("cli.schedule.opt_once")).option("--at <HH:mm>", t33("cli.schedule.opt_at")).option("--start <YYYY-MM-DD>", t33("cli.schedule.opt_start")).option("--end <YYYY-MM-DD>", t33("cli.schedule.opt_end")).option("--workdir <dir>", t33("cli.schedule.opt_workdir")).option("--model <model>", t33("cli.schedule.opt_model")).option("--permission <level>", t33("cli.schedule.opt_permission")).option("--allow-tool <name>", t33("cli.schedule.opt_allow_tool"), collect3, []).option("--allow-operation <type>", t33("cli.schedule.opt_allow_operation"), collect3, []).option("--allow-rule <rule>", t33("cli.schedule.opt_allow_rule"), collect3, []).option("--max-turns <n>", t33("cli.schedule.opt_max_turns")).option("--budget <usd>", t33("cli.schedule.opt_budget")).option("--timeout <minutes>", t33("cli.schedule.opt_timeout")).option("--disabled", t33("cli.schedule.opt_disabled")).option("--i-understand-bypass", t33("cli.schedule.opt_bypass_ack")).action((opts) => runAdd(opts));
|
|
4088
|
+
cmd.command("list", { isDefault: true }).description(t33("cli.schedule.list")).action(() => runList());
|
|
4089
|
+
cmd.command("show").description(t33("cli.schedule.show")).argument("<id>", t33("cli.schedule.arg_id")).action((id) => runShow(id));
|
|
4090
|
+
cmd.command("enable").description(t33("cli.schedule.enable")).argument("<id>", t33("cli.schedule.arg_id")).action((id) => runSetEnabled(id, true));
|
|
4091
|
+
cmd.command("disable").description(t33("cli.schedule.disable")).argument("<id>", t33("cli.schedule.arg_id")).action((id) => runSetEnabled(id, false));
|
|
4092
|
+
cmd.command("rm").alias("remove").description(t33("cli.schedule.rm")).argument("<id>", t33("cli.schedule.arg_id")).action((id) => runRemove(id));
|
|
4093
|
+
cmd.command("run").description(t33("cli.schedule.run")).argument("<id>", t33("cli.schedule.arg_id")).option("--fix", t33("cli.schedule.opt_fix")).action((id, opts) => runManual(id, opts));
|
|
4094
|
+
cmd.command("logs").description(t33("cli.schedule.logs")).argument("<id>", t33("cli.schedule.arg_id")).option("-n, --limit <n>", t33("cli.schedule.opt_limit"), String(DEFAULT_LOG_LIMIT)).option("--tail", t33("cli.schedule.opt_tail")).action(
|
|
3945
4095
|
(id, opts) => runLogs(id, Number(opts.limit ?? DEFAULT_LOG_LIMIT), opts.tail === true)
|
|
3946
4096
|
);
|
|
3947
|
-
cmd.command("fire").description(
|
|
3948
|
-
cmd.command("doctor").description(
|
|
4097
|
+
cmd.command("fire").description(t33("cli.schedule.fire")).argument("<id>", t33("cli.schedule.arg_id")).option("--home <dir>", t33("cli.schedule.opt_home")).action((id, opts) => runFire(id, opts.home));
|
|
4098
|
+
cmd.command("doctor").description(t33("cli.schedule.doctor")).option("--repair", t33("cli.schedule.opt_repair")).option("--prune", t33("cli.schedule.opt_prune")).action((opts) => runDoctor(opts));
|
|
3949
4099
|
}
|
|
3950
4100
|
function collect3(value, previous) {
|
|
3951
4101
|
return [...previous, value];
|
|
@@ -3956,7 +4106,7 @@ import { statfs } from "fs/promises";
|
|
|
3956
4106
|
import {
|
|
3957
4107
|
describeIsolation,
|
|
3958
4108
|
getConfigIssues,
|
|
3959
|
-
loadConfig as
|
|
4109
|
+
loadConfig as loadConfig4,
|
|
3960
4110
|
SANDBOX_COVERS,
|
|
3961
4111
|
SANDBOX_EXCLUDES
|
|
3962
4112
|
} from "@epoch-agent/core";
|
|
@@ -3964,17 +4114,13 @@ import {
|
|
|
3964
4114
|
allJobs,
|
|
3965
4115
|
confine,
|
|
3966
4116
|
resolveProfile,
|
|
3967
|
-
t as
|
|
4117
|
+
t as t35
|
|
3968
4118
|
} from "@epoch-agent/infra";
|
|
3969
4119
|
import { hasFailure } from "@epoch-agent/protocol";
|
|
3970
|
-
import {
|
|
3971
|
-
classifyTerminal,
|
|
3972
|
-
probeShiftEnter
|
|
3973
|
-
} from "@epoch-agent/tui";
|
|
3974
4120
|
// src/statusline.ts
|
|
3975
4121
|
import { execFile as execFile2 } from "child_process";
|
|
3976
4122
|
import { sanitizeToolOutput } from "@epoch-agent/core";
|
|
3977
|
-
import { shellSpawnArgs, t as
|
|
4123
|
+
import { shellSpawnArgs, t as t34 } from "@epoch-agent/infra";
|
|
3978
4124
|
var TIMEOUT_MS2 = 2e3;
|
|
3979
4125
|
var MAX_WIDTH = 60;
|
|
3980
4126
|
var MAX_BUFFER = 64 * 1024;
|
|
@@ -4002,7 +4148,7 @@ function runStatusLineCommand(command, cwd) {
|
|
|
4002
4148
|
const killed = err2.killed === true;
|
|
4003
4149
|
settle({
|
|
4004
4150
|
text: null,
|
|
4005
|
-
error: killed ?
|
|
4151
|
+
error: killed ? t34("statusline.timeout", { ms: TIMEOUT_MS2 }) : err2.message.trim()
|
|
4006
4152
|
});
|
|
4007
4153
|
return;
|
|
4008
4154
|
}
|
|
@@ -4016,12 +4162,12 @@ async function probeStatusLine(config, cwd) {
|
|
|
4016
4162
|
if (!command) return null;
|
|
4017
4163
|
const result = await runStatusLineCommand(command, cwd);
|
|
4018
4164
|
if (result.text === null) {
|
|
4019
|
-
return { command, ok: false, detail: result.error ??
|
|
4165
|
+
return { command, ok: false, detail: result.error ?? t34("statusline.no_output") };
|
|
4020
4166
|
}
|
|
4021
4167
|
return { command, ok: true, detail: result.text };
|
|
4022
4168
|
}
|
|
4023
4169
|
// src/commands/status.ts
|
|
4024
|
-
var
|
|
4170
|
+
var log10 = (msg) => {
|
|
4025
4171
|
process.stdout.write(msg + "\n");
|
|
4026
4172
|
};
|
|
4027
4173
|
var STATUS_MARK = {
|
|
@@ -4049,10 +4195,10 @@ async function describeDisk(path = ".") {
|
|
|
4049
4195
|
}
|
|
4050
4196
|
}
|
|
4051
4197
|
function describeConfig() {
|
|
4052
|
-
if (!existsSync7(CONFIG_PATH)) return
|
|
4053
|
-
|
|
4198
|
+
if (!existsSync7(CONFIG_PATH)) return t35("cli.status.config_absent");
|
|
4199
|
+
loadConfig4();
|
|
4054
4200
|
const n = getConfigIssues().length;
|
|
4055
|
-
return n === 0 ?
|
|
4201
|
+
return n === 0 ? t35("cli.status.config_present") : t35("cli.status.config_present_with_issues", { count: n });
|
|
4056
4202
|
}
|
|
4057
4203
|
async function describeSecretBackend() {
|
|
4058
4204
|
try {
|
|
@@ -4060,18 +4206,18 @@ async function describeSecretBackend() {
|
|
|
4060
4206
|
const store = await createSecretStore();
|
|
4061
4207
|
return store.encrypted ? store.detail : `\u26A0 ${store.detail}`;
|
|
4062
4208
|
} catch (err2) {
|
|
4063
|
-
return
|
|
4209
|
+
return t35("cli.status.secret_probe_failed", {
|
|
4064
4210
|
message: err2 instanceof Error ? err2.message : String(err2)
|
|
4065
4211
|
});
|
|
4066
4212
|
}
|
|
4067
4213
|
}
|
|
4068
4214
|
function workspaceLines(rt) {
|
|
4069
|
-
const lines = [` ${
|
|
4215
|
+
const lines = [` ${t35("cli.doctor.workspace_root", { root: rt.workspace.root })}`];
|
|
4070
4216
|
lines.push(
|
|
4071
|
-
` ${rt.trusted ?
|
|
4217
|
+
` ${rt.trusted ? t35("cli.doctor.workspace_trusted") : t35("cli.doctor.workspace_untrusted")}`
|
|
4072
4218
|
);
|
|
4073
4219
|
lines.push(
|
|
4074
|
-
` ${rt.workspace.extra.length === 0 ?
|
|
4220
|
+
` ${rt.workspace.extra.length === 0 ? t35("cli.doctor.workspace_no_extra") : t35("cli.doctor.workspace_extra", { count: rt.workspace.extra.length })}`
|
|
4075
4221
|
);
|
|
4076
4222
|
for (const dir of rt.workspace.extra) lines.push(` \xB7 ${dir}`);
|
|
4077
4223
|
return lines;
|
|
@@ -4079,94 +4225,94 @@ function workspaceLines(rt) {
|
|
|
4079
4225
|
function sandboxLines(cwd, sandbox) {
|
|
4080
4226
|
const iso = describeIsolation();
|
|
4081
4227
|
const lines = [`
|
|
4082
|
-
${
|
|
4228
|
+
${t35("cli.doctor.sandbox_head")}`];
|
|
4083
4229
|
if (iso.backend === "none") {
|
|
4084
4230
|
lines.push(` ${STATUS_MARK.skipped} ${iso.detail}`);
|
|
4085
4231
|
lines.push(
|
|
4086
|
-
` ${STATUS_MARK.warn} ${
|
|
4232
|
+
` ${STATUS_MARK.warn} ${t35(
|
|
4087
4233
|
iso.reason === "platform-unsupported" ? "cli.doctor.sandbox_absent_by_design" : "cli.doctor.sandbox_absent_tools"
|
|
4088
4234
|
)}`
|
|
4089
4235
|
);
|
|
4090
4236
|
return lines;
|
|
4091
4237
|
}
|
|
4092
4238
|
lines.push(
|
|
4093
|
-
` ${STATUS_MARK.ok} ${
|
|
4239
|
+
` ${STATUS_MARK.ok} ${t35("cli.doctor.sandbox_backend", { backend: iso.backend, platform: iso.platform })}`
|
|
4094
4240
|
);
|
|
4095
|
-
lines.push(` \xB7 ${
|
|
4241
|
+
lines.push(` \xB7 ${t35("cli.doctor.sandbox_code_exec", { detail: iso.detail })}`);
|
|
4096
4242
|
const probe = (mode) => confine("/bin/sh", ["-c", "true"], { mode, workspaceRoot: cwd, allowNetwork: true });
|
|
4097
4243
|
for (const mode of ["workspace-write", "read-only"]) {
|
|
4098
4244
|
const c = probe(mode);
|
|
4099
4245
|
if (!c.confined) continue;
|
|
4100
|
-
lines.push(` \xB7 ${
|
|
4101
|
-
if (c.enforcement === "partial") lines.push(` ${
|
|
4246
|
+
lines.push(` \xB7 ${t35("cli.doctor.sandbox_terminal", { mode, enforcement: c.enforcement })}`);
|
|
4247
|
+
if (c.enforcement === "partial") lines.push(` ${t35("cli.doctor.sandbox_partial_devnull")}`);
|
|
4102
4248
|
}
|
|
4103
4249
|
const writable = probe("workspace-write");
|
|
4104
4250
|
if (writable.confined) {
|
|
4105
|
-
lines.push(` \xB7 ${
|
|
4251
|
+
lines.push(` \xB7 ${t35("cli.doctor.sandbox_writable")}`);
|
|
4106
4252
|
for (const dir of writable.writableDirs) lines.push(` \xB7 ${dir}`);
|
|
4107
4253
|
}
|
|
4108
4254
|
lines.push(
|
|
4109
|
-
sandbox?.terminal === false ? ` ${STATUS_MARK.warn} ${
|
|
4255
|
+
sandbox?.terminal === false ? ` ${STATUS_MARK.warn} ${t35("cli.doctor.sandbox_switch_off")}` : ` ${STATUS_MARK.ok} ${t35("cli.doctor.sandbox_switch_on")}`
|
|
4110
4256
|
);
|
|
4111
|
-
lines.push(` \xB7 ${
|
|
4257
|
+
lines.push(` \xB7 ${t35("cli.doctor.sandbox_covers", { tools: SANDBOX_COVERS.join(", ") })}`);
|
|
4112
4258
|
lines.push(
|
|
4113
|
-
SANDBOX_EXCLUDES.length === 0 ? ` ${STATUS_MARK.ok} ${
|
|
4259
|
+
SANDBOX_EXCLUDES.length === 0 ? ` ${STATUS_MARK.ok} ${t35("cli.doctor.sandbox_excludes_none")}` : ` ${STATUS_MARK.warn} ${t35("cli.doctor.sandbox_excludes", { tools: SANDBOX_EXCLUDES.join(", ") })}`
|
|
4114
4260
|
);
|
|
4115
4261
|
return lines;
|
|
4116
4262
|
}
|
|
4117
4263
|
function ftsLines(health) {
|
|
4118
4264
|
const lines = [`
|
|
4119
|
-
${
|
|
4265
|
+
${t35("cli.doctor.fts_head")}`];
|
|
4120
4266
|
const indexed = health.tables.map((tb) => `${tb.table} ${tb.rows}`).join(" \xB7 ");
|
|
4121
|
-
lines.push(` \xB7 ${
|
|
4267
|
+
lines.push(` \xB7 ${t35("cli.doctor.fts_rows", { messages: health.messages, indexed })}`);
|
|
4122
4268
|
const behind = health.tables.filter((tb) => tb.rows !== health.messages);
|
|
4123
4269
|
lines.push(
|
|
4124
|
-
behind.length === 0 ? ` ${STATUS_MARK.ok} ${
|
|
4125
|
-
` ${STATUS_MARK.warn} ${
|
|
4270
|
+
behind.length === 0 ? ` ${STATUS_MARK.ok} ${t35("cli.doctor.fts_in_sync")}` : (
|
|
4271
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.fts_out_of_sync", {
|
|
4126
4272
|
tables: behind.map((tb) => `${tb.table} (${tb.rows - health.messages})`).join(", ")
|
|
4127
4273
|
})}`
|
|
4128
4274
|
)
|
|
4129
4275
|
);
|
|
4130
4276
|
lines.push(
|
|
4131
|
-
health.missingTriggers.length === 0 ? ` ${STATUS_MARK.ok} ${
|
|
4277
|
+
health.missingTriggers.length === 0 ? ` ${STATUS_MARK.ok} ${t35("cli.doctor.fts_triggers_ok")}` : ` ${STATUS_MARK.failed} ${t35("cli.doctor.fts_triggers_missing", {
|
|
4132
4278
|
triggers: health.missingTriggers.join(", ")
|
|
4133
4279
|
})}`
|
|
4134
4280
|
);
|
|
4135
4281
|
for (const tb of health.tables) {
|
|
4136
4282
|
if (tb.error !== null) {
|
|
4137
4283
|
lines.push(
|
|
4138
|
-
` ${STATUS_MARK.failed} ${
|
|
4284
|
+
` ${STATUS_MARK.failed} ${t35("cli.doctor.fts_corrupt", { table: tb.table, detail: tb.error })}`
|
|
4139
4285
|
);
|
|
4140
4286
|
}
|
|
4141
4287
|
if (tb.drift !== null) {
|
|
4142
4288
|
lines.push(
|
|
4143
|
-
` ${STATUS_MARK.warn} ${
|
|
4289
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.fts_drift", { table: tb.table, detail: tb.drift })}`
|
|
4144
4290
|
);
|
|
4145
4291
|
}
|
|
4146
4292
|
}
|
|
4147
4293
|
if (health.tables.some((tb) => !tb.external)) {
|
|
4148
|
-
lines.push(` ${STATUS_MARK.warn} ${
|
|
4294
|
+
lines.push(` ${STATUS_MARK.warn} ${t35("cli.doctor.fts_legacy_schema")}`);
|
|
4149
4295
|
}
|
|
4150
4296
|
if (health.indexBytes !== void 0) {
|
|
4151
|
-
lines.push(` \xB7 ${
|
|
4297
|
+
lines.push(` \xB7 ${t35("cli.doctor.fts_size", { size: formatBytes(health.indexBytes) })}`);
|
|
4152
4298
|
}
|
|
4153
4299
|
if (health.space?.fileBytes !== void 0) {
|
|
4154
4300
|
lines.push(
|
|
4155
|
-
` \xB7 ${
|
|
4301
|
+
` \xB7 ${t35("cli.doctor.fts_space", {
|
|
4156
4302
|
file: formatBytes(health.space.fileBytes),
|
|
4157
4303
|
free: formatBytes(health.space.freeBytes)
|
|
4158
4304
|
})}`
|
|
4159
4305
|
);
|
|
4160
|
-
lines.push(` \xB7 ${
|
|
4306
|
+
lines.push(` \xB7 ${t35("cli.doctor.fts_reclaim_hint")}`);
|
|
4161
4307
|
}
|
|
4162
4308
|
return lines;
|
|
4163
4309
|
}
|
|
4164
4310
|
function reclaimLines(report) {
|
|
4165
4311
|
const lines = [`
|
|
4166
|
-
${
|
|
4312
|
+
${t35("cli.doctor.reclaim_head")}`];
|
|
4167
4313
|
const secs = (ms) => `${(ms / 1e3).toFixed(1)}s`;
|
|
4168
4314
|
lines.push(
|
|
4169
|
-
` ${STATUS_MARK.ok} ${
|
|
4315
|
+
` ${STATUS_MARK.ok} ${t35("cli.doctor.reclaim_optimize", {
|
|
4170
4316
|
before: formatBytes(report.before.freeBytes),
|
|
4171
4317
|
after: formatBytes(report.afterOptimize.freeBytes),
|
|
4172
4318
|
ms: secs(report.optimizeMs)
|
|
@@ -4174,7 +4320,7 @@ ${t34("cli.doctor.reclaim_head")}`];
|
|
|
4174
4320
|
);
|
|
4175
4321
|
if (report.vacuumed) {
|
|
4176
4322
|
lines.push(
|
|
4177
|
-
` ${STATUS_MARK.ok} ${
|
|
4323
|
+
` ${STATUS_MARK.ok} ${t35("cli.doctor.reclaim_vacuum", {
|
|
4178
4324
|
before: formatBytes(report.before.pageBytes),
|
|
4179
4325
|
after: formatBytes(report.after.pageBytes),
|
|
4180
4326
|
ms: secs(report.vacuumMs)
|
|
@@ -4182,14 +4328,14 @@ ${t34("cli.doctor.reclaim_head")}`];
|
|
|
4182
4328
|
);
|
|
4183
4329
|
const saved = report.before.pageBytes - report.after.pageBytes;
|
|
4184
4330
|
lines.push(
|
|
4185
|
-
` ${STATUS_MARK.ok} ${
|
|
4331
|
+
` ${STATUS_MARK.ok} ${t35("cli.doctor.reclaim_total", {
|
|
4186
4332
|
saved: formatBytes(saved),
|
|
4187
4333
|
pct: `${(saved / Math.max(1, report.before.pageBytes) * 100).toFixed(1)}%`
|
|
4188
4334
|
})}`
|
|
4189
4335
|
);
|
|
4190
4336
|
} else if (report.vacuumError !== null) {
|
|
4191
4337
|
lines.push(
|
|
4192
|
-
` ${STATUS_MARK.warn} ${
|
|
4338
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.reclaim_vacuum_skipped", {
|
|
4193
4339
|
detail: report.vacuumError
|
|
4194
4340
|
})}`
|
|
4195
4341
|
);
|
|
@@ -4209,17 +4355,17 @@ function hookLines(rt) {
|
|
|
4209
4355
|
const { sources } = rt.hook;
|
|
4210
4356
|
if (sources.length === 0) return [];
|
|
4211
4357
|
const lines = [`
|
|
4212
|
-
${
|
|
4358
|
+
${t35("cli.doctor.hook_head")}`];
|
|
4213
4359
|
for (const source of sources) {
|
|
4214
|
-
const label =
|
|
4360
|
+
const label = t35(HOOK_SOURCE_LABEL[source.kind]);
|
|
4215
4361
|
if (source.skipped) {
|
|
4216
4362
|
lines.push(` ${STATUS_MARK.skipped} ${label}: ${source.path}`);
|
|
4217
|
-
lines.push(` ${
|
|
4363
|
+
lines.push(` ${t35("cli.doctor.hook_skipped")}`);
|
|
4218
4364
|
continue;
|
|
4219
4365
|
}
|
|
4220
4366
|
const name = source.plugin === void 0 ? label : `${label} ${source.plugin}`;
|
|
4221
4367
|
lines.push(
|
|
4222
|
-
` ${STATUS_MARK.ok} ${name}: ${source.path}${
|
|
4368
|
+
` ${STATUS_MARK.ok} ${name}: ${source.path}${t35("cli.doctor.hook_count", {
|
|
4223
4369
|
n: source.count
|
|
4224
4370
|
})}`
|
|
4225
4371
|
);
|
|
@@ -4233,65 +4379,65 @@ var JOB_KIND_LABEL = {
|
|
|
4233
4379
|
};
|
|
4234
4380
|
function jobLines(jobs) {
|
|
4235
4381
|
const lines = [`
|
|
4236
|
-
${
|
|
4382
|
+
${t35("cli.doctor.jobs_head")}`];
|
|
4237
4383
|
const running = jobs.filter((job) => job.status === "running");
|
|
4238
4384
|
if (running.length === 0) {
|
|
4239
|
-
lines.push(` ${STATUS_MARK.ok} ${
|
|
4385
|
+
lines.push(` ${STATUS_MARK.ok} ${t35("cli.doctor.jobs_none")}`);
|
|
4240
4386
|
return lines;
|
|
4241
4387
|
}
|
|
4242
|
-
lines.push(` ${STATUS_MARK.warn} ${
|
|
4388
|
+
lines.push(` ${STATUS_MARK.warn} ${t35("cli.doctor.jobs_running", { n: running.length })}`);
|
|
4243
4389
|
for (const job of running) {
|
|
4244
4390
|
lines.push(
|
|
4245
|
-
` \xB7 ${
|
|
4246
|
-
kind:
|
|
4391
|
+
` \xB7 ${t35("cli.doctor.jobs_line", {
|
|
4392
|
+
kind: t35(JOB_KIND_LABEL[job.kind]),
|
|
4247
4393
|
id: job.id,
|
|
4248
4394
|
label: job.label
|
|
4249
4395
|
})}`
|
|
4250
4396
|
);
|
|
4251
4397
|
}
|
|
4252
|
-
lines.push(` \xB7 ${
|
|
4398
|
+
lines.push(` \xB7 ${t35("cli.doctor.jobs_lifetime")}`);
|
|
4253
4399
|
return lines;
|
|
4254
4400
|
}
|
|
4255
4401
|
function terminalLines(result, terminal) {
|
|
4256
4402
|
const head = `
|
|
4257
|
-
${
|
|
4403
|
+
${t35("cli.doctor.terminal_head")}`;
|
|
4258
4404
|
if (!result.ok) {
|
|
4259
4405
|
if (result.failure.kind === "no-tty") {
|
|
4260
4406
|
return [
|
|
4261
4407
|
head,
|
|
4262
|
-
` ${STATUS_MARK.warn} ${
|
|
4263
|
-
` \xB7 ${
|
|
4408
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_no_tty")}`,
|
|
4409
|
+
` \xB7 ${t35("cli.doctor.terminal_alternatives")}`
|
|
4264
4410
|
];
|
|
4265
4411
|
}
|
|
4266
4412
|
return [
|
|
4267
4413
|
head,
|
|
4268
|
-
` ${STATUS_MARK.warn} ${
|
|
4414
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_timeout", { n: result.failure.timeoutMs / 1e3 })}`
|
|
4269
4415
|
];
|
|
4270
4416
|
}
|
|
4271
4417
|
switch (result.sample.kind) {
|
|
4272
4418
|
case "shift-enter":
|
|
4273
|
-
return [head, ` ${STATUS_MARK.ok} ${
|
|
4419
|
+
return [head, ` ${STATUS_MARK.ok} ${t35("cli.doctor.terminal_shift_enter")}`];
|
|
4274
4420
|
case "enter-only":
|
|
4275
4421
|
if (terminal?.kind === "apple-terminal") {
|
|
4276
4422
|
return [
|
|
4277
4423
|
head,
|
|
4278
|
-
` ${STATUS_MARK.warn} ${
|
|
4279
|
-
` \xB7 ${
|
|
4424
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_apple")}`,
|
|
4425
|
+
` \xB7 ${t35("cli.doctor.terminal_alternatives")}`
|
|
4280
4426
|
];
|
|
4281
4427
|
}
|
|
4282
4428
|
return [
|
|
4283
4429
|
head,
|
|
4284
|
-
` ${STATUS_MARK.warn} ${
|
|
4285
|
-
` \xB7 ${
|
|
4286
|
-
` \xB7 ${
|
|
4430
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_enter_only")}`,
|
|
4431
|
+
` \xB7 ${t35("cli.doctor.terminal_alternatives")}`,
|
|
4432
|
+
` \xB7 ${t35("cli.doctor.terminal_setup_hint")}`
|
|
4287
4433
|
];
|
|
4288
4434
|
case "other":
|
|
4289
4435
|
return [
|
|
4290
4436
|
head,
|
|
4291
|
-
` ${STATUS_MARK.warn} ${
|
|
4437
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_other", { hex: result.sample.hex })}`
|
|
4292
4438
|
];
|
|
4293
4439
|
case "interrupted":
|
|
4294
|
-
return [head, ` \xB7 ${
|
|
4440
|
+
return [head, ` \xB7 ${t35("cli.doctor.terminal_interrupted")}`];
|
|
4295
4441
|
}
|
|
4296
4442
|
}
|
|
4297
4443
|
async function statusLineLines(config, cwd) {
|
|
@@ -4299,95 +4445,96 @@ async function statusLineLines(config, cwd) {
|
|
|
4299
4445
|
if (!probe) return [];
|
|
4300
4446
|
return [
|
|
4301
4447
|
`
|
|
4302
|
-
${
|
|
4303
|
-
` ${
|
|
4304
|
-
` ${probe.ok ?
|
|
4448
|
+
${t35("cli.doctor.status_line_head")}`,
|
|
4449
|
+
` ${t35("cli.doctor.status_line_command", { command: probe.command })}`,
|
|
4450
|
+
` ${probe.ok ? t35("cli.doctor.status_line_ok", { detail: probe.detail }) : t35("cli.doctor.status_line_broken", { detail: probe.detail })}`
|
|
4305
4451
|
];
|
|
4306
4452
|
}
|
|
4307
4453
|
function registerStatusCommands(program2) {
|
|
4308
|
-
program2.command("status").description(
|
|
4309
|
-
|
|
4454
|
+
program2.command("status").description(t35("cli.status.cmd_root")).action(async () => {
|
|
4455
|
+
log10(
|
|
4310
4456
|
[
|
|
4311
|
-
|
|
4457
|
+
t35("cli.status.version", { version: VERSION }),
|
|
4312
4458
|
`Node: ${process.version} (${process.platform}/${process.arch})`,
|
|
4313
|
-
|
|
4459
|
+
t35("cli.status.home", { path: EPOCH_HOME }),
|
|
4314
4460
|
`Profile: ${resolveProfile()}`,
|
|
4315
|
-
|
|
4316
|
-
state: hasCredentials() ?
|
|
4461
|
+
t35("cli.status.credentials", {
|
|
4462
|
+
state: hasCredentials() ? t35("cli.status.credentials_set") : t35("cli.status.credentials_unset")
|
|
4317
4463
|
}),
|
|
4318
|
-
|
|
4464
|
+
t35("cli.status.secret_backend", { detail: await describeSecretBackend() }),
|
|
4319
4465
|
`config.yaml: ${describeConfig()}`,
|
|
4320
|
-
`.env: ${existsSync7(ENV_PATH) ?
|
|
4466
|
+
`.env: ${existsSync7(ENV_PATH) ? t35("cli.status.env_present") : t35("cli.status.env_absent")}`
|
|
4321
4467
|
].join("\n")
|
|
4322
4468
|
);
|
|
4323
4469
|
});
|
|
4324
|
-
program2.command("doctor").description(
|
|
4325
|
-
|
|
4470
|
+
program2.command("doctor").description(t35("cli.doctor.cmd_root")).option("--reclaim", t35("cli.doctor.reclaim_opt")).action(async (opts) => {
|
|
4471
|
+
log10(t35("cli.doctor.node", { version: process.version }));
|
|
4326
4472
|
const avail = await describeDisk();
|
|
4327
|
-
if (avail)
|
|
4328
|
-
|
|
4329
|
-
const configIssues = existsSync7(CONFIG_PATH) ? (
|
|
4473
|
+
if (avail) log10(t35("cli.doctor.disk", { avail }));
|
|
4474
|
+
log10(`${hasCredentials() ? "\u2713" : "\u25CB"} ${t35("cli.doctor.credentials")}`);
|
|
4475
|
+
const configIssues = existsSync7(CONFIG_PATH) ? (loadConfig4(), getConfigIssues()) : [];
|
|
4330
4476
|
const configMark = !existsSync7(CONFIG_PATH) ? "\u25CB" : configIssues.length > 0 ? "\u26A0" : "\u2713";
|
|
4331
|
-
|
|
4477
|
+
log10(`${configMark} config: ${CONFIG_PATH}`);
|
|
4332
4478
|
try {
|
|
4333
4479
|
const { buildRuntime: buildRuntime4 } = await import("@epoch-agent/runtime");
|
|
4334
4480
|
const rt = await buildRuntime4({ installSignalHandlers: true });
|
|
4335
|
-
|
|
4336
|
-
${
|
|
4337
|
-
for (const line of workspaceLines(rt))
|
|
4338
|
-
for (const line of sandboxLines(rt.workspace.root, rt.config.sandbox))
|
|
4339
|
-
for (const line of hookLines(rt))
|
|
4340
|
-
for (const line of jobLines(allJobs().map((j) => j.info)))
|
|
4481
|
+
log10(`
|
|
4482
|
+
${t35("cli.doctor.workspace_head")}`);
|
|
4483
|
+
for (const line of workspaceLines(rt)) log10(line);
|
|
4484
|
+
for (const line of sandboxLines(rt.workspace.root, rt.config.sandbox)) log10(line);
|
|
4485
|
+
for (const line of hookLines(rt)) log10(line);
|
|
4486
|
+
for (const line of jobLines(allJobs().map((j) => j.info))) log10(line);
|
|
4487
|
+
const { classifyTerminal, probeShiftEnter } = await import("@epoch-agent/tui");
|
|
4341
4488
|
for (const line of terminalLines(
|
|
4342
|
-
await probeShiftEnter({ prompt:
|
|
4489
|
+
await probeShiftEnter({ prompt: t35("cli.doctor.terminal_prompt"), timeoutMs: 8e3 }),
|
|
4343
4490
|
classifyTerminal(process.env)
|
|
4344
4491
|
))
|
|
4345
|
-
|
|
4346
|
-
for (const line of await statusLineLines(rt.config, process.cwd()))
|
|
4492
|
+
log10(line);
|
|
4493
|
+
for (const line of await statusLineLines(rt.config, process.cwd())) log10(line);
|
|
4347
4494
|
if (rt.sessionStore) {
|
|
4348
4495
|
try {
|
|
4349
|
-
for (const line of ftsLines(rt.sessionStore.ftsHealth()))
|
|
4496
|
+
for (const line of ftsLines(rt.sessionStore.ftsHealth())) log10(line);
|
|
4350
4497
|
} catch (err2) {
|
|
4351
|
-
|
|
4352
|
-
${
|
|
4353
|
-
|
|
4354
|
-
` ${STATUS_MARK.warn} ${
|
|
4498
|
+
log10(`
|
|
4499
|
+
${t35("cli.doctor.fts_head")}`);
|
|
4500
|
+
log10(
|
|
4501
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.fts_probe_failed", {
|
|
4355
4502
|
detail: err2 instanceof Error ? err2.message : String(err2)
|
|
4356
4503
|
})}`
|
|
4357
4504
|
);
|
|
4358
4505
|
}
|
|
4359
4506
|
if (opts.reclaim) {
|
|
4360
4507
|
try {
|
|
4361
|
-
for (const line of reclaimLines(rt.sessionStore.reclaimFts()))
|
|
4508
|
+
for (const line of reclaimLines(rt.sessionStore.reclaimFts())) log10(line);
|
|
4362
4509
|
} catch (err2) {
|
|
4363
|
-
|
|
4364
|
-
${
|
|
4365
|
-
|
|
4366
|
-
` ${STATUS_MARK.failed} ${
|
|
4510
|
+
log10(`
|
|
4511
|
+
${t35("cli.doctor.reclaim_head")}`);
|
|
4512
|
+
log10(
|
|
4513
|
+
` ${STATUS_MARK.failed} ${t35("cli.doctor.reclaim_failed", {
|
|
4367
4514
|
detail: err2 instanceof Error ? err2.message : String(err2)
|
|
4368
4515
|
})}`
|
|
4369
4516
|
);
|
|
4370
4517
|
}
|
|
4371
4518
|
}
|
|
4372
4519
|
}
|
|
4373
|
-
|
|
4374
|
-
${
|
|
4520
|
+
log10(`
|
|
4521
|
+
${t35("cli.doctor.modules_head")}`);
|
|
4375
4522
|
const order = { failed: 0, warn: 1, skipped: 2, ok: 3 };
|
|
4376
4523
|
const sorted = [...rt.diagnosticList].sort(
|
|
4377
4524
|
(a, b) => (order[a.status] ?? 9) - (order[b.status] ?? 9)
|
|
4378
4525
|
);
|
|
4379
|
-
for (const d of sorted)
|
|
4526
|
+
for (const d of sorted) log10(` ${STATUS_MARK[d.status]} ${d.module}: ${d.detail}`);
|
|
4380
4527
|
await rt.dispose();
|
|
4381
4528
|
if (hasFailure(rt.diagnosticList)) {
|
|
4382
4529
|
const bad = rt.diagnosticList.filter((d) => d.status === "failed").length;
|
|
4383
|
-
|
|
4384
|
-
${
|
|
4530
|
+
log10(`
|
|
4531
|
+
${t35("cli.doctor.modules_failed", { count: bad })}`);
|
|
4385
4532
|
process.exitCode = EXIT_CODES.FAILURE;
|
|
4386
4533
|
}
|
|
4387
4534
|
} catch (err2) {
|
|
4388
|
-
|
|
4535
|
+
log10(
|
|
4389
4536
|
`
|
|
4390
|
-
${
|
|
4537
|
+
${t35("cli.doctor.build_failed", {
|
|
4391
4538
|
message: err2 instanceof Error ? err2.message : String(err2)
|
|
4392
4539
|
})}`
|
|
4393
4540
|
);
|
|
@@ -4397,21 +4544,21 @@ ${t34("cli.doctor.build_failed", {
|
|
|
4397
4544
|
// src/commands/trust.ts
|
|
4398
4545
|
import { existsSync as existsSync8, realpathSync as realpathSync3 } from "fs";
|
|
4399
4546
|
import { resolve as resolve3 } from "path";
|
|
4400
|
-
import { loadConfig as
|
|
4401
|
-
import { t as
|
|
4402
|
-
var
|
|
4547
|
+
import { loadConfig as loadConfig5, TrustManager as TrustManager3 } from "@epoch-agent/core";
|
|
4548
|
+
import { t as t36, trustPath as trustPath3, uiDateLocale as uiDateLocale4 } from "@epoch-agent/infra";
|
|
4549
|
+
var log11 = (msg) => process.stdout.write(msg + "\n");
|
|
4403
4550
|
var warn3 = (msg) => process.stderr.write(msg + "\n");
|
|
4404
4551
|
function scopeLabel(scope) {
|
|
4405
|
-
return scope === "directory-tree" ?
|
|
4552
|
+
return scope === "directory-tree" ? t36("trust.scope_tree") : t36("trust.scope_dir");
|
|
4406
4553
|
}
|
|
4407
4554
|
function levelLabel(level) {
|
|
4408
4555
|
switch (level) {
|
|
4409
4556
|
case "trusted":
|
|
4410
|
-
return
|
|
4557
|
+
return t36("trust.level_trusted");
|
|
4411
4558
|
case "untrusted":
|
|
4412
|
-
return
|
|
4559
|
+
return t36("trust.level_untrusted");
|
|
4413
4560
|
case "unknown":
|
|
4414
|
-
return
|
|
4561
|
+
return t36("trust.level_unknown");
|
|
4415
4562
|
}
|
|
4416
4563
|
}
|
|
4417
4564
|
function physicalPath(input2) {
|
|
@@ -4424,63 +4571,63 @@ function physicalPath(input2) {
|
|
|
4424
4571
|
}
|
|
4425
4572
|
}
|
|
4426
4573
|
function registerTrustCommand(program2) {
|
|
4427
|
-
const cmd = program2.command("trust").description(
|
|
4428
|
-
cmd.command("list", { isDefault: true }).description(
|
|
4429
|
-
cmd.command("add").description(
|
|
4574
|
+
const cmd = program2.command("trust").description(t36("trust.cmd_root"));
|
|
4575
|
+
cmd.command("list", { isDefault: true }).description(t36("trust.cmd_list")).action(() => withTrust((tm) => printList3(tm)));
|
|
4576
|
+
cmd.command("add").description(t36("trust.cmd_add")).argument("[path]", t36("trust.arg_path_default")).option("--tree", t36("trust.opt_tree_add")).action((path, opts) => {
|
|
4430
4577
|
withTrust((tm) => {
|
|
4431
4578
|
const dir = physicalPath(path ?? process.cwd());
|
|
4432
4579
|
tm.record(dir, "trusted", opts.tree ? "directory-tree" : "directory");
|
|
4433
|
-
|
|
4434
|
-
`\u2713 ${
|
|
4580
|
+
log11(
|
|
4581
|
+
`\u2713 ${t36("trust.added", {
|
|
4435
4582
|
dir,
|
|
4436
4583
|
scope: scopeLabel(opts.tree ? "directory-tree" : "directory")
|
|
4437
4584
|
})}`
|
|
4438
4585
|
);
|
|
4439
4586
|
});
|
|
4440
4587
|
});
|
|
4441
|
-
cmd.command("deny").description(
|
|
4588
|
+
cmd.command("deny").description(t36("trust.cmd_deny")).argument("[path]", t36("trust.arg_path_default")).option("--tree", t36("trust.opt_tree_deny")).action((path, opts) => {
|
|
4442
4589
|
withTrust((tm) => {
|
|
4443
4590
|
const dir = physicalPath(path ?? process.cwd());
|
|
4444
4591
|
tm.record(dir, "untrusted", opts.tree ? "directory-tree" : "directory");
|
|
4445
|
-
|
|
4446
|
-
`\u2717 ${
|
|
4592
|
+
log11(
|
|
4593
|
+
`\u2717 ${t36("trust.denied", {
|
|
4447
4594
|
dir,
|
|
4448
4595
|
scope: scopeLabel(opts.tree ? "directory-tree" : "directory")
|
|
4449
4596
|
})}`
|
|
4450
4597
|
);
|
|
4451
4598
|
});
|
|
4452
4599
|
});
|
|
4453
|
-
cmd.command("remove").alias("rm").description(
|
|
4600
|
+
cmd.command("remove").alias("rm").description(t36("trust.cmd_remove")).argument("<path>", t36("trust.arg_path")).action((path) => {
|
|
4454
4601
|
withTrust((tm) => {
|
|
4455
4602
|
const dir = physicalPath(path);
|
|
4456
4603
|
if (!tm.list().some((r) => resolve3(r.path) === dir)) {
|
|
4457
|
-
warn3(
|
|
4604
|
+
warn3(t36("trust.no_record", { dir }));
|
|
4458
4605
|
process.exit(1);
|
|
4459
4606
|
}
|
|
4460
4607
|
tm.revoke(dir);
|
|
4461
|
-
|
|
4608
|
+
log11(t36("trust.removed", { dir, level: levelLabel(tm.check(dir)) }));
|
|
4462
4609
|
});
|
|
4463
4610
|
});
|
|
4464
4611
|
}
|
|
4465
4612
|
function printList3(tm) {
|
|
4466
|
-
if (
|
|
4467
|
-
warn3(
|
|
4613
|
+
if (loadConfig5().trust?.enabled === false) {
|
|
4614
|
+
warn3(t36("trust.gate_off"));
|
|
4468
4615
|
}
|
|
4469
|
-
|
|
4616
|
+
log11(t36("trust.list_head", { path: trustPath3(EPOCH_HOME) }));
|
|
4470
4617
|
const records = [...tm.list()].sort((a, b) => a.path.localeCompare(b.path));
|
|
4471
4618
|
if (records.length === 0) {
|
|
4472
|
-
|
|
4619
|
+
log11(t36("trust.list_empty"));
|
|
4473
4620
|
}
|
|
4474
4621
|
for (const r of records) {
|
|
4475
4622
|
const mark = r.level === "trusted" ? "\u2713" : "\u2717";
|
|
4476
|
-
const when = r.decidedAt ? new Date(r.decidedAt).toLocaleString(uiDateLocale4()) :
|
|
4477
|
-
|
|
4623
|
+
const when = r.decidedAt ? new Date(r.decidedAt).toLocaleString(uiDateLocale4()) : t36("trust.when_unknown");
|
|
4624
|
+
log11(` ${mark} ${r.path} [${scopeLabel(r.scope)}] ${when}`);
|
|
4478
4625
|
}
|
|
4479
4626
|
const cwd = process.cwd();
|
|
4480
|
-
|
|
4627
|
+
log11(
|
|
4481
4628
|
`
|
|
4482
|
-
${
|
|
4483
|
-
${
|
|
4629
|
+
${t36("trust.cwd_line", { cwd })}
|
|
4630
|
+
${t36("trust.cwd_verdict", { level: levelLabel(tm.check(cwd)) })}`
|
|
4484
4631
|
);
|
|
4485
4632
|
}
|
|
4486
4633
|
function withTrust(fn) {
|
|
@@ -4489,46 +4636,46 @@ function withTrust(fn) {
|
|
|
4489
4636
|
try {
|
|
4490
4637
|
fn(tm);
|
|
4491
4638
|
} catch (err2) {
|
|
4492
|
-
warn3(
|
|
4639
|
+
warn3(t36("trust.error", { message: err2 instanceof Error ? err2.message : String(err2) }));
|
|
4493
4640
|
process.exit(1);
|
|
4494
4641
|
}
|
|
4495
4642
|
}
|
|
4496
4643
|
// src/commands/upgrade.ts
|
|
4497
|
-
import { t as
|
|
4498
|
-
var
|
|
4644
|
+
import { t as t37 } from "@epoch-agent/infra";
|
|
4645
|
+
var log12 = (msg) => process.stdout.write(`${msg}
|
|
4499
4646
|
`);
|
|
4500
4647
|
function registerUpgradeCommand(program2) {
|
|
4501
|
-
program2.command("upgrade").description(
|
|
4648
|
+
program2.command("upgrade").description(t37("upgrade.cmd_root")).action(async () => {
|
|
4502
4649
|
const self = selfPackage();
|
|
4503
4650
|
const info = getInstallationInfo(self.name);
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4651
|
+
log12(t37("upgrade.current", { version: self.version }));
|
|
4652
|
+
log12(
|
|
4653
|
+
t37("upgrade.installed_via", {
|
|
4507
4654
|
manager: info.packageManager,
|
|
4508
|
-
global: info.isGlobal ?
|
|
4655
|
+
global: info.isGlobal ? t37("installation.global_suffix") : "",
|
|
4509
4656
|
note: info.note
|
|
4510
4657
|
})
|
|
4511
4658
|
);
|
|
4512
4659
|
const notice = isUpdateCheckDisabled() ? readUpdateNotice() : await refreshUpdateCache({ ttlMs: 0 }).catch(() => null);
|
|
4513
4660
|
if (notice) {
|
|
4514
|
-
|
|
4515
|
-
${
|
|
4516
|
-
} else
|
|
4517
|
-
${
|
|
4661
|
+
log12(`
|
|
4662
|
+
${t37("upgrade.available", { current: notice.current, latest: notice.latest })}`);
|
|
4663
|
+
} else log12(`
|
|
4664
|
+
${t37("upgrade.none")}`);
|
|
4518
4665
|
if (info.updateCommand) {
|
|
4519
|
-
|
|
4520
|
-
${
|
|
4666
|
+
log12(`
|
|
4667
|
+
${t37("upgrade.how")}
|
|
4521
4668
|
${info.updateCommand}`);
|
|
4522
|
-
|
|
4523
|
-
${
|
|
4669
|
+
log12(`
|
|
4670
|
+
${t37("upgrade.why_manual")}`);
|
|
4524
4671
|
}
|
|
4525
4672
|
});
|
|
4526
|
-
program2.command(UPDATE_CHECK_SUBCOMMAND, { hidden: true }).description(
|
|
4673
|
+
program2.command(UPDATE_CHECK_SUBCOMMAND, { hidden: true }).description(t37("upgrade.cmd_refresh")).action(async () => {
|
|
4527
4674
|
await refreshUpdateCache().catch(() => null);
|
|
4528
4675
|
});
|
|
4529
4676
|
}
|
|
4530
4677
|
// src/commands/web.ts
|
|
4531
|
-
import { t as
|
|
4678
|
+
import { t as t38 } from "@epoch-agent/infra";
|
|
4532
4679
|
import { buildRuntime as buildRuntime3 } from "@epoch-agent/runtime";
|
|
4533
4680
|
import {
|
|
4534
4681
|
createWebServer,
|
|
@@ -4575,7 +4722,7 @@ var err = (msg) => {
|
|
|
4575
4722
|
`);
|
|
4576
4723
|
};
|
|
4577
4724
|
function registerWebCommand(program2) {
|
|
4578
|
-
program2.command("web").description(
|
|
4725
|
+
program2.command("web").description(t38("cli.web.summary")).option("-p, --port <port>", t38("cli.web.opt_port", { port: DEFAULT_WEB_PORT })).option("--host <host>", t38("cli.web.opt_host", { host: DEFAULT_WEB_HOST })).option("--token <token>", t38("cli.web.opt_token")).option("--no-open", t38("cli.web.opt_no_open")).option("--json", t38("cli.web.opt_json")).option("--plugins", t38("cli.web.opt_plugins")).action(async (opts, command) => {
|
|
4579
4726
|
await runWeb({ ...opts, json: jsonRequested(opts.json, command.parent?.opts()["json"]) });
|
|
4580
4727
|
});
|
|
4581
4728
|
}
|
|
@@ -4584,7 +4731,7 @@ function jsonRequested(own, fromProgram) {
|
|
|
4584
4731
|
}
|
|
4585
4732
|
async function runWeb(opts) {
|
|
4586
4733
|
const port = parsePort2(opts.port);
|
|
4587
|
-
if (port === null) return fail(
|
|
4734
|
+
if (port === null) return fail(t38("cli.web.err_port", { value: String(opts.port) }));
|
|
4588
4735
|
const binding = decideBinding({
|
|
4589
4736
|
...opts.host === void 0 ? {} : { host: opts.host },
|
|
4590
4737
|
...port === void 0 ? {} : { port },
|
|
@@ -4609,10 +4756,10 @@ async function runWeb(opts) {
|
|
|
4609
4756
|
if (lines.machine) out(lines.machine);
|
|
4610
4757
|
for (const line of lines.human) say(line);
|
|
4611
4758
|
if (opts.open && !openInBrowser(server.url)) say(browserFailed());
|
|
4612
|
-
|
|
4759
|
+
installShutdown2(server, say);
|
|
4613
4760
|
}
|
|
4614
|
-
var missingWebRoot = () =>
|
|
4615
|
-
var browserFailed = () => ` ${
|
|
4761
|
+
var missingWebRoot = () => t38("cli.web.missing_root");
|
|
4762
|
+
var browserFailed = () => ` ${t38("cli.web.browser_failed")}`;
|
|
4616
4763
|
function pluginsOption(plugins) {
|
|
4617
4764
|
return plugins ? { hostMarketplaces: { sources: [] } } : {};
|
|
4618
4765
|
}
|
|
@@ -4624,7 +4771,7 @@ async function boot(binding, plugins) {
|
|
|
4624
4771
|
});
|
|
4625
4772
|
if (!runtime.session) {
|
|
4626
4773
|
await runtime.dispose();
|
|
4627
|
-
fail(`${
|
|
4774
|
+
fail(`${t38("cli.web.no_provider")}
|
|
4628
4775
|
${runtime.diagnostics.join("\n")}`);
|
|
4629
4776
|
return null;
|
|
4630
4777
|
}
|
|
@@ -4646,11 +4793,11 @@ function announceLines(input2) {
|
|
|
4646
4793
|
const human = [];
|
|
4647
4794
|
if (!input2.json) {
|
|
4648
4795
|
human.push(` Epoch Web http://${input2.host}:${input2.port}`);
|
|
4649
|
-
human.push(` ${
|
|
4796
|
+
human.push(` ${t38("cli.web.open_this")}
|
|
4650
4797
|
${input2.url}`);
|
|
4651
4798
|
}
|
|
4652
4799
|
if (input2.lanExposed) {
|
|
4653
|
-
human.push(` ${
|
|
4800
|
+
human.push(` ${t38("cli.web.lan_exposed")}`);
|
|
4654
4801
|
}
|
|
4655
4802
|
if (input2.placeholder) human.push(` \u26A0 ${missingWebRoot()}`);
|
|
4656
4803
|
if (!input2.json) return { human };
|
|
@@ -4662,13 +4809,13 @@ function announceLines(input2) {
|
|
|
4662
4809
|
});
|
|
4663
4810
|
return { machine, human };
|
|
4664
4811
|
}
|
|
4665
|
-
function
|
|
4812
|
+
function installShutdown2(server, say) {
|
|
4666
4813
|
let shuttingDown = false;
|
|
4667
4814
|
const onSignal = () => {
|
|
4668
4815
|
if (shuttingDown) process.exit(1);
|
|
4669
4816
|
shuttingDown = true;
|
|
4670
4817
|
say(`
|
|
4671
|
-
${
|
|
4818
|
+
${t38("cli.web.shutting_down")}`);
|
|
4672
4819
|
server.close().then(
|
|
4673
4820
|
() => process.exit(0),
|
|
4674
4821
|
() => process.exit(1)
|
|
@@ -4687,18 +4834,18 @@ function fail(message) {
|
|
|
4687
4834
|
process.exit(1);
|
|
4688
4835
|
}
|
|
4689
4836
|
// src/language.ts
|
|
4690
|
-
import { loadConfig as
|
|
4837
|
+
import { loadConfig as loadConfig6 } from "@epoch-agent/core";
|
|
4691
4838
|
import { resolveLang, setLang } from "@epoch-agent/infra";
|
|
4692
4839
|
function applyConfiguredLanguage() {
|
|
4693
4840
|
try {
|
|
4694
|
-
setLang(resolveLang(
|
|
4841
|
+
setLang(resolveLang(loadConfig6().display?.language));
|
|
4695
4842
|
} catch {
|
|
4696
4843
|
}
|
|
4697
4844
|
}
|
|
4698
4845
|
// src/index.ts
|
|
4699
4846
|
applyConfiguredLanguage();
|
|
4700
4847
|
var program = new Command();
|
|
4701
|
-
program.name("epoch").description(
|
|
4848
|
+
program.name("epoch").description(t39("cli.program_description"));
|
|
4702
4849
|
registerConfigCommand(program);
|
|
4703
4850
|
registerMcpCommand(program);
|
|
4704
4851
|
registerModelCommand(program);
|
|
@@ -4707,6 +4854,7 @@ registerSessionsCommand(program);
|
|
|
4707
4854
|
registerAgentsCommand(program);
|
|
4708
4855
|
registerPluginCommand(program);
|
|
4709
4856
|
registerTrustCommand(program);
|
|
4857
|
+
registerComplianceCommand(program);
|
|
4710
4858
|
registerScheduleCommand(program);
|
|
4711
4859
|
registerUpgradeCommand(program);
|
|
4712
4860
|
registerWebCommand(program);
|