@plurnk/plurnk-execs 1.16.4 → 1.17.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.
Files changed (47) hide show
  1. package/.env.defaults +6 -11
  2. package/README.md +31 -5
  3. package/SPEC.md +136 -47
  4. package/dist/BaseExecutor.d.ts +3 -1
  5. package/dist/BaseExecutor.d.ts.map +1 -1
  6. package/dist/BaseExecutor.js +8 -0
  7. package/dist/BaseExecutor.js.map +1 -1
  8. package/dist/DocFile.d.ts +5 -0
  9. package/dist/DocFile.d.ts.map +1 -0
  10. package/dist/DocFile.js +28 -0
  11. package/dist/DocFile.js.map +1 -0
  12. package/dist/InvocationMetadata.d.ts +22 -0
  13. package/dist/InvocationMetadata.d.ts.map +1 -0
  14. package/dist/InvocationMetadata.js +63 -0
  15. package/dist/InvocationMetadata.js.map +1 -0
  16. package/dist/RuntimeInvocation.d.ts.map +1 -1
  17. package/dist/RuntimeInvocation.js +13 -10
  18. package/dist/RuntimeInvocation.js.map +1 -1
  19. package/dist/RuntimeSummary.d.ts +0 -3
  20. package/dist/RuntimeSummary.d.ts.map +1 -1
  21. package/dist/RuntimeSummary.js +7 -13
  22. package/dist/RuntimeSummary.js.map +1 -1
  23. package/dist/SubprocessExecutor.d.ts +3 -2
  24. package/dist/SubprocessExecutor.d.ts.map +1 -1
  25. package/dist/SubprocessExecutor.js +30 -4
  26. package/dist/SubprocessExecutor.js.map +1 -1
  27. package/dist/SubprocessInput.d.ts +10 -0
  28. package/dist/SubprocessInput.d.ts.map +1 -0
  29. package/dist/SubprocessInput.js +86 -0
  30. package/dist/SubprocessInput.js.map +1 -0
  31. package/dist/discover.d.ts.map +1 -1
  32. package/dist/discover.js +2 -19
  33. package/dist/discover.js.map +1 -1
  34. package/dist/index.d.ts +4 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -3
  37. package/dist/index.js.map +1 -1
  38. package/dist/runtime.d.ts.map +1 -1
  39. package/dist/runtime.js +4 -8
  40. package/dist/runtime.js.map +1 -1
  41. package/dist/types.d.ts +22 -2
  42. package/dist/types.d.ts.map +1 -1
  43. package/package.json +4 -4
  44. package/dist/advertise.d.ts +0 -13
  45. package/dist/advertise.d.ts.map +0 -1
  46. package/dist/advertise.js +0 -12
  47. package/dist/advertise.js.map +0 -1
package/.env.defaults CHANGED
@@ -1,16 +1,11 @@
1
- # --- @plurnk/plurnk-execs : runtime registration policy (SPEC §3.3) ---
2
- # Purely subtractive: these only ever REMOVE tags from the registered set — there
3
- # is no force-enable verb. Unset ⇒ every discovered runtime is registered. This
4
- # package sets no operative default; the comments below ARE the operator docs.
1
+ # @plurnk/plurnk-execs — installed runtime admission.
2
+ # These filters only remove registered runtimes; they cannot install or force-enable tools.
5
3
 
6
- # Allowlist: register ONLY these tags (comma-separated); every other tag is off.
4
+ # Comma-separated allowlist; unset = all discovered runtimes.
7
5
  # PLURNK_EXECS_ONLY=sh,jq
8
-
9
- # Per-tag kill-switch (case-insensitive): set any registered tag to 0 to drop it.
10
- # Absent ⇒ the runtime stays enabled. e.g.:
6
+ # Per-runtime switch, case-insensitive: 0 = disabled; absent = enabled.
11
7
  # PLURNK_EXECS_PYTHON3=0
12
8
  # PLURNK_EXECS_BC=0
13
-
14
- # Maximum source characters retained from third-party diagnostics in a failed
15
- # operation's model-facing Problem detail. Empty/unset leaves them unbounded.
9
+ # PLURNK_EXECS_QUESTION=0
10
+ # Maximum diagnostic characters in model-facing errors; empty/unset = unbounded.
16
11
  PLURNK_EXECS_ERROR_DETAIL_LIMIT=512
package/README.md CHANGED
@@ -51,10 +51,15 @@ One package may claim several tags; the consumer instantiates and probes each
51
51
  tag independently. A name uses lowercase URI-scheme syntax
52
52
  `[a-z][a-z0-9+.-]*`; `only` is reserved by runtime policy. `summary` is the
53
53
  one-line capability description. `invocation` defines the body and optional
54
- target buckets plus one canonical example used for dispatch enforcement and the
55
- generated `worker://~/_plurnk/tools/` document. A `docs/<tag>.md` file supplies
54
+ target buckets used for dispatch enforcement and a canonical example for its
55
+ generated `worker:///_plurnk/plurnk/<tag>.md` document. A `docs/<tag>.md` file supplies
56
56
  supplemental detail. See {§executor-runtime-declaration}.
57
57
 
58
+ For JSON inputs, supply `invocation.inputSchema` instead of an authored example
59
+ or signature. The shared renderer lists required top-level fields and links to
60
+ the complete raw schema and description; nested and optional inputs stay in
61
+ that on-demand document ({§executor-input-schema-preview}).
62
+
58
63
  ### Implement the executor
59
64
 
60
65
  | Runtime shape | Base class | Author-owned hooks |
@@ -85,11 +90,33 @@ The EXEC `(target)` slot is runtime-specific:
85
90
  The consumer supplies both `cwd` and a resolved `target`; the leaf maps them
86
91
  to its tool rather than reconstructing filesystem or scheme policy.
87
92
 
