@rho-workspace/rho 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -5
- package/dist/browser-access.js +3 -2
- package/dist/capabilities.d.ts +29 -2
- package/dist/capabilities.js +79 -2
- package/dist/cli.js +237 -99
- package/dist/contract.generated.d.ts +441 -16
- package/dist/contract.generated.js +1 -1
- package/dist/contracts/owners.generated.js +459 -10
- package/dist/conversation-presentation.d.ts +3 -1
- package/dist/conversation-presentation.js +16 -6
- package/dist/conversations.d.ts +10 -6
- package/dist/conversations.js +17 -13
- package/dist/discovery.d.ts +1 -0
- package/dist/discovery.js +3 -3
- package/dist/extension-contract.d.ts +99 -0
- package/dist/extension-contract.js +52 -0
- package/dist/extension-directory.d.ts +9 -0
- package/dist/extension-directory.js +11 -0
- package/dist/extension-worker.d.ts +1 -0
- package/dist/extension-worker.js +36 -0
- package/dist/extensions.d.ts +157 -0
- package/dist/extensions.js +349 -0
- package/dist/installs.d.ts +11 -0
- package/dist/installs.js +179 -0
- package/dist/plugin-cli.d.ts +1 -0
- package/dist/plugin-cli.js +139 -0
- package/dist/plugin-inventory.json +18 -0
- package/dist/plugins.d.ts +3 -3
- package/dist/project-notes.js +2 -0
- package/dist/project-work.d.ts +3 -1
- package/dist/project-work.js +15 -2
- package/dist/protocol.d.ts +7 -0
- package/dist/protocol.js +11 -7
- package/dist/screen-check-skill.d.ts +2 -0
- package/dist/screen-check-skill.js +39 -0
- package/dist/server.d.ts +6 -1
- package/dist/server.js +105 -15
- package/dist/web/.vite/manifest.json +65 -65
- package/dist/web/assets/{canvas-BXhC8RDZ.js → canvas-D8rDdfDz.js} +1 -1
- package/dist/web/assets/choices-b0XhrGQV.js +1 -0
- package/dist/web/assets/{console-e1P5qYQ5.js → console-BSjLGe0T.js} +1 -1
- package/dist/web/assets/{dist-Dd3iAGFy.js → dist-Buk0lwzI.js} +2 -2
- package/dist/web/assets/dist-Cw8hRIlT.js +1 -0
- package/dist/web/assets/dist-Tzoe_zm-.js +3 -0
- package/dist/web/assets/{dist-DEorbOW0.js → dist-foH9IKc6.js} +1 -1
- package/dist/web/assets/explore-in-BVUy3ZW_.js +1 -0
- package/dist/web/assets/graph-1OdIJ9l7.js +2 -0
- package/dist/web/assets/index-CsC3sTRq.css +1 -0
- package/dist/web/assets/{index-BYuKpyoi.js → index-CxmItXqa.js} +220 -121
- package/dist/web/assets/{notes-meaning-B3HFbh84.js → notes-meaning-pVbbQUGP.js} +1 -1
- package/dist/web/assets/overview-B9UtVJ32.js +6 -0
- package/dist/web/assets/preview-CksD_5KS.js +1 -0
- package/dist/web/assets/{review-BVh9jWMB.js → review-CMxQlQ-C.js} +1 -1
- package/dist/web/assets/{skills-DPaEYkln.js → skills-BFNSyrnD.js} +1 -1
- package/dist/web/assets/{skills-work-dvjfy_tu.js → skills-work-BGPSnF_I.js} +1 -1
- package/dist/web/assets/{studio-process-BYm8mjRJ.js → studio-process-CsRoTHOh.js} +2 -2
- package/dist/web/assets/{timeline-events-CDZ7wiJX.js → timeline-events-BbuuFWHV.js} +1 -1
- package/dist/web/assets/{work-turns-BeCt303c.js → work-turns-BvJlIMmA.js} +1 -1
- package/dist/web/assets/workspace-CWlkjLNx.js +5 -0
- package/dist/web/composition.json +80 -80
- package/dist/web/index.html +3 -3
- package/dist/web/renderer.json +1 -1
- package/dist/web-assets.d.ts +2 -1
- package/dist/web-assets.js +3 -2
- package/dist/work-contract.js +2 -2
- package/docs/FRONTEND.md +30 -14
- package/docs/HEADLESS.md +60 -14
- package/docs/PLUGIN-AUTHORING.md +4 -0
- package/docs/THIRD-PARTY-PLUGINS.md +190 -0
- package/example/pa14-qs/.rho/rho/conversations/c-67f77cfebaba2cea.json +1 -1
- package/example/pa14-qs/.rho/rho/conversations/c-91aa2e46b1ac1a6c.json +1 -1
- package/example/pa14-qs/.rho/rho/conversations/c-fa24dfd80dfe4bcb.json +1 -1
- package/node_modules/@hono/node-server/dist/index.js +11 -0
- package/node_modules/@hono/node-server/dist/index.mjs +11 -0
- package/node_modules/@hono/node-server/dist/listener.js +11 -0
- package/node_modules/@hono/node-server/dist/listener.mjs +11 -0
- package/node_modules/@hono/node-server/dist/request.js +11 -0
- package/node_modules/@hono/node-server/dist/request.mjs +11 -0
- package/node_modules/@hono/node-server/dist/server.js +11 -0
- package/node_modules/@hono/node-server/dist/server.mjs +11 -0
- package/node_modules/@hono/node-server/dist/vercel.js +11 -0
- package/node_modules/@hono/node-server/dist/vercel.mjs +11 -0
- package/node_modules/@hono/node-server/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-agent/README.md +42 -1
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-conversation.js +14 -3
- package/node_modules/@rho-workspace/plugin-agent/dist/activity-normalize.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/activity-normalize.js +2 -2
- package/node_modules/@rho-workspace/plugin-agent/dist/conversation.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/conversation.js +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/dsh-session.d.ts +6 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/dsh-session.js +174 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.d.ts +82 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.js +15 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.d.ts +73 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.js +190 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.d.ts +11 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.js +7 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/index.d.ts +373 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/index.js +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/mcp.js +5 -1
- package/node_modules/@rho-workspace/plugin-agent/dist/operations.d.ts +33 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/operations.js +3 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/presentation.d.ts +14 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/presentation.js +2 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/ui-external-session.d.ts +15 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/ui-external-session.js +37 -0
- package/node_modules/@rho-workspace/plugin-agent/package.json +5 -1
- package/node_modules/@rho-workspace/plugin-annotations/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-documents/README.md +4 -0
- package/node_modules/@rho-workspace/plugin-documents/dist/index.d.ts +3 -0
- package/node_modules/@rho-workspace/plugin-documents/dist/index.js +1 -1
- package/node_modules/@rho-workspace/plugin-documents/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/README.md +71 -10
- package/node_modules/@rho-workspace/plugin-kernel/dist/code.d.ts +2 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/code.js +4 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/contract.js +5 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/index.d.ts +7 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/index.js +55 -8
- package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.d.ts +9 -6
- package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.js +18 -41
- package/node_modules/@rho-workspace/plugin-kernel/dist/operations.js +5 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.d.ts +39 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.js +661 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.d.ts +3 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.js +10 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.d.ts +38 -4
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.js +194 -97
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook-work.js +1 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.d.ts +10 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.js +79 -18
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-styles.js +115 -45
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui.d.ts +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui.js +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.d.ts +63 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.js +344 -0
- package/node_modules/@rho-workspace/plugin-kernel/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/runners/runner.R +6 -3
- package/node_modules/@rho-workspace/plugin-kernel/runners/runner.py +6 -3
- package/node_modules/express-rate-limit/dist/index.cjs +1 -1
- package/node_modules/express-rate-limit/dist/index.mjs +1 -1
- package/node_modules/express-rate-limit/package.json +1 -1
- package/node_modules/ip-address/dist/ipv6.js +1 -1
- package/node_modules/ip-address/package.json +1 -1
- package/node_modules/playwright-core/LICENSE +202 -0
- package/node_modules/playwright-core/NOTICE +5 -0
- package/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
- package/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
- package/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/browsers.json +52 -0
- package/node_modules/playwright-core/cli.js +22 -0
- package/node_modules/playwright-core/index.js +17 -0
- package/node_modules/playwright-core/index.mjs +28 -0
- package/node_modules/playwright-core/lib/bootstrap.js +149 -0
- package/node_modules/playwright-core/lib/coreBundle.js +78041 -0
- package/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
- package/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
- package/node_modules/playwright-core/lib/entry/mcp.js +10 -0
- package/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
- package/node_modules/playwright-core/lib/package.js +50 -0
- package/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/server/electron/loader.js +123 -0
- package/node_modules/playwright-core/lib/serverRegistry.js +1939 -0
- package/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +65 -0
- package/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
- package/node_modules/playwright-core/lib/tools/cli-client/cli.js +7 -0
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +808 -0
- package/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
- package/node_modules/playwright-core/lib/tools/cli-client/output.js +352 -0
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +422 -0
- package/node_modules/playwright-core/lib/tools/cli-client/registry.js +180 -0
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +261 -0
- package/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +112 -0
- package/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
- package/node_modules/playwright-core/lib/utilsBundle.js +86078 -0
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +1890 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule-CQ8RZGBm.js +32 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-AwgwcYie.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-hRvO4yU2.js +12 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DK72OMRi.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-BZpYJZ-P.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-Cn0mmDuv.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BEB2e8M3.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-D-g2vrvf.js +184 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-B794rD2Z.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.C8BeHuxd.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.oNry3Hjv.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CYwC_gH1.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CyMfwkXJ.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +1267 -0
- package/node_modules/playwright-core/package.json +34 -0
- package/package.json +13 -7
- package/protocol/index.json +75 -21
- package/protocol/operations/agent.sessions.list.json +100 -0
- package/protocol/operations/agent.sessions.read.json +266 -0
- package/protocol/operations/conversation.settings.check.json +147 -4
- package/protocol/operations/conversation.settings.read.json +145 -3
- package/protocol/operations/conversation.settings.update.json +166 -4
- package/protocol/operations/conversation.start.json +2 -1
- package/protocol/operations/extension.call.json +132 -0
- package/protocol/operations/extension.content.json +78 -0
- package/protocol/operations/extension.describe.json +195 -0
- package/protocol/operations/extension.install.json +198 -0
- package/protocol/operations/extension.list.json +123 -0
- package/protocol/operations/extension.manage.json +55 -0
- package/protocol/operations/extension.read.json +112 -0
- package/protocol/operations/kernel.interrupt.json +20 -0
- package/protocol/operations/kernel.read.json +30 -0
- package/protocol/operations/kernel.run.json +36 -1
- package/protocol/operations/kernel.stop.json +10 -0
- package/protocol/operations/project.work.read.json +3 -2
- package/protocol/operations/session.grant.json +9 -0
- package/scripts/browser-checks.mjs +53 -0
- package/scripts/screen-check.mjs +193 -0
- package/scripts/third-party-acceptance.mjs +77 -0
- package/vendor/composition.json +111 -111
- package/vendor/rho-workspace-plugin-agent-0.4.29.tgz +0 -0
- package/vendor/rho-workspace-plugin-annotations-0.5.2.tgz +0 -0
- package/vendor/rho-workspace-plugin-documents-0.4.11.tgz +0 -0
- package/vendor/rho-workspace-plugin-kernel-0.5.5.tgz +0 -0
- package/web/theme.css +17 -2
- package/dist/web/assets/choices-B-y2OYvE.js +0 -1
- package/dist/web/assets/dist-CBM2oq6g.js +0 -1
- package/dist/web/assets/dist-CH56yNDn.js +0 -3
- package/dist/web/assets/explore-in-muWXOGOY.js +0 -1
- package/dist/web/assets/graph-CK-df5lh.js +0 -2
- package/dist/web/assets/index-DBd77u4a.css +0 -1
- package/dist/web/assets/overview-D1Ev2OC_.js +0 -6
- package/dist/web/assets/preview-HbmQKUGY.js +0 -1
- package/dist/web/assets/workspace-d5Hw_ClZ.js +0 -5
- package/vendor/rho-workspace-plugin-agent-0.4.27.tgz +0 -0
- package/vendor/rho-workspace-plugin-documents-0.4.10.tgz +0 -0
- package/vendor/rho-workspace-plugin-kernel-0.5.2.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -10,12 +10,41 @@ function openBrowser(target) {
|
|
|
10
10
|
const [command, args] = process.platform === 'darwin' ? ['open', [target]] : process.platform === 'win32' ? ['cmd', ['/c', 'start', '""', target]] : ['xdg-open', [target]];
|
|
11
11
|
spawn(command, args, { stdio: 'ignore', detached: true }).on('error', () => process.stderr.write('Could not open a browser. Open openUrl from the output instead.\n')).unref();
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
// A generic ACP agent is any program that speaks ACP v1 over stdio. Its command is either a
|
|
14
|
+
// JSON argv array (safest for paths with spaces) or a simple command line.
|
|
15
|
+
function parseAcpCommand(spec) {
|
|
16
|
+
const text = (spec ?? '').trim();
|
|
17
|
+
if (!text)
|
|
18
|
+
throw new Error('--agent-backend acp needs --acp-command: a JSON argv array such as \'["/usr/bin/node","/path/agent.js"]\', or a command line.');
|
|
19
|
+
if (text.startsWith('[')) {
|
|
20
|
+
let parts;
|
|
21
|
+
try {
|
|
22
|
+
parts = JSON.parse(text);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error('--acp-command is not valid JSON.');
|
|
26
|
+
}
|
|
27
|
+
if (!Array.isArray(parts) || !parts.length || parts.some(x => typeof x !== 'string'))
|
|
28
|
+
throw new Error('--acp-command JSON must be a non-empty array of strings.');
|
|
29
|
+
return { file: parts[0], args: parts.slice(1) };
|
|
30
|
+
}
|
|
31
|
+
const parts = (text.match(/"[^"]*"|'[^']*'|\S+/g) ?? []).map(p => p.replace(/^(["'])(.*)\1$/, '$2'));
|
|
32
|
+
if (!parts.length)
|
|
33
|
+
throw new Error('--acp-command is empty.');
|
|
34
|
+
return { file: parts[0], args: parts.slice(1) };
|
|
35
|
+
}
|
|
36
|
+
const usage = `Develop, install and manage independent plugins:
|
|
37
|
+
rho plugin --help
|
|
38
|
+
Verify a read-only evidence package:
|
|
14
39
|
rho --verify-evidence PATH
|
|
40
|
+
Start Rho on ~/Rho/projects and ~/Rho/state, and say where it is and what is missing:
|
|
41
|
+
rho
|
|
15
42
|
Start Rho:
|
|
16
|
-
rho --root PROJECTS_DIRECTORY --state-dir SERVICE_STATE [--core ABSOLUTE_CORE_BINARY] [--rscript ABSOLUTE_RSCRIPT] [--python PYTHON] [--ssh SSH] [--serverctl SERVERCTL] [--claude CLAUDE_CODE_BINARY] [--codex CODEX_BINARY] [--agent-backend claude-acp|codex-acp|claude-cli] [--port
|
|
43
|
+
rho --root PROJECTS_DIRECTORY --state-dir SERVICE_STATE [--core ABSOLUTE_CORE_BINARY] [--rscript ABSOLUTE_RSCRIPT] [--python PYTHON] [--ssh SSH] [--serverctl SERVERCTL] [--claude CLAUDE_CODE_BINARY] [--codex CODEX_BINARY] [--agent-backend claude-acp|codex-acp|claude-cli|acp] [--acp-command JSON_ARGV] [--port PORT] [--limits JSON_FILE] [--plugins documents,files,...] [--example on|off] [--open]
|
|
17
44
|
A narrower browser link (optional):
|
|
18
45
|
rho --pair PRIVATE_CONNECTION_FILE [--root PROJECTS_RELATIVE_DIRECTORY] [--open]
|
|
46
|
+
Add Rho to your own Claude Code or Codex for one project, as the project page's button does:
|
|
47
|
+
rho --grant PRIVATE_CONNECTION_FILE --project PROJECTS_RELATIVE_DIRECTORY --agent claude-code|codex
|
|
19
48
|
Give an Agent access to one project, and the command that adds it to Claude Code:
|
|
20
49
|
rho --grant PRIVATE_CONNECTION_FILE --project PROJECTS_RELATIVE_DIRECTORY [--session NAME]
|
|
21
50
|
Serve that access to the Agent over MCP (stdio):
|
|
@@ -24,120 +53,229 @@ Observe an external ACP client (use this as its agent command):
|
|
|
24
53
|
rho --acp /absolute/project --acp-agent claude|codex
|
|
25
54
|
--example off starts Rho without the project it ships; a new state otherwise gets that project
|
|
26
55
|
once, copied into the projects directory. --open opens the printed url in the default browser.
|
|
56
|
+
Without --port a start takes the port this state's last start had, and a new one only when that
|
|
57
|
+
port is taken; --port 0 always takes a new one.
|
|
27
58
|
Opening that url on this machine needs no link, code or session credential; --pair makes a
|
|
28
59
|
narrower, expiring link for one folder.
|
|
29
60
|
PRIVATE_CONNECTION_FILE is the connectionFile the running service printed; it and every
|
|
30
61
|
connection file are credentials.
|
|
31
62
|
`;
|
|
32
|
-
const options = { example: { type: 'string' }, 'verify-evidence': { type: 'string' }, acp: { type: 'string' }, 'acp-agent': { type: 'string' }, root: { type: 'string' }, 'state-dir': { type: 'string' }, core: { type: 'string' }, rscript: { type: 'string' }, python: { type: 'string' }, ssh: { type: 'string' }, serverctl: { type: 'string' }, claude: { type: 'string' }, codex: { type: 'string' }, 'agent-backend': { type: 'string' }, port: { type: 'string' }, limits: { type: 'string' }, plugins: { type: 'string' }, pair: { type: 'string' }, open: { type: 'boolean' },
|
|
33
|
-
grant: { type: 'string' }, project: { type: 'string' }, session: { type: 'string' }, mcp: { type: 'string' }, help: { type: 'boolean', short: 'h' } };
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
const options = { example: { type: 'string' }, 'verify-evidence': { type: 'string' }, acp: { type: 'string' }, 'acp-agent': { type: 'string' }, root: { type: 'string' }, 'state-dir': { type: 'string' }, core: { type: 'string' }, rscript: { type: 'string' }, python: { type: 'string' }, ssh: { type: 'string' }, serverctl: { type: 'string' }, claude: { type: 'string' }, codex: { type: 'string' }, 'agent-backend': { type: 'string' }, 'acp-command': { type: 'string' }, port: { type: 'string' }, limits: { type: 'string' }, plugins: { type: 'string' }, pair: { type: 'string' }, open: { type: 'boolean' },
|
|
64
|
+
grant: { type: 'string' }, project: { type: 'string' }, session: { type: 'string' }, agent: { type: 'string' }, mcp: { type: 'string' }, help: { type: 'boolean', short: 'h' } };
|
|
65
|
+
// A command that is refused says why and what to do. A service that is not running is the
|
|
66
|
+
// common case for --grant and --pair; anything without a known reason keeps its stack.
|
|
67
|
+
function refuse(error, url) {
|
|
68
|
+
const e = error;
|
|
69
|
+
if (e?.cause?.code === 'ECONNREFUSED' && url)
|
|
70
|
+
process.stderr.write(`Rho is not running at ${url}. Start it with the same --state-dir, then run this again.\n`);
|
|
71
|
+
else if (e?.error?.message || e?.kind || e?.syscall)
|
|
72
|
+
process.stderr.write((e.error?.message ?? e.message) + '\n');
|
|
73
|
+
else
|
|
74
|
+
throw error;
|
|
75
|
+
process.exit(1);
|
|
41
76
|
}
|
|
42
|
-
|
|
43
|
-
if (values['verify-evidence']) {
|
|
77
|
+
if (process.argv[2] === 'plugin') {
|
|
44
78
|
try {
|
|
45
|
-
|
|
46
|
-
process.stdout.write(JSON.stringify(await verifyEvidence(path.resolve(values['verify-evidence']))) + '\n');
|
|
79
|
+
await (await import('./plugin-cli.js')).pluginCli(process.argv.slice(3));
|
|
47
80
|
}
|
|
48
|
-
catch (
|
|
49
|
-
process.stderr.write((
|
|
81
|
+
catch (error) {
|
|
82
|
+
process.stderr.write((error instanceof Error ? error.message : String(error)) + '\n');
|
|
50
83
|
process.exitCode = 1;
|
|
51
84
|
}
|
|
52
|
-
process.exit(process.exitCode ?? 0);
|
|
53
85
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (!['claude', 'codex'].includes(values['acp-agent'] ?? '')) {
|
|
62
|
-
process.stderr.write('Select --acp-agent claude or codex.\n');
|
|
86
|
+
else {
|
|
87
|
+
let parsed;
|
|
88
|
+
try {
|
|
89
|
+
parsed = parseArgs({ options });
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
process.stderr.write((error instanceof Error ? error.message : String(error)) + '\n\n' + usage);
|
|
63
93
|
process.exit(2);
|
|
64
94
|
}
|
|
65
|
-
const {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
await import('@rho-workspace/plugin-agent/mcp');
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
// Access comes from whoever runs Rho: the operator's connection grants one project to one
|
|
74
|
-
// Agent session, and the reply says how Claude Code starts that session's MCP server.
|
|
75
|
-
if (values.grant) {
|
|
76
|
-
if (!values.project) {
|
|
77
|
-
process.stderr.write('--grant needs --project, the project folder relative to the service root.\n\n' + usage);
|
|
78
|
-
process.exit(2);
|
|
95
|
+
const { values } = parsed;
|
|
96
|
+
if (values['verify-evidence']) {
|
|
97
|
+
try {
|
|
98
|
+
const { verifyEvidence } = await import('./evidence-export.js');
|
|
99
|
+
process.stdout.write(JSON.stringify(await verifyEvidence(path.resolve(values['verify-evidence']))) + '\n');
|
|
79
100
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const { mcpCommand, mcpName } = await import('./agent-connect.js');
|
|
86
|
-
const command = mcpCommand(granted.connectionFile), name = mcpName(project.root);
|
|
87
|
-
const quote = (value) => /^[A-Za-z0-9_./:@=-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
88
|
-
process.stdout.write(JSON.stringify({ projectId: project.id, session, connectionFile: granted.connectionFile, methods: granted.methods.length,
|
|
89
|
-
mcp: { name, command: command[0], args: command.slice(1) }, claude: ['claude', 'mcp', 'add', name, '--', ...command].map(quote).join(' ') }) + '\n');
|
|
90
|
-
process.exit(0);
|
|
101
|
+
catch (e) {
|
|
102
|
+
process.stderr.write((e instanceof Error ? e.message : String(e)) + '\n');
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
}
|
|
105
|
+
process.exit(process.exitCode ?? 0);
|
|
91
106
|
}
|
|
92
|
-
if (values.
|
|
93
|
-
|
|
94
|
-
const client = await RhoClient.fromFile(path.resolve(values.pair));
|
|
95
|
-
const pairing = await client.call('session.browser.pair', values.root ? { path: values.root } : {});
|
|
96
|
-
const openUrl = link(client.connection.url, pairing.code);
|
|
97
|
-
process.stdout.write(JSON.stringify({ url: client.connection.url, openUrl, pairingCode: pairing.code, pairingExpiresAt: pairing.expiresAt }) + '\n');
|
|
98
|
-
if (values.open)
|
|
99
|
-
openBrowser(openUrl);
|
|
107
|
+
if (values.help) {
|
|
108
|
+
process.stdout.write(usage);
|
|
100
109
|
process.exit(0);
|
|
101
110
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
// The Agent's side of a grant: its MCP server reads the connection file and speaks on stdio,
|
|
112
|
+
// so nothing else may write to stdout in this mode.
|
|
113
|
+
if (values.acp) {
|
|
114
|
+
if (!['claude', 'codex'].includes(values['acp-agent'] ?? '')) {
|
|
115
|
+
process.stderr.write('Select --acp-agent claude or codex.\n');
|
|
116
|
+
process.exit(2);
|
|
117
|
+
}
|
|
118
|
+
const { runExternalAcp } = await import('@rho-workspace/plugin-agent/acp-cli');
|
|
119
|
+
runExternalAcp(path.resolve(values.acp), values['acp-agent']);
|
|
105
120
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
else if (values.mcp) {
|
|
122
|
+
process.env.RHO_AGENT_CONNECTION = path.resolve(values.mcp);
|
|
123
|
+
await import('@rho-workspace/plugin-agent/mcp');
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// Access comes from whoever runs Rho: the operator's connection grants one project to one
|
|
127
|
+
// Agent session, and the reply says how Claude Code starts that session's MCP server.
|
|
128
|
+
if (values.grant) {
|
|
129
|
+
if (!values.project) {
|
|
130
|
+
process.stderr.write('--grant needs --project, the project folder relative to the service root.\n\n' + usage);
|
|
131
|
+
process.exit(2);
|
|
132
|
+
}
|
|
133
|
+
if (values.agent !== undefined && !['claude-code', 'codex'].includes(values.agent)) {
|
|
134
|
+
process.stderr.write('Choose --agent claude-code or codex.\n');
|
|
135
|
+
process.exit(2);
|
|
136
|
+
}
|
|
137
|
+
if (values.agent !== undefined && values.session !== undefined) {
|
|
138
|
+
process.stderr.write('--agent names the session itself; leave out --session.\n');
|
|
139
|
+
process.exit(2);
|
|
140
|
+
}
|
|
141
|
+
const { RhoClient } = await import('./client.js');
|
|
142
|
+
const client = await RhoClient.fromFile(path.resolve(values.grant)).catch(error => refuse(error));
|
|
143
|
+
try {
|
|
144
|
+
const project = await client.call('project.open', { path: values.project });
|
|
145
|
+
// The same as the project page's button: the grant that Agent keeps, then its own
|
|
146
|
+
// `mcp add`, recorded so the page shows it under that Agent.
|
|
147
|
+
if (values.agent) {
|
|
148
|
+
const connected = await client.call('session.connect', { projectId: project.id, agent: values.agent });
|
|
149
|
+
process.stdout.write(JSON.stringify({ projectId: project.id, root: project.root, agent: values.agent, methods: connected.methods, granted: connected.granted, added: connected.added }) + '\n');
|
|
150
|
+
process.exit(0);
|
|
151
|
+
}
|
|
152
|
+
const session = values.session ?? 'claude-code';
|
|
153
|
+
const granted = await client.call('session.grant', { projectId: project.id, externalSessionId: session });
|
|
154
|
+
const { mcpCommand, mcpName } = await import('./agent-connect.js');
|
|
155
|
+
const command = mcpCommand(granted.connectionFile), name = mcpName(project.root);
|
|
156
|
+
const quote = (value) => /^[A-Za-z0-9_./:@=-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
157
|
+
process.stdout.write(JSON.stringify({ projectId: project.id, session, connectionFile: granted.connectionFile, methods: granted.methods.length,
|
|
158
|
+
mcp: { name, command: command[0], args: command.slice(1) }, claude: ['claude', 'mcp', 'add', name, '--', ...command].map(quote).join(' ') }) + '\n');
|
|
159
|
+
process.exit(0);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
refuse(error, client.connection.url);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (values.pair) {
|
|
166
|
+
const { RhoClient } = await import('./client.js');
|
|
167
|
+
const client = await RhoClient.fromFile(path.resolve(values.pair)).catch(error => refuse(error));
|
|
168
|
+
try {
|
|
169
|
+
const pairing = await client.call('session.browser.pair', values.root ? { path: values.root } : {});
|
|
170
|
+
const openUrl = link(client.connection.url, pairing.code);
|
|
171
|
+
process.stdout.write(JSON.stringify({ url: client.connection.url, openUrl, pairingCode: pairing.code, pairingExpiresAt: pairing.expiresAt }) + '\n');
|
|
172
|
+
if (values.open)
|
|
173
|
+
openBrowser(openUrl);
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
refuse(error, client.connection.url);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// A person who runs `npx @rho-workspace/rho` with no arguments wants the application, not a
|
|
181
|
+
// usage page: start it on their own folders, say in plain words where it is, and open it when
|
|
182
|
+
// this is an interactive terminal. Every flagged start is unchanged, so scripts and
|
|
183
|
+
// acceptance runs keep the output they read.
|
|
184
|
+
const quickStart = !values.root && !values['state-dir'];
|
|
185
|
+
if (!quickStart && (!values.root || !values['state-dir'])) {
|
|
186
|
+
process.stderr.write(usage);
|
|
187
|
+
process.exit(2);
|
|
188
|
+
}
|
|
189
|
+
let quickRoot = '', quickState = '';
|
|
190
|
+
if (quickStart) {
|
|
191
|
+
const { homedir } = await import('node:os');
|
|
192
|
+
quickRoot = path.join(homedir(), 'Rho', 'projects');
|
|
193
|
+
quickState = path.join(homedir(), 'Rho', 'state');
|
|
194
|
+
await fs.mkdir(quickRoot, { recursive: true });
|
|
195
|
+
await fs.mkdir(quickState, { recursive: true });
|
|
196
|
+
}
|
|
197
|
+
const projectsRoot = values.root ?? quickRoot, stateDirectory = values['state-dir'] ?? quickState;
|
|
198
|
+
const limits = values.limits ? JSON.parse(await fs.readFile(values.limits, 'utf8')) : {};
|
|
199
|
+
const allowed = new Set(['maxRequestBytes', 'maxEventBytes', 'eventRetention', 'maxSubscriptions', 'maxQueue', 'maxContexts', 'maxBindings', 'maxActivities', 'maxContextBytes', 'maxRetainedMetadataBytes', 'maxConcurrentActions', 'maxOperations', 'maxLogPageBytes', 'maxCodeBytes', 'maxReconciled', 'documents', 'resources', 'workspace', 'r', 'annotations', 'interactions', 'capabilityLimits', 'browser', 'nativeFolders', 'conversations', 'evidence', 'console']);
|
|
200
|
+
if (Object.keys(limits).some(k => !allowed.has(k)))
|
|
201
|
+
throw new Error('Unknown service budget in --limits file.');
|
|
202
|
+
const { RhoService } = await import('./server.js'), { runtime } = await import('./runtime.js');
|
|
203
|
+
const agentBackend = values['agent-backend'] ?? process.env.RHO_AGENT_BACKEND ?? 'claude-acp';
|
|
204
|
+
if (!['claude-acp', 'codex-acp', 'claude-cli', 'acp'].includes(agentBackend))
|
|
205
|
+
throw new Error('Choose --agent-backend claude-acp, codex-acp, claude-cli or acp.');
|
|
206
|
+
const acpCommand = agentBackend === 'acp' ? parseAcpCommand(values['acp-command'] ?? process.env.RHO_ACP_COMMAND) : undefined;
|
|
207
|
+
if (values.port !== undefined && !(/^\d+$/.test(values.port) && Number(values.port) <= 65535)) {
|
|
208
|
+
process.stderr.write('Choose --port 0 to 65535, or leave it out.\n');
|
|
209
|
+
process.exit(2);
|
|
210
|
+
}
|
|
211
|
+
// A Core that cannot be used (none for this platform, a changed digest) is read by a person.
|
|
212
|
+
const native = await runtime(values.core, values.rscript).catch(e => { process.stderr.write((e instanceof Error ? e.message : String(e)) + '\n'); process.exit(1); });
|
|
213
|
+
// A person starting Rho sees the project it ships with; a composition that starts a service
|
|
214
|
+
// for its own work says so itself and is left alone.
|
|
215
|
+
if (values.example !== undefined && !['on', 'off'].includes(values.example))
|
|
216
|
+
throw new Error('Choose --example on or off.');
|
|
217
|
+
const example = values.example !== 'off';
|
|
218
|
+
const service = await new RhoService({ example, ...limits, root: path.resolve(projectsRoot), stateDir: path.resolve(stateDirectory), ...native, agentBackend: agentBackend, acpCommand, codex: values.codex ?? process.env.RHO_CODEX ?? 'codex', claude: values.claude ?? process.env.RHO_CLAUDE ?? 'claude', python: values.python ?? process.env.RHO_PYTHON, ssh: values.ssh ?? process.env.RHO_SSH, serverctl: values.serverctl ?? process.env.RHO_SERVERCTL, port: values.port === undefined ? undefined : Number(values.port), ...(values.plugins !== undefined ? { plugins: values.plugins ? values.plugins.split(',') : [] } : {}) }).start().catch(async (error) => {
|
|
219
|
+
// Starting Rho again on a state it already runs on is the common refusal: say where it runs.
|
|
220
|
+
const held = error?.details?.stateDirectory;
|
|
221
|
+
const running = typeof held === 'string' ? await fs.readFile(path.join(held, 'connection.json'), 'utf8').then(text => String(JSON.parse(text).url), () => '') : '';
|
|
222
|
+
return refuse(running ? Object.assign(error, { message: `Rho is already running for ${held} at ${running}. Open that url, or stop that service before starting it again.` }) : error);
|
|
223
|
+
});
|
|
224
|
+
// The narrow conclusion a person needs on one line: what was selected, where state
|
|
225
|
+
// lives, and how to open it. It stays inside `ready` so a parser reading line one is
|
|
226
|
+
// unaffected. Opening this url on this machine is the access; no link is minted.
|
|
227
|
+
const rscript = values.rscript ?? process.env.RHO_RSCRIPT ?? 'Rscript';
|
|
228
|
+
process.stdout.write(JSON.stringify({ ready: {
|
|
229
|
+
url: service.url, connectionFile: path.join(service.state.directory, 'connection.json'), apiVersion: '2.0',
|
|
230
|
+
openUrl: service.url,
|
|
231
|
+
startup: {
|
|
232
|
+
projects: path.resolve(projectsRoot), state: path.resolve(stateDirectory),
|
|
233
|
+
core: service.bridge.binary, rscript: service.bridge.rscript,
|
|
234
|
+
note: 'Open this url in a browser on this machine. Execution stays off until an Agent submits a run; project.doctor answers what is missing.'
|
|
235
|
+
}
|
|
236
|
+
} }) + '\n');
|
|
237
|
+
// What a person needs, after the machine line: where it is, and what is missing to work in it.
|
|
238
|
+
// A missing Agent or R does not stop the workspace; it decides what the person can do next.
|
|
239
|
+
if (quickStart) {
|
|
240
|
+
const say = (text) => process.stdout.write(text + '\n');
|
|
241
|
+
say('');
|
|
242
|
+
say('Rho is running. Open this address in a browser on this machine:');
|
|
243
|
+
say(' ' + service.url);
|
|
244
|
+
say('');
|
|
245
|
+
say('Projects folder: ' + quickRoot);
|
|
246
|
+
say('Service state: ' + quickState);
|
|
247
|
+
say('');
|
|
248
|
+
const agentCommand = await import('@rho-workspace/plugin-agent/acp-command').catch(() => null);
|
|
249
|
+
// The selection the service itself got: an explicit flag, then the environment, then the name
|
|
250
|
+
// on PATH. Reporting "installed" for a client the person pointed at a missing path is worse
|
|
251
|
+
// than saying nothing.
|
|
252
|
+
const claudePath = values.claude ?? process.env.RHO_CLAUDE, codexPath = values.codex ?? process.env.RHO_CODEX;
|
|
253
|
+
const env = { ...process.env, ...(claudePath ? { CLAUDE_CODE_EXECUTABLE: claudePath } : {}), ...(codexPath ? { CODEX_PATH: codexPath } : {}) };
|
|
254
|
+
// An ACP agent named with --acp-command is the operator's own command; it is named, not checked.
|
|
255
|
+
if (acpCommand)
|
|
256
|
+
say('Agent: ' + acpCommand.file + ', named with --acp-command.');
|
|
257
|
+
else {
|
|
258
|
+
const missing = agentCommand ? agentCommand.missingClient(agentBackend === 'codex-acp' ? 'codex' : 'claude', env) : null;
|
|
259
|
+
say(missing
|
|
260
|
+
? 'Agent: ' + missing + ' Until then folders can be opened and files read here; the Agent cannot run in them.'
|
|
261
|
+
: 'Agent: ' + (agentBackend === 'codex-acp' ? 'Codex' : 'Claude Code') + ' is installed; a conversation can start in any project.');
|
|
133
262
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
263
|
+
const hasR = await fs.access(service.bridge.rscript).then(() => true, () => false);
|
|
264
|
+
say(hasR
|
|
265
|
+
? 'R: ' + service.bridge.rscript
|
|
266
|
+
: 'R: Rscript was not found. Reading, editing and viewing work; running R needs R installed (or --rscript).');
|
|
267
|
+
say('');
|
|
268
|
+
say('Stop it with Ctrl+C. Start it again with: npx @rho-workspace/rho');
|
|
269
|
+
if (process.stdout.isTTY)
|
|
270
|
+
openBrowser(service.url);
|
|
271
|
+
}
|
|
272
|
+
if (values.open)
|
|
273
|
+
openBrowser(service.url);
|
|
274
|
+
let closing = false;
|
|
275
|
+
// Windows has no SIGTERM another process can send, and a console may pass on Ctrl+C turned off;
|
|
276
|
+
// there Ctrl+Break on Rho's console (SIGBREAK) stops it in order too.
|
|
277
|
+
for (const signal of ['SIGTERM', 'SIGINT', ...(process.platform === 'win32' ? ['SIGBREAK'] : [])])
|
|
278
|
+
process.on(signal, async () => { if (closing)
|
|
279
|
+
return; closing = true; const reports = await service.close(); process.stdout.write(JSON.stringify({ shutdown: reports }) + '\n'); });
|
|
280
|
+
}
|
|
143
281
|
}
|