@openagents-org/agent-launcher 0.2.164 → 0.2.165

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagents-org/agent-launcher",
3
- "version": "0.2.164",
3
+ "version": "0.2.165",
4
4
  "description": "OpenAgents Launcher \u2014 install, configure, and run AI coding agents from your terminal",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -1,5 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ // Before ./index so the daemon and every `agn` subprocess inherit the same
4
+ // no-stray-console-window default on Windows. See win-console.js.
5
+ require('./win-console').installWindowsHideDefault();
6
+
3
7
  const { AgentConnector, Daemon } = require('./index');
4
8
  const { hasCredentialMetadata, formatAuthGuidance } = require('./auth-guidance');
5
9
 
package/src/daemon.js CHANGED
@@ -265,6 +265,10 @@ class Daemon {
265
265
  // Run from home so a created agent's default working dir is sensible
266
266
  // (not the ~/.openagents config dir).
267
267
  cwd: os.homedir(),
268
+ // The daemon has no console of its own (it is spawned DETACHED), so a
269
+ // child without this gets a fresh console window — the blank black box
270
+ // that used to appear every time _refreshRuntimes() ran.
271
+ windowsHide: true,
268
272
  });
269
273
  let stdout = '';
270
274
  let stderr = '';
@@ -921,6 +925,9 @@ class Daemon {
921
925
  stdio: ['ignore', 'pipe', 'pipe'],
922
926
  env: getEnhancedEnv(opts.env),
923
927
  cwd: opts.cwd,
928
+ // Windows: cmd.exe would otherwise open a console window per agent (the
929
+ // daemon has none to inherit) and leave it up for the agent's lifetime.
930
+ windowsHide: true,
924
931
  };
925
932
 
926
933
  if (IS_WINDOWS) {
package/src/index.js CHANGED
@@ -1,5 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ // Must run before anything captures a child_process reference: on Windows it
4
+ // keeps every spawn from popping a stray console window. See win-console.js.
5
+ require('./win-console').installWindowsHideDefault();
6
+
3
7
  const { Config } = require('./config');
4
8
  const { EnvManager } = require('./env');
5
9
  const { Registry } = require('./registry');
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Stop Windows from popping a console window for every child process we spawn.
5
+ *
6
+ * Neither process that runs this code owns a console: the daemon is spawned
7
+ * DETACHED (`agn up --foreground`), and the launcher's Electron main process is
8
+ * a GUI app. On Windows, when a console-less process launches a console
9
+ * executable *without* CREATE_NO_WINDOW, the OS allocates a brand-new console
10
+ * for the child — an empty black cmd/Windows-Terminal window that pops to the
11
+ * foreground and steals focus. With stdio piped nothing is ever drawn in it,
12
+ * so what the user sees is a blank window appearing out of nowhere.
13
+ *
14
+ * That is the "命令框 keeps popping up" report: `_refreshRuntimes()` shells out
15
+ * to `agn runtimes --json` every two minutes, and the probe it runs keeps the
16
+ * window on screen for as long as it takes to check every installed CLI.
17
+ *
18
+ * Node exposes CREATE_NO_WINDOW as the `windowsHide` spawn option and most call
19
+ * sites pass it — but there are ~60 of them across the adapters and a single
20
+ * miss brings the window back. So the default lives here, applied once at the
21
+ * process entry points (src/index.js, src/cli.js) before any module has
22
+ * captured a reference to child_process.
23
+ *
24
+ * Two things this deliberately does NOT do:
25
+ * - override an explicit `windowsHide: false` — the launcher opens a real
26
+ * terminal for CLI sign-in that way, and that window is the point; and
27
+ * - break interactive output: libuv only applies CREATE_NO_WINDOW when no
28
+ * stdio handle is inherited, so `agn` run from a terminal (stdio 'inherit')
29
+ * is unaffected by this and still prints where the user can see it.
30
+ */
31
+
32
+ const childProcess = require('child_process');
33
+
34
+ const PATCHED = Symbol.for('openagents.windowsHideDefault');
35
+ const WRAPPED = ['spawn', 'spawnSync', 'exec', 'execSync', 'execFile', 'execFileSync'];
36
+
37
+ /**
38
+ * Return a copy of `args` whose options object carries `windowsHide: true`.
39
+ *
40
+ * Every child_process signature ends with `[options][, callback]`, so the
41
+ * options object — if there is one — is the last non-function argument. The
42
+ * caller's object is copied rather than mutated: callers reuse option objects
43
+ * and must not see ours leak into their state.
44
+ */
45
+ function withWindowsHide(args) {
46
+ const out = args.slice();
47
+ const cbIdx = typeof out[out.length - 1] === 'function' ? out.length - 1 : -1;
48
+ const optIdx = cbIdx === -1 ? out.length - 1 : cbIdx - 1;
49
+ const opts = optIdx >= 0 ? out[optIdx] : undefined;
50
+
51
+ if (opts && typeof opts === 'object' && !Array.isArray(opts)) {
52
+ if ('windowsHide' in opts) return args; // explicit wins, either way
53
+ out[optIdx] = { ...opts, windowsHide: true };
54
+ return out;
55
+ }
56
+ // No options argument at all — insert one ahead of any callback.
57
+ out.splice(cbIdx === -1 ? out.length : cbIdx, 0, { windowsHide: true });
58
+ return out;
59
+ }
60
+
61
+ /**
62
+ * Default `windowsHide: true` for this process's child_process calls.
63
+ * No-op off Windows, and idempotent.
64
+ */
65
+ function installWindowsHideDefault() {
66
+ if (process.platform !== 'win32') return;
67
+ if (childProcess[PATCHED]) return;
68
+
69
+ for (const name of WRAPPED) {
70
+ const original = childProcess[name];
71
+ if (typeof original !== 'function') continue;
72
+ const wrapper = function (...args) {
73
+ return original.apply(this, withWindowsHide(args));
74
+ };
75
+ // Keep util.promisify(exec) and friends working.
76
+ for (const sym of Object.getOwnPropertySymbols(original)) {
77
+ wrapper[sym] = original[sym];
78
+ }
79
+ Object.defineProperty(wrapper, 'name', { value: name });
80
+ childProcess[name] = wrapper;
81
+ }
82
+ childProcess[PATCHED] = true;
83
+ }
84
+
85
+ module.exports = { installWindowsHideDefault, withWindowsHide };