mtech-ai-local-runner 0.1.107 → 0.1.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.1.109: Codex runs the reviewed GPT model the employee picks
2
+
3
+ The reviewed Codex releases now allow `gpt-6.1-sol`, `gpt-6.1-terra`, `gpt-5.6-sol`, `gpt-5.6-terra` and `gpt-5.6-luna`
4
+ (Work Decision c85f8c16), so the model chosen in the Sentry modal's Local Codex Runner picker can run. Nothing else
5
+ changes from 0.1.108.
6
+
7
+ ## 0.1.108: the menu bar app reconnects to a new runner
8
+
9
+ After the daemon opens the menu bar app it waits up to 10 s for it to connect; if it does not (an app already
10
+ running keeps the socket it was first given), the daemon quits it and opens it once more. Nothing else changes from
11
+ 0.1.107.
12
+
1
13
  ## 0.1.107: the AI Gateway menu bar app
2
14
 
3
15
  `setup` installs `~/Applications/AI Gateway.app` and the daemon opens it (Gateway Work Item b9218937). A bell in the
@@ -21,6 +21,7 @@ export class MenubarChannel {
21
21
  state;
22
22
  waiting = new Map();
23
23
  listeners = { refresh: [], quit: [] };
24
+ appWaiters = new Set();
24
25
  constructor(options) {
25
26
  this.options = options;
26
27
  this.state = { ...options.initial };
@@ -31,6 +32,22 @@ export class MenubarChannel {
31
32
  on(event, listener) {
32
33
  this.listeners[event].push(listener);
33
34
  }
35
+ /** True once an app is connected, at once when one already is; false when none connects within timeoutMs. */
36
+ waitForApp(timeoutMs) {
37
+ if (this.connected)
38
+ return Promise.resolve(true);
39
+ return new Promise(resolve => {
40
+ const settle = (connected) => {
41
+ clearTimeout(timer);
42
+ this.appWaiters.delete(onConnect);
43
+ resolve(connected);
44
+ };
45
+ const onConnect = () => settle(true);
46
+ const timer = setTimeout(() => settle(this.connected), timeoutMs);
47
+ timer.unref?.();
48
+ this.appWaiters.add(onConnect);
49
+ });
50
+ }
34
51
  async start() {
35
52
  if (this.server !== null)
36
53
  throw new Error("MENUBAR_CHANNEL_ALREADY_LISTENING");
@@ -128,6 +145,8 @@ export class MenubarChannel {
128
145
  socket.once("close", () => { if (this.app === socket)
129
146
  this.app = null; });
130
147
  socket.write(stateLine(this.state));
148
+ for (const waiter of [...this.appWaiters])
149
+ waiter();
131
150
  }
132
151
  handle(message) {
133
152
  if (message === null)
@@ -1,5 +1,5 @@
1
1
  import { stat } from "node:fs/promises";
2
- import { installedAppPath, menubarRemovedByPerson, runFile } from "./install.js";
2
+ import { MENUBAR_EXECUTABLE, installedAppPath, menubarRemovedByPerson, runFile } from "./install.js";
3
3
  /**
4
4
  * Opens the installed app in the background (Gateway Work Item b9218937), unless the person removed it. An app that
5
5
  * already runs stays as it is. Never throws.
@@ -20,3 +20,24 @@ export async function launchMenubarApp(input) {
20
20
  return "skipped";
21
21
  }
22
22
  }
23
+ /**
24
+ * Quits a running app and waits, up to 3 s, until it is gone, so a following `open` starts a new instance that
25
+ * receives its arguments instead of bringing the old one forward. Never throws.
26
+ */
27
+ export async function quitMenubarApp(input = {}) {
28
+ if ((input.platform ?? process.platform) !== "darwin")
29
+ return;
30
+ const run = input.run ?? runFile;
31
+ try {
32
+ await run("/usr/bin/pkill", ["-x", MENUBAR_EXECUTABLE]);
33
+ for (let i = 0; i < 15; i++) {
34
+ // pgrep exits 1 once no process has the name.
35
+ if ((await run("/usr/bin/pgrep", ["-x", MENUBAR_EXECUTABLE])).code !== 0)
36
+ return;
37
+ await new Promise(resolve => setTimeout(resolve, 200));
38
+ }
39
+ }
40
+ catch {
41
+ /* Quitting is best effort; the launch that follows still runs. */
42
+ }
43
+ }
@@ -1,7 +1,9 @@
1
1
  import { packageVersion } from "../package-version.js";
2
2
  import { MenubarChannel, menubarSocketPath } from "./channel.js";
3
- import { launchMenubarApp } from "./launcher.js";
3
+ import { launchMenubarApp, quitMenubarApp } from "./launcher.js";
4
4
  import { MenubarRuntime } from "./runtime.js";
5
+ /** How long the daemon waits for the app it opened to connect before it reopens it once. */
6
+ export const APP_CONNECT_TIMEOUT_MS = 10_000;
5
7
  /**
6
8
  * The menu bar parts of one daemon (Gateway Work Item b9218937). A channel that cannot start is logged and left off:
7
9
  * the daemon and its osascript notices run as before.
@@ -13,6 +15,30 @@ export function buildMenubar(input) {
13
15
  const runtime = new MenubarRuntime({ channel, gatewayMcpUrl: input.gatewayMcpUrl, runnerVersion: packageVersion(),
14
16
  readInbox: input.readInbox, rescanRooms: input.rescanRooms, log: input.log });
15
17
  channel.on("quit", () => input.log({ event: "menubar_app_quit" }));
18
+ const launch = input.launch ?? (() => launchMenubarApp({ home: input.home, stateRoot: input.stateRoot, socketPath }));
19
+ const quit = input.quit ?? (() => quitMenubarApp());
20
+ const timeoutMs = input.connectTimeoutMs ?? APP_CONNECT_TIMEOUT_MS;
21
+ let closed = false;
22
+ // An app that already runs keeps the socket it was first given, and `open` passes no new arguments to it. One that
23
+ // was opened but does not connect is therefore quit and opened again, once.
24
+ const ensureApp = async () => {
25
+ const status = await launch();
26
+ if (status !== "launched") {
27
+ input.log({ event: "menubar_app", status });
28
+ return;
29
+ }
30
+ if (await channel.waitForApp(timeoutMs)) {
31
+ input.log({ event: "menubar_app", status, connected: true });
32
+ return;
33
+ }
34
+ if (closed)
35
+ return;
36
+ await quit();
37
+ if (closed)
38
+ return;
39
+ const again = await launch();
40
+ input.log({ event: "menubar_app_relaunched", status: again, connected: again === "launched" && await channel.waitForApp(timeoutMs) });
41
+ };
16
42
  return {
17
43
  runtime,
18
44
  channel,
@@ -25,9 +51,11 @@ export function buildMenubar(input) {
25
51
  return;
26
52
  }
27
53
  runtime.start();
28
- input.log({ event: "menubar_app", status: await launchMenubarApp({ home: input.home, stateRoot: input.stateRoot, socketPath }) });
54
+ // In the background: the daemon's loops never wait for the app.
55
+ void ensureApp().catch(() => input.log({ event: "menubar_app_failed" }));
29
56
  },
30
57
  close: async () => {
58
+ closed = true;
31
59
  runtime.stop();
32
60
  await channel.stop();
33
61
  },
@@ -10,9 +10,13 @@ export const CODEX_DAEMON_EXEC_RELEASES = Object.freeze([{
10
10
  "executableVersion": "codex-cli 0.163.0-alpha.11",
11
11
  "binarySha256": "80df47f6ca9d316c732014bc5f6e6514696fafb5593e08e064332a1d3718b466",
12
12
  "modelIds": [
13
- "gpt-5.6-sol"
13
+ "gpt-6.1-sol",
14
+ "gpt-6.1-terra",
15
+ "gpt-5.6-sol",
16
+ "gpt-5.6-terra",
17
+ "gpt-5.6-luna"
14
18
  ],
15
- "decisionId": "21d710f0-d8f2-4a5f-b8c5-f7412e2a2100",
19
+ "decisionId": "c85f8c16-c1ad-42e7-880a-e5d49ec6f770",
16
20
  "acceptedResidualRisks": [
17
21
  "vendor_native_policy_enforcement",
18
22
  "managed_policy_not_directly_observed_per_job",
@@ -9,9 +9,13 @@ export const REVIEWED_MANUAL_CODEX_RELEASE = {
9
9
  "executableVersion": "codex-cli 0.163.0-alpha.11",
10
10
  "binarySha256": "80df47f6ca9d316c732014bc5f6e6514696fafb5593e08e064332a1d3718b466",
11
11
  "modelIds": [
12
- "gpt-5.6-sol"
12
+ "gpt-6.1-sol",
13
+ "gpt-6.1-terra",
14
+ "gpt-5.6-sol",
15
+ "gpt-5.6-terra",
16
+ "gpt-5.6-luna"
13
17
  ],
14
- "decisionId": "21d710f0-d8f2-4a5f-b8c5-f7412e2a2100",
18
+ "decisionId": "c85f8c16-c1ad-42e7-880a-e5d49ec6f770",
15
19
  "acceptedResidualRisks": [
16
20
  "vendor_native_policy_enforcement",
17
21
  "managed_policy_not_directly_observed_per_job",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mtech-ai-local-runner",
3
- "version": "0.1.107",
3
+ "version": "0.1.109",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "bin": {