@amaster.ai/mirrorx-device-client 0.0.0-stage → 0.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/LICENSE +172 -0
- package/README.md +487 -2
- package/dist/codebuddy-auth.d.ts +2 -0
- package/dist/codebuddy-auth.d.ts.map +1 -0
- package/dist/codebuddy-auth.js +80 -0
- package/dist/codebuddy-auth.js.map +1 -0
- package/dist/codebuddy-locks.d.ts +12 -0
- package/dist/codebuddy-locks.d.ts.map +1 -0
- package/dist/codebuddy-locks.js +101 -0
- package/dist/codebuddy-locks.js.map +1 -0
- package/dist/codebuddy-redaction.d.ts +3 -0
- package/dist/codebuddy-redaction.d.ts.map +1 -0
- package/dist/codebuddy-redaction.js +31 -0
- package/dist/codebuddy-redaction.js.map +1 -0
- package/dist/codebuddy-runtime.d.ts +82 -0
- package/dist/codebuddy-runtime.d.ts.map +1 -0
- package/dist/codebuddy-runtime.js +285 -0
- package/dist/codebuddy-runtime.js.map +1 -0
- package/dist/codebuddy-session.d.ts +13 -0
- package/dist/codebuddy-session.d.ts.map +1 -0
- package/dist/codebuddy-session.js +73 -0
- package/dist/codebuddy-session.js.map +1 -0
- package/dist/codebuddy-tool-gate.d.ts +4 -0
- package/dist/codebuddy-tool-gate.d.ts.map +1 -0
- package/dist/codebuddy-tool-gate.js +10 -0
- package/dist/codebuddy-tool-gate.js.map +1 -0
- package/dist/codex-runtime.d.ts +6 -0
- package/dist/codex-runtime.d.ts.map +1 -0
- package/dist/codex-runtime.js +24 -0
- package/dist/codex-runtime.js.map +1 -0
- package/dist/computer.d.ts +25 -0
- package/dist/computer.d.ts.map +1 -0
- package/dist/computer.js +161 -0
- package/dist/computer.js.map +1 -0
- package/dist/configuration-lifetime.d.ts +15 -0
- package/dist/configuration-lifetime.d.ts.map +1 -0
- package/dist/configuration-lifetime.js +143 -0
- package/dist/configuration-lifetime.js.map +1 -0
- package/dist/device-id.d.ts +7 -0
- package/dist/device-id.d.ts.map +1 -0
- package/dist/device-id.js +84 -0
- package/dist/device-id.js.map +1 -0
- package/dist/index.d.ts +209 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1459 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +126 -0
- package/dist/main.js.map +1 -0
- package/dist/managed-workspace.d.ts +33 -0
- package/dist/managed-workspace.d.ts.map +1 -0
- package/dist/managed-workspace.js +149 -0
- package/dist/managed-workspace.js.map +1 -0
- package/dist/pi-runtime.d.ts +24 -0
- package/dist/pi-runtime.d.ts.map +1 -0
- package/dist/pi-runtime.js +156 -0
- package/dist/pi-runtime.js.map +1 -0
- package/dist/readiness.d.ts +12 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +66 -0
- package/dist/readiness.js.map +1 -0
- package/dist/resource-lock.d.ts +11 -0
- package/dist/resource-lock.d.ts.map +1 -0
- package/dist/resource-lock.js +74 -0
- package/dist/resource-lock.js.map +1 -0
- package/dist/session-index.d.ts +41 -0
- package/dist/session-index.d.ts.map +1 -0
- package/dist/session-index.js +99 -0
- package/dist/session-index.js.map +1 -0
- package/dist/user-service.d.ts +21 -0
- package/dist/user-service.d.ts.map +1 -0
- package/dist/user-service.js +224 -0
- package/dist/user-service.js.map +1 -0
- package/package.json +46 -4
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { lstat, open, realpath, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { acquireCodebuddyLocks } from "./codebuddy-locks.js";
|
|
6
|
+
export const codebuddySessionPattern = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/;
|
|
7
|
+
const digest = (value) => createHash("sha256").update(value).digest("hex");
|
|
8
|
+
export const codebuddySessionLockKeys = (cwd, identity, sessionId) => [digest(`workspace:${cwd}`), digest(`session:${identity}:${sessionId}`)];
|
|
9
|
+
async function privateDirectory(directory) {
|
|
10
|
+
const entry = await lstat(directory);
|
|
11
|
+
if (!entry.isDirectory() || entry.isSymbolicLink() || (entry.mode & 0o022)
|
|
12
|
+
|| (process.getuid && entry.uid !== process.getuid()))
|
|
13
|
+
throw new Error("Unsafe WorkBuddy session directory");
|
|
14
|
+
}
|
|
15
|
+
async function readIdentity(directory) {
|
|
16
|
+
const file = await open(path.join(directory, ".identity.json"), constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
17
|
+
try {
|
|
18
|
+
const entry = await file.stat();
|
|
19
|
+
if (!entry.isFile() || entry.nlink !== 1 || entry.size > 16_384 || (entry.mode & 0o077)
|
|
20
|
+
|| (process.getuid && entry.uid !== process.getuid()))
|
|
21
|
+
throw new Error("Unsafe WorkBuddy session identity");
|
|
22
|
+
return await file.readFile("utf8");
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
await file.close();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Non-secret identity is retained even when the private launch profile is reclaimed. */
|
|
29
|
+
export async function rememberCodebuddySession(stateRoot, directory, identity) {
|
|
30
|
+
await privateDirectory(path.join(stateRoot, "codebuddy-sessions"));
|
|
31
|
+
await privateDirectory(directory);
|
|
32
|
+
try {
|
|
33
|
+
await writeFile(path.join(directory, ".identity.json"), identity, { flag: "wx", mode: 0o600 });
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error.code !== "EEXIST")
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
if (await readIdentity(directory) !== identity)
|
|
40
|
+
throw new Error("WorkBuddy session identity changed");
|
|
41
|
+
}
|
|
42
|
+
/** Explicit destructive local recovery: never restores a stale checkpoint or changes a command receipt. */
|
|
43
|
+
export async function discardCodebuddySession(input) {
|
|
44
|
+
if (process.platform === "win32")
|
|
45
|
+
throw new Error("WorkBuddy session reset is not supported on Windows");
|
|
46
|
+
if (!input.confirmedStopped)
|
|
47
|
+
throw new Error("Confirm the original tools have stopped and reconcile the command before discarding its native session");
|
|
48
|
+
if (!/^[a-f0-9]{64}$/.test(input.sessionKey) || !codebuddySessionPattern.test(input.sessionId))
|
|
49
|
+
throw new Error("Invalid WorkBuddy session identity");
|
|
50
|
+
const root = await realpath(input.stateRoot), parent = path.join(root, "codebuddy-sessions"), directory = path.join(parent, input.sessionKey);
|
|
51
|
+
await privateDirectory(parent);
|
|
52
|
+
await privateDirectory(directory);
|
|
53
|
+
const identity = await readIdentity(directory);
|
|
54
|
+
if (digest(identity) !== input.sessionKey)
|
|
55
|
+
throw new Error("WorkBuddy session identity changed");
|
|
56
|
+
const scope = JSON.parse(identity);
|
|
57
|
+
if (scope.companyId !== input.companyId || typeof scope.cwd !== "string" || await realpath(scope.cwd) !== scope.cwd)
|
|
58
|
+
throw new Error("WorkBuddy session does not belong to this company and workspace");
|
|
59
|
+
const locks = await acquireCodebuddyLocks(root, codebuddySessionLockKeys(scope.cwd, identity, input.sessionId));
|
|
60
|
+
try {
|
|
61
|
+
const marker = path.join(directory, input.sessionId + ".unsettled");
|
|
62
|
+
const entry = await lstat(marker);
|
|
63
|
+
if (!entry.isFile() || entry.isSymbolicLink() || (process.getuid && entry.uid !== process.getuid()))
|
|
64
|
+
throw new Error("Unsafe WorkBuddy session fence");
|
|
65
|
+
// A crash between removals leaves the fence in place. Never expose the old checkpoint again.
|
|
66
|
+
await rm(path.join(directory, input.sessionId + ".jsonl"), { force: true });
|
|
67
|
+
await rm(marker);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
await locks.release();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=codebuddy-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codebuddy-session.js","sourceRoot":"","sources":["../src/codebuddy-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,CAAC,MAAM,uBAAuB,GAAG,mCAAmC,CAAC;AAC3E,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAAW,EAAE,QAAgB,EAAE,SAAiB,EAAE,EAAE,CAC3F,CAAC,MAAM,CAAC,aAAa,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,WAAW,QAAQ,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;AAE3E,KAAK,UAAU,gBAAgB,CAAC,SAAiB;IAC/C,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;WACrE,CAAC,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;AACjH,CAAC;AACD,KAAK,UAAU,YAAY,CAAC,SAAiB;IAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IAC3G,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;eAClF,CAAC,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAC9G,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;YAAS,CAAC;QAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IAAC,CAAC;AACnC,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,SAAiB,EAAE,SAAiB,EAAE,QAAgB;IACnG,MAAM,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACnE,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAClC,IAAI,CAAC;QAAC,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAAC,CAAC;IACvG,OAAO,KAAK,EAAE,CAAC;QAAC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IAAC,CAAC;IACtF,IAAI,MAAM,YAAY,CAAC,SAAS,CAAC,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;AACxG,CAAC;AAED,2GAA2G;AAC3G,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAE7C;IACC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzG,IAAI,CAAC,KAAK,CAAC,gBAAgB;QAAE,MAAM,IAAI,KAAK,CAAC,wGAAwG,CAAC,CAAC;IACvJ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACtJ,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9I,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAAC,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACjG,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACxM,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,IAAI,EAAE,wBAAwB,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAChH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,YAAY,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACvJ,6FAA6F;QAC7F,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5E,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;YAAS,CAAC;QAAC,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;IAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codebuddy-tool-gate.d.ts","sourceRoot":"","sources":["../src/codebuddy-tool-gate.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,UAMhD;AACD,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,WAA+C,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A fixed PreToolUse gate denies every tool outside this run's exact MCP names. */
|
|
2
|
+
export function codebuddyToolGate(tools) {
|
|
3
|
+
return `const allowed=new Set(${JSON.stringify(tools)});let input='',oversized=false;
|
|
4
|
+
process.stdin.setEncoding('utf8');process.stdin.on('data',chunk=>{if(input.length+chunk.length>8388608){oversized=true;input='';}else if(!oversized)input+=chunk;});
|
|
5
|
+
process.stdin.on('end',()=>{let allow=false;try{const value=JSON.parse(input);allow=!oversized&&allowed.has(value.tool_name);}catch{}
|
|
6
|
+
process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:'PreToolUse',permissionDecision:allow?'allow':'deny',permissionDecisionReason:allow?'Authorized run MCP tool':'Tool is outside this run MCP allowlist'}}));});
|
|
7
|
+
`;
|
|
8
|
+
}
|
|
9
|
+
export const shellQuote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
|
|
10
|
+
//# sourceMappingURL=codebuddy-tool-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codebuddy-tool-gate.js","sourceRoot":"","sources":["../src/codebuddy-tool-gate.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAAC,KAAe;IAC/C,OAAO,yBAAyB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;;;;CAItD,CAAC;AACF,CAAC;AACD,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Codex discovers companion binaries beside argv[0], including code-mode-host.
|
|
2
|
+
* Resolve on the device, whose installation/PATH can differ from the server's.
|
|
3
|
+
* Only the selected Codex executable is canonicalized; transfer commands stay intact.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveCodexExecutable(command: string, cwd: string, env: NodeJS.ProcessEnv): Promise<string>;
|
|
6
|
+
//# sourceMappingURL=codex-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-runtime.d.ts","sourceRoot":"","sources":["../src/codex-runtime.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAalH"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { access, constants, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/** Codex discovers companion binaries beside argv[0], including code-mode-host.
|
|
4
|
+
* Resolve on the device, whose installation/PATH can differ from the server's.
|
|
5
|
+
* Only the selected Codex executable is canonicalized; transfer commands stay intact.
|
|
6
|
+
*/
|
|
7
|
+
export async function resolveCodexExecutable(command, cwd, env) {
|
|
8
|
+
if (!/^codex(?:\.exe)?$/i.test(path.basename(command)))
|
|
9
|
+
return command;
|
|
10
|
+
const candidates = command.includes("/") || command.includes("\\")
|
|
11
|
+
? [path.resolve(cwd, command)]
|
|
12
|
+
: (env.PATH ?? "").split(path.delimiter).map(directory => path.resolve(cwd, directory, command));
|
|
13
|
+
for (const candidate of candidates) {
|
|
14
|
+
try {
|
|
15
|
+
if (!(await stat(candidate)).isFile())
|
|
16
|
+
continue;
|
|
17
|
+
await access(candidate, constants.X_OK);
|
|
18
|
+
return await realpath(candidate);
|
|
19
|
+
}
|
|
20
|
+
catch { /* Preserve normal spawn failure if no installed executable resolves. */ }
|
|
21
|
+
}
|
|
22
|
+
return command;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=codex-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-runtime.js","sourceRoot":"","sources":["../src/codex-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,OAAe,EAAE,GAAW,EAAE,GAAsB;IAC/F,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IACvE,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QAChE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IACnG,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE;gBAAE,SAAS;YAChD,MAAM,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;YACxC,OAAO,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC,CAAC,wEAAwE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare function pairMirrorxComputer(input: {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
code: string;
|
|
4
|
+
}): Promise<{
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
principalId: string;
|
|
7
|
+
companyId: string;
|
|
8
|
+
computerId: string;
|
|
9
|
+
deviceProfileId: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function listMirrorxComputers(): Promise<{
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
principalId: string;
|
|
14
|
+
companyId: string;
|
|
15
|
+
computerId: string;
|
|
16
|
+
deviceProfileId: string;
|
|
17
|
+
}[]>;
|
|
18
|
+
export declare function runMirrorxComputer(input: {
|
|
19
|
+
computerId: string;
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
drainSignal?: AbortSignal;
|
|
23
|
+
onState?(state: string): void;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=computer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"computer.d.ts","sourceRoot":"","sources":["../src/computer.ts"],"names":[],"mappings":"AA4BA,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;aAjBjD,MAAM;iBAAe,MAAM;eAAa,MAAM;gBAAc,MAAM;qBAAmB,MAAM;GA+B3H;AAYD,wBAAsB,oBAAoB;aA3CT,MAAM;iBAAe,MAAM;eAAa,MAAM;gBAAc,MAAM;qBAAmB,MAAM;KA2CW;AACvI,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,iBAyEtK"}
|
package/dist/computer.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/** Presence belongs to the computer. Employee execution is started only for explicit enrollments. */
|
|
2
|
+
import manifest from "../package.json" with { type: "json" };
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { access, chmod, constants, lstat, mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
8
|
+
import { canonicalServerOrigin, mirrorxIdentityHome, readOrCreateDeviceProfile } from "./device-id.js";
|
|
9
|
+
import { acquireDeviceResourceLock } from "./resource-lock.js";
|
|
10
|
+
import { connectMirrorxComputerIntent, DeviceRequestError, listMirrorxDevicePairings, restoreMirrorxComputerEmployee, runMirrorxDevice } from "./index.js";
|
|
11
|
+
const root = () => path.join(mirrorxIdentityHome(), "computers");
|
|
12
|
+
const configPath = (config) => path.join(root(), createHash("sha256").update(JSON.stringify([config.baseUrl, config.principalId, config.companyId, config.computerId])).digest("hex") + ".json");
|
|
13
|
+
async function rpc(baseUrl, route, options = {}) {
|
|
14
|
+
const response = await fetch(new URL(route, canonicalServerOrigin(baseUrl)), { method: options.body === undefined ? "GET" : "POST", redirect: "error",
|
|
15
|
+
headers: { "content-type": "application/json", "x-mirrorx-device-protocol-min": String(manifest.mirrorxDeviceProtocol.min),
|
|
16
|
+
"x-mirrorx-device-protocol-max": String(manifest.mirrorxDeviceProtocol.max), ...(options.token ? { "x-mirrorx-device-token": options.token } : {}) },
|
|
17
|
+
...(options.body === undefined ? {} : { body: JSON.stringify(options.body) }), signal: AbortSignal.any([AbortSignal.timeout(10_000), ...(options.signal ? [options.signal] : [])]) });
|
|
18
|
+
if (!response.ok)
|
|
19
|
+
throw new DeviceRequestError(response.status, options.body === undefined ? "GET" : "POST", route);
|
|
20
|
+
return response.status === 204 ? undefined : await response.json();
|
|
21
|
+
}
|
|
22
|
+
async function safeDirectory() {
|
|
23
|
+
await mkdir(root(), { recursive: true, mode: 0o700 });
|
|
24
|
+
const info = await lstat(root());
|
|
25
|
+
if (!info.isDirectory() || info.isSymbolicLink() || process.getuid && info.uid !== process.getuid())
|
|
26
|
+
throw new Error("Computer configuration ownership is invalid");
|
|
27
|
+
await chmod(root(), 0o700);
|
|
28
|
+
}
|
|
29
|
+
export async function pairMirrorxComputer(input) {
|
|
30
|
+
const baseUrl = canonicalServerOrigin(input.baseUrl);
|
|
31
|
+
const context = await rpc(baseUrl, "/api/computers/pairing/context", { body: { pairingCode: input.code } });
|
|
32
|
+
const deviceProfileId = await readOrCreateDeviceProfile(baseUrl, context.principalId);
|
|
33
|
+
await safeDirectory();
|
|
34
|
+
const paired = await rpc(baseUrl, "/api/computers/pairing/claim", { body: { pairingCode: input.code, deviceProfileId, name: os.hostname().slice(0, 60) || "Computer" } });
|
|
35
|
+
if (paired.principalId !== context.principalId || paired.computer.companyId !== context.companyId || paired.computer.deviceProfileId !== deviceProfileId || !/^mxcomputer_[A-Za-z0-9_-]{43}$/.test(paired.token))
|
|
36
|
+
throw new Error("Computer pairing response identity mismatch");
|
|
37
|
+
const config = { baseUrl, principalId: context.principalId, companyId: context.companyId, computerId: paired.computer.id, deviceProfileId, computerToken: paired.token };
|
|
38
|
+
const temporary = `${configPath(config)}.${randomUUID()}.tmp`;
|
|
39
|
+
await writeFile(temporary, JSON.stringify(config), { flag: "wx", mode: 0o600 });
|
|
40
|
+
await rename(temporary, configPath(config));
|
|
41
|
+
const { computerToken: _token, ...summary } = config;
|
|
42
|
+
return summary;
|
|
43
|
+
}
|
|
44
|
+
async function configurations() {
|
|
45
|
+
const files = await readdir(root()).catch(error => { if (error.code === "ENOENT")
|
|
46
|
+
return []; throw error; });
|
|
47
|
+
return Promise.all(files.filter(file => /^[a-f0-9]{64}\.json$/.test(file)).map(async (file) => {
|
|
48
|
+
const full = path.join(root(), file), info = await lstat(full);
|
|
49
|
+
if (!info.isFile() || info.isSymbolicLink() || process.getuid && info.uid !== process.getuid())
|
|
50
|
+
throw new Error("Computer configuration ownership is invalid");
|
|
51
|
+
await chmod(full, 0o600);
|
|
52
|
+
const config = JSON.parse(await readFile(full, "utf8"));
|
|
53
|
+
if (configPath(config) !== full || canonicalServerOrigin(config.baseUrl) !== config.baseUrl)
|
|
54
|
+
throw new Error("Computer configuration identity is invalid");
|
|
55
|
+
return config;
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
export async function listMirrorxComputers() { return (await configurations()).map(({ computerToken: _token, ...config }) => config); }
|
|
59
|
+
export async function runMirrorxComputer(input) {
|
|
60
|
+
const config = (await configurations()).find(row => row.computerId === input.computerId && row.baseUrl === canonicalServerOrigin(input.baseUrl));
|
|
61
|
+
if (!config)
|
|
62
|
+
throw new Error("Pair this computer first");
|
|
63
|
+
const instanceId = randomUUID();
|
|
64
|
+
const unlock = await acquireDeviceResourceLock({ ...config, executorKind: "computer", bindingId: config.computerId }, instanceId);
|
|
65
|
+
const children = new Map(), stopped = new Set();
|
|
66
|
+
const abort = new AbortController(), drain = new AbortController();
|
|
67
|
+
const signal = AbortSignal.any([abort.signal, ...(input.signal ? [input.signal] : [])]);
|
|
68
|
+
const drainSignal = AbortSignal.any([drain.signal, ...(input.drainSignal ? [input.drainSignal] : [])]);
|
|
69
|
+
let childFailed = false;
|
|
70
|
+
const capable = process.platform === "darwin" && await access("/usr/bin/sandbox-exec", constants.X_OK).then(() => true, () => false);
|
|
71
|
+
const startEmployees = async () => {
|
|
72
|
+
for (const employee of await listMirrorxDevicePairings({ expectedBaseUrl: config.baseUrl })) {
|
|
73
|
+
if (employee.computerId !== config.computerId || employee.principalId !== config.principalId || employee.companyId !== config.companyId || children.has(employee.bindingId) || stopped.has(employee.bindingId))
|
|
74
|
+
continue;
|
|
75
|
+
const task = (async () => {
|
|
76
|
+
try {
|
|
77
|
+
await runMirrorxDevice({ executorKind: employee.executorKind, bindingId: employee.bindingId, companyId: config.companyId, expectedBaseUrl: config.baseUrl, signal, drainSignal });
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (!(error instanceof DeviceRequestError) || error.code !== "DEVICE_SESSION_EXPIRED" || signal.aborted || drainSignal.aborted)
|
|
81
|
+
throw error;
|
|
82
|
+
await restoreMirrorxComputerEmployee({ ...config, bindingId: employee.bindingId, executorKind: employee.executorKind, signal });
|
|
83
|
+
await runMirrorxDevice({ executorKind: employee.executorKind, bindingId: employee.bindingId, companyId: config.companyId, expectedBaseUrl: config.baseUrl, signal, drainSignal });
|
|
84
|
+
}
|
|
85
|
+
})().catch(() => { if (!signal.aborted) {
|
|
86
|
+
childFailed = true;
|
|
87
|
+
input.onState?.("An employee needs attention. Check its connection details before retrying.");
|
|
88
|
+
} }).finally(() => { children.delete(employee.bindingId); stopped.add(employee.bindingId); });
|
|
89
|
+
children.set(employee.bindingId, task);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
let heartbeatFailure;
|
|
93
|
+
let heartbeatPending = Promise.resolve();
|
|
94
|
+
let beating = false;
|
|
95
|
+
const heartbeat = async () => {
|
|
96
|
+
try {
|
|
97
|
+
await rpc(config.baseUrl, "/api/computers/heartbeat", { token: config.computerToken, signal, body: { instanceId, platform: `${process.platform}-${process.arch}`, clientVersion: manifest.version, capabilities: capable ? { managedWorkspaces: "seatbelt" } : {} } });
|
|
98
|
+
input.onState?.("Computer online");
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error instanceof DeviceRequestError && [401, 403, 409, 426].includes(error.status)) {
|
|
102
|
+
heartbeatFailure = error;
|
|
103
|
+
abort.abort();
|
|
104
|
+
}
|
|
105
|
+
else if (!signal.aborted)
|
|
106
|
+
input.onState?.("Computer disconnected; retrying");
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
// Keep the presence lease alive even while enrollment or an employee drains.
|
|
110
|
+
const timer = setInterval(() => {
|
|
111
|
+
if (beating || signal.aborted)
|
|
112
|
+
return;
|
|
113
|
+
beating = true;
|
|
114
|
+
heartbeatPending = heartbeat().finally(() => { beating = false; });
|
|
115
|
+
}, 15_000);
|
|
116
|
+
try {
|
|
117
|
+
await heartbeat();
|
|
118
|
+
while (!signal.aborted && !drainSignal.aborted) {
|
|
119
|
+
try {
|
|
120
|
+
await startEmployees();
|
|
121
|
+
const intents = await rpc(config.baseUrl, "/api/computers/intents", { token: config.computerToken, signal });
|
|
122
|
+
for (const intent of intents) {
|
|
123
|
+
if (signal.aborted || drainSignal.aborted)
|
|
124
|
+
break;
|
|
125
|
+
try {
|
|
126
|
+
await connectMirrorxComputerIntent({ ...config, enrollmentIntentId: intent.enrollmentIntentId, signal });
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
input.onState?.("Employee creation needs attention; check the request in MirrorX.");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
await startEmployees();
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (signal.aborted)
|
|
136
|
+
break;
|
|
137
|
+
if (error instanceof DeviceRequestError && [401, 403, 409, 426].includes(error.status)) {
|
|
138
|
+
abort.abort();
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
input.onState?.("Computer disconnected; retrying");
|
|
142
|
+
}
|
|
143
|
+
await delay(3_000, undefined, { signal: AbortSignal.any([signal, drainSignal]) }).catch(() => undefined);
|
|
144
|
+
}
|
|
145
|
+
drain.abort();
|
|
146
|
+
await Promise.allSettled(children.values());
|
|
147
|
+
if (heartbeatFailure)
|
|
148
|
+
throw heartbeatFailure;
|
|
149
|
+
if (childFailed)
|
|
150
|
+
throw new Error("One or more employees need attention; inspect their connection details");
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
drain.abort();
|
|
154
|
+
await Promise.allSettled(children.values());
|
|
155
|
+
clearInterval(timer);
|
|
156
|
+
await heartbeatPending;
|
|
157
|
+
await rpc(config.baseUrl, "/api/computers/disconnect", { token: config.computerToken, body: { instanceId } }).catch(() => undefined);
|
|
158
|
+
await unlock();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=computer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"computer.js","sourceRoot":"","sources":["../src/computer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
export type ConfigurationReference = {
|
|
3
|
+
configurationDirectory?: string;
|
|
4
|
+
configurationToken?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function ownConfiguration(directory: string, manualRecovery?: boolean): Promise<string>;
|
|
7
|
+
export declare function configurationGroupAlive(pid: number): boolean;
|
|
8
|
+
/** Returns false when shutdown evidence is absent; never signals a recovered PID. */
|
|
9
|
+
export declare function reclaimConfiguration(reference: ConfigurationReference, confirmedStopped?: boolean, beforeRemove?: () => Promise<void>): Promise<boolean>;
|
|
10
|
+
/** Keep EOF evidence separate from local stream destruction and from command ACK. */
|
|
11
|
+
export declare function observeConfiguration(reference: Required<ConfigurationReference>, child: ChildProcess, onError: () => void): {
|
|
12
|
+
completed(): Promise<void>;
|
|
13
|
+
retain(): void;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=configuration-lifetime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration-lifetime.d.ts","sourceRoot":"","sources":["../src/configuration-lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQvD,MAAM,MAAM,sBAAsB,GAAG;IAAE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAQtG,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAIjG;AAuBD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO5D;AAED,qFAAqF;AACrF,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,sBAAsB,EAAE,gBAAgB,UAAQ,EAAE,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CA2B5J;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,CAAC,sBAAsB,CAAC,EAAE,KAAK,EAAE,YAAY,EACnG,OAAO,EAAE,MAAM,IAAI;;;EA6CpB"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
const markerName = ".mirrorx-configuration-owner.json";
|
|
6
|
+
async function saveOwner(directory, owner) {
|
|
7
|
+
const temporary = path.join(directory, `${markerName}.${randomUUID()}.tmp`);
|
|
8
|
+
await writeFile(temporary, JSON.stringify(owner), { mode: 0o600 });
|
|
9
|
+
await rename(temporary, path.join(directory, markerName));
|
|
10
|
+
}
|
|
11
|
+
export async function ownConfiguration(directory, manualRecovery = false) {
|
|
12
|
+
const token = randomUUID();
|
|
13
|
+
await saveOwner(directory, { token, ...(manualRecovery ? { manualRecovery: true } : {}) });
|
|
14
|
+
return token;
|
|
15
|
+
}
|
|
16
|
+
// The journal is recovery input, not authority to recursively remove any path.
|
|
17
|
+
async function checkedDirectory(directory) {
|
|
18
|
+
// A trailing separator can make lstat follow the final symlink on POSIX.
|
|
19
|
+
if (!path.isAbsolute(directory) || directory !== path.resolve(directory)
|
|
20
|
+
|| !/^mirrorx-(?:pi|codebuddy)-command-[a-zA-Z0-9]{6}$/.test(path.basename(directory))
|
|
21
|
+
|| await realpath(path.dirname(directory)) !== await realpath(os.tmpdir())) {
|
|
22
|
+
throw new Error("Refusing to reclaim an unexpected Pi configuration directory");
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const entry = await lstat(directory);
|
|
26
|
+
if (!entry.isDirectory() || entry.isSymbolicLink()
|
|
27
|
+
|| (process.getuid && entry.uid !== process.getuid())) {
|
|
28
|
+
throw new Error("Refusing to reclaim an unowned Pi configuration directory");
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error.code === "ENOENT")
|
|
34
|
+
return false;
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function configurationGroupAlive(pid) {
|
|
39
|
+
try {
|
|
40
|
+
process.kill(-pid, 0);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error.code === "ESRCH")
|
|
45
|
+
return false;
|
|
46
|
+
if (error.code === "EPERM")
|
|
47
|
+
return true;
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Returns false when shutdown evidence is absent; never signals a recovered PID. */
|
|
52
|
+
export async function reclaimConfiguration(reference, confirmedStopped = false, beforeRemove) {
|
|
53
|
+
const directory = reference.configurationDirectory;
|
|
54
|
+
if (!directory)
|
|
55
|
+
return true;
|
|
56
|
+
if (!await checkedDirectory(directory))
|
|
57
|
+
return true;
|
|
58
|
+
let owner;
|
|
59
|
+
try {
|
|
60
|
+
const marker = path.join(directory, markerName);
|
|
61
|
+
const entry = await lstat(marker);
|
|
62
|
+
if (!entry.isFile() || entry.isSymbolicLink())
|
|
63
|
+
throw new Error("Invalid Pi configuration ownership marker");
|
|
64
|
+
owner = JSON.parse(await readFile(marker, "utf8"));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error.code !== "ENOENT")
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
if (reference.configurationToken && owner?.token !== reference.configurationToken) {
|
|
71
|
+
throw new Error("Pi configuration ownership does not match its journal");
|
|
72
|
+
}
|
|
73
|
+
if (!confirmedStopped) {
|
|
74
|
+
if (!reference.configurationToken || !owner)
|
|
75
|
+
return false; // Older journals need explicit operator confirmation.
|
|
76
|
+
if (owner.completed !== true) {
|
|
77
|
+
if (owner.manualRecovery)
|
|
78
|
+
return false;
|
|
79
|
+
if (process.platform === "win32" || owner.platform !== process.platform || owner.pipesEnded !== true
|
|
80
|
+
|| !Number.isSafeInteger(owner.pid) || owner.pid <= 0 || configurationGroupAlive(owner.pid))
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
await beforeRemove?.();
|
|
85
|
+
await rm(directory, { recursive: true, force: true });
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
/** Keep EOF evidence separate from local stream destruction and from command ACK. */
|
|
89
|
+
export function observeConfiguration(reference, child, onError) {
|
|
90
|
+
const owner = { ...(path.basename(reference.configurationDirectory).startsWith("mirrorx-codebuddy-") ? { manualRecovery: true } : {}), token: reference.configurationToken, ...(child.pid ? { pid: child.pid } : {}), platform: process.platform };
|
|
91
|
+
let exited = false, retained = false, timer;
|
|
92
|
+
let writes = Promise.resolve();
|
|
93
|
+
const save = () => {
|
|
94
|
+
const snapshot = { ...owner };
|
|
95
|
+
writes = writes.then(() => saveOwner(reference.configurationDirectory, snapshot));
|
|
96
|
+
void writes.catch(onError);
|
|
97
|
+
return writes;
|
|
98
|
+
};
|
|
99
|
+
const attempt = async () => {
|
|
100
|
+
try {
|
|
101
|
+
await writes;
|
|
102
|
+
if (await reclaimConfiguration(reference)) {
|
|
103
|
+
if (timer)
|
|
104
|
+
clearInterval(timer);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
if (timer)
|
|
109
|
+
clearInterval(timer);
|
|
110
|
+
onError();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const ended = () => {
|
|
114
|
+
if (owner.pipesEnded || !exited || !child.stdout?.readableEnded || !child.stderr?.readableEnded)
|
|
115
|
+
return;
|
|
116
|
+
owner.pipesEnded = true;
|
|
117
|
+
void save().then(() => { if (retained)
|
|
118
|
+
void attempt(); }, () => undefined);
|
|
119
|
+
};
|
|
120
|
+
child.once("exit", () => { exited = true; ended(); });
|
|
121
|
+
child.stdout?.once("end", ended);
|
|
122
|
+
child.stderr?.once("end", ended);
|
|
123
|
+
return {
|
|
124
|
+
async completed() { owner.completed = true; await save(); },
|
|
125
|
+
retain() {
|
|
126
|
+
retained = true;
|
|
127
|
+
// A live desktop can observe late EOF without keeping a closing CLI alive.
|
|
128
|
+
// Never synthesize EOF by destroying a pipe whose descendant is still using it.
|
|
129
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
130
|
+
stream?.removeAllListeners("data");
|
|
131
|
+
stream?.resume();
|
|
132
|
+
const socket = stream;
|
|
133
|
+
socket?.unref?.();
|
|
134
|
+
}
|
|
135
|
+
if (process.platform !== "win32") {
|
|
136
|
+
timer = setInterval(() => { void attempt(); }, 1_000);
|
|
137
|
+
timer.unref();
|
|
138
|
+
void attempt();
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=configuration-lifetime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration-lifetime.js","sourceRoot":"","sources":["../src/configuration-lifetime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function canonicalServerOrigin(value: string): string;
|
|
2
|
+
export declare function mirrorxIdentityHome(): string;
|
|
3
|
+
export declare function identityNamespace(server: string, principalId: string): string;
|
|
4
|
+
export declare function readOrCreateDeviceProfile(server: string, principalId: string, retainedProfileId?: string): Promise<string>;
|
|
5
|
+
/** @deprecated Retained export for older embedders. Never reads OS identifiers. */
|
|
6
|
+
export declare function readDesktopDeviceId(): Promise<undefined>;
|
|
7
|
+
//# sourceMappingURL=device-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-id.d.ts","sourceRoot":"","sources":["../src/device-id.ts"],"names":[],"mappings":"AAMA,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO3D;AACD,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AACD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAG7E;AACD,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuChI;AACD,mFAAmF;AACnF,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,SAAS,CAAC,CAAsB"}
|