@chengxianglibra/dsh-data-analysis 0.3.0 → 0.3.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 (53) hide show
  1. package/README.en.md +7 -2
  2. package/README.md +7 -2
  3. package/assets/logo-horizontal.svg +42 -0
  4. package/assets/logo-mark.svg +20 -0
  5. package/lib/bin/environment.js +99 -76
  6. package/lib/client/analysis-ui/offline-runtime.generated.js +1 -1
  7. package/lib/client/credentials/install.js +42 -38
  8. package/lib/client/credentials/model.js +9 -0
  9. package/lib/client/credentials/report-publishing.js +84 -0
  10. package/lib/client/credentials/styles.js +2 -0
  11. package/lib/client/i18n/credentials.js +18 -0
  12. package/lib/client/i18n/presentation.js +24 -0
  13. package/lib/client/right-tabs/install.js +9 -1
  14. package/lib/client/right-tabs/report-actions.js +30 -4
  15. package/lib/client/right-tabs/report-publishing.js +107 -0
  16. package/lib/client/right-tabs/styles.js +3 -0
  17. package/lib/client/ui/styles.generated.js +3 -0
  18. package/lib/client-composition.js +202 -191
  19. package/lib/client.js +205 -194
  20. package/lib/datasource/resolver-program.js +4 -4
  21. package/lib/environment/binding.js +16 -23
  22. package/lib/environment/doctor.js +8 -1
  23. package/lib/environment/identity.js +32 -0
  24. package/lib/environment/project-preparation.js +5 -5
  25. package/lib/environment/runtime.js +15 -16
  26. package/lib/plugin-agents.js +6 -1
  27. package/lib/plugin.js +18 -0
  28. package/lib/presentation/rpc.js +19 -0
  29. package/lib/report-publishing/adapters.js +136 -0
  30. package/lib/report-publishing/config.js +58 -0
  31. package/lib/report-publishing/credential-contract.js +25 -0
  32. package/lib/report-publishing/service.js +133 -0
  33. package/lib/skill-build.generated.js +2 -2
  34. package/lib/types/client/credentials/install.d.ts +2 -1
  35. package/lib/types/client/credentials/model.d.ts +30 -0
  36. package/lib/types/client/credentials/report-publishing.d.ts +5 -0
  37. package/lib/types/client/credentials/styles.d.ts +1 -1
  38. package/lib/types/client/i18n/copy.d.ts +21 -0
  39. package/lib/types/client/i18n/credentials.d.ts +18 -0
  40. package/lib/types/client/i18n/presentation.d.ts +24 -0
  41. package/lib/types/client/right-tabs/report-actions.d.ts +5 -1
  42. package/lib/types/client/right-tabs/report-publishing.d.ts +21 -0
  43. package/lib/types/client/right-tabs/styles.d.ts +1 -1
  44. package/lib/types/environment/identity.d.ts +6 -0
  45. package/lib/types/plugin-agents.d.ts +2 -0
  46. package/lib/types/plugin.d.ts +4 -0
  47. package/lib/types/presentation/rpc.d.ts +1358 -0
  48. package/lib/types/report-publishing/adapters.d.ts +6 -0
  49. package/lib/types/report-publishing/config.d.ts +23 -0
  50. package/lib/types/report-publishing/credential-contract.d.ts +61 -0
  51. package/lib/types/report-publishing/service.d.ts +31 -0
  52. package/package.json +4 -1
  53. package/skills/dsh-data-analysis-files/references/delivery.md +3 -1
package/README.en.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="assets/logo-horizontal.svg" alt="DSH Data Analysis" width="520">
3
+ </p>
4
+
1
5
  # @chengxianglibra/dsh-data-analysis
2
6
 
3
7
  [简体中文](README.md) | English
@@ -115,14 +119,15 @@ Analysis views use `marivo_report_write` to present conclusions, charts, exact v
115
119
  - **Ask a follow-up**: Select a chart point or supported cell in a DataFrame or Frame and add it to the question draft.
116
120
  - **Read the view**: The first preview in the foreground session opens and selects a right-hand tab. Reopen it from Chat after closing it; later updates do not repeatedly open the tab. Catalog and history entries also open on the right.
117
121
  - **Suggested next steps**: After analysis, Chat offers a row of temporary follow-up buttons. Clicking uses the native Harness composer to submit the question; existing drafts are preserved. Suggestions stay in Chat and disappear on reload, reconnection or session switch.
118
- - **Export offline**: Download the displayed version as a single HTML file from the analysis view tab. It includes the saved snapshots and supports offline viewing and local interactions; review its data before sharing.
122
+ - **Export offline**: With object storage publishing disabled, download the displayed version as a single HTML file from the actions menu. It includes the saved snapshots and supports offline viewing and local interactions; review its data before sharing.
123
+ - **Publish to object storage**: With `reportPublishing` enabled in the Harness plugin configuration, choose Publish to object storage in the actions menu to upload the displayed saved version and get its URL. The menu links to Credential management if credentials are missing. The assistant can also publish an exact saved version with `marivo_publish_report`.
119
124
  - **Delete a view**: Confirm deletion in the analysis view tab’s actions menu to permanently remove the view and all its historical versions, or ask the assistant to delete a saved view. Exported HTML and Marivo results are retained.
120
125
 
121
126
  Initial previews, revisions and final saving update the same analysis view. Subtle chart motion indicates generation and updates, then stops after saving; reduced motion preferences use a static indicator. When analysis is ongoing, the assistant shows a draft early, updates it as results become ready, and saves one version at completion. Native generation also displays program candidates. Failures and cancellation preserve the last successful content. When refreshing a saved view, the assistant retains its layout, components and body while replacing component data sources. Static text is preserved, and the layout within an auto chart may adapt to the data. Refresh rejects changes to the chart type.
122
127
 
123
128
  Analysis views support tables, complete single values, Native generation previews, and updates to an exact saved version.
124
129
  Updates reuse saved snapshots; failures and cancellation preserve the previous successful version.
125
- Frame views support charts selected from their structure. Analysis views do not provide global filters, in-page editing or object-storage publishing.
130
+ Frame views support charts selected from their structure. Analysis views do not provide global filters or in-page editing.
126
131
  View history requires current v4 pointers, but the saved Catalog build need not match the current one. The current reader validates report contents and reports unsupported content as an error. Successful reading does not guarantee that component defaults or rendering semantics match the original version. Current v2/v3 pointers (including earlier saved views) and Frame v1 are no longer readable or updatable. Files stay on disk; capture a current snapshot and create a new analysis view to continue.
127
132
 
