mtech-ai-local-runner 0.1.30 → 0.1.32

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.
Files changed (49) hide show
  1. package/README.md +64 -1
  2. package/dist/src/adapters/claude-code/adapter.js +76 -17
  3. package/dist/src/adapters/codex/adapter.js +146 -30
  4. package/dist/src/adapters/codex/exec-launch-host.js +50 -11
  5. package/dist/src/adapters/codex/stream-parser.js +8 -17
  6. package/dist/src/adapters/completion-report.js +8 -5
  7. package/dist/src/cli.js +23 -0
  8. package/dist/src/config.js +4 -4
  9. package/dist/src/daemon/daemon.js +62 -4
  10. package/dist/src/daemon/execution-coordinator.js +351 -77
  11. package/dist/src/daemon/job-poller.js +4 -1
  12. package/dist/src/daemon/manual-codex.js +10 -3
  13. package/dist/src/daemon/preparation-lease.js +142 -0
  14. package/dist/src/daemon/runtime.js +65 -23
  15. package/dist/src/daemon/terminal-recovery.js +29 -5
  16. package/dist/src/gateway-client.js +120 -22
  17. package/dist/src/platform/macos/launchd.js +13 -2
  18. package/dist/src/process/node-process-group-host.js +89 -34
  19. package/dist/src/process/owned-process-guardian.js +140 -0
  20. package/dist/src/publication/candidate.js +95 -0
  21. package/dist/src/publication/execution-host.js +55 -0
  22. package/dist/src/publication/git-credential-broker.js +150 -0
  23. package/dist/src/publication/git-credential-helper.js +29 -0
  24. package/dist/src/publication/git-snapshot.js +321 -0
  25. package/dist/src/publication/git-transport.js +156 -0
  26. package/dist/src/publication/installed-host.js +120 -0
  27. package/dist/src/publication/isolated-verifier.js +67 -0
  28. package/dist/src/publication/journal.js +87 -0
  29. package/dist/src/publication/production-host.js +155 -0
  30. package/dist/src/publication/project-registration.js +49 -0
  31. package/dist/src/publication/publisher.js +166 -0
  32. package/dist/src/publication/recovery.js +53 -0
  33. package/dist/src/publication/runtime-release.js +91 -0
  34. package/dist/src/publication/server-ci-error.js +15 -0
  35. package/dist/src/publication/server-ci-host.js +160 -0
  36. package/dist/src/runner.js +33 -4
  37. package/dist/src/safety/codex-daemon-host.js +2 -2
  38. package/dist/src/safety/command-policy-core.js +124 -0
  39. package/dist/src/safety/command-policy.js +1 -124
  40. package/dist/src/safety/execution-failure.js +4 -0
  41. package/dist/src/safety/job-tool-policy.js +9 -0
  42. package/dist/src/safety/local-tool-policy.js +1 -1
  43. package/dist/src/safety/terminal-evidence.js +67 -16
  44. package/dist/src/safety/unsupported-supervisor-command.js +12 -0
  45. package/dist/src/state-store.js +50 -7
  46. package/dist/src/workspace/workspace-manager.js +14 -0
  47. package/package.json +3 -2
  48. package/verification/README.md +63 -0
  49. package/verification/runner-policy.cjs +24 -0
@@ -6,11 +6,11 @@ export function isExecutionConfig(config) {
6
6
  return config.mode === "shadow" || config.mode === "once";
7
7
  }
8
8
  export function isServiceConfig(config) {
9
- return ["setup", "doctor", "run", "start", "stop", "status", "uninstall", "manual-codex"].includes(config.mode);
9
+ return ["setup", "doctor", "run", "start", "stop", "status", "uninstall", "manual-codex", "publication-recover"].includes(config.mode);
10
10
  }
11
11
  const EXECUTION_MODES = ["shadow", "once"];
12
12
  const AUTH_MODES = ["login", "logout"];
13
- const SERVICE_MODES = ["setup", "doctor", "run", "start", "stop", "status", "uninstall", "manual-codex"];
13
+ const SERVICE_MODES = ["setup", "doctor", "run", "start", "stop", "status", "uninstall", "manual-codex", "publication-recover"];
14
14
  const ALL_MODES = [...EXECUTION_MODES, ...AUTH_MODES, ...SERVICE_MODES];
