oh-my-codex 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +5 -5
- package/Cargo.toml +1 -1
- package/crates/omx-explore/src/main.rs +115 -9
- package/dist/cli/__tests__/ask.test.js +3 -3
- package/dist/cli/__tests__/ask.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-team.test.js +12 -2
- package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +4 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/hooks.test.d.ts +2 -0
- package/dist/cli/__tests__/hooks.test.d.ts.map +1 -0
- package/dist/cli/__tests__/hooks.test.js +55 -0
- package/dist/cli/__tests__/hooks.test.js.map +1 -0
- package/dist/cli/__tests__/index.test.js +6 -2
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +3 -10
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-packaging.test.js +1 -1
- package/dist/cli/__tests__/sparkshell-packaging.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +4 -4
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/ask.js +1 -1
- package/dist/cli/ask.js.map +1 -1
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +2 -2
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +26 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +5 -4
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/hooks/__tests__/codebase-map.test.js +1 -1
- package/dist/hooks/__tests__/codebase-map.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +46 -36
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +6 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +24 -24
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-linked-sync.test.js +45 -1
- package/dist/hooks/__tests__/notify-hook-linked-sync.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.js +1 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-native-dispatch-contract.test.js +5 -5
- package/dist/hooks/__tests__/notify-hook-native-dispatch-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js +22 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +126 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +5 -4
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +141 -3
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +11 -8
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js +1 -1
- package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js +1 -1
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js.map +1 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js +65 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.d.ts +2 -0
- package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.d.ts.map +1 -0
- package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.js +32 -0
- package/dist/hooks/extensibility/__tests__/sdk-public-surface.test.js.map +1 -0
- package/dist/hooks/extensibility/__tests__/sdk.test.js +172 -0
- package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
- package/dist/hooks/extensibility/sdk/logging.d.ts +6 -0
- package/dist/hooks/extensibility/sdk/logging.d.ts.map +1 -0
- package/dist/hooks/extensibility/sdk/logging.js +32 -0
- package/dist/hooks/extensibility/sdk/logging.js.map +1 -0
- package/dist/hooks/extensibility/sdk/paths.d.ts +7 -0
- package/dist/hooks/extensibility/sdk/paths.d.ts.map +1 -0
- package/dist/hooks/extensibility/sdk/paths.js +22 -0
- package/dist/hooks/extensibility/sdk/paths.js.map +1 -0
- package/dist/hooks/extensibility/sdk/plugin-state.d.ts +5 -0
- package/dist/hooks/extensibility/sdk/plugin-state.d.ts.map +1 -0
- package/dist/hooks/extensibility/sdk/plugin-state.js +66 -0
- package/dist/hooks/extensibility/sdk/plugin-state.js.map +1 -0
- package/dist/hooks/extensibility/sdk/runtime-state.d.ts +3 -0
- package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -0
- package/dist/hooks/extensibility/sdk/runtime-state.js +41 -0
- package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -0
- package/dist/hooks/extensibility/sdk/tmux.d.ts +10 -0
- package/dist/hooks/extensibility/sdk/tmux.d.ts.map +1 -0
- package/dist/hooks/extensibility/sdk/tmux.js +178 -0
- package/dist/hooks/extensibility/sdk/tmux.js.map +1 -0
- package/dist/hooks/extensibility/sdk.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk.js +14 -246
- package/dist/hooks/extensibility/sdk.js.map +1 -1
- package/dist/hooks/extensibility/types.d.ts +52 -0
- package/dist/hooks/extensibility/types.d.ts.map +1 -1
- package/dist/hud/__tests__/authority.test.js +2 -2
- package/dist/hud/__tests__/authority.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +9 -2
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server-schema.test.js +10 -0
- package/dist/mcp/__tests__/state-server-schema.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +152 -1
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +1 -0
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/modes/__tests__/base-autoresearch-contract.test.js +17 -0
- package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
- package/dist/modes/base.d.ts +2 -2
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +1 -1
- package/dist/notifications/__tests__/reply-listener.test.js +375 -8
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts +57 -0
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +85 -40
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +8 -41
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/__tests__/test-reply-listener-live.test.d.ts +2 -0
- package/dist/scripts/__tests__/test-reply-listener-live.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/test-reply-listener-live.test.js +82 -0
- package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -0
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +7 -3
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +4 -3
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +27 -8
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +0 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +123 -112
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +1 -3
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +14 -155
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/scripts/test-reply-listener-live.d.ts +24 -0
- package/dist/scripts/test-reply-listener-live.d.ts.map +1 -0
- package/dist/scripts/test-reply-listener-live.js +138 -0
- package/dist/scripts/test-reply-listener-live.js.map +1 -0
- package/dist/scripts/tmux-hook-engine.d.ts +3 -3
- package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
- package/dist/scripts/tmux-hook-engine.js +17 -16
- package/dist/scripts/tmux-hook-engine.js.map +1 -1
- package/dist/team/__tests__/hardening-e2e.test.js +11 -1
- package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
- package/dist/team/__tests__/mcp-comm.test.js +11 -1
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +11 -1
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +9 -1
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-test-fixture.d.ts +9 -3
- package/dist/team/__tests__/tmux-test-fixture.d.ts.map +1 -1
- package/dist/team/__tests__/tmux-test-fixture.js +69 -24
- package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
- package/dist/team/__tests__/tmux-test-fixture.test.js +80 -3
- package/dist/team/__tests__/tmux-test-fixture.test.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +2 -7
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +13 -0
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +15 -6
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
- package/dist/verification/__tests__/native-release-manifest.test.js +1 -1
- package/dist/verification/__tests__/native-release-manifest.test.js.map +1 -1
- package/package.json +2 -1
- package/src/scripts/__tests__/smoke-packed-install.test.ts +9 -50
- package/src/scripts/__tests__/test-reply-listener-live.test.ts +101 -0
- package/src/scripts/notify-hook/team-dispatch.ts +8 -2
- package/src/scripts/notify-hook/team-leader-nudge.ts +4 -3
- package/src/scripts/notify-hook/team-worker.ts +26 -8
- package/src/scripts/notify-hook/tmux-injection.ts +118 -107
- package/src/scripts/smoke-packed-install.ts +17 -164
- package/src/scripts/test-reply-listener-live.ts +188 -0
- package/src/scripts/tmux-hook-engine.ts +18 -16
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import { createServer } from 'node:http';
|
|
2
|
-
import type { AddressInfo } from 'node:net';
|
|
3
1
|
import {
|
|
4
|
-
cpSync,
|
|
5
2
|
existsSync,
|
|
6
3
|
lstatSync,
|
|
7
4
|
mkdtempSync,
|
|
8
|
-
readFileSync,
|
|
9
5
|
rmSync,
|
|
10
6
|
symlinkSync,
|
|
11
|
-
writeFileSync,
|
|
12
7
|
} from 'node:fs';
|
|
13
|
-
import {
|
|
8
|
+
import { mkdirSync } from 'node:fs';
|
|
14
9
|
import { tmpdir } from 'node:os';
|
|
15
10
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
16
11
|
import { spawnSync } from 'node:child_process';
|
|
@@ -23,12 +18,17 @@ const REQUIRED_NODE_MODULE_MARKERS = [
|
|
|
23
18
|
join('zod', 'package.json'),
|
|
24
19
|
];
|
|
25
20
|
|
|
21
|
+
export const PACKED_INSTALL_SMOKE_CORE_COMMANDS = [
|
|
22
|
+
['--help'],
|
|
23
|
+
['version'],
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
26
|
function usage(): string {
|
|
27
27
|
return [
|
|
28
|
-
'Usage: node scripts/smoke-packed-install.mjs
|
|
28
|
+
'Usage: node scripts/smoke-packed-install.mjs',
|
|
29
29
|
'',
|
|
30
30
|
'Creates an npm tarball, installs it into an isolated prefix, and smoke tests the installed omx CLI.',
|
|
31
|
-
'
|
|
31
|
+
'Release smoke stays intentionally minimal: install + boot + 1-2 core commands only.',
|
|
32
32
|
].join('\n');
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -151,43 +151,24 @@ export function ensureRepoDependencies(repoRoot: string, options: EnsureRepoDeps
|
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function parseArgs(argv: string[]): { releaseAssetsDir: string | undefined; requireNoFallback: boolean } {
|
|
159
|
-
let releaseAssetsDir: string | undefined;
|
|
160
|
-
let requireNoFallback = false;
|
|
161
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
162
|
-
const token = argv[index];
|
|
154
|
+
function parseArgs(argv: string[]): void {
|
|
155
|
+
for (const token of argv) {
|
|
163
156
|
if (token === '--help' || token === '-h') {
|
|
164
157
|
console.log(usage());
|
|
165
158
|
process.exit(0);
|
|
166
159
|
}
|
|
167
|
-
if (token === '--release-assets-dir') {
|
|
168
|
-
const value = argv[index + 1];
|
|
169
|
-
if (!value) throw new Error('Missing value after --release-assets-dir');
|
|
170
|
-
releaseAssetsDir = resolve(value);
|
|
171
|
-
index += 1;
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
if (token === '--require-no-fallback') {
|
|
175
|
-
requireNoFallback = true;
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
160
|
throw new Error(`Unknown argument: ${token}\n${usage()}`);
|
|
179
161
|
}
|
|
180
|
-
return { releaseAssetsDir, requireNoFallback };
|
|
181
162
|
}
|
|
182
163
|
|
|
183
|
-
function run(cmd: string, args: string[], options: Record<string, unknown> = {}): ReturnType<typeof spawnSync> {
|
|
184
|
-
const result = spawnSync(cmd, args, {
|
|
164
|
+
function run(cmd: string, args: readonly string[], options: Record<string, unknown> = {}): ReturnType<typeof spawnSync> {
|
|
165
|
+
const result = spawnSync(cmd, [...args], {
|
|
185
166
|
encoding: 'utf-8',
|
|
186
167
|
stdio: 'pipe',
|
|
187
168
|
...options,
|
|
188
169
|
});
|
|
189
170
|
if (result.status !== 0) {
|
|
190
|
-
throw new Error(formatCommandFailure(cmd, args, result));
|
|
171
|
+
throw new Error(formatCommandFailure(cmd, [...args], result));
|
|
191
172
|
}
|
|
192
173
|
return result;
|
|
193
174
|
}
|
|
@@ -196,115 +177,14 @@ function npmBinName(name: string): string {
|
|
|
196
177
|
return process.platform === 'win32' ? `${name}.cmd` : name;
|
|
197
178
|
}
|
|
198
179
|
|
|
199
|
-
function startStaticServer(root: string): Promise<{ baseUrl: string; close: () => Promise<void> }> {
|
|
200
|
-
return new Promise((resolveServer, reject) => {
|
|
201
|
-
const server = createServer((req, res) => {
|
|
202
|
-
try {
|
|
203
|
-
const url = new URL(req.url || '/', 'http://127.0.0.1');
|
|
204
|
-
const requested = resolve(root, url.pathname.replace(/^\//, ''));
|
|
205
|
-
if (!requested.startsWith(root) || !existsSync(requested)) {
|
|
206
|
-
res.writeHead(404);
|
|
207
|
-
res.end('missing');
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
res.writeHead(200);
|
|
211
|
-
res.end(readFileSync(requested));
|
|
212
|
-
} catch (error) {
|
|
213
|
-
res.writeHead(500);
|
|
214
|
-
res.end(String(error));
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
server.on('error', reject);
|
|
218
|
-
server.listen(0, '127.0.0.1', () => {
|
|
219
|
-
const address = server.address() as AddressInfo | null;
|
|
220
|
-
if (!address || typeof address === 'string') {
|
|
221
|
-
reject(new Error('Failed to bind local asset server'));
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
resolveServer({
|
|
225
|
-
baseUrl: `http://127.0.0.1:${address.port}`,
|
|
226
|
-
close: () => new Promise<void>((done, fail) => server.close((error) => error ? fail(error) : done())),
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function writeCodexStub(binDir: string): string {
|
|
233
|
-
const stubPath = join(binDir, process.platform === 'win32' ? 'codex.cmd' : 'codex');
|
|
234
|
-
if (process.platform === 'win32') {
|
|
235
|
-
writeFileSync(stubPath, [
|
|
236
|
-
'@echo off',
|
|
237
|
-
'setlocal enabledelayedexpansion',
|
|
238
|
-
'set output_path=',
|
|
239
|
-
':loop',
|
|
240
|
-
'if "%~1"=="" goto done',
|
|
241
|
-
'if "%~1"=="-o" (',
|
|
242
|
-
' set output_path=%~2',
|
|
243
|
-
' shift',
|
|
244
|
-
')',
|
|
245
|
-
'shift',
|
|
246
|
-
'goto loop',
|
|
247
|
-
':done',
|
|
248
|
-
'if "%output_path%"=="" exit /b 1',
|
|
249
|
-
'> "%output_path%" echo # Answer',
|
|
250
|
-
'>> "%output_path%" echo - packed install smoke harness',
|
|
251
|
-
'exit /b 0',
|
|
252
|
-
'',
|
|
253
|
-
].join('\r\n'));
|
|
254
|
-
} else {
|
|
255
|
-
writeFileSync(stubPath, [
|
|
256
|
-
'#!/bin/sh',
|
|
257
|
-
'set -eu',
|
|
258
|
-
"output_path=''",
|
|
259
|
-
'while [ "$#" -gt 0 ]; do',
|
|
260
|
-
' if [ "$1" = "-o" ] && [ "$#" -ge 2 ]; then',
|
|
261
|
-
' output_path="$2"',
|
|
262
|
-
' shift 2',
|
|
263
|
-
' continue',
|
|
264
|
-
' fi',
|
|
265
|
-
' shift',
|
|
266
|
-
'done',
|
|
267
|
-
'if [ -z "$output_path" ]; then',
|
|
268
|
-
" printf 'missing -o output path\\n' >&2",
|
|
269
|
-
' exit 1',
|
|
270
|
-
'fi',
|
|
271
|
-
"printf '# Answer\\n- packed install smoke harness\\n' > \"$output_path\"",
|
|
272
|
-
'',
|
|
273
|
-
].join('\n'));
|
|
274
|
-
chmodSync(stubPath, 0o755);
|
|
275
|
-
}
|
|
276
|
-
return stubPath;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
export function prepareLocalHydrationAssetDirectory(sourceDir: string, tempRoot: string): string {
|
|
280
|
-
const localDir = join(tempRoot, 'hydration-assets');
|
|
281
|
-
cpSync(sourceDir, localDir, { recursive: true });
|
|
282
|
-
return localDir;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export function rewriteManifestDownloadUrls(manifestPath: string, baseUrl: string): void {
|
|
286
|
-
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) as { assets: Array<{ archive: string; download_url: string }> };
|
|
287
|
-
manifest.assets = Array.isArray(manifest.assets)
|
|
288
|
-
? manifest.assets.map((asset) => ({
|
|
289
|
-
...asset,
|
|
290
|
-
download_url: new URL(asset.archive, `${baseUrl}/`).toString(),
|
|
291
|
-
}))
|
|
292
|
-
: [];
|
|
293
|
-
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
180
|
async function main(): Promise<void> {
|
|
297
|
-
|
|
181
|
+
parseArgs(process.argv.slice(2));
|
|
182
|
+
|
|
298
183
|
const repoRoot = process.cwd();
|
|
299
184
|
const tempRoot = mkdtempSync(join(tmpdir(), 'omx-packed-install-'));
|
|
300
185
|
const prefixDir = join(tempRoot, 'prefix');
|
|
301
|
-
const cacheDir = join(tempRoot, 'cache');
|
|
302
|
-
const helperBinDir = join(tempRoot, 'bin');
|
|
303
186
|
mkdirSync(prefixDir, { recursive: true });
|
|
304
|
-
mkdirSync(cacheDir, { recursive: true });
|
|
305
|
-
mkdirSync(helperBinDir, { recursive: true });
|
|
306
187
|
|
|
307
|
-
let server: { baseUrl: string; close: () => Promise<void> } | undefined;
|
|
308
188
|
let tarballPath: string | undefined;
|
|
309
189
|
try {
|
|
310
190
|
ensureRepoDependencies(repoRoot, {
|
|
@@ -320,40 +200,13 @@ async function main(): Promise<void> {
|
|
|
320
200
|
run('npm', ['install', '-g', tarballPath, '--prefix', prefixDir], { cwd: repoRoot });
|
|
321
201
|
|
|
322
202
|
const omxPath = join(prefixDir, process.platform === 'win32' ? '' : 'bin', npmBinName('omx'));
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (releaseAssetsDir) {
|
|
327
|
-
const hydrationAssetsDir = prepareLocalHydrationAssetDirectory(releaseAssetsDir, tempRoot);
|
|
328
|
-
server = await startStaticServer(hydrationAssetsDir);
|
|
329
|
-
rewriteManifestDownloadUrls(join(hydrationAssetsDir, 'native-release-manifest.json'), server.baseUrl);
|
|
330
|
-
const codexStub = writeCodexStub(helperBinDir);
|
|
331
|
-
const env = {
|
|
332
|
-
...process.env,
|
|
333
|
-
PATH: `${helperBinDir}${process.platform === 'win32' ? ';' : ':'}${process.env.PATH || ''}`,
|
|
334
|
-
OMX_NATIVE_MANIFEST_URL: `${server.baseUrl}/native-release-manifest.json`,
|
|
335
|
-
OMX_NATIVE_CACHE_DIR: cacheDir,
|
|
336
|
-
OMX_EXPLORE_CODEX_BIN: codexStub,
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
const sparkshell = run(omxPath, ['sparkshell', 'node', '--version'], { cwd: repoRoot, env });
|
|
340
|
-
if (requireNoFallback && hasSparkShellFallbackBanner(sparkshell.stderr as string)) {
|
|
341
|
-
throw new Error(`Unexpected sparkshell fallback stderr:\n${sparkshell.stderr}`);
|
|
342
|
-
}
|
|
343
|
-
if (!/v\d+\./.test(sparkshell.stdout as string)) {
|
|
344
|
-
throw new Error(`Unexpected sparkshell stdout:\n${sparkshell.stdout}`);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const explore = run(omxPath, ['explore', '--prompt', 'where is buildExploreRoutingGuidance defined'], { cwd: repoRoot, env });
|
|
348
|
-
if (!(explore.stdout as string).includes('# Answer') || !(explore.stdout as string).includes('packed install smoke harness')) {
|
|
349
|
-
throw new Error(`Unexpected explore stdout:\n${explore.stdout}`);
|
|
350
|
-
}
|
|
203
|
+
for (const argv of PACKED_INSTALL_SMOKE_CORE_COMMANDS) {
|
|
204
|
+
run(omxPath, argv, { cwd: repoRoot });
|
|
351
205
|
}
|
|
352
206
|
|
|
353
207
|
console.log('packed install smoke: PASS');
|
|
354
208
|
} finally {
|
|
355
209
|
if (tarballPath) rmSync(tarballPath, { force: true });
|
|
356
|
-
if (server) await server.close();
|
|
357
210
|
rmSync(tempRoot, { recursive: true, force: true });
|
|
358
211
|
}
|
|
359
212
|
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
interface ReplyListenerLiveConfig {
|
|
2
|
+
discordBotToken: string;
|
|
3
|
+
discordChannelId: string;
|
|
4
|
+
telegramBotToken: string;
|
|
5
|
+
telegramChatId: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface ReplyListenerLiveEnvResolution {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
missing: string[];
|
|
11
|
+
config: ReplyListenerLiveConfig | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ReplyListenerLiveSmokeResult {
|
|
15
|
+
discordMessageId: string;
|
|
16
|
+
telegramMessageId: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ReplyListenerLiveSmokeDeps {
|
|
20
|
+
fetchImpl?: typeof fetch;
|
|
21
|
+
log?: (message: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const LIVE_ENABLE_ENV = 'OMX_REPLY_LISTENER_LIVE';
|
|
25
|
+
const REQUIRED_ENV_KEYS = [
|
|
26
|
+
'OMX_DISCORD_NOTIFIER_BOT_TOKEN',
|
|
27
|
+
'OMX_DISCORD_NOTIFIER_CHANNEL',
|
|
28
|
+
'OMX_TELEGRAM_BOT_TOKEN',
|
|
29
|
+
'OMX_TELEGRAM_CHAT_ID',
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
function requireJsonObject(value: unknown, label: string): Record<string, unknown> {
|
|
33
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
34
|
+
throw new Error(`${label} returned a non-object JSON payload`);
|
|
35
|
+
}
|
|
36
|
+
return value as Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function parseResponseJson(response: Response, label: string): Promise<Record<string, unknown>> {
|
|
40
|
+
const body = await response.json() as unknown;
|
|
41
|
+
return requireJsonObject(body, label);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function resolveReplyListenerLiveEnv(env: NodeJS.ProcessEnv = process.env): ReplyListenerLiveEnvResolution {
|
|
45
|
+
const enabled = env[LIVE_ENABLE_ENV] === '1';
|
|
46
|
+
if (!enabled) {
|
|
47
|
+
return { enabled: false, missing: [], config: null };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const missing = REQUIRED_ENV_KEYS.filter((key) => {
|
|
51
|
+
const value = env[key];
|
|
52
|
+
return typeof value !== 'string' || value.trim().length === 0;
|
|
53
|
+
});
|
|
54
|
+
if (missing.length > 0) {
|
|
55
|
+
return { enabled: true, missing: [...missing], config: null };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
enabled: true,
|
|
60
|
+
missing: [],
|
|
61
|
+
config: {
|
|
62
|
+
discordBotToken: env.OMX_DISCORD_NOTIFIER_BOT_TOKEN!.trim(),
|
|
63
|
+
discordChannelId: env.OMX_DISCORD_NOTIFIER_CHANNEL!.trim(),
|
|
64
|
+
telegramBotToken: env.OMX_TELEGRAM_BOT_TOKEN!.trim(),
|
|
65
|
+
telegramChatId: env.OMX_TELEGRAM_CHAT_ID!.trim(),
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function runReplyListenerLiveSmoke(
|
|
71
|
+
config: ReplyListenerLiveConfig,
|
|
72
|
+
deps: ReplyListenerLiveSmokeDeps = {},
|
|
73
|
+
): Promise<ReplyListenerLiveSmokeResult> {
|
|
74
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
75
|
+
const log = deps.log ?? console.log;
|
|
76
|
+
const stamp = new Date().toISOString();
|
|
77
|
+
|
|
78
|
+
const discordSendResponse = await fetchImpl(
|
|
79
|
+
`https://discord.com/api/v10/channels/${config.discordChannelId}/messages`,
|
|
80
|
+
{
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: {
|
|
83
|
+
Authorization: `Bot ${config.discordBotToken}`,
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
content: `[omx live smoke ${stamp}] reply-listener Discord connectivity probe`,
|
|
88
|
+
}),
|
|
89
|
+
signal: AbortSignal.timeout(10_000),
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
if (!discordSendResponse.ok) {
|
|
93
|
+
throw new Error(`Discord live smoke failed: HTTP ${discordSendResponse.status}`);
|
|
94
|
+
}
|
|
95
|
+
const discordPayload = await parseResponseJson(discordSendResponse, 'Discord sendMessage');
|
|
96
|
+
const discordMessageId = typeof discordPayload.id === 'string' && discordPayload.id.trim()
|
|
97
|
+
? discordPayload.id
|
|
98
|
+
: null;
|
|
99
|
+
if (!discordMessageId) {
|
|
100
|
+
throw new Error('Discord live smoke failed: missing message id');
|
|
101
|
+
}
|
|
102
|
+
log(`Discord probe message sent: ${discordMessageId}`);
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
await fetchImpl(
|
|
106
|
+
`https://discord.com/api/v10/channels/${config.discordChannelId}/messages/${discordMessageId}`,
|
|
107
|
+
{
|
|
108
|
+
method: 'DELETE',
|
|
109
|
+
headers: { Authorization: `Bot ${config.discordBotToken}` },
|
|
110
|
+
signal: AbortSignal.timeout(10_000),
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
} catch {
|
|
114
|
+
log(`Discord probe cleanup skipped for ${discordMessageId}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const telegramSendResponse = await fetchImpl(
|
|
118
|
+
`https://api.telegram.org/bot${config.telegramBotToken}/sendMessage`,
|
|
119
|
+
{
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
|
+
body: JSON.stringify({
|
|
123
|
+
chat_id: config.telegramChatId,
|
|
124
|
+
text: `[omx live smoke ${stamp}] reply-listener Telegram connectivity probe`,
|
|
125
|
+
}),
|
|
126
|
+
signal: AbortSignal.timeout(10_000),
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
if (!telegramSendResponse.ok) {
|
|
130
|
+
throw new Error(`Telegram live smoke failed: HTTP ${telegramSendResponse.status}`);
|
|
131
|
+
}
|
|
132
|
+
const telegramPayload = await parseResponseJson(telegramSendResponse, 'Telegram sendMessage');
|
|
133
|
+
const telegramResult = requireJsonObject(telegramPayload.result, 'Telegram sendMessage.result');
|
|
134
|
+
const telegramMessageId = typeof telegramResult.message_id === 'number' || typeof telegramResult.message_id === 'string'
|
|
135
|
+
? String(telegramResult.message_id)
|
|
136
|
+
: null;
|
|
137
|
+
if (!telegramMessageId) {
|
|
138
|
+
throw new Error('Telegram live smoke failed: missing message id');
|
|
139
|
+
}
|
|
140
|
+
log(`Telegram probe message sent: ${telegramMessageId}`);
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
await fetchImpl(
|
|
144
|
+
`https://api.telegram.org/bot${config.telegramBotToken}/deleteMessage`,
|
|
145
|
+
{
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: { 'Content-Type': 'application/json' },
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
chat_id: config.telegramChatId,
|
|
150
|
+
message_id: Number(telegramMessageId),
|
|
151
|
+
}),
|
|
152
|
+
signal: AbortSignal.timeout(10_000),
|
|
153
|
+
},
|
|
154
|
+
);
|
|
155
|
+
} catch {
|
|
156
|
+
log(`Telegram probe cleanup skipped for ${telegramMessageId}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { discordMessageId, telegramMessageId };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function main(): Promise<void> {
|
|
163
|
+
const resolution = resolveReplyListenerLiveEnv();
|
|
164
|
+
if (!resolution.enabled) {
|
|
165
|
+
console.log(`reply-listener live smoke: SKIP (${LIVE_ENABLE_ENV}=1 to enable)`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (!resolution.config) {
|
|
169
|
+
console.log(`reply-listener live smoke: SKIP (missing env: ${resolution.missing.join(', ')})`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const result = await runReplyListenerLiveSmoke(resolution.config);
|
|
174
|
+
console.log('reply-listener live smoke: PASS');
|
|
175
|
+
console.log(`discord_message_id=${result.discordMessageId}`);
|
|
176
|
+
console.log(`telegram_message_id=${result.telegramMessageId}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const isMain = process.argv[1]
|
|
180
|
+
? import.meta.url === new URL(`file://${process.argv[1]}`).href
|
|
181
|
+
: false;
|
|
182
|
+
|
|
183
|
+
if (isMain) {
|
|
184
|
+
main().catch((error) => {
|
|
185
|
+
console.error(`reply-listener live smoke: FAIL\n${error instanceof Error ? error.message : String(error)}`);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
@@ -182,13 +182,17 @@ export function isPaneRunningShell(paneCurrentCommand: any): boolean {
|
|
|
182
182
|
// Codex agent commands — do NOT include 'claude' (that's Claude Code CLI, a different tool)
|
|
183
183
|
const AGENT_COMMANDS = new Set(['node', 'codex', 'npx']);
|
|
184
184
|
|
|
185
|
+
function isHudStartCommand(startCommand: string): boolean {
|
|
186
|
+
return /\bomx\b.*\bhud\b.*--watch/i.test(startCommand);
|
|
187
|
+
}
|
|
188
|
+
|
|
185
189
|
/**
|
|
186
190
|
* Canonical codex pane resolver. Finds the tmux pane running a codex/claude agent.
|
|
187
191
|
*
|
|
188
192
|
* Resolution order:
|
|
189
|
-
* 1. TMUX_PANE env var — but only if the pane
|
|
190
|
-
* 2. Scan all panes in the same tmux session for one
|
|
191
|
-
* 3.
|
|
193
|
+
* 1. TMUX_PANE env var — but only if the pane looks like a real agent pane, not HUD
|
|
194
|
+
* 2. Scan all panes in the same tmux session for one started with codex
|
|
195
|
+
* 3. Fail closed instead of guessing a shell or HUD pane
|
|
192
196
|
*
|
|
193
197
|
* All callers (auto-nudge, ralph steer, team dispatch, tmux injection) should
|
|
194
198
|
* use this instead of raw `process.env.TMUX_PANE`.
|
|
@@ -197,43 +201,42 @@ export function resolveCodexPane(): string {
|
|
|
197
201
|
const envPane = (process.env.TMUX_PANE || '').trim();
|
|
198
202
|
if (!envPane) return '';
|
|
199
203
|
|
|
200
|
-
// Check if TMUX_PANE is actually running an agent
|
|
201
204
|
try {
|
|
202
205
|
const cmd = execFileSync('tmux', ['display-message', '-t', envPane, '-p', '#{pane_current_command}'], {
|
|
203
206
|
encoding: 'utf-8', timeout: 2000,
|
|
204
207
|
}).trim().toLowerCase();
|
|
208
|
+
const startCmd = execFileSync('tmux', ['display-message', '-t', envPane, '-p', '#{pane_start_command}'], {
|
|
209
|
+
encoding: 'utf-8', timeout: 2000,
|
|
210
|
+
}).trim().toLowerCase();
|
|
205
211
|
const base = cmd.split('/').pop()?.replace(/^-/, '') || '';
|
|
206
|
-
if (AGENT_COMMANDS.has(base)) {
|
|
207
|
-
return envPane;
|
|
212
|
+
if (AGENT_COMMANDS.has(base) && !isHudStartCommand(startCmd)) {
|
|
213
|
+
return envPane;
|
|
208
214
|
}
|
|
209
215
|
if (!SHELL_COMMANDS.has(base)) {
|
|
210
|
-
// Not a shell and not a known agent (e.g.
|
|
211
|
-
//
|
|
216
|
+
// Not a shell and not a known agent (e.g. claude CLI) — fall through to
|
|
217
|
+
// session scan so we can still reject HUD or locate a codex pane.
|
|
212
218
|
}
|
|
213
219
|
} catch {
|
|
214
|
-
|
|
220
|
+
// Fall through to session scan instead of guessing.
|
|
215
221
|
}
|
|
216
222
|
|
|
217
|
-
// TMUX_PANE is a shell — find the actual agent pane in the same session
|
|
218
223
|
try {
|
|
219
|
-
const sessionName = execFileSync('tmux', ['display-message', '-t', envPane, '-p', '#
|
|
224
|
+
const sessionName = execFileSync('tmux', ['display-message', '-t', envPane, '-p', '#S'], {
|
|
220
225
|
encoding: 'utf-8', timeout: 2000,
|
|
221
226
|
}).trim();
|
|
222
|
-
if (!sessionName) return
|
|
227
|
+
if (!sessionName) return '';
|
|
223
228
|
|
|
224
229
|
const panes = execFileSync('tmux', [
|
|
225
230
|
'list-panes', '-s', '-t', sessionName,
|
|
226
231
|
'-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}',
|
|
227
232
|
], { encoding: 'utf-8', timeout: 2000 }).trim().split('\n');
|
|
228
233
|
|
|
229
|
-
// Find the pane that was STARTED with codex (not the HUD or shell)
|
|
230
234
|
for (const line of panes) {
|
|
231
235
|
const parts = line.split('\t');
|
|
232
236
|
const paneId = parts[0];
|
|
233
237
|
const startCmd = (parts[2] || '').toLowerCase();
|
|
234
238
|
if (!paneId) continue;
|
|
235
|
-
|
|
236
|
-
if (startCmd.includes('codex') && !startCmd.includes('hud')) {
|
|
239
|
+
if (startCmd.includes('codex') && !isHudStartCommand(startCmd)) {
|
|
237
240
|
return paneId;
|
|
238
241
|
}
|
|
239
242
|
}
|
|
@@ -241,7 +244,6 @@ export function resolveCodexPane(): string {
|
|
|
241
244
|
// Fall through
|
|
242
245
|
}
|
|
243
246
|
|
|
244
|
-
// No agent pane found in the session — don't fall back to a shell pane
|
|
245
247
|
return '';
|
|
246
248
|
}
|
|
247
249
|
|