threadwire 0.1.6 → 0.1.9
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/CHANGELOG.md +52 -0
- package/README.md +16 -5
- package/TELEGRAM-INGRESS.md +13 -3
- package/bin/isolated-runtime.js +5 -0
- package/bin/kimi-model-broker.js +5 -0
- package/bin/model-broker.js +5 -0
- package/docs/container-runtime.md +22 -0
- package/docs/isolated-provider-runtime.md +232 -0
- package/package.json +10 -3
- package/scripts/provider-shims/front-door.sh.template +18 -0
- package/scripts/verify-package.js +23 -1
- package/src/absolute-deadline.js +94 -0
- package/src/activity-log.js +3 -3
- package/src/cli.js +130 -25
- package/src/delegated-result-admission.js +1 -1
- package/src/docker-api.js +131 -0
- package/src/isolated-runtime-client.js +158 -0
- package/src/isolated-runtime.js +1117 -0
- package/src/isolated-state.js +470 -0
- package/src/isolated-worker.js +144 -0
- package/src/kimi-model-broker-policy.js +209 -0
- package/src/kimi-model-broker.js +325 -0
- package/src/kimi-oauth-store.js +267 -0
- package/src/model-broker-policy.js +139 -0
- package/src/model-broker.js +313 -0
- package/src/mount-policy.js +28 -0
- package/src/normalized-output.js +68 -0
- package/src/providers/index.js +8 -3
- package/src/providers/kimi.js +165 -0
- package/src/relay-write.js +44 -0
- package/src/telegram-ingress/command.js +4 -4
- package/src/telegram-ingress/config.js +18 -0
- package/src/telegram-ingress/core.js +65 -2
- package/src/telegram-ingress/http.js +2 -1
- package/src/telegram-webhook.js +11 -0
- package/src/workspace-profile.js +3 -3
- package/threadwire.workspace-profiles.json +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.1.9 - 2026-07-28
|
|
6
|
+
|
|
7
|
+
- Add native Kimi Code subscription support through official OAuth, a distinct
|
|
8
|
+
narrow Kimi model broker, an immutable pinned worker/auth image set, and a
|
|
9
|
+
mandatory provider-dedicated isolated runtime with no direct fallback.
|
|
10
|
+
- Normalize only trusted Kimi assistant/tool/session envelopes, preserve exact
|
|
11
|
+
native continuation and Telegram topic routing, and suppress thinking, raw
|
|
12
|
+
tool payloads, arbitrary metadata, stderr/stdout, grants, and OAuth material.
|
|
13
|
+
- Bind Kimi grants and authenticated session state to provider, task, session,
|
|
14
|
+
run network, approved alias, and wire model; add adversarial fresh/resume,
|
|
15
|
+
failure, cancellation, refresh, redaction, network, and cross-provider tests.
|
|
16
|
+
|
|
17
|
+
## 0.1.8 - 2026-07-26
|
|
18
|
+
|
|
19
|
+
- Add `threadwire run --transcript <path>` to durably capture the exact normalized
|
|
20
|
+
stdout JSONL protocol in a private, exclusive file while keeping activity logs
|
|
21
|
+
and scoped raw evidence artifacts separate.
|
|
22
|
+
- Add an opt-in, fail-closed isolated Codex write runtime. A root-owned
|
|
23
|
+
supervisor launches one worktree-only worker per task while a separate narrow
|
|
24
|
+
model broker retains upstream credentials. Fresh/resume, secret rotation,
|
|
25
|
+
filesystem/network/socket escapes, configuration overrides and cleanup are
|
|
26
|
+
covered by adversarial Docker E2E.
|
|
27
|
+
- Bind broker grants to dedicated trusted run-network listeners and an exact
|
|
28
|
+
allowlisted model/request schema; add stable worktree ancestry checks,
|
|
29
|
+
task-private persistent resume state, contained nested cwd and structured
|
|
30
|
+
Codex arguments. The Compose deployment is now an explicit opt-in override.
|
|
31
|
+
- Allocate authenticated, uniquely labeled state per fresh Codex lineage;
|
|
32
|
+
reject volume adoption and nested worktree mounts; use Docker's structured
|
|
33
|
+
host-bind API; and require an explicit supervisor-side model allowlist.
|
|
34
|
+
- Bound and eagerly expire preflight capabilities, serialize writable lineage
|
|
35
|
+
resumes, and automatically reconcile and garbage-collect authenticated state
|
|
36
|
+
volumes with restart-safe cleanup retry.
|
|
37
|
+
- Bound active runs and Docker/broker operations through cleanup, reserve state
|
|
38
|
+
before awaited revalidation, autonomously close expired grant listeners, and
|
|
39
|
+
reconcile exactly owned workers and networks across every container state.
|
|
40
|
+
- Abort accepted broker work and caller-disconnected runs at one absolute
|
|
41
|
+
deadline; make crash cleanup authority an fsync'd authenticated full run
|
|
42
|
+
identity rather than labels; and durably fsync registry rename evidence.
|
|
43
|
+
- Carry one launch deadline from preflight through prompt/secret/evidence/
|
|
44
|
+
Telegram setup, worker execution and cleanup; make supervisor and broker
|
|
45
|
+
shutdown abort control uploads and active work; move Docker GC outside the
|
|
46
|
+
registry transaction; and stream-cap broker/runtime responses.
|
|
47
|
+
- Gate worker start on one-shot activation of a verified pending broker grant
|
|
48
|
+
and address its listener by the inspected per-run IP, removing dynamic
|
|
49
|
+
network-alias readiness races.
|
|
50
|
+
- Route relay-profile Telegram Codex jobs exclusively through the isolated
|
|
51
|
+
runtime, keep validate-only launches local, preserve bounded stdout/stderr
|
|
52
|
+
evidence channels, and protect live lineages from concurrent reconciliation.
|
package/README.md
CHANGED
|
@@ -7,6 +7,13 @@ non-root Codex provider, Docker secrets, loopback TLS proxy, verification, and
|
|
|
7
7
|
rollback path, see [Container runtime](docs/container-runtime.md). The legacy
|
|
8
8
|
host launcher below remains supported and is separate from that deployment.
|
|
9
9
|
|
|
10
|
+
Relay-originated Codex writes are explicit and default off. `--relay-write`
|
|
11
|
+
requires a reviewed `--workspace-profile` plus the root-owned isolated runtime
|
|
12
|
+
and credential broker. The worker gets one writable task worktree, no upstream
|
|
13
|
+
or Git credential, and no route except its run-scoped broker. Missing
|
|
14
|
+
prerequisites fail before prompt or credential reads with no native fallback.
|
|
15
|
+
See [Isolated provider runtime](docs/isolated-provider-runtime.md).
|
|
16
|
+
|
|
10
17
|
## Run with npx
|
|
11
18
|
|
|
12
19
|
Threadwire requires Node.js 22 or newer. Run the published CLI without a global
|
|
@@ -53,11 +60,11 @@ Use the local launcher. Install or refresh it after updating Threadwire with `np
|
|
|
53
60
|
-- --model example-model
|
|
54
61
|
```
|
|
55
62
|
|
|
56
|
-
The target always has the form `telegram:<chat-id>:<thread-id>` for topics, or `telegram:<chat-id>` for direct messages (DMs) and ordinary chats. The chat ID is always required and must be a nonzero signed integer. The thread ID is optional and valid only as the explicit topic suffix: when present it must be a positive safe integer, and a DM target simply omits it — a missing thread ID is never an error. `--provider` must be exactly one of `codex`, `claude`, or `opencode`. Malformed or missing provider, chat ID, or thread ID arguments are hard failures (exit code 2) reported through Threadwire's normal error path with fixed messages; they fail before the Telegram token is read, before any transport is created, and before any provider is launched, and the messages never echo the bot token or the untrusted argument value. `--process-number` accepts an explicit positive safe integer for automation; when omitted, Threadwire uses its launcher PID. Every Telegram message is labeled consistently, such as `[P42] …`, including chunked continuations.
|
|
63
|
+
The target always has the form `telegram:<chat-id>:<thread-id>` for topics, or `telegram:<chat-id>` for direct messages (DMs) and ordinary chats. The chat ID is always required and must be a nonzero signed integer. The thread ID is optional and valid only as the explicit topic suffix: when present it must be a positive safe integer, and a DM target simply omits it — a missing thread ID is never an error. `--provider` must be exactly one of `codex`, `claude`, `kimi`, or `opencode`. Malformed or missing provider, chat ID, or thread ID arguments are hard failures (exit code 2) reported through Threadwire's normal error path with fixed messages; they fail before the Telegram token is read, before any transport is created, and before any provider is launched, and the messages never echo the bot token or the untrusted argument value. `--process-number` accepts an explicit positive safe integer for automation; when omitted, Threadwire uses its launcher PID. Every Telegram message is labeled consistently, such as `[P42] …`, including chunked continuations.
|
|
57
64
|
|
|
58
|
-
Pass `--workspace-profile <name>` only when invoking Threadwire inside the container-native runtime, for example with `node bin/threadwire.js run` as shown in [Container runtime](docs/container-runtime.md). A profile selects a reviewed entry from the checked-in [threadwire.workspace-profiles.json](threadwire.workspace-profiles.json). It supplies the absolute in-container repository root, the exact in-container working directory, and the allowlisted providers for that workspace. Threadwire validates the selected profile, rejects malformed or unknown names, verifies the configured workspace exists and is still a healthy Git worktree, and records the selected profile plus immutable revision/worktree identity in the local activity log before provider spawn. The checked-in `container-runtime` default targets `/workspace/threadwire` inside the container-native runtime and
|
|
65
|
+
Pass `--workspace-profile <name>` only when invoking Threadwire inside the container-native runtime, for example with `node bin/threadwire.js run` as shown in [Container runtime](docs/container-runtime.md). A profile selects a reviewed entry from the checked-in [threadwire.workspace-profiles.json](threadwire.workspace-profiles.json). It supplies the absolute in-container repository root, the exact in-container working directory, and the allowlisted providers for that workspace. Threadwire validates the selected profile, rejects malformed or unknown names, verifies the configured workspace exists and is still a healthy Git worktree, and records the selected profile plus immutable revision/worktree identity in the local activity log before provider spawn. The checked-in `container-runtime` default targets `/workspace/threadwire` inside the container-native runtime and allowlists `codex` and isolated `kimi`.
|
|
59
66
|
|
|
60
|
-
Provider tool-start and tool-finish events do not produce Telegram messages by default. Pass the boolean `--tool-messages` flag to opt in, as shown above. When enabled, each tool is one concise, silent status line rather than a pair of noisy start/finish notices: when a tool starts, Threadwire sends `🛠 [P42] Tool: <safe command or tool description>` (for example `🛠 [P42] Tool: command — git status --short`), and when that same tool finishes, it edits the original message in place so it begins `✅` instead — no second completion message is sent, and Telegram edits raise no notification. The command/description shows a safe CLI-style preview with credential-bearing values (tokens, passwords, keys, authorization/cookie forms, credentials in URLs) redacted and control characters stripped; only normalized Codex command-completion output may also be appended to that same edited status as a Telegram-native expandable HTML blockquote with the visible label `Output →`; it is credential-redacted, HTML-escaped, and Unicode-safe bounded. Stderr, reasoning, and every other raw provider result remain excluded. Use `--max-output-length <positive-integer>` to truncate only that preview Unicode-safely; omit the option for unlimited tool detail. A prompt may instead come from `--prompt-file` or piped stdin, and every source rejects blank/whitespace content. Arguments following `--` pass to the selected provider, except output-stream options owned by Threadwire.
|
|
67
|
+
Provider tool-start and tool-finish events do not produce Telegram messages by default. Pass the boolean `--tool-messages` flag to opt in, as shown above. When enabled, each tool is one concise, silent status line rather than a pair of noisy start/finish notices: when a tool starts, Threadwire sends `🛠 [P42] Tool: <safe command or tool description>` (for example `🛠 [P42] Tool: command — git status --short`), and when that same tool finishes, it edits the original message in place so it begins `✅` instead — no second completion message is sent, and Telegram edits raise no notification. The command/description shows a safe CLI-style preview with credential-bearing values (tokens, passwords, keys, authorization/cookie forms, credentials in URLs) redacted and control characters stripped; only normalized Codex command-completion output may also be appended to that same edited status as a Telegram-native expandable HTML blockquote with the visible label `Output →`; it is credential-redacted, HTML-escaped, and Unicode-safe bounded. Stderr, reasoning, and every other raw provider result remain excluded. Use `--max-output-length <positive-integer>` to truncate only that preview Unicode-safely; omit the option for unlimited tool detail. A prompt may instead come from `--prompt-file` or piped stdin, and every source rejects blank/whitespace content. Arguments following `--` pass to the selected provider, except output-stream options owned by Threadwire. Kimi is narrower: it always uses the dedicated isolated subscription runtime and accepts only one server-approved model alias through `--model <alias>` (or `-m <alias>`); Threadwire owns its prompt, output, session, permissions, configuration, tools, skills, plugins, MCP, and directories.
|
|
61
68
|
|
|
62
69
|
Threadwire directly spawns the structurally separate terminal adapters `/opt/data/libexec/threadwire/codex`, `/opt/data/libexec/threadwire/claude`, or `/opt/data/libexec/threadwire/opencode-local-fleet` with an argv array and `shell: false`. These libexec adapters hold the real provider-CLI logic and are never on a worker's `PATH`, so relaying can never recurse back into the front-door shims. Each spawn also sets `THREADWIRE_ACTIVE=1` in the provider child environment. The executable can be overridden per provider with `THREADWIRE_CODEX_BIN`, `THREADWIRE_CLAUDE_BIN`, or `THREADWIRE_OPENCODE_BIN` for staged cutover and rollback. It does not impose a worker timeout. `SIGINT` and `SIGTERM` are forwarded to the worker as explicit cancellation.
|
|
63
70
|
|
|
@@ -67,13 +74,17 @@ Threadwire directly spawns the structurally separate terminal adapters `/opt/dat
|
|
|
67
74
|
|
|
68
75
|
The relay contract: the caller supplies `THREADWIRE_TARGET` (`telegram:<chat-id>` or `telegram:<chat-id>:<thread-id>`) and, optionally, `THREADWIRE_PROCESS_NUMBER`. The prompt arrives on a piped stdin; all native provider flags pass through after `--`. A positional prompt is never parsed. A missing `THREADWIRE_TARGET` fails closed (exit code 2) and a malformed one is rejected by the launcher's token-free preflight — the shim never invents a default destination and never silently downgrades a requested relay to a direct run. A shim relays only for a worker mode (`codex exec …`, `opencode-local-fleet run …`, or any non-interactive `claude …`); it drops the worker subcommand before relaying and Threadwire re-adds it around the prompt. Version and help queries, non-worker subcommands, interactive TTY sessions, and any invocation already inside a relayed run (`THREADWIRE_ACTIVE=1`, so nested provider workers stay local/native) run the real CLI directly through the libexec adapter.
|
|
69
76
|
|
|
70
|
-
Pass `--resume-session <exact-provider-session-id>` to continue a stored Codex, Claude, or OpenCode session. Threadwire preserves the provider's native resume ordering and streaming options and never uses implicit latest-session behavior.
|
|
77
|
+
Pass `--resume-session <exact-provider-session-id>` to continue a stored Codex, Claude, or OpenCode session. Threadwire preserves the provider's native resume ordering and streaming options and never uses implicit latest-session behavior.
|
|
78
|
+
|
|
79
|
+
Pass `--transcript <path>` to capture the exact normalized, caller-facing JSONL protocol written to run stdout. The file is created privately (mode `0600`), must not already exist, and contains the same records in the same byte order as stdout. It never contains provider JSONL, reasoning, tool payloads, prompts, stderr, or credentials.
|
|
80
|
+
|
|
81
|
+
`--activity-log <path>` is different: it retains its append contract and writes only minimal safe lifecycle facts—the selected workspace profile and immutable provenance, provider start (`provider`, child `pid`), and available native session ID. It is local-only and never enters Telegram or normalized stdout.
|
|
71
82
|
|
|
72
83
|
Provider stdout is decoded as JSONL with a finite 1 MiB UTF-8 byte limit for each pending unterminated record. Exceeding it rejects the run, removes listeners and signal handlers, and terminates the provider without parsing or rendering the oversized content.
|
|
73
84
|
|
|
74
85
|
For normal `threadwire run` execution, Threadwire stdout is the parent-model admission plane. It emits exactly one versioned terminal `delegated_result` JSON object and no progress records. The envelope contains a bounded, credential-redacted, control-normalized final conclusion when one was produced, structured terminal state and exit code, and the exact bounded provider session handle when available. Its closed schema can also represent concise blocker/decision requests, opaque artifact handles, commit/URL references, and a concise validation summary; current provider adapters do not infer these optional fields from prose. Unknown, nested, wrongly typed, cardinality-invalid, or oversized admission candidates fail closed. See [Delegated-result protocol](docs/delegated-result-protocol.md) for the complete schema, limits, and caller obligations.
|
|
75
86
|
|
|
76
|
-
Set the trusted operator setting `THREADWIRE_EVIDENCE_ROOT` for standalone runs to persist the delegated prompt and raw provider stdout/stderr as a scoped file-backed artifact; Telegram ingress uses its dedicated evidence volume automatically. The parent or owning Telegram topic receives only an opaque bearer handle. Retrieve later with `threadwire evidence read` or the authenticated `/evidence` Telegram command and an explicit bounded selector. See [Evidence artifacts](docs/evidence-artifacts.md) for permissions, quotas, retention, recovery, authorization, redaction, privacy, and cleanup behavior.
|
|
87
|
+
Scoped raw evidence artifacts are separate from both files above. Set the trusted operator setting `THREADWIRE_EVIDENCE_ROOT` for standalone runs to persist the delegated prompt and raw provider stdout/stderr as a scoped file-backed artifact; Telegram ingress uses its dedicated evidence volume automatically. The parent or owning Telegram topic receives only an opaque bearer handle. Retrieve later with `threadwire evidence read` or the authenticated `/evidence` Telegram command and an explicit bounded selector. See [Evidence artifacts](docs/evidence-artifacts.md) for permissions, quotas, retention, recovery, authorization, redaction, privacy, and cleanup behavior.
|
|
77
88
|
|
|
78
89
|
Telegram is the separate user-visible progress plane. Lifecycle notices, diagnostics, tool activity, and assistant streaming continue there independently; `--tool-messages` affects only Telegram. Execution-environment details, provider records and streams, lifecycle progress, diagnostics, tool names/arguments/details/output, stderr, prompts, narration/reasoning, and full transcripts never enter stdout. Threadwire's bounded error diagnostic remains on stderr. Help output is the sole non-run, human-readable stdout mode. This default is an intentional pre-1.0 protocol break from the former mixed Hermes JSONL stream.
|
|
79
90
|
|
package/TELEGRAM-INGRESS.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Telegram Ingress for Threadwire
|
|
2
2
|
|
|
3
|
-
Standalone Threadwire webhook service that accepts Telegram Bot API updates
|
|
3
|
+
Standalone Threadwire webhook service that accepts Telegram Bot API updates.
|
|
4
|
+
Codex, Claude, and OpenCode retain the existing worker behavior. Kimi always
|
|
5
|
+
uses its dedicated isolated preflight/run authority with no native fallback;
|
|
6
|
+
a missing Kimi runtime fails before provider or transport construction. Hermes
|
|
7
|
+
is not involved.
|
|
4
8
|
|
|
5
9
|
## Important bot ownership
|
|
6
10
|
|
|
@@ -41,7 +45,7 @@ The process binds to **loopback (`127.0.0.1`) by default**. Expose it only throu
|
|
|
41
45
|
- **Residual restart boundary:** this store is process-local memory only. A process restart clears reservations and completed IDs, so Telegram redelivery after restart can launch work again until IDs are re-observed. This is not durable multi-instance replay protection.
|
|
42
46
|
- Bound concurrent spawned workers (default 4, configurable). When at capacity, reject with `503` without launching. The concurrency slot is released when the background worker settles (success or failure after spawn), or immediately on pre-spawn failure.
|
|
43
47
|
- Both chat and sender allowlists are mandatory and exact; missing IDs fail closed.
|
|
44
|
-
- Command grammar is exact: `/code[optional @bot] <codex|claude|opencode> <nonblank prompt>`.
|
|
48
|
+
- Command grammar is exact: `/code[optional @bot] <codex|claude|kimi|opencode> <nonblank prompt>`.
|
|
45
49
|
- Direct target is derived only from authenticated `message.chat.id` and optional positive `message_thread_id`.
|
|
46
50
|
- DM/chat: `telegram:<chat-id>`
|
|
47
51
|
- Topic: `telegram:<chat-id>:<thread-id>`
|
|
@@ -49,7 +53,10 @@ The process binds to **loopback (`127.0.0.1`) by default**. Expose it only throu
|
|
|
49
53
|
- Provider child env strips Telegram tokens, webhook secret, allowlists, bind/port, concurrency, tool-message, and update-guard service config. `runWorker` also strips Telegram tokens.
|
|
50
54
|
- No logging of headers, bodies, prompts, or secrets.
|
|
51
55
|
- Webhook secret must be at least 32 characters after trim.
|
|
52
|
-
-
|
|
56
|
+
- Native work returns `202` only after the worker child has actually spawned
|
|
57
|
+
(`onSpawn`). Isolated Codex work returns `202` only after the authenticated
|
|
58
|
+
isolated run completes successfully; preflight alone is never treated as
|
|
59
|
+
dispatch success.
|
|
53
60
|
- Failure before spawn rejects the request with `500` and is reported once via the operational error reporter.
|
|
54
61
|
- Failure after spawn is reported once via the worker-failure reporter (not double-reported through both channels in the real entrypoint wiring).
|
|
55
62
|
- Authenticated irrelevant updates return `200`.
|
|
@@ -76,6 +83,8 @@ Optional:
|
|
|
76
83
|
| `THREADWIRE_TELEGRAM_REQUEST_TIMEOUT_MS` | Deadline for each outbound Telegram request, including response parsing (default `30000`, positive safe integer no greater than `2147483647`) |
|
|
77
84
|
| `THREADWIRE_UPDATE_ID_CAPACITY` | Max retained completed `update_id` entries (default `10000`) |
|
|
78
85
|
| `THREADWIRE_UPDATE_ID_TTL_MS` | Retention TTL for completed `update_id` entries in ms (default `86400000`) |
|
|
86
|
+
| `THREADWIRE_KIMI_ISOLATED_RUNTIME_URL` | Dedicated Kimi supervisor URL; Compose defaults to `http://kimi-isolated-runtime:8790` |
|
|
87
|
+
| `THREADWIRE_KIMI_ISOLATED_RUNTIME_CONTROL_TOKEN` | Dedicated Kimi control token. When absent, `/code kimi` remains recognized but fails closed before launch. |
|
|
79
88
|
|
|
80
89
|
## Run
|
|
81
90
|
|
|
@@ -105,6 +114,7 @@ The wrapper starts the service exported by the importable
|
|
|
105
114
|
/code codex write a hello world program
|
|
106
115
|
/code@your_bot claude explain quantum computing
|
|
107
116
|
/code opencode build a todo app
|
|
117
|
+
/code kimi inspect this repository
|
|
108
118
|
```
|
|
109
119
|
|
|
110
120
|
## Modules
|
|
@@ -199,3 +199,25 @@ Back them up using an operator-approved volume backup process before migration.
|
|
|
199
199
|
`docker compose down --volumes` is destructive and removes the container-owned
|
|
200
200
|
repository and provider state; use it only for an explicitly confirmed teardown.
|
|
201
201
|
Never export or archive mounted secret files with workspace/provider backups.
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
## Kimi subscription containers
|
|
205
|
+
|
|
206
|
+
Kimi is intentionally excluded from the same-UID container-native provider
|
|
207
|
+
launch described above. Never mount `threadwire-kimi-oauth`, its access/refresh
|
|
208
|
+
tokens, or the auth home into the Threadwire runtime, supervisor, worker,
|
|
209
|
+
workspace, or session-state volume. Kimi uses four immutable build targets:
|
|
210
|
+
`kimi-auth`, `kimi-model-broker`, `kimi-relay-worker`, and `isolated-runtime`.
|
|
211
|
+
The first two share UID/GID 10003 and only the OAuth volume; the worker uses
|
|
212
|
+
UID/GID 10002 with only the selected worktree and provider-bound state; the
|
|
213
|
+
root supervisor alone receives the Docker socket. The broker/auth service uses
|
|
214
|
+
Node `22.19.0` and exactly `@moonshot-ai/kimi-code@0.29.2` with the checked npm
|
|
215
|
+
integrity and archive SHA-512 in the Dockerfile.
|
|
216
|
+
|
|
217
|
+
Enable the checked-in override with `--profile kimi` only after the OAuth/model
|
|
218
|
+
operator flow in [Isolated provider runtime](isolated-provider-runtime.md).
|
|
219
|
+
The default workspace profile allowlists Kimi because isolation is mandatory;
|
|
220
|
+
without the dedicated Kimi control token/client, both CLI and Telegram Kimi
|
|
221
|
+
jobs fail closed rather than using the native child path. Keep broker egress
|
|
222
|
+
restricted to official Kimi Code subscription/auth endpoints and do not provide
|
|
223
|
+
a Moonshot API key, custom base URL, Docker socket, or ingress secret.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# Isolated provider runtime
|
|
2
|
+
|
|
3
|
+
Relay-originated Codex writes are disabled unless the caller supplies both
|
|
4
|
+
`--relay-write` and a reviewed `--workspace-profile`. The write path never
|
|
5
|
+
falls back to the native provider launcher.
|
|
6
|
+
When the Telegram service is configured with isolated-runtime authority, Codex
|
|
7
|
+
webhook jobs use the same preflight/run boundary exclusively; an unavailable
|
|
8
|
+
runtime fails the job rather than invoking the native runner. Raw provider
|
|
9
|
+
stdout and stderr are returned as separately identified, bounded chunks for
|
|
10
|
+
the existing evidence callbacks.
|
|
11
|
+
|
|
12
|
+
## Security boundary
|
|
13
|
+
|
|
14
|
+
Telegram ingress has neither provider secrets nor container-runtime authority.
|
|
15
|
+
The root-owned isolated-runtime supervisor has Docker authority and a read-only
|
|
16
|
+
view of allowlisted worktree roots, but no provider credential. The model
|
|
17
|
+
broker alone mounts the upstream credential. Each grant creates a listener
|
|
18
|
+
bound to the broker's trusted per-run network interface. That listener accepts
|
|
19
|
+
one token, one run/network lineage, one allowlisted model, and a closed
|
|
20
|
+
`/v1/responses` schema; it is not a generic HTTP proxy.
|
|
21
|
+
The listener remains non-authorizing while the supervisor creates and verifies
|
|
22
|
+
the worker. The supervisor then activates it exactly once immediately before
|
|
23
|
+
worker start. Workers receive the inspected per-run broker IP directly, so
|
|
24
|
+
launch does not depend on asynchronous container-DNS alias propagation.
|
|
25
|
+
|
|
26
|
+
For each fresh or explicit-resume run the supervisor creates a unique Docker
|
|
27
|
+
internal network, attaches the broker, and launches an immutable worker. The
|
|
28
|
+
worker has a read-only root, all capabilities dropped, no-new-privileges,
|
|
29
|
+
container-local tmp/run, resource limits, and exactly one writable
|
|
30
|
+
worktree mount. It has no host home, common Git directory, sibling worktree,
|
|
31
|
+
host temp, shared provider state, secret, Docker/SSH socket, or external route. Its
|
|
32
|
+
short-lived broker grant is revoked during cleanup.
|
|
33
|
+
The grant owns one absolute deadline and every accepted socket. Expiry,
|
|
34
|
+
explicit revoke, credential reload, or broker shutdown aborts an incomplete
|
|
35
|
+
upload or upstream request, destroys accepted sockets, and closes the listener;
|
|
36
|
+
authorization is checked again after the bounded request body is acquired.
|
|
37
|
+
|
|
38
|
+
Codex state lives in a unique, random, immutable-label Docker volume for each
|
|
39
|
+
fresh lineage, never a host or common Codex home. An HMAC-authenticated,
|
|
40
|
+
root-private supervisor registry maps returned session IDs to that exact
|
|
41
|
+
volume. Resume revalidates the volume's owner, namespace, task, and lineage
|
|
42
|
+
labels before every mount. A collision, unlabeled or differently labeled
|
|
43
|
+
preexisting volume, sibling,
|
|
44
|
+
missing, expired, or lost state fails before worker creation.
|
|
45
|
+
Only one worker may mount a lineage at a time; the supervisor holds an atomic
|
|
46
|
+
reservation from preflight consumption through worker cleanup. Expired aliases
|
|
47
|
+
are removed by authenticated periodic GC. The registry retains lineage
|
|
48
|
+
ownership until Docker confirms deletion, retries cleanup failures, and
|
|
49
|
+
reconciles exactly labelled orphan volumes on startup while preserving volumes
|
|
50
|
+
referenced by in-process runs. On restart, exactly owned created, running, or
|
|
51
|
+
exited orphan workers and their networks are removed before volume GC retries.
|
|
52
|
+
An fsync'd, HMAC-authenticated run record binds the generated names, pinned
|
|
53
|
+
image digest, entrypoint policy, private network, state volume, and exact
|
|
54
|
+
worktree mount source/options. Reconciliation inspects that full identity and
|
|
55
|
+
network endpoint membership; forgeable labels or any mismatched name, image,
|
|
56
|
+
mount, option, command, or endpoint are never deletion authority.
|
|
57
|
+
|
|
58
|
+
Preflight verifies Docker, the immutable image, broker readiness, the
|
|
59
|
+
allowlisted non-symlink worktree with no nested mounts, stable device/inode ancestry, and mount policy before prompt, Telegram
|
|
60
|
+
token, dotenv, evidence, resume state, executable override, or provider
|
|
61
|
+
credential reads. The ancestry is revalidated immediately before container
|
|
62
|
+
creation and start, and the immutable entrypoint verifies the mounted worktree
|
|
63
|
+
and reviewed nested cwd before Codex. Only `--model` and `--color` are accepted;
|
|
64
|
+
they are transported as a JSON array and passed exactly once without shell
|
|
65
|
+
splitting. All other provider arguments are rejected before launch.
|
|
66
|
+
Authenticated preflight admission has strict endpoint/field size limits,
|
|
67
|
+
global and per-task caps, and an eager timer sweep, so abandoned capabilities
|
|
68
|
+
expire without requiring a matching `/run`.
|
|
69
|
+
Consumed capabilities enter a separate global/per-task active-run admission
|
|
70
|
+
before validation can yield. The lineage reservation is acquired synchronously
|
|
71
|
+
at that boundary, preventing periodic GC from deleting resume state during
|
|
72
|
+
revalidation. Docker and broker operations have bounded deadlines, and each
|
|
73
|
+
worker has a bounded operator-configured lifetime; timeout cleanup revokes the
|
|
74
|
+
grant and removes the exactly labelled worker and network before capacity is
|
|
75
|
+
released.
|
|
76
|
+
The relay creates one absolute launch deadline before preflight. The same
|
|
77
|
+
deadline and cancellation signal span prompt/stdin and file-secret reads,
|
|
78
|
+
evidence and Telegram setup, Docker setup, broker readiness, execution,
|
|
79
|
+
response/log reads, upstream work and exact cleanup. A disconnected caller or
|
|
80
|
+
supervisor shutdown aborts its run. Independent per-step timeouts cannot extend
|
|
81
|
+
the overall budget, and response bodies are stream-limited to 2 MiB.
|
|
82
|
+
|
|
83
|
+
Codex bypasses its nested sandbox only inside this mandatory outer container.
|
|
84
|
+
Its task-private home contains only that lineage's session state; persisted
|
|
85
|
+
config, rules, hooks, plugins, MCP files and instruction files are rejected
|
|
86
|
+
before Codex starts. MCP is forced empty and project instruction loading is disabled.
|
|
87
|
+
Failure to create the outer boundary is fatal.
|
|
88
|
+
|
|
89
|
+
## Deployment and rotation
|
|
90
|
+
|
|
91
|
+
The default `compose.yaml` contains no provider secret, broker, supervisor, or
|
|
92
|
+
Docker socket. Enable writes only with both files and the explicit profile:
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
docker compose -f compose.yaml -f compose.relay-write.yaml --profile relay-write up
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The override fails interpolation unless every required image, control token,
|
|
99
|
+
worktree root, allowlist and credential-file setting is present. Set immutable supervisor, broker, and worker images; a host
|
|
100
|
+
`THREADWIRE_WORKSPACE_ROOT`; JSON `THREADWIRE_ALLOWED_WORKTREE_ROOTS`; separate
|
|
101
|
+
runtime and broker control tokens; `THREADWIRE_ALLOWED_CODEX_MODELS` as a JSON
|
|
102
|
+
server-side model allowlist; a unique `THREADWIRE_STATE_NAMESPACE`; a random
|
|
103
|
+
`THREADWIRE_STATE_AUTH_KEY` of at least 32 characters; and a deployment secret file for the Codex
|
|
104
|
+
credential. The supervisor alone receives `/var/run/docker.sock`. Restrict
|
|
105
|
+
broker egress at the host/firewall to provider endpoints.
|
|
106
|
+
|
|
107
|
+
The supervisor defaults to 16 active runs globally, two per reviewed task, a
|
|
108
|
+
30-second maximum preflight slice, and a one-hour end-to-end launch deadline. Operators may
|
|
109
|
+
lower these finite limits with `THREADWIRE_PREFLIGHT_TIMEOUT_MS`,
|
|
110
|
+
`THREADWIRE_ACTIVE_RUN_CAPACITY`, `THREADWIRE_ACTIVE_TASK_CAPACITY`, and
|
|
111
|
+
`THREADWIRE_WORKER_TIMEOUT_MS`; invalid, zero, or excessive values fail startup.
|
|
112
|
+
|
|
113
|
+
The authenticated registry persists through file fsync, atomic rename, and
|
|
114
|
+
parent-directory fsync. Startup removes only strictly named abandoned temp
|
|
115
|
+
files whose complete contents authenticate under the deployment key; malformed
|
|
116
|
+
or foreign files are left untouched for operator inspection.
|
|
117
|
+
|
|
118
|
+
`THREADWIRE_WORKTREE_VOLUME` is only for nested-Docker/test deployments. Docker
|
|
119
|
+
volume `Subpath` still mounts only the selected task directory. Production uses
|
|
120
|
+
the exact host bind through Docker's structured Mount API with private
|
|
121
|
+
propagation. The supervisor verifies the resulting two-mount inventory before
|
|
122
|
+
start, and the entrypoint independently rejects nested worktree mounts.
|
|
123
|
+
|
|
124
|
+
Replace the broker secret atomically and restart it, or call authenticated
|
|
125
|
+
`/admin/reload`. Reload increments the generation and revokes existing grants.
|
|
126
|
+
Missing, writable, symlinked, empty, or oversized secret files keep it unready.
|
|
127
|
+
|
|
128
|
+
If Docker, broker, pinned image, worktree, security option, or private network
|
|
129
|
+
is unavailable, relay write fails closed. There is no native fallback.
|
|
130
|
+
|
|
131
|
+
## Verification
|
|
132
|
+
|
|
133
|
+
Run `npm run all-checks`, `npm run verify:package`, and
|
|
134
|
+
`npm run test:isolated-runtime-e2e` with broker, runtime, hostile-probe,
|
|
135
|
+
production relay-worker, and fake-upstream image variables documented
|
|
136
|
+
by that script. E2E uses host-volume canaries and fake-upstream authorization
|
|
137
|
+
logs; worker self-report is not the sole assertion.
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
## Native Kimi Code subscription boundary
|
|
141
|
+
|
|
142
|
+
Kimi uses the same proven supervisor mechanics but a distinct protocol and
|
|
143
|
+
credential service. `THREADWIRE_ISOLATED_PROVIDER=kimi` makes the supervisor
|
|
144
|
+
accept only Kimi preflights, select only `THREADWIRE_KIMI_RELAY_WORKER_IMAGE`,
|
|
145
|
+
issue grants only to `THREADWIRE_KIMI_MODEL_BROKER_URL`, and persist
|
|
146
|
+
provider-bound state in the separate Kimi namespace. Codex requests, sessions,
|
|
147
|
+
workers, grants, and volumes cannot be adopted by this service, and the Codex
|
|
148
|
+
broker remains unchanged.
|
|
149
|
+
|
|
150
|
+
The Kimi broker is not a generic credential proxy. It accepts only
|
|
151
|
+
`POST /v1/chat/completions`, validates a closed request schema and one approved
|
|
152
|
+
wire model, substitutes OAuth immediately before the fixed official
|
|
153
|
+
`https://api.kimi.com/coding/v1/chat/completions` request, and streams the
|
|
154
|
+
bounded response. A grant is pending until the inspected worker is ready and is
|
|
155
|
+
bound to provider, task hash, exact resume session (or fresh lineage), run,
|
|
156
|
+
Docker network, approved alias, and wire model. Revocation, timeout, shutdown,
|
|
157
|
+
or cleanup closes its private listener and active sockets.
|
|
158
|
+
|
|
159
|
+
The immutable Kimi worker image pins `@moonshot-ai/kimi-code@0.29.2` and verifies
|
|
160
|
+
the npm archive SHA-512 before installation. It receives no OAuth file, Docker
|
|
161
|
+
socket, Telegram credential, supervisor token, broker admin token, ambient
|
|
162
|
+
provider executable override, external network, or unrelated home directory.
|
|
163
|
+
Its fixed entrypoint owns `--model`, optional exact `--session`, `--prompt`,
|
|
164
|
+
`--output-format stream-json`, and an empty skills directory. Static deny rules,
|
|
165
|
+
workspace/config ancestry checks, a sanitized private home, read-only root,
|
|
166
|
+
and the existing mount/inode checks remain mandatory. There is no native unsafe
|
|
167
|
+
fallback and no `THREADWIRE_KIMI_BIN` control.
|
|
168
|
+
|
|
169
|
+
Only exact pinned stream records are normalized: assistant string content,
|
|
170
|
+
validated tool name/key start and finish, and one exact
|
|
171
|
+
`session.resume_hint`. Thinking, tool arguments/results, retry errors, version
|
|
172
|
+
or arbitrary metadata, malformed records, raw stdout/stderr, synthetic grants,
|
|
173
|
+
and OAuth material are discarded. The worker is the only reader of native Kimi
|
|
174
|
+
stdout and Docker receives only sanitized envelopes and fixed lifecycle events;
|
|
175
|
+
the supervisor returns `rawChunks: []` for every Kimi run.
|
|
176
|
+
|
|
177
|
+
### Operator OAuth and model approval
|
|
178
|
+
|
|
179
|
+
Build and publish the `kimi-auth`, `kimi-model-broker`, `kimi-relay-worker`, and
|
|
180
|
+
existing `isolated-runtime` targets as immutable digests. Set distinct random
|
|
181
|
+
Kimi runtime and broker admin tokens of at least 32 characters, the Kimi image digests, and an allowlist
|
|
182
|
+
such as:
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
export THREADWIRE_ALLOWED_KIMI_MODELS='{"default":{"model":"<verified-account-model>","protocol":"kimi"}}'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
OAuth writes only the named `threadwire-kimi-oauth` volume at the official
|
|
189
|
+
`credentials/kimi-code.json` location. Stop the broker during interactive auth
|
|
190
|
+
so the CLI and broker cannot race a refresh, then run the opt-in, workspace-free
|
|
191
|
+
auth service:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
docker compose -f compose.yaml -f compose.relay-write.yaml stop kimi-isolated-runtime kimi-model-broker
|
|
195
|
+
docker compose -f compose.yaml -f compose.relay-write.yaml --profile kimi-auth run --rm kimi-auth login
|
|
196
|
+
docker compose -f compose.yaml -f compose.relay-write.yaml --profile kimi-auth run --rm kimi-auth verify '<verified-account-model>'
|
|
197
|
+
docker compose -f compose.yaml -f compose.relay-write.yaml --profile kimi up --detach --wait
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Complete the official device flow directly in the operator terminal. Do not
|
|
201
|
+
copy device codes into tickets, chat, logs, environment files, or Threadwire.
|
|
202
|
+
The auth service has no workspace, ingress network, Docker socket, Threadwire
|
|
203
|
+
control token, or Telegram credential and uses Docker logging driver `none`.
|
|
204
|
+
`verify <account-model>` discards native output and prints only a fixed
|
|
205
|
+
success/failure; use it to confirm each exact account model value before mapping
|
|
206
|
+
that value to a server-approved Threadwire alias.
|
|
207
|
+
API keys, Moonshot platform mode, custom base URLs, discovery by ordinary jobs,
|
|
208
|
+
and arbitrary aliases are unsupported.
|
|
209
|
+
|
|
210
|
+
For logout or account rotation, stop the Kimi runtime and broker, run
|
|
211
|
+
`--profile kimi-auth run --rm kimi-auth logout` or `login`, verify approved
|
|
212
|
+
models, then restart the Kimi profile. The broker validates strict owner/mode,
|
|
213
|
+
regular-file, link-count, schema, and size constraints; refreshes are serialized
|
|
214
|
+
and atomically persisted, and corruption, symlinks, revocation, or unreadable
|
|
215
|
+
state makes health and preflight fail closed with redacted errors.
|
|
216
|
+
|
|
217
|
+
### Kimi validation and rollback
|
|
218
|
+
|
|
219
|
+
Run `npm run test:kimi-isolated-runtime-e2e` from an approved Docker supervisor
|
|
220
|
+
boundary after supplying the documented immutable image variables. A real
|
|
221
|
+
account smoke must then cover fresh execution, capture of exactly one native
|
|
222
|
+
session ID, exact resume with that ID, an approved non-default alias if used,
|
|
223
|
+
Telegram DM/topic routing, cancellation, and confirmation that service logs and
|
|
224
|
+
activity/evidence contain no OAuth token, device code, raw Kimi record, tool
|
|
225
|
+
payload, or synthetic grant.
|
|
226
|
+
|
|
227
|
+
Rollback by stopping `kimi-isolated-runtime` and `kimi-model-broker`, removing
|
|
228
|
+
the `kimi` Compose profile/control variables, and reverting the Threadwire image.
|
|
229
|
+
Kimi commands then fail closed; Codex behavior is unaffected. Preserve the OAuth
|
|
230
|
+
and Kimi state volumes for a recoverable rollback. Remove them only as an
|
|
231
|
+
explicit credential/session teardown after OAuth revocation; never archive them
|
|
232
|
+
with workspace backups.
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "threadwire",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Stream Codex, Claude, and OpenCode worker progress to an explicit Telegram destination",
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "Stream Codex, Claude, Kimi Code, and OpenCode worker progress to an explicit Telegram destination",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agent",
|
|
7
7
|
"claude",
|
|
8
8
|
"cli",
|
|
9
9
|
"codex",
|
|
10
|
+
"kimi",
|
|
10
11
|
"opencode",
|
|
11
12
|
"telegram"
|
|
12
13
|
],
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
"private": false,
|
|
15
16
|
"type": "module",
|
|
16
17
|
"files": [
|
|
18
|
+
"CHANGELOG.md",
|
|
17
19
|
"bin/",
|
|
18
20
|
"docs/",
|
|
19
21
|
"scripts/",
|
|
@@ -22,6 +24,9 @@
|
|
|
22
24
|
"threadwire.workspace-profiles.json"
|
|
23
25
|
],
|
|
24
26
|
"bin": {
|
|
27
|
+
"threadwire-isolated-runtime": "bin/isolated-runtime.js",
|
|
28
|
+
"threadwire-kimi-model-broker": "bin/kimi-model-broker.js",
|
|
29
|
+
"threadwire-model-broker": "bin/model-broker.js",
|
|
25
30
|
"threadwire": "bin/threadwire.js",
|
|
26
31
|
"threadwire-telegram-webhook": "bin/telegram-webhook.js"
|
|
27
32
|
},
|
|
@@ -49,8 +54,10 @@
|
|
|
49
54
|
"prepublishOnly": "npm run all-checks && npm run verify:package",
|
|
50
55
|
"test": "node --test \"test/**/*.test.js\"",
|
|
51
56
|
"test:container-e2e": "sh docker/test/production-e2e.sh",
|
|
57
|
+
"test:isolated-runtime-e2e": "sh docker/test/isolated-runtime-e2e.sh",
|
|
52
58
|
"typecheck": "tsc --noEmit",
|
|
53
|
-
"verify:package": "node scripts/verify-package.js"
|
|
59
|
+
"verify:package": "node scripts/verify-package.js",
|
|
60
|
+
"test:kimi-isolated-runtime-e2e": "sh docker/test/kimi-isolated-runtime-e2e.sh"
|
|
54
61
|
},
|
|
55
62
|
"devDependencies": {
|
|
56
63
|
"@eslint/js": "10.0.1",
|
|
@@ -55,6 +55,24 @@ fi
|
|
|
55
55
|
@@WORKER_SUBCOMMAND_SHIFT@@
|
|
56
56
|
|
|
57
57
|
threadwire_relay_arguments=(run --provider "$THREADWIRE_PROVIDER" --target "$THREADWIRE_TARGET")
|
|
58
|
+
case "${THREADWIRE_RELAY_WRITE:-0}" in
|
|
59
|
+
0) ;;
|
|
60
|
+
1)
|
|
61
|
+
if [[ ! "${THREADWIRE_WORKSPACE_PROFILE:-}" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; then
|
|
62
|
+
printf '%s\n' 'threadwire: relay write capability requires a valid THREADWIRE_WORKSPACE_PROFILE' >&2
|
|
63
|
+
exit 2
|
|
64
|
+
fi
|
|
65
|
+
if [[ "$THREADWIRE_PROVIDER" != codex ]]; then
|
|
66
|
+
printf '%s\n' 'threadwire: relay write capability is only supported for codex' >&2
|
|
67
|
+
exit 2
|
|
68
|
+
fi
|
|
69
|
+
threadwire_relay_arguments+=(--workspace-profile "$THREADWIRE_WORKSPACE_PROFILE" --relay-write)
|
|
70
|
+
;;
|
|
71
|
+
*)
|
|
72
|
+
printf '%s\n' 'threadwire: relay write capability must be enabled with THREADWIRE_RELAY_WRITE=1' >&2
|
|
73
|
+
exit 2
|
|
74
|
+
;;
|
|
75
|
+
esac
|
|
58
76
|
if [[ -n "${THREADWIRE_PROCESS_NUMBER:-}" ]]; then
|
|
59
77
|
threadwire_relay_arguments+=(--process-number "$THREADWIRE_PROCESS_NUMBER")
|
|
60
78
|
fi
|
|
@@ -13,16 +13,22 @@ const npmEnvironment = {
|
|
|
13
13
|
NPM_CONFIG_DRY_RUN: "false",
|
|
14
14
|
npm_config_dry_run: "false"
|
|
15
15
|
}
|
|
16
|
+
const packageTmpdir = process.env.THREADWIRE_PACKAGE_TMPDIR ?? tmpdir()
|
|
16
17
|
const EXPECTED_FILES = [
|
|
18
|
+
"CHANGELOG.md",
|
|
17
19
|
"LICENSE",
|
|
18
20
|
"README.md",
|
|
19
21
|
"TELEGRAM-INGRESS.md",
|
|
20
22
|
"bin/telegram-webhook.js",
|
|
21
23
|
"bin/threadwire.js",
|
|
24
|
+
"bin/isolated-runtime.js",
|
|
25
|
+
"bin/kimi-model-broker.js",
|
|
26
|
+
"bin/model-broker.js",
|
|
22
27
|
"docs/card-10520-plan.md",
|
|
23
28
|
"docs/container-runtime.md",
|
|
24
29
|
"docs/delegated-result-protocol.md",
|
|
25
30
|
"docs/evidence-artifacts.md",
|
|
31
|
+
"docs/isolated-provider-runtime.md",
|
|
26
32
|
"package.json",
|
|
27
33
|
"scripts/atomic-install.js",
|
|
28
34
|
"scripts/install-local-launcher.js",
|
|
@@ -32,11 +38,24 @@ const EXPECTED_FILES = [
|
|
|
32
38
|
"scripts/provider-shims/front-door.sh.template",
|
|
33
39
|
"scripts/provider-shims/opencode-local-fleet.adapter.sh",
|
|
34
40
|
"scripts/verify-package.js",
|
|
41
|
+
"src/absolute-deadline.js",
|
|
35
42
|
"src/activity-log.js",
|
|
43
|
+
"src/normalized-output.js",
|
|
36
44
|
"src/cli.js",
|
|
37
45
|
"src/context-budget-metrics.js",
|
|
38
46
|
"src/delegated-result-admission.js",
|
|
47
|
+
"src/docker-api.js",
|
|
39
48
|
"src/evidence-store.js",
|
|
49
|
+
"src/isolated-runtime-client.js",
|
|
50
|
+
"src/isolated-runtime.js",
|
|
51
|
+
"src/isolated-state.js",
|
|
52
|
+
"src/isolated-worker.js",
|
|
53
|
+
"src/kimi-model-broker-policy.js",
|
|
54
|
+
"src/kimi-model-broker.js",
|
|
55
|
+
"src/kimi-oauth-store.js",
|
|
56
|
+
"src/model-broker-policy.js",
|
|
57
|
+
"src/model-broker.js",
|
|
58
|
+
"src/mount-policy.js",
|
|
40
59
|
"src/notice-queue.js",
|
|
41
60
|
"src/notifiers/fetch-transport.js",
|
|
42
61
|
"src/notifiers/telegram.js",
|
|
@@ -44,8 +63,10 @@ const EXPECTED_FILES = [
|
|
|
44
63
|
"src/providers/codex.js",
|
|
45
64
|
"src/providers/executable.js",
|
|
46
65
|
"src/providers/index.js",
|
|
66
|
+
"src/providers/kimi.js",
|
|
47
67
|
"src/providers/opencode.js",
|
|
48
68
|
"src/relay.js",
|
|
69
|
+
"src/relay-write.js",
|
|
49
70
|
"src/run-worker.js",
|
|
50
71
|
"src/sentence-buffer.js",
|
|
51
72
|
"src/telegram-ingress/command.js",
|
|
@@ -63,7 +84,7 @@ const EXPECTED_FILES = [
|
|
|
63
84
|
|
|
64
85
|
/** @returns {Promise<void>} */
|
|
65
86
|
async function main() {
|
|
66
|
-
const temporaryDirectory = await mkdtemp(join(
|
|
87
|
+
const temporaryDirectory = await mkdtemp(join(packageTmpdir, "threadwire-package-"))
|
|
67
88
|
const cacheDirectory = join(temporaryDirectory, "npm-cache")
|
|
68
89
|
|
|
69
90
|
try {
|
|
@@ -118,6 +139,7 @@ async function main() {
|
|
|
118
139
|
], {
|
|
119
140
|
env: {
|
|
120
141
|
PATH: process.env.PATH,
|
|
142
|
+
HOME: process.env.HOME,
|
|
121
143
|
NPM_CONFIG_DRY_RUN: "false",
|
|
122
144
|
npm_config_dry_run: "false"
|
|
123
145
|
},
|