@rho-workspace/rho 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/README.md +24 -5
  2. package/dist/capabilities.d.ts +27 -0
  3. package/dist/capabilities.js +73 -1
  4. package/dist/cli.js +150 -25
  5. package/dist/contract.generated.d.ts +112 -12
  6. package/dist/contracts/owners.generated.js +103 -8
  7. package/dist/conversation-presentation.d.ts +3 -1
  8. package/dist/conversation-presentation.js +16 -6
  9. package/dist/conversations.d.ts +10 -6
  10. package/dist/conversations.js +17 -13
  11. package/dist/installs.d.ts +11 -0
  12. package/dist/installs.js +179 -0
  13. package/dist/project-notes.js +2 -0
  14. package/dist/protocol.js +7 -5
  15. package/dist/screen-check-skill.d.ts +2 -0
  16. package/dist/screen-check-skill.js +39 -0
  17. package/dist/server.d.ts +2 -0
  18. package/dist/server.js +50 -7
  19. package/dist/web/.vite/manifest.json +51 -51
  20. package/dist/web/assets/{choices-B-y2OYvE.js → choices-DU9kHlyQ.js} +1 -1
  21. package/dist/web/assets/{console-e1P5qYQ5.js → console-Dbdo-5oO.js} +1 -1
  22. package/dist/web/assets/{dist-Dd3iAGFy.js → dist-B2V0VDO0.js} +1 -1
  23. package/dist/web/assets/{dist-CH56yNDn.js → dist-BV3hWfn_.js} +1 -1
  24. package/dist/web/assets/{dist-CBM2oq6g.js → dist-CLNF2g-E.js} +1 -1
  25. package/dist/web/assets/{dist-DEorbOW0.js → dist-D25Vn17i.js} +1 -1
  26. package/dist/web/assets/{explore-in-muWXOGOY.js → explore-in-8Q5ftt-E.js} +1 -1
  27. package/dist/web/assets/{graph-CK-df5lh.js → graph-hS2kvHTI.js} +1 -1
  28. package/dist/web/assets/{index-BYuKpyoi.js → index-BHQIaDsx.js} +220 -121
  29. package/dist/web/assets/index-Cj1_iqyK.css +1 -0
  30. package/dist/web/assets/{notes-meaning-B3HFbh84.js → notes-meaning-DdPbtNzJ.js} +1 -1
  31. package/dist/web/assets/{overview-D1Ev2OC_.js → overview-BV6ZO5TO.js} +3 -3
  32. package/dist/web/assets/{preview-HbmQKUGY.js → preview-n5lOOdjN.js} +1 -1
  33. package/dist/web/assets/{review-BVh9jWMB.js → review-CmWou4Iv.js} +1 -1
  34. package/dist/web/assets/{skills-DPaEYkln.js → skills-Dy7n3L9d.js} +1 -1
  35. package/dist/web/assets/{skills-work-dvjfy_tu.js → skills-work-Cj0W6b-I.js} +1 -1
  36. package/dist/web/assets/{studio-process-BYm8mjRJ.js → studio-process-QOY_n2_8.js} +1 -1
  37. package/dist/web/assets/{timeline-events-CDZ7wiJX.js → timeline-events-DmX1BCh3.js} +1 -1
  38. package/dist/web/assets/{work-turns-BeCt303c.js → work-turns-Co0C71rJ.js} +1 -1
  39. package/dist/web/assets/{workspace-d5Hw_ClZ.js → workspace-BKFefRfJ.js} +2 -2
  40. package/dist/web/composition.json +73 -73
  41. package/dist/web/index.html +2 -2
  42. package/dist/web/renderer.json +1 -1
  43. package/docs/FRONTEND.md +30 -14
  44. package/docs/HEADLESS.md +59 -13
  45. package/example/pa14-qs/.rho/rho/conversations/c-67f77cfebaba2cea.json +1 -1
  46. package/example/pa14-qs/.rho/rho/conversations/c-91aa2e46b1ac1a6c.json +1 -1
  47. package/example/pa14-qs/.rho/rho/conversations/c-fa24dfd80dfe4bcb.json +1 -1
  48. package/node_modules/@hono/node-server/dist/index.js +11 -0
  49. package/node_modules/@hono/node-server/dist/index.mjs +11 -0
  50. package/node_modules/@hono/node-server/dist/listener.js +11 -0
  51. package/node_modules/@hono/node-server/dist/listener.mjs +11 -0
  52. package/node_modules/@hono/node-server/dist/request.js +11 -0
  53. package/node_modules/@hono/node-server/dist/request.mjs +11 -0
  54. package/node_modules/@hono/node-server/dist/server.js +11 -0
  55. package/node_modules/@hono/node-server/dist/server.mjs +11 -0
  56. package/node_modules/@hono/node-server/dist/vercel.js +11 -0
  57. package/node_modules/@hono/node-server/dist/vercel.mjs +11 -0
  58. package/node_modules/@hono/node-server/package.json +1 -1
  59. package/node_modules/@rho-workspace/plugin-agent/README.md +3 -1
  60. package/node_modules/@rho-workspace/plugin-agent/dist/acp-conversation.js +14 -3
  61. package/node_modules/@rho-workspace/plugin-agent/dist/conversation.d.ts +1 -0
  62. package/node_modules/@rho-workspace/plugin-agent/dist/conversation.js +1 -0
  63. package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.d.ts +11 -0
  64. package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.js +7 -0
  65. package/node_modules/@rho-workspace/plugin-agent/dist/mcp.js +5 -1
  66. package/node_modules/@rho-workspace/plugin-agent/package.json +1 -1
  67. package/node_modules/@rho-workspace/plugin-annotations/package.json +1 -1
  68. package/node_modules/@rho-workspace/plugin-documents/README.md +4 -0
  69. package/node_modules/@rho-workspace/plugin-documents/dist/index.d.ts +3 -0
  70. package/node_modules/@rho-workspace/plugin-documents/dist/index.js +1 -1
  71. package/node_modules/@rho-workspace/plugin-documents/package.json +1 -1
  72. package/node_modules/@rho-workspace/plugin-kernel/README.md +71 -10
  73. package/node_modules/@rho-workspace/plugin-kernel/dist/code.d.ts +2 -0
  74. package/node_modules/@rho-workspace/plugin-kernel/dist/code.js +4 -1
  75. package/node_modules/@rho-workspace/plugin-kernel/dist/contract.js +5 -0
  76. package/node_modules/@rho-workspace/plugin-kernel/dist/index.d.ts +7 -0
  77. package/node_modules/@rho-workspace/plugin-kernel/dist/index.js +55 -8
  78. package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.d.ts +9 -6
  79. package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.js +18 -41
  80. package/node_modules/@rho-workspace/plugin-kernel/dist/operations.js +5 -2
  81. package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.d.ts +39 -0
  82. package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.js +661 -0
  83. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.d.ts +3 -0
  84. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.js +10 -0
  85. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.d.ts +38 -4
  86. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.js +194 -97
  87. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook-work.js +1 -2
  88. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.d.ts +10 -2
  89. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.js +79 -18
  90. package/node_modules/@rho-workspace/plugin-kernel/dist/ui-styles.js +115 -45
  91. package/node_modules/@rho-workspace/plugin-kernel/dist/ui.d.ts +1 -1
  92. package/node_modules/@rho-workspace/plugin-kernel/dist/ui.js +1 -1
  93. package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.d.ts +63 -0
  94. package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.js +344 -0
  95. package/node_modules/@rho-workspace/plugin-kernel/package.json +1 -1
  96. package/node_modules/@rho-workspace/plugin-kernel/runners/runner.R +6 -3
  97. package/node_modules/@rho-workspace/plugin-kernel/runners/runner.py +6 -3
  98. package/node_modules/express-rate-limit/dist/index.cjs +1 -1
  99. package/node_modules/express-rate-limit/dist/index.mjs +1 -1
  100. package/node_modules/express-rate-limit/package.json +1 -1
  101. package/node_modules/ip-address/dist/ipv6.js +1 -1
  102. package/node_modules/ip-address/package.json +1 -1
  103. package/node_modules/playwright-core/LICENSE +202 -0
  104. package/node_modules/playwright-core/NOTICE +5 -0
  105. package/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
  106. package/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
  107. package/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
  108. package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
  109. package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
  110. package/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
  111. package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
  112. package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
  113. package/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
  114. package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
  115. package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
  116. package/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
  117. package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
  118. package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
  119. package/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
  120. package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
  121. package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
  122. package/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
  123. package/node_modules/playwright-core/browsers.json +52 -0
  124. package/node_modules/playwright-core/cli.js +22 -0
  125. package/node_modules/playwright-core/index.js +17 -0
  126. package/node_modules/playwright-core/index.mjs +28 -0
  127. package/node_modules/playwright-core/lib/bootstrap.js +149 -0
  128. package/node_modules/playwright-core/lib/coreBundle.js +78041 -0
  129. package/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
  130. package/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
  131. package/node_modules/playwright-core/lib/entry/mcp.js +10 -0
  132. package/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
  133. package/node_modules/playwright-core/lib/package.js +50 -0
  134. package/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
  135. package/node_modules/playwright-core/lib/server/electron/loader.js +123 -0
  136. package/node_modules/playwright-core/lib/serverRegistry.js +1939 -0
  137. package/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +65 -0
  138. package/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
  139. package/node_modules/playwright-core/lib/tools/cli-client/cli.js +7 -0
  140. package/node_modules/playwright-core/lib/tools/cli-client/help.json +808 -0
  141. package/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
  142. package/node_modules/playwright-core/lib/tools/cli-client/output.js +352 -0
  143. package/node_modules/playwright-core/lib/tools/cli-client/program.js +422 -0
  144. package/node_modules/playwright-core/lib/tools/cli-client/registry.js +180 -0
  145. package/node_modules/playwright-core/lib/tools/cli-client/session.js +261 -0
  146. package/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
  147. package/node_modules/playwright-core/lib/tools/utils/extension.js +112 -0
  148. package/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
  149. package/node_modules/playwright-core/lib/utilsBundle.js +86078 -0
  150. package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +1890 -0
  151. package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule--QdMvsKi.css +1 -0
  152. package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule-CQ8RZGBm.js +32 -0
  153. package/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
  154. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
  155. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
  156. package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
  157. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-AwgwcYie.css +1 -0
  158. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-hRvO4yU2.js +12 -0
  159. package/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
  160. package/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
  161. package/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
  162. package/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
  163. package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
  164. package/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
  165. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
  166. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DK72OMRi.js +32 -0
  167. package/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
  168. package/node_modules/playwright-core/lib/vite/recorder/assets/index-BZpYJZ-P.css +1 -0
  169. package/node_modules/playwright-core/lib/vite/recorder/assets/index-Cn0mmDuv.js +129 -0
  170. package/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
  171. package/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
  172. package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BEB2e8M3.js +32 -0
  173. package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-D-g2vrvf.js +184 -0
  174. package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
  175. package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-B794rD2Z.js +7 -0
  176. package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
  177. package/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
  178. package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.C8BeHuxd.css +1 -0
  179. package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
  180. package/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
  181. package/node_modules/playwright-core/lib/vite/traceViewer/index.oNry3Hjv.js +1 -0
  182. package/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
  183. package/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
  184. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
  185. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
  186. package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
  187. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CYwC_gH1.js +5 -0
  188. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CyMfwkXJ.css +1 -0
  189. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
  190. package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
  191. package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
  192. package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
  193. package/node_modules/playwright-core/lib/xdg-open +1267 -0
  194. package/node_modules/playwright-core/package.json +34 -0
  195. package/package.json +10 -6
  196. package/protocol/index.json +17 -17
  197. package/protocol/operations/conversation.settings.check.json +147 -4
  198. package/protocol/operations/conversation.settings.read.json +145 -3
  199. package/protocol/operations/conversation.settings.update.json +166 -4
  200. package/protocol/operations/conversation.start.json +2 -1
  201. package/protocol/operations/kernel.interrupt.json +20 -0
  202. package/protocol/operations/kernel.read.json +30 -0
  203. package/protocol/operations/kernel.run.json +36 -1
  204. package/protocol/operations/kernel.stop.json +10 -0
  205. package/scripts/browser-checks.mjs +53 -0
  206. package/scripts/screen-check.mjs +193 -0
  207. package/vendor/composition.json +102 -102
  208. package/vendor/rho-workspace-plugin-agent-0.4.28.tgz +0 -0
  209. package/vendor/rho-workspace-plugin-annotations-0.5.2.tgz +0 -0
  210. package/vendor/rho-workspace-plugin-documents-0.4.11.tgz +0 -0
  211. package/vendor/rho-workspace-plugin-kernel-0.5.5.tgz +0 -0
  212. package/web/theme.css +17 -2
  213. package/dist/web/assets/index-DBd77u4a.css +0 -1
  214. package/vendor/rho-workspace-plugin-agent-0.4.27.tgz +0 -0
  215. package/vendor/rho-workspace-plugin-documents-0.4.10.tgz +0 -0
  216. package/vendor/rho-workspace-plugin-kernel-0.5.2.tgz +0 -0
