remote-codex 0.11.27 → 0.11.28

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.
@@ -4,16 +4,16 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Remote Codex Supervisor</title>
7
- <script type="module" crossorigin src="/assets/index-pMPBEcL2.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-Dpf8YNxk.js"></script>
8
8
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-CgLzZcV4.js">
9
9
  <link rel="modulepreload" crossorigin href="/assets/ui-vendor-CeKGesq3.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/graph-vendor-DVPtkh3h.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/terminal-vendor-B365Go3Z.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/markdown-vendor-BQJfKm05.js">
13
- <link rel="modulepreload" crossorigin href="/assets/thread-ui-CpukI6ql.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/thread-ui-BnLdOiXc.js">
14
14
  <link rel="stylesheet" crossorigin href="/assets/graph-vendor-C5ap-Sga.css">
15
15
  <link rel="stylesheet" crossorigin href="/assets/terminal-vendor-Beg8tuEN.css">
16
- <link rel="stylesheet" crossorigin href="/assets/index-BVYGmxwf.css">
16
+ <link rel="stylesheet" crossorigin href="/assets/index-IJffwc2e.css">
17
17
  </head>
18
18
  <body class="bg-stone-950">
19
19
  <div id="root"></div>
@@ -5,7 +5,7 @@ import path from 'node:path';
5
5
  import os from 'node:os';
6
6
  import crypto from 'node:crypto';
7
7
  import readline from 'node:readline/promises';
8
- import { spawn } from 'node:child_process';
8
+ import { spawn, spawnSync } from 'node:child_process';
9
9
  import { fileURLToPath } from 'node:url';
10
10
 
11
11
  const binDir = path.dirname(fileURLToPath(import.meta.url));