93
+ ### Own invocation metadata
94
+
95
+ `prepare(input)` validates the tool's raw header metadata before admission and
96
+ returns the effective cwd. `run(args)` receives that cwd and the original
97
+ metadata, separately from the unchanged body. Source acquisition never inherits
98
+ EXEC metadata. `BaseExecutor` supplies `[{"cwd": "..."}]`; `SubprocessExecutor` adds
99
+ literal script arguments with `[{"args": ["arg",...]}]`. Override preparation for
100
+ tool-specific options. See {§executor-metadata}.
101
+
102
+ ### Receive live input
103
+
104
+ In-process executors may call `args.registerInput?.(async ({ body, metadata,
105
+ signal }) => result)` once during `run()`. Keep the receiver local to that
106
+ invocation, honor cancellation, and return a standard `SchemeResult` for each
107
+ delivery. The consumer routes SEND to the existing execution address and owns
108
+ ordering, deadlines, permissions, and proposals. This does not expose Worker
109
+ identity or grant access to stored output.
110
+
111
+ `SubprocessExecutor` supplies this receiver when launched with `[{"stdin": "open"}]`.
112
+ SEND writes exact UTF-8 input; `[{"eof": true}]` ends stdin. No option means ordinary
113
+ batch EOF. See [the live-input contract](SPEC.md#executor-live-input-invocation-local-input).
114
+
88
115
  ### Address output
89
116
 
90
117
  The runtime tag is also the output scheme. A subprocess result is therefore
91
- read at an address such as `sh:///1/2/3#stdout`, while a structured result may
92
- be `sqlite:///1/2/3#results`. `exec://` is not an output address. Executors only
118
+ read at an address such as `sh:///ab3d5678#stdout`, while a structured result may
119
+ be `sqlite:///f2c49a10#results`. `exec://` is not an output address. Executors only
93
120
  produce channels; the consumer owns storage and every later READ/FIND. See
94
121
  {§executor-output-address}.
95
122
 
@@ -116,7 +143,6 @@ Policy is subtractive; a downstream layer cannot restore a removed tag. See
116
143
 
117
144
  - `BaseExecutor`, `SubprocessExecutor`, and `SpawnArgs`.
118
145
  - `discover`, `Policy`, `RuntimeTag`, and runtime discovery types.
119
- - `Advertise`, a deprecated frozen 1.x compatibility helper with no current service path.
120
146
  - `ErrorDetail` and `PLURNK_EXECS_ERROR_DETAIL_LIMIT`.
121
147
  - Executor arguments, channel, availability, and effect types.
122
148
  - `Results`, result types, and runtime-neutral Notice types.
package/SPEC.md CHANGED
@@ -39,6 +39,7 @@ abstract class BaseExecutor {
39
39
 
40
40
  abstract get channels(): Readonly<Record<string, ChannelDecl>>;
41
41
  get defaultChannel(): string;
42
+ prepare(input: ExecInput): Promise<ExecPreparation>;
42
43
  abstract run(args: ExecArgs): Promise<ExecResult>;
43
44
  probe(signal?: AbortSignal): Promise<RuntimeAvailability>;
44
45
  effect(target: string | null): Effect;
@@ -55,8 +56,10 @@ interface ExecArgs {
55
56
  body: string;
56
57
  cwd: string | null;
57
58
  target: string | null;
59
+ metadata: readonly string[] | null;
58
60
  env?: NodeJS.ProcessEnv;
59
61
  signal: AbortSignal;
62
+ registerInput?(receiver: ExecInputReceiver): void;
60
63
  write(channel: string, chunk: string, mimetype?: string): void;
61
64
  setState(channel: string, state: ChannelState): void;
62
65
  emit(notice: Notice): void;
@@ -71,10 +74,9 @@ interface ExecArgs {
71
74
  `ExecArgs` deliberately carries **no Worker identity**: an executor is a
72
75
  worker-agnostic capability, and `run` may assume nothing about which Worker,
73
76
  Loop, or workspace invoked it beyond the arguments given. A runtime whose
74
- behavior depends on a Worker — an attached MCP server, a Worker Functionality
75
- manager — is published per Worker by its owning module, closing over that
76
- identity at publication ({§functionality-model-projection} in the core
77
- specification; the MCP host is the precedent). Consumers must not smuggle
77
+ behavior depends on a workspace — an attached MCP server or Functionality
78
+ manager — is published per workspace by its module, closing over that
79
+ identity at publication ({§functionality-model-projection}). Consumers must not smuggle
78
80
  identity through `env` or `body` conventions.
79
81
 
80
82
  interface RuntimeAvailability {
@@ -101,8 +103,10 @@ surface.
101
103
  | `body` | The authored EXEC body, interpreted according to the runtime's invocation declaration. |
102
104
  | `cwd` | Consumer-selected project working directory, or `null` for a runtime that has none. |
103
105
  | `target` | Consumer-realized optional EXEC target. Its representation and role come from the invocation declaration. |
106
+ | `metadata` | Exact ordered header blocks owned by the invoked executor, retained separately from the body and target. |
104
107
  | `env` | Exact child environment when supplied. A subprocess must use it instead of reconstructing host policy. |
105
108
  | `signal` | Consumer cancellation. Every executor must honor it at each cancellable boundary. |
109
+ | `registerInput` | Optionally register one live input receiver for this invocation under {§executor-live-input}. |
106
110
  | `write` | Append to a declared channel. An optional mimetype replaces that channel's per-call output type. |
107
111
  | `setState` | Move a declared channel from `active` to terminal `closed` or `errored`. |
108
112
  | `emit` | Publish a transient, nonterminal Notice. |
@@ -113,6 +117,61 @@ The executor receives callbacks, never a database, subscription registry,
113
117
  packet builder, or wake mechanism. Related lifecycle behavior remains on the
114
118
  consumer side of {§executor-role}.
115
119
 
120
+ ### §executor-metadata Invocation metadata ownership
121
+
122
+ The invoked executor owns its `[metadata]` for every target kind. The source
123
+ scheme supplies the program or data; it does not inherit the invoking tool's
124
+ options. An internal source READ therefore carries no EXEC metadata. An
125
+ authored READ retains its own source-scheme metadata contract.
126
+
127
+ | Stage | Contract |
128
+ | ----- | -------- |
129
+ | Preparation | Optional `prepare(input)` receives `runtime`, `body`, logical `target`, default `cwd`, and ordered raw `metadata`, before effect admission or source acquisition. It validates options without executing the program and returns `200` with a concrete `cwd` or `null`, or one universal failure. No hook means no metadata support. |
130
+ | Framework default | `BaseExecutor.prepare` accepts `[{"cwd": "<directory>"}]`. Relative directories resolve against the supplied default cwd; an absent override preserves it. Unknown fields, duplicates, malformed values, and nonexistent directories are refused. Tools can override preparation to own different metadata. |
131
+ | Subprocess options | `SubprocessExecutor` additionally accepts `[{"stdin": "open"}]` ({§executor-stdin}) and `[{"args": ["arg",...]}]`. The JSON array contains strings without NUL, preserves order and empty/whitespace-containing arguments, and appends directly to the spawn argument vector without shell parsing. Inline programs retain their interpreter's usual argument conventions. |
132
+ | Execution | `run()` receives the prepared cwd, realized target, unchanged body, and original metadata. The subprocess family uses the same option parser to obtain argv; cwd is already prepared and is not resolved a second time. |
133
+ | Evidence | Raw metadata follows the existing transient proposal handoff, never added to proposal attrs or emitted as receipt metadata. Expected preparation failures retain the tool's Problem; malformed preparation results are internal contract failures. |
134
+
135
+ Metadata does not turn inline programs or script stdin into JSON envelopes.
136
+ Executable references document advanced options on demand; no language-level
137
+ option names or hot-path teaching are required.
138
+
139
+ ### §executor-live-input Invocation-local input
140
+
141
+ `ExecArgs.registerInput?(receiver)` binds one receiver to the current invocation.
142
+ The receiver accepts `{ body: string, metadata: readonly string[] | null,
143
+ signal: AbortSignal }` and resolves a universal `SchemeResult`. The body is exact
144
+ authored text; metadata belongs to the receiver. Registration is once per run,
145
+ never executor-instance state. An absent callback means the consumer cannot
146
+ deliver live input. A receiver must honor cancellation at each delivery boundary.
147
+
148
+ | Responsibility | Owner |
149
+ | --- | --- |
150
+ | Address, workspace admission, capability checks, proposals | Consumer, using the existing execution identity and effect. |
151
+ | Delivery ordering, bounded backpressure, lifetime | Consumer; serialize accepted deliveries, never replay an ambiguous write, retire on execution settlement/cancellation. |
152
+ | Input meaning, framing, EOF, delivery result | Receiver; acceptance does not imply consumption, command success, or execution completion. |
153
+ | Output, progress, completion | Existing `write`, `setState`, and `run` result; input introduces no second output or wake path. |
154
+
155
+ #### §executor-stdin Subprocess input
156
+
157
+ `SubprocessExecutor` accepts `[{"stdin": "open"}]` at invocation. The initial recipe
158
+ input precedes SEND bodies and stdin remains open; without this option, existing
159
+ batch input and EOF behavior is unchanged. The same input adapter serves `jq`.
160
+
161
+ | SEND input | Effect |
162
+ | --- | --- |
163
+ | Body, no metadata | Write its exact UTF-8 bytes, including authored newlines; no implicit newline. Empty input is a no-op, not EOF. |
164
+ | `[{"eof": true}]` with optional body | Write the body, then close stdin. Later SENDs return `410 input-closed`, including repeated EOF. |
165
+ | Other or duplicate metadata | `400`, before writing. |
166
+ | Pipe failure | Factual delivery failure with the native error code; no automatic retry. |
167
+ | Delivery cancellation | Stop the pending write and close stdin. The process retains its ordinary lifecycle. |
168
+
169
+ A successful receipt reports `bytesAccepted` and `inputClosed`, with those facts
170
+ in the ordinary model-visible `detail`. It acknowledges the writable pipe, not
171
+ application processing. Subprocess leaves inherit this
172
+ contract; logical executors need not offer a receiver. In particular, SQLite,
173
+ client-interaction tools, and MCP transports do not gain stdin through this API.
174
+
116
175
  ### §executor-channels Channels
117
176
 
118
177
  The executor declares every channel it may write. The consumer seeds a stream
@@ -135,8 +194,10 @@ reformatting arbitrary source JSON read from an entry.
135
194
  subprocess `exitCode`. A result with `status >= 400` carries exactly one RFC
136
195
  9457 Problem whose status agrees with the result. Expected runtime failures
137
196
  resolve as failure results and leave affected channels `errored`; they do not
138
- throw. The consumer validates the boundary and converts a throw or invalid
139
- result into its own durable failure before closing the stream.
197
+ throw. Rejection by an aborted execution's signal reason or an `AbortError`
198
+ is cancellation, not an executor defect; the consumer preserves its distinct
199
+ cancellation/deadline result. Other throws and invalid results become durable
200
+ contract failures before the consumer closes the stream.
140
201
 
141
202
  A nonzero subprocess exit directs the caller only to inspect both stdout and
142
203
  stderr because either may contain the useful diagnostic; it does not presume
@@ -202,27 +263,42 @@ no second scheme manifest.
202
263
 
203
264
  | Output | Model-facing address |
204
265
  | ------------------------- | ------------------------------------------------------------------ |
205
- | Calling worker's stream | `<tag>:///<loop>/<turn>/<sequence>/EXEC#<channel>` |
206
- | Fragmentless read | `<tag>:///<loop>/<turn>/<sequence>/EXEC` → the declared default channel |
207
- | Example subprocess stdout | `sh:///1/2/3/EXEC#stdout` |
208
- | Example structured result | `sqlite:///1/2/3/EXEC#results` |
266
+ | Workspace execution stream | `<tag>:///<id>#<channel>` |
267
+ | Fragmentless read | `<tag>:///<id>` → the declared default channel |
268
+ | Example subprocess stdout | `sh:///ab3d5678#stdout` |
269
+ | Example structured result | `sqlite:///c2d45678#results` |
209
270
 
210
271
  The executor only produces through `write` and `setState`. The consumer stores
211
272
  the entry and serves every later READ/FIND through uniform entry machinery.
212
273
  An executor does not implement a private read face, orientation receipt,
213
- slicer, or index. Owner-qualified cross-worker addresses and packet projection
214
- belong to core's {§stream-owner-scoped} and {§exec-stream} contracts.
274
+ slicer, or index. Workspace-stable resource addresses and packet projection
275
+ belong to core's {§execution-output-identity} and {§exec-stream} contracts.
276
+ Address resolution binds the workspace without requiring an exact stored row:
277
+ FIND patterns and descendant resource paths participate in ordinary discovery;
278
+ an exact READ reports its own missing resource ({§entry-address-resolution}).
279
+
280
+ `publishedChannel` selects one declared channel for automatic stream observations;
281
+ absent/null selects all channels. Non-published channels remain READ-addressable.
282
+ This is the ordinary subscription publication selection, not another delivery path.
215
283
 
216
284
  ### §executor-entry-sink Optional materialization
217
285
 
218
- `entry(path, content, { tags, mimetype? })` is a consumer-implemented sink, not
219
- executor access to storage. The consumer materializes or updates an entry,
220
- uses the tags to classify its ordinary journal announcement, and resolves the
221
- canonical model-facing address. Tags never become resource metadata.
222
- `content === null` requests consumer-sourced
223
- acquisition. Rejection means only that this materialization failed; it does not
224
- invalidate the executor's upstream result. When the sink is absent, the
225
- executor preserves its result without inventing a materialization verdict.
286
+ `entry(path, content, { mimetype?, name? })` is a consumer-implemented sink, not
287
+ executor access to storage. It returns the canonical address of an ordinary
288
+ resource; publication alone does not attach media or wake a Worker.
289
+
290
+ | Input | Meaning |
291
+ | --- | --- |
292
+ | Absolute resource URL | Materialize or update that resource through its scheme owner. |
293
+ | `path: null` | Publish beneath this invocation's output address, in `resources/`. Preserve `name` as one URI-encoded component; otherwise allocate an eight-character hexadecimal identifier. Duplicate names receive a hash suffix, never overwrite another publication. The returned address is workspace-stable and carries no Worker selector. |
294
+ | String content + mimetype | Supplied text; preserve its source layout. |
295
+ | `Uint8Array` content + mimetype | Supplied bytes, retained as bytes, not a JSON/base64 body. |
296
+ | `content: null` | Consumer-sourced acquisition from an absolute HTTP(S) URL. |
297
+
298
+ Core owns names, storage, publication evidence, and byte acquisition. Executors
299
+ publish returned addresses in their result. Rejection means materialization
300
+ failed, not that the upstream operation had no effect. An absent sink cannot
301
+ be reported as successful resource publication.
226
302
 
227
303
  ## §executor-discovery Discovery and registration
228
304
 
@@ -284,7 +360,7 @@ at boot; changing package membership or configuration requires a restart.
284
360
  | ------------- | -------------------------------------------------------------------------------------------------- |
285
361
  | `name` | Canonical runtime tag and derived output-scheme name, admitted below. |
286
362
  | `glyph` | Optional presentation glyph. |
287
- | `summary` | Required one-line description, or `{ from: "tools" }` for an exact family inventory. |
363
+ | `summary` | Required one-line description, or `{ from: "tools", description?: string }` for an exact family inventory with an optional one-line purpose. |
288
364
  | `invocation` | Required body and target contract, validated and normalized below. |
289
365
  | `details` | Optional supplemental Markdown. `docs/<tag>.md` wins over the inline manifest field. |
290
366
  | `attribution` | Published per-tag projection of the validated package declaration ({§plugin-attribution}). |
@@ -294,7 +370,11 @@ The framework validates and carries the summary source, invocation, and suppleme
294
370
  details as separate facts. A `{ from: "tools" }` summary is valid only for a
295
371
  runtime implementing {§executor-tool-registry}; the consumer resolves it from
296
372
  the exact effective tool set, after capability attenuation, so a denied tool
297
- cannot survive in the family orientation line. The consumer deterministically renders the
373
+ cannot survive in the family orientation line. The inventory renders as
374
+ one compact executable fence naming `runtime (target|...)`, with the optional
375
+ purpose as a trailing aside and body line breaks represented by literal `\n`;
376
+ an authored string remains a description, without expanding its tool inventory.
377
+ The consumer deterministically renders the
298
378
  model-facing tool document from either the static invocation or the executor's exact
299
379
  {§executor-tool-registry}; authors never duplicate Summary or Invocation in
300
380
  prose. A
@@ -320,19 +400,20 @@ details; the generated tool document owns the one model-facing H1.
320
400
  | `target.kind` | One of the structural realization modes below. |
321
401
  | `exclusive` | Optional `true`: body and target are alternative inputs and supplying both is refused. |
322
402
  | `example` | Concise executable witness with a one-line `body`, `target`, or both as the declared shape permits. |
323
- | `signature` | One-line structural body signature for a schema-backed invocation; no fabricated argument values. |
403
+ | `signature` | Authored one-line structural body signature when no JSON Schema defines the input. |
404
+ | `inputSchema` | Original JSON Schema object for the input; the common document renderer derives its preview. |
324
405
 
325
406
  | Target kind | Consumer realization |
326
407
  | ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
327
408
  | `literal` | Preserve the complete authored target string. It is an executor identifier, never a scheme read or filesystem classification. |
328
409
  | `path` | Accept only a local or `file://` path and pass that path directly. |
329
- | `resource` | Pass a local/file path directly; resolve a non-file data-scheme address through one exact READ and materialize its string representation to a temporary file. |
410
+ | `resource` | Pass a local/file path directly. After acceptance, resolve a non-file data-scheme address through one exact READ; use its owner-supplied native file or a standalone temporary representation ({§exec-source-temporary}). |
330
411
  | `script` | As `resource`, and the local path must be an existing file: a directory or an absent path is refused before anything spawns ({§exec-executor-slot}). |
331
412
 
332
413
  Every EXEC must supply at least a body or target even when neither field is
333
414
  independently required. A target's meaning never changes because the body is
334
- empty or non-empty. `exclusive` requires a target declaration. Exactly one of `example` or
335
- `signature` is present. An example must satisfy the same required, refused,
415
+ empty or non-empty. `exclusive` requires a target declaration. Exactly one of `example`,
416
+ `signature`, or `inputSchema` is present. An example must satisfy the same required, refused,
336
417
  and exclusive buckets and parse as exactly one EXEC section for the runtime. A
337
418
  signature is presentation, not an executable example; dispatch still enforces
338
419
  the invocation's body and target declarations. An invocation declaration
@@ -377,19 +458,32 @@ I/O boundary rather than making packet assembly perform network discovery.
377
458
  §executor-tool-document **Tool documents are the model-facing executor
378
459
  directory.** A general runtime's document carries exact H2 `Summary` and
379
460
  `Invocation` sections plus its executable witness. Its Summary is that compact
380
- invocation witness in inline code, with the authored description as an
381
- operation annotation on its invocation line and a literal `\n` before a
382
- one-line body. An exact registry instead renders one compact family document
383
- whose Summary remains the family description and whose H2 `Invocation`
384
- contains one `example` fence with
385
- every exact annotated EXEC heading and signature, plus one child document per
386
- tool. Each child's Summary is its annotated invocation form; its H2 `Invocation`
387
- contains the literal target, signature, and supplemental input details.
461
+ invocation witness in plain text, with the authored description as an
462
+ operation aside on its invocation line and a literal `\n` before a
463
+ one-line body. The bundled subprocess interpreters declare concrete inline-program
464
+ examples; their catalog summaries include the program body without a script target.
465
+ An exact registry instead renders one compact family document whose Summary is
466
+ authored or derived from its effective tools. Its H2 `Tools` contains one
467
+ executable fence per tool, naming the runtime and exact target, with its aside
468
+ and input preview. A schema-backed invocation
469
+ links from its aside to a child document containing the full description and
470
+ original input JSON Schema. Non-schema invocations retain their authored witness
471
+ and supplemental details.
388
472
  Supplemental `details` follows framework-owned sections and cannot own identity,
389
473
  invocation, or admission. The consumer chooses resource addresses, materializes
390
474
  the documents, and exposes each Summary through ordinary FIND metadata. No
391
475
  executor table or executor-specific discovery protocol exists.
392
476
 
477
+ §executor-input-schema-preview Schema-backed previews list only explicitly
478
+ required top-level fields, with quoted names and declared broad JSON types.
479
+ Nested objects and arrays stay opaque; an undeclared type is `unknown`. Optional
480
+ fields, references, constraints, and conditional branches are not interpreted.
481
+ The preview is not a complete signature or validation promise. Its schema link
482
+ provides the complete original schema, including descriptions, definitions,
483
+ references, and constraints; repository-owned referenced schemas are included
484
+ unchanged alongside it. Documentation performs no network reference resolution.
485
+ Output schemas are not input teaching.
486
+
393
487
  Runtime-name admission is one identity contract:
394
488
 
395
489
  | Constraint | Contract |
@@ -429,7 +523,8 @@ swallowed.
429
523
 
430
524
  ### §executor-policy Subtractive runtime policy
431
525
 
432
- Discovery applies the daemon's registration policy to every tag. The exported
526
+ Registration applies the daemon's policy to every tag, whether discovered from
527
+ a package or supplied by a daemon module, including workspace-scoped runtimes. The exported
433
528
  `Policy` parser can apply the same grammar to additional consumer-owned layers.
434
529
 
435
530
  | Variable | Enforced effect |
@@ -440,18 +535,11 @@ Discovery applies the daemon's registration policy to every tag. The exported
440
535
  | `Policy.enabledAcross(tag, [a, b, ...])` | Keep the tag only when every supplied layer enables it. |
441
536
 
442
537
  Policy is purely subtractive: no layer can re-enable a tag removed by another.
443
- Boot-disabled tags are absent from the registry and returned in
444
- `Discovery.disabled`. Workspace and loop admission remain consumer concerns;
538
+ Disabled tags publish neither an executor nor its scheme or tool documents;
539
+ package discovery also returns them in `Discovery.disabled`.
540
+ Workspace and loop admission remain consumer concerns;
445
541
  the framework does not define a second Active/Available state machine.
446
542
 
447
- ### §executor-advertise-compat Frozen `Advertise` compatibility
448
-
449
- | Surface | 1.x contract |
450
- |--------------------------------------|-------------------------------------------------------------------------------------------------------------|
451
- | `Advertise.contribute(registry, fn)` | Filters the supplied registry; returns `NO_EXECS_NOTICE` only when the filtered result is empty. |
452
- | Architectural ownership | None. It does not discover, probe, admit, dispatch, or render; the composed service never calls it. |
453
- | Evolution | Frozen. New consumers compose discovery and `Policy` with their own presentation. Removal is SemVer-major. |
454
-
455
543
  ## §executor-default-inventory Current installed set
456
544
 
457
545
  The default `@plurnk/plurnk-service` composition installs the following
@@ -490,9 +578,10 @@ interface SpawnArgs {
490
578
  | `python3` | `python3 -c <body>`. |
491
579
  | Other default-base runtime | `<runtime> -c <body>`; specialized leaves override this fallback. |
492
580
 
493
- With a target, the target is the program and the body is its stdin. The working
581
+ With a target, the target is the program and the body is its stdin. Each leaf
582
+ uses its interpreter's file form (`awk -f <target>`, for example). The working
494
583
  directory is the consumer's `cwd` — the project root, or the directory a
495
- `{cwd=<directory>}` block on the heading names ({§exec-executor-slot}); a
584
+ `[{"cwd": "<directory>"}]` block on the heading names ({§exec-executor-slot}); a
496
585
  directory is never a target. Data runtimes define their own declared target
497
586
  kind and role.
498
587
 
@@ -1,5 +1,5 @@
1
1
  import type { SchemeHandler, SchemeManifest } from "@plurnk/plurnk-schemes";
2
- import type { ChannelDecl, Effect, ExecArgs, ExecResult, ExecutorMetadata, RuntimeAvailability } from "./types.ts";
2
+ import type { ChannelDecl, Effect, ExecArgs, ExecInput, ExecPreparation, ExecResult, ExecutorMetadata, RuntimeAvailability } from "./types.ts";
3
3
  export default abstract class BaseExecutor implements SchemeHandler {
4
4
  readonly runtime: string;
5
5
  readonly glyph: string;
@@ -7,7 +7,9 @@ export default abstract class BaseExecutor implements SchemeHandler {
7
7
  get manifest(): SchemeManifest;
8
8
  get traits(): ReadonlyArray<string> | undefined;
9
9
  get defaultChannel(): string;
10
+ get publishedChannel(): string | null;
10
11
  abstract get channels(): Readonly<Record<string, ChannelDecl>>;
12
+ prepare(input: ExecInput): Promise<ExecPreparation>;
11
13
  abstract run(args: ExecArgs): Promise<ExecResult>;
12
14
  probe(_signal?: AbortSignal): Promise<RuntimeAvailability>;
13
15
  effect(_target: string | null): Effect;
@@ -1 +1 @@
1
- {"version":3,"file":"BaseExecutor.d.ts","sourceRoot":"","sources":["../src/BaseExecutor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAWnH,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,YAAa,YAAW,aAAa;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,gBAAgB,EAG/C;IAaD,IAAI,QAAQ,IAAI,cAAc,CAU7B;IAGD,IAAI,MAAM,IAAI,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAE9C;IAID,IAAI,cAAc,IAAI,MAAM,CAE3B;IAOD,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAO/D,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAmB5C,KAAK,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAE/D;IAQD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAErC;CACJ"}
1
+ {"version":3,"file":"BaseExecutor.d.ts","sourceRoot":"","sources":["../src/BaseExecutor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAY/I,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,YAAa,YAAW,aAAa;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,gBAAgB,EAG/C;IAaD,IAAI,QAAQ,IAAI,cAAc,CAU7B;IAGD,IAAI,MAAM,IAAI,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAE9C;IAID,IAAI,cAAc,IAAI,MAAM,CAE3B;IAGD,IAAI,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEpC;IAOD,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAE/D,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,eAAe,CAAC,CAElD;IAOD,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAmB5C,KAAK,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAE/D;IAQD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAErC;CACJ"}
@@ -1,4 +1,5 @@
1
1
  import { OutputScheme } from "@plurnk/plurnk-schemes";
2
+ import InvocationMetadata from "./InvocationMetadata.js";
2
3
  // Base class for runtime executors (parallel to plurnk-mimetypes' BaseHandler).
3
4
  // A `@plurnk/plurnk-execs-*` sibling subclasses this and implements `run()`.
4
5
  // The framework instantiates one executor per matched runtime tag, injecting
@@ -44,6 +45,13 @@ export default class BaseExecutor {
44
45
  get defaultChannel() {
45
46
  return Object.keys(this.channels)[0] ?? "";
46
47
  }
48
+ // Select the existing stream-publication contract; null publishes every channel.
49
+ get publishedChannel() {
50
+ return null;
51
+ }
52
+ prepare(input) {
53
+ return InvocationMetadata.prepare(input);
54
+ }
47
55
  // Whether this runtime's execution environment is usable in the current
48
56
  // deployment. Default: available (pure / in-process runtimes — e.g. node,
49
57
  // sqlite, where the daemon itself satisfies the dependency). Subclasses
@@ -1 +1 @@
1
- {"version":3,"file":"BaseExecutor.js","sourceRoot":"","sources":["../src/BaseExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAItD,gFAAgF;AAChF,6EAA6E;AAC7E,6EAA6E;AAC7E,yEAAyE;AACzE,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,4DAA4D;AAC5D,MAAM,CAAC,OAAO,OAAgB,YAAY;IAC7B,OAAO,CAAS;IAChB,KAAK,CAAS;IAEvB,YAAY,EAAE,OAAO,EAAE,KAAK,EAAoB;QAC5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,kEAAkE;IAClE,gCAAgC;IAChC,8EAA8E;IAC9E,8EAA8E;IAC9E,6EAA6E;IAC7E,yDAAyD;IACzD,2EAA2E;IAC3E,0EAA0E;IAC1E,6EAA6E;IAC7E,2EAA2E;IAC3E,8EAA8E;IAC9E,IAAI,QAAQ;QACR,OAAO,YAAY,CAAC,mBAAmB,CAAC;YACpC,IAAI,EAAE,IAAI,CAAC,OAAO;YAClB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,MAAM,CAAC,WAAW,CACxB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAC7E;YACD,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,MAAM,EAAE,IAAI,CAAC,MAAM;SACtB,CAAC,CAAC;IACP,CAAC;IAED,+DAA+D;IAC/D,IAAI,MAAM;QACN,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,yEAAyE;IACzE,oEAAoE;IACpE,IAAI,cAAc;QACd,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,CAAC;IAgBD,wEAAwE;IACxE,0EAA0E;IAC1E,wEAAwE;IACxE,iEAAiE;IACjE,EAAE;IACF,8EAA8E;IAC9E,yEAAyE;IACzE,2EAA2E;IAC3E,yEAAyE;IACzE,4DAA4D;IAC5D,EAAE;IACF,sEAAsE;IACtE,4EAA4E;IAC5E,yEAAyE;IACzE,kEAAkE;IAClE,6EAA6E;IAC7E,iBAAiB;IACjB,KAAK,CAAC,KAAK,CAAC,OAAqB;QAC7B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,4EAA4E;IAC5E,6EAA6E;IAC7E,8EAA8E;IAC9E,yEAAyE;IACzE,wEAAwE;IACxE,gEAAgE;IAChE,MAAM,CAAC,OAAsB;QACzB,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ"}
1
+ {"version":3,"file":"BaseExecutor.js","sourceRoot":"","sources":["../src/BaseExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAGtD,OAAO,kBAAkB,MAAM,yBAAyB,CAAC;AAEzD,gFAAgF;AAChF,6EAA6E;AAC7E,6EAA6E;AAC7E,yEAAyE;AACzE,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,4DAA4D;AAC5D,MAAM,CAAC,OAAO,OAAgB,YAAY;IAC7B,OAAO,CAAS;IAChB,KAAK,CAAS;IAEvB,YAAY,EAAE,OAAO,EAAE,KAAK,EAAoB;QAC5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,kEAAkE;IAClE,gCAAgC;IAChC,8EAA8E;IAC9E,8EAA8E;IAC9E,6EAA6E;IAC7E,yDAAyD;IACzD,2EAA2E;IAC3E,0EAA0E;IAC1E,6EAA6E;IAC7E,2EAA2E;IAC3E,8EAA8E;IAC9E,IAAI,QAAQ;QACR,OAAO,YAAY,CAAC,mBAAmB,CAAC;YACpC,IAAI,EAAE,IAAI,CAAC,OAAO;YAClB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,MAAM,CAAC,WAAW,CACxB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAC7E;YACD,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,MAAM,EAAE,IAAI,CAAC,MAAM;SACtB,CAAC,CAAC;IACP,CAAC;IAED,+DAA+D;IAC/D,IAAI,MAAM;QACN,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,yEAAyE;IACzE,oEAAoE;IACpE,IAAI,cAAc;QACd,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,CAAC;IAED,iFAAiF;IACjF,IAAI,gBAAgB;QAChB,OAAO,IAAI,CAAC;IAChB,CAAC;IASD,OAAO,CAAC,KAAgB;QACpB,OAAO,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IASD,wEAAwE;IACxE,0EAA0E;IAC1E,wEAAwE;IACxE,iEAAiE;IACjE,EAAE;IACF,8EAA8E;IAC9E,yEAAyE;IACzE,2EAA2E;IAC3E,yEAAyE;IACzE,4DAA4D;IAC5D,EAAE;IACF,sEAAsE;IACtE,4EAA4E;IAC5E,yEAAyE;IACzE,kEAAkE;IAClE,6EAA6E;IAC7E,iBAAiB;IACjB,KAAK,CAAC,KAAK,CAAC,OAAqB;QAC7B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,4EAA4E;IAC5E,6EAA6E;IAC7E,8EAA8E;IAC9E,yEAAyE;IACzE,wEAAwE;IACxE,gEAAgE;IAChE,MAAM,CAAC,OAAsB;QACzB,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ"}
@@ -0,0 +1,5 @@
1
+ export default class DocFile {
2
+ static read(dir: string, tag: string): Promise<string | null>;
3
+ static body(source: string, tag: string): string;
4
+ }
5
+ //# sourceMappingURL=DocFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocFile.d.ts","sourceRoot":"","sources":["../src/DocFile.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,OAAO,OAAO,OAAO;IAExB,OAAa,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMlE;IAED,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAM/C;CACJ"}
@@ -0,0 +1,28 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ // A package's `docs/<tag>.md` — the authored teaching carried beneath a generated tool
4
+ // document's header ({§executor-discovery}). Runtimes and Functionality families share this
5
+ // one rule: the file stays a valid standalone document whose exact authoring title `# <tag>`
6
+ // is removed, because the generated document owns the one model-facing H1.
7
+ export default class DocFile {
8
+ // The tag's supplemental detail file, or null when the package ships none.
9
+ static async read(dir, tag) {
10
+ try {
11
+ return DocFile.body(await fs.readFile(path.join(dir, "docs", `${tag}.md`), "utf-8"), tag);
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ static body(source, tag) {
18
+ const title = `# ${tag}`;
19
+ if (source === title || source === `${title}\n`)
20
+ return "";
21
+ if (source.startsWith(`${title}\r\n`))
22
+ return source.slice(title.length + 2).replace(/^\r?\n/u, "");
23
+ if (source.startsWith(`${title}\n`))
24
+ return source.slice(title.length + 1).replace(/^\r?\n/u, "");
25
+ return source;
26
+ }
27
+ }
28
+ //# sourceMappingURL=DocFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocFile.js","sourceRoot":"","sources":["../src/DocFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,uFAAuF;AACvF,4FAA4F;AAC5F,6FAA6F;AAC7F,2EAA2E;AAC3E,MAAM,CAAC,OAAO,OAAO,OAAO;IACxB,2EAA2E;IAC3E,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,GAAW;QACtC,IAAI,CAAC;YACD,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;QAC9F,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,MAAc,EAAE,GAAW;QACnC,MAAM,KAAK,GAAG,KAAK,GAAG,EAAE,CAAC;QACzB,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,GAAG,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC;QAC3D,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,KAAK,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACpG,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,KAAK,IAAI,CAAC;YAAE,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAClG,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ"}
@@ -0,0 +1,22 @@
1
+ import { type SchemeResult } from "@plurnk/plurnk-schemes";
2
+ import type { ExecInput, ExecPreparation } from "./types.ts";
3
+ type Options = {
4
+ cwd?: string;
5
+ args: string[];
6
+ stdin?: "open";
7
+ };
8
+ type Accepted = {
9
+ args?: boolean;
10
+ stdin?: boolean;
11
+ };
12
+ type Parsed = {
13
+ options: Options;
14
+ } | {
15
+ failure: SchemeResult;
16
+ };
17
+ export default class InvocationMetadata {
18
+ static parse(input: ExecInput, accepted?: Accepted): Parsed;
19
+ static prepare(input: ExecInput, accepted?: Accepted): Promise<ExecPreparation>;
20
+ }
21
+ export {};
22
+ //# sourceMappingURL=InvocationMetadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvocationMetadata.d.ts","sourceRoot":"","sources":["../src/InvocationMetadata.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7D,KAAK,OAAO,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAChE,KAAK,QAAQ,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AACpD,KAAK,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAK/D,MAAM,CAAC,OAAO,OAAO,kBAAkB;IACnC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,GAAE,QAAa,GAAG,MAAM,CA6B9D;IAED,OAAa,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,GAAE,QAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAgBxF;CACJ"}
@@ -0,0 +1,63 @@
1
+ import { stat } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { MetadataOptions, Results } from "@plurnk/plurnk-schemes";
4
+ // {§executor-metadata} The executor framework owns these options, not Core. The
5
+ // heading's `[metadata]` is one JSON array of option objects ({§scheme-metadata-modifier});
6
+ // the keys this framework knows are `cwd`, `args`, and `stdin`.
7
+ export default class InvocationMetadata {
8
+ static parse(input, accepted = {}) {
9
+ const fail = (code, detail) => ({
10
+ failure: Results.failure("executor:metadata", code, 400, detail, {}, {
11
+ runtime: input.runtime, retryable: false,
12
+ }),
13
+ });
14
+ const read = MetadataOptions.parse(input.metadata, "executor:metadata", { runtime: input.runtime });
15
+ if ("failure" in read)
16
+ return read;
17
+ const options = { args: [] };
18
+ for (const [field, value] of Object.entries(read.options)) {
19
+ if (field !== "cwd" && !(accepted.args && field === "args") && !(accepted.stdin && field === "stdin")) {
20
+ return fail("metadata-unsupported", `Executable tool '${input.runtime}' does not accept metadata field '${field}'.`);
21
+ }
22
+ if (field === "stdin") {
23
+ if (value !== "open")
24
+ return fail("invalid-stdin", 'Execution stdin accepts only "open".');
25
+ options.stdin = "open";
26
+ continue;
27
+ }
28
+ if (field === "cwd") {
29
+ if (typeof value !== "string" || value.length === 0 || value.includes("\0"))
30
+ return fail("invalid-cwd", "Execution cwd must be a nonempty directory path string without NUL.");
31
+ options.cwd = value;
32
+ continue;
33
+ }
34
+ if (!Array.isArray(value) || !value.every((arg) => typeof arg === "string" && !arg.includes("\0"))) {
35
+ return fail("invalid-args", "Execution args must be a JSON array of strings without NUL.");
36
+ }
37
+ options.args = value;
38
+ }
39
+ return { options };
40
+ }
41
+ static async prepare(input, accepted = {}) {
42
+ const parsed = this.parse(input, accepted);
43
+ if ("failure" in parsed)
44
+ return parsed.failure;
45
+ const directory = parsed.options.cwd;
46
+ if (directory === undefined)
47
+ return { status: 200, cwd: input.cwd };
48
+ const cwd = resolve(input.cwd ?? process.cwd(), directory);
49
+ try {
50
+ if ((await stat(cwd)).isDirectory())
51
+ return { status: 200, cwd };
52
+ }
53
+ catch (cause) {
54
+ const code = cause.code;
55
+ if (code !== "ENOENT" && code !== "ENOTDIR")
56
+ throw cause;
57
+ }
58
+ return Results.failure("executor:metadata", "cwd-not-found", 400, `Execution cwd '${directory}' is not an existing directory.`, {}, {
59
+ runtime: input.runtime, retryable: false,
60
+ });
61
+ }
62
+ }
63
+ //# sourceMappingURL=InvocationMetadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvocationMetadata.js","sourceRoot":"","sources":["../src/InvocationMetadata.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RuntimeInvocation.d.ts","sourceRoot":"","sources":["../src/RuntimeInvocation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGR,iBAAiB,IAAI,qBAAqB,EAE1C,mBAAmB,EAGtB,MAAM,YAAY,CAAC;AAoEpB,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAoFzF;IAED,MAAM,CAAC,kBAAkB,CACrB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GAChB,mBAAmB,CA4CrB;CACJ"}
1
+ {"version":3,"file":"RuntimeInvocation.d.ts","sourceRoot":"","sources":["../src/RuntimeInvocation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGR,iBAAiB,IAAI,qBAAqB,EAE1C,mBAAmB,EAGtB,MAAM,YAAY,CAAC;AAqEpB,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAsFzF;IAED,MAAM,CAAC,kBAAkB,CACrB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GAChB,mBAAmB,CA4CrB;CACJ"}