nebula-notebook 0.1.1 → 0.2.1

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 (142) hide show
  1. package/README.md +110 -16
  2. package/dist/assets/errorwidget-Wy5ajPiM.js +5 -0
  3. package/dist/assets/fa-brands-400-CEJbCg16.woff +0 -0
  4. package/dist/assets/fa-brands-400-CSYNqBb_.ttf +0 -0
  5. package/dist/assets/fa-brands-400-DnkPfk3o.eot +0 -0
  6. package/dist/assets/fa-brands-400-UxlILjvJ.woff2 +0 -0
  7. package/dist/assets/fa-brands-400-cH1MgKbP.svg +3717 -0
  8. package/dist/assets/fa-regular-400-BhTwtT8w.eot +0 -0
  9. package/dist/assets/fa-regular-400-D1vz6WBx.ttf +0 -0
  10. package/dist/assets/fa-regular-400-DFnMcJPd.woff +0 -0
  11. package/dist/assets/fa-regular-400-DGzu1beS.woff2 +0 -0
  12. package/dist/assets/fa-regular-400-gwj8Pxq-.svg +801 -0
  13. package/dist/assets/fa-solid-900-B4ZZ7kfP.svg +5034 -0
  14. package/dist/assets/fa-solid-900-B6Axprfb.eot +0 -0
  15. package/dist/assets/fa-solid-900-BUswJgRo.woff2 +0 -0
  16. package/dist/assets/fa-solid-900-DOXgCApm.woff +0 -0
  17. package/dist/assets/fa-solid-900-mxuxnBEa.ttf +0 -0
  18. package/dist/assets/index-B_Bs5koo.js +1 -0
  19. package/dist/assets/index-BtWv4MIT.css +7 -0
  20. package/dist/assets/index-DK4nS74r.js +829 -0
  21. package/dist/assets/index-DeQN03nW.js +81 -0
  22. package/dist/assets/index-DzjpmUak.css +32 -0
  23. package/dist/assets/index-g6MjT-VG.js +1 -0
  24. package/dist/assets/services-shim-BirHVsih.js +33 -0
  25. package/dist/assets/viewlist-uomDf7I7.js +1 -0
  26. package/dist/assets/widgets-X7J3NxEn.css +1 -0
  27. package/dist/index.html +2 -2
  28. package/node-server/dist/auth/auth-middleware.js +22 -4
  29. package/node-server/dist/auth/auth-service.js +37 -7
  30. package/node-server/dist/cluster/client-registration.d.ts +12 -1
  31. package/node-server/dist/cluster/client-registration.js +38 -2
  32. package/node-server/dist/cluster/kernel-proxy.js +43 -12
  33. package/node-server/dist/cluster/server-registry.d.ts +17 -0
  34. package/node-server/dist/cluster/server-registry.js +57 -7
  35. package/node-server/dist/discovery/conda-locations.d.ts +73 -0
  36. package/node-server/dist/discovery/conda-locations.js +427 -0
  37. package/node-server/dist/discovery/discovery-service.d.ts +73 -4
  38. package/node-server/dist/discovery/discovery-service.js +211 -127
  39. package/node-server/dist/discovery/types.d.ts +0 -1
  40. package/node-server/dist/discovery/types.js +1 -2
  41. package/node-server/dist/environment.d.ts +12 -0
  42. package/node-server/dist/environment.js +104 -0
  43. package/node-server/dist/fs/fs-service.d.ts +86 -8
  44. package/node-server/dist/fs/fs-service.js +640 -99
  45. package/node-server/dist/fs/notebook-formats/percent.d.ts +25 -0
  46. package/node-server/dist/fs/notebook-formats/percent.js +286 -0
  47. package/node-server/dist/fs/notebook-formats/qmd.d.ts +29 -0
  48. package/node-server/dist/fs/notebook-formats/qmd.js +307 -0
  49. package/node-server/dist/fs/notebook-formats/registry.d.ts +12 -0
  50. package/node-server/dist/fs/notebook-formats/registry.js +77 -0
  51. package/node-server/dist/fs/notebook-formats/types.d.ts +37 -0
  52. package/node-server/dist/fs/notebook-formats/types.js +13 -0
  53. package/node-server/dist/fs/types.d.ts +9 -0
  54. package/node-server/dist/fs/types.js +8 -0
  55. package/node-server/dist/idle-exit.d.ts +52 -0
  56. package/node-server/dist/idle-exit.js +83 -0
  57. package/node-server/dist/index.js +198 -8
  58. package/node-server/dist/kernel/kernel-service.d.ts +130 -2
  59. package/node-server/dist/kernel/kernel-service.js +834 -64
  60. package/node-server/dist/kernel/kernelspec.d.ts +27 -0
  61. package/node-server/dist/kernel/kernelspec.js +75 -4
  62. package/node-server/dist/notebook/headless-handler.d.ts +12 -0
  63. package/node-server/dist/notebook/headless-handler.js +125 -29
  64. package/node-server/dist/notebook/operation-router.js +7 -2
  65. package/node-server/dist/notebook/undoRedoManager.d.ts +12 -1
  66. package/node-server/dist/notebook/undoRedoManager.js +49 -18
  67. package/node-server/dist/output/display-data.js +2 -0
  68. package/node-server/dist/private-tmp.d.ts +14 -0
  69. package/node-server/dist/private-tmp.js +70 -0
  70. package/node-server/dist/routes/autocomplete.d.ts +19 -0
  71. package/node-server/dist/routes/autocomplete.js +294 -0
  72. package/node-server/dist/routes/cluster.js +2 -2
  73. package/node-server/dist/routes/compute.d.ts +8 -0
  74. package/node-server/dist/routes/compute.js +136 -0
  75. package/node-server/dist/routes/fs.js +23 -6
  76. package/node-server/dist/routes/kernel.js +156 -3
  77. package/node-server/dist/routes/notebook.js +37 -3
  78. package/node-server/dist/routes/python.d.ts +1 -2
  79. package/node-server/dist/routes/python.js +139 -3
  80. package/node-server/dist/scheduler/allocation-service.d.ts +63 -0
  81. package/node-server/dist/scheduler/allocation-service.js +285 -0
  82. package/node-server/dist/scheduler/job-template.d.ts +30 -0
  83. package/node-server/dist/scheduler/job-template.js +85 -0
  84. package/node-server/dist/scheduler/mock-scheduler.d.ts +30 -0
  85. package/node-server/dist/scheduler/mock-scheduler.js +125 -0
  86. package/node-server/dist/scheduler/slurm-scheduler.d.ts +31 -0
  87. package/node-server/dist/scheduler/slurm-scheduler.js +406 -0
  88. package/node-server/dist/scheduler/types.d.ts +118 -0
  89. package/node-server/dist/scheduler/types.js +8 -0
  90. package/node-server/dist/scheduler/util.d.ts +7 -0
  91. package/node-server/dist/scheduler/util.js +20 -0
  92. package/node-server/dist/terminal/agent-registry.d.ts +87 -0
  93. package/node-server/dist/terminal/agent-registry.js +246 -0
  94. package/node-server/dist/terminal/binding-store.d.ts +57 -0
  95. package/node-server/dist/terminal/binding-store.js +0 -0
  96. package/node-server/dist/terminal/pty-manager.d.ts +23 -6
  97. package/node-server/dist/terminal/pty-manager.js +138 -19
  98. package/node-server/dist/terminal/server.js +155 -23
  99. package/node-server/dist/terminal/types.d.ts +2 -0
  100. package/node-server/dist/update-check.d.ts +20 -0
  101. package/node-server/dist/update-check.js +114 -0
  102. package/node-server/package.json +2 -0
  103. package/node_modules/nebula-autocomplete/README.md +145 -0
  104. package/node_modules/nebula-autocomplete/dist/backends/claude.d.ts +69 -0
  105. package/node_modules/nebula-autocomplete/dist/backends/claude.js +449 -0
  106. package/node_modules/nebula-autocomplete/dist/backends/codex.d.ts +45 -0
  107. package/node_modules/nebula-autocomplete/dist/backends/codex.js +108 -0
  108. package/node_modules/nebula-autocomplete/dist/client/fetcher.d.ts +16 -0
  109. package/node_modules/nebula-autocomplete/dist/client/fetcher.js +41 -0
  110. package/node_modules/nebula-autocomplete/dist/codemirror/ghostText.d.ts +43 -0
  111. package/node_modules/nebula-autocomplete/dist/codemirror/ghostText.js +175 -0
  112. package/node_modules/nebula-autocomplete/dist/core/engine.d.ts +17 -0
  113. package/node_modules/nebula-autocomplete/dist/core/engine.js +135 -0
  114. package/node_modules/nebula-autocomplete/dist/core/lru.d.ts +7 -0
  115. package/node_modules/nebula-autocomplete/dist/core/lru.js +26 -0
  116. package/node_modules/nebula-autocomplete/dist/core/prompt.d.ts +12 -0
  117. package/node_modules/nebula-autocomplete/dist/core/prompt.js +0 -0
  118. package/node_modules/nebula-autocomplete/dist/core/text.d.ts +53 -0
  119. package/node_modules/nebula-autocomplete/dist/core/text.js +169 -0
  120. package/node_modules/nebula-autocomplete/dist/index.d.ts +7 -0
  121. package/node_modules/nebula-autocomplete/dist/index.js +5 -0
  122. package/node_modules/nebula-autocomplete/dist/server/fastify.d.ts +40 -0
  123. package/node_modules/nebula-autocomplete/dist/server/fastify.js +52 -0
  124. package/node_modules/nebula-autocomplete/dist/transport.d.ts +55 -0
  125. package/node_modules/nebula-autocomplete/dist/transport.js +66 -0
  126. package/node_modules/nebula-autocomplete/dist/types.d.ts +103 -0
  127. package/node_modules/nebula-autocomplete/dist/types.js +1 -0
  128. package/node_modules/nebula-autocomplete/package.json +58 -0
  129. package/node_modules/nebula-autocomplete/server.d.ts +3 -0
  130. package/node_modules/nebula-autocomplete/server.js +3 -0
  131. package/package.json +18 -5
  132. package/scripts/postinstall.cjs +25 -5
  133. package/dist/assets/index-BvrHu37J.js +0 -648
  134. package/dist/assets/index-Dfj_xsKU.css +0 -32
  135. package/node-server/dist/llm/index.d.ts +0 -5
  136. package/node-server/dist/llm/index.js +0 -21
  137. package/node-server/dist/llm/llm-service.d.ts +0 -77
  138. package/node-server/dist/llm/llm-service.js +0 -454
  139. package/node-server/dist/llm/types.d.ts +0 -40
  140. package/node-server/dist/llm/types.js +0 -15
  141. package/node-server/dist/routes/llm.d.ts +0 -8
  142. package/node-server/dist/routes/llm.js +0 -105
