@epoch-agent/cli 0.2.0 → 0.3.1
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);
|
|
@@ -2829,10 +2967,10 @@ 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
|
}
|
|
2838
2976
|
try {
|
|
@@ -2840,7 +2978,7 @@ async function runOnce(query, opts, resumeId) {
|
|
|
2840
2978
|
if (resumeId) {
|
|
2841
2979
|
const restored = runtime.session.getHistory().length;
|
|
2842
2980
|
process.stderr.write(
|
|
2843
|
-
`${
|
|
2981
|
+
`${t22("run_once.resumed", {
|
|
2844
2982
|
id: resumeId.slice(0, SHORT_ID_LEN),
|
|
2845
2983
|
count: restored
|
|
2846
2984
|
})}
|
|
@@ -2903,7 +3041,7 @@ function collect2(out2, ev, w) {
|
|
|
2903
3041
|
} else if (ev.type === "usage") {
|
|
2904
3042
|
out2.usage = ev.cumulative;
|
|
2905
3043
|
} else if (ev.type === "error") {
|
|
2906
|
-
out2.text =
|
|
3044
|
+
out2.text = t22("run_once.error_prefix", { message: ev.message });
|
|
2907
3045
|
process.exitCode = EXIT_CODES.FAILURE;
|
|
2908
3046
|
} else if (ev.type === "finish") {
|
|
2909
3047
|
out2.finishReason = ev.reason;
|
|
@@ -2938,14 +3076,14 @@ function emitOutcome(out2, opts, format, emitter, tasks = []) {
|
|
|
2938
3076
|
return;
|
|
2939
3077
|
}
|
|
2940
3078
|
if (out2.reasoning) {
|
|
2941
|
-
process.stderr.write(`${
|
|
3079
|
+
process.stderr.write(`${t22("run_once.reasoning_head")}
|
|
2942
3080
|
${out2.reasoning}
|
|
2943
3081
|
|
|
2944
3082
|
`);
|
|
2945
3083
|
}
|
|
2946
3084
|
if (opts.stream === false && out2.text) process.stdout.write(out2.text + "\n");
|
|
2947
3085
|
if (out2.aborted) process.stderr.write(`
|
|
2948
|
-
${
|
|
3086
|
+
${t22("run_once.aborted")}
|
|
2949
3087
|
`);
|
|
2950
3088
|
if (taskLines) process.stderr.write(taskLines + "\n");
|
|
2951
3089
|
const summary = formatSummary(out2.usage, out2.budgetExceeded);
|
|
@@ -2962,10 +3100,10 @@ function noteworthy(diagnostics, verbose) {
|
|
|
2962
3100
|
}
|
|
2963
3101
|
function formatSummary(usage, budgetExceeded) {
|
|
2964
3102
|
if (!usage) return "";
|
|
2965
|
-
const cached = usage.cacheHitTokens ?
|
|
3103
|
+
const cached = usage.cacheHitTokens ? t22("run_once.cache_hit", { count: usage.cacheHitTokens }) : "";
|
|
2966
3104
|
const tokens = `${promptTokens(usage)} in${cached} / ${usage.outputTokens} out`;
|
|
2967
|
-
const cost = usage.costUsd === void 0 ?
|
|
2968
|
-
const stopped = budgetExceeded ?
|
|
3105
|
+
const cost = usage.costUsd === void 0 ? t22("run_once.cost_unknown") : formatUsd(usage.costUsd);
|
|
3106
|
+
const stopped = budgetExceeded ? t22("run_once.budget_reached") : "";
|
|
2969
3107
|
return `\u2500 ${tokens} \xB7 ${cost}${stopped}`;
|
|
2970
3108
|
}
|
|
2971
3109
|
function reportHeadlessAudit(permission) {
|
|
@@ -2974,7 +3112,7 @@ function reportHeadlessAudit(permission) {
|
|
|
2974
3112
|
for (const e of audit) {
|
|
2975
3113
|
const vars = { tool: e.toolName, target: e.target, reason: e.reason };
|
|
2976
3114
|
process.stderr.write(
|
|
2977
|
-
`${e.outcome === "granted" ?
|
|
3115
|
+
`${e.outcome === "granted" ? t22("run_once.audit_granted", vars) : t22("run_once.audit_denied", vars)}
|
|
2978
3116
|
`
|
|
2979
3117
|
);
|
|
2980
3118
|
}
|
|
@@ -2984,7 +3122,7 @@ function reportHeadlessAudit(permission) {
|
|
|
2984
3122
|
}
|
|
2985
3123
|
// src/commands/run-overrides.ts
|
|
2986
3124
|
import { isPermissionLevel as isPermissionLevel2, PERMISSION_LEVELS as PERMISSION_LEVELS2 } from "@epoch-agent/core";
|
|
2987
|
-
import { t as
|
|
3125
|
+
import { t as t23 } from "@epoch-agent/infra";
|
|
2988
3126
|
import { isProviderType as isProviderType2, PROVIDER_TYPES as PROVIDER_TYPES3 } from "@epoch-agent/protocol";
|
|
2989
3127
|
function applyRunFlags(opts) {
|
|
2990
3128
|
applySettingsPath(opts.settings);
|
|
@@ -3003,9 +3141,9 @@ function applyOverrides(opts) {
|
|
|
3003
3141
|
if (opts.provider) {
|
|
3004
3142
|
if (!isProviderType2(opts.provider)) {
|
|
3005
3143
|
throw new CliError(
|
|
3006
|
-
|
|
3144
|
+
t23("flags.provider_unknown", { value: opts.provider }),
|
|
3007
3145
|
1,
|
|
3008
|
-
|
|
3146
|
+
t23("flags.choices", { choices: PROVIDER_TYPES3.join(", ") })
|
|
3009
3147
|
);
|
|
3010
3148
|
}
|
|
3011
3149
|
process.env.EPOCH_PROVIDER = opts.provider;
|
|
@@ -3013,9 +3151,9 @@ function applyOverrides(opts) {
|
|
|
3013
3151
|
if (opts.permission) {
|
|
3014
3152
|
if (!isPermissionLevel2(opts.permission)) {
|
|
3015
3153
|
throw new CliError(
|
|
3016
|
-
|
|
3154
|
+
t23("flags.permission_unknown", { value: opts.permission }),
|
|
3017
3155
|
1,
|
|
3018
|
-
|
|
3156
|
+
t23("flags.choices", { choices: PERMISSION_LEVELS2.join(", ") })
|
|
3019
3157
|
);
|
|
3020
3158
|
}
|
|
3021
3159
|
process.env.EPOCH_PERMISSION = opts.permission;
|
|
@@ -3026,9 +3164,9 @@ function assertModelName(value, flag) {
|
|
|
3026
3164
|
const spec = value.trim();
|
|
3027
3165
|
if (MODEL_ID.test(spec)) return value;
|
|
3028
3166
|
throw new CliError(
|
|
3029
|
-
|
|
3167
|
+
t23("flags.model_not_a_name", { flag, value }),
|
|
3030
3168
|
1,
|
|
3031
|
-
|
|
3169
|
+
t23("flags.model_not_a_name_hint", { value: spec })
|
|
3032
3170
|
);
|
|
3033
3171
|
}
|
|
3034
3172
|
function assertHttpUrl(value) {
|
|
@@ -3038,60 +3176,60 @@ function assertHttpUrl(value) {
|
|
|
3038
3176
|
} catch {
|
|
3039
3177
|
}
|
|
3040
3178
|
throw new CliError(
|
|
3041
|
-
|
|
3179
|
+
t23("flags.base_url_invalid", { value }),
|
|
3042
3180
|
1,
|
|
3043
|
-
|
|
3181
|
+
t23("flags.base_url_invalid_hint", { value: value.trim() })
|
|
3044
3182
|
);
|
|
3045
3183
|
}
|
|
3046
3184
|
// src/commands/sessions.ts
|
|
3047
3185
|
import { isNonInteractive as isNonInteractive7, resolveProjectRoot as resolveProjectRoot3, SessionManager } from "@epoch-agent/core";
|
|
3048
|
-
import { dbPath, t as
|
|
3049
|
-
var
|
|
3186
|
+
import { dbPath, t as t24, uiDateLocale as uiDateLocale2 } from "@epoch-agent/infra";
|
|
3187
|
+
var log7 = (msg) => process.stdout.write(msg + "\n");
|
|
3050
3188
|
var SHORT_ID_LEN2 = 8;
|
|
3051
3189
|
var PICK_LIMIT = 20;
|
|
3052
3190
|
function whenOf(startedAt) {
|
|
3053
|
-
return startedAt ? new Date(startedAt).toLocaleString(uiDateLocale2()) :
|
|
3191
|
+
return startedAt ? new Date(startedAt).toLocaleString(uiDateLocale2()) : t24("sessions.when_unknown");
|
|
3054
3192
|
}
|
|
3055
3193
|
function titleOf(s) {
|
|
3056
|
-
return s.title || s.preview ||
|
|
3194
|
+
return s.title || s.preview || t24("sessions.untitled");
|
|
3057
3195
|
}
|
|
3058
3196
|
function registerSessionsCommand(program2) {
|
|
3059
|
-
const cmd = program2.command("sessions").alias("session").description(
|
|
3060
|
-
cmd.command("list").description(
|
|
3197
|
+
const cmd = program2.command("sessions").alias("session").description(t24("sessions.cmd_root"));
|
|
3198
|
+
cmd.command("list").description(t24("sessions.cmd_list")).option("-n, --limit <n>", t24("sessions.opt_limit"), "10").action((opts) => {
|
|
3061
3199
|
withSessions((sm) => {
|
|
3062
3200
|
const r = sm.list({ limit: parseInt(opts.limit, 10) });
|
|
3063
3201
|
if (r.sessions.length === 0) {
|
|
3064
|
-
|
|
3202
|
+
log7(` ${t24("sessions.list_empty")}`);
|
|
3065
3203
|
return;
|
|
3066
3204
|
}
|
|
3067
3205
|
for (const s of r.sessions) {
|
|
3068
|
-
|
|
3206
|
+
log7(` ${s.id.slice(0, SHORT_ID_LEN2)} ${whenOf(s.startedAt)} ${titleOf(s)}`);
|
|
3069
3207
|
}
|
|
3070
3208
|
});
|
|
3071
3209
|
});
|
|
3072
|
-
cmd.command("resume").description(
|
|
3210
|
+
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
3211
|
const resolved = resolveSessionId(id);
|
|
3074
3212
|
if (!resolved.ok) {
|
|
3075
3213
|
throw new CliError(resolved.error, EXIT_CODES.FAILURE);
|
|
3076
3214
|
}
|
|
3077
|
-
await runOnce(query ||
|
|
3215
|
+
await runOnce(query || t24("sessions.default_query"), {}, resolved.id);
|
|
3078
3216
|
});
|
|
3079
3217
|
}
|
|
3080
3218
|
function resolveSessionId(input2) {
|
|
3081
3219
|
return withSessions((sm) => {
|
|
3082
3220
|
if (!input2) {
|
|
3083
3221
|
const latest = sm.getLatest()?.meta.id;
|
|
3084
|
-
return latest ? { ok: true, id: latest } : { ok: false, error:
|
|
3222
|
+
return latest ? { ok: true, id: latest } : { ok: false, error: t24("sessions.err_none") };
|
|
3085
3223
|
}
|
|
3086
3224
|
if (sm.get(input2)) return { ok: true, id: input2 };
|
|
3087
3225
|
const candidates = sm.list({ limit: 200 }).sessions.filter((s) => s.id.startsWith(input2)).map((s) => s.id);
|
|
3088
3226
|
if (candidates.length === 1) return { ok: true, id: candidates[0] };
|
|
3089
3227
|
if (candidates.length === 0) {
|
|
3090
|
-
return { ok: false, error:
|
|
3228
|
+
return { ok: false, error: t24("sessions.err_not_found", { input: input2 }) };
|
|
3091
3229
|
}
|
|
3092
3230
|
return {
|
|
3093
3231
|
ok: false,
|
|
3094
|
-
error:
|
|
3232
|
+
error: t24("sessions.err_ambiguous", { input: input2, count: candidates.length })
|
|
3095
3233
|
};
|
|
3096
3234
|
});
|
|
3097
3235
|
}
|
|
@@ -3102,9 +3240,9 @@ function latestSessionIdForProject(cwd = process.cwd()) {
|
|
|
3102
3240
|
async function pickSessionId() {
|
|
3103
3241
|
if (isNonInteractive7()) {
|
|
3104
3242
|
throw new CliError(
|
|
3105
|
-
|
|
3243
|
+
t24("sessions.err_needs_id"),
|
|
3106
3244
|
EXIT_CODES.FAILURE,
|
|
3107
|
-
|
|
3245
|
+
t24("sessions.err_needs_id_hint")
|
|
3108
3246
|
);
|
|
3109
3247
|
}
|
|
3110
3248
|
const rows = withSessions(
|
|
@@ -3112,22 +3250,22 @@ async function pickSessionId() {
|
|
|
3112
3250
|
id: s.id,
|
|
3113
3251
|
title: titleOf(s),
|
|
3114
3252
|
when: whenOf(s.startedAt),
|
|
3115
|
-
where: s.cwd ??
|
|
3253
|
+
where: s.cwd ?? t24("sessions.cwd_unrecorded"),
|
|
3116
3254
|
messageCount: s.messageCount
|
|
3117
3255
|
}))
|
|
3118
3256
|
);
|
|
3119
3257
|
if (rows.length === 0) {
|
|
3120
|
-
|
|
3258
|
+
log7(t24("sessions.pick_empty"));
|
|
3121
3259
|
return void 0;
|
|
3122
3260
|
}
|
|
3123
3261
|
const { select: select2 } = await import("@inquirer/prompts");
|
|
3124
3262
|
try {
|
|
3125
3263
|
return await select2({
|
|
3126
|
-
message:
|
|
3264
|
+
message: t24("sessions.pick_prompt"),
|
|
3127
3265
|
choices: rows.map((r) => ({
|
|
3128
3266
|
name: `${r.id.slice(0, SHORT_ID_LEN2)} ${r.when} ${r.title}`,
|
|
3129
3267
|
value: r.id,
|
|
3130
|
-
description: `${
|
|
3268
|
+
description: `${t24("tui.resume.message_count", { n: r.messageCount })} \xB7 ${r.where}`
|
|
3131
3269
|
})),
|
|
3132
3270
|
pageSize: 12
|
|
3133
3271
|
});
|
|
@@ -3144,27 +3282,27 @@ function withSessions(fn) {
|
|
|
3144
3282
|
}
|
|
3145
3283
|
}
|
|
3146
3284
|
// src/commands/run.ts
|
|
3147
|
-
var
|
|
3285
|
+
var log8 = (msg) => {
|
|
3148
3286
|
process.stdout.write(msg + "\n");
|
|
3149
3287
|
};
|
|
3150
3288
|
function registerRunCommand(program2) {
|
|
3151
|
-
program2.argument("[query]",
|
|
3289
|
+
program2.argument("[query]", t25("run.arg_query")).option("--allow-tool <name>", t25("run.opt_allow_tool"), collect, []).option(
|
|
3152
3290
|
"--allow-operation <type>",
|
|
3153
|
-
|
|
3291
|
+
t25("run.opt_allow_operation", { types: OPERATION_TYPES3.join(" / ") }),
|
|
3154
3292
|
collect,
|
|
3155
3293
|
[]
|
|
3156
|
-
).option("-i, --image <path>",
|
|
3294
|
+
).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
3295
|
"--permission <level>",
|
|
3158
|
-
|
|
3159
|
-
).option("--settings <file>",
|
|
3296
|
+
t25("run.opt_permission", { levels: PERMISSION_LEVELS3.join(" / ") })
|
|
3297
|
+
).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
3298
|
"--output-format <fmt>",
|
|
3161
|
-
|
|
3299
|
+
t25("run.opt_output_format", { formats: HEADLESS_OUTPUT_FORMATS2.join(" / ") })
|
|
3162
3300
|
).option(
|
|
3163
3301
|
"--input-format <fmt>",
|
|
3164
|
-
|
|
3165
|
-
).option("--permission-prompt-tool <program>",
|
|
3302
|
+
t25("run.opt_input_format", { formats: HEADLESS_INPUT_FORMATS2.join(" / ") })
|
|
3303
|
+
).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
3304
|
if (opts.version === true) {
|
|
3167
|
-
|
|
3305
|
+
log8(describeVersion(selfPackage().name));
|
|
3168
3306
|
return;
|
|
3169
3307
|
}
|
|
3170
3308
|
applyRunFlags(opts);
|
|
@@ -3174,39 +3312,39 @@ function registerRunCommand(program2) {
|
|
|
3174
3312
|
if (formats.input === "stream-json") {
|
|
3175
3313
|
if (opts.continue === true || resumeArg.kind !== "off") {
|
|
3176
3314
|
throw new CliError(
|
|
3177
|
-
|
|
3315
|
+
t25("run.conflict_resume"),
|
|
3178
3316
|
EXIT_CODES.FAILURE,
|
|
3179
|
-
|
|
3317
|
+
t25("run.conflict_resume_hint")
|
|
3180
3318
|
);
|
|
3181
3319
|
}
|
|
3182
3320
|
if (opts.worktree === true) {
|
|
3183
3321
|
throw new CliError(
|
|
3184
|
-
|
|
3322
|
+
t25("run.conflict_worktree"),
|
|
3185
3323
|
EXIT_CODES.FAILURE,
|
|
3186
|
-
|
|
3324
|
+
t25("run.conflict_worktree_hint")
|
|
3187
3325
|
);
|
|
3188
3326
|
}
|
|
3189
3327
|
if (opts.permissionPromptTool !== void 0) {
|
|
3190
3328
|
throw new CliError(
|
|
3191
|
-
|
|
3329
|
+
t25("run.conflict_prompt_tool"),
|
|
3192
3330
|
EXIT_CODES.FAILURE,
|
|
3193
|
-
|
|
3331
|
+
t25("run.conflict_prompt_tool_hint")
|
|
3194
3332
|
);
|
|
3195
3333
|
}
|
|
3196
3334
|
if (!hasCredentials()) {
|
|
3197
3335
|
throw new CliError(
|
|
3198
|
-
|
|
3336
|
+
t25("run.no_credentials_stream"),
|
|
3199
3337
|
EXIT_CODES.FAILURE,
|
|
3200
|
-
|
|
3338
|
+
t25("run.no_credentials_hint")
|
|
3201
3339
|
);
|
|
3202
3340
|
}
|
|
3203
3341
|
process.exitCode = await runStreamJsonSession(opts);
|
|
3204
3342
|
return;
|
|
3205
3343
|
}
|
|
3206
3344
|
const carried = resumeArg.kind === "pick" ? resumeArg.query : void 0;
|
|
3207
|
-
const prompt = composePrompt(query ?? carried, await
|
|
3345
|
+
const prompt = composePrompt(query ?? carried, await readStdin2());
|
|
3208
3346
|
if (!hasCredentials()) {
|
|
3209
|
-
|
|
3347
|
+
log8(t25("run.welcome"));
|
|
3210
3348
|
return;
|
|
3211
3349
|
}
|
|
3212
3350
|
const resumeId = await resolveResumeTarget(opts, resumeArg);
|
|
@@ -3232,7 +3370,7 @@ async function resolveResumeTarget(opts, resumeArg) {
|
|
|
3232
3370
|
if (opts.continue === true) {
|
|
3233
3371
|
const latest = latestSessionIdForProject();
|
|
3234
3372
|
if (!latest) {
|
|
3235
|
-
process.stderr.write(`${
|
|
3373
|
+
process.stderr.write(`${t25("run.no_history")}
|
|
3236
3374
|
`);
|
|
3237
3375
|
}
|
|
3238
3376
|
return latest;
|
|
@@ -3243,7 +3381,7 @@ async function resolveResumeTarget(opts, resumeArg) {
|
|
|
3243
3381
|
throw new CliError(
|
|
3244
3382
|
resolved.error,
|
|
3245
3383
|
EXIT_CODES.FAILURE,
|
|
3246
|
-
|
|
3384
|
+
t25("run.resume_not_id_hint")
|
|
3247
3385
|
);
|
|
3248
3386
|
}
|
|
3249
3387
|
return resolved.id;
|
|
@@ -3262,7 +3400,7 @@ function resolveTuiEntry(baseDir, exists = existsSync6) {
|
|
|
3262
3400
|
async function launchTui(resumeId) {
|
|
3263
3401
|
const target = resolveTuiEntry(dirname3(fileURLToPath3(import.meta.url)));
|
|
3264
3402
|
if (!target) {
|
|
3265
|
-
process.stderr.write(`${
|
|
3403
|
+
process.stderr.write(`${t25("run.tui_entry_missing")}
|
|
3266
3404
|
`);
|
|
3267
3405
|
process.exitCode = EXIT_CODES.FAILURE;
|
|
3268
3406
|
return;
|
|
@@ -3277,7 +3415,7 @@ async function launchTui(resumeId) {
|
|
|
3277
3415
|
});
|
|
3278
3416
|
await new Promise((done) => {
|
|
3279
3417
|
child.on("error", (err2) => {
|
|
3280
|
-
process.stderr.write(`${
|
|
3418
|
+
process.stderr.write(`${t25("run.tui_spawn_failed", { message: err2.message })}
|
|
3281
3419
|
`);
|
|
3282
3420
|
process.exitCode = EXIT_CODES.FAILURE;
|
|
3283
3421
|
done();
|
|
@@ -3289,13 +3427,13 @@ async function launchTui(resumeId) {
|
|
|
3289
3427
|
});
|
|
3290
3428
|
}
|
|
3291
3429
|
// src/commands/schedule.ts
|
|
3292
|
-
import { t as
|
|
3430
|
+
import { t as t33 } from "@epoch-agent/infra";
|
|
3293
3431
|
// src/schedule/add.ts
|
|
3294
3432
|
import {
|
|
3295
3433
|
SCHEDULE_DEFAULTS,
|
|
3296
3434
|
validateSchedule
|
|
3297
3435
|
} from "@epoch-agent/core";
|
|
3298
|
-
import { automationWorkDir, t as
|
|
3436
|
+
import { automationWorkDir, t as t28 } from "@epoch-agent/infra";
|
|
3299
3437
|
import {
|
|
3300
3438
|
isOperationType as isOperationType2,
|
|
3301
3439
|
isPermissionLevel as isPermissionLevel3,
|
|
@@ -3304,7 +3442,7 @@ import {
|
|
|
3304
3442
|
SCHEDULE_INTERVAL_MINUTES
|
|
3305
3443
|
} from "@epoch-agent/protocol";
|
|
3306
3444
|
// src/schedule/labels.ts
|
|
3307
|
-
import { t as
|
|
3445
|
+
import { t as t26 } from "@epoch-agent/infra";
|
|
3308
3446
|
import {
|
|
3309
3447
|
collectPendingApprovals
|
|
3310
3448
|
} from "@epoch-agent/protocol";
|
|
@@ -3356,35 +3494,35 @@ var DRIFT_KEYS = {
|
|
|
3356
3494
|
"never-registered": "schedule.drift.never_registered"
|
|
3357
3495
|
};
|
|
3358
3496
|
function renderIssues(issues) {
|
|
3359
|
-
return issues.map((i) => ` \xB7 ${
|
|
3497
|
+
return issues.map((i) => ` \xB7 ${t26(ISSUE_KEYS[i.code], { detail: i.detail ?? "" })}`).join("\n");
|
|
3360
3498
|
}
|
|
3361
3499
|
function statusLabel(status) {
|
|
3362
3500
|
const mark = status === "ok" ? "\u2705" : status === "failed" || status === "denied" ? "\u274C" : "\u26A0\uFE0F";
|
|
3363
|
-
return `${mark} ${
|
|
3501
|
+
return `${mark} ${t26(STATUS_KEYS[status])}`;
|
|
3364
3502
|
}
|
|
3365
3503
|
function driftLabel(kind) {
|
|
3366
|
-
return
|
|
3504
|
+
return t26(DRIFT_KEYS[kind]);
|
|
3367
3505
|
}
|
|
3368
3506
|
function describeTrigger(trigger) {
|
|
3369
3507
|
if (trigger.kind === "interval") {
|
|
3370
|
-
return trigger.everyMinutes < 60 ?
|
|
3508
|
+
return trigger.everyMinutes < 60 ? t26("schedule.trigger.every_minutes", { n: trigger.everyMinutes }) : t26("schedule.trigger.every_hours", { n: trigger.everyMinutes / 60 });
|
|
3371
3509
|
}
|
|
3372
3510
|
if (trigger.kind === "once") {
|
|
3373
|
-
return
|
|
3511
|
+
return t26("schedule.trigger.once", { date: trigger.date, at: trigger.at });
|
|
3374
3512
|
}
|
|
3375
|
-
if (trigger.cycle === "daily") return
|
|
3513
|
+
if (trigger.cycle === "daily") return t26("schedule.trigger.daily", { at: trigger.at });
|
|
3376
3514
|
if (trigger.cycle === "weekly") {
|
|
3377
|
-
const days = trigger.weekdays.map((d) =>
|
|
3378
|
-
return
|
|
3515
|
+
const days = trigger.weekdays.map((d) => t26(WEEKDAY_KEYS[d])).join("/");
|
|
3516
|
+
return t26("schedule.trigger.weekly", { days, at: trigger.at });
|
|
3379
3517
|
}
|
|
3380
|
-
return
|
|
3518
|
+
return t26("schedule.trigger.monthly", { days: trigger.days.join("/"), at: trigger.at });
|
|
3381
3519
|
}
|
|
3382
3520
|
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
|
|
3521
|
+
if (level === "plan") return t26("schedule.permission.plan");
|
|
3522
|
+
if (level === "acceptEdits") return t26("schedule.permission.accept_edits");
|
|
3523
|
+
if (level === "bypass") return t26("schedule.permission.bypass");
|
|
3524
|
+
if (level === "auto") return t26("schedule.permission.auto");
|
|
3525
|
+
return t26("schedule.permission.default");
|
|
3388
3526
|
}
|
|
3389
3527
|
// src/schedule/shared.ts
|
|
3390
3528
|
import { realpathSync as realpathSync2 } from "fs";
|
|
@@ -3393,8 +3531,8 @@ import {
|
|
|
3393
3531
|
ScheduleRegistrar,
|
|
3394
3532
|
ScheduleStore
|
|
3395
3533
|
} from "@epoch-agent/core";
|
|
3396
|
-
import { dbPath as dbPath2, t as
|
|
3397
|
-
var
|
|
3534
|
+
import { dbPath as dbPath2, t as t27, uiDateLocale as uiDateLocale3 } from "@epoch-agent/infra";
|
|
3535
|
+
var log9 = (msg) => void process.stdout.write(`${msg}
|
|
3398
3536
|
`);
|
|
3399
3537
|
var warn2 = (msg) => void process.stderr.write(`${msg}
|
|
3400
3538
|
`);
|
|
@@ -3422,24 +3560,24 @@ function mustFind(store, idOrPrefix) {
|
|
|
3422
3560
|
if (hits.length === 1) return hits[0];
|
|
3423
3561
|
if (hits.length === 0) {
|
|
3424
3562
|
throw new CliError(
|
|
3425
|
-
|
|
3563
|
+
t27("schedule.err_not_found", { id: idOrPrefix }),
|
|
3426
3564
|
EXIT_CODES.FAILURE,
|
|
3427
|
-
|
|
3565
|
+
t27("schedule.err_not_found_hint")
|
|
3428
3566
|
);
|
|
3429
3567
|
}
|
|
3430
3568
|
throw new CliError(
|
|
3431
|
-
|
|
3569
|
+
t27("schedule.err_ambiguous", { id: idOrPrefix }),
|
|
3432
3570
|
EXIT_CODES.FAILURE,
|
|
3433
3571
|
hits.map((h) => ` ${h.id} ${h.name}`).join("\n")
|
|
3434
3572
|
);
|
|
3435
3573
|
}
|
|
3436
3574
|
function reportRegistration(outcome, fatal) {
|
|
3437
3575
|
if (outcome.ok) {
|
|
3438
|
-
if (outcome.warnings.includes("source")) warn2(
|
|
3576
|
+
if (outcome.warnings.includes("source")) warn2(t27("schedule.warn_source_entry"));
|
|
3439
3577
|
return;
|
|
3440
3578
|
}
|
|
3441
|
-
const message = outcome.reason === "unsupported-platform" ?
|
|
3442
|
-
const hint = outcome.reason === "refused" && outcome.refusal === "npx" ?
|
|
3579
|
+
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");
|
|
3580
|
+
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
3581
|
if (!fatal) {
|
|
3444
3582
|
warn2(message);
|
|
3445
3583
|
if (hint) warn2(hint);
|
|
@@ -3477,7 +3615,7 @@ async function runAdd(opts) {
|
|
|
3477
3615
|
});
|
|
3478
3616
|
if (validation.issues.length > 0) {
|
|
3479
3617
|
throw new CliError(
|
|
3480
|
-
|
|
3618
|
+
t28("schedule.err_invalid"),
|
|
3481
3619
|
EXIT_CODES.FAILURE,
|
|
3482
3620
|
renderIssues(validation.issues)
|
|
3483
3621
|
);
|
|
@@ -3507,26 +3645,26 @@ async function fillInteractively(opts) {
|
|
|
3507
3645
|
if (!missing) return opts;
|
|
3508
3646
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
3509
3647
|
throw new CliError(
|
|
3510
|
-
|
|
3648
|
+
t28("schedule.err_missing_flags"),
|
|
3511
3649
|
EXIT_CODES.FAILURE,
|
|
3512
|
-
|
|
3650
|
+
t28("schedule.err_missing_flags_hint")
|
|
3513
3651
|
);
|
|
3514
3652
|
}
|
|
3515
3653
|
const { input: input2, select: select2 } = await import("@inquirer/prompts");
|
|
3516
3654
|
const next = { ...opts };
|
|
3517
|
-
next.name ||= await input2({ message:
|
|
3518
|
-
next.prompt ||= await input2({ message:
|
|
3655
|
+
next.name ||= await input2({ message: t28("schedule.ask_name") });
|
|
3656
|
+
next.prompt ||= await input2({ message: t28("schedule.ask_prompt") });
|
|
3519
3657
|
if (!hasTrigger) {
|
|
3520
|
-
const at = await input2({ message:
|
|
3658
|
+
const at = await input2({ message: t28("schedule.ask_at"), default: "09:00" });
|
|
3521
3659
|
next.daily = at;
|
|
3522
3660
|
}
|
|
3523
|
-
next.budget ||= await input2({ message:
|
|
3661
|
+
next.budget ||= await input2({ message: t28("schedule.ask_budget"), default: "0.50" });
|
|
3524
3662
|
next.permission ||= await select2({
|
|
3525
|
-
message:
|
|
3663
|
+
message: t28("schedule.ask_permission"),
|
|
3526
3664
|
choices: [
|
|
3527
|
-
{ name:
|
|
3528
|
-
{ name:
|
|
3529
|
-
{ name:
|
|
3665
|
+
{ name: t28("schedule.permission.default"), value: "default" },
|
|
3666
|
+
{ name: t28("schedule.permission.plan"), value: "plan" },
|
|
3667
|
+
{ name: t28("schedule.permission.accept_edits"), value: "acceptEdits" }
|
|
3530
3668
|
]
|
|
3531
3669
|
});
|
|
3532
3670
|
return next;
|
|
@@ -3535,8 +3673,8 @@ function parseTrigger(opts) {
|
|
|
3535
3673
|
const given = [opts.daily, opts.weekly, opts.monthly, opts.every, opts.once].filter(
|
|
3536
3674
|
(v) => v !== void 0
|
|
3537
3675
|
);
|
|
3538
|
-
if (given.length === 0) throw new CliError(
|
|
3539
|
-
if (given.length > 1) throw new CliError(
|
|
3676
|
+
if (given.length === 0) throw new CliError(t28("schedule.err_no_trigger"), EXIT_CODES.FAILURE);
|
|
3677
|
+
if (given.length > 1) throw new CliError(t28("schedule.err_many_triggers"), EXIT_CODES.FAILURE);
|
|
3540
3678
|
if (opts.daily !== void 0) return { kind: "cron", cycle: "daily", at: opts.daily };
|
|
3541
3679
|
if (opts.weekly !== void 0) {
|
|
3542
3680
|
return {
|
|
@@ -3560,14 +3698,14 @@ function parseTrigger(opts) {
|
|
|
3560
3698
|
return { kind: "interval", everyMinutes: parseEvery(opts.every) };
|
|
3561
3699
|
}
|
|
3562
3700
|
function requireAt(opts) {
|
|
3563
|
-
if (!opts.at) throw new CliError(
|
|
3701
|
+
if (!opts.at) throw new CliError(t28("schedule.err_at_required"), EXIT_CODES.FAILURE);
|
|
3564
3702
|
return opts.at;
|
|
3565
3703
|
}
|
|
3566
3704
|
function parseWeekday(raw) {
|
|
3567
3705
|
const idx = WEEKDAY_CODES.indexOf(raw.trim().toUpperCase());
|
|
3568
3706
|
if (idx < 0) {
|
|
3569
3707
|
throw new CliError(
|
|
3570
|
-
|
|
3708
|
+
t28("schedule.err_weekday", { value: raw }),
|
|
3571
3709
|
EXIT_CODES.FAILURE,
|
|
3572
3710
|
WEEKDAY_CODES.join(",")
|
|
3573
3711
|
);
|
|
@@ -3579,9 +3717,9 @@ function parseEvery(raw) {
|
|
|
3579
3717
|
const minutes = m ? Number(m[1]) * (m[2]?.toLowerCase() === "h" ? 60 : 1) : Number.NaN;
|
|
3580
3718
|
if (!SCHEDULE_INTERVAL_MINUTES.includes(minutes)) {
|
|
3581
3719
|
throw new CliError(
|
|
3582
|
-
|
|
3720
|
+
t28("schedule.err_every", { value: raw }),
|
|
3583
3721
|
EXIT_CODES.FAILURE,
|
|
3584
|
-
|
|
3722
|
+
t28("schedule.err_every_hint", {
|
|
3585
3723
|
values: SCHEDULE_INTERVAL_MINUTES.map((n) => n < 60 ? `${n}m` : `${n / 60}h`).join(" / ")
|
|
3586
3724
|
})
|
|
3587
3725
|
);
|
|
@@ -3592,7 +3730,7 @@ function parsePermission(raw) {
|
|
|
3592
3730
|
if (raw === void 0) return "default";
|
|
3593
3731
|
if (!isPermissionLevel3(raw)) {
|
|
3594
3732
|
throw new CliError(
|
|
3595
|
-
|
|
3733
|
+
t28("schedule.err_permission", { value: raw }),
|
|
3596
3734
|
EXIT_CODES.FAILURE,
|
|
3597
3735
|
PERMISSION_LEVELS4.join(" / ")
|
|
3598
3736
|
);
|
|
@@ -3604,7 +3742,7 @@ function parseOperations(raw) {
|
|
|
3604
3742
|
for (const value of raw ?? []) {
|
|
3605
3743
|
if (!isOperationType2(value)) {
|
|
3606
3744
|
throw new CliError(
|
|
3607
|
-
|
|
3745
|
+
t28("schedule.err_operation", { value }),
|
|
3608
3746
|
EXIT_CODES.FAILURE,
|
|
3609
3747
|
OPERATION_TYPES4.join(" / ")
|
|
3610
3748
|
);
|
|
@@ -3617,9 +3755,9 @@ function buildInput(opts, trigger, permission) {
|
|
|
3617
3755
|
const budget = Number(opts.budget);
|
|
3618
3756
|
if (!Number.isFinite(budget) || budget <= 0) {
|
|
3619
3757
|
throw new CliError(
|
|
3620
|
-
|
|
3758
|
+
t28("schedule.err_budget", { value: opts.budget ?? "" }),
|
|
3621
3759
|
EXIT_CODES.FAILURE,
|
|
3622
|
-
|
|
3760
|
+
t28("schedule.err_budget_hint")
|
|
3623
3761
|
);
|
|
3624
3762
|
}
|
|
3625
3763
|
return {
|
|
@@ -3655,52 +3793,52 @@ function applyDefaultAllowlist(store, def, opts) {
|
|
|
3655
3793
|
}) ?? def;
|
|
3656
3794
|
}
|
|
3657
3795
|
function printSummary(def) {
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3796
|
+
log9(t28("schedule.created", { id: def.id, name: def.name }));
|
|
3797
|
+
log9("");
|
|
3798
|
+
log9(t28("schedule.created_next", { id: def.id.slice(0, 12) }));
|
|
3661
3799
|
}
|
|
3662
3800
|
// src/schedule/doctor.ts
|
|
3663
3801
|
import { diagnoseSchedules, pruneSchedules, repairSchedules } from "@epoch-agent/core";
|
|
3664
|
-
import { t as
|
|
3802
|
+
import { t as t29 } from "@epoch-agent/infra";
|
|
3665
3803
|
async function runDoctor(opts) {
|
|
3666
3804
|
const ctx = await openSchedule();
|
|
3667
3805
|
try {
|
|
3668
3806
|
const deps = { store: ctx.store, backend: ctx.backend, registrar: ctx.registrar };
|
|
3669
3807
|
const report = await diagnoseSchedules(deps);
|
|
3670
3808
|
if (report.unsupported) {
|
|
3671
|
-
warn2(
|
|
3809
|
+
warn2(t29("schedule.doctor_unsupported"));
|
|
3672
3810
|
return;
|
|
3673
3811
|
}
|
|
3674
3812
|
if (report.drifts.length === 0) {
|
|
3675
|
-
|
|
3813
|
+
log9(t29("schedule.doctor_clean", { n: report.checked }));
|
|
3676
3814
|
return;
|
|
3677
3815
|
}
|
|
3678
|
-
|
|
3816
|
+
log9(t29("schedule.doctor_found", { n: report.drifts.length }));
|
|
3679
3817
|
for (const drift of report.drifts) {
|
|
3680
|
-
|
|
3818
|
+
log9(
|
|
3681
3819
|
` \xB7 ${driftLabel(drift.kind)} ${drift.name ?? drift.scheduleId ?? ""} [${drift.osTaskId || "\u2014"}]${drift.detail ? `
|
|
3682
3820
|
${drift.detail}` : ""}`
|
|
3683
3821
|
);
|
|
3684
3822
|
}
|
|
3685
3823
|
if (opts.repair !== true && opts.prune !== true) {
|
|
3686
|
-
|
|
3687
|
-
|
|
3824
|
+
log9("");
|
|
3825
|
+
log9(t29("schedule.doctor_hint"));
|
|
3688
3826
|
return;
|
|
3689
3827
|
}
|
|
3690
3828
|
if (opts.repair === true) {
|
|
3691
3829
|
for (const outcome of await repairSchedules(report.drifts, deps)) {
|
|
3692
3830
|
const label = outcome.drift.name ?? outcome.drift.scheduleId ?? "";
|
|
3693
|
-
if (outcome.ok)
|
|
3831
|
+
if (outcome.ok) log9(t29("schedule.doctor_repaired", { name: label }));
|
|
3694
3832
|
else
|
|
3695
|
-
warn2(
|
|
3833
|
+
warn2(t29("schedule.doctor_repair_failed", { name: label, detail: outcome.detail ?? "" }));
|
|
3696
3834
|
}
|
|
3697
3835
|
}
|
|
3698
3836
|
if (opts.prune === true) {
|
|
3699
3837
|
for (const outcome of await pruneSchedules(report.drifts, deps)) {
|
|
3700
|
-
if (outcome.ok)
|
|
3838
|
+
if (outcome.ok) log9(t29("schedule.doctor_pruned", { id: outcome.drift.osTaskId }));
|
|
3701
3839
|
else {
|
|
3702
3840
|
warn2(
|
|
3703
|
-
|
|
3841
|
+
t29("schedule.doctor_prune_failed", {
|
|
3704
3842
|
id: outcome.drift.osTaskId,
|
|
3705
3843
|
detail: outcome.detail ?? ""
|
|
3706
3844
|
})
|
|
@@ -3714,15 +3852,15 @@ async function runDoctor(opts) {
|
|
|
3714
3852
|
}
|
|
3715
3853
|
}
|
|
3716
3854
|
// src/schedule/manage.ts
|
|
3717
|
-
import { automationLogsDir, t as
|
|
3855
|
+
import { automationLogsDir, t as t30 } from "@epoch-agent/infra";
|
|
3718
3856
|
async function runSetEnabled(idOrPrefix, enabled) {
|
|
3719
3857
|
const ctx = await openSchedule();
|
|
3720
3858
|
try {
|
|
3721
3859
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3722
3860
|
const outcome = await ctx.registrar.setEnabled(def, enabled);
|
|
3723
3861
|
reportRegistration(outcome, false);
|
|
3724
|
-
|
|
3725
|
-
enabled ?
|
|
3862
|
+
log9(
|
|
3863
|
+
enabled ? t30("schedule.enabled_done", { name: def.name }) : t30("schedule.disabled_done", { name: def.name })
|
|
3726
3864
|
);
|
|
3727
3865
|
} finally {
|
|
3728
3866
|
ctx.close();
|
|
@@ -3734,14 +3872,14 @@ async function runRemove(idOrPrefix) {
|
|
|
3734
3872
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3735
3873
|
if (def.osTaskId) reportRegistration(await ctx.registrar.unregister(def), false);
|
|
3736
3874
|
ctx.store.remove(def.id);
|
|
3737
|
-
|
|
3738
|
-
|
|
3875
|
+
log9(t30("schedule.removed", { name: def.name }));
|
|
3876
|
+
log9(t30("schedule.removed_logs_kept", { dir: automationLogsDir(def.id, ctx.homeDir) }));
|
|
3739
3877
|
} finally {
|
|
3740
3878
|
ctx.close();
|
|
3741
3879
|
}
|
|
3742
3880
|
}
|
|
3743
3881
|
// src/schedule/run.ts
|
|
3744
|
-
import { t as
|
|
3882
|
+
import { t as t31 } from "@epoch-agent/infra";
|
|
3745
3883
|
import { unfixedRules } from "@epoch-agent/protocol";
|
|
3746
3884
|
import { fireSchedule } from "@epoch-agent/runtime";
|
|
3747
3885
|
async function runFire(scheduleId, homeDir) {
|
|
@@ -3754,9 +3892,9 @@ async function runFire(scheduleId, homeDir) {
|
|
|
3754
3892
|
ctx.close();
|
|
3755
3893
|
}
|
|
3756
3894
|
if (result.missing) {
|
|
3757
|
-
warn2(
|
|
3895
|
+
warn2(t31("schedule.fire_missing", { id: scheduleId }));
|
|
3758
3896
|
} else if (result.disabled) {
|
|
3759
|
-
warn2(
|
|
3897
|
+
warn2(t31("schedule.fire_disabled", { id: scheduleId }));
|
|
3760
3898
|
} else {
|
|
3761
3899
|
warn2(`${statusLabel(result.status)}${result.detail ? ` ${result.detail}` : ""}`);
|
|
3762
3900
|
}
|
|
@@ -3766,14 +3904,14 @@ async function runManual(idOrPrefix, opts) {
|
|
|
3766
3904
|
const ctx = await openSchedule();
|
|
3767
3905
|
try {
|
|
3768
3906
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3769
|
-
|
|
3907
|
+
log9(t31("schedule.run_start", { name: def.name }));
|
|
3770
3908
|
const result = await fireSchedule({
|
|
3771
3909
|
scheduleId: def.id,
|
|
3772
3910
|
homeDir: ctx.homeDir,
|
|
3773
3911
|
manual: true,
|
|
3774
3912
|
registrar: ctx.registrar
|
|
3775
3913
|
});
|
|
3776
|
-
|
|
3914
|
+
log9(`${statusLabel(result.status)}${result.detail ? ` ${result.detail}` : ""}`);
|
|
3777
3915
|
const pending = result.run?.pendingApprovals ?? [];
|
|
3778
3916
|
if (pending.length === 0) {
|
|
3779
3917
|
process.exitCode = result.exitCode;
|
|
@@ -3782,21 +3920,21 @@ async function runManual(idOrPrefix, opts) {
|
|
|
3782
3920
|
printPending(pending);
|
|
3783
3921
|
if (opts.fix === true) {
|
|
3784
3922
|
const added = applyFix(ctx.store, def.id, pending);
|
|
3785
|
-
|
|
3923
|
+
log9(added.length > 0 ? t31("schedule.fix_done", { n: added.length }) : t31("schedule.fix_none"));
|
|
3786
3924
|
return;
|
|
3787
3925
|
}
|
|
3788
|
-
|
|
3926
|
+
log9(t31("schedule.fix_hint", { id: def.id.slice(0, 12) }));
|
|
3789
3927
|
process.exitCode = result.exitCode;
|
|
3790
3928
|
} finally {
|
|
3791
3929
|
ctx.close();
|
|
3792
3930
|
}
|
|
3793
3931
|
}
|
|
3794
3932
|
function printPending(pending) {
|
|
3795
|
-
|
|
3796
|
-
|
|
3933
|
+
log9("");
|
|
3934
|
+
log9(t31("schedule.pending_header", { n: pending.length }));
|
|
3797
3935
|
for (const item of pending) {
|
|
3798
|
-
|
|
3799
|
-
if (item.suggestedRule)
|
|
3936
|
+
log9(` \xB7 ${t31("schedule.pending_line", { tool: item.toolName, target: item.target })}`);
|
|
3937
|
+
if (item.suggestedRule) log9(` \u2192 ${item.suggestedRule}`);
|
|
3800
3938
|
}
|
|
3801
3939
|
}
|
|
3802
3940
|
function applyFix(store, scheduleId, pending) {
|
|
@@ -3808,23 +3946,23 @@ function applyFix(store, scheduleId, pending) {
|
|
|
3808
3946
|
}
|
|
3809
3947
|
// src/schedule/view.ts
|
|
3810
3948
|
import { nextRunAt, readRecording } from "@epoch-agent/core";
|
|
3811
|
-
import { t as
|
|
3949
|
+
import { t as t32 } from "@epoch-agent/infra";
|
|
3812
3950
|
async function runList() {
|
|
3813
3951
|
const ctx = await openSchedule();
|
|
3814
3952
|
try {
|
|
3815
3953
|
const defs = ctx.store.list();
|
|
3816
3954
|
if (defs.length === 0) {
|
|
3817
|
-
|
|
3818
|
-
|
|
3955
|
+
log9(t32("schedule.list_empty"));
|
|
3956
|
+
log9(t32("schedule.list_empty_hint"));
|
|
3819
3957
|
return;
|
|
3820
3958
|
}
|
|
3821
|
-
|
|
3959
|
+
log9(t32("schedule.list_header"));
|
|
3822
3960
|
for (const def of defs) {
|
|
3823
|
-
const next = def.enabled ? formatWhen(nextRunAt(def.trigger, Date.now(), def.startDate, def.endDate) ?? void 0) :
|
|
3961
|
+
const next = def.enabled ? formatWhen(nextRunAt(def.trigger, Date.now(), def.startDate, def.endDate) ?? void 0) : t32("schedule.disabled_mark");
|
|
3824
3962
|
const danger = def.permission === "bypass" ? " \u{1F534}" : "";
|
|
3825
|
-
|
|
3963
|
+
log9(
|
|
3826
3964
|
` ${def.id.slice(0, 12)} ${def.name}${danger}
|
|
3827
|
-
${describeTrigger(def.trigger)} \xB7 ${
|
|
3965
|
+
${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
3966
|
);
|
|
3829
3967
|
}
|
|
3830
3968
|
} finally {
|
|
@@ -3835,45 +3973,45 @@ async function runShow(idOrPrefix) {
|
|
|
3835
3973
|
const ctx = await openSchedule();
|
|
3836
3974
|
try {
|
|
3837
3975
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3976
|
+
log9(`${def.name} (${def.id})`);
|
|
3977
|
+
log9(` ${t32("schedule.field_enabled")}: ${def.enabled ? t32("schedule.yes") : t32("schedule.no")}`);
|
|
3978
|
+
log9(` ${t32("schedule.field_trigger")}: ${describeTrigger(def.trigger)}`);
|
|
3841
3979
|
if (def.startDate ?? def.endDate) {
|
|
3842
|
-
|
|
3980
|
+
log9(` ${t32("schedule.field_window")}: ${def.startDate ?? "\u2014"} .. ${def.endDate ?? "\u2014"}`);
|
|
3843
3981
|
}
|
|
3844
|
-
|
|
3845
|
-
` ${
|
|
3982
|
+
log9(
|
|
3983
|
+
` ${t32("schedule.field_next")}: ${formatWhen(
|
|
3846
3984
|
nextRunAt(def.trigger, Date.now(), def.startDate, def.endDate) ?? void 0
|
|
3847
3985
|
)}`
|
|
3848
3986
|
);
|
|
3849
|
-
|
|
3850
|
-
if (def.model)
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
` ${
|
|
3987
|
+
log9(` ${t32("schedule.field_workdir")}: ${def.workDir}`);
|
|
3988
|
+
if (def.model) log9(` ${t32("schedule.field_model")}: ${def.model}`);
|
|
3989
|
+
log9(` ${t32("schedule.field_permission")}: ${permissionLabel(def.permission)}`);
|
|
3990
|
+
log9(
|
|
3991
|
+
` ${t32("schedule.field_limits")}: ` + t32("schedule.limits_value", {
|
|
3854
3992
|
turns: def.maxTurns,
|
|
3855
3993
|
budget: def.maxBudgetUsd.toFixed(2),
|
|
3856
3994
|
minutes: Math.round(def.timeoutMs / 6e4)
|
|
3857
3995
|
})
|
|
3858
3996
|
);
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
for (const line of def.prompt.split("\n"))
|
|
3862
|
-
|
|
3997
|
+
log9(` ${t32("schedule.field_backend")}: ${def.osBackend} ${def.osTaskId || "\u2014"}`);
|
|
3998
|
+
log9(` ${t32("schedule.field_prompt")}:`);
|
|
3999
|
+
for (const line of def.prompt.split("\n")) log9(` ${line}`);
|
|
4000
|
+
log9("");
|
|
3863
4001
|
printAllowlist(def);
|
|
3864
|
-
|
|
3865
|
-
|
|
4002
|
+
log9("");
|
|
4003
|
+
log9(t32("schedule.field_last_run", { when: formatWhen(def.lastRunAt) }));
|
|
3866
4004
|
} finally {
|
|
3867
4005
|
ctx.close();
|
|
3868
4006
|
}
|
|
3869
4007
|
}
|
|
3870
4008
|
function printAllowlist(def) {
|
|
3871
4009
|
if (def.permission === "bypass") {
|
|
3872
|
-
|
|
4010
|
+
log9(t32("schedule.allowlist_bypass"));
|
|
3873
4011
|
return;
|
|
3874
4012
|
}
|
|
3875
4013
|
if (def.permission === "plan") {
|
|
3876
|
-
|
|
4014
|
+
log9(t32("schedule.allowlist_readonly"));
|
|
3877
4015
|
return;
|
|
3878
4016
|
}
|
|
3879
4017
|
const lines = [
|
|
@@ -3881,9 +4019,9 @@ function printAllowlist(def) {
|
|
|
3881
4019
|
...def.allowOperations.map((v) => ` ${v}`),
|
|
3882
4020
|
...def.allowRules.map((v) => ` ${v}`)
|
|
3883
4021
|
];
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
4022
|
+
log9(t32("schedule.allowlist_header"));
|
|
4023
|
+
log9(lines.length > 0 ? lines.join("\n") : ` ${t32("schedule.allowlist_empty")}`);
|
|
4024
|
+
log9(t32("schedule.allowlist_scope_note"));
|
|
3887
4025
|
}
|
|
3888
4026
|
async function runLogs(idOrPrefix, limit, tail) {
|
|
3889
4027
|
const ctx = await openSchedule();
|
|
@@ -3891,7 +4029,7 @@ async function runLogs(idOrPrefix, limit, tail) {
|
|
|
3891
4029
|
const def = mustFind(ctx.store, idOrPrefix);
|
|
3892
4030
|
const runs = ctx.store.listRuns(def.id, limit);
|
|
3893
4031
|
if (runs.length === 0) {
|
|
3894
|
-
|
|
4032
|
+
log9(t32("schedule.logs_empty"));
|
|
3895
4033
|
return;
|
|
3896
4034
|
}
|
|
3897
4035
|
for (const run of runs) printRun(run);
|
|
@@ -3901,31 +4039,31 @@ async function runLogs(idOrPrefix, limit, tail) {
|
|
|
3901
4039
|
}
|
|
3902
4040
|
}
|
|
3903
4041
|
function printRun(run) {
|
|
3904
|
-
|
|
3905
|
-
` ${formatWhen(run.startedAt)} ${statusLabel(run.status)} ` +
|
|
4042
|
+
log9(
|
|
4043
|
+
` ${formatWhen(run.startedAt)} ${statusLabel(run.status)} ` + t32("schedule.run_meta", {
|
|
3906
4044
|
turns: run.turns,
|
|
3907
4045
|
cost: run.costUsd.toFixed(4),
|
|
3908
|
-
manual: run.manual ?
|
|
4046
|
+
manual: run.manual ? t32("schedule.run_manual") : ""
|
|
3909
4047
|
})
|
|
3910
4048
|
);
|
|
3911
|
-
if (run.reason)
|
|
4049
|
+
if (run.reason) log9(` ${run.reason}`);
|
|
3912
4050
|
for (const pending of run.pendingApprovals) {
|
|
3913
|
-
|
|
3914
|
-
` \u26A0 ${
|
|
4051
|
+
log9(
|
|
4052
|
+
` \u26A0 ${t32("schedule.pending_line", { tool: pending.toolName, target: pending.target })}`
|
|
3915
4053
|
);
|
|
3916
4054
|
if (pending.suggestedRule) {
|
|
3917
|
-
|
|
4055
|
+
log9(` ${t32("schedule.pending_fix", { rule: pending.suggestedRule })}`);
|
|
3918
4056
|
}
|
|
3919
4057
|
}
|
|
3920
4058
|
}
|
|
3921
4059
|
function printRecording(run) {
|
|
3922
4060
|
if (!run.logPath) return;
|
|
3923
|
-
|
|
3924
|
-
|
|
4061
|
+
log9("");
|
|
4062
|
+
log9(t32("schedule.logs_recording", { path: run.logPath }));
|
|
3925
4063
|
for (const envelope of readRecording(run.logPath)) {
|
|
3926
4064
|
const event = envelope.event;
|
|
3927
4065
|
if (event.type === "text-delta" && event.text) process.stdout.write(event.text);
|
|
3928
|
-
else if (event.type === "error" && event.message)
|
|
4066
|
+
else if (event.type === "error" && event.message) log9(`
|
|
3929
4067
|
[error] ${event.message}`);
|
|
3930
4068
|
}
|
|
3931
4069
|
process.stdout.write("\n");
|
|
@@ -3933,19 +4071,19 @@ function printRecording(run) {
|
|
|
3933
4071
|
// src/commands/schedule.ts
|
|
3934
4072
|
var DEFAULT_LOG_LIMIT = 10;
|
|
3935
4073
|
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(
|
|
4074
|
+
const cmd = program2.command("schedule").description(t33("cli.schedule.summary"));
|
|
4075
|
+
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));
|
|
4076
|
+
cmd.command("list", { isDefault: true }).description(t33("cli.schedule.list")).action(() => runList());
|
|
4077
|
+
cmd.command("show").description(t33("cli.schedule.show")).argument("<id>", t33("cli.schedule.arg_id")).action((id) => runShow(id));
|
|
4078
|
+
cmd.command("enable").description(t33("cli.schedule.enable")).argument("<id>", t33("cli.schedule.arg_id")).action((id) => runSetEnabled(id, true));
|
|
4079
|
+
cmd.command("disable").description(t33("cli.schedule.disable")).argument("<id>", t33("cli.schedule.arg_id")).action((id) => runSetEnabled(id, false));
|
|
4080
|
+
cmd.command("rm").alias("remove").description(t33("cli.schedule.rm")).argument("<id>", t33("cli.schedule.arg_id")).action((id) => runRemove(id));
|
|
4081
|
+
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));
|
|
4082
|
+
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
4083
|
(id, opts) => runLogs(id, Number(opts.limit ?? DEFAULT_LOG_LIMIT), opts.tail === true)
|
|
3946
4084
|
);
|
|
3947
|
-
cmd.command("fire").description(
|
|
3948
|
-
cmd.command("doctor").description(
|
|
4085
|
+
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));
|
|
4086
|
+
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
4087
|
}
|
|
3950
4088
|
function collect3(value, previous) {
|
|
3951
4089
|
return [...previous, value];
|
|
@@ -3956,7 +4094,7 @@ import { statfs } from "fs/promises";
|
|
|
3956
4094
|
import {
|
|
3957
4095
|
describeIsolation,
|
|
3958
4096
|
getConfigIssues,
|
|
3959
|
-
loadConfig as
|
|
4097
|
+
loadConfig as loadConfig4,
|
|
3960
4098
|
SANDBOX_COVERS,
|
|
3961
4099
|
SANDBOX_EXCLUDES
|
|
3962
4100
|
} from "@epoch-agent/core";
|
|
@@ -3964,17 +4102,13 @@ import {
|
|
|
3964
4102
|
allJobs,
|
|
3965
4103
|
confine,
|
|
3966
4104
|
resolveProfile,
|
|
3967
|
-
t as
|
|
4105
|
+
t as t35
|
|
3968
4106
|
} from "@epoch-agent/infra";
|
|
3969
4107
|
import { hasFailure } from "@epoch-agent/protocol";
|
|
3970
|
-
import {
|
|
3971
|
-
classifyTerminal,
|
|
3972
|
-
probeShiftEnter
|
|
3973
|
-
} from "@epoch-agent/tui";
|
|
3974
4108
|
// src/statusline.ts
|
|
3975
4109
|
import { execFile as execFile2 } from "child_process";
|
|
3976
4110
|
import { sanitizeToolOutput } from "@epoch-agent/core";
|
|
3977
|
-
import { shellSpawnArgs, t as
|
|
4111
|
+
import { shellSpawnArgs, t as t34 } from "@epoch-agent/infra";
|
|
3978
4112
|
var TIMEOUT_MS2 = 2e3;
|
|
3979
4113
|
var MAX_WIDTH = 60;
|
|
3980
4114
|
var MAX_BUFFER = 64 * 1024;
|
|
@@ -4002,7 +4136,7 @@ function runStatusLineCommand(command, cwd) {
|
|
|
4002
4136
|
const killed = err2.killed === true;
|
|
4003
4137
|
settle({
|
|
4004
4138
|
text: null,
|
|
4005
|
-
error: killed ?
|
|
4139
|
+
error: killed ? t34("statusline.timeout", { ms: TIMEOUT_MS2 }) : err2.message.trim()
|
|
4006
4140
|
});
|
|
4007
4141
|
return;
|
|
4008
4142
|
}
|
|
@@ -4016,12 +4150,12 @@ async function probeStatusLine(config, cwd) {
|
|
|
4016
4150
|
if (!command) return null;
|
|
4017
4151
|
const result = await runStatusLineCommand(command, cwd);
|
|
4018
4152
|
if (result.text === null) {
|
|
4019
|
-
return { command, ok: false, detail: result.error ??
|
|
4153
|
+
return { command, ok: false, detail: result.error ?? t34("statusline.no_output") };
|
|
4020
4154
|
}
|
|
4021
4155
|
return { command, ok: true, detail: result.text };
|
|
4022
4156
|
}
|
|
4023
4157
|
// src/commands/status.ts
|
|
4024
|
-
var
|
|
4158
|
+
var log10 = (msg) => {
|
|
4025
4159
|
process.stdout.write(msg + "\n");
|
|
4026
4160
|
};
|
|
4027
4161
|
var STATUS_MARK = {
|
|
@@ -4049,10 +4183,10 @@ async function describeDisk(path = ".") {
|
|
|
4049
4183
|
}
|
|
4050
4184
|
}
|
|
4051
4185
|
function describeConfig() {
|
|
4052
|
-
if (!existsSync7(CONFIG_PATH)) return
|
|
4053
|
-
|
|
4186
|
+
if (!existsSync7(CONFIG_PATH)) return t35("cli.status.config_absent");
|
|
4187
|
+
loadConfig4();
|
|
4054
4188
|
const n = getConfigIssues().length;
|
|
4055
|
-
return n === 0 ?
|
|
4189
|
+
return n === 0 ? t35("cli.status.config_present") : t35("cli.status.config_present_with_issues", { count: n });
|
|
4056
4190
|
}
|
|
4057
4191
|
async function describeSecretBackend() {
|
|
4058
4192
|
try {
|
|
@@ -4060,18 +4194,18 @@ async function describeSecretBackend() {
|
|
|
4060
4194
|
const store = await createSecretStore();
|
|
4061
4195
|
return store.encrypted ? store.detail : `\u26A0 ${store.detail}`;
|
|
4062
4196
|
} catch (err2) {
|
|
4063
|
-
return
|
|
4197
|
+
return t35("cli.status.secret_probe_failed", {
|
|
4064
4198
|
message: err2 instanceof Error ? err2.message : String(err2)
|
|
4065
4199
|
});
|
|
4066
4200
|
}
|
|
4067
4201
|
}
|
|
4068
4202
|
function workspaceLines(rt) {
|
|
4069
|
-
const lines = [` ${
|
|
4203
|
+
const lines = [` ${t35("cli.doctor.workspace_root", { root: rt.workspace.root })}`];
|
|
4070
4204
|
lines.push(
|
|
4071
|
-
` ${rt.trusted ?
|
|
4205
|
+
` ${rt.trusted ? t35("cli.doctor.workspace_trusted") : t35("cli.doctor.workspace_untrusted")}`
|
|
4072
4206
|
);
|
|
4073
4207
|
lines.push(
|
|
4074
|
-
` ${rt.workspace.extra.length === 0 ?
|
|
4208
|
+
` ${rt.workspace.extra.length === 0 ? t35("cli.doctor.workspace_no_extra") : t35("cli.doctor.workspace_extra", { count: rt.workspace.extra.length })}`
|
|
4075
4209
|
);
|
|
4076
4210
|
for (const dir of rt.workspace.extra) lines.push(` \xB7 ${dir}`);
|
|
4077
4211
|
return lines;
|
|
@@ -4079,94 +4213,94 @@ function workspaceLines(rt) {
|
|
|
4079
4213
|
function sandboxLines(cwd, sandbox) {
|
|
4080
4214
|
const iso = describeIsolation();
|
|
4081
4215
|
const lines = [`
|
|
4082
|
-
${
|
|
4216
|
+
${t35("cli.doctor.sandbox_head")}`];
|
|
4083
4217
|
if (iso.backend === "none") {
|
|
4084
4218
|
lines.push(` ${STATUS_MARK.skipped} ${iso.detail}`);
|
|
4085
4219
|
lines.push(
|
|
4086
|
-
` ${STATUS_MARK.warn} ${
|
|
4220
|
+
` ${STATUS_MARK.warn} ${t35(
|
|
4087
4221
|
iso.reason === "platform-unsupported" ? "cli.doctor.sandbox_absent_by_design" : "cli.doctor.sandbox_absent_tools"
|
|
4088
4222
|
)}`
|
|
4089
4223
|
);
|
|
4090
4224
|
return lines;
|
|
4091
4225
|
}
|
|
4092
4226
|
lines.push(
|
|
4093
|
-
` ${STATUS_MARK.ok} ${
|
|
4227
|
+
` ${STATUS_MARK.ok} ${t35("cli.doctor.sandbox_backend", { backend: iso.backend, platform: iso.platform })}`
|
|
4094
4228
|
);
|
|
4095
|
-
lines.push(` \xB7 ${
|
|
4229
|
+
lines.push(` \xB7 ${t35("cli.doctor.sandbox_code_exec", { detail: iso.detail })}`);
|
|
4096
4230
|
const probe = (mode) => confine("/bin/sh", ["-c", "true"], { mode, workspaceRoot: cwd, allowNetwork: true });
|
|
4097
4231
|
for (const mode of ["workspace-write", "read-only"]) {
|
|
4098
4232
|
const c = probe(mode);
|
|
4099
4233
|
if (!c.confined) continue;
|
|
4100
|
-
lines.push(` \xB7 ${
|
|
4101
|
-
if (c.enforcement === "partial") lines.push(` ${
|
|
4234
|
+
lines.push(` \xB7 ${t35("cli.doctor.sandbox_terminal", { mode, enforcement: c.enforcement })}`);
|
|
4235
|
+
if (c.enforcement === "partial") lines.push(` ${t35("cli.doctor.sandbox_partial_devnull")}`);
|
|
4102
4236
|
}
|
|
4103
4237
|
const writable = probe("workspace-write");
|
|
4104
4238
|
if (writable.confined) {
|
|
4105
|
-
lines.push(` \xB7 ${
|
|
4239
|
+
lines.push(` \xB7 ${t35("cli.doctor.sandbox_writable")}`);
|
|
4106
4240
|
for (const dir of writable.writableDirs) lines.push(` \xB7 ${dir}`);
|
|
4107
4241
|
}
|
|
4108
4242
|
lines.push(
|
|
4109
|
-
sandbox?.terminal === false ? ` ${STATUS_MARK.warn} ${
|
|
4243
|
+
sandbox?.terminal === false ? ` ${STATUS_MARK.warn} ${t35("cli.doctor.sandbox_switch_off")}` : ` ${STATUS_MARK.ok} ${t35("cli.doctor.sandbox_switch_on")}`
|
|
4110
4244
|
);
|
|
4111
|
-
lines.push(` \xB7 ${
|
|
4245
|
+
lines.push(` \xB7 ${t35("cli.doctor.sandbox_covers", { tools: SANDBOX_COVERS.join(", ") })}`);
|
|
4112
4246
|
lines.push(
|
|
4113
|
-
SANDBOX_EXCLUDES.length === 0 ? ` ${STATUS_MARK.ok} ${
|
|
4247
|
+
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
4248
|
);
|
|
4115
4249
|
return lines;
|
|
4116
4250
|
}
|
|
4117
4251
|
function ftsLines(health) {
|
|
4118
4252
|
const lines = [`
|
|
4119
|
-
${
|
|
4253
|
+
${t35("cli.doctor.fts_head")}`];
|
|
4120
4254
|
const indexed = health.tables.map((tb) => `${tb.table} ${tb.rows}`).join(" \xB7 ");
|
|
4121
|
-
lines.push(` \xB7 ${
|
|
4255
|
+
lines.push(` \xB7 ${t35("cli.doctor.fts_rows", { messages: health.messages, indexed })}`);
|
|
4122
4256
|
const behind = health.tables.filter((tb) => tb.rows !== health.messages);
|
|
4123
4257
|
lines.push(
|
|
4124
|
-
behind.length === 0 ? ` ${STATUS_MARK.ok} ${
|
|
4125
|
-
` ${STATUS_MARK.warn} ${
|
|
4258
|
+
behind.length === 0 ? ` ${STATUS_MARK.ok} ${t35("cli.doctor.fts_in_sync")}` : (
|
|
4259
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.fts_out_of_sync", {
|
|
4126
4260
|
tables: behind.map((tb) => `${tb.table} (${tb.rows - health.messages})`).join(", ")
|
|
4127
4261
|
})}`
|
|
4128
4262
|
)
|
|
4129
4263
|
);
|
|
4130
4264
|
lines.push(
|
|
4131
|
-
health.missingTriggers.length === 0 ? ` ${STATUS_MARK.ok} ${
|
|
4265
|
+
health.missingTriggers.length === 0 ? ` ${STATUS_MARK.ok} ${t35("cli.doctor.fts_triggers_ok")}` : ` ${STATUS_MARK.failed} ${t35("cli.doctor.fts_triggers_missing", {
|
|
4132
4266
|
triggers: health.missingTriggers.join(", ")
|
|
4133
4267
|
})}`
|
|
4134
4268
|
);
|
|
4135
4269
|
for (const tb of health.tables) {
|
|
4136
4270
|
if (tb.error !== null) {
|
|
4137
4271
|
lines.push(
|
|
4138
|
-
` ${STATUS_MARK.failed} ${
|
|
4272
|
+
` ${STATUS_MARK.failed} ${t35("cli.doctor.fts_corrupt", { table: tb.table, detail: tb.error })}`
|
|
4139
4273
|
);
|
|
4140
4274
|
}
|
|
4141
4275
|
if (tb.drift !== null) {
|
|
4142
4276
|
lines.push(
|
|
4143
|
-
` ${STATUS_MARK.warn} ${
|
|
4277
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.fts_drift", { table: tb.table, detail: tb.drift })}`
|
|
4144
4278
|
);
|
|
4145
4279
|
}
|
|
4146
4280
|
}
|
|
4147
4281
|
if (health.tables.some((tb) => !tb.external)) {
|
|
4148
|
-
lines.push(` ${STATUS_MARK.warn} ${
|
|
4282
|
+
lines.push(` ${STATUS_MARK.warn} ${t35("cli.doctor.fts_legacy_schema")}`);
|
|
4149
4283
|
}
|
|
4150
4284
|
if (health.indexBytes !== void 0) {
|
|
4151
|
-
lines.push(` \xB7 ${
|
|
4285
|
+
lines.push(` \xB7 ${t35("cli.doctor.fts_size", { size: formatBytes(health.indexBytes) })}`);
|
|
4152
4286
|
}
|
|
4153
4287
|
if (health.space?.fileBytes !== void 0) {
|
|
4154
4288
|
lines.push(
|
|
4155
|
-
` \xB7 ${
|
|
4289
|
+
` \xB7 ${t35("cli.doctor.fts_space", {
|
|
4156
4290
|
file: formatBytes(health.space.fileBytes),
|
|
4157
4291
|
free: formatBytes(health.space.freeBytes)
|
|
4158
4292
|
})}`
|
|
4159
4293
|
);
|
|
4160
|
-
lines.push(` \xB7 ${
|
|
4294
|
+
lines.push(` \xB7 ${t35("cli.doctor.fts_reclaim_hint")}`);
|
|
4161
4295
|
}
|
|
4162
4296
|
return lines;
|
|
4163
4297
|
}
|
|
4164
4298
|
function reclaimLines(report) {
|
|
4165
4299
|
const lines = [`
|
|
4166
|
-
${
|
|
4300
|
+
${t35("cli.doctor.reclaim_head")}`];
|
|
4167
4301
|
const secs = (ms) => `${(ms / 1e3).toFixed(1)}s`;
|
|
4168
4302
|
lines.push(
|
|
4169
|
-
` ${STATUS_MARK.ok} ${
|
|
4303
|
+
` ${STATUS_MARK.ok} ${t35("cli.doctor.reclaim_optimize", {
|
|
4170
4304
|
before: formatBytes(report.before.freeBytes),
|
|
4171
4305
|
after: formatBytes(report.afterOptimize.freeBytes),
|
|
4172
4306
|
ms: secs(report.optimizeMs)
|
|
@@ -4174,7 +4308,7 @@ ${t34("cli.doctor.reclaim_head")}`];
|
|
|
4174
4308
|
);
|
|
4175
4309
|
if (report.vacuumed) {
|
|
4176
4310
|
lines.push(
|
|
4177
|
-
` ${STATUS_MARK.ok} ${
|
|
4311
|
+
` ${STATUS_MARK.ok} ${t35("cli.doctor.reclaim_vacuum", {
|
|
4178
4312
|
before: formatBytes(report.before.pageBytes),
|
|
4179
4313
|
after: formatBytes(report.after.pageBytes),
|
|
4180
4314
|
ms: secs(report.vacuumMs)
|
|
@@ -4182,14 +4316,14 @@ ${t34("cli.doctor.reclaim_head")}`];
|
|
|
4182
4316
|
);
|
|
4183
4317
|
const saved = report.before.pageBytes - report.after.pageBytes;
|
|
4184
4318
|
lines.push(
|
|
4185
|
-
` ${STATUS_MARK.ok} ${
|
|
4319
|
+
` ${STATUS_MARK.ok} ${t35("cli.doctor.reclaim_total", {
|
|
4186
4320
|
saved: formatBytes(saved),
|
|
4187
4321
|
pct: `${(saved / Math.max(1, report.before.pageBytes) * 100).toFixed(1)}%`
|
|
4188
4322
|
})}`
|
|
4189
4323
|
);
|
|
4190
4324
|
} else if (report.vacuumError !== null) {
|
|
4191
4325
|
lines.push(
|
|
4192
|
-
` ${STATUS_MARK.warn} ${
|
|
4326
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.reclaim_vacuum_skipped", {
|
|
4193
4327
|
detail: report.vacuumError
|
|
4194
4328
|
})}`
|
|
4195
4329
|
);
|
|
@@ -4209,17 +4343,17 @@ function hookLines(rt) {
|
|
|
4209
4343
|
const { sources } = rt.hook;
|
|
4210
4344
|
if (sources.length === 0) return [];
|
|
4211
4345
|
const lines = [`
|
|
4212
|
-
${
|
|
4346
|
+
${t35("cli.doctor.hook_head")}`];
|
|
4213
4347
|
for (const source of sources) {
|
|
4214
|
-
const label =
|
|
4348
|
+
const label = t35(HOOK_SOURCE_LABEL[source.kind]);
|
|
4215
4349
|
if (source.skipped) {
|
|
4216
4350
|
lines.push(` ${STATUS_MARK.skipped} ${label}: ${source.path}`);
|
|
4217
|
-
lines.push(` ${
|
|
4351
|
+
lines.push(` ${t35("cli.doctor.hook_skipped")}`);
|
|
4218
4352
|
continue;
|
|
4219
4353
|
}
|
|
4220
4354
|
const name = source.plugin === void 0 ? label : `${label} ${source.plugin}`;
|
|
4221
4355
|
lines.push(
|
|
4222
|
-
` ${STATUS_MARK.ok} ${name}: ${source.path}${
|
|
4356
|
+
` ${STATUS_MARK.ok} ${name}: ${source.path}${t35("cli.doctor.hook_count", {
|
|
4223
4357
|
n: source.count
|
|
4224
4358
|
})}`
|
|
4225
4359
|
);
|
|
@@ -4233,65 +4367,65 @@ var JOB_KIND_LABEL = {
|
|
|
4233
4367
|
};
|
|
4234
4368
|
function jobLines(jobs) {
|
|
4235
4369
|
const lines = [`
|
|
4236
|
-
${
|
|
4370
|
+
${t35("cli.doctor.jobs_head")}`];
|
|
4237
4371
|
const running = jobs.filter((job) => job.status === "running");
|
|
4238
4372
|
if (running.length === 0) {
|
|
4239
|
-
lines.push(` ${STATUS_MARK.ok} ${
|
|
4373
|
+
lines.push(` ${STATUS_MARK.ok} ${t35("cli.doctor.jobs_none")}`);
|
|
4240
4374
|
return lines;
|
|
4241
4375
|
}
|
|
4242
|
-
lines.push(` ${STATUS_MARK.warn} ${
|
|
4376
|
+
lines.push(` ${STATUS_MARK.warn} ${t35("cli.doctor.jobs_running", { n: running.length })}`);
|
|
4243
4377
|
for (const job of running) {
|
|
4244
4378
|
lines.push(
|
|
4245
|
-
` \xB7 ${
|
|
4246
|
-
kind:
|
|
4379
|
+
` \xB7 ${t35("cli.doctor.jobs_line", {
|
|
4380
|
+
kind: t35(JOB_KIND_LABEL[job.kind]),
|
|
4247
4381
|
id: job.id,
|
|
4248
4382
|
label: job.label
|
|
4249
4383
|
})}`
|
|
4250
4384
|
);
|
|
4251
4385
|
}
|
|
4252
|
-
lines.push(` \xB7 ${
|
|
4386
|
+
lines.push(` \xB7 ${t35("cli.doctor.jobs_lifetime")}`);
|
|
4253
4387
|
return lines;
|
|
4254
4388
|
}
|
|
4255
4389
|
function terminalLines(result, terminal) {
|
|
4256
4390
|
const head = `
|
|
4257
|
-
${
|
|
4391
|
+
${t35("cli.doctor.terminal_head")}`;
|
|
4258
4392
|
if (!result.ok) {
|
|
4259
4393
|
if (result.failure.kind === "no-tty") {
|
|
4260
4394
|
return [
|
|
4261
4395
|
head,
|
|
4262
|
-
` ${STATUS_MARK.warn} ${
|
|
4263
|
-
` \xB7 ${
|
|
4396
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_no_tty")}`,
|
|
4397
|
+
` \xB7 ${t35("cli.doctor.terminal_alternatives")}`
|
|
4264
4398
|
];
|
|
4265
4399
|
}
|
|
4266
4400
|
return [
|
|
4267
4401
|
head,
|
|
4268
|
-
` ${STATUS_MARK.warn} ${
|
|
4402
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_timeout", { n: result.failure.timeoutMs / 1e3 })}`
|
|
4269
4403
|
];
|
|
4270
4404
|
}
|
|
4271
4405
|
switch (result.sample.kind) {
|
|
4272
4406
|
case "shift-enter":
|
|
4273
|
-
return [head, ` ${STATUS_MARK.ok} ${
|
|
4407
|
+
return [head, ` ${STATUS_MARK.ok} ${t35("cli.doctor.terminal_shift_enter")}`];
|
|
4274
4408
|
case "enter-only":
|
|
4275
4409
|
if (terminal?.kind === "apple-terminal") {
|
|
4276
4410
|
return [
|
|
4277
4411
|
head,
|
|
4278
|
-
` ${STATUS_MARK.warn} ${
|
|
4279
|
-
` \xB7 ${
|
|
4412
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_apple")}`,
|
|
4413
|
+
` \xB7 ${t35("cli.doctor.terminal_alternatives")}`
|
|
4280
4414
|
];
|
|
4281
4415
|
}
|
|
4282
4416
|
return [
|
|
4283
4417
|
head,
|
|
4284
|
-
` ${STATUS_MARK.warn} ${
|
|
4285
|
-
` \xB7 ${
|
|
4286
|
-
` \xB7 ${
|
|
4418
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_enter_only")}`,
|
|
4419
|
+
` \xB7 ${t35("cli.doctor.terminal_alternatives")}`,
|
|
4420
|
+
` \xB7 ${t35("cli.doctor.terminal_setup_hint")}`
|
|
4287
4421
|
];
|
|
4288
4422
|
case "other":
|
|
4289
4423
|
return [
|
|
4290
4424
|
head,
|
|
4291
|
-
` ${STATUS_MARK.warn} ${
|
|
4425
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.terminal_other", { hex: result.sample.hex })}`
|
|
4292
4426
|
];
|
|
4293
4427
|
case "interrupted":
|
|
4294
|
-
return [head, ` \xB7 ${
|
|
4428
|
+
return [head, ` \xB7 ${t35("cli.doctor.terminal_interrupted")}`];
|
|
4295
4429
|
}
|
|
4296
4430
|
}
|
|
4297
4431
|
async function statusLineLines(config, cwd) {
|
|
@@ -4299,95 +4433,96 @@ async function statusLineLines(config, cwd) {
|
|
|
4299
4433
|
if (!probe) return [];
|
|
4300
4434
|
return [
|
|
4301
4435
|
`
|
|
4302
|
-
${
|
|
4303
|
-
` ${
|
|
4304
|
-
` ${probe.ok ?
|
|
4436
|
+
${t35("cli.doctor.status_line_head")}`,
|
|
4437
|
+
` ${t35("cli.doctor.status_line_command", { command: probe.command })}`,
|
|
4438
|
+
` ${probe.ok ? t35("cli.doctor.status_line_ok", { detail: probe.detail }) : t35("cli.doctor.status_line_broken", { detail: probe.detail })}`
|
|
4305
4439
|
];
|
|
4306
4440
|
}
|
|
4307
4441
|
function registerStatusCommands(program2) {
|
|
4308
|
-
program2.command("status").description(
|
|
4309
|
-
|
|
4442
|
+
program2.command("status").description(t35("cli.status.cmd_root")).action(async () => {
|
|
4443
|
+
log10(
|
|
4310
4444
|
[
|
|
4311
|
-
|
|
4445
|
+
t35("cli.status.version", { version: VERSION }),
|
|
4312
4446
|
`Node: ${process.version} (${process.platform}/${process.arch})`,
|
|
4313
|
-
|
|
4447
|
+
t35("cli.status.home", { path: EPOCH_HOME }),
|
|
4314
4448
|
`Profile: ${resolveProfile()}`,
|
|
4315
|
-
|
|
4316
|
-
state: hasCredentials() ?
|
|
4449
|
+
t35("cli.status.credentials", {
|
|
4450
|
+
state: hasCredentials() ? t35("cli.status.credentials_set") : t35("cli.status.credentials_unset")
|
|
4317
4451
|
}),
|
|
4318
|
-
|
|
4452
|
+
t35("cli.status.secret_backend", { detail: await describeSecretBackend() }),
|
|
4319
4453
|
`config.yaml: ${describeConfig()}`,
|
|
4320
|
-
`.env: ${existsSync7(ENV_PATH) ?
|
|
4454
|
+
`.env: ${existsSync7(ENV_PATH) ? t35("cli.status.env_present") : t35("cli.status.env_absent")}`
|
|
4321
4455
|
].join("\n")
|
|
4322
4456
|
);
|
|
4323
4457
|
});
|
|
4324
|
-
program2.command("doctor").description(
|
|
4325
|
-
|
|
4458
|
+
program2.command("doctor").description(t35("cli.doctor.cmd_root")).option("--reclaim", t35("cli.doctor.reclaim_opt")).action(async (opts) => {
|
|
4459
|
+
log10(t35("cli.doctor.node", { version: process.version }));
|
|
4326
4460
|
const avail = await describeDisk();
|
|
4327
|
-
if (avail)
|
|
4328
|
-
|
|
4329
|
-
const configIssues = existsSync7(CONFIG_PATH) ? (
|
|
4461
|
+
if (avail) log10(t35("cli.doctor.disk", { avail }));
|
|
4462
|
+
log10(`${hasCredentials() ? "\u2713" : "\u25CB"} ${t35("cli.doctor.credentials")}`);
|
|
4463
|
+
const configIssues = existsSync7(CONFIG_PATH) ? (loadConfig4(), getConfigIssues()) : [];
|
|
4330
4464
|
const configMark = !existsSync7(CONFIG_PATH) ? "\u25CB" : configIssues.length > 0 ? "\u26A0" : "\u2713";
|
|
4331
|
-
|
|
4465
|
+
log10(`${configMark} config: ${CONFIG_PATH}`);
|
|
4332
4466
|
try {
|
|
4333
4467
|
const { buildRuntime: buildRuntime4 } = await import("@epoch-agent/runtime");
|
|
4334
4468
|
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)))
|
|
4469
|
+
log10(`
|
|
4470
|
+
${t35("cli.doctor.workspace_head")}`);
|
|
4471
|
+
for (const line of workspaceLines(rt)) log10(line);
|
|
4472
|
+
for (const line of sandboxLines(rt.workspace.root, rt.config.sandbox)) log10(line);
|
|
4473
|
+
for (const line of hookLines(rt)) log10(line);
|
|
4474
|
+
for (const line of jobLines(allJobs().map((j) => j.info))) log10(line);
|
|
4475
|
+
const { classifyTerminal, probeShiftEnter } = await import("@epoch-agent/tui");
|
|
4341
4476
|
for (const line of terminalLines(
|
|
4342
|
-
await probeShiftEnter({ prompt:
|
|
4477
|
+
await probeShiftEnter({ prompt: t35("cli.doctor.terminal_prompt"), timeoutMs: 8e3 }),
|
|
4343
4478
|
classifyTerminal(process.env)
|
|
4344
4479
|
))
|
|
4345
|
-
|
|
4346
|
-
for (const line of await statusLineLines(rt.config, process.cwd()))
|
|
4480
|
+
log10(line);
|
|
4481
|
+
for (const line of await statusLineLines(rt.config, process.cwd())) log10(line);
|
|
4347
4482
|
if (rt.sessionStore) {
|
|
4348
4483
|
try {
|
|
4349
|
-
for (const line of ftsLines(rt.sessionStore.ftsHealth()))
|
|
4484
|
+
for (const line of ftsLines(rt.sessionStore.ftsHealth())) log10(line);
|
|
4350
4485
|
} catch (err2) {
|
|
4351
|
-
|
|
4352
|
-
${
|
|
4353
|
-
|
|
4354
|
-
` ${STATUS_MARK.warn} ${
|
|
4486
|
+
log10(`
|
|
4487
|
+
${t35("cli.doctor.fts_head")}`);
|
|
4488
|
+
log10(
|
|
4489
|
+
` ${STATUS_MARK.warn} ${t35("cli.doctor.fts_probe_failed", {
|
|
4355
4490
|
detail: err2 instanceof Error ? err2.message : String(err2)
|
|
4356
4491
|
})}`
|
|
4357
4492
|
);
|
|
4358
4493
|
}
|
|
4359
4494
|
if (opts.reclaim) {
|
|
4360
4495
|
try {
|
|
4361
|
-
for (const line of reclaimLines(rt.sessionStore.reclaimFts()))
|
|
4496
|
+
for (const line of reclaimLines(rt.sessionStore.reclaimFts())) log10(line);
|
|
4362
4497
|
} catch (err2) {
|
|
4363
|
-
|
|
4364
|
-
${
|
|
4365
|
-
|
|
4366
|
-
` ${STATUS_MARK.failed} ${
|
|
4498
|
+
log10(`
|
|
4499
|
+
${t35("cli.doctor.reclaim_head")}`);
|
|
4500
|
+
log10(
|
|
4501
|
+
` ${STATUS_MARK.failed} ${t35("cli.doctor.reclaim_failed", {
|
|
4367
4502
|
detail: err2 instanceof Error ? err2.message : String(err2)
|
|
4368
4503
|
})}`
|
|
4369
4504
|
);
|
|
4370
4505
|
}
|
|
4371
4506
|
}
|
|
4372
4507
|
}
|
|
4373
|
-
|
|
4374
|
-
${
|
|
4508
|
+
log10(`
|
|
4509
|
+
${t35("cli.doctor.modules_head")}`);
|
|
4375
4510
|
const order = { failed: 0, warn: 1, skipped: 2, ok: 3 };
|
|
4376
4511
|
const sorted = [...rt.diagnosticList].sort(
|
|
4377
4512
|
(a, b) => (order[a.status] ?? 9) - (order[b.status] ?? 9)
|
|
4378
4513
|
);
|
|
4379
|
-
for (const d of sorted)
|
|
4514
|
+
for (const d of sorted) log10(` ${STATUS_MARK[d.status]} ${d.module}: ${d.detail}`);
|
|
4380
4515
|
await rt.dispose();
|
|
4381
4516
|
if (hasFailure(rt.diagnosticList)) {
|
|
4382
4517
|
const bad = rt.diagnosticList.filter((d) => d.status === "failed").length;
|
|
4383
|
-
|
|
4384
|
-
${
|
|
4518
|
+
log10(`
|
|
4519
|
+
${t35("cli.doctor.modules_failed", { count: bad })}`);
|
|
4385
4520
|
process.exitCode = EXIT_CODES.FAILURE;
|
|
4386
4521
|
}
|
|
4387
4522
|
} catch (err2) {
|
|
4388
|
-
|
|
4523
|
+
log10(
|
|
4389
4524
|
`
|
|
4390
|
-
${
|
|
4525
|
+
${t35("cli.doctor.build_failed", {
|
|
4391
4526
|
message: err2 instanceof Error ? err2.message : String(err2)
|
|
4392
4527
|
})}`
|
|
4393
4528
|
);
|
|
@@ -4397,21 +4532,21 @@ ${t34("cli.doctor.build_failed", {
|
|
|
4397
4532
|
// src/commands/trust.ts
|
|
4398
4533
|
import { existsSync as existsSync8, realpathSync as realpathSync3 } from "fs";
|
|
4399
4534
|
import { resolve as resolve3 } from "path";
|
|
4400
|
-
import { loadConfig as
|
|
4401
|
-
import { t as
|
|
4402
|
-
var
|
|
4535
|
+
import { loadConfig as loadConfig5, TrustManager as TrustManager3 } from "@epoch-agent/core";
|
|
4536
|
+
import { t as t36, trustPath as trustPath3, uiDateLocale as uiDateLocale4 } from "@epoch-agent/infra";
|
|
4537
|
+
var log11 = (msg) => process.stdout.write(msg + "\n");
|
|
4403
4538
|
var warn3 = (msg) => process.stderr.write(msg + "\n");
|
|
4404
4539
|
function scopeLabel(scope) {
|
|
4405
|
-
return scope === "directory-tree" ?
|
|
4540
|
+
return scope === "directory-tree" ? t36("trust.scope_tree") : t36("trust.scope_dir");
|
|
4406
4541
|
}
|
|
4407
4542
|
function levelLabel(level) {
|
|
4408
4543
|
switch (level) {
|
|
4409
4544
|
case "trusted":
|
|
4410
|
-
return
|
|
4545
|
+
return t36("trust.level_trusted");
|
|
4411
4546
|
case "untrusted":
|
|
4412
|
-
return
|
|
4547
|
+
return t36("trust.level_untrusted");
|
|
4413
4548
|
case "unknown":
|
|
4414
|
-
return
|
|
4549
|
+
return t36("trust.level_unknown");
|
|
4415
4550
|
}
|
|
4416
4551
|
}
|
|
4417
4552
|
function physicalPath(input2) {
|
|
@@ -4424,63 +4559,63 @@ function physicalPath(input2) {
|
|
|
4424
4559
|
}
|
|
4425
4560
|
}
|
|
4426
4561
|
function registerTrustCommand(program2) {
|
|
4427
|
-
const cmd = program2.command("trust").description(
|
|
4428
|
-
cmd.command("list", { isDefault: true }).description(
|
|
4429
|
-
cmd.command("add").description(
|
|
4562
|
+
const cmd = program2.command("trust").description(t36("trust.cmd_root"));
|
|
4563
|
+
cmd.command("list", { isDefault: true }).description(t36("trust.cmd_list")).action(() => withTrust((tm) => printList3(tm)));
|
|
4564
|
+
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
4565
|
withTrust((tm) => {
|
|
4431
4566
|
const dir = physicalPath(path ?? process.cwd());
|
|
4432
4567
|
tm.record(dir, "trusted", opts.tree ? "directory-tree" : "directory");
|
|
4433
|
-
|
|
4434
|
-
`\u2713 ${
|
|
4568
|
+
log11(
|
|
4569
|
+
`\u2713 ${t36("trust.added", {
|
|
4435
4570
|
dir,
|
|
4436
4571
|
scope: scopeLabel(opts.tree ? "directory-tree" : "directory")
|
|
4437
4572
|
})}`
|
|
4438
4573
|
);
|
|
4439
4574
|
});
|
|
4440
4575
|
});
|
|
4441
|
-
cmd.command("deny").description(
|
|
4576
|
+
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
4577
|
withTrust((tm) => {
|
|
4443
4578
|
const dir = physicalPath(path ?? process.cwd());
|
|
4444
4579
|
tm.record(dir, "untrusted", opts.tree ? "directory-tree" : "directory");
|
|
4445
|
-
|
|
4446
|
-
`\u2717 ${
|
|
4580
|
+
log11(
|
|
4581
|
+
`\u2717 ${t36("trust.denied", {
|
|
4447
4582
|
dir,
|
|
4448
4583
|
scope: scopeLabel(opts.tree ? "directory-tree" : "directory")
|
|
4449
4584
|
})}`
|
|
4450
4585
|
);
|
|
4451
4586
|
});
|
|
4452
4587
|
});
|
|
4453
|
-
cmd.command("remove").alias("rm").description(
|
|
4588
|
+
cmd.command("remove").alias("rm").description(t36("trust.cmd_remove")).argument("<path>", t36("trust.arg_path")).action((path) => {
|
|
4454
4589
|
withTrust((tm) => {
|
|
4455
4590
|
const dir = physicalPath(path);
|
|
4456
4591
|
if (!tm.list().some((r) => resolve3(r.path) === dir)) {
|
|
4457
|
-
warn3(
|
|
4592
|
+
warn3(t36("trust.no_record", { dir }));
|
|
4458
4593
|
process.exit(1);
|
|
4459
4594
|
}
|
|
4460
4595
|
tm.revoke(dir);
|
|
4461
|
-
|
|
4596
|
+
log11(t36("trust.removed", { dir, level: levelLabel(tm.check(dir)) }));
|
|
4462
4597
|
});
|
|
4463
4598
|
});
|
|
4464
4599
|
}
|
|
4465
4600
|
function printList3(tm) {
|
|
4466
|
-
if (
|
|
4467
|
-
warn3(
|
|
4601
|
+
if (loadConfig5().trust?.enabled === false) {
|
|
4602
|
+
warn3(t36("trust.gate_off"));
|
|
4468
4603
|
}
|
|
4469
|
-
|
|
4604
|
+
log11(t36("trust.list_head", { path: trustPath3(EPOCH_HOME) }));
|
|
4470
4605
|
const records = [...tm.list()].sort((a, b) => a.path.localeCompare(b.path));
|
|
4471
4606
|
if (records.length === 0) {
|
|
4472
|
-
|
|
4607
|
+
log11(t36("trust.list_empty"));
|
|
4473
4608
|
}
|
|
4474
4609
|
for (const r of records) {
|
|
4475
4610
|
const mark = r.level === "trusted" ? "\u2713" : "\u2717";
|
|
4476
|
-
const when = r.decidedAt ? new Date(r.decidedAt).toLocaleString(uiDateLocale4()) :
|
|
4477
|
-
|
|
4611
|
+
const when = r.decidedAt ? new Date(r.decidedAt).toLocaleString(uiDateLocale4()) : t36("trust.when_unknown");
|
|
4612
|
+
log11(` ${mark} ${r.path} [${scopeLabel(r.scope)}] ${when}`);
|
|
4478
4613
|
}
|
|
4479
4614
|
const cwd = process.cwd();
|
|
4480
|
-
|
|
4615
|
+
log11(
|
|
4481
4616
|
`
|
|
4482
|
-
${
|
|
4483
|
-
${
|
|
4617
|
+
${t36("trust.cwd_line", { cwd })}
|
|
4618
|
+
${t36("trust.cwd_verdict", { level: levelLabel(tm.check(cwd)) })}`
|
|
4484
4619
|
);
|
|
4485
4620
|
}
|
|
4486
4621
|
function withTrust(fn) {
|
|
@@ -4489,46 +4624,46 @@ function withTrust(fn) {
|
|
|
4489
4624
|
try {
|
|
4490
4625
|
fn(tm);
|
|
4491
4626
|
} catch (err2) {
|
|
4492
|
-
warn3(
|
|
4627
|
+
warn3(t36("trust.error", { message: err2 instanceof Error ? err2.message : String(err2) }));
|
|
4493
4628
|
process.exit(1);
|
|
4494
4629
|
}
|
|
4495
4630
|
}
|
|
4496
4631
|
// src/commands/upgrade.ts
|
|
4497
|
-
import { t as
|
|
4498
|
-
var
|
|
4632
|
+
import { t as t37 } from "@epoch-agent/infra";
|
|
4633
|
+
var log12 = (msg) => process.stdout.write(`${msg}
|
|
4499
4634
|
`);
|
|
4500
4635
|
function registerUpgradeCommand(program2) {
|
|
4501
|
-
program2.command("upgrade").description(
|
|
4636
|
+
program2.command("upgrade").description(t37("upgrade.cmd_root")).action(async () => {
|
|
4502
4637
|
const self = selfPackage();
|
|
4503
4638
|
const info = getInstallationInfo(self.name);
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4639
|
+
log12(t37("upgrade.current", { version: self.version }));
|
|
4640
|
+
log12(
|
|
4641
|
+
t37("upgrade.installed_via", {
|
|
4507
4642
|
manager: info.packageManager,
|
|
4508
|
-
global: info.isGlobal ?
|
|
4643
|
+
global: info.isGlobal ? t37("installation.global_suffix") : "",
|
|
4509
4644
|
note: info.note
|
|
4510
4645
|
})
|
|
4511
4646
|
);
|
|
4512
4647
|
const notice = isUpdateCheckDisabled() ? readUpdateNotice() : await refreshUpdateCache({ ttlMs: 0 }).catch(() => null);
|
|
4513
4648
|
if (notice) {
|
|
4514
|
-
|
|
4515
|
-
${
|
|
4516
|
-
} else
|
|
4517
|
-
${
|
|
4649
|
+
log12(`
|
|
4650
|
+
${t37("upgrade.available", { current: notice.current, latest: notice.latest })}`);
|
|
4651
|
+
} else log12(`
|
|
4652
|
+
${t37("upgrade.none")}`);
|
|
4518
4653
|
if (info.updateCommand) {
|
|
4519
|
-
|
|
4520
|
-
${
|
|
4654
|
+
log12(`
|
|
4655
|
+
${t37("upgrade.how")}
|
|
4521
4656
|
${info.updateCommand}`);
|
|
4522
|
-
|
|
4523
|
-
${
|
|
4657
|
+
log12(`
|
|
4658
|
+
${t37("upgrade.why_manual")}`);
|
|
4524
4659
|
}
|
|
4525
4660
|
});
|
|
4526
|
-
program2.command(UPDATE_CHECK_SUBCOMMAND, { hidden: true }).description(
|
|
4661
|
+
program2.command(UPDATE_CHECK_SUBCOMMAND, { hidden: true }).description(t37("upgrade.cmd_refresh")).action(async () => {
|
|
4527
4662
|
await refreshUpdateCache().catch(() => null);
|
|
4528
4663
|
});
|
|
4529
4664
|
}
|
|
4530
4665
|
// src/commands/web.ts
|
|
4531
|
-
import { t as
|
|
4666
|
+
import { t as t38 } from "@epoch-agent/infra";
|
|
4532
4667
|
import { buildRuntime as buildRuntime3 } from "@epoch-agent/runtime";
|
|
4533
4668
|
import {
|
|
4534
4669
|
createWebServer,
|
|
@@ -4575,7 +4710,7 @@ var err = (msg) => {
|
|
|
4575
4710
|
`);
|
|
4576
4711
|
};
|
|
4577
4712
|
function registerWebCommand(program2) {
|
|
4578
|
-
program2.command("web").description(
|
|
4713
|
+
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
4714
|
await runWeb({ ...opts, json: jsonRequested(opts.json, command.parent?.opts()["json"]) });
|
|
4580
4715
|
});
|
|
4581
4716
|
}
|
|
@@ -4584,7 +4719,7 @@ function jsonRequested(own, fromProgram) {
|
|
|
4584
4719
|
}
|
|
4585
4720
|
async function runWeb(opts) {
|
|
4586
4721
|
const port = parsePort2(opts.port);
|
|
4587
|
-
if (port === null) return fail(
|
|
4722
|
+
if (port === null) return fail(t38("cli.web.err_port", { value: String(opts.port) }));
|
|
4588
4723
|
const binding = decideBinding({
|
|
4589
4724
|
...opts.host === void 0 ? {} : { host: opts.host },
|
|
4590
4725
|
...port === void 0 ? {} : { port },
|
|
@@ -4611,8 +4746,8 @@ async function runWeb(opts) {
|
|
|
4611
4746
|
if (opts.open && !openInBrowser(server.url)) say(browserFailed());
|
|
4612
4747
|
installShutdown(server, say);
|
|
4613
4748
|
}
|
|
4614
|
-
var missingWebRoot = () =>
|
|
4615
|
-
var browserFailed = () => ` ${
|
|
4749
|
+
var missingWebRoot = () => t38("cli.web.missing_root");
|
|
4750
|
+
var browserFailed = () => ` ${t38("cli.web.browser_failed")}`;
|
|
4616
4751
|
function pluginsOption(plugins) {
|
|
4617
4752
|
return plugins ? { hostMarketplaces: { sources: [] } } : {};
|
|
4618
4753
|
}
|
|
@@ -4624,7 +4759,7 @@ async function boot(binding, plugins) {
|
|
|
4624
4759
|
});
|
|
4625
4760
|
if (!runtime.session) {
|
|
4626
4761
|
await runtime.dispose();
|
|
4627
|
-
fail(`${
|
|
4762
|
+
fail(`${t38("cli.web.no_provider")}
|
|
4628
4763
|
${runtime.diagnostics.join("\n")}`);
|
|
4629
4764
|
return null;
|
|
4630
4765
|
}
|
|
@@ -4646,11 +4781,11 @@ function announceLines(input2) {
|
|
|
4646
4781
|
const human = [];
|
|
4647
4782
|
if (!input2.json) {
|
|
4648
4783
|
human.push(` Epoch Web http://${input2.host}:${input2.port}`);
|
|
4649
|
-
human.push(` ${
|
|
4784
|
+
human.push(` ${t38("cli.web.open_this")}
|
|
4650
4785
|
${input2.url}`);
|
|
4651
4786
|
}
|
|
4652
4787
|
if (input2.lanExposed) {
|
|
4653
|
-
human.push(` ${
|
|
4788
|
+
human.push(` ${t38("cli.web.lan_exposed")}`);
|
|
4654
4789
|
}
|
|
4655
4790
|
if (input2.placeholder) human.push(` \u26A0 ${missingWebRoot()}`);
|
|
4656
4791
|
if (!input2.json) return { human };
|
|
@@ -4668,7 +4803,7 @@ function installShutdown(server, say) {
|
|
|
4668
4803
|
if (shuttingDown) process.exit(1);
|
|
4669
4804
|
shuttingDown = true;
|
|
4670
4805
|
say(`
|
|
4671
|
-
${
|
|
4806
|
+
${t38("cli.web.shutting_down")}`);
|
|
4672
4807
|
server.close().then(
|
|
4673
4808
|
() => process.exit(0),
|
|
4674
4809
|
() => process.exit(1)
|
|
@@ -4687,18 +4822,18 @@ function fail(message) {
|
|
|
4687
4822
|
process.exit(1);
|
|
4688
4823
|
}
|
|
4689
4824
|
// src/language.ts
|
|
4690
|
-
import { loadConfig as
|
|
4825
|
+
import { loadConfig as loadConfig6 } from "@epoch-agent/core";
|
|
4691
4826
|
import { resolveLang, setLang } from "@epoch-agent/infra";
|
|
4692
4827
|
function applyConfiguredLanguage() {
|
|
4693
4828
|
try {
|
|
4694
|
-
setLang(resolveLang(
|
|
4829
|
+
setLang(resolveLang(loadConfig6().display?.language));
|
|
4695
4830
|
} catch {
|
|
4696
4831
|
}
|
|
4697
4832
|
}
|
|
4698
4833
|
// src/index.ts
|
|
4699
4834
|
applyConfiguredLanguage();
|
|
4700
4835
|
var program = new Command();
|
|
4701
|
-
program.name("epoch").description(
|
|
4836
|
+
program.name("epoch").description(t39("cli.program_description"));
|
|
4702
4837
|
registerConfigCommand(program);
|
|
4703
4838
|
registerMcpCommand(program);
|
|
4704
4839
|
registerModelCommand(program);
|
|
@@ -4707,6 +4842,7 @@ registerSessionsCommand(program);
|
|
|
4707
4842
|
registerAgentsCommand(program);
|
|
4708
4843
|
registerPluginCommand(program);
|
|
4709
4844
|
registerTrustCommand(program);
|
|
4845
|
+
registerComplianceCommand(program);
|
|
4710
4846
|
registerScheduleCommand(program);
|
|
4711
4847
|
registerUpgradeCommand(program);
|
|
4712
4848
|
registerWebCommand(program);
|