@stim-cli/server 1.20.2 → 1.22.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 +785 -276
- package/dist/{protocol-CjUChTWJ.mjs → protocol-ZY9j2h32.mjs} +1038 -79
- package/dist/protocol.mjs +1 -1
- package/dist/protocol.schema.json +2760 -122
- package/dist/stim-frames/ProcessStream.swift +2 -1
- package/dist/stim-frames/SimulatorHID.swift +33 -7
- package/dist/stim-frames/SimulatorPosture.swift +0 -6
- package/dist/{stim-server-FWcuNpW7.mjs → stim-server-BKcEacbU.mjs} +4894 -2942
- package/dist/stim-server.mjs +1 -1
- package/dist/stim-service-runner.d.mts +13 -0
- package/dist/stim-service-runner.mjs +96 -0
- package/package.json +8 -3
- package/dist/stats-read.d.mts +0 -1
- package/dist/stats-read.mjs +0 -46
package/dist/stim-server.mjs
CHANGED
|
@@ -4,6 +4,6 @@ const encode = (version) => version.split(".", 3).reduce((sum, part) => sum * 1e
|
|
|
4
4
|
if (encode(process.versions.node) < encode("22.12.0")) {
|
|
5
5
|
process.stderr.write(`STIM_NODE_UNSUPPORTED: stim-server needs Node 22.12.0 or later; this is Node ${process.versions.node} at ${process.execPath}. To run stim-server where a project pins an older Node, see https://stim.appandflow.com/docs/requirements#older-node-pins\n`);
|
|
6
6
|
process.exitCode = 1;
|
|
7
|
-
} else import("./stim-server-
|
|
7
|
+
} else import("./stim-server-BKcEacbU.mjs");
|
|
8
8
|
//#endregion
|
|
9
9
|
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/service-runner.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The Windows supervisor of `stim-server service`. It imports nothing but Node, so `install` can copy this one file
|
|
4
|
+
* beside the service and it keeps working whatever server version the service runs.
|
|
5
|
+
*/
|
|
6
|
+
interface SupervisorState {
|
|
7
|
+
supervisor: number;
|
|
8
|
+
server: number | null;
|
|
9
|
+
runs: number;
|
|
10
|
+
lastExitCode: string | null;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { SupervisorState };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { setTimeout } from "node:timers/promises";
|
|
5
|
+
//#region src/service-runner.ts
|
|
6
|
+
function program(file) {
|
|
7
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
8
|
+
const value = parsed && typeof parsed === "object" ? { ...parsed } : {};
|
|
9
|
+
const argv = Array.isArray(value.programArguments) ? value.programArguments.filter((entry) => typeof entry === "string") : [];
|
|
10
|
+
if (value.managed !== true || !argv.length || typeof value.logPath !== "string") throw new Error(`${file} is not a service file install wrote.`);
|
|
11
|
+
const environment = {};
|
|
12
|
+
if (value.environment && typeof value.environment === "object") {
|
|
13
|
+
for (const [name, entry] of Object.entries(value.environment)) if (typeof entry === "string") environment[name] = entry;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
argv,
|
|
17
|
+
environment,
|
|
18
|
+
workingDirectory: typeof value.workingDirectory === "string" ? value.workingDirectory : "",
|
|
19
|
+
logPath: value.logPath
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async function supervise(file, statePath, throttleMs) {
|
|
23
|
+
let runs = 0;
|
|
24
|
+
let lastExitCode = null;
|
|
25
|
+
const log = (line) => {
|
|
26
|
+
try {
|
|
27
|
+
appendFileSync(join(dirname(statePath), "supervisor.log"), `${(/* @__PURE__ */ new Date()).toISOString()} ${line}\n`);
|
|
28
|
+
} catch {}
|
|
29
|
+
};
|
|
30
|
+
const record = (server) => {
|
|
31
|
+
const state = {
|
|
32
|
+
supervisor: process.pid,
|
|
33
|
+
server,
|
|
34
|
+
runs,
|
|
35
|
+
lastExitCode
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
mkdirSync(dirname(statePath), { recursive: true });
|
|
39
|
+
const partial = `${statePath}.${process.pid}.tmp`;
|
|
40
|
+
writeFileSync(partial, `${JSON.stringify(state)}\n`);
|
|
41
|
+
renameSync(partial, statePath);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
log(`could not record ${statePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
record(null);
|
|
47
|
+
for (;;) {
|
|
48
|
+
try {
|
|
49
|
+
lastExitCode = await runOnce(file, (server) => {
|
|
50
|
+
runs += 1;
|
|
51
|
+
record(server);
|
|
52
|
+
});
|
|
53
|
+
} catch (error) {
|
|
54
|
+
lastExitCode = `supervisor: ${error instanceof Error ? error.message : String(error)}`;
|
|
55
|
+
log(lastExitCode);
|
|
56
|
+
}
|
|
57
|
+
record(null);
|
|
58
|
+
await setTimeout(throttleMs);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Starts the server once from the service file and resolves with how it ended. */
|
|
62
|
+
async function runOnce(file, started) {
|
|
63
|
+
const { argv, environment, workingDirectory, logPath } = program(file);
|
|
64
|
+
const [node = "", ...args] = argv;
|
|
65
|
+
mkdirSync(dirname(logPath), { recursive: true });
|
|
66
|
+
const out = openSync(logPath, "a");
|
|
67
|
+
const child = spawn(node, args, {
|
|
68
|
+
cwd: workingDirectory || void 0,
|
|
69
|
+
env: {
|
|
70
|
+
...process.env,
|
|
71
|
+
...environment
|
|
72
|
+
},
|
|
73
|
+
stdio: [
|
|
74
|
+
"ignore",
|
|
75
|
+
out,
|
|
76
|
+
out
|
|
77
|
+
],
|
|
78
|
+
windowsHide: true
|
|
79
|
+
});
|
|
80
|
+
closeSync(out);
|
|
81
|
+
started(child.pid ?? null);
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
child.once("error", (error) => resolve(error.message));
|
|
84
|
+
child.once("exit", (code, signal) => resolve(code === null ? `signal ${signal}` : String(code)));
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const [file, statePath, throttle] = process.argv.slice(2);
|
|
88
|
+
if (!file || !statePath || !/^\d+$/.test(throttle ?? "")) {
|
|
89
|
+
process.stderr.write("usage: stim-service-runner <service file> <state file> <restart delay seconds>\n");
|
|
90
|
+
process.exitCode = 2;
|
|
91
|
+
} else supervise(file, statePath, Number(throttle) * 1e3).catch((error) => {
|
|
92
|
+
process.stderr.write(`stim-service-runner: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
});
|
|
95
|
+
//#endregion
|
|
96
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stim-cli/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"description": "Serves Stim status to paired clients over Tailscale, and runs reload and stop for clients granted control.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,8 +28,13 @@
|
|
|
28
28
|
"./protocol.schema.json": "./dist/protocol.schema.json"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@stim-cli/
|
|
32
|
-
"stim": "1.
|
|
31
|
+
"@stim-cli/agent-driver": "^1.22.0",
|
|
32
|
+
"@stim-cli/agent-driver-agent-device": "^1.22.0",
|
|
33
|
+
"@stim-cli/cache": "^1.22.0",
|
|
34
|
+
"@stim-cli/core": "^1.22.0",
|
|
35
|
+
"@stim-cli/transport": "^1.22.0",
|
|
36
|
+
"@stim-cli/transport-tailscale": "^1.22.0",
|
|
37
|
+
"stim": "1.22.0",
|
|
33
38
|
"ws": "^8.21.3"
|
|
34
39
|
},
|
|
35
40
|
"devDependencies": {
|
package/dist/stats-read.d.mts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {}
|
package/dist/stats-read.mjs
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { sep } from "node:path";
|
|
3
|
-
import { findProjectRoot, readStatsReport, statsProjectKey } from "@stim-cli/core/state";
|
|
4
|
-
//#region src/stats-read.ts
|
|
5
|
-
let git = null;
|
|
6
|
-
let cancelled = false;
|
|
7
|
-
const cancel = () => {
|
|
8
|
-
cancelled = true;
|
|
9
|
-
git?.kill("SIGKILL");
|
|
10
|
-
};
|
|
11
|
-
process.on("SIGTERM", cancel);
|
|
12
|
-
process.on("message", (message) => {
|
|
13
|
-
if (message === "cancel") cancel();
|
|
14
|
-
});
|
|
15
|
-
function gitPath(root, args) {
|
|
16
|
-
return new Promise((resolve) => {
|
|
17
|
-
git = execFile("git", [
|
|
18
|
-
"-C",
|
|
19
|
-
root,
|
|
20
|
-
"rev-parse",
|
|
21
|
-
...args
|
|
22
|
-
], { encoding: "utf8" }, (error, stdout) => {
|
|
23
|
-
git = null;
|
|
24
|
-
const path = error ? "" : stdout.trim();
|
|
25
|
-
resolve(path ? sep === "/" ? path : path.replaceAll("/", sep) : null);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
try {
|
|
30
|
-
const root = findProjectRoot(process.cwd());
|
|
31
|
-
const commonDir = root ? await gitPath(root, ["--path-format=absolute", "--git-common-dir"]) : null;
|
|
32
|
-
const repoRoot = root && !cancelled ? await gitPath(root, ["--show-toplevel"]) : null;
|
|
33
|
-
if (!cancelled) {
|
|
34
|
-
const key = root ? statsProjectKey({
|
|
35
|
-
root,
|
|
36
|
-
commonDir,
|
|
37
|
-
repoRoot
|
|
38
|
-
}) : null;
|
|
39
|
-
const { report } = readStatsReport(key, Date.now());
|
|
40
|
-
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
41
|
-
}
|
|
42
|
-
} finally {
|
|
43
|
-
if (process.connected) process.disconnect();
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
export {};
|