@qilin-agent/subprocess 0.0.0-stage → 3.1.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/LICENSE +21 -0
- package/README.i18n.yaml +73 -0
- package/README.md +162 -2
- package/README.zh.md +163 -0
- package/lib/control.js +27 -0
- package/lib/index.js +103 -0
- package/lib/types/control.d.ts +15 -0
- package/lib/types/index.d.ts +121 -0
- package/lib/types/types.d.ts +279 -0
- package/package.json +43 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kkutysllb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/subprocess/subprocess/README.md
|
|
5
|
+
/:
|
|
6
|
+
en: 04735af7a998f2ae
|
|
7
|
+
zh: ab6bf3d0f098877a
|
|
8
|
+
/qilin-agent-subprocess:
|
|
9
|
+
en: 6ab08fb7aa6e9736
|
|
10
|
+
zh: fd7b901fe1374a75
|
|
11
|
+
/qilin-agent-subprocess/summary:
|
|
12
|
+
en: 87c6ecd841999072
|
|
13
|
+
zh: 8e0d4601d15c0b08
|
|
14
|
+
/qilin-agent-subprocess/table-of-contents:
|
|
15
|
+
en: d152484eb41ac6b4
|
|
16
|
+
zh: 09388d293f9be9cb
|
|
17
|
+
/qilin-agent-subprocess/use-this-package:
|
|
18
|
+
en: a43391b5840a4c58
|
|
19
|
+
zh: a0a9c3fa577d92c0
|
|
20
|
+
/qilin-agent-subprocess/use-this-package/mounting-the-service:
|
|
21
|
+
en: 82be7c63ab95994f
|
|
22
|
+
zh: 8810e7c489b444df
|
|
23
|
+
/qilin-agent-subprocess/use-this-package/starting-a-managed-process:
|
|
24
|
+
en: 891cb74d5d35095c
|
|
25
|
+
zh: 296cb1890116fb05
|
|
26
|
+
/qilin-agent-subprocess/use-this-package/choosing-how-output-is-delivered:
|
|
27
|
+
en: c02fe7246326c164
|
|
28
|
+
zh: 1d31096e31e1feee
|
|
29
|
+
/qilin-agent-subprocess/use-this-package/using-a-control-pipe:
|
|
30
|
+
en: 6a246be51761434a
|
|
31
|
+
zh: c82c245484ebc810
|
|
32
|
+
/qilin-agent-subprocess/use-this-package/managing-process-lifetime:
|
|
33
|
+
en: 83d662d278184411
|
|
34
|
+
zh: 2c4e7fbe76823fa8
|
|
35
|
+
/qilin-agent-subprocess/use-this-package/running-a-terminal-session:
|
|
36
|
+
en: e3631497344c2ebd
|
|
37
|
+
zh: c4ebc7b08f0a316e
|
|
38
|
+
/qilin-agent-subprocess/use-this-package/environment-every-child-starts-from:
|
|
39
|
+
en: 9a653a40ff7f91a4
|
|
40
|
+
zh: 11521e762b848df1
|
|
41
|
+
/qilin-agent-subprocess/use-this-package/what-can-go-wrong:
|
|
42
|
+
en: 51f2f43087a1ed18
|
|
43
|
+
zh: 6d2ebb099b707df3
|
|
44
|
+
/qilin-agent-subprocess/understand-the-implementation:
|
|
45
|
+
en: 25cd344c959cfd9e
|
|
46
|
+
zh: 045880776ab82d1e
|
|
47
|
+
/qilin-agent-subprocess/understand-the-implementation/design-concept:
|
|
48
|
+
en: 88202ec45795ea88
|
|
49
|
+
zh: ce25ca53dfe367b9
|
|
50
|
+
/qilin-agent-subprocess/understand-the-implementation/source-map:
|
|
51
|
+
en: 0c0a492befb161e1
|
|
52
|
+
zh: dc22dd87990a7fac
|
|
53
|
+
/qilin-agent-subprocess/understand-the-implementation/data-model-and-flow:
|
|
54
|
+
en: 9998dc0a990fbcf1
|
|
55
|
+
zh: ba179617f60cb9bc
|
|
56
|
+
/qilin-agent-subprocess/understand-the-implementation/lifecycle-and-invariants:
|
|
57
|
+
en: a132325eea7cadb4
|
|
58
|
+
zh: 434cbbc61ff9cd0e
|
|
59
|
+
/qilin-agent-subprocess/further-exploration:
|
|
60
|
+
en: cbc95f1e4d71fb55
|
|
61
|
+
zh: 6d759e6c514aeba8
|
|
62
|
+
/qilin-agent-subprocess/model-experience:
|
|
63
|
+
en: 36d8f1820f81de29
|
|
64
|
+
zh: da18a4123bdd28b7
|
|
65
|
+
/qilin-agent-subprocess/model-experience/kv-cache-effect:
|
|
66
|
+
en: e290ccc9b3c92c08
|
|
67
|
+
zh: d75d48f485447a74
|
|
68
|
+
/qilin-agent-subprocess/known-limitations-and-deferred-work:
|
|
69
|
+
en: 0b1c46aec1ba628d
|
|
70
|
+
zh: cf2a0db9d0682155
|
|
71
|
+
/qilin-agent-subprocess/known-limitations-and-deferred-work/dev-note:
|
|
72
|
+
en: 56709c22873429b5
|
|
73
|
+
zh: c0e86c62982a3e29
|
package/README.md
CHANGED
|
@@ -1,3 +1,163 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "The subprocess service (ctx.subprocess) for composition authors and capability consumers starting, observing, and terminating managed child processes and terminal sessions."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
# @qilin-agent/subprocess
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`ctx.subprocess` resolves executables, starts explicitly specified child processes or real terminal sessions, streams or collects bounded output, and terminates the full managed process range. Configure one subprocess implementation for each composition, choosing local or remote execution according to where commands must run. Each request sets argv, working directory, stdio, environment overrides, termination grace, and cancellation, with no shell interpretation or hidden execution defaults. Child environments remove ambient credentials and `QILIN_*` values before applying explicit overrides; callers own deadlines, teardown policy, and model-facing rendering, while collected output remains readable after exit.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Mount a subprocess provider in any composition that must run child processes, and call `ctx.subprocess` from the capability that owns the command. The common path is explicit: resolve the executable, spawn with a fully specified request, read the output you asked for, and terminate the managed range when the work is done.
|
|
29
|
+
|
|
30
|
+
### Mounting the service
|
|
31
|
+
|
|
32
|
+
One provider registers `ctx.subprocess` per composition; load it beside the consumers that spawn through it — the bash executors, the LSP host, the PTY shell backend, or an out-of-process subagent backend. Loading a second provider fails loudly (one service per context, Cordis standard).
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
- name: '@qilin-agent/subprocess-local'
|
|
36
|
+
- name: '@qilin-agent/bash-local'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Starting a managed process
|
|
40
|
+
|
|
41
|
+
The request is fully explicit: the program and arguments, the working directory, one stdio disposition per stream, a termination grace, an optional abort signal, and optional environment overrides. Target and managed-range identities remain provider-private. `done` resolves with the direct command's exit facts (`exitCode` and `signal`) and rejects for spawn or provider failures; collected output stays readable after exit.
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
const executable = await ctx.subprocess.resolveExecutable('bash')
|
|
45
|
+
const handle = ctx.subprocess.spawn({
|
|
46
|
+
argv: [executable, '-c', 'echo hello'],
|
|
47
|
+
cwd: '/workspace',
|
|
48
|
+
stdio: { stdin: 'ignore', stdout: { maxBytes: 64 * 1024 }, stderr: 'inherit' },
|
|
49
|
+
graceMs: 5000,
|
|
50
|
+
})
|
|
51
|
+
const { exitCode, signal } = await handle.done
|
|
52
|
+
const output = handle.collected.stdout?.readFrom(0)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Choosing how output is delivered
|
|
56
|
+
|
|
57
|
+
- `'pipe'` hands you the raw stream for your own protocol framing — JSON-RPC for the LSP host, ndjson for the ACP backend.
|
|
58
|
+
- `'inherit'` lets the child write to the parent's own stream, for pass-through diagnostics.
|
|
59
|
+
- A collect object buffers a bounded in-memory tail; add a `spill` cap and the complete stream is also recoverable from a spill file.
|
|
60
|
+
|
|
61
|
+
Reads are offset-based and non-consuming: a background reader and a final batch read can share one stream without stealing each other's bytes.
|
|
62
|
+
|
|
63
|
+
<a id="using-a-control-pipe"></a>
|
|
64
|
+
### Using a control pipe
|
|
65
|
+
|
|
66
|
+
Set `stdio.control: 'pipe'` to receive a separate raw `Duplex` in `handle.control`. The Node child opens fd 7 with `openInheritedControlChannel()` from `@qilin-agent/subprocess/control`; this helper consumes the provider-owned `QILIN_SUBPROCESS_CONTROL=pipe` marker. Callers cannot supply that marker through `env`. Control bytes never enter stdout/stderr collectors. The consumer owns framing, validation, backpressure, and closing its endpoint; provider disposal destroys any endpoint remaining after process teardown. Omitting the request returns `control: undefined`. This channel is available only for ordinary processes, and carries no authority to bypass tool approval.
|
|
67
|
+
|
|
68
|
+
### Managing process lifetime
|
|
69
|
+
|
|
70
|
+
Termination and waiting use one provider-managed range. `terminate()` starts the provider's documented procedure, is idempotent, and becomes a no-op after that range is empty; the request's abort signal starts the same procedure. `waitForExit()` observes the same range and resolves only after the provider proves it quiescent, so direct command completion does not hide a surviving descendant. It rejects when the selected owner can no longer prove quiescence. Providers document their native owners and weaker fallbacks; callers own deadlines, teardown ladders, and cause classification.
|
|
71
|
+
|
|
72
|
+
### Running a terminal session
|
|
73
|
+
|
|
74
|
+
For interactive programs, `spawnTerminal` allocates a real PTY: write text, read UTF-8 output, inspect and signal the current foreground process group, and await one `terminate()` that settles every session member the provider can still observe. Readiness, scrollback, and prompt policy stay with the PTY consumer.
|
|
75
|
+
|
|
76
|
+
A terminal request can opt into `shellActivity`. `inspectActivity()` combines supported shell lifecycle evidence with owned-job observations and returns `idle`, `busy` or `unknown`, plus a handle-scoped revision. Unsupported or incomplete observations never imply idle; input invalidates existing prompt evidence. Opted-in terminals keep remaining work owned after the root shell exits instead of using that exit as permission to terminate descendants. Consumers own retention and cleanup deadlines.
|
|
77
|
+
|
|
78
|
+
### Environment every child starts from
|
|
79
|
+
|
|
80
|
+
Children never inherit the harness's ambient secrets: credential-shaped names and ambient `QILIN_*` facts are scrubbed, and the caller's explicit `env` merges after that scrub. A deliberately forwarded credential or a current `QILIN_*` deployment fact still reaches the child; an explicit `undefined` tombstone removes an ordinary ambient entry.
|
|
81
|
+
|
|
82
|
+
### What can go wrong
|
|
83
|
+
|
|
84
|
+
An executable that cannot be resolved fails loudly with a stable error. A spawn that never starts rejects `done`; there is no buffered output for a process that never ran. `waitForExit()` also rejects when the provider cannot prove its selected range is empty, and a provider fallback may not own descendants that escape its process group or observed session. When a transport owns its own spawn (the SDK client, MCP), route around the service and import `scrubbedParentEnv` directly so environment policy stays single-sourced.
|
|
85
|
+
|
|
86
|
+
-----
|
|
87
|
+
|
|
88
|
+
<a id="understand-the-implementation"></a>
|
|
89
|
+
## Understand the implementation
|
|
90
|
+
|
|
91
|
+
<details>
|
|
92
|
+
<summary>Implementation internals — click to expand</summary>
|
|
93
|
+
|
|
94
|
+
This section explains the design decisions behind the seam and points at the code that realizes them; the observable behavior is covered in [Use this package](#use-this-package).
|
|
95
|
+
|
|
96
|
+
### Design concept
|
|
97
|
+
|
|
98
|
+
The seam is built on one separation: the service owns process coordinates and lifetime; consumers own what a process means and every default that shapes one. That is why the spawn request is fully explicit — no hidden subprocess-service default — and why `SubprocessOutcome` carries exit facts only: callers own deadlines, teardown ladders, and cause classification. The `qilin-shell` request/spec split is the owning template.
|
|
99
|
+
|
|
100
|
+
### Source map
|
|
101
|
+
|
|
102
|
+
| File | Role |
|
|
103
|
+
|---|---|
|
|
104
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: abstract `SubprocessRuntime`, `ctx.subprocess` registration, the shared `scrubbedParentEnv` scrub |
|
|
105
|
+
| [`src/types.ts`](src/types.ts) | Vocabulary: spawn spec, stdio modes, handles, readers, outcomes, `QILIN_*` namespace |
|
|
106
|
+
|
|
107
|
+
### Data model and flow
|
|
108
|
+
|
|
109
|
+
A spawn returns a live handle immediately without exposing target identity. `done` independently reports the direct command outcome or failure, while `waitForExit()` reports managed-range quiescence. The request's abort signal drives the same termination procedure as `terminate()`. Collected readers are cursor-free: offsets are whole-stream byte coordinates the caller owns, so independent readers cannot consume one another's output, and a read whose offset slid out of the in-memory tail is `lossy` and points at the spill file when one exists. `spawnTerminal` is one deep primitive because ordinary pipes cannot allocate a controlling terminal or clean terminal-session members.
|
|
110
|
+
|
|
111
|
+
### Lifecycle and invariants
|
|
112
|
+
|
|
113
|
+
One implementation registers per context; loading a second throws (Cordis standard). Disposal of the service terminates every still-running managed process and awaits its exit, so process lifetime survives consumer reloads. `argv` is never shell-interpreted; a consumer that wants a shell passes `['bash', '-c', command]` itself. Terminal allocation cancellation (the spec signal) is separate from the published handle's lifetime.
|
|
114
|
+
|
|
115
|
+
</details>
|
|
116
|
+
|
|
117
|
+
-----
|
|
118
|
+
|
|
119
|
+
<a id="further-exploration"></a>
|
|
120
|
+
## Further Exploration
|
|
121
|
+
|
|
122
|
+
Read these pages when the package-level contract is not enough. They move from the exhaustive type reference to the providers and the decision evidence behind the seam.
|
|
123
|
+
|
|
124
|
+
- [Subprocess subsystem](../../../docs/subsystems/subprocess.md) — spawn specs, output readers, outcomes, and the `QILIN_*` environment in full.
|
|
125
|
+
- [qilin-subprocess-local](../subprocess-local/README.md) — the local host provider that implements this contract.
|
|
126
|
+
- [qilin-bash-local](../../shell/bash-local/README.md) — the largest consumer: bash commands over this service.
|
|
127
|
+
- [Subprocess seam Agent Note](../../../.agents/notes/archived/architecture/2026-07-26-subprocess-seam.md) — why the process half became its own seam and what moved with it.
|
|
128
|
+
|
|
129
|
+
-----
|
|
130
|
+
|
|
131
|
+
Terminal consumers use `terminalEnvironment()` to read the provider platform and preferred shell, and `resolveExecutable()` to verify candidates. A completed lookup miss throws `SubprocessExecutableNotFoundError`; transport failures remain distinct. `spawnTerminal` requires `terminalType` and initial dimensions, and its handle supports `resize(cols, rows)` without reallocating the process.
|
|
132
|
+
|
|
133
|
+
<a id="model-experience"></a>
|
|
134
|
+
## Model Experience
|
|
135
|
+
|
|
136
|
+
Indirectly, through consumer seams such as the bash executor family, which own all model-facing rendering of process output and lifecycle.
|
|
137
|
+
|
|
138
|
+
#### KV Cache effect
|
|
139
|
+
|
|
140
|
+
No direct invalidation; the named consumers own any request-prefix changes.
|
|
141
|
+
|
|
142
|
+
## Known Limitations and Deferred Work
|
|
143
|
+
|
|
144
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
These limits define when the seam is a poor fit or leaves work to its consumers. They are current package constraints, not a comparison or a backlog.
|
|
148
|
+
|
|
149
|
+
- **SDK-managed spawns remain outside** — a transport that owns its internal spawn (the SDK client, MCP) cannot route that call through this service; it can still import `scrubbedParentEnv` so environment policy stays single-sourced.
|
|
150
|
+
- **Teardown ladders are consumer-owned** — the seam ships signalling verbs and the managed-range wait, not a canned quiesce sequence; each out-of-process consumer encodes its child's cooperation shape itself (the ACP backend's stdin-EOF-first ladder is the in-repo template).
|
|
151
|
+
- **Observability is provider-specific** — native providers may own escaped descendants through systemd scopes or Windows Jobs, while fallback providers expose weaker process-group, tree, or session visibility. The seam adds no continuous process-table monitor.
|
|
152
|
+
|
|
153
|
+
<a id="dev-note"></a>
|
|
154
|
+
### Dev Note
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
158
|
+
|
|
159
|
+
This Dev Note is working context for maintainers: open design questions and directions that are not decided. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes.
|
|
160
|
+
|
|
161
|
+
Future: non-shell runners. The seam was split so a direct-argv executor or worker supervisor could consume it without reaching into bash internals; none is shipped yet, and the terminal primitive keeps readiness policy in its consumer.
|
|
162
|
+
|
|
163
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "面向组合作者与能力消费方的子进程服务(`ctx.subprocess`)说明:启动、观察并终止受管子进程与终端会话。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @qilin-agent/subprocess
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`ctx.subprocess` 可解析可执行文件、启动显式指定的子进程或真实终端会话、流式读取或有界收集输出,并终止完整的受管进程范围。每个组合配置一个 subprocess 实现,并根据命令运行位置选择本地或远程执行。每次请求都指定 argv、工作目录、stdio、环境覆盖、终止宽限期与取消信号,不会添加 shell 解释或隐藏的执行默认值。子进程环境会先移除环境中的凭据与 `QILIN_*` 值,再应用显式覆盖;时限、拆卸策略与面向模型的渲染由调用方负责,收集的输出在进程退出后仍可读取。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
在需要运行子进程的组合中挂载一个 subprocess 提供方,并从拥有该命令的能力调用 `ctx.subprocess`。常用路径是显式的:解析可执行文件、用完全明确的请求 spawn、读取你要的输出,并在工作完成时终止受管范围。
|
|
29
|
+
|
|
30
|
+
### 挂载服务
|
|
31
|
+
|
|
32
|
+
每个组合由唯一一个提供方注册 `ctx.subprocess`;把它与经由它 spawn 的消费方放在一起加载——bash 执行器、LSP 主机、PTY shell 后端或进程外 subagent 后端。加载第二个提供方会快速失败(每个上下文只有一个服务,这是 Cordis 的标准行为)。
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
- name: '@qilin-agent/subprocess-local'
|
|
36
|
+
- name: '@qilin-agent/bash-local'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 启动受管进程
|
|
40
|
+
|
|
41
|
+
请求完全明确:程序与参数、工作目录、每条流一种 stdio 处置方式、终止宽限期、可选的中止信号与可选的环境覆盖。目标与受管范围标识保留在提供方内部。`done` 以直接命令的退出事实(`exitCode` 与 `signal`)resolve,并在 spawn 或提供方失败时 reject;收集输出在退出后仍可读取。
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
const executable = await ctx.subprocess.resolveExecutable('bash')
|
|
45
|
+
const handle = ctx.subprocess.spawn({
|
|
46
|
+
argv: [executable, '-c', 'echo hello'],
|
|
47
|
+
cwd: '/workspace',
|
|
48
|
+
stdio: { stdin: 'ignore', stdout: { maxBytes: 64 * 1024 }, stderr: 'inherit' },
|
|
49
|
+
graceMs: 5000,
|
|
50
|
+
})
|
|
51
|
+
const { exitCode, signal } = await handle.done
|
|
52
|
+
const output = handle.collected.stdout?.readFrom(0)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 选择输出投递方式
|
|
56
|
+
|
|
57
|
+
- `'pipe'` 把原始流交给你做自己的协议分帧——LSP 主机用 JSON-RPC,ACP(Agent Client Protocol)后端用 ndjson。
|
|
58
|
+
- `'inherit'` 让子进程直接写父进程自己的流,用于直通诊断输出。
|
|
59
|
+
- 收集对象(collect object)在内存中缓冲一段有界尾部;加上 `spill` 上限后,完整流还可以从 spill 文件中恢复。
|
|
60
|
+
|
|
61
|
+
读取基于偏移量且从不消费:后台读取与最终批量读取可以共享同一条流,而不会抢走彼此的字节。
|
|
62
|
+
|
|
63
|
+
<a id="using-a-control-pipe"></a>
|
|
64
|
+
### 使用控制管道
|
|
65
|
+
|
|
66
|
+
设置 `stdio.control: 'pipe'` 后,`handle.control` 会返回独立的原始 `Duplex`。Node 子进程通过 `@qilin-agent/subprocess/control` 的 `openInheritedControlChannel()` 打开 fd 7;该辅助函数会消费提供方拥有的 `QILIN_SUBPROCESS_CONTROL=pipe` 标记。调用方不能通过 `env` 提供该标记。控制字节不会进入 stdout/stderr 收集器。消费方负责分帧、校验、背压和关闭自身端点;提供方销毁时会在进程拆卸后销毁仍存在的端点。省略请求则返回 `control: undefined`。该通道仅适用于普通进程,不授予绕过工具审批的权限。
|
|
67
|
+
|
|
68
|
+
### 管理进程生命周期
|
|
69
|
+
|
|
70
|
+
终止与等待使用同一个由提供方管理的范围。`terminate()` 会启动提供方记录的流程,具有幂等性,并在该范围为空后成为空操作;请求的中止信号会启动同一流程。`waitForExit()` 观察同一范围,只在提供方证明它完全停稳后 resolve,因此直接命令结束不会掩盖仍存活的后代。所选 owner 无法再证明完全停稳时,它会 reject。提供方记录其 native owner 与较弱 fallback;时限、拆卸阶梯与原因分类归调用方所有。
|
|
71
|
+
|
|
72
|
+
### 运行终端会话
|
|
73
|
+
|
|
74
|
+
对于交互式程序,`spawnTerminal` 分配真实 PTY:写入文本、读取 UTF-8 输出、检查当前前台进程组并向其发送信号,以及等待一次 `terminate()`,让提供方仍可观察到的每个会话成员完全停稳。就绪状态、scrollback 与提示符策略仍归 PTY 消费方所有。
|
|
75
|
+
|
|
76
|
+
终端请求可显式启用 `shellActivity`。`inspectActivity()` 结合支持的 shell 生命周期信号与自有任务观察,返回 `idle`、`busy` 或 `unknown`,以及句柄内的 revision。不支持或不完整的观察不能推出空闲;输入会使已有提示符证据失效。启用后,根 shell 退出时继续持有剩余工作,不把该退出视为终止后代进程的许可。保活和清理期限由消费者决定。
|
|
77
|
+
|
|
78
|
+
### 每个子进程起步时的环境
|
|
79
|
+
|
|
80
|
+
子进程永远不会隐式继承 harness 的环境秘密:形似凭据的名称与环境中的 `QILIN_*` 事实都会被清除,调用方显式的 `env` 在该清除之后合并。有意转发的凭据或当前的 `QILIN_*` 部署事实仍会到达子进程;显式的 `undefined` 墓碑值则移除一个普通的环境项。
|
|
81
|
+
|
|
82
|
+
### 可能出错的地方
|
|
83
|
+
|
|
84
|
+
无法解析可执行文件时,服务会明确报出稳定的错误。从未启动成功的 spawn 会让 `done` reject;从未运行过的进程没有任何缓冲输出。提供方无法证明所选范围为空时,`waitForExit()` 也会 reject;提供方 fallback 可能无法拥有逃离其进程组或已观察会话的后代。当传输拥有自己的 spawn(SDK 客户端、MCP)时,请绕开本服务并直接导入 `scrubbedParentEnv`,让环境策略保持单一来源。
|
|
85
|
+
|
|
86
|
+
-----
|
|
87
|
+
|
|
88
|
+
<a id="understand-the-implementation"></a>
|
|
89
|
+
## 理解实现
|
|
90
|
+
|
|
91
|
+
<details>
|
|
92
|
+
<summary>实现细节——点击展开</summary>
|
|
93
|
+
|
|
94
|
+
本节解释 seam 背后的设计决策,并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中说明。
|
|
95
|
+
|
|
96
|
+
### 设计理念
|
|
97
|
+
|
|
98
|
+
本 seam 建立在一个分离之上:服务负责进程坐标与生命周期;消费方负责定义进程的含义,以及决定塑造该进程的每一项默认值。正因如此,spawn 请求完全明确——没有任何隐藏的子进程服务默认值——`SubprocessOutcome` 也只携带退出事实:时限、拆卸阶梯与原因分类归调用方所有。`qilin-shell` 的 request/spec 拆分是这条规则的所属模板。
|
|
99
|
+
|
|
100
|
+
### 源码地图
|
|
101
|
+
|
|
102
|
+
| 文件 | 职责 |
|
|
103
|
+
|---|---|
|
|
104
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:抽象 `SubprocessRuntime`、`ctx.subprocess` 注册、共享的 `scrubbedParentEnv` 清除 |
|
|
105
|
+
| [`src/types.ts`](src/types.ts) | 词汇:spawn spec、stdio 模式、句柄、读取器、结果、`QILIN_*` 命名空间 |
|
|
106
|
+
|
|
107
|
+
### 数据模型与流程
|
|
108
|
+
|
|
109
|
+
spawn 会立即返回活动句柄,而不公开目标身份。`done` 独立报告直接命令的结果或失败,`waitForExit()` 则报告受管范围是否完全停稳。请求的中止信号驱动与 `terminate()` 相同的终止流程。收集模式的读取器无游标:偏移量是调用方拥有的全流字节坐标,因此独立读取器不会消费彼此的输出,偏移量滑出内存尾部的读取标记为 `lossy`,并在 spill 文件存在时指向它。`spawnTerminal` 是一项底层原语,因为普通管道无法分配控制终端或清理终端会话成员。
|
|
110
|
+
|
|
111
|
+
### 生命周期与不变式
|
|
112
|
+
|
|
113
|
+
每个上下文只注册一个实现;加载第二个会抛错(Cordis 标准行为)。服务自身的 dispose(资源释放)会终止所有仍在运行的受管进程并等待其退出,因此进程生命周期在消费方重载后依然延续。`argv` 绝不经过 shell 解释;需要 shell 的消费方自行传入 `['bash', '-c', command]`。终端分配的取消(spec 信号)与已发布句柄的生命周期相互独立。
|
|
114
|
+
|
|
115
|
+
</details>
|
|
116
|
+
|
|
117
|
+
-----
|
|
118
|
+
|
|
119
|
+
<a id="further-exploration"></a>
|
|
120
|
+
## 进一步探索
|
|
121
|
+
|
|
122
|
+
当包级约定不够用时阅读以下页面。它们从穷尽式类型参考逐步进入各提供方,以及 seam 背后的决策证据。
|
|
123
|
+
|
|
124
|
+
- [子进程子系统](../../../docs/subsystems/subprocess.zh.md)——spawn spec、输出读取器、结果与完整的 `QILIN_*` 环境。
|
|
125
|
+
- [qilin-subprocess-local](../subprocess-local/README.zh.md)——实现本约定的本地宿主提供方。
|
|
126
|
+
- [qilin-bash-local](../../shell/bash-local/README.zh.md)——最大的消费方:经由本服务运行 bash 命令。
|
|
127
|
+
- [subprocess seam Agent Note](../../../.agents/notes/archived/architecture/2026-07-26-subprocess-seam.md)——进程部分为何成为独立的 seam,以及随之迁移的内容。
|
|
128
|
+
|
|
129
|
+
-----
|
|
130
|
+
|
|
131
|
+
终端消费者通过 `terminalEnvironment()` 读取 provider 平台和首选 shell,通过 `resolveExecutable()` 验证候选。确定未找到可执行文件时抛出 `SubprocessExecutableNotFoundError`,传输故障仍单独报告。`spawnTerminal` 要求 `terminalType` 和初始尺寸,返回的 handle 通过 `resize(cols, rows)` 调整尺寸,不重新分配进程。
|
|
132
|
+
|
|
133
|
+
<a id="model-experience"></a>
|
|
134
|
+
## 模型体验
|
|
135
|
+
|
|
136
|
+
通过消费方 seam(例如 bash 执行器家族)间接影响,它们负责进程输出与生命周期的全部面向模型渲染。
|
|
137
|
+
|
|
138
|
+
#### KV Cache 影响
|
|
139
|
+
|
|
140
|
+
不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
|
|
141
|
+
|
|
142
|
+
## 已知限制与延期工作
|
|
143
|
+
|
|
144
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
这些限制说明该 seam 何时不合适,或何时把工作留给消费方。它们是当前包约束,不是对比或任务积压。
|
|
148
|
+
|
|
149
|
+
- **由 SDK 管理的 spawn 仍在服务之外**——拥有内部 spawn 的传输(SDK 客户端、MCP)无法把该调用路由到本服务;它仍可导入 `scrubbedParentEnv`,使环境策略保持单一来源。
|
|
150
|
+
- **拆卸阶梯归消费方所有**——该 seam 只提供信号动词与受管范围等待,不提供现成的完全停稳序列;每个进程外消费方自行编码其子进程的配合方式(ACP 后端以 stdin EOF 打头的阶梯是仓库内模板)。
|
|
151
|
+
- **可观察性取决于提供方**——native 提供方可以通过 systemd scope 或 Windows Job 拥有逃逸后代,fallback 提供方则只暴露较弱的进程组、进程树或会话可见性。该 seam 不新增持续的进程表监视器。
|
|
152
|
+
|
|
153
|
+
<a id="dev-note"></a>
|
|
154
|
+
### 开发备注
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
158
|
+
|
|
159
|
+
本开发备注是维护者的工作上下文:开放设计问题与尚未决定的探索方向。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码和相关 Agent Note 为准。
|
|
160
|
+
|
|
161
|
+
未来:非 shell 运行器。该 seam 拆分的目的就是让直接 argv 执行器或 worker supervisor 无需深入 bash 内部即可消费它;目前尚无任何实现交付,终端原语也把就绪策略留在其消费方。
|
|
162
|
+
|
|
163
|
+
</details>
|
package/lib/control.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Socket } from "node:net";
|
|
2
|
+
//#region lib/types/control.js
|
|
3
|
+
/** Inherited byte-channel protocol shared by subprocess launchers and Node children. */
|
|
4
|
+
/** Child descriptor reserved for the optional subprocess control channel. */
|
|
5
|
+
const SUBPROCESS_CONTROL_FD = 7;
|
|
6
|
+
/** Private launch marker consumed before a Node child executes application code. */
|
|
7
|
+
const SUBPROCESS_CONTROL_ENV = "QILIN_SUBPROCESS_CONTROL";
|
|
8
|
+
/**
|
|
9
|
+
* Consume the launch marker and open the inherited control pipe at fd 7.
|
|
10
|
+
* The returned stream owns the descriptor. Call once before executing untrusted code;
|
|
11
|
+
* messages remain untrusted even though the endpoint was inherited.
|
|
12
|
+
* @returns a connected byte-mode duplex stream owned by the caller.
|
|
13
|
+
* @throws when the marker is missing/invalid or the inherited descriptor cannot be opened.
|
|
14
|
+
*/
|
|
15
|
+
function openInheritedControlChannel() {
|
|
16
|
+
const marker = process.env[SUBPROCESS_CONTROL_ENV];
|
|
17
|
+
Reflect.deleteProperty(process.env, SUBPROCESS_CONTROL_ENV);
|
|
18
|
+
if (marker !== "pipe") throw new Error("subprocess control channel was not inherited");
|
|
19
|
+
return new Socket({
|
|
20
|
+
fd: 7,
|
|
21
|
+
readable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
allowHalfOpen: true
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { SUBPROCESS_CONTROL_ENV, SUBPROCESS_CONTROL_FD, openInheritedControlChannel };
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Service } from "@qilin-agent/kylin";
|
|
2
|
+
import { proxyEnvironmentForChild } from "@qilin-agent/http-proxy";
|
|
3
|
+
//#region lib/types/types.js
|
|
4
|
+
/**
|
|
5
|
+
* Vocabulary for the subprocess Service Definition: fully-specified spawn requests with
|
|
6
|
+
* Node-shaped per-stream stdio modes, bounded collected output with spill
|
|
7
|
+
* recovery, raw piped streams, and managed-range termination. Command
|
|
8
|
+
* defaulting, shell semantics, protocol framing, and presentation belong to
|
|
9
|
+
* consumers such as the bash executor seam.
|
|
10
|
+
* @module qilin-subprocess/types
|
|
11
|
+
*/
|
|
12
|
+
/** Namespace prefix reserved for QiLin-managed child environment facts. */
|
|
13
|
+
const QILIN_ENV_PREFIX = "QILIN_";
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region lib/types/index.js
|
|
16
|
+
/**
|
|
17
|
+
* Service Definition for the subprocess capability seam (`ctx.subprocess`): execution-world executable lookup,
|
|
18
|
+
* fully specified provider-managed process ranges with raw or
|
|
19
|
+
* collected stdio, and one terminal-process primitive. Command defaulting,
|
|
20
|
+
* shell semantics, deadlines, protocol framing, terminal readiness, and
|
|
21
|
+
* presentation belong to consumers. The local implementation lives in
|
|
22
|
+
* `@qilin-agent/subprocess-local`.
|
|
23
|
+
* @module @qilin-agent/subprocess
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Credential-shaped environment names are NOT forwarded to children (the
|
|
27
|
+
* harness's own `DEEPSEEK_API_KEY`/secrets must not leak into a spawned
|
|
28
|
+
* process implicitly). One heuristic for every in-repo spawner; a
|
|
29
|
+
* deliberately supplied entry survives because explicit env layers merge
|
|
30
|
+
* after the scrub.
|
|
31
|
+
*/
|
|
32
|
+
const SENSITIVE_ENV_PATTERN = /KEY|PASSWORD|SECRET|TOKEN/i;
|
|
33
|
+
/**
|
|
34
|
+
* The ambient parent environment minus credential-shaped names and minus all
|
|
35
|
+
* `QILIN_*` names — the canonical base every harness child starts from. `PATH`,
|
|
36
|
+
* `HOME`, locale, and proxy variables survive, so child CLIs run normally;
|
|
37
|
+
* harness identity never leaks implicitly (a deliberately forwarded
|
|
38
|
+
* credential or current `QILIN_*` fact goes through the spec's explicit `env`,
|
|
39
|
+
* which merges after this scrub). Both scrubs match case-insensitively:
|
|
40
|
+
* Windows environment names are case-insensitive, so a parent `qilin_*` entry
|
|
41
|
+
* would otherwise survive and read back as `$env:QILIN_*` in the child;
|
|
42
|
+
* deliberate lowercase `qilin_*` names on POSIX are implausible. Exported as a plain function so spawners
|
|
43
|
+
* that cannot route through the service (node-pty backends, SDK-managed
|
|
44
|
+
* transports) share the one scrub definition.
|
|
45
|
+
*
|
|
46
|
+
* When a proxy is active the result also carries the resolved proxy names and the flag a child Node
|
|
47
|
+
* needs to honor them, so a child inherits the same routing as its parent.
|
|
48
|
+
* @returns a fresh environment object safe to hand to a child spawn.
|
|
49
|
+
*/
|
|
50
|
+
function scrubbedParentEnv() {
|
|
51
|
+
const env = {};
|
|
52
|
+
for (const [key, value] of Object.entries(process.env)) if (value !== void 0 && !SENSITIVE_ENV_PATTERN.test(key) && !key.toUpperCase().startsWith("QILIN_")) env[key] = value;
|
|
53
|
+
for (const [name, value] of Object.entries(proxyEnvironmentForChild())) if (value === void 0) Reflect.deleteProperty(env, name);
|
|
54
|
+
else env[name] = value;
|
|
55
|
+
return env;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Abstract subprocess service. Subclass, implement {@link spawn}, and load the
|
|
59
|
+
* subclass as a plugin — it registers as `ctx.subprocess` (one implementation
|
|
60
|
+
* per context; loading a second throws, which is cordis' standard
|
|
61
|
+
* duplicate-service behavior).
|
|
62
|
+
*
|
|
63
|
+
* Implementations must honor these semantics:
|
|
64
|
+
* - Executable paths belong to one execution world shared with the mounted
|
|
65
|
+
* filesystem provider.
|
|
66
|
+
* - {@link spawn} returns a live handle synchronously. Target identity remains
|
|
67
|
+
* provider-private; `done` resolves with the spawned command's exit facts and
|
|
68
|
+
* may reject for spawn or provider failures.
|
|
69
|
+
* - Collect-mode readers are offset-based and non-consuming, so independent
|
|
70
|
+
* readers never consume one another's output; lossy reads report truncation
|
|
71
|
+
* and the spill file holding the complete stream when one exists. Piped
|
|
72
|
+
* streams are handed to the caller raw and never buffered here.
|
|
73
|
+
* - {@link SubprocessHandle.terminate} (and the spec's abort signal) starts the
|
|
74
|
+
* provider's documented procedure against its managed range.
|
|
75
|
+
* {@link SubprocessHandle.waitForExit} observes that same range so a
|
|
76
|
+
* consumer-owned teardown ladder can hold each tier on real quiescence; each
|
|
77
|
+
* provider documents its signalling and observability limits.
|
|
78
|
+
* - Disposal of the service terminates all still-running managed processes
|
|
79
|
+
* and awaits their exit.
|
|
80
|
+
* - {@link spawnTerminal} owns terminal allocation, text transport,
|
|
81
|
+
* foreground groups, signalling, and whole-session quiescence behind one
|
|
82
|
+
* awaited termination method; readiness and persistent-shell policy stay
|
|
83
|
+
* in the PTY consumer. Its output stream ends after queued terminal output
|
|
84
|
+
* when the top-level process exits.
|
|
85
|
+
*/
|
|
86
|
+
var SubprocessRuntime = class extends Service {
|
|
87
|
+
constructor(ctx) {
|
|
88
|
+
super(ctx, "subprocess");
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
/** Executable lookup completed without finding an executable file. */
|
|
92
|
+
var SubprocessExecutableNotFoundError = class extends Error {
|
|
93
|
+
/**
|
|
94
|
+
* @param message - provider-specific lookup diagnostic.
|
|
95
|
+
* @param options - original provider failure, when available.
|
|
96
|
+
*/
|
|
97
|
+
constructor(message, options) {
|
|
98
|
+
super(message, options);
|
|
99
|
+
this.name = "SubprocessExecutableNotFoundError";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
//#endregion
|
|
103
|
+
export { QILIN_ENV_PREFIX, SENSITIVE_ENV_PATTERN, SubprocessExecutableNotFoundError, SubprocessRuntime, SubprocessRuntime as default, scrubbedParentEnv };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Inherited byte-channel protocol shared by subprocess launchers and Node children. */
|
|
2
|
+
import type { Duplex } from 'node:stream';
|
|
3
|
+
/** Child descriptor reserved for the optional subprocess control channel. */
|
|
4
|
+
export declare const SUBPROCESS_CONTROL_FD = 7;
|
|
5
|
+
/** Private launch marker consumed before a Node child executes application code. */
|
|
6
|
+
export declare const SUBPROCESS_CONTROL_ENV: "QILIN_SUBPROCESS_CONTROL";
|
|
7
|
+
/**
|
|
8
|
+
* Consume the launch marker and open the inherited control pipe at fd 7.
|
|
9
|
+
* The returned stream owns the descriptor. Call once before executing untrusted code;
|
|
10
|
+
* messages remain untrusted even though the endpoint was inherited.
|
|
11
|
+
* @returns a connected byte-mode duplex stream owned by the caller.
|
|
12
|
+
* @throws when the marker is missing/invalid or the inherited descriptor cannot be opened.
|
|
13
|
+
*/
|
|
14
|
+
export declare function openInheritedControlChannel(): Duplex;
|
|
15
|
+
//# sourceMappingURL=control.d.ts.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Definition for the subprocess capability seam (`ctx.subprocess`): execution-world executable lookup,
|
|
3
|
+
* fully specified provider-managed process ranges with raw or
|
|
4
|
+
* collected stdio, and one terminal-process primitive. Command defaulting,
|
|
5
|
+
* shell semantics, deadlines, protocol framing, terminal readiness, and
|
|
6
|
+
* presentation belong to consumers. The local implementation lives in
|
|
7
|
+
* `@qilin-agent/subprocess-local`.
|
|
8
|
+
* @module @qilin-agent/subprocess
|
|
9
|
+
*/
|
|
10
|
+
import { Context, Service } from '@qilin-agent/kylin';
|
|
11
|
+
import type { SubprocessHandle, SubprocessSpawnSpec } from './types.ts';
|
|
12
|
+
import type { SubprocessTerminalEnvironment, SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from './types.ts';
|
|
13
|
+
export { QILIN_ENV_PREFIX } from './types.ts';
|
|
14
|
+
export type { CollectedOutput, QilinEnvironment, QilinEnvironmentKey, SubprocessCollect, SubprocessCollectedOutputs, SubprocessHandle, SubprocessOutcome, SubprocessOutputMode, SubprocessOutputRead, SubprocessOutputReader, SubprocessSpawnSpec, SubprocessStdinMode, SubprocessStdio, SubprocessTerminalForeground, SubprocessTerminalActivity, SubprocessTerminalEnvironment, SubprocessTerminalHandle, SubprocessTerminalSignal, SubprocessTerminalSpawnSpec, } from './types.ts';
|
|
15
|
+
/**
|
|
16
|
+
* Credential-shaped environment names are NOT forwarded to children (the
|
|
17
|
+
* harness's own `DEEPSEEK_API_KEY`/secrets must not leak into a spawned
|
|
18
|
+
* process implicitly). One heuristic for every in-repo spawner; a
|
|
19
|
+
* deliberately supplied entry survives because explicit env layers merge
|
|
20
|
+
* after the scrub.
|
|
21
|
+
*/
|
|
22
|
+
export declare const SENSITIVE_ENV_PATTERN: RegExp;
|
|
23
|
+
/**
|
|
24
|
+
* The ambient parent environment minus credential-shaped names and minus all
|
|
25
|
+
* `QILIN_*` names — the canonical base every harness child starts from. `PATH`,
|
|
26
|
+
* `HOME`, locale, and proxy variables survive, so child CLIs run normally;
|
|
27
|
+
* harness identity never leaks implicitly (a deliberately forwarded
|
|
28
|
+
* credential or current `QILIN_*` fact goes through the spec's explicit `env`,
|
|
29
|
+
* which merges after this scrub). Both scrubs match case-insensitively:
|
|
30
|
+
* Windows environment names are case-insensitive, so a parent `qilin_*` entry
|
|
31
|
+
* would otherwise survive and read back as `$env:QILIN_*` in the child;
|
|
32
|
+
* deliberate lowercase `qilin_*` names on POSIX are implausible. Exported as a plain function so spawners
|
|
33
|
+
* that cannot route through the service (node-pty backends, SDK-managed
|
|
34
|
+
* transports) share the one scrub definition.
|
|
35
|
+
*
|
|
36
|
+
* When a proxy is active the result also carries the resolved proxy names and the flag a child Node
|
|
37
|
+
* needs to honor them, so a child inherits the same routing as its parent.
|
|
38
|
+
* @returns a fresh environment object safe to hand to a child spawn.
|
|
39
|
+
*/
|
|
40
|
+
export declare function scrubbedParentEnv(): Record<string, string>;
|
|
41
|
+
declare module '@qilin-agent/kylin' {
|
|
42
|
+
interface Context {
|
|
43
|
+
subprocess: SubprocessRuntime;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Abstract subprocess service. Subclass, implement {@link spawn}, and load the
|
|
48
|
+
* subclass as a plugin — it registers as `ctx.subprocess` (one implementation
|
|
49
|
+
* per context; loading a second throws, which is cordis' standard
|
|
50
|
+
* duplicate-service behavior).
|
|
51
|
+
*
|
|
52
|
+
* Implementations must honor these semantics:
|
|
53
|
+
* - Executable paths belong to one execution world shared with the mounted
|
|
54
|
+
* filesystem provider.
|
|
55
|
+
* - {@link spawn} returns a live handle synchronously. Target identity remains
|
|
56
|
+
* provider-private; `done` resolves with the spawned command's exit facts and
|
|
57
|
+
* may reject for spawn or provider failures.
|
|
58
|
+
* - Collect-mode readers are offset-based and non-consuming, so independent
|
|
59
|
+
* readers never consume one another's output; lossy reads report truncation
|
|
60
|
+
* and the spill file holding the complete stream when one exists. Piped
|
|
61
|
+
* streams are handed to the caller raw and never buffered here.
|
|
62
|
+
* - {@link SubprocessHandle.terminate} (and the spec's abort signal) starts the
|
|
63
|
+
* provider's documented procedure against its managed range.
|
|
64
|
+
* {@link SubprocessHandle.waitForExit} observes that same range so a
|
|
65
|
+
* consumer-owned teardown ladder can hold each tier on real quiescence; each
|
|
66
|
+
* provider documents its signalling and observability limits.
|
|
67
|
+
* - Disposal of the service terminates all still-running managed processes
|
|
68
|
+
* and awaits their exit.
|
|
69
|
+
* - {@link spawnTerminal} owns terminal allocation, text transport,
|
|
70
|
+
* foreground groups, signalling, and whole-session quiescence behind one
|
|
71
|
+
* awaited termination method; readiness and persistent-shell policy stay
|
|
72
|
+
* in the PTY consumer. Its output stream ends after queued terminal output
|
|
73
|
+
* when the top-level process exits.
|
|
74
|
+
*/
|
|
75
|
+
export declare abstract class SubprocessRuntime extends Service {
|
|
76
|
+
constructor(ctx: Context);
|
|
77
|
+
/**
|
|
78
|
+
* Resolve one configured executable in this provider's execution world.
|
|
79
|
+
* Absolute paths are verified; bare names use the provider's scrubbed PATH
|
|
80
|
+
* plus explicit environment overrides. Relative paths containing separators
|
|
81
|
+
* are rejected: the resolution base is undefined, so providers fail loud
|
|
82
|
+
* instead of guessing.
|
|
83
|
+
* @param command - absolute executable path or bare PATH name.
|
|
84
|
+
* @param env - explicit environment entries used for lookup.
|
|
85
|
+
* @param signal - aborts remote or local lookup.
|
|
86
|
+
* @returns a canonical executable path.
|
|
87
|
+
*/
|
|
88
|
+
abstract resolveExecutable(command: string, env?: Readonly<Record<string, string>>, signal?: AbortSignal): Promise<string>;
|
|
89
|
+
/**
|
|
90
|
+
* Inspect shell-selection facts in the provider's execution environment.
|
|
91
|
+
* @param signal - cancellation of remote environment inspection.
|
|
92
|
+
* @returns platform and preferred shell; executable lookup and allocation remain separate operations.
|
|
93
|
+
*/
|
|
94
|
+
abstract terminalEnvironment(signal?: AbortSignal): Promise<SubprocessTerminalEnvironment>;
|
|
95
|
+
/**
|
|
96
|
+
* Start one managed child process from a fully-specified spec; this seam
|
|
97
|
+
* applies no defaults.
|
|
98
|
+
* @param spec - argv, directory, stdio dispositions, grace, cancellation, and environment.
|
|
99
|
+
* @returns the live process handle (streams/readers, signalling, outcome promise).
|
|
100
|
+
* @throws synchronously when pre-aborted or when argv, cwd, environment, or grace is invalid before handle creation.
|
|
101
|
+
*/
|
|
102
|
+
abstract spawn(spec: SubprocessSpawnSpec): SubprocessHandle;
|
|
103
|
+
/**
|
|
104
|
+
* Allocate a real terminal and start one owned process session. This is the
|
|
105
|
+
* only non-pipe process primitive: implementations own terminal byte I/O,
|
|
106
|
+
* foreground groups, signals, and whole-session quiescence.
|
|
107
|
+
* @param spec - fully specified argv, cwd, environment, dimensions, grace, and allocation cancellation.
|
|
108
|
+
* @returns the live terminal handle after allocation succeeds.
|
|
109
|
+
*/
|
|
110
|
+
abstract spawnTerminal(spec: SubprocessTerminalSpawnSpec): Promise<SubprocessTerminalHandle>;
|
|
111
|
+
}
|
|
112
|
+
export default SubprocessRuntime;
|
|
113
|
+
/** Executable lookup completed without finding an executable file. */
|
|
114
|
+
export declare class SubprocessExecutableNotFoundError extends Error {
|
|
115
|
+
/**
|
|
116
|
+
* @param message - provider-specific lookup diagnostic.
|
|
117
|
+
* @param options - original provider failure, when available.
|
|
118
|
+
*/
|
|
119
|
+
constructor(message: string, options?: ErrorOptions);
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vocabulary for the subprocess Service Definition: fully-specified spawn requests with
|
|
3
|
+
* Node-shaped per-stream stdio modes, bounded collected output with spill
|
|
4
|
+
* recovery, raw piped streams, and managed-range termination. Command
|
|
5
|
+
* defaulting, shell semantics, protocol framing, and presentation belong to
|
|
6
|
+
* consumers such as the bash executor seam.
|
|
7
|
+
* @module qilin-subprocess/types
|
|
8
|
+
*/
|
|
9
|
+
import type { Duplex, Readable, Writable } from 'node:stream';
|
|
10
|
+
/** Namespace prefix reserved for QiLin-managed child environment facts. */
|
|
11
|
+
export declare const QILIN_ENV_PREFIX: "QILIN_";
|
|
12
|
+
/** One environment key inside the managed {@link QILIN_ENV_PREFIX} namespace. */
|
|
13
|
+
export type QilinEnvironmentKey = `${typeof QILIN_ENV_PREFIX}${string}`;
|
|
14
|
+
/** Trusted QiLin variables for one child-process execution. */
|
|
15
|
+
export type QilinEnvironment = Readonly<Record<QilinEnvironmentKey, string>>;
|
|
16
|
+
/** One captured stream: the (possibly truncated) text plus recovery info. */
|
|
17
|
+
export interface CollectedOutput {
|
|
18
|
+
/** Collected text — the TAIL of the stream when truncated. */
|
|
19
|
+
text: string;
|
|
20
|
+
/** True when bytes were dropped from `text`. */
|
|
21
|
+
truncated: boolean;
|
|
22
|
+
/** Path to a file holding the COMPLETE stream, when truncated and available. */
|
|
23
|
+
spillPath?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* stdin disposition. `'ignore'` leaves fd 0 on `/dev/null`; `'pipe'` exposes
|
|
27
|
+
* {@link SubprocessHandle.stdin} for the caller's ongoing protocol writes;
|
|
28
|
+
* `{ data }` writes the bytes and closes (the batch shape).
|
|
29
|
+
*/
|
|
30
|
+
export type SubprocessStdinMode = 'ignore' | 'pipe' | {
|
|
31
|
+
readonly data: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Bounded in-memory collection for one output stream, with an optional
|
|
35
|
+
* full-stream spill file. Omitting `spill` keeps only the in-memory tail —
|
|
36
|
+
* the diagnostic-tail shape (a language server's stderr); including it makes
|
|
37
|
+
* the complete stream recoverable up to its cap (the bash tool shape).
|
|
38
|
+
*/
|
|
39
|
+
export interface SubprocessCollect {
|
|
40
|
+
/** In-memory cap in bytes; overflow keeps the TAIL. */
|
|
41
|
+
maxBytes: number;
|
|
42
|
+
/** Full-stream spill file; absent disables spilling entirely. */
|
|
43
|
+
spill?: {
|
|
44
|
+
/** Whole-stream byte cap; a larger stream discards its now-incomplete spill. */
|
|
45
|
+
maxBytes: number;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* stdout/stderr disposition. `'pipe'` exposes the raw `Readable` for the
|
|
50
|
+
* caller's protocol decoding; `'inherit'` passes the parent's descriptor
|
|
51
|
+
* through (child diagnostics land on the harness's own stream); a
|
|
52
|
+
* {@link SubprocessCollect} object buffers boundedly with offset-based reads.
|
|
53
|
+
*/
|
|
54
|
+
export type SubprocessOutputMode = 'pipe' | 'inherit' | SubprocessCollect;
|
|
55
|
+
/** Per-stream stdio dispositions, all explicit — this seam applies no defaults. */
|
|
56
|
+
export interface SubprocessStdio {
|
|
57
|
+
stdin: SubprocessStdinMode;
|
|
58
|
+
stdout: SubprocessOutputMode;
|
|
59
|
+
stderr: SubprocessOutputMode;
|
|
60
|
+
/** Request a separate byte-mode duplex channel; omission creates none. */
|
|
61
|
+
control?: 'pipe';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A fully-specified spawn request. This seam applies no defaults: every
|
|
65
|
+
* disposition, limit, and directory is explicit, so the caller's own config —
|
|
66
|
+
* not a hidden subprocess-service default — decides them (the `qilin-shell`
|
|
67
|
+
* request/spec split is the owning template).
|
|
68
|
+
*/
|
|
69
|
+
export interface SubprocessSpawnSpec {
|
|
70
|
+
/** Executable and arguments; `argv[0]` is the program. Never shell-interpreted here. */
|
|
71
|
+
argv: readonly string[];
|
|
72
|
+
/** Working directory for the child. */
|
|
73
|
+
cwd: string;
|
|
74
|
+
/** Per-stream stdio dispositions. */
|
|
75
|
+
stdio: SubprocessStdio;
|
|
76
|
+
/**
|
|
77
|
+
* Positive finite grace period in milliseconds, no greater than
|
|
78
|
+
* `MAX_TIMER_DELAY_MS`, available to the provider's termination procedure
|
|
79
|
+
* and used for draining still-open collected pipes after the process exits
|
|
80
|
+
* (an inherited descriptor held by a survivor cannot hold the outcome open
|
|
81
|
+
* indefinitely). Providers document whether range termination is staged or
|
|
82
|
+
* immediate.
|
|
83
|
+
*/
|
|
84
|
+
graceMs: number;
|
|
85
|
+
/**
|
|
86
|
+
* Abort signal — starts the terminate escalation on the managed range when
|
|
87
|
+
* it fires. The caller owns deadlines and cause classification; this seam
|
|
88
|
+
* only reacts to the abort.
|
|
89
|
+
*/
|
|
90
|
+
signal?: AbortSignal | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Explicit environment entries merged onto the implementation's scrubbed
|
|
93
|
+
* parent base (see `scrubbedParentEnv`), with no namespace validation. A
|
|
94
|
+
* string is a deliberate caller opt-in, so a forwarded credential-shaped
|
|
95
|
+
* entry or current `QILIN_*` fact survives the scrub; `undefined` is a
|
|
96
|
+
* tombstone that removes an ordinary ambient entry from the child.
|
|
97
|
+
*/
|
|
98
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Exit facts of one closed process — Node's `close`-event vocabulary.
|
|
102
|
+
* Deliberately carries NO timeout or cancellation classification (the caller
|
|
103
|
+
* reads the signal it owns to classify causes) and NO output: collected
|
|
104
|
+
* streams stay readable through {@link SubprocessHandle.collected} after
|
|
105
|
+
* settlement, so batch and streaming callers share one access path.
|
|
106
|
+
*/
|
|
107
|
+
export interface SubprocessOutcome {
|
|
108
|
+
/** Exit code; null when the process died from a signal. */
|
|
109
|
+
exitCode: number | null;
|
|
110
|
+
/** Terminating signal (e.g. 'SIGTERM'); null on normal exit. */
|
|
111
|
+
signal: NodeJS.Signals | null;
|
|
112
|
+
}
|
|
113
|
+
/** One incremental {@link SubprocessOutputReader.readFrom} read. */
|
|
114
|
+
export interface SubprocessOutputRead {
|
|
115
|
+
/** Stream text from the requested offset (the whole retained tail when lossy). */
|
|
116
|
+
text: string;
|
|
117
|
+
/** Whole-stream offset to resume from on the next read. */
|
|
118
|
+
nextOffset: number;
|
|
119
|
+
/** True when the requested offset slid out of the in-memory tail window. */
|
|
120
|
+
lossy: boolean;
|
|
121
|
+
/** Path to the full-stream spill file, when one was created and remains intact. */
|
|
122
|
+
spillPath?: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Cursor-free incremental access to one collected output stream. Offsets are
|
|
126
|
+
* whole-stream byte coordinates owned by the caller, so independent readers
|
|
127
|
+
* cannot consume one another's output; `readFrom(0)` after settlement is the
|
|
128
|
+
* batch result (`lossy` then means the in-memory tail lost its head — the
|
|
129
|
+
* {@link CollectedOutput.truncated} fact).
|
|
130
|
+
*/
|
|
131
|
+
export interface SubprocessOutputReader {
|
|
132
|
+
/**
|
|
133
|
+
* Read everything captured since `fromByte`. When that offset has slid out
|
|
134
|
+
* of the in-memory tail window the read is `lossy` — it returns the whole
|
|
135
|
+
* retained tail and the gap is only recoverable from the spill file.
|
|
136
|
+
* @param fromByte - whole-stream offset to resume from (a prior read's `nextOffset`; 0 for the first read).
|
|
137
|
+
* @returns the delta text, the next offset, the `lossy` flag, and the spill path when one exists.
|
|
138
|
+
*/
|
|
139
|
+
readFrom(fromByte: number): SubprocessOutputRead;
|
|
140
|
+
}
|
|
141
|
+
/** Offset-based readers for the streams spawned in collect mode. */
|
|
142
|
+
export interface SubprocessCollectedOutputs {
|
|
143
|
+
/** Present iff stdout is a {@link SubprocessCollect}. */
|
|
144
|
+
readonly stdout?: SubprocessOutputReader;
|
|
145
|
+
/** Present iff stderr is a {@link SubprocessCollect}. */
|
|
146
|
+
readonly stderr?: SubprocessOutputReader;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* A live subprocess and its provider-managed process range. Collected output
|
|
150
|
+
* remains readable after exit; piped streams belong to the caller.
|
|
151
|
+
*
|
|
152
|
+
* Termination and {@link SubprocessHandle.waitForExit} use the same managed
|
|
153
|
+
* range. Each provider documents the range it can observe and its signalling
|
|
154
|
+
* and observation limits.
|
|
155
|
+
*/
|
|
156
|
+
export interface SubprocessHandle {
|
|
157
|
+
/** The child's stdin, present iff spawned with `stdin: 'pipe'`. */
|
|
158
|
+
readonly stdin: Writable | undefined;
|
|
159
|
+
/** The child's raw stdout, present iff spawned with `stdout: 'pipe'`. */
|
|
160
|
+
readonly stdout: Readable | undefined;
|
|
161
|
+
/** The child's raw stderr, present iff spawned with `stderr: 'pipe'`. */
|
|
162
|
+
readonly stderr: Readable | undefined;
|
|
163
|
+
/** Separate caller-owned byte channel when requested; native startup failure may leave it absent. */
|
|
164
|
+
readonly control: Duplex | undefined;
|
|
165
|
+
/** Offset-based readers for collect-mode streams (also readable after exit). */
|
|
166
|
+
readonly collected: SubprocessCollectedOutputs;
|
|
167
|
+
/** Resolves with spawned-command exit facts; rejects for spawn or provider failures. */
|
|
168
|
+
readonly done: Promise<SubprocessOutcome>;
|
|
169
|
+
/**
|
|
170
|
+
* Begin the provider's documented termination procedure on the managed range
|
|
171
|
+
* — the seam's only termination verb. Idempotent, a no-op once that range is
|
|
172
|
+
* gone, and also triggered by the spec's abort signal.
|
|
173
|
+
*/
|
|
174
|
+
terminate(): void;
|
|
175
|
+
/**
|
|
176
|
+
* Wait until the same managed range is empty — not just until the spawned
|
|
177
|
+
* command reports its outcome, so surviving work remains observable.
|
|
178
|
+
* @param signal - optional bound for the wait.
|
|
179
|
+
* @returns `true` when the managed range is empty, `false` when the signal aborted first.
|
|
180
|
+
* @throws when the selected provider can no longer observe its managed range.
|
|
181
|
+
*/
|
|
182
|
+
waitForExit(signal?: AbortSignal): Promise<boolean>;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Signals supported by the terminal-process primitive. Kept member-identical
|
|
186
|
+
* to `TerminalSignal` in `@qilin-agent/terminal` without a cross-seam dependency;
|
|
187
|
+
* change both together.
|
|
188
|
+
*/
|
|
189
|
+
export type SubprocessTerminalSignal = 'SIGINT' | 'SIGTERM' | 'SIGKILL' | 'SIGTSTP' | 'SIGHUP';
|
|
190
|
+
/** Shell-selection facts from the subprocess provider's execution environment. */
|
|
191
|
+
export interface SubprocessTerminalEnvironment {
|
|
192
|
+
/** Operating-system family that interprets executable paths and shell arguments. */
|
|
193
|
+
platform: 'posix' | 'windows';
|
|
194
|
+
/** Login or environment-selected shell, when the provider can resolve one. */
|
|
195
|
+
defaultShell?: string;
|
|
196
|
+
}
|
|
197
|
+
/** A fully specified terminal-process spawn. */
|
|
198
|
+
export interface SubprocessTerminalSpawnSpec {
|
|
199
|
+
/** Executable and arguments; `argv[0]` is the program. */
|
|
200
|
+
argv: readonly string[];
|
|
201
|
+
/** Working directory in this subprocess provider's execution world. */
|
|
202
|
+
cwd: string;
|
|
203
|
+
/** Explicit environment layered after the provider's ambient scrub. */
|
|
204
|
+
env?: Record<string, string> | undefined;
|
|
205
|
+
/** Initial terminal row count. */
|
|
206
|
+
rows: number;
|
|
207
|
+
/** Initial terminal column count. */
|
|
208
|
+
cols: number;
|
|
209
|
+
/** Terminal emulation advertised to the child through TERM. */
|
|
210
|
+
terminalType: string;
|
|
211
|
+
/** Request supported interactive-shell lifecycle observation; unsupported launches report unknown activity. */
|
|
212
|
+
shellActivity?: boolean | undefined;
|
|
213
|
+
/** TERM-to-KILL cleanup grace for the complete terminal session. */
|
|
214
|
+
graceMs: number;
|
|
215
|
+
/** Cancellation of terminal allocation; a published handle owns its later lifetime. */
|
|
216
|
+
signal?: AbortSignal | undefined;
|
|
217
|
+
}
|
|
218
|
+
/** Current foreground process-group facts for one terminal. */
|
|
219
|
+
export interface SubprocessTerminalForeground {
|
|
220
|
+
/** Foreground process-group id published by the terminal driver. */
|
|
221
|
+
processGroupId: number;
|
|
222
|
+
/** Whether the provider can currently prove that group is waiting on terminal input. */
|
|
223
|
+
inputWaiting: boolean;
|
|
224
|
+
}
|
|
225
|
+
/** Provider observation of shell lifecycle and surviving owned jobs. */
|
|
226
|
+
export interface SubprocessTerminalActivity {
|
|
227
|
+
/** Idle requires positive prompt evidence and no observed foreground, background, or stopped jobs. */
|
|
228
|
+
state: 'idle' | 'busy' | 'unknown';
|
|
229
|
+
/** Changes with input, shell transitions, and changed process observations; scoped to this handle. */
|
|
230
|
+
revision: number;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* One live terminal process and its owned OS session. Terminal allocation,
|
|
234
|
+
* foreground-group inspection/signalling, and whole-session quiescence are one
|
|
235
|
+
* deep subprocess primitive because none can be reconstructed from ordinary
|
|
236
|
+
* piped stdio without substrate-specific process control.
|
|
237
|
+
*/
|
|
238
|
+
export interface SubprocessTerminalHandle {
|
|
239
|
+
/** Top-level terminal process id. */
|
|
240
|
+
readonly pid: number;
|
|
241
|
+
/** UTF-8 terminal output bytes in delivery order; ends after queued output when the terminal exits. */
|
|
242
|
+
readonly output: Readable;
|
|
243
|
+
/** Resolves when the top-level process exits; rejects for a terminal startup, provider, or live transport failure. */
|
|
244
|
+
readonly done: Promise<SubprocessOutcome>;
|
|
245
|
+
/**
|
|
246
|
+
* Write text to the terminal input.
|
|
247
|
+
* @param data - text to deliver without implicit newline conversion.
|
|
248
|
+
*/
|
|
249
|
+
write(data: string): Promise<void>;
|
|
250
|
+
/**
|
|
251
|
+
* Change the terminal dimensions and notify its foreground application.
|
|
252
|
+
* @param cols - positive terminal column count.
|
|
253
|
+
* @param rows - positive terminal row count.
|
|
254
|
+
*/
|
|
255
|
+
resize(cols: number, rows: number): Promise<void>;
|
|
256
|
+
/**
|
|
257
|
+
* Inspect the current foreground process group.
|
|
258
|
+
* @returns its id and input-wait fact, or undefined when no foreground group can be resolved.
|
|
259
|
+
*/
|
|
260
|
+
inspectForeground(): Promise<SubprocessTerminalForeground | undefined>;
|
|
261
|
+
/**
|
|
262
|
+
* Observe command activity without interpreting output or treating silence as completion.
|
|
263
|
+
* @returns a fresh observation; unsupported shells and incomplete observations report unknown.
|
|
264
|
+
*/
|
|
265
|
+
inspectActivity(): Promise<SubprocessTerminalActivity>;
|
|
266
|
+
/**
|
|
267
|
+
* Deliver a signal to the current foreground process group.
|
|
268
|
+
* @param signal - permitted terminal signal.
|
|
269
|
+
* @returns the exact group id that received it.
|
|
270
|
+
*/
|
|
271
|
+
signalForeground(signal: SubprocessTerminalSignal): Promise<number>;
|
|
272
|
+
/**
|
|
273
|
+
* Idempotently terminate every terminal-session member the provider can still observe and await quiescence.
|
|
274
|
+
* After settlement, no write, inspection, or signal call remains in flight.
|
|
275
|
+
* Providers document substrate-specific observability limits.
|
|
276
|
+
*/
|
|
277
|
+
terminate(): Promise<void>;
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=types.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qilin-agent/subprocess",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"description": "Subprocess seam (ctx.subprocess) for the QiLin — managed process groups, bounded spill-backed output, and escalated kills behind one abstract service",
|
|
4
|
+
"version": "3.1.4",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/kkutysllb/QiLin.git",
|
|
11
|
+
"directory": "packages/subprocess/subprocess"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "lib/index.js",
|
|
18
|
+
"types": "lib/types/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./lib/types/index.d.ts",
|
|
22
|
+
"default": "./lib/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./src/*": "./src/*",
|
|
25
|
+
"./package.json": "./package.json",
|
|
26
|
+
"./control": {
|
|
27
|
+
"types": "./lib/types/control.d.ts",
|
|
28
|
+
"default": "./lib/control.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"lib/index.js",
|
|
33
|
+
"lib/control.js",
|
|
34
|
+
"lib/types/**/*.d.ts"
|
|
35
|
+
],
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@qilin-agent/kylin": "~4.0.3",
|
|
39
|
+
"@qilin-agent/http-proxy": "3.1.4"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@qilin-agent/http-proxy": "3.1.4",
|
|
43
|
+
"@qilin-agent/kylin": "~4.0.3",
|
|
44
|
+
"@qilin-agent/launch-environment": "3.1.4"
|
|
45
|
+
}
|
|
6
46
|
}
|