@plurnk/plurnk-execs-common 3.0.2 → 3.0.4
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 +23 -58
- package/dist/Common.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtimes.d.ts.map +1 -1
- package/dist/runtimes.js +31 -24
- package/dist/runtimes.js.map +1 -1
- package/docs/js.md +6 -14
- package/docs/python3.md +4 -12
- package/docs/sh.md +14 -55
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,73 +1,38 @@
|
|
|
1
1
|
# @plurnk/plurnk-execs-common
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[plurnk-service](https://github.com/plurnk/plurnk-service)'s runtime-tag execution (`sh`, `js`, `python3`). One
|
|
5
|
-
package offers the shell, Node.js, and Python 3. Node is guaranteed; the rest
|
|
6
|
-
are detected.
|
|
3
|
+
Subprocess executor for [`@plurnk/plurnk-service`](https://github.com/plurnk/plurnk-service) (`sh`, `js`, `python3`), built on [`@plurnk/plurnk-execs`](https://github.com/plurnk/plurnk-service/tree/main/plurnk-execs). `js` (`node`) is always present; `sh` and `python3` register when `command -v` finds them on `PATH`.
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
## Runtimes & invocation ({§executor-subprocess}, {§executor-contract})
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
| Tag | Binary | Inline body via |
|
|
17
|
-
| ------------ | ------- | ------------------------------ |
|
|
18
|
-
| `sh` 🐚 | sh | `-c <body>` |
|
|
19
|
-
| `js` ⬢ | node | `-e <body>` (always available) |
|
|
20
|
-
| `python3` 🐍 | python3 | `-c <body>` |
|
|
21
|
-
|
|
22
|
-
Each enabled runtime's catalog Summary includes a short executable inline body, with
|
|
23
|
-
literal `\n` separators keeping the invocation on one discovery line.
|
|
24
|
-
|
|
25
|
-
### The `(target)` slot — a script or working directory
|
|
26
|
-
|
|
27
|
-
The table above is the **inline** form: the body is the program. A directory in
|
|
28
|
-
the `(target)` slot selects the working directory for that inline program. A
|
|
29
|
-
file in the `(target)` slot is instead the script each interpreter reads
|
|
30
|
-
directly, and the body is tokenized by shell quoting (`tokenizeArgv`) into the
|
|
31
|
-
script's arguments ({§executor-subprocess}, {§executor-contract}).
|
|
7
|
+
| Tag | Binary | Inline `{ text, cwd? }` |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `sh` 🐚 | `sh` (`-c`) | `plurnk.sh({ text: "uname -s" })` |
|
|
10
|
+
| `js` ⬢ | `node` (`-e`) | `plurnk.js({ text: "console.log(process.version)" })` |
|
|
11
|
+
| `python3` 🐍 | `python3` (`-c`) | `plurnk.python3({ text: "import sys; print(sys.version)" })` |
|
|
32
12
|
|
|
33
|
-
|
|
34
|
-
--release "two words"
|
|
35
|
-
```
|
|
13
|
+
Local script form (`{ script, args?, cwd? }`):
|
|
36
14
|
|
|
37
|
-
```
|
|
38
|
-
--
|
|
15
|
+
```js
|
|
16
|
+
plurnk.sh({ script: "./deploy.sh", args: ["--release", "two words"] })
|
|
17
|
+
plurnk.python3({ script: "transform.py", args: ["--format", "json"] })
|
|
39
18
|
```
|
|
40
19
|
|
|
41
|
-
|
|
42
|
-
|
|
20
|
+
- `cwd` selects the working directory for inline or script runs; `script` executes the file directly with `args` (`string[]`, lowered via `SubprocessExecutor` / `tokenizeArgv`).
|
|
21
|
+
- All three runtimes declare `effect: "host"` and are proposal-gated ({§executor-results}), streaming output to `<tag>:///<id>#{stdout,stderr}`.
|
|
22
|
+
- Subtractive boot policy: `PLURNK_EXECS_<TAG>=0` disables one tag; `PLURNK_EXECS_ONLY=<list>` restricts registration ({§executor-policy}).
|
|
43
23
|
|
|
44
|
-
##
|
|
24
|
+
## Node `--permission` confinement
|
|
45
25
|
|
|
46
|
-
|
|
47
|
-
`PLURNK_EXECS_ONLY` allowlist are honored by framework discovery, uniformly
|
|
48
|
-
across every extension ({§executor-policy}). A disabled tag is not registered and
|
|
49
|
-
never reaches this executor.
|
|
26
|
+
Passing `--permission` via `NODE_OPTIONS` confines `js` child processes (`ERR_ACCESS_DENIED`) with zero PLURNK-specific flags ({§execution-output-identity}):
|
|
50
27
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Node's `--permission` model is natively conditional: unflagged `js` invocations run
|
|
54
|
-
with normal Node semantics, while passing `--permission` flags through
|
|
55
|
-
`NODE_OPTIONS` confines `js` child processes at access time (`ERR_ACCESS_DENIED`)
|
|
56
|
-
without adding any PLURNK-specific knob or altering default behavior.
|
|
57
|
-
|
|
58
|
-
| Step | Existing surface | Effect |
|
|
28
|
+
| Scope | Surface | Effect |
|
|
59
29
|
| --- | --- | --- |
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
64
|
-
**Scope choices** ({§execution-output-identity}):
|
|
65
|
-
|
|
66
|
-
- **Workspace scope** (`plurnk_env (add)` with `scope: "workspace"`): `NODE_OPTIONS` reaches both `js` execution children and Node-based MCP server subprocesses in the workspace.
|
|
67
|
-
- **Session scope** (`plurnk_env (add)` with `scope: "session"`): `NODE_OPTIONS` reaches `js` execution children of that session only.
|
|
68
|
-
|
|
69
|
-
**Honest ledger:** Node's permission model provides same-UID accident containment, not adversarial security boundaries. PLURNK owns authority, consent, and audit, and never claims containment; for OS-account separation, combine with `PLURNK_EXECS_SPAWN_USER` ({§executor-policy}) or an external container/VM sandbox.
|
|
30
|
+
| Unregister shell/Python | `PLURNK_EXECS_ONLY=js` | Restricts registered subprocess tags to `js` ({§executor-policy}). |
|
|
31
|
+
| Confine `js` children | `NODE_OPTIONS="--permission --allow-fs-read=<dir> --allow-fs-write=<dir>"` | Applies to `js` + Node MCP stdio servers at `scope: "workspace"`, or `js` children only at `scope: "session"` (`plurnk.env`). |
|
|
32
|
+
| OS boundary | `PLURNK_EXECS_SPAWN_USER` / container / VM | Required for adversarial isolation (Node `--permission` is same-UID accident containment). |
|
|
70
33
|
|
|
71
34
|
## Tests
|
|
72
35
|
|
|
73
|
-
|
|
36
|
+
```sh
|
|
37
|
+
npm test -w @plurnk/plurnk-execs-common
|
|
38
|
+
```
|
package/dist/Common.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Common.js","sourceRoot":"","sources":["../src/Common.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Common.js","sourceRoot":"","sources":["../src/Common.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { isExecutionOp, type RuntimeTag } from \"@plurnk/plurnk-contracts\";\nimport { SubprocessExecutor, SpawnAccounts, Shell, tokenizeArgv } from \"@plurnk/plurnk-execs\";\nimport type { RuntimeAvailability, SpawnArgs } from \"@plurnk/plurnk-execs\";\n\n// Per-interpreter spawn recipe. `bin` is the executable (probed for presence).\ninterface Recipe {\n bin: string;\n arg: (command: string) => string[];\n // node is guaranteed (the daemon IS node) — reported available without a\n // PATH probe. Everything else is detected.\n alwaysAvailable?: boolean;\n}\n\nconst RECIPES: Readonly<Record<string, Recipe>> = Object.freeze({\n // Subprocess floor. `sh`'s bin is the shell the environment names ({§executor-subprocess}), resolved at use.\n sh: { bin: \"sh\", arg: (c) => [\"-c\", c] },\n js: { bin: \"node\", arg: (c) => [\"-e\", c], alwaysAvailable: true },\n python3: { bin: \"python3\", arg: (c) => [\"-c\", c] },\n});\n\n// Exposed for tests / consumers wanting the candidate set.\nexport const RUNTIME_TAGS: readonly RuntimeTag[] = Object.freeze(Object.keys(RECIPES).filter(isExecutionOp));\n\nconst onPath = (bin: string): boolean =>\n spawnSync(\"sh\", [\"-c\", `command -v \"$1\"`, \"sh\", bin], { ...SpawnAccounts.options() }).status === 0; // {§executor-policy}\n\nexport default class Common extends SubprocessExecutor {\n protected override spawnArgs(runtime: string, command: string, target: string | null = null): SpawnArgs {\n const r = Common.#recipe(runtime);\n if (r === undefined) throw new Error(`plurnk-execs-common received unclaimed runtime tag '${runtime}'`);\n // With a target the program IS the target and the body is its arguments,\n // tokenized by shell quoting ({§executor-subprocess}, {§executor-contract}).\n // The interpreter reads the file; no executable bit is consulted or changed.\n if (target !== null) {\n return { cmd: r.bin, args: [target, ...tokenizeArgv(command)], useShell: false };\n }\n return { cmd: r.bin, args: r.arg(command), useShell: false };\n }\n\n // {§executor-subprocess} `sh` runs the environment's shell; every other recipe is fixed.\n static #recipe(runtime: string): Recipe | undefined {\n const r = RECIPES[runtime];\n return runtime === \"sh\" && r !== undefined ? { ...r, bin: Shell.current().path } : r;\n }\n\n override async probe(): Promise<RuntimeAvailability> {\n const r = Common.#recipe(this.runtime);\n if (r === undefined) return { available: false, detail: `unknown runtime '${this.runtime}'` };\n if (r.alwaysAvailable) return { available: true, detail: r.bin === \"node\" ? process.version : r.bin };\n return onPath(r.bin)\n ? { available: true, detail: r.bin }\n : { available: false, detail: `${r.bin} not on PATH` };\n }\n}\n"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export { default as Common, RUNTIME_TAGS } from \"./Common.ts\";\nexport { default } from \"./Common.ts\";\n"]}
|
package/dist/runtimes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtimes.d.ts","sourceRoot":"","sources":["../src/runtimes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"runtimes.d.ts","sourceRoot":"","sources":["../src/runtimes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAoE/D,eAAO,MAAM,QAAQ,QAAO,WAAW,EAItC,CAAC"}
|
package/dist/runtimes.js
CHANGED
|
@@ -10,7 +10,7 @@ const SH = {
|
|
|
10
10
|
required: false,
|
|
11
11
|
},
|
|
12
12
|
target: {
|
|
13
|
-
role: "script, or working directory for an inline program",
|
|
13
|
+
role: "script, or working directory for an inline program (defaults to the workspace root)",
|
|
14
14
|
required: false,
|
|
15
15
|
kind: "script",
|
|
16
16
|
},
|
|
@@ -19,37 +19,40 @@ const SH = {
|
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
let python3Version;
|
|
23
|
+
const currentPython3Version = () => python3Version === undefined ? (python3Version = Shell.version("python3")) : python3Version;
|
|
24
|
+
const jsDecl = () => ({
|
|
25
|
+
name: "js",
|
|
26
|
+
glyph: "⬢",
|
|
27
|
+
summary: `Run JavaScript code or scripts in the workspace with node (${process.version}).`,
|
|
28
|
+
invocation: {
|
|
29
|
+
body: {
|
|
30
|
+
role: "JavaScript program; arguments when the target is a script",
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
target: {
|
|
34
|
+
role: "script, or working directory for an inline program (defaults to the workspace root)",
|
|
35
|
+
required: false,
|
|
36
|
+
kind: "script",
|
|
37
|
+
},
|
|
38
|
+
example: {
|
|
39
|
+
body: "console.log(42)",
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
|
-
|
|
42
|
+
});
|
|
43
|
+
const python3Decl = () => {
|
|
44
|
+
const version = currentPython3Version();
|
|
45
|
+
return {
|
|
43
46
|
name: "python3",
|
|
44
47
|
glyph: "🐍",
|
|
45
|
-
summary:
|
|
48
|
+
summary: `Run Python 3 code or scripts in the workspace with python3${version === null ? "" : ` (v${version})`}.`,
|
|
46
49
|
invocation: {
|
|
47
50
|
body: {
|
|
48
51
|
role: "Python program; arguments when the target is a script",
|
|
49
52
|
required: false,
|
|
50
53
|
},
|
|
51
54
|
target: {
|
|
52
|
-
role: "script, or working directory for an inline program",
|
|
55
|
+
role: "script, or working directory for an inline program (defaults to the workspace root)",
|
|
53
56
|
required: false,
|
|
54
57
|
kind: "script",
|
|
55
58
|
},
|
|
@@ -57,7 +60,11 @@ const FIXED = [
|
|
|
57
60
|
body: "print(42)",
|
|
58
61
|
},
|
|
59
62
|
},
|
|
60
|
-
}
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
export const runtimes = () => [
|
|
66
|
+
{ ...SH, summary: Shell.summary(Shell.current()) },
|
|
67
|
+
jsDecl(),
|
|
68
|
+
python3Decl(),
|
|
61
69
|
];
|
|
62
|
-
export const runtimes = () => [{ ...SH, summary: Shell.summary(Shell.current()) }, ...FIXED];
|
|
63
70
|
//# sourceMappingURL=runtimes.js.map
|
package/dist/runtimes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtimes.js","sourceRoot":"","sources":["../src/runtimes.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,qFAAqF;AACrF,OAAO,EAAE,KAAK,EAAoB,MAAM,sBAAsB,CAAC;AAE/D,MAAM,EAAE,GAAiC;IACrC,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,IAAI;IACX,UAAU,EAAE;QACR,IAAI,EAAE;YACF,IAAI,EAAE,0DAA0D;YAChE,QAAQ,EAAE,KAAK;SAClB;QACD,MAAM,EAAE;YACJ,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"runtimes.js","sourceRoot":"","sources":["../src/runtimes.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,qFAAqF;AACrF,OAAO,EAAE,KAAK,EAAoB,MAAM,sBAAsB,CAAC;AAE/D,MAAM,EAAE,GAAiC;IACrC,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,IAAI;IACX,UAAU,EAAE;QACR,IAAI,EAAE;YACF,IAAI,EAAE,0DAA0D;YAChE,QAAQ,EAAE,KAAK;SAClB;QACD,MAAM,EAAE;YACJ,IAAI,EAAE,qFAAqF;YAC3F,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,QAAQ;SACjB;QACD,OAAO,EAAE;YACL,IAAI,EAAE,oBAAoB;SAC7B;KACJ;CACJ,CAAC;AAEF,IAAI,cAAyC,CAAC;AAC9C,MAAM,qBAAqB,GAAG,GAAkB,EAAE,CAC9C,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;AAEhG,MAAM,MAAM,GAAG,GAAgB,EAAE,CAAC,CAAC;IAC/B,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,GAAG;IACV,OAAO,EAAE,8DAA8D,OAAO,CAAC,OAAO,IAAI;IAC1F,UAAU,EAAE;QACR,IAAI,EAAE;YACF,IAAI,EAAE,2DAA2D;YACjE,QAAQ,EAAE,KAAK;SAClB;QACD,MAAM,EAAE;YACJ,IAAI,EAAE,qFAAqF;YAC3F,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,QAAQ;SACjB;QACD,OAAO,EAAE;YACL,IAAI,EAAE,iBAAiB;SAC1B;KACJ;CACJ,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,GAAgB,EAAE;IAClC,MAAM,OAAO,GAAG,qBAAqB,EAAE,CAAC;IACxC,OAAO;QACH,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,6DAA6D,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,OAAO,GAAG,GAAG;QACjH,UAAU,EAAE;YACR,IAAI,EAAE;gBACF,IAAI,EAAE,uDAAuD;gBAC7D,QAAQ,EAAE,KAAK;aAClB;YACD,MAAM,EAAE;gBACJ,IAAI,EAAE,qFAAqF;gBAC3F,QAAQ,EAAE,KAAK;gBACf,IAAI,EAAE,QAAQ;aACjB;YACD,OAAO,EAAE;gBACL,IAAI,EAAE,WAAW;aACpB;SACJ;KACJ,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAkB,EAAE,CAAC;IACzC,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE;IAClD,MAAM,EAAE;IACR,WAAW,EAAE;CAChB,CAAC","sourcesContent":["// {§executor-discovery} The common package's declarations. `sh` names the shell it runs\n// ({§executor-subprocess}), resolved on this host; every other declaration is fixed.\nimport { Shell, type RuntimeDecl } from \"@plurnk/plurnk-execs\";\n\nconst SH: Omit<RuntimeDecl, \"summary\"> = {\n name: \"sh\",\n glyph: \"🐚\",\n invocation: {\n body: {\n role: \"the shell program; arguments when the target is a script\",\n required: false,\n },\n target: {\n role: \"script, or working directory for an inline program (defaults to the workspace root)\",\n required: false,\n kind: \"script\",\n },\n example: {\n body: \"git status --short\",\n },\n },\n};\n\nlet python3Version: string | null | undefined;\nconst currentPython3Version = (): string | null =>\n python3Version === undefined ? (python3Version = Shell.version(\"python3\")) : python3Version;\n\nconst jsDecl = (): RuntimeDecl => ({\n name: \"js\",\n glyph: \"⬢\",\n summary: `Run JavaScript code or scripts in the workspace with node (${process.version}).`,\n invocation: {\n body: {\n role: \"JavaScript program; arguments when the target is a script\",\n required: false,\n },\n target: {\n role: \"script, or working directory for an inline program (defaults to the workspace root)\",\n required: false,\n kind: \"script\",\n },\n example: {\n body: \"console.log(42)\",\n },\n },\n});\n\nconst python3Decl = (): RuntimeDecl => {\n const version = currentPython3Version();\n return {\n name: \"python3\",\n glyph: \"🐍\",\n summary: `Run Python 3 code or scripts in the workspace with python3${version === null ? \"\" : ` (v${version})`}.`,\n invocation: {\n body: {\n role: \"Python program; arguments when the target is a script\",\n required: false,\n },\n target: {\n role: \"script, or working directory for an inline program (defaults to the workspace root)\",\n required: false,\n kind: \"script\",\n },\n example: {\n body: \"print(42)\",\n },\n },\n };\n};\n\nexport const runtimes = (): RuntimeDecl[] => [\n { ...SH, summary: Shell.summary(Shell.current()) },\n jsDecl(),\n python3Decl(),\n];\n"]}
|
package/docs/js.md
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
# js
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
is always available (no PATH probe).
|
|
3
|
+
Runs JavaScript via `node -e` (inline `text`) or `node <script>` (`script` with optional `args: string[]`, including `session://` and `skill://` targets).
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
const os = require("node:os");
|
|
8
|
-
console.log(JSON.stringify({ platform: os.platform(), cpus: os.cpus().length }));
|
|
9
|
-
```
|
|
5
|
+
plurnk.js({ text: "const os = require(\"node:os\");\nconsole.log(JSON.stringify({ platform: os.platform(), cpus: os.cpus().length }));" })
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Relative
|
|
14
|
-
|
|
15
|
-
output on completion, exit status and lifetime are every subprocess executor's,
|
|
16
|
-
as `plurnk:///sh.md` states. Stdout is text, so structured output is serialized
|
|
17
|
-
(`console.log(JSON.stringify(value))`); a thrown error exits nonzero with its
|
|
18
|
-
stack on stderr.
|
|
7
|
+
plurnk.js({ script: "tool.js", args: ["--flag"] })
|
|
8
|
+
|
|
9
|
+
- Relative `import` / `require` resolves from the script; relative filesystem paths resolve from `cwd` (project root by default).
|
|
10
|
+
- Environment, `cwd`, `#stdout`/`#stderr` channels, and `exitCode` follow `mcp:///plurnk/sh.json`.
|
package/docs/python3.md
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
# python3
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
slot selects the working directory for that inline code. A script target instead
|
|
5
|
-
runs that file and passes the body as its arguments (tokenized by shell
|
|
6
|
-
quoting), as for every interpreter (`plurnk:///sh.md`).
|
|
3
|
+
Runs Python 3 via `python3 -c` (inline `text`) or `python3 <script>` (`script` with optional `args: string[]`, including `session://` and `skill://` targets).
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
import json, sys
|
|
10
|
-
print(json.dumps({"python": list(sys.version_info[:2])}))
|
|
11
|
-
```
|
|
5
|
+
plurnk.python3({ text: "import json, sys\nprint(json.dumps({\"python\": list(sys.version_info[:2])}))" })
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
--help
|
|
15
|
-
```
|
|
7
|
+
plurnk.python3({ script: "tools/report.py", args: ["--help"] })
|
|
16
8
|
|
|
17
|
-
Native skill
|
|
9
|
+
Native skill scripts retain sibling imports and source-relative assets; environment, `cwd`, `#stdout`/`#stderr` channels, and `exitCode` follow `mcp:///plurnk/sh.json`.
|
package/docs/sh.md
CHANGED
|
@@ -1,66 +1,25 @@
|
|
|
1
1
|
# sh
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
environment's `$SHELL`, or `/bin/sh` when that names no executable file. The body
|
|
5
|
-
reaches it character-perfect, whitespace included; write for that shell.
|
|
3
|
+
`plurnk.sh` runs `text` with `-c` in the shell its summary names (`$SHELL`, or `/bin/sh` when unset).
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
printf 'hello\n' > hello.txt
|
|
9
|
-
wc -l hello.txt
|
|
10
|
-
```
|
|
5
|
+
plurnk.sh({ text: "printf 'hello\\n' > hello.txt\nwc -l hello.txt" })
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
ordinary executable-bit rules. The same applies to local, `session://`, and
|
|
18
|
-
`skill://` script targets across every interpreter.
|
|
7
|
+
| Input | Behavior |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `{ text, cwd? }` | Run `text` inline via `<shell> -c`. |
|
|
10
|
+
| `{ script, args?, cwd? }` | Run a local, `session://`, or `skill://` script directly in the interpreter with `args` (`string[]`); no executable bit is required. |
|
|
11
|
+
| `cwd` | Working directory (defaults to the workspace project root). |
|
|
19
12
|
|
|
20
13
|
## Environment
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
setting are stripped before the child starts, so `printenv` cannot read
|
|
24
|
-
plurnk's credentials, and the project's environment passes through, overlaid
|
|
25
|
-
with the `env` registry (`mcp:///plurnk_env.md`).
|
|
15
|
+
Provider credentials and `PLURNK_*` settings are stripped before launch; admitted host variables and `plurnk.env` (`mcp:///plurnk/env.json`) entries pass through.
|
|
26
16
|
|
|
27
|
-
##
|
|
17
|
+
## Channels and settlement
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
one the directory the shell would run in anyway. A directory in the `(target)`
|
|
31
|
-
slot overrides it for an inline body (`sh (packages/api)`); a receipt names the
|
|
32
|
-
directory, relative to the project root, only when the command ran elsewhere.
|
|
19
|
+
Output streams under the receipt's `stream` address (e.g. `sh:///ab3d5678`):
|
|
33
20
|
|
|
34
|
-
|
|
21
|
+
- `#stdout` (default) and `#stderr` (`text/stream`): while running, `## Open Work` reports each channel's size and growth; on completion, `_plurnk` emits one `read` per non-empty channel (full output when it fits the budget, or a bodiless size/range receipt with `exitCode`).
|
|
22
|
+
- Nonzero `exitCode` is the command's exit status, not an operation failure.
|
|
23
|
+
- Read a slice of a running or settled stream with `range`:
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
under the receipt's `stream` address, such as `sh:///ab3d5678`: `#stdout` is
|
|
38
|
-
the default channel and `#stderr` the second; both are `text/stream`. While it
|
|
39
|
-
runs, the packet's `## Open Work` streams list reports each channel's size and
|
|
40
|
-
growth, and READ can inspect any range. On completion, the harness adds one
|
|
41
|
-
`_plurnk` READ per channel: the whole output when it fits the context budget,
|
|
42
|
-
otherwise a bodiless receipt naming its size, with the `range` extent and the
|
|
43
|
-
terminal exit status. READ a range of the observation's `path` for any part:
|
|
44
|
-
|
|
45
|
-
```READ (sh:///ab3d5678#stdout) <17,40>
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
The observation carries the `exitCode`; a nonzero one is the command's answer,
|
|
49
|
-
not a failure. Stdout and stderr are separate channels,
|
|
50
|
-
and a diagnostic may be on either. The `log:///…/sh` receipt's own body is the
|
|
51
|
-
program exactly as sent, never output. A receipt with a non-200 status and no
|
|
52
|
-
`stream` address ran nothing; its body is still the program, and its Problem
|
|
53
|
-
says why it was refused.
|
|
54
|
-
|
|
55
|
-
## Lifetime
|
|
56
|
-
|
|
57
|
-
The heading's `lifetime` metadata bounds the command's total runtime, not idle
|
|
58
|
-
time. This command is stopped after 30 seconds if it has not finished:
|
|
59
|
-
|
|
60
|
-
```sh [{"lifetime": "30s"}]
|
|
61
|
-
npm test
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Expiry closes the stream with status 504. By contrast, `WAIT [30]` limits one
|
|
65
|
-
wait to 30 seconds without stopping the command. Without `lifetime`,
|
|
66
|
-
the command is loop-bound; see `mcp:///plurnk_env.md` for other `lifetime` values (`detached`, `turn`).
|
|
25
|
+
plurnk.read({ path: "sh:///ab3d5678#stdout", range: "17,40" })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plurnk/plurnk-execs-common",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
4
4
|
"description": "Subprocess executor for plurnk-service's exec scheme — exposing the shell, Node.js, and Python 3.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"plurnk",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
],
|
|
16
16
|
"homepage": "https://github.com/plurnk/plurnk-service/tree/main/plurnk-execs-common#readme",
|
|
17
17
|
"bugs": {
|
|
18
|
-
"url": "https://
|
|
18
|
+
"url": "https://github.com/plurnk/plurnk-service/issues"
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"@plurnk/plurnk-contracts": "^7.0.0",
|
|
66
66
|
"@plurnk/plurnk-execs": "^4.0.0"
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "49f92d9351021ae0729d69e72ab9b1b95e3a974b"
|
|
69
69
|
}
|