@mclean-capital/neura 2.0.0 → 2.1.0
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 +43 -1
- package/core/server.bundled.mjs +18 -1
- package/core/server.bundled.mjs.map +3 -3
- package/core/version.txt +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +19 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/service/detect.js +1 -1
- package/dist/service/detect.js.map +1 -1
- package/dist/service/windows.d.ts +116 -0
- package/dist/service/windows.d.ts.map +1 -1
- package/dist/service/windows.js +303 -60
- package/dist/service/windows.js.map +1 -1
- package/package.json +1 -1
package/core/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.1.0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,cAAc,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAsB,cAAc,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyM7E"}
|
package/dist/commands/install.js
CHANGED
|
@@ -137,14 +137,31 @@ export async function installCommand(opts = {}) {
|
|
|
137
137
|
}
|
|
138
138
|
await svc.install();
|
|
139
139
|
console.log(chalk.green(` ✓ Service ${wasInstalled ? 're-registered' : 'registered'} (${getPlatformLabel()})`));
|
|
140
|
+
// Windows has two install paths (Scheduled Task → preferred, or
|
|
141
|
+
// Startup folder shim → fallback). Tell the user which one was
|
|
142
|
+
// used so they understand what to expect — e.g. Task Scheduler
|
|
143
|
+
// manageability vs. runs-on-next-login. Empty import on non-Windows.
|
|
144
|
+
if (process.platform === 'win32') {
|
|
145
|
+
const win = await import('../service/windows.js');
|
|
146
|
+
const mode = win.getLastInstallMode();
|
|
147
|
+
if (mode === 'startup-shim') {
|
|
148
|
+
console.log(chalk.dim(' (Using Startup folder shim — schtasks.exe refused to register\n' +
|
|
149
|
+
' the Scheduled Task on this machine, likely due to Windows\n' +
|
|
150
|
+
' policy or corporate restrictions. The core will still run\n' +
|
|
151
|
+
' at each user login. It can be removed from\n' +
|
|
152
|
+
' %APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\.)'));
|
|
153
|
+
}
|
|
154
|
+
else if (mode === 'scheduled-task') {
|
|
155
|
+
console.log(chalk.dim(' (Registered in Task Scheduler under name "neura-core")'));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
140
158
|
svc.start();
|
|
141
159
|
serviceRegistered = true;
|
|
142
160
|
}
|
|
143
161
|
catch (err) {
|
|
144
162
|
console.log(chalk.yellow(' Service registration skipped:'));
|
|
145
163
|
console.log(chalk.yellow(' ' + (err instanceof Error ? err.message : String(err))));
|
|
146
|
-
console.log(chalk.dim(' Config was saved.
|
|
147
|
-
console.log(chalk.dim(' npm run dev -w @neura/core'));
|
|
164
|
+
console.log(chalk.dim(' Config was saved. Try again after resolving the issue.'));
|
|
148
165
|
}
|
|
149
166
|
// Wait for health (only if service was started)
|
|
150
167
|
if (serviceRegistered) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
|
package/dist/service/detect.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/service/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIzC,MAAM,UAAU,cAAc;IAC5B,QAAQ,QAAQ,EAAE,EAAE,CAAC;QACnB,KAAK,OAAO;YACV,OAAO,SAAS,CAAC;QACnB,KAAK,QAAQ;YACX,OAAO,OAAO,CAAC;QACjB;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU;IACxB,IAAI,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,QAAQ,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,MAAM,GAA6B;QACvC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/service/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIzC,MAAM,UAAU,cAAc;IAC5B,QAAQ,QAAQ,EAAE,EAAE,CAAC;QACnB,KAAK,OAAO;YACV,OAAO,SAAS,CAAC;QACnB,KAAK,QAAQ;YACX,OAAO,OAAO,CAAC;QACjB;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU;IACxB,IAAI,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,QAAQ,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,MAAM,GAA6B;QACvC,OAAO,EAAE,qCAAqC;QAC9C,KAAK,EAAE,eAAe;QACtB,KAAK,EAAE,sBAAsB;KAC9B,CAAC;IACF,OAAO,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -1,5 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows service manager.
|
|
3
|
+
*
|
|
4
|
+
* Windows does not have a clean "run as background service" path for a
|
|
5
|
+
* voice-first AI assistant. A real SCM service (via nssm/WinSW) requires
|
|
6
|
+
* admin rights to install and runs in Session 0, which is isolated from
|
|
7
|
+
* the user's audio devices — the microphone is unreachable from a
|
|
8
|
+
* LocalSystem service. That makes wake-word detection impossible.
|
|
9
|
+
*
|
|
10
|
+
* So we do what `openclaw` does on Windows:
|
|
11
|
+
*
|
|
12
|
+
* 1. Primary path — `schtasks.exe /Create /SC ONLOGON /RL LIMITED` to
|
|
13
|
+
* register a per-user logon-triggered Scheduled Task. `/RL LIMITED`
|
|
14
|
+
* keeps the task out of the elevated-integrity bucket, so no UAC
|
|
15
|
+
* prompt, no admin rights, no bundled service wrapper.
|
|
16
|
+
*
|
|
17
|
+
* 2. Fallback path — if schtasks.exe refuses (GPO, corporate lockdown,
|
|
18
|
+
* "access denied" on `/Create`), drop a `neura-core.cmd` shim into
|
|
19
|
+
* `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\`. The
|
|
20
|
+
* shim runs on next logon; on first install we also spawn the core
|
|
21
|
+
* detached immediately so the user doesn't have to log out to get
|
|
22
|
+
* a working assistant.
|
|
23
|
+
*
|
|
24
|
+
* 3. Dual state — every lifecycle op (`isInstalled`, `stop`, `restart`,
|
|
25
|
+
* `uninstall`) has to handle both "Scheduled Task is registered"
|
|
26
|
+
* and "Startup shim is present". They're mutually-exclusive install
|
|
27
|
+
* states but the code has to check both.
|
|
28
|
+
*
|
|
29
|
+
* Trade-offs we accept:
|
|
30
|
+
* - The core only runs while the user is logged in (dies on logout).
|
|
31
|
+
* - Status telemetry is weaker than a real service (no exit-code
|
|
32
|
+
* history, no restart policy beyond "schtasks will re-run on next
|
|
33
|
+
* logon"). We compensate with a PID file for `isRunning` / `stop`.
|
|
34
|
+
* - No pre-login boot. If you want that, use macOS or Linux.
|
|
35
|
+
*/
|
|
36
|
+
interface ShimContext {
|
|
37
|
+
nodePath: string;
|
|
38
|
+
corePath: string;
|
|
39
|
+
home: string;
|
|
40
|
+
logDir: string;
|
|
41
|
+
logFile: string;
|
|
42
|
+
errLogFile: string;
|
|
43
|
+
pidFile: string;
|
|
44
|
+
cmdPath: string;
|
|
45
|
+
}
|
|
46
|
+
declare function getShimContext(): ShimContext;
|
|
47
|
+
declare function getStartupShimPath(): string;
|
|
48
|
+
/**
|
|
49
|
+
* Escape a string for safe embedding in a Windows `cmd.exe` batch file.
|
|
50
|
+
*
|
|
51
|
+
* The rules here are painful: `cmd.exe` treats `%`, `^`, `&`, `|`, `<`, `>`
|
|
52
|
+
* as special outside quoted strings, and `%` is still special inside quotes
|
|
53
|
+
* when it looks like an environment variable reference. We use `^` to
|
|
54
|
+
* escape the metacharacters and double `%%` so environment-variable
|
|
55
|
+
* expansion only happens where we actually want it.
|
|
56
|
+
*/
|
|
57
|
+
declare function escapeCmd(str: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Produce the contents of the launcher `.cmd` file that the Scheduled
|
|
60
|
+
* Task (or the Startup folder shim) will invoke on each logon.
|
|
61
|
+
*
|
|
62
|
+
* The shim is intentionally dumb: set NEURA_HOME so the core knows
|
|
63
|
+
* where to read `config.json` from, redirect output to log files, then
|
|
64
|
+
* exec Node on the core bundle.
|
|
65
|
+
*
|
|
66
|
+
* IMPORTANT: the shim sets ONLY `NEURA_HOME`. It does NOT bake in the
|
|
67
|
+
* port, auth token, or API keys from the install-time config — even
|
|
68
|
+
* though we have those values handy. Reason: the core's config loader
|
|
69
|
+
* in `packages/core/src/config/config.ts` prefers `process.env.*` over
|
|
70
|
+
* `file.*`, so any value baked into the shim would win over the user's
|
|
71
|
+
* live `config.json`. That meant `neura config set port 20000` (or
|
|
72
|
+
* `config set apiKeys.xai ...`) would silently do nothing on Windows
|
|
73
|
+
* until the user re-ran `neura install`, because the old values in the
|
|
74
|
+
* shim's `set` statements still shadowed the new ones in `config.json`.
|
|
75
|
+
* By shipping only `NEURA_HOME`, all runtime config flows through
|
|
76
|
+
* `config.json` → the core reads the latest values on every restart,
|
|
77
|
+
* matching the macOS and Linux behavior.
|
|
78
|
+
*
|
|
79
|
+
* The shim also does NOT track PIDs — the core writes its own
|
|
80
|
+
* `$NEURA_HOME/neura-core.pid` on startup (see
|
|
81
|
+
* `packages/core/src/server/lifecycle.ts`), which `isRunning()` and
|
|
82
|
+
* `stop()` key off of.
|
|
83
|
+
*/
|
|
84
|
+
declare function renderCmdShim(ctx: ShimContext): string;
|
|
1
85
|
export declare function isInstalled(): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Check if the core process is currently alive.
|
|
88
|
+
*
|
|
89
|
+
* Reads `$NEURA_HOME/neura-core.pid` (written by the core itself in
|
|
90
|
+
* `packages/core/src/server/lifecycle.ts`) and asks `tasklist` whether
|
|
91
|
+
* a process with that PID is running. Any living process is treated as
|
|
92
|
+
* "the core" — we don't fingerprint by name, which matches the contract
|
|
93
|
+
* of `stop()` below.
|
|
94
|
+
*
|
|
95
|
+
* Self-heals stale pid files: if the file exists but refers to a dead
|
|
96
|
+
* or unreadable PID, we delete it as a side-effect so the next
|
|
97
|
+
* `isRunning()` / `start()` call isn't fooled by Windows PID reuse
|
|
98
|
+
* (which is real and can happen within seconds on busy machines).
|
|
99
|
+
*/
|
|
2
100
|
export declare function isRunning(): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Which of the two dual-path install modes was used.
|
|
103
|
+
*
|
|
104
|
+
* `scheduled-task` is the preferred path — registered via schtasks,
|
|
105
|
+
* managed from Task Scheduler. `startup-shim` is the fallback used when
|
|
106
|
+
* schtasks refuses (access denied, GPO lockdown, etc.). Exposed so the
|
|
107
|
+
* caller can tell the user which path was taken and what it means.
|
|
108
|
+
*/
|
|
109
|
+
export type InstallMode = 'scheduled-task' | 'startup-shim';
|
|
110
|
+
/** The install mode used by the most recent install() call, or null. */
|
|
111
|
+
export declare function getLastInstallMode(): InstallMode | null;
|
|
3
112
|
export declare function install(): void;
|
|
4
113
|
export declare function uninstall(): void;
|
|
5
114
|
export declare function start(): void;
|
|
@@ -17,4 +126,11 @@ declare const _default: {
|
|
|
17
126
|
readonly getLogPath: typeof getLogPath;
|
|
18
127
|
};
|
|
19
128
|
export default _default;
|
|
129
|
+
export declare const __test__: {
|
|
130
|
+
TASK_NAME: string;
|
|
131
|
+
getShimContext: typeof getShimContext;
|
|
132
|
+
getStartupShimPath: typeof getStartupShimPath;
|
|
133
|
+
renderCmdShim: typeof renderCmdShim;
|
|
134
|
+
escapeCmd: typeof escapeCmd;
|
|
135
|
+
};
|
|
20
136
|
//# sourceMappingURL=windows.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../../src/service/windows.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../../src/service/windows.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,iBAAS,cAAc,IAAI,WAAW,CAarC;AAED,iBAAS,kBAAkB,IAAI,MAAM,CAYpC;AAED;;;;;;;;GAQG;AACH,iBAAS,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,iBAAS,aAAa,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAc/C;AA2GD,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAgBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,IAAI,OAAO,CA0BnC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAI5D,wEAAwE;AACxE,wBAAgB,kBAAkB,IAAI,WAAW,GAAG,IAAI,CAEvD;AAED,wBAAgB,OAAO,IAAI,IAAI,CAuC9B;AAED,wBAAgB,SAAS,IAAI,IAAI,CAOhC;AAED,wBAAgB,KAAK,IAAI,IAAI,CAG5B;AAED,wBAAgB,IAAI,IAAI,IAAI,CA6B3B;AAED,wBAAgB,OAAO,IAAI,IAAI,CAG9B;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;;;;;;;;;;;AAED,wBASW;AAGX,eAAO,MAAM,QAAQ;;;;;;CAMpB,CAAC"}
|
package/dist/service/windows.js
CHANGED
|
@@ -1,90 +1,325 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { writeFileSync, existsSync, unlinkSync, mkdirSync, readFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { execSync, spawnSync, spawn } from 'child_process';
|
|
3
5
|
import { getNeuraHome } from '../config.js';
|
|
4
|
-
import {
|
|
5
|
-
const
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
import { getCoreBinaryPath } from '../download.js';
|
|
7
|
+
const TASK_NAME = 'neura-core';
|
|
8
|
+
function getShimContext() {
|
|
9
|
+
const home = getNeuraHome();
|
|
10
|
+
const logDir = join(home, 'logs');
|
|
11
|
+
return {
|
|
12
|
+
nodePath: process.execPath,
|
|
13
|
+
corePath: getCoreBinaryPath(),
|
|
14
|
+
home,
|
|
15
|
+
logDir,
|
|
16
|
+
logFile: join(logDir, 'core.log'),
|
|
17
|
+
errLogFile: join(logDir, 'core.error.log'),
|
|
18
|
+
pidFile: join(home, 'neura-core.pid'),
|
|
19
|
+
cmdPath: join(home, 'neura-core.cmd'),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function getStartupShimPath() {
|
|
23
|
+
// %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\neura-core.cmd
|
|
24
|
+
const appData = process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming');
|
|
25
|
+
return join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', `${TASK_NAME}.cmd`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Escape a string for safe embedding in a Windows `cmd.exe` batch file.
|
|
29
|
+
*
|
|
30
|
+
* The rules here are painful: `cmd.exe` treats `%`, `^`, `&`, `|`, `<`, `>`
|
|
31
|
+
* as special outside quoted strings, and `%` is still special inside quotes
|
|
32
|
+
* when it looks like an environment variable reference. We use `^` to
|
|
33
|
+
* escape the metacharacters and double `%%` so environment-variable
|
|
34
|
+
* expansion only happens where we actually want it.
|
|
35
|
+
*/
|
|
36
|
+
function escapeCmd(str) {
|
|
37
|
+
return str
|
|
38
|
+
.replace(/\^/g, '^^')
|
|
39
|
+
.replace(/%/g, '%%')
|
|
40
|
+
.replace(/([&|<>])/g, '^$1');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Produce the contents of the launcher `.cmd` file that the Scheduled
|
|
44
|
+
* Task (or the Startup folder shim) will invoke on each logon.
|
|
45
|
+
*
|
|
46
|
+
* The shim is intentionally dumb: set NEURA_HOME so the core knows
|
|
47
|
+
* where to read `config.json` from, redirect output to log files, then
|
|
48
|
+
* exec Node on the core bundle.
|
|
49
|
+
*
|
|
50
|
+
* IMPORTANT: the shim sets ONLY `NEURA_HOME`. It does NOT bake in the
|
|
51
|
+
* port, auth token, or API keys from the install-time config — even
|
|
52
|
+
* though we have those values handy. Reason: the core's config loader
|
|
53
|
+
* in `packages/core/src/config/config.ts` prefers `process.env.*` over
|
|
54
|
+
* `file.*`, so any value baked into the shim would win over the user's
|
|
55
|
+
* live `config.json`. That meant `neura config set port 20000` (or
|
|
56
|
+
* `config set apiKeys.xai ...`) would silently do nothing on Windows
|
|
57
|
+
* until the user re-ran `neura install`, because the old values in the
|
|
58
|
+
* shim's `set` statements still shadowed the new ones in `config.json`.
|
|
59
|
+
* By shipping only `NEURA_HOME`, all runtime config flows through
|
|
60
|
+
* `config.json` → the core reads the latest values on every restart,
|
|
61
|
+
* matching the macOS and Linux behavior.
|
|
62
|
+
*
|
|
63
|
+
* The shim also does NOT track PIDs — the core writes its own
|
|
64
|
+
* `$NEURA_HOME/neura-core.pid` on startup (see
|
|
65
|
+
* `packages/core/src/server/lifecycle.ts`), which `isRunning()` and
|
|
66
|
+
* `stop()` key off of.
|
|
67
|
+
*/
|
|
68
|
+
function renderCmdShim(ctx) {
|
|
69
|
+
// Notes:
|
|
70
|
+
// - `@echo off` suppresses command echoing.
|
|
71
|
+
// - `chcp 65001 >nul` sets the console to UTF-8 so pino's log output
|
|
72
|
+
// doesn't mangle when it contains non-ASCII characters.
|
|
73
|
+
// - stdout/stderr are redirected with `>> "<log>" 2>> "<err>"` so
|
|
74
|
+
// we don't need a separate supervisor for log capture.
|
|
75
|
+
return `@echo off\r
|
|
76
|
+
chcp 65001 >nul\r
|
|
77
|
+
set "NEURA_HOME=${escapeCmd(ctx.home)}"\r
|
|
78
|
+
if not exist "${escapeCmd(ctx.logDir)}" mkdir "${escapeCmd(ctx.logDir)}"\r
|
|
79
|
+
echo %~nx0 started at %date% %time% >> "${escapeCmd(ctx.logFile)}"\r
|
|
80
|
+
"${escapeCmd(ctx.nodePath)}" "${escapeCmd(ctx.corePath)}" >> "${escapeCmd(ctx.logFile)}" 2>> "${escapeCmd(ctx.errLogFile)}"\r
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Write the shim .cmd that both the Scheduled Task and the Startup
|
|
85
|
+
* folder fallback invoke. Idempotent — overwrites whatever's there.
|
|
86
|
+
*/
|
|
87
|
+
function writeCmdShim(ctx) {
|
|
88
|
+
mkdirSync(dirname(ctx.cmdPath), { recursive: true });
|
|
89
|
+
mkdirSync(ctx.logDir, { recursive: true });
|
|
90
|
+
writeFileSync(ctx.cmdPath, renderCmdShim(ctx), 'utf-8');
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Register the Scheduled Task via `schtasks /Create`.
|
|
94
|
+
*
|
|
95
|
+
* Returns true on success, false on failure. Never throws — we want the
|
|
96
|
+
* caller to cleanly fall back to the Startup-folder path without trying
|
|
97
|
+
* to parse schtasks's error output.
|
|
98
|
+
*/
|
|
99
|
+
function tryCreateScheduledTask(ctx) {
|
|
100
|
+
// /F = force overwrite of existing task
|
|
101
|
+
// /SC ONLOGON = trigger on user logon (no admin needed)
|
|
102
|
+
// /RL LIMITED = limited run level — NOT elevated, no UAC prompt
|
|
103
|
+
// /TN = task name
|
|
104
|
+
// /TR = the command to run; must be fully quoted when it contains
|
|
105
|
+
// a path with spaces. schtasks does its own quoting layer which
|
|
106
|
+
// means we end up needing \" inside the outer quotes. spawnSync
|
|
107
|
+
// handles the outer argv layer, so we just pass the path with
|
|
108
|
+
// embedded quotes.
|
|
109
|
+
const result = spawnSync('schtasks.exe', [
|
|
110
|
+
'/Create',
|
|
111
|
+
'/F',
|
|
112
|
+
'/SC',
|
|
113
|
+
'ONLOGON',
|
|
114
|
+
'/RL',
|
|
115
|
+
'LIMITED',
|
|
116
|
+
'/TN',
|
|
117
|
+
TASK_NAME,
|
|
118
|
+
'/TR',
|
|
119
|
+
`"${ctx.cmdPath}"`,
|
|
120
|
+
], { stdio: ['ignore', 'ignore', 'pipe'], windowsHide: true });
|
|
121
|
+
return result.status === 0;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check whether the Scheduled Task exists.
|
|
125
|
+
*
|
|
126
|
+
* `schtasks /Query /TN <name>` returns exit 0 if the task exists, 1 if
|
|
127
|
+
* it doesn't (and also writes a message to stderr that we don't need).
|
|
128
|
+
*/
|
|
129
|
+
function isTaskRegistered() {
|
|
130
|
+
const result = spawnSync('schtasks.exe', ['/Query', '/TN', TASK_NAME], {
|
|
131
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
132
|
+
windowsHide: true,
|
|
133
|
+
});
|
|
134
|
+
return result.status === 0;
|
|
135
|
+
}
|
|
136
|
+
function deleteScheduledTask() {
|
|
137
|
+
spawnSync('schtasks.exe', ['/Delete', '/F', '/TN', TASK_NAME], {
|
|
138
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
139
|
+
windowsHide: true,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function isStartupShimInstalled() {
|
|
143
|
+
return existsSync(getStartupShimPath());
|
|
11
144
|
}
|
|
145
|
+
function installStartupShim(ctx) {
|
|
146
|
+
const shimPath = getStartupShimPath();
|
|
147
|
+
mkdirSync(dirname(shimPath), { recursive: true });
|
|
148
|
+
// The startup-folder file is a tiny launcher that calls the real shim
|
|
149
|
+
// in NEURA_HOME. We keep the real shim in NEURA_HOME so `neura config`
|
|
150
|
+
// can regenerate it without touching Windows-managed directories.
|
|
151
|
+
const launcher = `@echo off\r\nstart "" /min cmd.exe /d /c "${escapeCmd(ctx.cmdPath)}"\r\n`;
|
|
152
|
+
writeFileSync(shimPath, launcher, 'utf-8');
|
|
153
|
+
}
|
|
154
|
+
function removeStartupShim() {
|
|
155
|
+
const shimPath = getStartupShimPath();
|
|
156
|
+
if (existsSync(shimPath))
|
|
157
|
+
unlinkSync(shimPath);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Spawn the launcher `.cmd` as a detached child so the core starts
|
|
161
|
+
* immediately on first install (instead of waiting for the next logon).
|
|
162
|
+
*
|
|
163
|
+
* We use `spawn` + `{ detached: true, stdio: 'ignore' }` + `.unref()` so
|
|
164
|
+
* the parent `neura install` process can exit cleanly while core keeps
|
|
165
|
+
* running. `windowsHide: true` suppresses the flash of a cmd console.
|
|
166
|
+
*/
|
|
167
|
+
function spawnDetachedCore(ctx) {
|
|
168
|
+
const child = spawn('cmd.exe', ['/d', '/c', ctx.cmdPath], {
|
|
169
|
+
detached: true,
|
|
170
|
+
stdio: 'ignore',
|
|
171
|
+
windowsHide: true,
|
|
172
|
+
});
|
|
173
|
+
child.unref();
|
|
174
|
+
}
|
|
175
|
+
// ── ServiceManager interface ─────────────────────────────────────────
|
|
12
176
|
export function isInstalled() {
|
|
177
|
+
return isTaskRegistered() || isStartupShimInstalled();
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Unlink the pid file, swallowing "already gone" errors. Used by
|
|
181
|
+
* `isRunning()` to self-heal stale pid files left behind when the core
|
|
182
|
+
* dies without running its cleanup handlers (crash, `taskkill /F`, OS
|
|
183
|
+
* reboot).
|
|
184
|
+
*/
|
|
185
|
+
function unlinkPidFile(pidFile) {
|
|
13
186
|
try {
|
|
14
|
-
|
|
15
|
-
encoding: 'utf-8',
|
|
16
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
17
|
-
});
|
|
18
|
-
return !result.includes('does not exist');
|
|
187
|
+
unlinkSync(pidFile);
|
|
19
188
|
}
|
|
20
189
|
catch {
|
|
21
|
-
|
|
190
|
+
// Already removed; fine.
|
|
22
191
|
}
|
|
23
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Check if the core process is currently alive.
|
|
195
|
+
*
|
|
196
|
+
* Reads `$NEURA_HOME/neura-core.pid` (written by the core itself in
|
|
197
|
+
* `packages/core/src/server/lifecycle.ts`) and asks `tasklist` whether
|
|
198
|
+
* a process with that PID is running. Any living process is treated as
|
|
199
|
+
* "the core" — we don't fingerprint by name, which matches the contract
|
|
200
|
+
* of `stop()` below.
|
|
201
|
+
*
|
|
202
|
+
* Self-heals stale pid files: if the file exists but refers to a dead
|
|
203
|
+
* or unreadable PID, we delete it as a side-effect so the next
|
|
204
|
+
* `isRunning()` / `start()` call isn't fooled by Windows PID reuse
|
|
205
|
+
* (which is real and can happen within seconds on busy machines).
|
|
206
|
+
*/
|
|
24
207
|
export function isRunning() {
|
|
208
|
+
const ctx = getShimContext();
|
|
209
|
+
if (!existsSync(ctx.pidFile))
|
|
210
|
+
return false;
|
|
211
|
+
let pid;
|
|
25
212
|
try {
|
|
26
|
-
|
|
27
|
-
encoding: 'utf-8',
|
|
28
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
29
|
-
});
|
|
30
|
-
return result.includes('RUNNING');
|
|
213
|
+
pid = parseInt(readFileSync(ctx.pidFile, 'utf-8').trim(), 10);
|
|
31
214
|
}
|
|
32
215
|
catch {
|
|
216
|
+
unlinkPidFile(ctx.pidFile);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
if (!pid || Number.isNaN(pid)) {
|
|
220
|
+
unlinkPidFile(ctx.pidFile);
|
|
33
221
|
return false;
|
|
34
222
|
}
|
|
223
|
+
const result = spawnSync('tasklist.exe', ['/FI', `PID eq ${pid}`, '/NH'], {
|
|
224
|
+
encoding: 'utf-8',
|
|
225
|
+
windowsHide: true,
|
|
226
|
+
});
|
|
227
|
+
// tasklist prints "INFO: No tasks are running..." when PID is missing.
|
|
228
|
+
const alive = typeof result.stdout === 'string' && !result.stdout.includes('No tasks');
|
|
229
|
+
if (!alive) {
|
|
230
|
+
unlinkPidFile(ctx.pidFile);
|
|
231
|
+
}
|
|
232
|
+
return alive;
|
|
233
|
+
}
|
|
234
|
+
let lastInstallMode = null;
|
|
235
|
+
/** The install mode used by the most recent install() call, or null. */
|
|
236
|
+
export function getLastInstallMode() {
|
|
237
|
+
return lastInstallMode;
|
|
35
238
|
}
|
|
36
239
|
export function install() {
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
240
|
+
const ctx = getShimContext();
|
|
241
|
+
// Always regenerate the launcher shim so it picks up any new paths
|
|
242
|
+
// or config changes since the last install. Idempotent: overwriting
|
|
243
|
+
// the .cmd file is safe whether or not the task is already registered.
|
|
244
|
+
writeCmdShim(ctx);
|
|
245
|
+
// Prefer the Scheduled Task path — it survives reboots cleanly and
|
|
246
|
+
// shows up in Task Scheduler so the user can manage it from the GUI.
|
|
247
|
+
// If it fails (GPO, corp lockdown, schtasks.exe missing from PATH,
|
|
248
|
+
// or a Windows config that requires elevation for user-level tasks),
|
|
249
|
+
// fall back to a Startup folder shim. Both paths register the same
|
|
250
|
+
// shim; they just differ in how Windows invokes it at logon.
|
|
42
251
|
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
252
|
+
// Both branches clean up the OTHER install mode so we never end up
|
|
253
|
+
// with a task AND a startup shim both firing at next logon. Without
|
|
254
|
+
// this symmetric cleanup, a reinstall that changes which path wins
|
|
255
|
+
// (e.g. a machine that previously accepted schtasks /Create but later
|
|
256
|
+
// tightened GPO) would leave both modes active — the next logon would
|
|
257
|
+
// start two copies of core, and because server.ts retries EADDRINUSE
|
|
258
|
+
// on port+1 the second copy comes up on a different port instead of
|
|
259
|
+
// failing loudly.
|
|
260
|
+
const taskCreated = tryCreateScheduledTask(ctx);
|
|
261
|
+
if (taskCreated) {
|
|
262
|
+
lastInstallMode = 'scheduled-task';
|
|
263
|
+
removeStartupShim();
|
|
53
264
|
}
|
|
54
|
-
|
|
55
|
-
|
|
265
|
+
else {
|
|
266
|
+
lastInstallMode = 'startup-shim';
|
|
267
|
+
deleteScheduledTask();
|
|
268
|
+
installStartupShim(ctx);
|
|
56
269
|
}
|
|
57
|
-
|
|
270
|
+
// NOTE: we do NOT spawn the core here. The caller (`installCommand`)
|
|
271
|
+
// always calls `svc.start()` right after `svc.install()`, and start()
|
|
272
|
+
// already handles the "spawn detached so the user gets a working core
|
|
273
|
+
// without logging out" behavior. Starting it here too would race the
|
|
274
|
+
// pid-file write with start()'s own isRunning() check and leave the
|
|
275
|
+
// user with two concurrent cores fighting for the same port.
|
|
276
|
+
}
|
|
277
|
+
export function uninstall() {
|
|
278
|
+
stop();
|
|
279
|
+
deleteScheduledTask();
|
|
280
|
+
removeStartupShim();
|
|
281
|
+
// Leave NEURA_HOME/neura-core.cmd in place — it's harmless and is
|
|
282
|
+
// re-written on next `neura install`. Don't delete NEURA_HOME itself;
|
|
283
|
+
// that's the user's config + memory store.
|
|
58
284
|
}
|
|
59
285
|
export function start() {
|
|
60
|
-
|
|
61
|
-
|
|
286
|
+
if (isRunning())
|
|
287
|
+
return;
|
|
288
|
+
spawnDetachedCore(getShimContext());
|
|
62
289
|
}
|
|
63
290
|
export function stop() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const result = execSync(`sc query ${SERVICE_NAME}`, {
|
|
72
|
-
encoding: 'utf-8',
|
|
73
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
74
|
-
});
|
|
75
|
-
if (result.includes('STOPPED'))
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return; // Service doesn't exist or query failed — treat as stopped
|
|
80
|
-
}
|
|
81
|
-
execSync('timeout /t 1 /nobreak >nul 2>&1', { stdio: 'ignore' });
|
|
291
|
+
const ctx = getShimContext();
|
|
292
|
+
if (!existsSync(ctx.pidFile))
|
|
293
|
+
return;
|
|
294
|
+
let pid;
|
|
295
|
+
try {
|
|
296
|
+
pid = parseInt(readFileSync(ctx.pidFile, 'utf-8').trim(), 10);
|
|
82
297
|
}
|
|
298
|
+
catch {
|
|
299
|
+
// Corrupt pid file — nothing to kill, just clean the stale file.
|
|
300
|
+
unlinkPidFile(ctx.pidFile);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (!pid || Number.isNaN(pid)) {
|
|
304
|
+
unlinkPidFile(ctx.pidFile);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
// `taskkill /T` walks the child-process tree so we catch both the
|
|
308
|
+
// launcher cmd.exe and the Node process it spawned. `/F` is force —
|
|
309
|
+
// we don't wait for graceful shutdown; the core's SIGTERM handlers
|
|
310
|
+
// wouldn't fire anyway because Node on Windows can't receive
|
|
311
|
+
// SIGTERM. The core's on('exit') pid-file cleanup also won't run
|
|
312
|
+
// under /F, which is why we unlink the pid file ourselves below.
|
|
313
|
+
try {
|
|
314
|
+
execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore' });
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
// Process already dead; fall through.
|
|
318
|
+
}
|
|
319
|
+
unlinkPidFile(ctx.pidFile);
|
|
83
320
|
}
|
|
84
321
|
export function restart() {
|
|
85
|
-
requireElevation();
|
|
86
322
|
stop();
|
|
87
|
-
waitForStopped();
|
|
88
323
|
start();
|
|
89
324
|
}
|
|
90
325
|
export function getLogPath() {
|
|
@@ -100,4 +335,12 @@ export default {
|
|
|
100
335
|
restart,
|
|
101
336
|
getLogPath,
|
|
102
337
|
};
|
|
338
|
+
// Exported for tests only.
|
|
339
|
+
export const __test__ = {
|
|
340
|
+
TASK_NAME,
|
|
341
|
+
getShimContext,
|
|
342
|
+
getStartupShimPath,
|
|
343
|
+
renderCmdShim,
|
|
344
|
+
escapeCmd,
|
|
345
|
+
};
|
|
103
346
|
//# sourceMappingURL=windows.js.map
|