128
133
  ## Analysis timeout
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="assets/logo-horizontal.svg" alt="DSH Data Analysis" width="520">
3
+ </p>
4
+
1
5
  # @chengxianglibra/dsh-data-analysis
2
6
 
3
7
  简体中文 | [English](README.en.md)
@@ -112,13 +116,14 @@ npx @deepseek-ai/dsh web
112
116
  - **继续提问**:在 DataFrame 或 Frame 中选择图点或支持的单元格后,将选中数据加入提问草稿。
113
117
  - **阅读视图**:前台会话首次展示视图时自动打开并选中右侧标签页。关闭后可从 Chat 入口重开;后续更新不会反复打开标签页。分析视图目录及历史入口也在右侧打开。
114
118
  - **下一步推荐**:分析完成后,Chat 中提供一排临时推荐按钮。点击会使用 Harness 原生输入框直接提交对应问题;已有草稿时不会覆盖。推荐保留在 Chat,刷新、重连或切换会话后清除。
115
- - **离线导出**:在分析视图标签页下载当前显示版本的单个 HTML,文件包含已保存快照,可离线查看并使用本地交互;分享前请确认其中的数据。
119
+ - **离线导出**:关闭对象存储发布时,可通过三点菜单下载当前显示版本的单个 HTML,文件包含已保存快照,可离线查看并使用本地交互;分享前请确认其中的数据。
120
+ - **发布到对象存储**:在 Harness 插件配置中开启 `reportPublishing` 后,三点菜单显示「发布到对象存储」,可上传当前显示的已保存版本并获取访问链接。缺凭据时可直接前往「凭证管理」。也可让助手通过 `marivo_publish_report` 发布指定版本。
116
121
  - **删除视图**:在分析视图标签页的操作菜单中确认删除,永久移除该视图及其历史版本;也可让助手删除已保存的视图。已导出的 HTML 和 Marivo 结果不随视图删除。
117
122
 
118
123
  首次预览、后续修订和正式保存始终更新同一个分析视图,用轻量图表动效显示生成与更新进度,保存后停止;开启“减少动态效果”时静态显示。需要持续计算时,助手会尽早展示草稿,并在结果就绪时更新同一草稿,完成后统一保存一个正式版本。Native 生成期间还会展示程序候选;生成失败或取消保留最后成功内容。刷新已保存视图的数据时,助手保留布局、组件和正文,逐组件替换数据源;静态正文保留原文,auto 图形内部布局可随数据变化。刷新会拒绝图表类型变化。
119
124
 
120
125
  分析视图支持表格、完整单值、Native 生成期预览和按准确版本的增量更新。更新复用已保存快照,
121
- 失败或取消保留上一成功版本。Frame 视图支持按结构选择图表;分析视图不提供全局筛选、页面编辑或对象存储发布。视图历史要求 current v4 指针,但不要求保存的 Catalog build 与当前版本相同;当前 reader 会校验报告内容,无法支持时明确报错。可读取不保证组件的默认值或渲染语义与保存时完全一致。current v2/v3(包括此前保存的新格式视图)和 Frame v1 不再读取或更新。文件保留在磁盘,需要继续使用时捕获当前快照并另建分析视图。
126
+ 失败或取消保留上一成功版本。Frame 视图支持按结构选择图表;分析视图不提供全局筛选或页面编辑。视图历史要求 current v4 指针,但不要求保存的 Catalog build 与当前版本相同;当前 reader 会校验报告内容,无法支持时明确报错。可读取不保证组件的默认值或渲染语义与保存时完全一致。current v2/v3(包括此前保存的新格式视图)和 Frame v1 不再读取或更新。文件保留在磁盘,需要继续使用时捕获当前快照并另建分析视图。
122
127
 
123
128
  ## 分析超时设置
124
129
 