@@ -0,0 +1,40 @@
1
+ import type { AutocompleteEngine } from "../core/engine.js";
2
+ import type { CompletionRequest } from "../types.js";
3
+ /**
4
+ * Minimal structural types so this plugin works with Fastify 4/5 without a
5
+ * hard dependency on the fastify package.
6
+ */
7
+ interface FastifyLikeRequest {
8
+ body: unknown;
9
+ }
10
+ interface FastifyLikeReply {
11
+ raw: {
12
+ writeHead(status: number, headers: Record<string, string>): void;
13
+ write(chunk: string): void;
14
+ end(): void;
15
+ on(event: "close", cb: () => void): void;
16
+ writableEnded: boolean;
17
+ };
18
+ hijack?: () => void;
19
+ }
20
+ interface FastifyLikeInstance {
21
+ post(path: string, handler: (req: FastifyLikeRequest, reply: FastifyLikeReply) => Promise<void>): void;
22
+ }
23
+ export interface AutocompleteRouteOptions {
24
+ /** Route path. Default "/autocomplete". */
25
+ path?: string;
26
+ }
27
+ export type EngineResolver = (req: CompletionRequest) => AutocompleteEngine;
28
+ /**
29
+ * Register `POST <path>` streaming completions as SSE.
30
+ *
31
+ * Request body: CompletionRequest (JSON).
32
+ * Response events: {type:"chunk",text} then {type:"done",...CompletionResult},
33
+ * or {type:"error",message}.
34
+ *
35
+ * Usage in nebula-notebook's node-server:
36
+ * const engine = new AutocompleteEngine({ backend: new ClaudeBackend() });
37
+ * app.register(async (f) => registerAutocompleteRoute(f, engine), { prefix: "/api" });
38
+ */
39
+ export declare function registerAutocompleteRoute(fastify: FastifyLikeInstance, engine: AutocompleteEngine | EngineResolver, options?: AutocompleteRouteOptions): void;
40
+ export {};
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Register `POST <path>` streaming completions as SSE.
3
+ *
4
+ * Request body: CompletionRequest (JSON).
5
+ * Response events: {type:"chunk",text} then {type:"done",...CompletionResult},
6
+ * or {type:"error",message}.
7
+ *
8
+ * Usage in nebula-notebook's node-server:
9
+ * const engine = new AutocompleteEngine({ backend: new ClaudeBackend() });
10
+ * app.register(async (f) => registerAutocompleteRoute(f, engine), { prefix: "/api" });
11
+ */
12
+ export function registerAutocompleteRoute(fastify, engine, options = {}) {
13
+ const path = options.path ?? "/autocomplete";
14
+ const resolve = typeof engine === "function" ? engine : () => engine;
15
+ fastify.post(path, async (req, reply) => {
16
+ reply.hijack?.(); // take over the raw socket for SSE (Fastify 4/5)
17
+ const res = reply.raw;
18
+ res.writeHead(200, {
19
+ "content-type": "text/event-stream",
20
+ "cache-control": "no-cache",
21
+ connection: "keep-alive",
22
+ });
23
+ const send = (obj) => res.write(`data: ${JSON.stringify(obj)}\n\n`);
24
+ const abort = new AbortController();
25
+ // Detect client disconnect via the RESPONSE stream closing before we
26
+ // ended it. (The request's "close" fires as soon as its body is fully
27
+ // consumed on modern Node — listening there aborts every request.)
28
+ res.on("close", () => {
29
+ if (!res.writableEnded)
30
+ abort.abort(new Error("client disconnected"));
31
+ });
32
+ try {
33
+ const body = req.body;
34
+ if (!body || typeof body.prefix !== "string") {
35
+ send({ type: "error", message: "prefix (string) is required" });
36
+ res.end();
37
+ return;
38
+ }
39
+ const result = await resolve(body).complete(body, {
40
+ signal: abort.signal,
41
+ onChunk: (text) => send({ type: "chunk", text }),
42
+ });
43
+ send({ type: "done", ...result });
44
+ }
45
+ catch (e) {
46
+ if (!abort.signal.aborted) {
47
+ send({ type: "error", message: e instanceof Error ? e.message : String(e) });
48
+ }
49
+ }
50
+ res.end();
51
+ });
52
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Process transport for backends: run a provider CLI either LOCALLY (same host
3
+ * as this process) or OVER SSH on a remote host.
4
+ *
5
+ * The over-ssh case powers "remote Nebula, local agent": the Nebula server runs
6
+ * on a cluster, but autocomplete should drive the user's OWN claude/codex on
7
+ * their laptop — reached over the reverse SSH tunnel the user already has. The
8
+ * CLI's stream-json stdio flows transparently over the ssh channel; a persistent
9
+ * warm worker keeps one ssh connection open (handshake paid once, not per turn),
10
+ * and ControlMaster makes any reconnect ~free. See the validated latency probe:
11
+ * per-turn overhead ≈ one network RTT (~50ms), negligible vs ~2s model latency.
12
+ */
13
+ export interface SshTransport {
14
+ kind: 'ssh';
15
+ /** Host to ssh to — typically 'localhost' (the reverse-tunnel endpoint). */
16
+ host: string;
17
+ /** Reverse-tunnel port on this (server) host that forwards to the user's sshd. */
18
+ port: number;
19
+ /** Username on the user's machine. */
20
+ user: string;
21
+ /** Absolute path to the provider binary ON THE USER'S machine (PATH isn't set
22
+ * for a non-interactive ssh, so this must be absolute). */
23
+ remoteBin: string;
24
+ /** ControlMaster socket path, so warm workers + reconnects reuse one connection. */
25
+ controlPath?: string;
26
+ /** Working dir on the remote side (created if absent). Keep it empty of any
27
+ * CLAUDE.md so the agent harness stays minimal. */
28
+ remoteCwd?: string;
29
+ }
30
+ export type Transport = {
31
+ kind: 'local';
32
+ } | SshTransport;
33
+ export interface SpawnPlan {
34
+ command: string;
35
+ args: string[];
36
+ /** Options to pass to child_process.spawn (env/cwd). For ssh these are empty
37
+ * because env/cwd are embedded in the remote command line. */
38
+ options: {
39
+ env?: NodeJS.ProcessEnv;
40
+ cwd?: string;
41
+ };
42
+ }
43
+ /** POSIX single-quote a string so it survives the remote shell verbatim. */
44
+ export declare function shellQuote(s: string): string;
45
+ /**
46
+ * Plan how to spawn `bin argv` with `env` in `cwd`, honoring the transport.
47
+ * - local: spawn the binary directly; env/cwd go to spawn().
48
+ * - ssh: spawn `ssh …host <remoteCmd>` where remoteCmd re-establishes cwd + env
49
+ * on the far side (via `cd` and `env K=V …`) and execs the binary. `cwd`/`env`
50
+ * passed here are interpreted on the REMOTE side.
51
+ */
52
+ export declare function planSpawn(transport: Transport, bin: string, argv: string[], env: Record<string, string>, cwd: string): SpawnPlan;
53
+ /** Best-effort remote cleanup (e.g. wipe an ephemeral config dir) over the same
54
+ * ssh path. Returns the command+args for a fire-and-forget spawn. */
55
+ export declare function planRemoteCleanup(transport: SshTransport, remotePath: string): SpawnPlan;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Process transport for backends: run a provider CLI either LOCALLY (same host
3
+ * as this process) or OVER SSH on a remote host.
4
+ *
5
+ * The over-ssh case powers "remote Nebula, local agent": the Nebula server runs
6
+ * on a cluster, but autocomplete should drive the user's OWN claude/codex on
7
+ * their laptop — reached over the reverse SSH tunnel the user already has. The
8
+ * CLI's stream-json stdio flows transparently over the ssh channel; a persistent
9
+ * warm worker keeps one ssh connection open (handshake paid once, not per turn),
10
+ * and ControlMaster makes any reconnect ~free. See the validated latency probe:
11
+ * per-turn overhead ≈ one network RTT (~50ms), negligible vs ~2s model latency.
12
+ */
13
+ /** POSIX single-quote a string so it survives the remote shell verbatim. */
14
+ export function shellQuote(s) {
15
+ return `'${String(s).replace(/'/g, `'\\''`)}'`;
16
+ }
17
+ const SSH_BASE_OPTS = [
18
+ '-o', 'ProxyCommand=none',
19
+ '-o', 'StrictHostKeyChecking=accept-new',
20
+ '-o', 'BatchMode=yes',
21
+ '-o', 'ServerAliveInterval=30',
22
+ ];
23
+ /**
24
+ * Plan how to spawn `bin argv` with `env` in `cwd`, honoring the transport.
25
+ * - local: spawn the binary directly; env/cwd go to spawn().
26
+ * - ssh: spawn `ssh …host <remoteCmd>` where remoteCmd re-establishes cwd + env
27
+ * on the far side (via `cd` and `env K=V …`) and execs the binary. `cwd`/`env`
28
+ * passed here are interpreted on the REMOTE side.
29
+ */
30
+ export function planSpawn(transport, bin, argv, env, cwd) {
31
+ if (transport.kind === 'local') {
32
+ return { command: bin, args: argv, options: { env: { ...process.env, ...env }, cwd } };
33
+ }
34
+ const sshOpts = [...SSH_BASE_OPTS, '-p', String(transport.port)];
35
+ if (transport.controlPath) {
36
+ // ControlPersist 1h (was 120s): the master handshake is the expensive part
37
+ // over the reverse tunnel, and a 2-minute persist meant any idle gap made
38
+ // the next worker spawn pay it again — part of the "autocomplete lags
39
+ // after I come back" cold start. An idle master on a localhost tunnel
40
+ // costs one file descriptor; keep it warm for the whole work session.
41
+ sshOpts.push('-o', 'ControlMaster=auto', '-o', `ControlPath=${transport.controlPath}`, '-o', 'ControlPersist=3600');
42
+ }
43
+ const remoteCwd = transport.remoteCwd ?? cwd;
44
+ const envPrefix = Object.entries(env).map(([k, v]) => `${k}=${shellQuote(v)}`).join(' ');
45
+ // mkdir+cd so the remote agent runs in a clean, CLAUDE.md-free dir; `exec env …`
46
+ // sets the child env on the far side (the local process.env doesn't reach it).
47
+ const remoteCmd = `mkdir -p ${shellQuote(remoteCwd)} 2>/dev/null; cd ${shellQuote(remoteCwd)} 2>/dev/null; ` +
48
+ `exec env ${envPrefix} ${shellQuote(transport.remoteBin)} ${argv.map(shellQuote).join(' ')}`;
49
+ return {
50
+ command: 'ssh',
51
+ args: [...sshOpts, `${transport.user}@${transport.host}`, remoteCmd],
52
+ options: {},
53
+ };
54
+ }
55
+ /** Best-effort remote cleanup (e.g. wipe an ephemeral config dir) over the same
56
+ * ssh path. Returns the command+args for a fire-and-forget spawn. */
57
+ export function planRemoteCleanup(transport, remotePath) {
58
+ const sshOpts = [...SSH_BASE_OPTS, '-p', String(transport.port)];
59
+ if (transport.controlPath)
60
+ sshOpts.push('-o', `ControlPath=${transport.controlPath}`);
61
+ return {
62
+ command: 'ssh',
63
+ args: [...sshOpts, `${transport.user}@${transport.host}`, `rm -rf ${shellQuote(remotePath)}`],
64
+ options: {},
65
+ };
66
+ }
@@ -0,0 +1,103 @@
1
+ export interface NotebookCellContext {
2
+ type: "code" | "markdown";
3
+ content: string;
4
+ }
5
+ export interface CompletionRequest {
6
+ /** Text before the cursor in the active cell. Required. */
7
+ prefix: string;
8
+ /** Text after the cursor in the active cell. */
9
+ suffix?: string;
10
+ /** Explicit language, if known. Usually omitted — prefer the hints below and
11
+ * let the model infer the language from the code + kernel/filename. */
12
+ language?: string;
13
+ /** Active kernel name/spec (e.g. "python3", "ir", "julia-1.9"). A hint. */
14
+ kernelName?: string;
15
+ /** Notebook filename (e.g. "analysis.ipynb"). A hint. */
16
+ filename?: string;
17
+ /** All notebook cells, in order, for cross-cell context. */
18
+ cells?: NotebookCellContext[];
19
+ /** Index of the active cell within `cells`. */
20
+ activeCellIndex?: number;
21
+ /**
22
+ * Requests sharing a sessionKey supersede each other: issuing a new one
23
+ * aborts the previous in-flight request. Use the cell id.
24
+ */
25
+ sessionKey?: string;
26
+ /** Model override (e.g. "haiku" | "sonnet"). Engines are pooled per model. */
27
+ model?: string;
28
+ /** Per-request cross-cell context budget in chars (clamped server-side).
29
+ * More context grounds suggestions in the notebook (fewer hallucinated
30
+ * names) at the cost of a bigger prompt. */
31
+ contextBudget?: number;
32
+ /** Per-request cap on suggested lines (clamped server-side). */
33
+ maxLines?: number;
34
+ /** Backend selector, for servers hosting more than one engine. */
35
+ backend?: string;
36
+ }
37
+ export interface CompletionResult {
38
+ text: string;
39
+ backend: string;
40
+ fromCache: boolean;
41
+ ttfbMs: number;
42
+ totalMs: number;
43
+ /** Per-request engine/worker diagnostics (see CompletionDiag). */
44
+ diag?: CompletionDiag;
45
+ }
46
+ /**
47
+ * Where a completion's latency went — filled by the backend, forwarded in the
48
+ * SSE done event so the browser console can show the breakdown.
49
+ */
50
+ export interface CompletionDiag {
51
+ /** Transport the worker ran on. */
52
+ transport?: string;
53
+ /** ms spent queued behind an earlier in-flight turn for the same session
54
+ * (single-flight per cell — typing bursts wait for the previous turn
55
+ * instead of fanning out across workers; see engine.complete). */
56
+ queueWaitMs?: number;
57
+ /** ms from request start until the turn was dispatched to a warm worker
58
+ * (pool scan + possible spawn + warmup wait). The silent part of TTFB. */
59
+ workerWaitMs?: number;
60
+ /** A fresh worker process had to be spawned for this request. */
61
+ coldSpawn?: boolean;
62
+ /** The turn was retried on a fresh worker after the first one died. */
63
+ retried?: boolean;
64
+ /** How many turns this worker had already served (conversation history
65
+ * grows with each turn, inflating time-to-first-byte). */
66
+ workerTurn?: number;
67
+ /** Accumulated prompt chars in this worker's history before this turn —
68
+ * the actual TTFB-drag driver (recycled past maxHistoryCharsPerWorker). */
69
+ workerHistoryChars?: number;
70
+ /** Busy workers / pool size at request time (busy=size → cold spawn). */
71
+ poolBusy?: number;
72
+ poolSize?: number;
73
+ /** Characters in the assembled prompt. */
74
+ promptChars?: number;
75
+ /** Characters in the RAW model reply, before tag/fence/overlap trimming —
76
+ * rawChars>0 with empty final text means post-processing removed it. */
77
+ rawChars?: number;
78
+ }
79
+ export interface CompleteOptions {
80
+ signal?: AbortSignal;
81
+ /** Called with each streamed text chunk (claude backend streams; codex emits once). */
82
+ onChunk?: (text: string) => void;
83
+ }
84
+ /** A completion backend: turns a prompt into text. */
85
+ export interface CompletionBackend {
86
+ readonly name: string;
87
+ complete(prompt: string, opts: {
88
+ signal?: AbortSignal;
89
+ onChunk?: (text: string) => void;
90
+ diag?: CompletionDiag;
91
+ }): Promise<string>;
92
+ /** Release child processes. */
93
+ dispose(): void;
94
+ }
95
+ export interface EngineOptions {
96
+ backend: CompletionBackend;
97
+ /** Max completions cached (keyed on context). Default 128. 0 disables. */
98
+ cacheSize?: number;
99
+ /** Character budget for cross-cell context in the prompt. Default 6000. */
100
+ contextBudget?: number;
101
+ /** Max lines the model is asked to produce. Default 5. */
102
+ maxLines?: number;
103
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "nebula-autocomplete",
3
+ "version": "0.1.0",
4
+ "description": "LLM code autocomplete engine backed by Claude Code / Codex CLI subscriptions, with a Fastify plugin and CodeMirror 6 ghost-text extension.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./server": {
15
+ "types": "./dist/server/fastify.d.ts",
16
+ "default": "./dist/server/fastify.js"
17
+ },
18
+ "./client": {
19
+ "types": "./dist/client/fetcher.d.ts",
20
+ "default": "./dist/client/fetcher.js"
21
+ },
22
+ "./codemirror": {
23
+ "types": "./dist/codemirror/ghostText.d.ts",
24
+ "default": "./dist/codemirror/ghostText.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "server.js",
30
+ "server.d.ts",
31
+ "README.md"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsc -p tsconfig.json",
35
+ "prepare": "node scripts/prepare.cjs",
36
+ "test": "vitest run",
37
+ "smoke": "node scripts/smoke.mjs"
38
+ },
39
+ "peerDependencies": {
40
+ "@codemirror/state": "^6.0.0",
41
+ "@codemirror/view": "^6.0.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "@codemirror/state": {
45
+ "optional": true
46
+ },
47
+ "@codemirror/view": {
48
+ "optional": true
49
+ }
50
+ },
51
+ "devDependencies": {
52
+ "@codemirror/state": "^6.5.0",
53
+ "@codemirror/view": "^6.36.0",
54
+ "@types/node": "^22.0.0",
55
+ "typescript": "^5.8.0",
56
+ "vitest": "^3.0.0"
57
+ }
58
+ }
@@ -0,0 +1,3 @@
1
+ // Type shim for consumers on classic (node10) module resolution, which cannot
2
+ // read the "exports" map. Runtime resolution still goes through "exports".
3
+ export * from "./dist/server/fastify.js";
@@ -0,0 +1,3 @@
1
+ // Runtime shim mirroring server.d.ts (only used by resolvers that bypass the
2
+ // "exports" map).
3
+ export * from "./dist/server/fastify.js";
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "nebula-notebook",
3
- "version": "0.1.1",
4
- "description": "AI-native notebook computing environment \u2014 real Jupyter kernels, real filesystem, built to be driven by agents (Claude Code / Codex) via MCP",
3
+ "version": "0.2.1",
4
+ "description": "AI-native notebook computing environment real Jupyter kernels, real filesystem, built to be driven by agents (Claude Code / Codex) via MCP",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/jzthree/nebula-notebook.git"
9
+ "url": "https://github.com/jzhoulab/nebula-notebook.git"
10
10
  },
11
11
  "keywords": [
12
12
  "notebook",
@@ -27,6 +27,9 @@
27
27
  "node-server/package.json",
28
28
  "scripts/postinstall.cjs"
29
29
  ],
30
+ "bundledDependencies": [
31
+ "nebula-autocomplete"
32
+ ],
30
33
  "engines": {
31
34
  "node": ">=20"
32
35
  },
@@ -42,10 +45,12 @@
42
45
  "mcp": "npm --prefix packages/mcp run mcp",
43
46
  "mcp:build": "npm --prefix packages/mcp run build",
44
47
  "mcp:test": "npm --prefix packages/mcp test",
48
+ "autocomplete:test": "npm --prefix packages/autocomplete test",
45
49
  "setup-mcp": "npm --prefix packages/mcp run setup-mcp",
46
50
  "setup": "node setup.cjs",
47
51
  "postinstall": "node scripts/postinstall.cjs",
48
- "prepublishOnly": "npm run build && npm run node-server:build",
52
+ "autocomplete:build": "npm --prefix packages/autocomplete run build",
53
+ "prepack": "npm --prefix packages/autocomplete install && npm run autocomplete:build && npm run build && npm run node-server:build",
49
54
  "stop": "lsof -ti:3000,3001,8000 | xargs kill -9 2>/dev/null; echo 'Servers stopped'",
50
55
  "test": "vitest",
51
56
  "test:run": "vitest run"
@@ -56,18 +61,23 @@
56
61
  "@fastify/static": "^9.0.0",
57
62
  "@fastify/websocket": "^11.2.0",
58
63
  "@homebridge/node-pty-prebuilt-multiarch": "^0.14.0",
64
+ "@xterm/addon-clipboard": "^0.2.0",
65
+ "@xterm/addon-webgl": "^0.19.0",
59
66
  "better-sqlite3": "^11.0.0",
60
67
  "fastify": "^5.8.2",
61
68
  "jsonwebtoken": "^9.0.2",
69
+ "nebula-autocomplete": "file:packages/autocomplete",
62
70
  "otplib": "^12.0.1",
63
71
  "qrcode-terminal": "^0.12.0",
64
72
  "uuid": "^9.0.1",
65
73
  "ws": "^8.16.0",
74
+ "yaml": "^2.9.0",
66
75
  "zeromq": "^6.4.2"
67
76
  },
68
77
  "devDependencies": {
69
78
  "@codemirror/lang-markdown": "^6.5.0",
70
79
  "@codemirror/lang-python": "^6.2.1",
80
+ "@jupyter-widgets/html-manager": "^1.0.14",
71
81
  "@playwright/test": "^1.57.0",
72
82
  "@tailwindcss/vite": "^4.2.1",
73
83
  "@testing-library/jest-dom": "^6.9.1",
@@ -92,5 +102,8 @@
92
102
  "typescript": "~5.8.2",
93
103
  "vite": "^6.2.0",
94
104
  "vitest": "^4.0.16"
95
- }
105
+ },
106
+ "bundleDependencies": [
107
+ "nebula-autocomplete"
108
+ ]
96
109
  }
@@ -1,10 +1,13 @@
1
1
  /**
2
- * Repo-checkout convenience: install the node-server and packages/mcp
3
- * subpackage dependencies after a root `npm install`.
2
+ * Repo-checkout convenience: install the packages/autocomplete, node-server,
3
+ * and packages/mcp subpackage dependencies after a root `npm install`.
4
+ * packages/autocomplete is listed first so its `prepare` step builds dist/
5
+ * before node-server (which links to it via file:) is set up.
4
6
  *
5
7
  * Skipped entirely when nebula-notebook is installed from npm as a
6
8
  * dependency — the published package is self-contained (server runtime
7
- * deps live in the root package.json; dist artifacts are prebuilt).
9
+ * deps live in the root package.json; nebula-autocomplete is a bundled
10
+ * dependency; dist artifacts are prebuilt).
8
11
  */
9
12
  const path = require('path');
10
13
  const fs = require('fs');
@@ -15,12 +18,29 @@ if (__dirname.split(path.sep).includes('node_modules')) {
15
18
  }
16
19
 
17
20
  const root = path.join(__dirname, '..');
18
- for (const dir of ['node-server', path.join('packages', 'mcp')]) {
21
+ for (const dir of [path.join('packages', 'autocomplete'), 'node-server', path.join('packages', 'mcp')]) {
19
22
  const pkg = path.join(root, dir, 'package.json');
20
23
  if (!fs.existsSync(pkg)) continue;
21
24
  try {
22
- execSync('npm install', { cwd: path.join(root, dir), stdio: 'inherit' });
25
+ // --include=dev: the subpackage builds (tsc) need their devDependencies
26
+ // even when the environment forces production installs (NODE_ENV=production
27
+ // in server shells, npm production config) — without it, tsc is missing
28
+ // and the prepare builds are skipped.
29
+ execSync('npm install --include=dev', { cwd: path.join(root, dir), stdio: 'inherit' });
23
30
  } catch {
24
31
  console.warn(`⚠️ postinstall: npm install failed in ${dir}`);
25
32
  }
26
33
  }
34
+
35
+ // The autocomplete build is load-bearing (the root depends on its dist/ via
36
+ // file:). Its prepare skips quietly when typescript is absent, so verify the
37
+ // artifact exists and fall back to an explicit build.
38
+ const autocompleteDir = path.join(root, 'packages', 'autocomplete');
39
+ if (fs.existsSync(path.join(autocompleteDir, 'package.json')) &&
40
+ !fs.existsSync(path.join(autocompleteDir, 'dist', 'index.js'))) {
41
+ try {
42
+ execSync('npm run build', { cwd: autocompleteDir, stdio: 'inherit' });
43
+ } catch {
44
+ console.warn('⚠️ postinstall: nebula-autocomplete build failed — run "npm install --include=dev && npm run build" in packages/autocomplete');
45
+ }
46
+ }