better-dsh 0.3.0 → 0.3.1-a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +49 -177
  2. package/cordis.patch.yml +5 -36
  3. package/docs/20_specs/kernel-provisioning/spec.md +2 -0
  4. package/docs/adr/0015-independent-bundles-agent-remote.md +40 -0
  5. package/docs/remote-tools.md +56 -0
  6. package/docs/specs/kernel-provisioning/spec.md +12 -40
  7. package/docs/specs/omp-python-repl-port/runtime-guide.md +35 -0
  8. package/docs/specs/omp-python-repl-port/spec.md +68 -0
  9. package/docs/specs/remote-tool-enhancement/spec.md +203 -0
  10. package/dsh-docs/baseline.md +13 -0
  11. package/dsh-docs/subsystems/invariants.md +3 -0
  12. package/dsh-docs/subsystems/invariants.zh.md +3 -0
  13. package/eval-description.md +7 -3
  14. package/lib/LaunchOptions-0Mr6meJc.js +4 -0
  15. package/lib/{LaunchOptions-kGXcZOe0.js → LaunchOptions-iE9M-STS.js} +1 -1
  16. package/lib/{bidi-BalpKaJc.js → bidi-CaQeBZHY.js} +1 -1
  17. package/lib/client/index.js +30 -6
  18. package/lib/failover/index.js +1 -1
  19. package/lib/fs-aware/sandbox-plugin.js +3 -3
  20. package/lib/{index-browser-CysrjMyM.js → index-browser-CJzxT9Vi.js} +4 -4
  21. package/lib/index.d.ts +16 -50
  22. package/lib/index.js +482 -1720
  23. package/lib/kernel-env-x_WkG6Fr.js +76 -0
  24. package/lib/kernel-env.d.ts +5 -69
  25. package/lib/kernel-env.js +2 -2
  26. package/lib/{lsp-server-registry-BXvUTBY7.js → lsp-server-registry-BcdkHROD.js} +1 -1
  27. package/lib/{lsp-server-registry-CH9WNFFt.js → lsp-server-registry-D4VLs1Sm.js} +1 -1
  28. package/lib/{main-BQeFrBM1.js → main-taSMz1KX.js} +1 -1
  29. package/lib/{main-CupOhMp5.js → main-uqGqYYnA.js} +7 -5
  30. package/lib/mobile/plugin.d.ts +1 -1
  31. package/lib/omp-python/DILL-LICENSE +35 -0
  32. package/lib/omp-python/OMP-LICENSE +23 -0
  33. package/lib/omp-python/better_snapshot.py +391 -0
  34. package/lib/omp-python/better_support.py +425 -0
  35. package/lib/omp-python/dill-provenance.json +55 -0
  36. package/lib/omp-python/provenance.json +13 -0
  37. package/lib/omp-python/runner.py +2499 -0
  38. package/lib/omp-python/vendor/dill/__diff.py +234 -0
  39. package/lib/omp-python/vendor/dill/__info__.py +291 -0
  40. package/lib/omp-python/vendor/dill/__init__.py +119 -0
  41. package/lib/omp-python/vendor/dill/_dill.py +2265 -0
  42. package/lib/omp-python/vendor/dill/_objects.py +545 -0
  43. package/lib/omp-python/vendor/dill/_shims.py +193 -0
  44. package/lib/omp-python/vendor/dill/detect.py +287 -0
  45. package/lib/omp-python/vendor/dill/logger.py +285 -0
  46. package/lib/omp-python/vendor/dill/objtypes.py +24 -0
  47. package/lib/omp-python/vendor/dill/pointers.py +122 -0
  48. package/lib/omp-python/vendor/dill/session.py +612 -0
  49. package/lib/omp-python/vendor/dill/settings.py +25 -0
  50. package/lib/omp-python/vendor/dill/source.py +1025 -0
  51. package/lib/omp-python/vendor/dill/temp.py +252 -0
  52. package/lib/{puppeteer-core-CSk2PtSm.js → puppeteer-core-C-4NyOpY.js} +8 -8
  53. package/lib/remote/plugin.d.ts +459 -5
  54. package/lib/remote/plugin.js +2389 -210
  55. package/lib/url-schemes/index.d.ts +11 -11
  56. package/lib/url-schemes/index.js +11 -11
  57. package/lib/web-password.d.ts +8 -8
  58. package/lib/web-password.js +5 -1
  59. package/lib/{web-trust-Dej_RI2B.d.ts → web-trust-C_Y6o8Z2.d.ts} +11 -11
  60. package/lib/web-trust.d.ts +1 -2
  61. package/lib/welcome-notice.d.ts +5 -7
  62. package/lib/welcome-notice.js +9 -48
  63. package/lib/{wrap-T4I0nfPM.js → wrap-0VA0uFx8.js} +1 -1
  64. package/package.json +7 -24
  65. package/scripts/kernel-check.mjs +5 -0
  66. package/lib/LaunchOptions-B874eBu2.js +0 -4
  67. package/lib/bundle-delivery.d.ts +0 -20
  68. package/lib/bundle-delivery.js +0 -209
  69. package/lib/hmr.d.ts +0 -32
  70. package/lib/hmr.js +0 -49
  71. package/lib/kernel-bridge.py +0 -172
  72. package/lib/kernel-env-D8UOWeji.js +0 -195
  73. package/lib/webserver.d.ts +0 -10
  74. package/lib/webserver.js +0 -19
  75. package/scripts/kernel-provision.mjs +0 -35
  76. package/scripts/register-bundle.mjs +0 -80