@@ -19,6 +19,30 @@ const supervisorSourceEntry = path.join(packageRoot, 'apps', 'supervisor-api', '
19
19
  const relaySupervisorConfigPath = process.env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG
20
20
  ? path.resolve(process.env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG)
21
21
  : path.join(os.homedir(), '.remote-codex', 'relay-supervisor.json');
22
+ const relaySupervisorTmuxSession = process.env.REMOTE_CODEX_RELAY_SUPERVISOR_TMUX_SESSION?.trim()
23
+ || 'remote-codex-relay-supervisor';
24
+ const relaySupervisorConfigKeys = [
25
+ 'REMOTE_CODEX_RELAY_SERVER_URL',
26
+ 'REMOTE_CODEX_RELAY_AGENT_TOKEN',
27
+ 'REMOTE_CODEX_ADMIN_USERNAME',
28
+ 'REMOTE_CODEX_ADMIN_PASSWORD',
29
+ 'REMOTE_CODEX_SESSION_SECRET',
30
+ 'REMOTE_CODEX_RELAY_SUPERVISOR_HOST',
31
+ 'REMOTE_CODEX_RELAY_SUPERVISOR_PORT',
32
+ 'DATABASE_URL',
33
+ 'WORKSPACE_ROOT',
34
+ 'CODEX_HOME',
35
+ 'CODEX_COMMAND',
36
+ 'CLAUDE_HOME',
37
+ 'CLAUDE_COMMAND',
38
+ 'OPENCODE_HOME',
39
+ 'OPENCODE_COMMAND',
40
+ 'REMOTE_CODEX_ENABLED_AGENT_PROVIDERS',
41
+ 'LOG_LEVEL',
42
+ 'REMOTE_CODEX_E2E_FAKE_RUNTIME',
43
+ 'REMOTE_CODEX_DISABLE_BUILD_RESTART',
44
+ 'REMOTE_CODEX_PACKAGE_ROOT',
45
+ ];
22
46
  const sourceCheckout =
23
47
  fs.existsSync(path.join(packageRoot, 'pnpm-workspace.yaml')) &&
24
48
  fs.existsSync(path.join(packageRoot, 'scripts', 'service-restart.mjs'));
@@ -174,11 +198,33 @@ function runRelayServer() {
174
198
  }
175
199
 
176
200
  async function runRelaySupervisor() {
177
- if (process.argv[3] === 'reset') {
201
+ const action = process.argv[3] ?? 'start';
202
+ if (action === 'reset') {
178
203
  resetRelaySupervisorConfig();
179
204
  return;
180
205
  }
206
+ if (action === 'status') {
207
+ relaySupervisorStatus();
208
+ return;
209
+ }
210
+ if (action === 'stop') {
211
+ stopRelaySupervisorTmux();
212
+ return;
213
+ }
214
+ if (action !== 'start' && action !== 'run') {
215
+ console.error(`Unknown relay-supervisor action: ${action}`);
216
+ console.error('Use one of: start, run, status, stop, reset.');
217
+ process.exit(1);
218
+ }
181
219
  await ensureRelaySupervisorConfig();
220
+ if (action === 'start' && shouldStartRelaySupervisorInTmux()) {
221
+ startRelaySupervisorTmux();
222
+ return;
223
+ }
224
+ runRelaySupervisorForeground();
225
+ }
226
+
227
+ function runRelaySupervisorForeground() {
182
228
  const guidance = {
183
229
  commandName: 'remote-codex relay-supervisor',
184
230
  description: 'Run the private-machine supervisor backend that connects outward to a public relay.',
@@ -198,6 +244,8 @@ async function runRelaySupervisor() {
198
244
  ],
199
245
  example: [
200
246
  'remote-codex relay-supervisor',
247
+ '# foreground/debug mode:',
248
+ 'remote-codex relay-supervisor run',
201
249
  '# or reset saved interactive configuration:',
202
250
  'remote-codex relay-supervisor reset',
203
251
  ],
@@ -278,6 +326,125 @@ async function runRelaySupervisor() {
278
326
  });
279
327
  }
280
328
 
329
+ function shouldStartRelaySupervisorInTmux() {
330
+ const setting = process.env.REMOTE_CODEX_RELAY_SUPERVISOR_TMUX?.trim().toLowerCase();
331
+ if (['0', 'false', 'no', 'off'].includes(setting ?? '')) {
332
+ return false;
333
+ }
334
+ if (process.env.TMUX) {
335
+ return false;
336
+ }
337
+ return commandExists('tmux');
338
+ }
339
+
340
+ function startRelaySupervisorTmux() {
341
+ persistRelaySupervisorRuntimeConfig();
342
+ if (tmuxSessionExists(relaySupervisorTmuxSession)) {
343
+ console.log(`remote-codex relay-supervisor is already running in tmux session: ${relaySupervisorTmuxSession}`);
344
+ printRelaySupervisorTmuxCommands();
345
+ return;
346
+ }
347
+
348
+ const command = relaySupervisorTmuxCommand();
349
+ const result = spawnSync('tmux', ['new-session', '-d', '-s', relaySupervisorTmuxSession, command], {
350
+ cwd: packageRoot,
351
+ env: relaySupervisorTmuxLaunchEnv(),
352
+ encoding: 'utf8',
353
+ });
354
+
355
+ if (result.status !== 0) {
356
+ console.error('Failed to start relay-supervisor in tmux.');
357
+ if (result.stderr?.trim()) {
358
+ console.error(result.stderr.trim());
359
+ }
360
+ console.error('Falling back to foreground mode.');
361
+ runRelaySupervisorForeground();
362
+ return;
363
+ }
364
+
365
+ console.log(`Started remote-codex relay-supervisor in tmux session: ${relaySupervisorTmuxSession}`);
366
+ printRelaySupervisorTmuxCommands();
367
+ }
368
+
369
+ function relaySupervisorTmuxCommand() {
370
+ const envPrefix = nonEmptyEnv('REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG')
371
+ ? `REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG=${shellQuote(process.env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG)}`
372
+ : '';
373
+ const command = `${shellQuote(process.execPath)} ${shellQuote(fileURLToPath(import.meta.url))} relay-supervisor run`;
374
+ return envPrefix ? `${envPrefix} ${command}` : command;
375
+ }
376
+
377
+ function relaySupervisorTmuxLaunchEnv() {
378
+ const env = {};
379
+ for (const name of ['PATH', 'HOME', 'SHELL', 'USER', 'LOGNAME', 'LANG', 'LC_ALL', 'TERM']) {
380
+ if (nonEmptyEnv(name)) {
381
+ env[name] = process.env[name];
382
+ }
383
+ }
384
+ if (nonEmptyEnv('REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG')) {
385
+ env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG = process.env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG;
386
+ }
387
+ return env;
388
+ }
389
+
390
+ function relaySupervisorStatus() {
391
+ if (!commandExists('tmux')) {
392
+ console.log('tmux is not installed; no managed relay-supervisor session can be inspected.');
393
+ process.exit(1);
394
+ }
395
+ if (!tmuxSessionExists(relaySupervisorTmuxSession)) {
396
+ console.log(`remote-codex relay-supervisor is not running in tmux session: ${relaySupervisorTmuxSession}`);
397
+ process.exit(1);
398
+ }
399
+ console.log(`remote-codex relay-supervisor is running in tmux session: ${relaySupervisorTmuxSession}`);
400
+ printRelaySupervisorTmuxCommands();
401
+ }
402
+
403
+ function stopRelaySupervisorTmux() {
404
+ if (!commandExists('tmux')) {
405
+ console.log('tmux is not installed; no managed relay-supervisor session can be stopped.');
406
+ return;
407
+ }
408
+ if (!tmuxSessionExists(relaySupervisorTmuxSession)) {
409
+ console.log(`remote-codex relay-supervisor is not running in tmux session: ${relaySupervisorTmuxSession}`);
410
+ return;
411
+ }
412
+ const result = spawnSync('tmux', ['kill-session', '-t', relaySupervisorTmuxSession], {
413
+ cwd: packageRoot,
414
+ encoding: 'utf8',
415
+ });
416
+ if (result.status !== 0) {
417
+ console.error(`Failed to stop tmux session: ${relaySupervisorTmuxSession}`);
418
+ if (result.stderr?.trim()) {
419
+ console.error(result.stderr.trim());
420
+ }
421
+ process.exit(1);
422
+ }
423
+ console.log(`Stopped remote-codex relay-supervisor tmux session: ${relaySupervisorTmuxSession}`);
424
+ }
425
+
426
+ function printRelaySupervisorTmuxCommands() {
427
+ console.log(`Attach logs: tmux attach -t ${shellQuote(relaySupervisorTmuxSession)}`);
428
+ console.log('Status: remote-codex relay-supervisor status');
429
+ console.log('Stop: remote-codex relay-supervisor stop');
430
+ }
431
+
432
+ function tmuxSessionExists(sessionName) {
433
+ const result = spawnSync('tmux', ['has-session', '-t', sessionName], {
434
+ cwd: packageRoot,
435
+ stdio: 'ignore',
436
+ });
437
+ return result.status === 0;
438
+ }
439
+
440
+ function commandExists(commandName) {
441
+ const result = spawnSync(commandName, ['-V'], {
442
+ cwd: packageRoot,
443
+ stdio: 'ignore',
444
+ });
445
+ return result.error?.code !== 'ENOENT';
446
+ }
447
+
281
448
  async function ensureRelaySupervisorConfig() {
282
449
  const existing = readRelaySupervisorConfig();
283
450
  const generated = {
@@ -296,9 +463,11 @@ async function ensureRelaySupervisorConfig() {
296
463
  const needsRelayUrl = !nonEmptyEnv('REMOTE_CODEX_RELAY_SERVER_URL');
297
464
  const needsAgentToken = !nonEmptyEnv('REMOTE_CODEX_RELAY_AGENT_TOKEN');
298
465
  if (!needsRelayUrl && !needsAgentToken) {
466
+ persistRelaySupervisorRuntimeConfig(config);
299
467
  return;
300
468
  }
301
469
  if (!process.stdin.isTTY || !process.stderr.isTTY) {
470
+ persistRelaySupervisorRuntimeConfig(config);
302
471
  return;
303
472
  }
304
473
 
@@ -326,15 +495,7 @@ async function ensureRelaySupervisorConfig() {
326
495
  rl.close();
327
496
  }
328
497
 
329
- writeRelaySupervisorConfig({
330
- ...config,
331
- REMOTE_CODEX_RELAY_SERVER_URL: process.env.REMOTE_CODEX_RELAY_SERVER_URL,
332
- REMOTE_CODEX_RELAY_AGENT_TOKEN: process.env.REMOTE_CODEX_RELAY_AGENT_TOKEN,
333
- REMOTE_CODEX_ADMIN_USERNAME: process.env.REMOTE_CODEX_ADMIN_USERNAME,
334
- REMOTE_CODEX_ADMIN_PASSWORD: process.env.REMOTE_CODEX_ADMIN_PASSWORD,
335
- REMOTE_CODEX_SESSION_SECRET: process.env.REMOTE_CODEX_SESSION_SECRET,
336
- DATABASE_URL: process.env.DATABASE_URL,
337
- });
498
+ persistRelaySupervisorRuntimeConfig(config);
338
499
  }
339
500
 
340
501
  async function promptRelaySupervisorValue(rl, prompt, validate, invalidMessage) {
@@ -368,6 +529,16 @@ function writeRelaySupervisorConfig(config) {
368
529
  }
369
530
  }
370
531
 
532
+ function persistRelaySupervisorRuntimeConfig(base = readRelaySupervisorConfig()) {
533
+ const config = { ...base };
534
+ for (const name of relaySupervisorConfigKeys) {
535
+ if (nonEmptyEnv(name)) {
536
+ config[name] = process.env[name];
537
+ }
538
+ }
539
+ writeRelaySupervisorConfig(config);
540
+ }
541
+
371
542
  function resetRelaySupervisorConfig() {
372
543
  try {
373
544
  fs.unlinkSync(relaySupervisorConfigPath);
@@ -527,6 +698,14 @@ function envValue(names, defaultValue) {
527
698
  return defaultValue;
528
699
  }
529
700
 
701
+ function shellQuote(value) {
702
+ const stringValue = String(value ?? '');
703
+ if (/^[A-Za-z0-9_./:@%+=,~-]+$/.test(stringValue)) {
704
+ return stringValue;
705
+ }
706
+ return `'${stringValue.replace(/'/g, `'\\''`)}'`;
707
+ }
708
+
530
709
  function relayServerEnv() {
531
710
  const env = { ...process.env };
532
711
  for (const name of [
@@ -806,11 +985,24 @@ public internet; it connects outward to a public relay server.
806
985
 
807
986
  Usage:
808
987
  remote-codex relay-supervisor
988
+ remote-codex relay-supervisor run
989
+ remote-codex relay-supervisor status
990
+ remote-codex relay-supervisor stop
809
991
  remote-codex relay-supervisor reset
810
992
 
811
993
  This command automatically sets for the child supervisor:
812
994
  REMOTE_CODEX_MODE=relay
813
995
 
996
+ Default process management:
997
+ By default, this command tries to start the supervisor in a detached tmux
998
+ session named "${relaySupervisorTmuxSession}" so the device stays online
999
+ after the launching terminal closes. If tmux is not installed, or if
1000
+ REMOTE_CODEX_RELAY_SUPERVISOR_TMUX=0 is set, it runs in the foreground.
1001
+
1002
+ Use "remote-codex relay-supervisor run" for explicit foreground/debug mode.
1003
+ Use "remote-codex relay-supervisor status" to inspect the tmux session.
1004
+ Use "remote-codex relay-supervisor stop" to stop the tmux session.
1005
+
814
1006
  Interactive setup:
815
1007
  When REMOTE_CODEX_RELAY_SERVER_URL or REMOTE_CODEX_RELAY_AGENT_TOKEN is
816
1008
  missing and stdin is interactive, the command asks for those values and saves
@@ -818,7 +1010,8 @@ Interactive setup:
818
1010
  ${relaySupervisorConfigPath}
819
1011
 
820
1012
  The saved config also includes generated local supervisor auth/session values
821
- so the normal first-run path only asks for relay URL and device token.
1013
+ and copied setup values such as REMOTE_CODEX_RELAY_SUPERVISOR_PORT, so the
1014
+ tmux child process can start with the same effective configuration.
822
1015
 
823
1016
  Use "remote-codex relay-supervisor reset" to delete the saved config.
824
1017
 
@@ -851,10 +1044,21 @@ Recommended environment:
851
1044
  Codex executable. Default codex.
852
1045
  REMOTE_CODEX_ENABLED_AGENT_PROVIDERS
853
1046
  Comma-separated provider ids, for example codex,claude.
1047
+ REMOTE_CODEX_RELAY_SUPERVISOR_TMUX
1048
+ Set to 0/false/no/off to disable default tmux management.
1049
+ REMOTE_CODEX_RELAY_SUPERVISOR_TMUX_SESSION
1050
+ tmux session name. Default ${relaySupervisorTmuxSession}.
854
1051
 
855
1052
  Example:
856
1053
  remote-codex relay-supervisor
857
1054
 
1055
+ Foreground/debug example:
1056
+ remote-codex relay-supervisor run
1057
+
1058
+ Management:
1059
+ remote-codex relay-supervisor status
1060
+ remote-codex relay-supervisor stop
1061
+
858
1062
  Non-interactive example:
859
1063
  REMOTE_CODEX_RELAY_SERVER_URL=wss://relay.example.com \\
860
1064
  REMOTE_CODEX_RELAY_AGENT_TOKEN=rcd_device_token_from_relay_portal \\
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-codex",
3
- "version": "0.11.27",
3
+ "version": "0.11.28",
4
4
  "description": "Local web supervisor for Codex workspaces and threads.",
5
5
  "license": "MIT",
6
6
  "type": "module",