@source-repo/rpc-cli 3.0.0 → 3.2.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.
@@ -0,0 +1,120 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ /**
5
+ * The dependencies a script directory is allowed to import.
6
+ *
7
+ * A script is an ordinary Node program, so sooner or later one wants something off the registry -
8
+ * a date library, a CSV parser, a driver for the thing on the other end of the serial port. Without
9
+ * this the only answer is "leave the conversation and run npm yourself", which is the wrong shape
10
+ * for a directory a model is otherwise managing end to end.
11
+ *
12
+ * **This is not a new grant.** `--scripts` already permits arbitrary Node processes, and a script
13
+ * could `child_process.exec('npm i …')` on its own. Putting it behind a second flag would be theatre;
14
+ * what a tool buys is that the model *declares* what it wants, where it can be seen in the tool log
15
+ * and in a committed package.json, rather than doing it sideways.
16
+ *
17
+ * What is worth defending against is different: **installing a package runs code from the registry
18
+ * that nobody reviewed.** So installs pass `--ignore-scripts` by default, which is the setting that
19
+ * stops a postinstall hook from being the attack. A package that genuinely needs one - anything with
20
+ * a native build - has to ask, and the asking is visible.
21
+ */
22
+ /** How long an install may take before it is abandoned. A cold cache on a slow link is the case to allow for. */
23
+ const INSTALL_BUDGET_MS = 180_000;
24
+ /**
25
+ * The manifest a script directory needs anyway.
26
+ *
27
+ * `type: module` is the load-bearing field: a `.ts` script uses `import`, and Node decides whether
28
+ * that is legal from the nearest package.json. Inside a CommonJS project there is one, it does not
29
+ * say module, and every run then prints a reparse warning that lands in the script's own output.
30
+ * `private` because this is a directory of programs, not something anybody publishes.
31
+ */
32
+ export const ensureManifest = (directory) => {
33
+ const file = join(resolve(directory), 'package.json');
34
+ if (existsSync(file))
35
+ return file;
36
+ writeFileSync(file, `${JSON.stringify({ name: 'source-rpc-scripts', private: true, type: 'module' }, null, 2)}\n`, 'utf8');
37
+ return file;
38
+ };
39
+ const manifest = (directory) => {
40
+ const file = join(resolve(directory), 'package.json');
41
+ if (!existsSync(file))
42
+ return {};
43
+ try {
44
+ return JSON.parse(readFileSync(file, 'utf8'));
45
+ }
46
+ catch {
47
+ return {};
48
+ }
49
+ };
50
+ /** What is declared, and what is actually on disk - which differ when an install failed halfway. */
51
+ export const listPackages = (directory) => Object.entries(manifest(directory).dependencies ?? {})
52
+ .map(([name, range]) => {
53
+ const installed = join(resolve(directory), 'node_modules', name, 'package.json');
54
+ let version;
55
+ try {
56
+ version = JSON.parse(readFileSync(installed, 'utf8')).version;
57
+ }
58
+ catch {
59
+ version = undefined;
60
+ }
61
+ return { name, range, ...(version ? { installed: version } : { installed: null }) };
62
+ })
63
+ .sort((a, b) => a.name.localeCompare(b.name));
64
+ /**
65
+ * A package name a shell would not reinterpret.
66
+ *
67
+ * The spec reaches npm as one argv element rather than through a shell, so this is not quoting - it
68
+ * is refusing the shapes that are not package specs at all: a flag that would change what npm does,
69
+ * or a path that would install something off disk.
70
+ */
71
+ const SAFE_SPEC = /^(@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*(@[\w.^~*><=|\s-]+)?$/i;
72
+ /**
73
+ * npm's own JavaScript entry point, run by the Node already running this - not the `npm` shim.
74
+ *
75
+ * On Windows the shim is `npm.cmd`, and since the fix for CVE-2024-27980 Node refuses to spawn a
76
+ * `.cmd` without `shell: true`. Turning the shell on would be worse than the problem: a version
77
+ * range is a legitimate part of a package spec and `>`, `<`, `|` and `^` are all permitted in one,
78
+ * which is ordinary text to `execFile` and metacharacters to `cmd.exe`. Reaching past the shim to
79
+ * the script it would have run keeps arguments as argv on every platform, which is the only version
80
+ * of this that is safe by construction rather than by quoting.
81
+ *
82
+ * Exported for the test, which checks both layouts without needing the other operating system.
83
+ */
84
+ export const npmEntryPoint = (execPath = process.execPath, platform = process.platform) => {
85
+ const here = dirname(execPath);
86
+ // Windows keeps npm beside node; the POSIX layout puts it under ../lib. Both are the standard
87
+ // install, and nvm, Volta and the Alpine image all follow whichever one their platform uses.
88
+ const candidates = platform === 'win32'
89
+ ? [join(here, 'node_modules', 'npm', 'bin', 'npm-cli.js')]
90
+ : [join(here, '..', 'lib', 'node_modules', 'npm', 'bin', 'npm-cli.js'), join(here, '..', 'npm', 'bin', 'npm-cli.js')];
91
+ return candidates.find((candidate) => existsSync(candidate));
92
+ };
93
+ const npm = (directory, args) => new Promise((done) => {
94
+ const entry = npmEntryPoint();
95
+ if (!entry)
96
+ return done({
97
+ ok: false,
98
+ output: 'npm could not be found next to this Node, so packages cannot be managed from here. Install one alongside the other, or add the dependency by hand.'
99
+ });
100
+ execFile(process.execPath, [entry, ...args], { cwd: resolve(directory), timeout: INSTALL_BUDGET_MS, maxBuffer: 8 * 1024 * 1024 }, (error, stdout, stderr) => {
101
+ const output = `${stdout}${stderr}`.trim();
102
+ if (!error)
103
+ return done({ ok: true, output });
104
+ done({ ok: false, output: output || error.message });
105
+ });
106
+ });
107
+ export const addPackage = async (directory, spec, allowInstallScripts = false) => {
108
+ if (!SAFE_SPEC.test(spec))
109
+ throw new Error(`'${spec}' is not a package name. Give it \`lodash\` or \`@scope/name@^2\`, not a flag or a path.`);
110
+ ensureManifest(directory);
111
+ // --ignore-scripts unless asked: an install hook is code from the registry running here, and it
112
+ // is the part of `npm install` that is not about files at all.
113
+ return await npm(directory, ['install', '--save', ...(allowInstallScripts ? [] : ['--ignore-scripts']), spec]);
114
+ };
115
+ export const removePackage = async (directory, name) => {
116
+ if (!SAFE_SPEC.test(name))
117
+ throw new Error(`'${name}' is not a package name.`);
118
+ return await npm(directory, ['uninstall', '--save', name]);
119
+ };
120
+ //# sourceMappingURL=packages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packages.js","sourceRoot":"","sources":["../src/packages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,171 @@
1
+ import { type RpcAuthorizer } from '@source-repo/rpc';
2
+ import { type ScriptLanguage } from './scripts.js';
3
+ /**
4
+ * Managing a node's scripts from another node, so a test hall is one place rather than a row of
5
+ * remote desktops.
6
+ *
7
+ * `--scripts` already lets a model write and run programs on the machine it is talking to. What it
8
+ * cannot do is reach the next machine along, and on a bench with a Linux box, a Windows PLC and a
9
+ * couple of devices, that is where the time goes: a remote desktop each, a file copied by hand, and
10
+ * the mistake you make on the fourth one.
11
+ *
12
+ * This is the same capability offered as an ordinary RPC namespace, which is the third time this
13
+ * codebase has reached for that shape - `bus.tap()` turned a restart-with-a-flag into a call, and
14
+ * `ConsoleService` let a browser and a model consume one surface. What it buys here is that
15
+ * everything already built for calling a peer works on it: argument checking from the contract,
16
+ * `describe()` so "which nodes can I script?" is answerable with `source-rpc peers`, the verbs
17
+ * (`source-rpc call node7 scripting.start deploy`), and the command semantics below.
18
+ *
19
+ * ## What this is
20
+ *
21
+ * **Remote code execution, offered as a method.** Not a reason to refuse to build it - it is what a
22
+ * test hall wants - but the trust model inverts and it is worth being exact about how.
23
+ *
24
+ * `--scripts` on its own is bounded by having a shell on the box: the model reaches it over stdio,
25
+ * as the user who started the server. Exposing this namespace replaces that boundary with "can
26
+ * reach the bus", and a broker relays for anyone that can open a socket to it unless told not to.
27
+ *
28
+ * So the rule is one line, and it is the strictest one that still works:
29
+ *
30
+ * **A call arriving over RPC is refused unless the caller is authenticated *and* named.**
31
+ *
32
+ * Local use does not go through this. The server that owns a node holds the object and calls its
33
+ * methods directly, which is what "local" means here - not a peer name to be compared, but no RPC
34
+ * at all. Anything that did arrive over the wire is by definition somebody else.
35
+ *
36
+ * ## Why authenticated *and* named
37
+ *
38
+ * A peer name off the wire is a claim. On socket.io a token pins the connection to one name and the
39
+ * transport drops frames claiming another, so an identity means something. **On MQTT there is no
40
+ * connection to authenticate**: `getIdentity` returns undefined unless frames are signed, so a
41
+ * network without `sign`/`verify` cannot tell anyone apart and this namespace refuses everybody.
42
+ * That is the correct failure, and it is worth knowing before wondering why the key is not working.
43
+ *
44
+ * ## Through a bus, sign
45
+ *
46
+ * The part that surprises, found by building it. **Identity is per connection, and does not survive
47
+ * a relay.** A bench authenticates to the broker; the node being scripted is connected to the broker
48
+ * too, so it has no connection to the bench and no way to learn who it is - and refuses, correctly,
49
+ * because the alternative is trusting a name that arrived through a third party.
50
+ *
51
+ * That rules out the arrangement most people would reach for first: a hall of nodes dialling one bus
52
+ * over socket.io, each expecting `--scriptable-by` to work through it. It does not, and no flag
53
+ * makes it, because the information genuinely is not there.
54
+ *
55
+ * What does work is signing, and it works because a signature is on the frame rather than on the
56
+ * link: whoever reads it can check it, whatever the broker did in between. So a relayed test hall is
57
+ * MQTT with `--sign` at both ends, each key file naming the other peer. There is a test for exactly
58
+ * that arrangement and one for the direct connection, and they are the two shapes worth copying.
59
+ */
60
+ export interface ScriptingOptions {
61
+ /** Where scripts are written and run. The same directory `--scripts` names. */
62
+ directory: string;
63
+ /** Handed to each script it starts, so a script reads its broker url rather than carrying one. */
64
+ environment?: {
65
+ [key: string]: string;
66
+ };
67
+ /**
68
+ * Peer names permitted to script this node from elsewhere. Empty - the default - means nobody:
69
+ * the namespace can be exposed for a local server's own use without opening it to the bus.
70
+ *
71
+ * A name here is only as good as the transport's ability to prove it, which is why the guard
72
+ * insists on an identity as well. See `scriptingAuthorizer`.
73
+ */
74
+ allow?: string[];
75
+ }
76
+ /**
77
+ * An authorizer that refuses `scripting` to anyone this node has not named, and leaves every other
78
+ * namespace to whatever policy was already in place.
79
+ *
80
+ * Shipped with the service rather than left to the reader, because the failure mode of forgetting it
81
+ * is an open remote shell on the bus. Compose it with your own by passing that as `inner`.
82
+ */
83
+ export declare const scriptingAuthorizer: (options: ScriptingOptions, inner?: RpcAuthorizer) => RpcAuthorizer;
84
+ /**
85
+ * The namespace name, for the guard to compare against. Written out again in the decorator below
86
+ * rather than referenced, because the extraction CLI reads the source rather than running it and
87
+ * only understands a string literal there - a constant produces a contract with no namespaces in
88
+ * it, and the only sign is the count in the line it prints.
89
+ */
90
+ export declare const SCRIPTING_NAMESPACE = "scripting";
91
+ /**
92
+ * The scripts on one node, as methods.
93
+ *
94
+ * Every method here is what the MCP tools of the same name already do; the difference is that this
95
+ * one can be called from the next machine along. Semantics are declared because they are true and
96
+ * because a caller deciding whether to retry after a lost answer needs them: installing a package
97
+ * twice is not the same as listing them twice.
98
+ */
99
+ export declare class ScriptingService {
100
+ private options;
101
+ private runner;
102
+ constructor(options: ScriptingOptions);
103
+ /** The scripts here, and which of them this node is running. */
104
+ list(): Promise<{
105
+ name: string;
106
+ language: ScriptLanguage;
107
+ running: boolean;
108
+ ended?: {
109
+ code: number | null;
110
+ signal: string | null;
111
+ at: number;
112
+ } | undefined;
113
+ }[]>;
114
+ /** Write one. Saving does not start it, the same as saving a file does not run it. */
115
+ save(name: string, source: string, language?: string): Promise<string>;
116
+ read(name: string): Promise<string>;
117
+ /** Stopped first if it is running, so a delete does not leave a process behind holding the name. */
118
+ remove(name: string): Promise<string>;
119
+ /**
120
+ * Run it, as a process of its own. Idempotent rather than non-repeatable because a second start
121
+ * is refused rather than obeyed - two of one script under one name is the thing to avoid.
122
+ */
123
+ start(name: string): Promise<{
124
+ name: string;
125
+ pid: number | null;
126
+ startedAt: number;
127
+ }>;
128
+ stop(name: string): Promise<{
129
+ name: string;
130
+ ended: {
131
+ code: number | null;
132
+ signal: string | null;
133
+ at: number;
134
+ } | null;
135
+ }>;
136
+ /** What it printed, with stderr lines marked. A script has no other channel back. */
137
+ output(name: string): Promise<{
138
+ name: string;
139
+ output: string[];
140
+ ended: {
141
+ code: number | null;
142
+ signal: string | null;
143
+ at: number;
144
+ } | null;
145
+ running: boolean;
146
+ }>;
147
+ packages(): Promise<({
148
+ name: string;
149
+ range: string;
150
+ installed: string;
151
+ } | {
152
+ name: string;
153
+ range: string;
154
+ installed: null;
155
+ })[]>;
156
+ /**
157
+ * Install one. Non-repeatable: it writes to node_modules and, if asked, runs the package's own
158
+ * install hooks - which is unreviewed code from the registry either way round.
159
+ */
160
+ addPackage(spec: string, allowInstallScripts?: boolean): Promise<{
161
+ ok: boolean;
162
+ output: string;
163
+ }>;
164
+ removePackage(name: string): Promise<{
165
+ ok: boolean;
166
+ output: string;
167
+ }>;
168
+ /** Everything this node started goes with it, rather than being orphaned holding peer names. */
169
+ close(): Promise<void>;
170
+ }
171
+ //# sourceMappingURL=scripting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripting.d.ts","sourceRoot":"","sources":["../src/scripting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,aAAa,EAAuB,MAAM,kBAAkB,CAAA;AAE7F,OAAO,EAAmE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAEnH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,MAAM,WAAW,gBAAgB;IAC7B,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAA;IACjB,kGAAkG;IAClG,WAAW,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACvC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,YAAa,gBAAgB,UAAU,aAAa,KAAG,aAUtF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,cAAc,CAAA;AAE9C;;;;;;;GAOG;AACH,qBACa,gBAAgB;IAGb,OAAO,CAAC,OAAO;IAF3B,OAAO,CAAC,MAAM,CAAc;IAE5B,YAAoB,OAAO,EAAE,gBAAgB,EAE5C;IAED,gEAAgE;IAE1D,IAAI;;;;;;;;;SAMT;IAED,sFAAsF;IAEhF,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,mBAEzD;IAGK,IAAI,CAAC,IAAI,EAAE,MAAM,mBAEtB;IAED,oGAAoG;IAE9F,MAAM,CAAC,IAAI,EAAE,MAAM,mBAIxB;IAED;;;OAGG;IAEG,KAAK,CAAC,IAAI,EAAE,MAAM;;;;OAGvB;IAGK,IAAI,CAAC,IAAI,EAAE,MAAM;;;;;;;OAGtB;IAED,qFAAqF;IAE/E,MAAM,CAAC,IAAI,EAAE,MAAM;;;;;;;;;OAIxB;IAGK,QAAQ;;;;;;;;UAEb;IAED;;;OAGG;IAEG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,OAAO;;;OAE3D;IAGK,aAAa,CAAC,IAAI,EAAE,MAAM;;;OAE/B;IAED,gGAAgG;IAC1F,KAAK,kBAEV;CACJ"}
@@ -0,0 +1,186 @@
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
34
+ };
35
+ import { rpc, rpcNamespace } from '@source-repo/rpc';
36
+ import { addPackage, listPackages, removePackage } from './packages.js';
37
+ import { deleteScript, listScripts, readScript, saveScript, ScriptRunner } from './scripts.js';
38
+ /**
39
+ * An authorizer that refuses `scripting` to anyone this node has not named, and leaves every other
40
+ * namespace to whatever policy was already in place.
41
+ *
42
+ * Shipped with the service rather than left to the reader, because the failure mode of forgetting it
43
+ * is an open remote shell on the bus. Compose it with your own by passing that as `inner`.
44
+ */
45
+ export const scriptingAuthorizer = (options, inner) => {
46
+ const allowed = new Set(options.allow ?? []);
47
+ return async (context) => {
48
+ if (context.instanceName !== SCRIPTING_NAMESPACE)
49
+ return inner ? await inner(context) : true;
50
+ // No identity, no scripting. `source` is a claim until a transport has checked it, and on
51
+ // MQTT nothing checks it unless frames are signed - so an unsigned network refuses everyone,
52
+ // which is the answer that cannot be wrong.
53
+ if (!context.identity)
54
+ return false;
55
+ return allowed.has(context.identity.name);
56
+ };
57
+ };
58
+ /**
59
+ * The namespace name, for the guard to compare against. Written out again in the decorator below
60
+ * rather than referenced, because the extraction CLI reads the source rather than running it and
61
+ * only understands a string literal there - a constant produces a contract with no namespaces in
62
+ * it, and the only sign is the count in the line it prints.
63
+ */
64
+ export const SCRIPTING_NAMESPACE = 'scripting';
65
+ /**
66
+ * The scripts on one node, as methods.
67
+ *
68
+ * Every method here is what the MCP tools of the same name already do; the difference is that this
69
+ * one can be called from the next machine along. Semantics are declared because they are true and
70
+ * because a caller deciding whether to retry after a lost answer needs them: installing a package
71
+ * twice is not the same as listing them twice.
72
+ */
73
+ let ScriptingService = (() => {
74
+ let _classDecorators = [rpcNamespace('scripting', { version: '1', execution: 'serial' })];
75
+ let _classDescriptor;
76
+ let _classExtraInitializers = [];
77
+ let _classThis;
78
+ let _instanceExtraInitializers = [];
79
+ let _list_decorators;
80
+ let _save_decorators;
81
+ let _read_decorators;
82
+ let _remove_decorators;
83
+ let _start_decorators;
84
+ let _stop_decorators;
85
+ let _output_decorators;
86
+ let _packages_decorators;
87
+ let _addPackage_decorators;
88
+ let _removePackage_decorators;
89
+ var ScriptingService = class {
90
+ static { _classThis = this; }
91
+ static {
92
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
93
+ _list_decorators = [rpc({ semantics: 'query' })];
94
+ _save_decorators = [rpc({ semantics: 'idempotent-command' })];
95
+ _read_decorators = [rpc({ semantics: 'query' })];
96
+ _remove_decorators = [rpc({ semantics: 'idempotent-command' })];
97
+ _start_decorators = [rpc({ semantics: 'idempotent-command' })];
98
+ _stop_decorators = [rpc({ semantics: 'idempotent-command' })];
99
+ _output_decorators = [rpc({ semantics: 'query' })];
100
+ _packages_decorators = [rpc({ semantics: 'query' })];
101
+ _addPackage_decorators = [rpc({ semantics: 'non-repeatable-command' })];
102
+ _removePackage_decorators = [rpc({ semantics: 'non-repeatable-command' })];
103
+ __esDecorate(this, null, _list_decorators, { kind: "method", name: "list", static: false, private: false, access: { has: obj => "list" in obj, get: obj => obj.list }, metadata: _metadata }, null, _instanceExtraInitializers);
104
+ __esDecorate(this, null, _save_decorators, { kind: "method", name: "save", static: false, private: false, access: { has: obj => "save" in obj, get: obj => obj.save }, metadata: _metadata }, null, _instanceExtraInitializers);
105
+ __esDecorate(this, null, _read_decorators, { kind: "method", name: "read", static: false, private: false, access: { has: obj => "read" in obj, get: obj => obj.read }, metadata: _metadata }, null, _instanceExtraInitializers);
106
+ __esDecorate(this, null, _remove_decorators, { kind: "method", name: "remove", static: false, private: false, access: { has: obj => "remove" in obj, get: obj => obj.remove }, metadata: _metadata }, null, _instanceExtraInitializers);
107
+ __esDecorate(this, null, _start_decorators, { kind: "method", name: "start", static: false, private: false, access: { has: obj => "start" in obj, get: obj => obj.start }, metadata: _metadata }, null, _instanceExtraInitializers);
108
+ __esDecorate(this, null, _stop_decorators, { kind: "method", name: "stop", static: false, private: false, access: { has: obj => "stop" in obj, get: obj => obj.stop }, metadata: _metadata }, null, _instanceExtraInitializers);
109
+ __esDecorate(this, null, _output_decorators, { kind: "method", name: "output", static: false, private: false, access: { has: obj => "output" in obj, get: obj => obj.output }, metadata: _metadata }, null, _instanceExtraInitializers);
110
+ __esDecorate(this, null, _packages_decorators, { kind: "method", name: "packages", static: false, private: false, access: { has: obj => "packages" in obj, get: obj => obj.packages }, metadata: _metadata }, null, _instanceExtraInitializers);
111
+ __esDecorate(this, null, _addPackage_decorators, { kind: "method", name: "addPackage", static: false, private: false, access: { has: obj => "addPackage" in obj, get: obj => obj.addPackage }, metadata: _metadata }, null, _instanceExtraInitializers);
112
+ __esDecorate(this, null, _removePackage_decorators, { kind: "method", name: "removePackage", static: false, private: false, access: { has: obj => "removePackage" in obj, get: obj => obj.removePackage }, metadata: _metadata }, null, _instanceExtraInitializers);
113
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
114
+ ScriptingService = _classThis = _classDescriptor.value;
115
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
116
+ __runInitializers(_classThis, _classExtraInitializers);
117
+ }
118
+ options = __runInitializers(this, _instanceExtraInitializers);
119
+ runner;
120
+ constructor(options) {
121
+ this.options = options;
122
+ this.runner = new ScriptRunner(options.directory, options.environment ?? {});
123
+ }
124
+ /** The scripts here, and which of them this node is running. */
125
+ async list() {
126
+ return listScripts(this.options.directory).map((script) => ({
127
+ ...script,
128
+ running: this.runner.isRunning(script.name),
129
+ ...(this.runner.status(script.name)?.ended ? { ended: this.runner.status(script.name).ended } : {})
130
+ }));
131
+ }
132
+ /** Write one. Saving does not start it, the same as saving a file does not run it. */
133
+ async save(name, source, language) {
134
+ return saveScript(this.options.directory, name, source, language === 'mjs' ? 'mjs' : 'ts');
135
+ }
136
+ async read(name) {
137
+ return readScript(this.options.directory, name);
138
+ }
139
+ /** Stopped first if it is running, so a delete does not leave a process behind holding the name. */
140
+ async remove(name) {
141
+ if (this.runner.isRunning(name))
142
+ await this.runner.stop(name);
143
+ deleteScript(this.options.directory, name);
144
+ return name;
145
+ }
146
+ /**
147
+ * Run it, as a process of its own. Idempotent rather than non-repeatable because a second start
148
+ * is refused rather than obeyed - two of one script under one name is the thing to avoid.
149
+ */
150
+ async start(name) {
151
+ const started = this.runner.start(name);
152
+ return { name: started.name, pid: started.pid ?? null, startedAt: started.startedAt };
153
+ }
154
+ async stop(name) {
155
+ const record = await this.runner.stop(name);
156
+ return { name: record.name, ended: record.ended ?? null };
157
+ }
158
+ /** What it printed, with stderr lines marked. A script has no other channel back. */
159
+ async output(name) {
160
+ const record = this.runner.status(name);
161
+ if (!record)
162
+ throw Object.assign(new Error(`'${name}' has not been started here`), { code: 'MethodNotFound' });
163
+ return { name, output: record.output, ended: record.ended ?? null, running: this.runner.isRunning(name) };
164
+ }
165
+ async packages() {
166
+ return listPackages(this.options.directory);
167
+ }
168
+ /**
169
+ * Install one. Non-repeatable: it writes to node_modules and, if asked, runs the package's own
170
+ * install hooks - which is unreviewed code from the registry either way round.
171
+ */
172
+ async addPackage(spec, allowInstallScripts) {
173
+ return await addPackage(this.options.directory, spec, allowInstallScripts === true);
174
+ }
175
+ async removePackage(name) {
176
+ return await removePackage(this.options.directory, name);
177
+ }
178
+ /** Everything this node started goes with it, rather than being orphaned holding peer names. */
179
+ async close() {
180
+ await this.runner.stopAll();
181
+ }
182
+ };
183
+ return ScriptingService = _classThis;
184
+ })();
185
+ export { ScriptingService };
186
+ //# sourceMappingURL=scripting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripting.js","sourceRoot":"","sources":["../src/scripting.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { NetworkOptions } from './network.js';
2
+ /** TypeScript first; `.mjs` for a Node too old to run it, or an author who would rather not. */
3
+ export declare const SCRIPT_EXTENSIONS: readonly ['.ts', '.mjs'];
4
+ export type ScriptLanguage = 'ts' | 'mjs';
5
+ /** Where a script of this language would be written. Proven to stay inside the directory. */
6
+ export declare const scriptPath: (directory: string, name: string, language?: ScriptLanguage) => string;
7
+ /** The file a saved script actually occupies, whichever language it was written in. */
8
+ export declare const scriptFile: (directory: string, name: string) => string;
9
+ export declare const saveScript: (directory: string, name: string, source: string, language?: ScriptLanguage) => string;
10
+ export declare const readScript: (directory: string, name: string) => string;
11
+ export declare const deleteScript: (directory: string, name: string) => void;
12
+ export declare const listScripts: (directory: string) => {
13
+ name: string;
14
+ language: ScriptLanguage;
15
+ }[];
16
+ /**
17
+ * What this Node needs in order to run that file.
18
+ *
19
+ * Type stripping landed behind `--experimental-strip-types` in 22.6 and became the default in 23.6,
20
+ * so the flag is passed only where it is both needed and understood - passing it to 24 is a warning,
21
+ * and passing it to 22.5 is an error about an unknown option rather than about TypeScript.
22
+ */
23
+ export declare const nodeArgvFor: (file: string, version?: string) => string[];
24
+ /**
25
+ * The network this server is on, handed to a script as environment.
26
+ *
27
+ * So a script does not hardcode a broker url that is right on one machine and wrong on the next -
28
+ * and so a model writing one has something to read rather than a value to invent. The names match
29
+ * the flags they came from, and `SOURCE_RPC_TOKEN` is the one the CLI already reads for credentials.
30
+ */
31
+ export declare const environmentFor: (options: NetworkOptions) => {
32
+ [key: string]: string;
33
+ };
34
+ export interface RunningScript {
35
+ name: string;
36
+ pid?: number;
37
+ startedAt: number;
38
+ /** Set once it has ended, so a stopped script reports why rather than merely being absent. */
39
+ ended?: {
40
+ code: number | null;
41
+ signal: string | null;
42
+ at: number;
43
+ };
44
+ output: string[];
45
+ }
46
+ /** Starts, stops and remembers the scripts this server is running. */
47
+ export declare class ScriptRunner {
48
+ private directory;
49
+ private environment;
50
+ private running;
51
+ private finished;
52
+ constructor(directory: string, environment?: {
53
+ [key: string]: string;
54
+ });
55
+ isRunning(name: string): boolean;
56
+ start(name: string): RunningScript;
57
+ stop(name: string): Promise<RunningScript>;
58
+ status(name: string): RunningScript | undefined;
59
+ all(): RunningScript[];
60
+ stopAll(): Promise<void>;
61
+ }
62
+ //# sourceMappingURL=scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripts.d.ts","sourceRoot":"","sources":["../src/scripts.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AA8BlD,gGAAgG;AAChG,eAAO,MAAM,iBAAiB,YAAI,KAAK,EAAE,MAAM,CAAU,CAAA;AACzD,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,KAAK,CAAA;AASzC,6FAA6F;AAC7F,eAAO,MAAM,UAAU,cAAe,MAAM,QAAQ,MAAM,aAAY,cAAc,WAMnF,CAAA;AAED,uFAAuF;AACvF,eAAO,MAAM,UAAU,cAAe,MAAM,QAAQ,MAAM,WAOzD,CAAA;AAED,eAAO,MAAM,UAAU,cAAe,MAAM,QAAQ,MAAM,UAAU,MAAM,aAAY,cAAc,WAcnG,CAAA;AAED,eAAO,MAAM,UAAU,cAAe,MAAM,QAAQ,MAAM,WAAsD,CAAA;AAEhH,eAAO,MAAM,YAAY,cAAe,MAAM,QAAQ,MAAM,SAAwC,CAAA;AAEpG,eAAO,MAAM,WAAW,cAAe,MAAM;;cAIiE,cAAc;GAM3H,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,SAAU,MAAM,+BAQvC,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,YAAa,cAAc,KAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAO9E,CAAA;AAEF,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,8FAA8F;IAC9F,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAClE,MAAM,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,sEAAsE;AACtE,qBAAa,YAAY;IAKjB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,WAAW;IALvB,OAAO,CAAC,OAAO,CAAoE;IACnF,OAAO,CAAC,QAAQ,CAAmC;IAEnD,YACY,SAAS,EAAE,MAAM,EACjB,WAAW,GAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAO,EACnD;IAEJ,SAAS,CAAC,IAAI,EAAE,MAAM,WAErB;IAED,KAAK,CAAC,IAAI,EAAE,MAAM,iBAiCjB;IAEK,IAAI,CAAC,IAAI,EAAE,MAAM,0BAetB;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,6BAElB;IAED,GAAG,IAAI,aAAa,EAAE,CAErB;IAEK,OAAO,kBAEZ;CACJ"}