15
15
  export const DEFAULT_GATEWAY_MCP_URL = "https://cloud.dev.service.ai-gateway.laravelvapor.org/api/v1/web/ai-gateway/mcp";
16
16
  const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -101,9 +101,9 @@ export function loadConfig(argv, env) {
101
101
  const stateRoot = defaultStateRootFromEnv(env);
102
102
  if (SERVICE_MODES.includes(mode)) {
103
103
  let manualJobId;
104
- if (mode === 'manual-codex') {
104
+ if (mode === 'manual-codex' || mode === 'publication-recover') {
105
105
  if (rest.length !== 1 || !rest[0].startsWith('--job-id=') || !UUID_PATTERN.test(rest[0].slice(9)))
106
- throw new Error('manual-codex requires exactly --job-id=<uuid>');
106
+ throw new Error(`${mode} requires exactly --job-id=<uuid>`);
107
107
  manualJobId = rest[0].slice(9).toLowerCase();
108
108
  }
109
109
  return {
@@ -1,15 +1,73 @@
1
1
  export class Daemon {
2
2
  deps;
3
+ stopping = new AbortController();
3
4
  constructor(deps) {
4
5
  this.deps = deps;
5
6
  }
7
+ stop() { this.deps.poller.stop?.(); this.stopping.abort(); }
8
+ async suspend(state, duringExecution = false) {
9
+ const phase = state && typeof state === 'object' && 'phase' in state ? state.phase : undefined;
10
+ const reason = duringExecution ? 'execution_outcome_unknown' : phase === 'binding' || phase === 'binding_uncertain' ? 'binding_outcome_unknown' : 'runner_restarted';
11
+ try {
12
+ await this.deps.state.suspend?.(reason);
13
+ }
14
+ catch { /* Preserve original state and remain alive even if the marker cannot be written. */ }
15
+ try {
16
+ this.deps.reportRecoveryRequired?.(reason);
17
+ }
18
+ catch { /* Logging cannot reopen claims or exit suspended recovery. */ }
19
+ if (this.stopping.signal.aborted)
20
+ return;
21
+ if (this.deps.wait) {
22
+ await this.deps.wait(this.stopping.signal);
23
+ return;
24
+ }
25
+ // A pending promise does not keep Node alive. This timer deliberately stays
26
+ // referenced, holds the process lock and wakes only for explicit close.
27
+ await new Promise(resolve => {
28
+ const timer = setInterval(() => { }, 60_000);
29
+ this.stopping.signal.addEventListener('abort', () => { clearInterval(timer); resolve(); }, { once: true });
30
+ });
31
+ }
6
32
  async run() {
7
33
  await this.deps.lock.acquire();
8
34
  try {
9
- const state = await this.deps.state.load();
10
- if (state)
11
- await this.deps.reconcile(state);
12
- await this.deps.poller.run();
35
+ let state;
36
+ try {
37
+ state = await this.deps.state.load();
38
+ if (state !== null && state !== undefined) {
39
+ await this.deps.reconcile(state);
40
+ state = await this.deps.state.load();
41
+ }
42
+ }
43
+ catch {
44
+ await this.suspend(state);
45
+ return;
46
+ }
47
+ if (state !== null && state !== undefined) {
48
+ await this.suspend(state);
49
+ return;
50
+ }
51
+ if (this.stopping.signal.aborted)
52
+ return;
53
+ try {
54
+ await this.deps.poller.run();
55
+ }
56
+ catch {
57
+ try {
58
+ this.deps.poller.stop?.();
59
+ }
60
+ catch { /* Still inspect retained state and suspend below. */ }
61
+ }
62
+ try {
63
+ state = await this.deps.state.load();
64
+ }
65
+ catch {
66
+ await this.suspend(undefined, true);
67
+ return;
68
+ }
69
+ if (state !== null && state !== undefined)
70
+ await this.suspend(state, true);
13
71
  }
14
72
  finally {
15
73
  await this.deps.lock.release();