@@ -27,6 +27,7 @@ export interface SessionRecord {
27
27
  cells: number;
28
28
  place?: SessionPlace | null;
29
29
  host?: string | null;
30
+ program?: string | null;
30
31
  }
31
32
  export interface SessionPlace {
32
33
  placeId: string;
@@ -80,6 +81,8 @@ export interface CellRecord {
80
81
  path: string;
81
82
  how: 'read' | 'wrote';
82
83
  }>;
84
+ program?: string | null;
85
+ version?: string | null;
83
86
  }
84
87
  export type InspectInput = {
85
88
  language: Language;
@@ -142,6 +145,7 @@ export declare class KernelHost {
142
145
  private loading?;
143
146
  private turn;
144
147
  private exclusive;
148
+ private programs;
145
149
  private placesRead?;
146
150
  private placesWriting;
147
151
  private ssh;
@@ -154,6 +158,8 @@ export declare class KernelHost {
154
158
  serverctl?: string;
155
159
  onChange?: () => void;
156
160
  });
161
+ private program;
162
+ private named;
157
163
  private transport;
158
164
  private get limits();
159
165
  private folder;
@@ -180,6 +186,7 @@ export declare class KernelHost {
180
186
  by: string;
181
187
  waitMs?: number;
182
188
  place?: string;
189
+ interpreter?: string;
183
190
  }): Promise<{
184
191
  cell: CellRecord;
185
192
  session: SessionRecord;
@@ -1,5 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import * as fs from 'node:fs/promises';
3
+ import { constants as fsConstants } from 'node:fs';
3
4
  import net from 'node:net';
4
5
  import path from 'node:path';
5
6
  import { fileURLToPath } from 'node:url';
@@ -96,6 +97,9 @@ export class KernelHost {
96
97
  this.turn = next.catch(() => { });
97
98
  return next;
98
99
  }
100
+ // The program each language's sessions start with when the project has named one (a path in
101
+ // the project, as named): the last one a cell named, until another is named.
102
+ programs = {};
99
103
  placesRead;
100
104
  placesWriting = Promise.resolve();
101
105
  ssh;
@@ -107,6 +111,24 @@ export class KernelHost {
107
111
  const limits = this.limits;
108
112
  this.serverctl = new Serverctl(options.serverctl ?? 'serverctl', { reuseS: 60, sessionS: limits.serverctlSessionS, readMs: limits.serverctlReadMs, skillBytes: limits.serverSkillBytes });
109
113
  }
114
+ // A program named for a session: one in a folder of the project, such as the python of a
115
+ // virtual environment made there (.venv/bin/python). Its folder must be the project's own; the
116
+ // file itself may be the link a virtual environment makes to the Python it was made from, and
117
+ // it is started by that name so the environment is the one that runs.
118
+ async program(named) {
119
+ const root = await fs.realpath(this.root), file = path.resolve(root, named);
120
+ const folder = await fs.realpath(path.dirname(file)).catch(() => null);
121
+ requireThat(folder && (folder === root || folder.startsWith(root + path.sep)), 'scope', `Name a program in a folder of this project, such as .venv/bin/python; ${named} is not.`, { interpreter: named });
122
+ const program = path.join(folder, path.basename(file));
123
+ const runs = await fs.stat(program).then(s => s.isFile(), () => false) && await fs.access(program, fsConstants.X_OK).then(() => true, () => false);
124
+ requireThat(runs, 'not_found', `${named} is not a program that can run here.`, { interpreter: named });
125
+ return program;
126
+ }
127
+ // A program in the project is named relative to it (.venv/bin/python), so the record stays true
128
+ // when the project moves; any other keeps the path the session reported.
129
+ named(live, program) {
130
+ return !live.place && program.startsWith(live.root + path.sep) ? path.relative(live.root, program) : program;
131
+ }
110
132
  transport(place) { return place.via === 'serverctl' ? this.serverctl : this.ssh; }
111
133
  get limits() { return { ...kernelLimits, ...this.options.limits }; }
112
134
  async folder() { return stateDirectory(this.root, '.rho/plugins/kernel'); }
@@ -114,6 +136,7 @@ export class KernelHost {
114
136
  load() { return this.loading ??= this.readRecord(); }
115
137
  async readRecord() {
116
138
  const state = await readJson(path.join(await this.folder(), 'state.json'), { sessions: [], cells: [] });
139
+ this.programs = state.programs ?? {};
117
140
  let changed = false;
118
141
  for (const s of state.sessions)
119
142
  if (s.state !== 'ended') {
@@ -134,7 +157,7 @@ export class KernelHost {
134
157
  save() {
135
158
  clearTimeout(this.saveTimer);
136
159
  this.saveTimer = undefined;
137
- this.writing = this.writing.then(async () => atomicJson(path.join(await this.folder(), 'state.json'), { sessions: this.sessions ?? [], cells: this.cells }));
160
+ this.writing = this.writing.then(async () => atomicJson(path.join(await this.folder(), 'state.json'), { sessions: this.sessions ?? [], cells: this.cells, programs: this.programs }));
138
161
  return this.writing;
139
162
  }
140
163
  // Output arrives in many small pieces; it is written at most twice a second.
@@ -178,7 +201,7 @@ export class KernelHost {
178
201
  }
179
202
  else {
180
203
  requireThat(input.code.trim().length > 0 && Buffer.byteLength(input.code) <= this.limits.maxCodeBytes, 'invalid', `Write code of at most ${this.limits.maxCodeBytes} bytes.`, { limit: 'maxCodeBytes' });
181
- const live = await this.ensure(input.language, input.by, input.place);
204
+ const live = await this.ensure(input.language, input.by, input.place, input.interpreter);
182
205
  requireThat(live.queue.length < this.limits.maxQueued, 'limit', `At most ${this.limits.maxQueued} cells wait in a session. Wait for some to run or interrupt the one running.`, { limit: 'maxQueued', maximum: this.limits.maxQueued });
183
206
  // The oldest finished cells make room, with their plots.
184
207
  while (this.cells.length >= this.limits.maxCells) {
@@ -202,14 +225,24 @@ export class KernelHost {
202
225
  // The language's live session, started for this caller when there is none: where the cell
203
226
  // names, else where the project runs that language (this machine unless chosen). A cell that
204
227
  // names another place than where the live session runs is refused; its values stay there.
205
- async ensure(language, by, placeId) {
228
+ async ensure(language, by, placeId, interpreter) {
206
229
  const running = this.live.get(language), name = language === 'r' ? 'R' : 'Python';
207
230
  const where = (live) => live.place ? `on ${live.place.label}` : 'on this machine';
231
+ requireThat(!interpreter || !placeId || placeId === 'local', 'invalid', 'An interpreter names a program in the project on this machine; a session on a server runs that server\'s.');
232
+ const named = interpreter ? await this.program(interpreter) : null;
208
233
  if (running && !running.stopping) {
209
234
  requireThat(!placeId || placeId === (running.place?.placeId ?? 'local'), 'conflict', `The ${name} session runs ${where(running)}. Run the cell there, or stop that session first; its values stay until then.`, { place: running.place?.placeId ?? 'local' });
210
- return running;
235
+ if (!named || !running.place && running.started === named)
236
+ return running;
237
+ // A session in which only this caller ran cells, none of them now, is its own to start
238
+ // again with the program it names. Once anyone else ran a cell there, the values are shared
239
+ // and only the person stops it.
240
+ const own = !running.place && !running.current && !running.queue.length && this.cells.filter(c => c.sessionId === running.record.sessionId).every(c => c.by === by);
241
+ requireThat(own, 'conflict', `The ${name} session runs ${running.record.program ?? 'another program'}${running.current || running.queue.length ? ' and has a cell running' : ', and others ran cells in it'}. Ask the person to stop it in the Notebook, then run this cell again to start one with ${interpreter}; its values stay until then.`, { program: running.started ?? running.record.program ?? null });
242
+ this.stopping(this.sessions, running.record.sessionId, by);
243
+ await running.ended;
211
244
  }
212
- requireThat(!running, 'conflict', 'The session is stopping. Run the cell again when it has ended.');
245
+ requireThat(!this.live.get(language), 'conflict', 'The session is stopping. Run the cell again when it has ended.');
213
246
  const chosen = placeId ?? (await this.placeState()).use[language] ?? 'local';
214
247
  const place = chosen === 'local' ? null : (await this.placeState()).places.find(p => p.placeId === chosen) ?? null;
215
248
  requireThat(chosen === 'local' || place, 'not_found', `No place is named ${chosen}. Read kernel.places for the places there are.`);
@@ -220,7 +253,15 @@ export class KernelHost {
220
253
  if (old.length > this.limits.maxEndedSessions)
221
254
  this.sessions.splice(this.sessions.indexOf(old[0]), 1);
222
255
  const windows = !place && process.platform === 'win32';
223
- const program = place ? 'ssh' : language === 'r' ? this.options.rscript ?? 'Rscript' : this.options.python ?? (windows ? 'python' : 'python3');
256
+ // A program the cell named, else the one the project named last while it is still there.
257
+ requireThat(!named || !place, 'invalid', `${name} runs on ${place?.label} in this project; an interpreter names a program in the project on this machine.`);
258
+ const kept = !named && !place && this.programs[language] ? await this.program(this.programs[language]).catch(() => null) : null;
259
+ if (named)
260
+ this.programs[language] = path.relative(await fs.realpath(this.root), named);
261
+ else if (!kept && this.programs[language])
262
+ delete this.programs[language];
263
+ const selected = named ?? kept;
264
+ const program = place ? 'ssh' : selected ?? (language === 'r' ? this.options.rscript ?? 'Rscript' : this.options.python ?? (windows ? 'python' : 'python3'));
224
265
  const root = await fs.realpath(this.root);
225
266
  // On a server the runner is sent first on the session's own input, named by its digest, so
226
267
  // the session needs nothing installed there but R or Python.
@@ -229,7 +270,9 @@ export class KernelHost {
229
270
  const pipe = windows && language === 'r' ? await answerPipe() : null;
230
271
  const child = place ? await this.transport(place).spawn(place, sessionCommand(place, language === 'r' ? 'Rscript' : 'python3', runner, source.length), root)
231
272
  : spawn(program, [runners[language], root], { cwd: root, ...ownGroup, stdio: ['pipe', 'pipe', 'pipe', 'pipe'], env: { ...process.env, MPLBACKEND: 'Agg', PYTHONUNBUFFERED: '1', ...(pipe ? { RHO_KERNEL_ANSWERS: pipe.name } : {}) } });
232
- const live = { record, child, place, remotePid: null, answers: '', early: '', order: Promise.resolve(), queue: [], current: null, stopping: false, printed: '', mark: 0, held: [], answered: 0, looking: null, lookups: Promise.resolve(), pipe: pipe?.server };
273
+ let settle = () => { };
274
+ const ended = new Promise(resolve => { settle = resolve; });
275
+ const live = { started: selected, root, ended, settle, record, child, place, remotePid: null, answers: '', early: '', order: Promise.resolve(), queue: [], current: null, stopping: false, printed: '', mark: 0, held: [], answered: 0, looking: null, lookups: Promise.resolve(), pipe: pipe?.server };
233
276
  this.live.set(language, live);
234
277
  record.pid = place ? null : child.pid ?? null;
235
278
  const startMs = place ? this.limits.remoteStartTimeoutMs : this.limits.startTimeoutMs;
@@ -411,6 +454,8 @@ export class KernelHost {
411
454
  cell.state = 'running';
412
455
  cell.startedAt = now();
413
456
  live.record.state = 'busy';
457
+ cell.program = live.record.program ?? null;
458
+ cell.version = live.record.version;
414
459
  // A session on a server draws in its own temporary folder and sends each plot with its answer.
415
460
  live.child.stdin.write(JSON.stringify({ op: 'run', id: cell.cellId, number: cell.number, code: cell.code, plots: live.place ? null : await this.plots(), inline: !!live.place, maxPlots: this.limits.maxPlots, maxVariables: this.limits.maxVariables }) + '\n');
416
461
  this.changed();
@@ -437,7 +482,7 @@ export class KernelHost {
437
482
  const record = live.record, cell = live.current;
438
483
  if (msg.kind === 'ready') {
439
484
  clearTimeout(live.timer);
440
- Object.assign(record, { state: 'idle', version: String(msg.version ?? ''), variables: msg.variables?.items ?? [], variablesOmitted: msg.variables?.omitted ?? 0, variablesAt: now(), host: typeof msg.host === 'string' ? msg.host.slice(0, 256) : null });
485
+ Object.assign(record, { state: 'idle', version: String(msg.version ?? ''), program: typeof msg.executable === 'string' && msg.executable ? this.named(live, msg.executable.slice(0, 1024)) : record.program ?? null, variables: msg.variables?.items ?? [], variablesOmitted: msg.variables?.omitted ?? 0, variablesAt: now(), host: typeof msg.host === 'string' ? msg.host.slice(0, 256) : null });
441
486
  if (live.place) {
442
487
  live.remotePid = Number.isInteger(msg.pid) && msg.pid > 1 ? msg.pid : null;
443
488
  record.pid = live.remotePid;
@@ -534,6 +579,7 @@ export class KernelHost {
534
579
  }
535
580
  if (this.live.get(record.language) === live)
536
581
  this.live.delete(record.language);
582
+ live.settle();
537
583
  this.changed();
538
584
  }
539
585
  // Interrupting a running cell stops it and leaves its session; a waiting cell is not run. A
@@ -761,6 +807,7 @@ const content = [
761
807
  'Results meant to last still go to files from a script, so they can be run again without the session.',
762
808
  'Sessions can also run on a server. kernel.places lists the places and where each language runs; kernel.place.set names one (the server, its folder there, and setup lines run there before a session starts) or chooses it for R or Python; kernel.place.check connects and reports R and Python there.',
763
809
  'kernel.servers lists the servers the person manages with serverctl and the skills bound to each: how that server is used (its environments, where heavy work goes, what to avoid there). Before you name a place on one or run code there, read the skills that apply with kernel.server.skill and follow them, for example in the place\'s setup lines. They guide the work on that server; they do not widen what you may do. A place on such a server is reached through serverctl, which holds its login; another server is reached with ssh.',
810
+ 'The project\'s Python is the machine\'s python3 until you name another. When the work needs packages it lacks, make an environment in the project (python3 -m venv .venv or uv venv, then install into it) and pass interpreter: ".venv/bin/python" to kernel.run; later sessions of that language start with it too, and each cell records the program that ran it. A live session in which only you ran cells starts again with the program you name; once the person ran a cell in it, ask them to stop it first. Do not add another environment\'s packages to a running session\'s path.',
764
811
  'Pass place to kernel.run to start a session there. Paths in a session on a server are its folder\'s; kernel.place.list lists them and kernel.place.copy copies a file into the project under remote/<place>/.',
765
812
  ].join('\n');
766
813
  // What each view keeps in its props: the Notebook's form, and which value a value's view shows.
@@ -57,10 +57,13 @@ export declare function lineage(cells: LineageCell[], values: string[], limits?:
57
57
  plots: number;
58
58
  files: number;
59
59
  }): Lineage;
60
- export declare function related(graph: Lineage, stepId: string): Set<string>;
61
- export declare function lanes(graph: Lineage): {
62
- order: LineageStep[];
63
- lane: Map<string, number>;
64
- count: number;
65
- };
60
+ export interface CellRelations {
61
+ reads: Map<string, Array<{
62
+ name: string;
63
+ from: LineageCell | null;
64
+ }>>;
65
+ readers: Map<string, Map<string, LineageCell[]>>;
66
+ maker: Map<string, LineageCell>;
67
+ }
68
+ export declare function cellRelations(cells: LineageCell[]): CellRelations;
66
69
  export declare function cellLabel(cell: LineageCell): string;
@@ -129,48 +129,25 @@ function place(steps, links) {
129
129
  step.nodes = step.nodes.map((n, i) => ({ n, i, at: toward(n.id) })).sort((a, b) => a.at - b.at || a.i - b.i).map(x => x.n);
130
130
  return layers;
131
131
  }
132
- // The steps a node comes from and leads to, through the links, for showing one path at a time.
133
- export function related(graph, stepId) {
134
- const stepOfNode = new Map(graph.steps.flatMap(s => s.nodes.map(n => [n.id, s.id])));
135
- const up = new Map(), down = new Map();
136
- for (const link of graph.links) {
137
- const from = stepOfNode.get(link.from);
138
- up.set(link.to, [...up.get(link.to) ?? [], from]);
139
- down.set(from, [...down.get(from) ?? [], link.to]);
140
- }
141
- const reach = (start, next) => {
142
- const seen = new Set([start]), queue = [start];
143
- while (queue.length)
144
- for (const id of next.get(queue.pop()) ?? [])
145
- if (!seen.has(id)) {
146
- seen.add(id);
147
- queue.push(id);
148
- }
149
- return seen;
150
- };
151
- return new Set([...reach(stepId, up), ...reach(stepId, down)]);
152
- }
153
- // The boxes in the order their cells ran (which is also an order in which each reads only from
154
- // boxes above it), and a lane in the margin for each value or file something read: from the line
155
- // that made it down past the boxes between, to each box that read it. Lanes whose spans do not
156
- // overlap share a place; lane 0 is nearest the boxes.
157
- export function lanes(graph) {
158
- const order = [...graph.steps].sort((a, b) => (a.cell?.number ?? -1) - (b.cell?.number ?? -1));
159
- const index = new Map(order.map((s, i) => [s.id, i])), stepOfNode = new Map(order.flatMap(s => s.nodes.map(n => [n.id, s.id])));
160
- const spans = new Map();
161
- for (const link of graph.links) {
162
- const from = index.get(stepOfNode.get(link.from)), to = index.get(link.to), span = spans.get(link.from);
163
- spans.set(link.from, { from, to: Math.max(to, span?.to ?? to) });
164
- }
165
- const ends = [], lane = new Map();
166
- for (const [node, span] of [...spans].sort((a, b) => a[1].from - b[1].from || a[1].to - b[1].to)) {
167
- let free = ends.findIndex(end => end <= span.from);
168
- if (free < 0)
169
- free = ends.length;
170
- ends[free] = span.to;
171
- lane.set(node, free);
132
+ export function cellRelations(cells) {
133
+ const reads = new Map(), readers = new Map(), maker = new Map();
134
+ for (const cell of finished(cells)) {
135
+ reads.set(cell.cellId, (cell.used ?? []).map(name => {
136
+ const from = maker.get(name) ?? null;
137
+ if (from) {
138
+ let by = readers.get(from.cellId);
139
+ if (!by)
140
+ readers.set(from.cellId, by = new Map());
141
+ by.set(name, [...by.get(name) ?? [], cell]);
142
+ }
143
+ return { name, from };
144
+ }));
145
+ for (const name of cell.removed ?? [])
146
+ maker.delete(name);
147
+ for (const name of cell.defined ?? [])
148
+ maker.set(name, cell);
172
149
  }
173
- return { order, lane, count: ends.length };
150
+ return { reads, readers, maker };
174
151
  }
175
152
  // What a cell is called where it is named: its purpose (a script's lines), else its first line.
176
153
  export function cellLabel(cell) {
@@ -3,9 +3,12 @@ import { cellId, cellSchema, changeId, copySchema, inspectStates, language, obje
3
3
  // Owner business contracts. Application project/authentication fields are separate.
4
4
  export const operationContracts = {
5
5
  // place names where a session that is not live yet starts ("local" is this machine); a cell
6
- // naming another place than the live session's is refused.
6
+ // naming another place than the live session's is refused. interpreter names the program a
7
+ // session on this machine starts with, in a folder of the project (.venv/bin/python); the
8
+ // project keeps it for the language's later sessions. A cell naming another program than the
9
+ // live session's is refused.
7
10
  'kernel.run': {
8
- inputSchema: obj({ language, code: { type: 'string', minLength: 1, maxLength: 65536 }, purpose: { type: 'string', maxLength: 200 }, changeId, waitMs: { type: 'integer', minimum: 0, maximum: 600000 }, place: placeRef }, ['language', 'code', 'changeId']),
11
+ inputSchema: obj({ language, code: { type: 'string', minLength: 1, maxLength: 65536 }, purpose: { type: 'string', maxLength: 200 }, changeId, waitMs: { type: 'integer', minimum: 0, maximum: 600000 }, place: placeRef, interpreter: { type: 'string', minLength: 1, maxLength: 1024 } }, ['language', 'code', 'changeId']),
9
12
  outputSchema: obj({ cell: cellSchema, session: sessionSchema }),
10
13
  requires: [], effect: 'native',
11
14
  },
@@ -0,0 +1,39 @@
1
+ import type { Language } from './explore-code.js';
2
+ import type { Lineage, LineageCell, LineageStep } from './lineage.js';
3
+ export interface ScriptStep extends LineageStep {
4
+ cell: LineageCell;
5
+ from: number;
6
+ to: number;
7
+ changed: string[];
8
+ functions: string[];
9
+ }
10
+ export interface ScriptFlow extends Lineage {
11
+ steps: ScriptStep[];
12
+ quiet: number;
13
+ cut: number;
14
+ quietReads: Array<{
15
+ from: number;
16
+ to: number;
17
+ used: string[];
18
+ means?: string;
19
+ }>;
20
+ means: Map<string, string>;
21
+ outside: Array<{
22
+ name: string;
23
+ to: string;
24
+ }>;
25
+ tooLong: boolean;
26
+ }
27
+ export declare const scriptFlowLimits: {
28
+ bytes: number;
29
+ steps: number;
30
+ };
31
+ export declare function looksLikePath(s: string): boolean;
32
+ export declare function markersAbove(lines: string[], line: number): Array<{
33
+ name: string | null;
34
+ text: string;
35
+ }>;
36
+ export declare function scriptFlow(text: string, language: Language, limits?: {
37
+ bytes: number;
38
+ steps: number;
39
+ }, outside?: string[]): ScriptFlow;