package/lib/hmr.d.ts DELETED
@@ -1,32 +0,0 @@
1
- import Hmr, { HmrConfig } from "@deepseek-ai/dsh-hmr";
2
- import { Context } from "@deepseek-ai/cordis";
3
-
4
- //#region src/hmr.d.ts
5
-
6
- /**
7
- * Native runExclusive keeps a boolean in ALS. HTTP servers created while
8
- * replacing webServer inherit that boolean after the transaction finishes,
9
- * so subsequent native Plugin Manager requests are refused as nested.
10
- *
11
- * The public runExclusive/watchConfig APIs expose no selective context reset.
12
- * AsyncLocalStorage.snapshot() also resets AgentRegistry initiator attribution
13
- * and its teardown lineage, so it cannot safely repair this host transaction.
14
- * This compatibility bridge reads the native TS-private `executing` storage
15
- * after checking its runtime shape. It never replaces that storage or changes
16
- * upstream files: only exit() clears its inherited value while the unchanged
17
- * native method establishes the next transaction. Native queue, closing,
18
- * exact watchers, module reload and disposal retain their original semantics.
19
- *
20
- * The mutable token rejects nesting only while its operation is still active;
21
- * resources created by that operation can initiate later transactions.
22
- * A native implementation change fails initialization explicitly instead of
23
- * silently dropping queue or lifecycle guarantees.
24
- */
25
- declare class BetterHmr extends Hmr {
26
- private readonly nativeTransactions;
27
- private readonly liveTransactions;
28
- constructor(ctx: Context, config: HmrConfig);
29
- runExclusive<T>(operation: () => Promise<T>): Promise<T>;
30
- }
31
- //#endregion
32
- export { BetterHmr as default };
package/lib/hmr.js DELETED
@@ -1,49 +0,0 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
- import Hmr from "@deepseek-ai/dsh-hmr";
3
-
4
- //#region src/hmr.ts
5
- /**
6
- * Native runExclusive keeps a boolean in ALS. HTTP servers created while
7
- * replacing webServer inherit that boolean after the transaction finishes,
8
- * so subsequent native Plugin Manager requests are refused as nested.
9
- *
10
- * The public runExclusive/watchConfig APIs expose no selective context reset.
11
- * AsyncLocalStorage.snapshot() also resets AgentRegistry initiator attribution
12
- * and its teardown lineage, so it cannot safely repair this host transaction.
13
- * This compatibility bridge reads the native TS-private `executing` storage
14
- * after checking its runtime shape. It never replaces that storage or changes
15
- * upstream files: only exit() clears its inherited value while the unchanged
16
- * native method establishes the next transaction. Native queue, closing,
17
- * exact watchers, module reload and disposal retain their original semantics.
18
- *
19
- * The mutable token rejects nesting only while its operation is still active;
20
- * resources created by that operation can initiate later transactions.
21
- * A native implementation change fails initialization explicitly instead of
22
- * silently dropping queue or lifecycle guarantees.
23
- */
24
- var BetterHmr = class extends Hmr {
25
- nativeTransactions;
26
- liveTransactions = new AsyncLocalStorage();
27
- constructor(ctx, config) {
28
- super(ctx, config);
29
- const storage = this.executing;
30
- if (!(storage instanceof AsyncLocalStorage) || typeof storage.getStore !== "function" || typeof storage.exit !== "function" || typeof storage.run !== "function") throw new Error("Better Dash HMR cannot reset this native HMR transaction context: expected executing AsyncLocalStorage");
31
- this.nativeTransactions = storage;
32
- }
33
- runExclusive(operation) {
34
- if (this.liveTransactions.getStore()?.active) return Promise.reject(/* @__PURE__ */ new Error("HMR transactions cannot be nested"));
35
- return this.nativeTransactions.exit(() => super.runExclusive(() => {
36
- const token = { active: true };
37
- return this.liveTransactions.run(token, async () => {
38
- try {
39
- return await operation();
40
- } finally {
41
- token.active = false;
42
- }
43
- });
44
- }));
45
- }
46
- };
47
-
48
- //#endregion
49
- export { BetterHmr as default };
@@ -1,172 +0,0 @@
1
- #!/usr/bin/env python3
2
- """stdio <-> ZeroMQ bridge for the DASHR REPL.
3
-
4
- The host (Node/TS) owns the kernel lifecycle but must not link a native ZMQ
5
- binding: `zeromq` was a hard npm dependency whose install script compiles C++
6
- inside the consumer's install transaction (and whose top-level import killed
7
- the whole plugin when the binding was missing). This process is the only ZMQ
8
- speaker on the host side; it speaks JSON lines on stdio to Node.
9
-
10
- Node ──stdio JSON lines──► kernel-bridge.py ──pyzmq/jupyter_client──► ipykernel
11
-
12
- Requirements come from the kernel's own environment: `jupyter_client` and
13
- `pyzmq` are transitive dependencies of the `ipykernel` we provision (see
14
- `kernel-env.ts`), so nothing extra is fetched from any package manager.
15
-
16
- Protocol
17
- --------
18
- stdin (one JSON object per line)::
19
-
20
- {"op":"send","channel":"shell"|"control","header":{...},"content":{...}}
21
- {"op":"interrupt"}
22
- {"op":"shutdown"}
23
-
24
- stdout (one JSON object per line)::
25
-
26
- {"ev":"ready"}
27
- {"ev":"msg","channel":"shell"|"iopub"|"control"|"stdin","msg":{...}}
28
- {"ev":"fatal","message":"..."}
29
-
30
- The caller-supplied `header` is passed to `Session.msg` verbatim, so the
31
- message id the host generated is the id on the wire — parent-matching and
32
- request/response correlation stay exactly as they were on the raw-socket path.
33
- """
34
- from __future__ import annotations
35
-
36
- import json
37
- import queue
38
- import sys
39
- import threading
40
-
41
- CHANNELS = ("iopub", "shell", "control", "stdin")
42
- POLL_SECONDS = 0.2
43
-
44
- # Protocol output goes to the real stdout; anything a library prints must never
45
- # corrupt the byte stream the host parses, so sys.stdout is repointed to stderr.
46
- _PROTOCOL_OUT = sys.stdout
47
- sys.stdout = sys.stderr
48
-
49
- _write_lock = threading.Lock()
50
-
51
-
52
- def emit(payload: dict) -> None:
53
- """Write one protocol line, atomically across the pump threads."""
54
- # `default=str`: jupyter_client hands back parsed values (a header `date`
55
- # arrives as `datetime`, for instance) that are not JSON-native; the host
56
- # only reads msg_id/msg_type/content, so stringifying the rest is lossless
57
- # for its purposes and keeps one pump thread from dying on a header field.
58
- line = json.dumps(payload, ensure_ascii=False, default=str)
59
- with _write_lock:
60
- _PROTOCOL_OUT.write(line + "\n")
61
- _PROTOCOL_OUT.flush()
62
-
63
-
64
- def _clean(value: object) -> object:
65
- """Strip binary buffers: this protocol carries JSON only."""
66
- if isinstance(value, dict):
67
- return {key: _clean(item) for key, item in value.items() if key != "buffers"}
68
- if isinstance(value, (list, tuple)):
69
- return [_clean(item) for item in value]
70
- if isinstance(value, bytes):
71
- return value.decode("utf-8", "replace")
72
- return value
73
-
74
-
75
- def pump(client, name: str, stop: threading.Event) -> None:
76
- """Relay one client channel onto stdout until `stop` is set."""
77
- channel = getattr(client, f"{name}_channel")
78
- while not stop.is_set():
79
- try:
80
- message = channel.get_msg(timeout=POLL_SECONDS)
81
- except queue.Empty:
82
- continue
83
- except Exception as error: # channel torn down, or the socket died
84
- if not stop.is_set():
85
- emit({"ev": "fatal", "channel": name, "message": f"{type(error).__name__}: {error}"})
86
- try:
87
- emit({
88
- "ev": "msg",
89
- "channel": name,
90
- "msg": {
91
- "header": message.get("header") or {},
92
- "parent_header": message.get("parent_header") or {},
93
- "content": _clean(message.get("content") or {}),
94
- "metadata": _clean(message.get("metadata") or {}),
95
- },
96
- })
97
- except Exception as error: # never let one message kill a channel pump
98
- sys.stderr.write(f"kernel-bridge: cannot relay {name} message: {type(error).__name__}: {error}\n")
99
- sys.stderr.flush()
100
-
101
-
102
- def main() -> int:
103
- if len(sys.argv) != 2:
104
- emit({"ev": "fatal", "message": "usage: kernel-bridge.py <connection-file>"})
105
- return 2
106
-
107
- try:
108
- from jupyter_client import BlockingKernelClient
109
- except Exception as error: # the kernel environment is incomplete
110
- emit({"ev": "fatal", "message": f"jupyter_client unavailable: {type(error).__name__}: {error}"})
111
- return 3
112
-
113
- try:
114
- client = BlockingKernelClient(connection_file=sys.argv[1])
115
- client.load_connection_file()
116
- client.start_channels()
117
- except Exception as error:
118
- emit({"ev": "fatal", "message": f"cannot connect to the kernel: {type(error).__name__}: {error}"})
119
- return 4
120
-
121
- stop = threading.Event()
122
- for name in CHANNELS:
123
- threading.Thread(target=pump, args=(client, name, stop), name=f"pump-{name}", daemon=True).start()
124
-
125
- emit({"ev": "ready"})
126
-
127
- try:
128
- for line in sys.stdin:
129
- line = line.strip()
130
- if not line:
131
- continue
132
- try:
133
- request = json.loads(line)
134
- except Exception:
135
- continue
136
- op = request.get("op")
137
- if op == "send":
138
- channel_name = request.get("channel")
139
- if channel_name not in ("shell", "control"):
140
- emit({"ev": "fatal", "message": f"unknown send channel {channel_name!r}"})
141
- continue
142
- channel = getattr(client, f"{channel_name}_channel")
143
- try:
144
- header = request.get("header") or None
145
- message = client.session.msg(
146
- header["msg_type"] if header else "comm_msg",
147
- content=request.get("content") or {},
148
- header=header,
149
- )
150
- channel.send(message)
151
- except Exception as error:
152
- emit({"ev": "fatal", "channel": channel_name, "message": f"send failed: {type(error).__name__}: {error}"})
153
- elif op == "interrupt":
154
- try:
155
- client.interrupt_kernel()
156
- except Exception as error:
157
- emit({"ev": "fatal", "message": f"interrupt failed: {type(error).__name__}: {error}"})
158
- elif op == "shutdown":
159
- break
160
- except (KeyboardInterrupt, BrokenPipeError):
161
- pass
162
- finally:
163
- stop.set()
164
- try:
165
- client.stop_channels()
166
- except Exception:
167
- pass
168
- return 0
169
-
170
-
171
- if __name__ == "__main__":
172
- sys.exit(main())
@@ -1,195 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { dirname, join, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { existsSync, mkdirSync } from "node:fs";
5
- import { promisify } from "node:util";
6
-
7
- //#region src/kernel-env.ts
8
- const execFileAsync = promisify(execFile);
9
- /** The installed package root (where `lib/` and `src/` live): the default home of the managed venv. */
10
- const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
11
- /** The config value meaning "no explicit interpreter — auto-manage a venv". */
12
- const AUTO_PYTHON_SENTINEL = "python3";
13
- /** Preferred CPython version for a managed venv. */
14
- const DEFAULT_KERNEL_PYTHON_VERSION = "3.11";
15
- /** Pinned, tested `ipykernel` version (2026-09-02 tested value; upgrade = explicit change + regression). */
16
- const IPYKERNEL_VERSION = "7.3.0";
17
- /** Pinned, tested `dill` version. */
18
- const DILL_VERSION = "0.4.1";
19
- /** A probe that never imports user modules and reports exactly what we need. */
20
- const PROBE_CODE = [
21
- "import json, sys",
22
- "r = {\"version\": sys.version.split()[0], \"ipykernel\": False, \"dill\": False}",
23
- "try:",
24
- " import ipykernel # noqa: F401",
25
- " r[\"ipykernel\"] = True",
26
- "except Exception:",
27
- " pass",
28
- "try:",
29
- " import dill # noqa: F401",
30
- " r[\"dill\"] = True",
31
- "except Exception:",
32
- " pass",
33
- "print(json.dumps(r))"
34
- ].join("\n");
35
- async function probePython(python) {
36
- try {
37
- const { stdout } = await execFileAsync(python, ["-c", PROBE_CODE], { timeout: 15e3 });
38
- const parsed = JSON.parse(stdout.trim());
39
- return {
40
- version: typeof parsed.version === "string" && parsed.version.length > 0 ? parsed.version : "unknown",
41
- ipykernel: parsed.ipykernel === true,
42
- dill: parsed.dill === true
43
- };
44
- } catch {
45
- return null;
46
- }
47
- }
48
- /** The venv interpreter path for this platform. */
49
- function venvPythonPath(venvDir) {
50
- return process.platform === "win32" ? join(venvDir, "Scripts", "python.exe") : join(venvDir, "bin", "python");
51
- }
52
- async function commandExists(cmd) {
53
- try {
54
- await execFileAsync(cmd, ["--version"], { timeout: 1e4 });
55
- return true;
56
- } catch {
57
- return false;
58
- }
59
- }
60
- /** Tool-cache env for uv: caches live NEXT TO the venv (plugin territory), so a read-only home can never block provisioning. */
61
- function uvToolEnv(venvDir) {
62
- const cacheDir = join(dirname(venvDir), ".uv-cache");
63
- return {
64
- ...process.env,
65
- UV_CACHE_DIR: cacheDir,
66
- UV_PYTHON_INSTALL_DIR: join(cacheDir, "python"),
67
- UV_LINK_MODE: "copy"
68
- };
69
- }
70
- /** uv venv arguments (pure; unit-tested). */
71
- function uvVenvArgs(venvDir, pythonVersion) {
72
- return [
73
- "venv",
74
- venvDir,
75
- "--python",
76
- pythonVersion
77
- ];
78
- }
79
- /** python3 -m venv fallback arguments (pure; cache-free by construction). */
80
- function venvFallbackArgs(venvDir) {
81
- return [
82
- "-m",
83
- "venv",
84
- venvDir
85
- ];
86
- }
87
- /** uv pip install arguments with pinned versions (pure; unit-tested). */
88
- function uvInstallArgs(python) {
89
- return [
90
- "pip",
91
- "install",
92
- "--python",
93
- python,
94
- "--quiet",
95
- `ipykernel==${IPYKERNEL_VERSION}`,
96
- `dill==${DILL_VERSION}`
97
- ];
98
- }
99
- /** pip install arguments with pinned versions for the venv interpreter (pure). */
100
- function pipInstallArgs() {
101
- return [
102
- "-m",
103
- "pip",
104
- "install",
105
- "--quiet",
106
- "--disable-pip-version-check",
107
- `ipykernel==${IPYKERNEL_VERSION}`,
108
- `dill==${DILL_VERSION}`
109
- ];
110
- }
111
- /** Create the venv (when absent) and return its interpreter path. */
112
- async function createVenv(venvDir, pythonVersion) {
113
- const python = venvPythonPath(venvDir);
114
- if (existsSync(python)) return python;
115
- if (await commandExists("uv")) {
116
- mkdirSync(join(dirname(venvDir), ".uv-cache"), { recursive: true });
117
- await execFileAsync("uv", uvVenvArgs(venvDir, pythonVersion), {
118
- timeout: 12e4,
119
- env: uvToolEnv(venvDir)
120
- });
121
- } else await execFileAsync("python3", venvFallbackArgs(venvDir), { timeout: 12e4 });
122
- return python;
123
- }
124
- /** Install pinned `ipykernel` + `dill` into the venv interpreter. */
125
- async function installDeps(python, venvDir) {
126
- if (await commandExists("uv")) await execFileAsync("uv", uvInstallArgs(python), {
127
- timeout: 18e4,
128
- env: uvToolEnv(venvDir)
129
- });
130
- else await execFileAsync(python, pipInstallArgs(), { timeout: 18e4 });
131
- }
132
- /** Provision (or repair) the managed venv so it has `ipykernel` + `dill`. */
133
- async function ensureVenv(venvDir, pythonVersion, log) {
134
- const python = venvPythonPath(venvDir);
135
- const existing = existsSync(python) ? await probePython(python) : null;
136
- if (existing?.ipykernel === true && existing.dill === true) return python;
137
- await createVenv(venvDir, pythonVersion);
138
- if (!existsSync(python)) throw new Error(`dashr-repl: managed kernel venv was not created at ${venvDir} (no interpreter at ${python})`);
139
- log?.("info", `dashr-repl: provisioning kernel venv at ${venvDir} (ipykernel + dill)`);
140
- await installDeps(python, venvDir);
141
- return python;
142
- }
143
- /**
144
- * Resolve the kernel interpreter, provisioning a managed venv when no
145
- * explicit interpreter is configured. Throws when the resolved interpreter
146
- * cannot run `ipykernel`; a missing `dill` only disables snapshots (warned).
147
- */
148
- async function resolveKernelEnv(options = {}) {
149
- const log = options.log;
150
- const explicit = options.python !== void 0 && options.python !== "" && options.python !== AUTO_PYTHON_SENTINEL ? options.python : void 0;
151
- if (explicit !== void 0) {
152
- const probe$1 = await probePython(explicit);
153
- if (probe$1 === null) throw new Error(`dashr-repl: kernel python ${JSON.stringify(explicit)} could not be executed — check the path`);
154
- if (!probe$1.ipykernel) throw new Error(`dashr-repl: kernel python ${JSON.stringify(explicit)} has no ipykernel — install it (pip install ipykernel) or set python to a managed venv`);
155
- if (!probe$1.dill) log?.("warn", `dashr-repl: kernel python ${JSON.stringify(explicit)} has no dill — namespace snapshots are DISABLED (install it: pip install dill)`);
156
- return {
157
- python: explicit,
158
- managed: false,
159
- venvDir: void 0,
160
- dill: probe$1.dill,
161
- ipykernel: true,
162
- version: probe$1.version
163
- };
164
- }
165
- const venvDir = options.venvDir ?? join(PACKAGE_ROOT, ".venv-kernel");
166
- const python = venvPythonPath(venvDir);
167
- if (options.autoInstall === false) {
168
- const probe$1 = await probePython(python);
169
- if (probe$1 === null || !probe$1.ipykernel) throw new Error(`dashr-repl: managed kernel venv is missing or incomplete at ${venvDir} — run 'npm run kernel:venv' in the dashr package, or set kernelAutoInstall: true (or python to a prepared interpreter)`);
170
- if (!probe$1.dill) log?.("warn", `dashr-repl: kernel venv at ${venvDir} has no dill — namespace snapshots are DISABLED (run 'npm run kernel:venv' to reinstall)`);
171
- return {
172
- python,
173
- managed: true,
174
- venvDir,
175
- dill: probe$1.dill,
176
- ipykernel: true,
177
- version: probe$1.version
178
- };
179
- }
180
- const resolved = await ensureVenv(venvDir, options.pythonVersion ?? DEFAULT_KERNEL_PYTHON_VERSION, log);
181
- const probe = await probePython(resolved);
182
- if (probe === null || !probe.ipykernel) throw new Error(`dashr-repl: managed kernel venv at ${venvDir} still has no ipykernel after provisioning — check uv/python3-venv availability and retry`);
183
- if (!probe.dill) log?.("warn", `dashr-repl: managed kernel venv at ${venvDir} has no dill after provisioning — namespace snapshots are DISABLED`);
184
- return {
185
- python: resolved,
186
- managed: true,
187
- venvDir,
188
- dill: probe.dill,
189
- ipykernel: true,
190
- version: probe.version
191
- };
192
- }
193
-
194
- //#endregion
195
- export { PACKAGE_ROOT as a, uvInstallArgs as c, venvFallbackArgs as d, IPYKERNEL_VERSION as i, uvToolEnv as l, DEFAULT_KERNEL_PYTHON_VERSION as n, pipInstallArgs as o, DILL_VERSION as r, resolveKernelEnv as s, AUTO_PYTHON_SENTINEL as t, uvVenvArgs as u };
@@ -1,10 +0,0 @@
1
- import { Service } from "@deepseek-ai/cordis";
2
- import { WebServer } from "@deepseek-ai/dsh-host-webserver";
3
-
4
- //#region src/webserver.d.ts
5
- declare class BetterWebServer extends WebServer {
6
- static inject: string[];
7
- [Service.init](): Promise<void>;
8
- }
9
- //#endregion
10
- export { BetterWebServer as default };
package/lib/webserver.js DELETED
@@ -1,19 +0,0 @@
1
- import { Service } from "@deepseek-ai/cordis";
2
- import { WebServer } from "@deepseek-ai/dsh-host-webserver";
3
-
4
- //#region src/webserver.ts
5
- var BetterWebServer = class extends WebServer {
6
- static inject = ["loader"];
7
- async [Service.init]() {
8
- const welcome = [...this.ctx.loader.entries()].find((entry) => entry.options.id === "dashr-welcome-notice");
9
- if (welcome !== void 0 && !welcome.disabled) {
10
- if (welcome.fiber === void 0) await welcome.init();
11
- await welcome.fiber?.await();
12
- }
13
- this.ctx.fiber.assertActive();
14
- await super[Service.init]();
15
- }
16
- };
17
-
18
- //#endregion
19
- export { BetterWebServer as default };
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Best-effort kernel provisioning entry — the single code path behind BOTH
4
- * `postinstall` and `npm run kernel:venv`.
5
- *
6
- * Fail-open by design (2026-09-03 ruling): any failure logs a remedy hint and
7
- * exits 0. The installation must NEVER fail because the kernel could not be
8
- * provisioned here — the daemon spin-up check (primary path) and first-use
9
- * lazy provisioning (final fallback) complete the three-trigger ladder.
10
- */
11
- import { access } from 'node:fs/promises'
12
-
13
- const libUrl = new URL('../lib/kernel-env.js', import.meta.url)
14
-
15
- try {
16
- await access(libUrl)
17
- } catch {
18
- // Dev checkout before the first build, or a pruned install: nothing to call.
19
- console.log('[dashr] kernel-env build output not present yet — skipping install-time pre-provision (the daemon spin-up check will provision on start)')
20
- process.exit(0)
21
- }
22
-
23
- const { resolveKernelEnv } = await import(libUrl)
24
-
25
- try {
26
- const env = await resolveKernelEnv({
27
- autoInstall: true,
28
- log: (level, message) => console[level === 'warn' ? 'warn' : 'log'](`[dashr] ${message}`),
29
- })
30
- console.log(`[dashr] kernel ready: python ${env.version} at ${env.python}${env.dill ? '' : ' (dill missing — snapshots disabled)'}`)
31
- } catch (error) {
32
- console.warn(`[dashr] kernel pre-provision failed (non-blocking): ${error instanceof Error ? error.message : String(error)}`)
33
- console.warn('[dashr] remedies: run "npm run kernel:venv" in the dashr package; or rely on the daemon spin-up check / first-use auto-install; or set python to a prepared interpreter')
34
- process.exit(0)
35
- }
@@ -1,80 +0,0 @@
1
- // Separate-process entry for the SAME native package operations used by dsh plugin.
2
- // No shell interpolation, package scripts, upstream edits, or companion registry lookup.
3
- import { createRequire } from 'node:module'
4
- import { createHash } from 'node:crypto'
5
- import { readFile, realpath } from 'node:fs/promises'
6
- import { dirname, join, relative, resolve, isAbsolute, sep } from 'node:path'
7
- import { pathToFileURL } from 'node:url'
8
- let input = ''
9
- for await (const bytes of process.stdin) input += bytes
10
- const {profile,archive,expected} = JSON.parse(input)
11
- const require = createRequire(profile.installAnchor)
12
- const {runProfilePnpm} = await import(pathToFileURL(require.resolve('@deepseek-ai/dsh-plugin-manager/operations')).href)
13
- const {withFileLock} = await import(pathToFileURL(require.resolve('@deepseek-ai/dsh-atomic-write')).href)
14
- const abort = new AbortController()
15
- process.once('SIGTERM', () => abort.abort())
16
- const json = async path => JSON.parse(await readFile(path, 'utf8'))
17
- const hash = bytes => createHash('sha256').update(bytes).digest('hex')
18
- async function contained(root, path) {
19
- if (typeof path !== 'string' || isAbsolute(path)) throw new Error('Pack delivery path must be relative')
20
- const base = await realpath(root), file = await realpath(resolve(base, path)), rel = relative(base, file)
21
- if (rel === '..' || rel.startsWith('..' + sep) || isAbsolute(rel)) throw new Error('Pack delivery path escapes its owner')
22
- return file
23
- }
24
- const skipped = disposition => ({ exitCode: 0, disposition })
25
- // Complete each native acquisition/release before observing cancellation;
26
- // exiting during its async lock-file write can strand incomplete lock metadata.
27
- async function withCancellableProfileLock(operation) {
28
- const path = join(profile.dir, 'package.json'), deadline = Date.now() + 120000
29
- for (;;) {
30
- abort.signal.throwIfAborted()
31
- let acquired = false
32
- try {
33
- return await withFileLock(path, async () => {
34
- acquired = true
35
- abort.signal.throwIfAborted()
36
- return operation()
37
- }, { waitMs: Math.min(250, Math.max(0, deadline - Date.now())) })
38
- } catch (error) {
39
- abort.signal.throwIfAborted()
40
- if (acquired || Date.now() >= deadline
41
- || error.message !== `atomic-write: timed out waiting for the writer lock at ${path}.lock`) throw error
42
- }
43
- }
44
- }
45
- const result = await withCancellableProfileLock(async () => {
46
- abort.signal.throwIfAborted()
47
- const current = await json(join(profile.dir, 'package.json'))
48
- if (typeof expected.ownerSpec !== 'string' || current.dependencies?.[expected.owner] !== expected.ownerSpec) return skipped('owner-changed')
49
- const ownerDir = join(profile.dir, 'node_modules', expected.owner)
50
- let owner, descriptorPath, descriptor
51
- try {
52
- owner = await json(join(ownerDir, 'package.json'))
53
- if (owner.dependencies?.[expected.companion.name] !== expected.companion.version || owner.superd?.delivery === undefined) return skipped('owner-changed')
54
- descriptorPath = await contained(ownerDir, owner.superd.delivery)
55
- descriptor = await json(descriptorPath)
56
- } catch (error) {
57
- if (error.code === 'ENOENT') return skipped('owner-changed')
58
- throw error
59
- }
60
- const item = descriptor.companions?.find(item => item.name === expected.companion.name)
61
- if (item?.version !== expected.companion.version || item.sha256 !== expected.companion.sha256
62
- || hash(await readFile(await contained(dirname(descriptorPath), item.tarball))) !== expected.companion.sha256) return skipped('owner-changed')
63
- const prior = current.dependencies?.[item.name]
64
- if (prior !== expected.priorSpec) return skipped(prior === undefined && expected.priorSpec !== undefined ? 'companion-removed' : 'companion-changed')
65
- if (hash(await readFile(archive)) !== item.sha256) throw new Error('Cached companion SHA256 mismatch')
66
- abort.signal.throwIfAborted()
67
- // Same public operation and writer lock used by the native Plugin Manager;
68
- // intent is checked after acquisition, before a queued install can undo removal.
69
- const operation = await runProfilePnpm({profile:profile.name,dir:profile.dir,installAnchor:profile.installAnchor,cwd:profile.dir,home:profile.home}, ['add','file:'+archive,'--ignore-scripts'], {
70
- ...profile.packageManager, execution:'service', signal:abort.signal, outputBytes:16384,
71
- lookupTimeoutMs:120000, idleTimeoutMs:120000,
72
- })
73
- if (operation.exitCode === 0) {
74
- const installed = await json(join(profile.dir, 'node_modules', item.name, 'package.json'))
75
- const after = await json(join(profile.dir, 'package.json'))
76
- if (installed.name !== item.name || installed.version !== item.version || after.dependencies?.[item.name] !== 'file:' + archive) throw new Error('Native companion registration did not install the exact local artifact')
77
- }
78
- return { exitCode: operation.exitCode, logPath: operation.logPath, disposition: 'installed' }
79
- })
80
- process.stdout.write(JSON.stringify(result)+'\n')