@@ -0,0 +1,42 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1460" height="360" viewBox="0 0 1460 360" role="img" aria-labelledby="logo-title logo-desc">
2
+ <title id="logo-title">dsh-data-analysis</title>
3
+ <desc id="logo-desc">A black sea lion with a green M, followed by the words dsh-data-analysis.</desc>
4
+ <!-- Wordmark outlines use Montserrat Medium, Copyright 2024 The Montserrat Project Authors, SIL Open Font License 1.1. -->
5
+ <svg x="0" y="0" width="360" height="360" viewBox="0 0 512 512" aria-hidden="true">
6
+ <defs>
7
+ <mask id="face-cutouts" maskUnits="userSpaceOnUse" x="0" y="0" width="512" height="512">
8
+ <rect width="512" height="512" fill="#fff"/>
9
+ <ellipse cx="214" cy="112" rx="7" ry="9" fill="#000"/>
10
+ <ellipse cx="298" cy="112" rx="7" ry="9" fill="#000"/>
11
+ <ellipse cx="256" cy="149" rx="41" ry="33" fill="#000"/>
12
+ <g fill="none" stroke="#000" stroke-width="4.5" stroke-linecap="round">
13
+ <path d="M220 138c-11 0-21 2-30 6m30 4c-11 1-21 5-29 11"/>
14
+ <path d="M292 138c11 0 21 2 30 6m-30 4c11 1 21 5 29 11"/>
15
+ </g>
16
+ </mask>
17
+ </defs>
18
+ <path d="M256 62c-31 0-55 14-69 42-14 0-24 7-26 15-2 8 7 12 15 5-4 16-4 32-3 48 3 43-49 111-49 185 0 34-4 44-19 54-14 9-49 19-58 32-12 17 13 15 42 14 56-1 73-11 79-25 6-15 8-38 0-89 22 31 22 74 10 94 19 6 43 9 78 9s59-3 78-9c-12-20-12-63 10-94-8 51-6 74 0 89 6 14 23 24 79 25 29 1 54 3 42-14-9-13-44-23-58-32-15-10-19-20-19-54 0-74-52-142-49-185 1-16 1-32-3-48 8 7 17 3 15-5-2-8-12-15-26-15-14-28-38-42-69-42Z" fill="#111" mask="url(#face-cutouts)"/>
19
+ <path d="M241 129c0-5 7-7 15-7s15 2 15 7c0 4-5 6-9 9-3 2-4 5-6 5s-3-3-6-5c-4-3-9-5-9-9Z" fill="#111"/>
20
+ <path d="M256 141c-1 10-6 16-13 17-6 1-11-1-15-5m28-12c1 10 6 16 13 17 6 1 11-1 15-5" fill="none" stroke="#111" stroke-width="5" stroke-linecap="round"/>
21
+ <path d="M210 322v-72h19l27 32 27-32h19v72h-20v-42l-26 31-26-31v42Z" fill="#138462"/>
22
+ </svg>
23
+ <g fill="#111" transform="translate(365 214) scale(0.12 -0.12)" aria-hidden="true">
24
+ <path d="M310 -6Q233 -6 172.5 28Q112 62 77 123Q42 184 42 265Q42 346 77 406.5Q112 467 172.5 501Q233 535 310 535Q377 535 431 505Q485 475 517.5 415Q550 355 550 265Q550 175 518.5 115Q487 55 433 24.5Q379 -6 310 -6ZM318 78Q368 78 408.5 101Q449 124 472.5 166.5Q496 209 496 265Q496 322 472.5 363.5Q449 405 408.5 428Q368 451 318 451Q267 451 227 428Q187 405 163 363.5Q139 322 139 265Q139 209 163 166.5Q187 124 227 101Q267 78 318 78ZM499 0L499 143L505 266L495 389L495 742L591 742L591 0Z" transform="translate(0 0)"/>
25
+ <path d="M243 -6Q177 -6 117.5 12Q58 30 24 56L64 132Q98 109 148 93Q198 77 250 77Q317 77 346.5 96Q376 115 376 149Q376 174 358 188Q340 202 310.5 209Q281 216 245 221.5Q209 227 173 235.5Q137 244 107 259.5Q77 275 59 302.5Q41 330 41 376Q41 424 68 460Q95 496 144.5 515.5Q194 535 262 535Q314 535 367.5 522.5Q421 510 455 487L414 411Q378 435 339 444Q300 453 261 453Q198 453 167 432.5Q136 412 136 380Q136 353 154.5 338.5Q173 324 202.5 316Q232 308 268 302.5Q304 297 340 288.5Q376 280 405.5 265Q435 250 453.5 223Q472 196 472 151Q472 103 444 68Q416 33 365 13.5Q314 -6 243 -6Z" transform="translate(682 0)"/>
26
+ <path d="M91 0L91 742L187 742L187 387L168 425Q194 477 248 506Q302 535 374 535Q439 535 488.5 510Q538 485 566 434Q594 383 594 305L594 0L498 0L498 294Q498 371 460.5 410Q423 449 355 449Q304 449 266 429Q228 409 207.5 370Q187 331 187 273L187 0Z" transform="translate(1183 0)"/>
27
+ <path d="M57 233L57 316L326 316L326 233Z" transform="translate(1864 0)"/>
28
+ <path d="M310 -6Q233 -6 172.5 28Q112 62 77 123Q42 184 42 265Q42 346 77 406.5Q112 467 172.5 501Q233 535 310 535Q377 535 431 505Q485 475 517.5 415Q550 355 550 265Q550 175 518.5 115Q487 55 433 24.5Q379 -6 310 -6ZM318 78Q368 78 408.5 101Q449 124 472.5 166.5Q496 209 496 265Q496 322 472.5 363.5Q449 405 408.5 428Q368 451 318 451Q267 451 227 428Q187 405 163 363.5Q139 322 139 265Q139 209 163 166.5Q187 124 227 101Q267 78 318 78ZM499 0L499 143L505 266L495 389L495 742L591 742L591 0Z" transform="translate(2257 0)"/>
29
+ <path d="M420 0L420 112L415 133L415 324Q415 385 379.5 418.5Q344 452 273 452Q226 452 181 436.5Q136 421 105 395L65 467Q106 500 163.5 517.5Q221 535 284 535Q393 535 452 482Q511 429 511 320L511 0ZM246 -6Q187 -6 142.5 14Q98 34 74 69.5Q50 105 50 150Q50 193 70.5 228Q91 263 137.5 284Q184 305 263 305L431 305L431 236L267 236Q195 236 170 212Q145 188 145 154Q145 115 176 91.5Q207 68 262 68Q316 68 356.5 92Q397 116 415 162L434 96Q415 49 367 21.5Q319 -6 246 -6Z" transform="translate(2939 0)"/>
30
+ <path d="M273 -6Q193 -6 149 37Q105 80 105 160L105 646L201 646L201 164Q201 121 222.5 98Q244 75 284 75Q329 75 359 100L389 31Q367 12 336.5 3Q306 -6 273 -6ZM15 451L15 530L353 530L353 451Z" transform="translate(3537 0)"/>
31
+ <path d="M420 0L420 112L415 133L415 324Q415 385 379.5 418.5Q344 452 273 452Q226 452 181 436.5Q136 421 105 395L65 467Q106 500 163.5 517.5Q221 535 284 535Q393 535 452 482Q511 429 511 320L511 0ZM246 -6Q187 -6 142.5 14Q98 34 74 69.5Q50 105 50 150Q50 193 70.5 228Q91 263 137.5 284Q184 305 263 305L431 305L431 236L267 236Q195 236 170 212Q145 188 145 154Q145 115 176 91.5Q207 68 262 68Q316 68 356.5 92Q397 116 415 162L434 96Q415 49 367 21.5Q319 -6 246 -6Z" transform="translate(3931 0)"/>
32
+ <path d="M57 233L57 316L326 316L326 233Z" transform="translate(4529 0)"/>
33
+ <path d="M420 0L420 112L415 133L415 324Q415 385 379.5 418.5Q344 452 273 452Q226 452 181 436.5Q136 421 105 395L65 467Q106 500 163.5 517.5Q221 535 284 535Q393 535 452 482Q511 429 511 320L511 0ZM246 -6Q187 -6 142.5 14Q98 34 74 69.5Q50 105 50 150Q50 193 70.5 228Q91 263 137.5 284Q184 305 263 305L431 305L431 236L267 236Q195 236 170 212Q145 188 145 154Q145 115 176 91.5Q207 68 262 68Q316 68 356.5 92Q397 116 415 162L434 96Q415 49 367 21.5Q319 -6 246 -6Z" transform="translate(4912 0)"/>
34
+ <path d="M91 0L91 530L183 530L183 387L168 425Q194 477 248 506Q302 535 374 535Q439 535 488.5 510Q538 485 566 434Q594 383 594 305L594 0L498 0L498 294Q498 371 460.5 410Q423 449 355 449Q304 449 266 429Q228 409 207.5 370Q187 331 187 273L187 0Z" transform="translate(5510 0)"/>
35
+ <path d="M420 0L420 112L415 133L415 324Q415 385 379.5 418.5Q344 452 273 452Q226 452 181 436.5Q136 421 105 395L65 467Q106 500 163.5 517.5Q221 535 284 535Q393 535 452 482Q511 429 511 320L511 0ZM246 -6Q187 -6 142.5 14Q98 34 74 69.5Q50 105 50 150Q50 193 70.5 228Q91 263 137.5 284Q184 305 263 305L431 305L431 236L267 236Q195 236 170 212Q145 188 145 154Q145 115 176 91.5Q207 68 262 68Q316 68 356.5 92Q397 116 415 162L434 96Q415 49 367 21.5Q319 -6 246 -6Z" transform="translate(6191 0)"/>
36
+ <path d="M91 0L91 742L187 742L187 0Z" transform="translate(6789 0)"/>
37
+ <path d="M114 -200Q76 -200 40 -187.5Q4 -175 -22 -151L19 -79Q39 -98 63 -108Q87 -118 114 -118Q149 -118 172 -100Q195 -82 215 -36L248 37L258 49L466 530L560 530L303 -53Q280 -109 251.5 -141Q223 -173 189 -186.5Q155 -200 114 -200ZM240 -17L-2 530L98 530L304 58Z" transform="translate(7066 0)"/>
38
+ <path d="M243 -6Q177 -6 117.5 12Q58 30 24 56L64 132Q98 109 148 93Q198 77 250 77Q317 77 346.5 96Q376 115 376 149Q376 174 358 188Q340 202 310.5 209Q281 216 245 221.5Q209 227 173 235.5Q137 244 107 259.5Q77 275 59 302.5Q41 330 41 376Q41 424 68 460Q95 496 144.5 515.5Q194 535 262 535Q314 535 367.5 522.5Q421 510 455 487L414 411Q378 435 339 444Q300 453 261 453Q198 453 167 432.5Q136 412 136 380Q136 353 154.5 338.5Q173 324 202.5 316Q232 308 268 302.5Q304 297 340 288.5Q376 280 405.5 265Q435 250 453.5 223Q472 196 472 151Q472 103 444 68Q416 33 365 13.5Q314 -6 243 -6Z" transform="translate(7618 0)"/>
39
+ <path d="M91 0L91 530L187 530L187 0ZM139 632Q111 632 92.5 650Q74 668 74 694Q74 721 92.5 739Q111 757 139 757Q167 757 185.5 739.5Q204 722 204 696Q204 669 186 650.5Q168 632 139 632Z" transform="translate(8119 0)"/>
40
+ <path d="M243 -6Q177 -6 117.5 12Q58 30 24 56L64 132Q98 109 148 93Q198 77 250 77Q317 77 346.5 96Q376 115 376 149Q376 174 358 188Q340 202 310.5 209Q281 216 245 221.5Q209 227 173 235.5Q137 244 107 259.5Q77 275 59 302.5Q41 330 41 376Q41 424 68 460Q95 496 144.5 515.5Q194 535 262 535Q314 535 367.5 522.5Q421 510 455 487L414 411Q378 435 339 444Q300 453 261 453Q198 453 167 432.5Q136 412 136 380Q136 353 154.5 338.5Q173 324 202.5 316Q232 308 268 302.5Q304 297 340 288.5Q376 280 405.5 265Q435 250 453.5 223Q472 196 472 151Q472 103 444 68Q416 33 365 13.5Q314 -6 243 -6Z" transform="translate(8398 0)"/>
41
+ </g>
42
+ </svg>
@@ -0,0 +1,20 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" role="img" aria-labelledby="title desc">
2
+ <title id="title">DSH Data Analysis</title>
3
+ <desc id="desc">A front-facing black sea lion silhouette with a green M on its chest.</desc>
4
+ <defs>
5
+ <mask id="face-cutouts" maskUnits="userSpaceOnUse" x="0" y="0" width="512" height="512">
6
+ <rect width="512" height="512" fill="#fff"/>
7
+ <ellipse cx="214" cy="112" rx="7" ry="9" fill="#000"/>
8
+ <ellipse cx="298" cy="112" rx="7" ry="9" fill="#000"/>
9
+ <ellipse cx="256" cy="149" rx="41" ry="33" fill="#000"/>
10
+ <g fill="none" stroke="#000" stroke-width="4.5" stroke-linecap="round">
11
+ <path d="M220 138c-11 0-21 2-30 6m30 4c-11 1-21 5-29 11"/>
12
+ <path d="M292 138c11 0 21 2 30 6m-30 4c11 1 21 5 29 11"/>
13
+ </g>
14
+ </mask>
15
+ </defs>
16
+ <path d="M256 62c-31 0-55 14-69 42-14 0-24 7-26 15-2 8 7 12 15 5-4 16-4 32-3 48 3 43-49 111-49 185 0 34-4 44-19 54-14 9-49 19-58 32-12 17 13 15 42 14 56-1 73-11 79-25 6-15 8-38 0-89 22 31 22 74 10 94 19 6 43 9 78 9s59-3 78-9c-12-20-12-63 10-94-8 51-6 74 0 89 6 14 23 24 79 25 29 1 54 3 42-14-9-13-44-23-58-32-15-10-19-20-19-54 0-74-52-142-49-185 1-16 1-32-3-48 8 7 17 3 15-5-2-8-12-15-26-15-14-28-38-42-69-42Z" fill="#111" mask="url(#face-cutouts)"/>
17
+ <path d="M241 129c0-5 7-7 15-7s15 2 15 7c0 4-5 6-9 9-3 2-4 5-6 5s-3-3-6-5c-4-3-9-5-9-9Z" fill="#111"/>
18
+ <path d="M256 141c-1 10-6 16-13 17-6 1-11-1-15-5m28-12c1 10 6 16 13 17 6 1 11-1 15-5" fill="none" stroke="#111" stroke-width="5" stroke-linecap="round"/>
19
+ <path d="M210 322v-72h19l27 32 27-32h19v72h-20v-42l-26 31-26-31v42Z" fill="#138462"/>
20
+ </svg>
@@ -33,9 +33,41 @@ var MarivoEnvironmentError = class extends Error {
33
33
  // lib/environment/binding.js
34
34
  import { createHash } from "node:crypto";
35
35
  import { access, constants, realpath, stat } from "node:fs/promises";
36
- import path from "node:path";
36
+ import path2 from "node:path";
37
37
  import process3 from "node:process";
38
38
 
39
+ // lib/environment/identity.js
40
+ import { realpathSync } from "node:fs";
41
+ import path from "node:path";
42
+ function canonicalPythonExecutable(executable) {
43
+ const absolute = path.resolve(executable);
44
+ return path.join(realpathSync(path.dirname(absolute)), path.basename(absolute));
45
+ }
46
+ function canonicalImportIdentity(identity) {
47
+ return {
48
+ pythonExecutable: canonicalPythonExecutable(identity.pythonExecutable),
49
+ marivoVersion: identity.marivoVersion,
50
+ packagePath: realpathSync(identity.packagePath)
51
+ };
52
+ }
53
+ var PYTHON_IMPORT_IDENTITY = String.raw`
54
+ import os.path as _dsh_identity_path
55
+
56
+ def _dsh_python_executable(executable):
57
+ absolute = _dsh_identity_path.abspath(executable)
58
+ return _dsh_identity_path.join(
59
+ _dsh_identity_path.realpath(_dsh_identity_path.dirname(absolute)),
60
+ _dsh_identity_path.basename(absolute),
61
+ )
62
+
63
+ def _dsh_import_identity(executable, version, package_path):
64
+ return {
65
+ "python_executable": _dsh_python_executable(executable),
66
+ "marivo_version": version,
67
+ "package_path": _dsh_identity_path.realpath(package_path),
68
+ }
69
+ `.trim();
70
+
39
71
  // lib/environment/subprocess.js
40
72
  import { spawn } from "node:child_process";
41
73
  import process2 from "node:process";
@@ -222,30 +254,25 @@ var IDENTITY_LIMITS = Object.freeze({
222
254
  stdoutMaxBytes: 16384,
223
255
  stderrMaxBytes: 16384
224
256
  });
225
- var CHECKED_IDENTITY_PRELUDE = String.raw`
257
+ var CHECKED_IDENTITY_PRELUDE = `${PYTHON_IMPORT_IDENTITY}
258
+ ${String.raw`
226
259
  import json as _dsh_json
227
260
  import os as _dsh_os
228
261
  import sys as _dsh_sys
229
262
  try:
230
263
  import marivo as _dsh_marivo
231
- _dsh_actual = {
232
- "python_executable": _dsh_os.path.abspath(_dsh_sys.executable),
233
- "marivo_version": _dsh_marivo.__version__,
234
- "package_path": _dsh_os.path.abspath(_dsh_marivo.__file__ or ""),
235
- }
264
+ _dsh_actual = _dsh_import_identity(
265
+ _dsh_sys.executable, _dsh_marivo.__version__, _dsh_marivo.__file__,
266
+ )
236
267
  except Exception:
237
268
  raise SystemExit(78) from None
238
269
 
239
- _dsh_expected = {
240
- "python_executable": _dsh_os.path.abspath(_dsh_sys.argv[1]),
241
- "marivo_version": _dsh_sys.argv[2],
242
- "package_path": _dsh_os.path.abspath(_dsh_sys.argv[3]),
243
- }
270
+ _dsh_expected = _dsh_import_identity(*_dsh_sys.argv[1:4])
244
271
  if _dsh_actual != _dsh_expected:
245
272
  print(_dsh_json.dumps({"kind": "identity-mismatch", "actual": _dsh_actual}), file=_dsh_sys.stderr)
246
273
  raise SystemExit(78)
247
274
  _dsh_sys.argv = [_dsh_sys.argv[0], *_dsh_sys.argv[4:]]
248
- `.trim();
275
+ `.trim()}`;
249
276
  var IMPORT_IDENTITY_PROGRAM = String.raw`
250
277
  print(_dsh_json.dumps(_dsh_actual, sort_keys=True))
251
278
  `.trim();
@@ -303,9 +330,6 @@ function redactSubprocessOutput(result, environmentOverlay, secretValues = []) {
303
330
  stderr: Buffer.from(redactText(result.stderr.toString("utf8")))
304
331
  };
305
332
  }
306
- function normalizeAbsolute(value) {
307
- return path.normalize(path.resolve(value));
308
- }
309
333
  async function assertExecutable(executable) {
310
334
  try {
311
335
  const info = await stat(executable);
@@ -317,12 +341,12 @@ async function assertExecutable(executable) {
317
341
  }
318
342
  }
319
343
  async function resolvePythonExecutable(projectRoot, configured) {
320
- if (configured !== void 0 && !path.isAbsolute(configured)) {
344
+ if (configured !== void 0 && !path2.isAbsolute(configured)) {
321
345
  throw new MarivoEnvironmentError("python-path-relative", `Configured Marivo Python executable must be absolute: ${configured}`, { pythonExecutable: configured });
322
346
  }
323
- const executable = configured === void 0 ? path.join(projectRoot, process3.platform === "win32" ? ".venv/Scripts/python.exe" : ".venv/bin/python") : path.normalize(configured);
347
+ const executable = configured === void 0 ? path2.join(projectRoot, process3.platform === "win32" ? ".venv/Scripts/python.exe" : ".venv/bin/python") : path2.normalize(configured);
324
348
  await assertExecutable(executable);
325
- return executable;
349
+ return canonicalPythonExecutable(executable);
326
350
  }
327
351
  function fingerprint(binding) {
328
352
  const payload = [
@@ -412,8 +436,9 @@ ${request.program}`,
412
436
  });
413
437
  }
414
438
  try {
415
- const identity = parseImportIdentity(result.stdout);
416
- if (normalizeAbsolute(identity.pythonExecutable) !== normalizeAbsolute(this.binding.pythonExecutable) || identity.marivoVersion !== this.binding.marivoVersion || normalizeAbsolute(identity.packagePath) !== normalizeAbsolute(this.binding.packagePath))
439
+ const identity = canonicalImportIdentity(parseImportIdentity(result.stdout));
440
+ const expected = this.binding;
441
+ if (identity.pythonExecutable !== expected.pythonExecutable || identity.marivoVersion !== expected.marivoVersion || identity.packagePath !== expected.packagePath)
417
442
  throw new Error("identity values differ from binding");
418
443
  return identity;
419
444
  } catch (cause) {
@@ -428,9 +453,7 @@ function createRuntimeRunner(projectRoot, identity, environment) {
428
453
  const subprocessPolicy = new FixedSubprocessPolicy(projectRoot, environment);
429
454
  const partialBinding = {
430
455
  projectRoot,
431
- pythonExecutable: identity.pythonExecutable,
432
- marivoVersion: identity.marivoVersion,
433
- packagePath: identity.packagePath,
456
+ ...canonicalImportIdentity(identity),
434
457
  subprocessPolicyId: subprocessPolicy.id
435
458
  };
436
459
  return new MarivoEnvironment({ ...partialBinding, fingerprint: fingerprint(partialBinding) }, subprocessPolicy);
@@ -440,7 +463,7 @@ async function bindSharedMarivoWorkspace(projectRoot, runtime) {
440
463
  const runner = createRuntimeRunner(projectRoot, {
441
464
  pythonExecutable,
442
465
  marivoVersion: runtime.marivoVersion,
443
- packagePath: normalizeAbsolute(runtime.packagePath)
466
+ packagePath: runtime.packagePath
444
467
  });
445
468
  const result = await runner.runChecked({
446
469
  program: PROJECT_CONFIG_PROGRAM,
@@ -461,7 +484,7 @@ async function bindSharedMarivoWorkspace(projectRoot, runtime) {
461
484
  import { randomUUID } from "node:crypto";
462
485
  import { constants as constants2 } from "node:fs";
463
486
  import { access as access2, chmod, cp, lstat, mkdir, readFile, realpath as realpath2, rename, rm, stat as stat2, writeFile } from "node:fs/promises";
464
- import path2 from "node:path";
487
+ import path3 from "node:path";
465
488
  import process4 from "node:process";
466
489
 
467
490
  // ../../node_modules/@deepseek-ai/dsh-home-paths/lib/index.js
@@ -475,10 +498,10 @@ var DEFAULT_AGENTS_HOME_DISPLAY = `~/${AGENTS_HOME_DIR_NAME}`;
475
498
  function defaultDshHome() {
476
499
  return join(homedir(), DSH_HOME_DIR_NAME);
477
500
  }
478
- function expandHomePath(path4) {
479
- if (path4 === "~") return homedir();
480
- if (path4.startsWith("~/") || path4.startsWith("~\\")) return join(homedir(), path4.slice(2));
481
- return path4;
501
+ function expandHomePath(path5) {
502
+ if (path5 === "~") return homedir();
503
+ if (path5.startsWith("~/") || path5.startsWith("~\\")) return join(homedir(), path5.slice(2));
504
+ return path5;
482
505
  }
483
506
  function resolveRoot(configured, env, envName, fallback) {
484
507
  const fromEnv = env[envName];
@@ -498,7 +521,8 @@ var INSTALLATION_SCHEMA = RUNTIME_INSTALLATION_VERSION;
498
521
  var INSTALLATION_FILENAME = "installation.json";
499
522
  var SKILL_NAMES = ["marivo-analysis", "marivo-semantic"];
500
523
  var SERVICE_SKILL = "marivo-service";
501
- var PROBE_SCRIPT = String.raw`
524
+ var PROBE_SCRIPT = `${PYTHON_IMPORT_IDENTITY}
525
+ ${String.raw`
502
526
  import json
503
527
  import os
504
528
  import sys
@@ -515,22 +539,21 @@ print(json.dumps({
515
539
  "marivo_distribution_version": dist.version,
516
540
  "marivo_index_install": dist.read_text("direct_url.json") is None,
517
541
  "marivo_import_identity": os.path.realpath(marivo.__file__) == os.path.realpath(dist.locate_file("marivo/__init__.py")),
518
- "python_executable": os.path.abspath(sys.executable),
519
- "marivo_version": marivo.__version__,
520
- "package_path": os.path.abspath(marivo.__file__ or ""),
542
+ **_dsh_import_identity(sys.executable, marivo.__version__, marivo.__file__),
521
543
  }, sort_keys=True))
522
- `.trim();
523
- var PYTHON_VERSION_SCRIPT = String.raw`
544
+ `.trim()}`;
545
+ var PYTHON_VERSION_SCRIPT = `${PYTHON_IMPORT_IDENTITY}
546
+ ${String.raw`
524
547
  import json
525
548
  import os
526
549
  import sys
527
550
 
528
551
  print(json.dumps({
529
- "python_executable": os.path.abspath(sys.executable),
552
+ "python_executable": _dsh_python_executable(sys.executable),
530
553
  "version": list(sys.version_info[:3]),
531
554
  "prefix": os.path.abspath(sys.prefix),
532
555
  }, sort_keys=True))
533
- `.trim();
556
+ `.trim()}`;
534
557
  function administratorRepair(executable) {
535
558
  const commands = [
536
559
  {
@@ -550,17 +573,17 @@ function positiveTimeout(value) {
550
573
  }
551
574
  return resolved;
552
575
  }
553
- function normalizeAbsolute2(name, value) {
554
- if (!path2.isAbsolute(value)) {
576
+ function normalizeAbsolute(name, value) {
577
+ if (!path3.isAbsolute(value)) {
555
578
  throw new MarivoEnvironmentError("shared-runtime-config-invalid", `${name} must be an absolute path: ${value}`, { [name]: value });
556
579
  }
557
- return path2.normalize(value);
580
+ return path3.normalize(value);
558
581
  }
559
582
  function defaultRuntimeRoot() {
560
- return path2.join(resolveDshHome(), "dsh-data-analysis", "runtimes", "marivo");
583
+ return path3.join(resolveDshHome(), "dsh-data-analysis", "runtimes", "marivo");
561
584
  }
562
585
  function venvPython(runtimeRoot) {
563
- return path2.join(runtimeRoot, ".venv", process4.platform === "win32" ? "Scripts/python.exe" : "bin/python");
586
+ return path3.join(runtimeRoot, ".venv", process4.platform === "win32" ? "Scripts/python.exe" : "bin/python");
564
587
  }
565
588
  function boundedText(buffer, max = 4e3) {
566
589
  return buffer.toString("utf8").slice(0, max);
@@ -577,13 +600,13 @@ function requireSuccess(stage, result) {
577
600
  });
578
601
  }
579
602
  async function assertExecutable2(executable) {
580
- const selected = path2.normalize(path2.resolve(executable));
603
+ const selected = path3.normalize(path3.resolve(executable));
581
604
  try {
582
605
  const info = await stat2(selected);
583
606
  if (!info.isFile())
584
607
  throw new Error("not a file");
585
608
  await access2(selected, process4.platform === "win32" ? constants2.F_OK : constants2.X_OK);
586
- return selected;
609
+ return canonicalPythonExecutable(selected);
587
610
  } catch (cause) {
588
611
  throw new MarivoEnvironmentError("python-unavailable", `Shared Marivo Python executable is missing or not executable: ${selected}`, { pythonExecutable: selected }, { cause });
589
612
  }
@@ -624,8 +647,8 @@ async function probeRuntime(runtimeRoot, executable, environment, timeoutMs, adm
624
647
  if (probe.marivo_distribution_version !== probe.marivo_version || probe.marivo_import_identity !== true || typeof probe.python_executable !== "string" || typeof probe.marivo_version !== "string" || probe.marivo_version.length === 0 || typeof probe.package_path !== "string" || probe.package_path.length === 0) {
625
648
  throw new MarivoEnvironmentError("shared-runtime-identity-mismatch", "Shared Runtime returned an incomplete or mismatched package identity", { probe, ...repair === void 0 ? {} : { repairCommands: repair.commands } });
626
649
  }
627
- const actualPython = await realpath2(path2.resolve(probe.python_executable));
628
- const selectedPython = await realpath2(canonical);
650
+ const actualPython = canonicalPythonExecutable(probe.python_executable);
651
+ const selectedPython = canonicalPythonExecutable(canonical);
629
652
  if (expectedMarivoVersion !== void 0 && probe.marivo_version !== expectedMarivoVersion) {
630
653
  throw new MarivoEnvironmentError("shared-runtime-version-unsupported", `Shared Marivo runtime version ${probe.marivo_version} is unsupported; install ${expectedMarivoVersion}.${repair === void 0 ? "" : ` ${repair.message}`}`, {
631
654
  supportedMarivoVersion: expectedMarivoVersion,
@@ -647,12 +670,12 @@ async function probeRuntime(runtimeRoot, executable, environment, timeoutMs, adm
647
670
  marivo_import_identity: true,
648
671
  python_executable: canonical,
649
672
  marivo_version: probe.marivo_version,
650
- package_path: path2.resolve(probe.package_path)
673
+ package_path: await realpath2(probe.package_path)
651
674
  };
652
675
  }
653
676
  async function availableSkills(skillsRoot) {
654
677
  try {
655
- await lstat(path2.join(skillsRoot, SERVICE_SKILL));
678
+ await lstat(path3.join(skillsRoot, SERVICE_SKILL));
656
679
  return [...SKILL_NAMES, SERVICE_SKILL];
657
680
  } catch (error) {
658
681
  if (error.code === "ENOENT")
@@ -662,7 +685,7 @@ async function availableSkills(skillsRoot) {
662
685
  }
663
686
  async function validateSkills(skillsRoot, skills) {
664
687
  for (const skill of skills) {
665
- const skillFile = path2.join(skillsRoot, skill, "SKILL.md");
688
+ const skillFile = path3.join(skillsRoot, skill, "SKILL.md");
666
689
  try {
667
690
  if (!(await stat2(skillFile)).isFile())
668
691
  throw new Error("not a file");
@@ -682,16 +705,16 @@ async function validateSkills(skillsRoot, skills) {
682
705
  }
683
706
  }
684
707
  async function syncSkills(runtimeRoot, packagePath) {
685
- const sourceRoot = path2.join(path2.dirname(packagePath), "skills");
708
+ const sourceRoot = path3.join(path3.dirname(packagePath), "skills");
686
709
  const skills = await availableSkills(sourceRoot);
687
710
  await validateSkills(sourceRoot, skills);
688
- const skillsRoot = path2.join(runtimeRoot, "skills");
689
- const staging = path2.join(runtimeRoot, `.skills-staging-${randomUUID()}`);
690
- const previous = path2.join(runtimeRoot, `.skills-previous-${randomUUID()}`);
711
+ const skillsRoot = path3.join(runtimeRoot, "skills");
712
+ const staging = path3.join(runtimeRoot, `.skills-staging-${randomUUID()}`);
713
+ const previous = path3.join(runtimeRoot, `.skills-previous-${randomUUID()}`);
691
714
  await mkdir(staging, { recursive: true });
692
715
  try {
693
716
  for (const skill of skills) {
694
- await cp(path2.join(sourceRoot, skill), path2.join(staging, skill), { recursive: true });
717
+ await cp(path3.join(sourceRoot, skill), path3.join(staging, skill), { recursive: true });
695
718
  }
696
719
  await validateSkills(staging, skills);
697
720
  let hadPrevious = false;
@@ -718,7 +741,7 @@ async function syncSkills(runtimeRoot, packagePath) {
718
741
  }
719
742
  async function readInstallation(runtimeRoot) {
720
743
  try {
721
- const value = JSON.parse(await readFile(path2.join(runtimeRoot, INSTALLATION_FILENAME), "utf8"));
744
+ const value = JSON.parse(await readFile(path3.join(runtimeRoot, INSTALLATION_FILENAME), "utf8"));
722
745
  if (typeof value !== "object" || value === null || Array.isArray(value))
723
746
  return void 0;
724
747
  const record = value;
@@ -738,8 +761,8 @@ async function readInstallation(runtimeRoot) {
738
761
  }
739
762
  }
740
763
  async function writeInstallation(runtimeRoot, record) {
741
- const installationPath = path2.join(runtimeRoot, INSTALLATION_FILENAME);
742
- const temporary = path2.join(runtimeRoot, `.installation-${randomUUID()}.json`);
764
+ const installationPath = path3.join(runtimeRoot, INSTALLATION_FILENAME);
765
+ const temporary = path3.join(runtimeRoot, `.installation-${randomUUID()}.json`);
743
766
  await writeFile(temporary, `${JSON.stringify(record, void 0, 2)}
744
767
  `, {
745
768
  encoding: "utf8",
@@ -757,17 +780,17 @@ async function validatedExisting(runtimeRoot, configuredPython, environment, tim
757
780
  if (record.marivoVersion !== PINNED_MARIVO_VERSION)
758
781
  return void 0;
759
782
  const expectedPython = configuredPython ?? venvPython(runtimeRoot);
760
- const expectedSkillsRoot = path2.join(runtimeRoot, "skills");
761
- if (path2.normalize(record.skillsRoot) !== path2.normalize(expectedSkillsRoot))
762
- return void 0;
783
+ const expectedSkillsRoot = path3.join(runtimeRoot, "skills");
763
784
  try {
785
+ if (await realpath2(record.skillsRoot) !== await realpath2(expectedSkillsRoot))
786
+ return void 0;
764
787
  const selectedPython = await assertExecutable2(expectedPython);
765
- if (path2.normalize(record.pythonExecutable) !== selectedPython)
788
+ if (canonicalPythonExecutable(record.pythonExecutable) !== selectedPython)
766
789
  return void 0;
767
790
  const probe = await probeRuntime(runtimeRoot, selectedPython, environment, timeoutMs, configuredPython !== void 0, record.marivoVersion);
768
- if (path2.normalize(probe.package_path) !== path2.normalize(record.packagePath))
791
+ if (probe.package_path !== await realpath2(record.packagePath))
769
792
  return void 0;
770
- const sourceRoot = path2.join(path2.dirname(probe.package_path), "skills");
793
+ const sourceRoot = path3.join(path3.dirname(probe.package_path), "skills");
771
794
  await validateSkills(record.skillsRoot, await availableSkills(sourceRoot));
772
795
  return {
773
796
  runtimeRoot,
@@ -775,7 +798,7 @@ async function validatedExisting(runtimeRoot, configuredPython, environment, tim
775
798
  marivoVersion: probe.marivo_version,
776
799
  packagePath: probe.package_path,
777
800
  skillsRoot: record.skillsRoot,
778
- installationPath: path2.join(runtimeRoot, INSTALLATION_FILENAME)
801
+ installationPath: path3.join(runtimeRoot, INSTALLATION_FILENAME)
779
802
  };
780
803
  } catch {
781
804
  return void 0;
@@ -786,7 +809,7 @@ async function acquireLock(lockPath, timeoutMs, waitIntervalMs) {
786
809
  while (true) {
787
810
  try {
788
811
  await mkdir(lockPath);
789
- await writeFile(path2.join(lockPath, "owner.json"), `${JSON.stringify({ pid: process4.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() })}
812
+ await writeFile(path3.join(lockPath, "owner.json"), `${JSON.stringify({ pid: process4.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() })}
790
813
  `);
791
814
  return async () => {
792
815
  await rm(lockPath, { recursive: true, force: true });
@@ -818,7 +841,7 @@ async function acquireLock(lockPath, timeoutMs, waitIntervalMs) {
818
841
  }
819
842
  async function lockOwnerAlive(lockPath) {
820
843
  try {
821
- const owner = JSON.parse(await readFile(path2.join(lockPath, "owner.json"), "utf8"));
844
+ const owner = JSON.parse(await readFile(path3.join(lockPath, "owner.json"), "utf8"));
822
845
  if (!Number.isSafeInteger(owner.pid) || owner.pid < 1)
823
846
  return false;
824
847
  try {
@@ -859,13 +882,13 @@ async function installManagedRuntime(runtimeRoot, bootstrapPythonExecutable, env
859
882
  if (!Array.isArray(version.version) || typeof version.version[0] !== "number" || typeof version.version[1] !== "number" || version.version[0] < 3 || version.version[0] === 3 && version.version[1] < 10) {
860
883
  throw new MarivoEnvironmentError("shared-runtime-install-failed", `Local Python does not satisfy Marivo >=${SHARED_PYTHON_SPEC}; detected ${Array.isArray(version.version) ? version.version.join(".") : "unknown"}`, { version: version.version, pythonExecutable: bootstrapPythonExecutable });
861
884
  }
862
- if (typeof version.python_executable !== "string" || !path2.isAbsolute(version.python_executable)) {
885
+ if (typeof version.python_executable !== "string" || !path3.isAbsolute(version.python_executable)) {
863
886
  throw new MarivoEnvironmentError("shared-runtime-install-failed", "Local Python returned a non-absolute interpreter path");
864
887
  }
865
888
  const canonicalPython = await assertExecutable2(version.python_executable);
866
889
  requireSuccess("create shared virtual environment (requires Python venv/ensurepip; install python3-venv on Debian/Ubuntu)", await policy.run({
867
890
  executable: canonicalPython,
868
- args: ["-m", "venv", path2.join(runtimeRoot, ".venv")],
891
+ args: ["-m", "venv", path3.join(runtimeRoot, ".venv")],
869
892
  limits
870
893
  }));
871
894
  const executable = venvPython(runtimeRoot);
@@ -877,16 +900,16 @@ async function installManagedRuntime(runtimeRoot, bootstrapPythonExecutable, env
877
900
  return probeRuntime(runtimeRoot, executable, environment, timeoutMs, false, PINNED_MARIVO_VERSION);
878
901
  }
879
902
  async function ensureSharedMarivoRuntime(config = {}, options = {}) {
880
- const runtimeRoot = config.runtimeRoot === void 0 ? defaultRuntimeRoot() : normalizeAbsolute2("runtimeRoot", config.runtimeRoot);
881
- const configuredPython = config.pythonExecutable === void 0 ? void 0 : normalizeAbsolute2("pythonExecutable", config.pythonExecutable);
882
- const bootstrapPythonExecutable = config.bootstrapPythonExecutable === void 0 ? process4.platform === "win32" ? "python" : "python3" : normalizeAbsolute2("bootstrapPythonExecutable", config.bootstrapPythonExecutable);
903
+ const runtimeRoot = config.runtimeRoot === void 0 ? defaultRuntimeRoot() : normalizeAbsolute("runtimeRoot", config.runtimeRoot);
904
+ const configuredPython = config.pythonExecutable === void 0 ? void 0 : normalizeAbsolute("pythonExecutable", config.pythonExecutable);
905
+ const bootstrapPythonExecutable = config.bootstrapPythonExecutable === void 0 ? process4.platform === "win32" ? "python" : "python3" : normalizeAbsolute("bootstrapPythonExecutable", config.bootstrapPythonExecutable);
883
906
  const timeoutMs = positiveTimeout(config.installTimeoutMs);
884
907
  const existing = await validatedExisting(runtimeRoot, configuredPython, options.environment, timeoutMs);
885
908
  if (existing !== void 0)
886
909
  return existing;
887
910
  if (options.existingOnly)
888
911
  throw new MarivoEnvironmentError("shared-runtime-installation-required", "A valid shared Marivo Runtime installation is required");
889
- await mkdir(path2.dirname(runtimeRoot), { recursive: true });
912
+ await mkdir(path3.dirname(runtimeRoot), { recursive: true });
890
913
  const lockPath = `${runtimeRoot}.install-lock`;
891
914
  const release = await acquireLock(lockPath, timeoutMs, options.waitIntervalMs ?? 100);
892
915
  try {
@@ -920,7 +943,7 @@ async function ensureSharedMarivoRuntime(config = {}, options = {}) {
920
943
 
921
944
  // lib/environment/workspace.js
922
945
  import { realpath as realpath3, stat as stat3 } from "node:fs/promises";
923
- import path3 from "node:path";
946
+ import path4 from "node:path";
924
947
 
925
948
  // lib/lifecycle.js
926
949
  var PendingTasks = class {
@@ -968,12 +991,12 @@ async function assertDirectory(target, label) {
968
991
  async function canonicalWorkspaceRoot(projectRoot) {
969
992
  let canonicalRoot;
970
993
  try {
971
- canonicalRoot = await realpath3(path3.resolve(projectRoot));
994
+ canonicalRoot = await realpath3(path4.resolve(projectRoot));
972
995
  await assertDirectory(canonicalRoot, "root");
973
996
  } catch (cause) {
974
997
  if (cause instanceof MarivoEnvironmentError)
975
998
  throw cause;
976
- throw new MarivoEnvironmentError("project-root-invalid", `Marivo project root is not an existing directory: ${path3.resolve(projectRoot)}`, { projectRoot: path3.resolve(projectRoot) }, { cause });
999
+ throw new MarivoEnvironmentError("project-root-invalid", `Marivo project root is not an existing directory: ${path4.resolve(projectRoot)}`, { projectRoot: path4.resolve(projectRoot) }, { cause });
977
1000
  }
978
1001
  return canonicalRoot;
979
1002
  }