starmux 0.11.0 → 0.13.0
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/CONTRIBUTING.md +1 -1
- package/README.md +169 -97
- package/SECURITY.md +3 -3
- package/dist/src/access-control.js +23 -20
- package/dist/src/access-control.js.map +1 -1
- package/dist/src/access-dashboard.js +8 -2
- package/dist/src/access-dashboard.js.map +1 -1
- package/dist/src/cli/instances.js +87 -14
- package/dist/src/cli/instances.js.map +1 -1
- package/dist/src/cli/mac-mcp-gateway.js +4 -3
- package/dist/src/cli/mac-mcp-gateway.js.map +1 -1
- package/dist/src/cli/starmux-status.js +122 -0
- package/dist/src/cli/starmux-status.js.map +1 -0
- package/dist/src/cli/starmux.js +222 -49
- package/dist/src/cli/starmux.js.map +1 -1
- package/dist/src/deny-dashboard.js +2 -2
- package/dist/src/deny-dashboard.js.map +1 -1
- package/dist/src/instance-runtime.js +11 -2
- package/dist/src/instance-runtime.js.map +1 -1
- package/dist/src/instance-store.js +3 -1
- package/dist/src/instance-store.js.map +1 -1
- package/dist/src/ops-dashboard.js +4 -4
- package/dist/src/ops-dashboard.js.map +1 -1
- package/dist/src/permissions-dashboard.js +2 -2
- package/dist/src/permissions-dashboard.js.map +1 -1
- package/dist/src/runtime-paths.js +3 -3
- package/dist/src/runtime-paths.js.map +1 -1
- package/dist/src/starmux-migration.js +1 -1
- package/dist/src/starmux-migration.js.map +1 -1
- package/dist/vendor/desktop-commander/LICENSE +21 -0
- package/dist/vendor/desktop-commander/dist/bootstrap.js +22 -0
- package/dist/vendor/desktop-commander/dist/command-manager.js +246 -0
- package/dist/vendor/desktop-commander/dist/config-field-definitions.js +37 -0
- package/dist/vendor/desktop-commander/dist/config-manager.js +634 -0
- package/dist/vendor/desktop-commander/dist/config-recovery.js +81 -0
- package/dist/vendor/desktop-commander/dist/config.js +19 -0
- package/dist/vendor/desktop-commander/dist/custom-stdio.js +358 -0
- package/dist/vendor/desktop-commander/dist/error-handlers.js +15 -0
- package/dist/vendor/desktop-commander/dist/handlers/edit-search-handlers.js +6 -0
- package/dist/vendor/desktop-commander/dist/handlers/filesystem-handlers.js +450 -0
- package/dist/vendor/desktop-commander/dist/handlers/history-handlers.js +67 -0
- package/dist/vendor/desktop-commander/dist/handlers/index.js +7 -0
- package/dist/vendor/desktop-commander/dist/handlers/process-handlers.js +15 -0
- package/dist/vendor/desktop-commander/dist/handlers/search-answers.js +172 -0
- package/dist/vendor/desktop-commander/dist/handlers/search-handlers.js +136 -0
- package/dist/vendor/desktop-commander/dist/handlers/terminal-handlers.js +35 -0
- package/dist/vendor/desktop-commander/dist/index.js +88 -0
- package/dist/vendor/desktop-commander/dist/search-manager.js +1262 -0
- package/dist/vendor/desktop-commander/dist/server.js +1414 -0
- package/dist/vendor/desktop-commander/dist/terminal-manager.js +757 -0
- package/dist/vendor/desktop-commander/dist/tools/config.js +211 -0
- package/dist/vendor/desktop-commander/dist/tools/edit.js +455 -0
- package/dist/vendor/desktop-commander/dist/tools/feedback.js +10 -0
- package/dist/vendor/desktop-commander/dist/tools/filesystem.js +1047 -0
- package/dist/vendor/desktop-commander/dist/tools/fuzzySearch.js +74 -0
- package/dist/vendor/desktop-commander/dist/tools/fuzzySearchCore.js +125 -0
- package/dist/vendor/desktop-commander/dist/tools/improved-process-tools.js +726 -0
- package/dist/vendor/desktop-commander/dist/tools/mime-types.js +28 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/extract-images.js +131 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/index.js +4 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/lib/pdf2md.js +78 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/manipulations.js +112 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/markdown.js +30 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/utils.js +45 -0
- package/dist/vendor/desktop-commander/dist/tools/process.js +90 -0
- package/dist/vendor/desktop-commander/dist/tools/prompts.js +259 -0
- package/dist/vendor/desktop-commander/dist/tools/schemas.js +242 -0
- package/dist/vendor/desktop-commander/dist/tools/usage.js +24 -0
- package/dist/vendor/desktop-commander/dist/types.js +1 -0
- package/dist/vendor/desktop-commander/dist/ui/config-editor/config-editor-runtime.js +150 -0
- package/dist/vendor/desktop-commander/dist/ui/config-editor/index.html +13 -0
- package/dist/vendor/desktop-commander/dist/ui/config-editor/styles.css +587 -0
- package/dist/vendor/desktop-commander/dist/ui/contracts.js +57 -0
- package/dist/vendor/desktop-commander/dist/ui/file-preview/index.html +16 -0
- package/dist/vendor/desktop-commander/dist/ui/file-preview/preview-runtime.js +471 -0
- package/dist/vendor/desktop-commander/dist/ui/file-preview/shared/preview-file-types.js +59 -0
- package/dist/vendor/desktop-commander/dist/ui/file-preview/styles.css +1631 -0
- package/dist/vendor/desktop-commander/dist/ui/resources.js +93 -0
- package/dist/vendor/desktop-commander/dist/utils/ab-test.js +122 -0
- package/dist/vendor/desktop-commander/dist/utils/atomic-write.js +80 -0
- package/dist/vendor/desktop-commander/dist/utils/capture.js +40 -0
- package/dist/vendor/desktop-commander/dist/utils/dockerPrompt.js +9 -0
- package/dist/vendor/desktop-commander/dist/utils/exit-process.js +31 -0
- package/dist/vendor/desktop-commander/dist/utils/feature-flags.js +18 -0
- package/dist/vendor/desktop-commander/dist/utils/files/base.js +5 -0
- package/dist/vendor/desktop-commander/dist/utils/files/binary.js +65 -0
- package/dist/vendor/desktop-commander/dist/utils/files/docx.js +694 -0
- package/dist/vendor/desktop-commander/dist/utils/files/excel.js +497 -0
- package/dist/vendor/desktop-commander/dist/utils/files/factory.js +168 -0
- package/dist/vendor/desktop-commander/dist/utils/files/image.js +89 -0
- package/dist/vendor/desktop-commander/dist/utils/files/index.js +14 -0
- package/dist/vendor/desktop-commander/dist/utils/files/pdf.js +124 -0
- package/dist/vendor/desktop-commander/dist/utils/files/text.js +423 -0
- package/dist/vendor/desktop-commander/dist/utils/fuzzySearchLogger.js +126 -0
- package/dist/vendor/desktop-commander/dist/utils/internal-facts.js +23 -0
- package/dist/vendor/desktop-commander/dist/utils/lazy-package.js +44 -0
- package/dist/vendor/desktop-commander/dist/utils/lineEndingHandler.js +77 -0
- package/dist/vendor/desktop-commander/dist/utils/logger.js +72 -0
- package/dist/vendor/desktop-commander/dist/utils/mcp-ui-ab-test.js +62 -0
- package/dist/vendor/desktop-commander/dist/utils/open-browser.js +2 -0
- package/dist/vendor/desktop-commander/dist/utils/process-detection.js +260 -0
- package/dist/vendor/desktop-commander/dist/utils/process-tree.js +223 -0
- package/dist/vendor/desktop-commander/dist/utils/rename.js +74 -0
- package/dist/vendor/desktop-commander/dist/utils/ripgrep-resolver.js +69 -0
- package/dist/vendor/desktop-commander/dist/utils/shell.js +187 -0
- package/dist/vendor/desktop-commander/dist/utils/system-info.js +727 -0
- package/dist/vendor/desktop-commander/dist/utils/terminal-timestamps.js +18 -0
- package/dist/vendor/desktop-commander/dist/utils/toolHistory.js +309 -0
- package/dist/vendor/desktop-commander/dist/utils/trackTools.js +55 -0
- package/dist/vendor/desktop-commander/dist/utils/unsupportedParams.js +66 -0
- package/dist/vendor/desktop-commander/dist/utils/usageTracker.js +225 -0
- package/dist/vendor/desktop-commander/dist/utils/welcome-onboarding.js +2 -0
- package/dist/vendor/desktop-commander/dist/utils/withTimeout.js +85 -0
- package/dist/vendor/desktop-commander/dist/version.js +1 -0
- package/dist/vendor/desktop-commander/package.json +15 -0
- package/docs/ACCESS_CONTROL.md +5 -5
- package/docs/CLIENTS.md +11 -19
- package/docs/CONNECT_CHATGPT.md +4 -6
- package/docs/INSTALL.md +9 -5
- package/docs/INSTANCES.md +5 -2
- package/docs/OAUTH.md +6 -14
- package/docs/PLATFORMS.md +4 -4
- package/docs/README.md +2 -11
- package/docs/REMOTE_SETUP.md +13 -13
- package/docs/SECURITY.md +5 -5
- package/docs/assets/starmux-architecture.svg +315 -0
- package/package.json +14 -2
- package/docs/ARCHITECTURE.md +0 -51
- package/docs/MIGRATION.md +0 -37
- package/docs/RELEASE_STATUS.md +0 -31
- package/docs/TYPESCRIPT_MIGRATION.md +0 -56
- package/docs/VALIDATION_OAUTH.md +0 -79
- package/docs/VALIDATION_TUNNEL.md +0 -39
- package/docs/VALIDATION_WORKBUDDY.md +0 -59
- package/docs/assets/mcp-connection-route.svg +0 -104
- package/docs/assets/mcp-gateway-simple.svg +0 -62
|
@@ -22,13 +22,13 @@ export function renderPermissionDashboard({ project, home, config, warning = "",
|
|
|
22
22
|
'<button type="submit">撤销</button></form></td></tr>').join("");
|
|
23
23
|
return '<!doctype html><html lang="zh-CN"><head>' +
|
|
24
24
|
'<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">' +
|
|
25
|
-
'<title>
|
|
25
|
+
'<title>Starmux · Workspace Permissions</title>' +
|
|
26
26
|
'<style>body{font:15px system-ui,-apple-system,sans-serif;max-width:940px;margin:30px auto;padding:0 20px;color:#233045;background:#f6f7fa}' +
|
|
27
27
|
'section{background:white;border:1px solid #ddd;padding:20px;margin:15px 0;border-radius:12px}' +
|
|
28
28
|
'table{border-collapse:collapse;width:100%}td,th{padding:10px;border-bottom:1px solid #ddd;text-align:left}' +
|
|
29
29
|
'input,select,button{font:inherit;padding:6px;margin:4px}input[name=target]{width:90%;max-width:550px}' +
|
|
30
30
|
'small{display:block;color:#657083}code{overflow-wrap:anywhere}button{cursor:pointer}</style></head><body>' +
|
|
31
|
-
'<h1>
|
|
31
|
+
'<h1>Starmux · 目录授权</h1>' +
|
|
32
32
|
'<p>本页在 Mac 本机管理 Gateway 权限。远程 AI 不可自行增加权限。' +
|
|
33
33
|
'增加或撤销授权后,下一次工具调用立即使用新权限。</p>' +
|
|
34
34
|
(warning ? '<section role="alert">' + e(warning) + '</section>' : "") +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions-dashboard.js","sourceRoot":"","sources":["../../src/permissions-dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage } from \"node:http\";\nimport type { GrantsConfig,PostResult,WorkspaceGrants } from \"./contracts.js\";\nimport { isSafeInteger } from \"./validation.js\";\n// Local owner-facing permissions console. No scripts, cookies or remote MCP\n// tool capable of elevating its own grants.\nconst e=(s: unknown)=>String(s??\"\").replace(/[&<>\"']/g,c=>\n ({\"&\":\"&\",\"<\":\"<\",\">\":\">\",'\"':\""\",\"'\":\"'\"}[c]!));\nexport const permissionHeaders=Object.freeze({\n \"cache-control\":\"no-store\",\"x-content-type-options\":\"nosniff\",\n \"referrer-policy\":\"no-referrer\",\n \"content-security-policy\":\"default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'\"\n});\nconst selector=(grant: unknown,op: string,label: string,warning=\"\")=>\n '<form method=\"post\" action=\"/permissions\">'+\n '<input type=\"hidden\" name=\"op\" value=\"'+e(op)+'\">'+\n '<label>设备所有者管理密钥 <input name=\"token\" type=\"password\" required autocomplete=\"off\"></label> '+\n '<button type=\"submit\">'+e(label)+'</button>'+\n (warning?'<small>'+e(warning)+'</small>':\"\")+\n '</form>';\nexport function renderPermissionDashboard({project,home,config,warning=\"\",nativeAccessEnabled=false}: {project:{path:string;mode:string};home:string;config:GrantsConfig;warning?:string;nativeAccessEnabled?:boolean}){\n const rows=config.custom.map(grant=>\n '<tr><td><code>'+e(grant.path)+'</code></td><td>'+e(grant.mode)+\n '</td><td><form method=\"post\" action=\"/permissions\">'+\n '<input type=\"hidden\" name=\"op\" value=\"revoke\">'+\n '<input type=\"hidden\" name=\"target\" value=\"'+e(grant.path)+'\">'+\n '<input type=\"password\" name=\"token\" autocomplete=\"off\" required placeholder=\"管理密钥\">'+\n '<button type=\"submit\">撤销</button></form></td></tr>').join(\"\");\n return '<!doctype html><html lang=\"zh-CN\"><head>'+\n '<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">'+\n '<title>Mac MCP Gateway · Workspace Permissions</title>'+\n '<style>body{font:15px system-ui,-apple-system,sans-serif;max-width:940px;margin:30px auto;padding:0 20px;color:#233045;background:#f6f7fa}'+\n 'section{background:white;border:1px solid #ddd;padding:20px;margin:15px 0;border-radius:12px}'+\n 'table{border-collapse:collapse;width:100%}td,th{padding:10px;border-bottom:1px solid #ddd;text-align:left}'+\n 'input,select,button{font:inherit;padding:6px;margin:4px}input[name=target]{width:90%;max-width:550px}'+\n 'small{display:block;color:#657083}code{overflow-wrap:anywhere}button{cursor:pointer}</style></head><body>'+\n '<h1>Mac MCP Gateway · 目录授权</h1>'+\n '<p>本页在 Mac 本机管理 Gateway 权限。远程 AI 不可自行增加权限。'+\n '增加或撤销授权后,下一次工具调用立即使用新权限。</p>'+\n (warning?'<section role=\"alert\">'+e(warning)+'</section>':\"\")+\n (nativeAccessEnabled?'<section role=\"alert\"><h2>可信开发模式 · 完整本地执行权限</h2>'+\n '<p>此设备所有者启用了原生 Desktop Commander 工具透传。'+\n '以下 Home/外部/自定义目录开关<strong>不限制原生 DC 文件和 Shell 工具</strong>;'+\n '可访问范围由 Desktop Commander 和 macOS 用户账户决定。'+\n '开关仅在切回受限工作区模式后生效。请勿将此服务暴露给未经认证的客户端。</p>'+\n '</section>':\"\")+\n '<section><h2>工作目录 · 读写范围</h2><code>'+e(project.path)+'</code>'+\n '<p>工作模式:<b>'+e(project.mode)+'</b>。该项目沿用原有 Gateway 安全规则。</p></section>'+\n '<section><h2>Home · 只读</h2><code>'+e(home)+'</code>'+\n '<p>当前:<b>'+(config.homeRead?\"已开启只读扫描\":\"未授权\")+'</b></p>'+\n selector(null,config.homeRead?\"home-disable\":\"home-enable\",\n config.homeRead?\"关闭 Home 读取\":\"允许 Home 只读\",\n \"默认仍保护 SSH、密钥、凭据及配置路径。\")+'</section>'+\n '<section><h2>Home 外 · 只读</h2>'+\n '<p>当前:<b>'+(config.outsideRead?\"已开启只读扫描\":\"未授权\")+'</b></p>'+\n selector(null,config.outsideRead?\"outside-disable\":\"outside-enable\",\n config.outsideRead?\"关闭 Home 外读取\":\"允许 Home 外只读\",\n \"不会授予删除、任意 Shell 或写操作。\")+'</section>'+\n '<section><h2>自定义目录 · 单独授权</h2>'+\n '<table><thead><tr><th>目录</th><th>权限</th><th>操作</th></tr></thead><tbody>'+\n (rows||'<tr><td colspan=\"3\">暂无自定义授权</td></tr>')+'</tbody></table>'+\n '<h3>新增或修改</h3><form method=\"post\" action=\"/permissions\">'+\n '<input type=\"hidden\" name=\"op\" value=\"add\">'+\n '<label>绝对路径 <input type=\"text\" name=\"target\" required placeholder=\"/Volumes/Disk/Folder\"></label>'+\n '<label>权限 <select name=\"mode\"><option value=\"read-only\">只读</option>'+\n '<option value=\"read-write\">读写</option></select></label>'+\n '<label>管理密钥 <input type=\"password\" name=\"token\" required autocomplete=\"off\"></label>'+\n '<button type=\"submit\">确认授权</button></form></section>'+\n '<p><a href=\"/dashboard\">返回运行 Dashboard</a> · '+\n '<a href=\"/filters\">工具黑名单</a></p>'+\n '<small>授权管理密钥仅在 Mac 本地终端显示;不可交给远程 Agent。'+\n '本页不改变 trusted-project 的同 UID Shell 风险。</small></body></html>';\n}\nexport async function applyPermissionPost(req: IncomingMessage,grants: WorkspaceGrants,host: string|undefined): Promise<PostResult>{\n if(req.headers.origin!==\"http://\"+host||\n (req.headers[\"content-type\"]||\"\").split(\";\")[0]!==\"application/x-www-form-urlencoded\")\n return {status:403,message:\"Owner authorization required\"};\n const size=Number(req.headers[\"content-length\"]||0);\n if(!isSafeInteger(size)||size>4096||size<1)\n return {status:413,message:\"Invalid request\"};\n let body=\"\";\n for await(const chunk of req){\n body+=chunk.toString(\"utf8\");\n if(Buffer.byteLength(body)>4096)return {status:413,message:\"Invalid request\"};\n }\n const q=new URLSearchParams(body);\n for(const key of q.keys())\n if(![\"op\",\"target\",\"mode\",\"token\"].includes(key)||q.getAll(key).length!==1)\n return {status:400,message:\"Invalid form\"};\n if(!await grants.authorized(q.get(\"token\")))\n return {status:403,message:\"Owner authorization required\"};\n const operation=q.get(\"op\")??\"\";\n if(![\"home-enable\",\"home-disable\",\"outside-enable\",\"outside-disable\",\"add\",\"revoke\"]\n .includes(operation))return {status:400,message:\"Invalid operation\"};\n if(operation===\"add\"&&![\"read-only\",\"read-write\"].includes(q.get(\"mode\")??\"\"))\n return {status:400,message:\"Invalid mode\"};\n try{\n await grants.update(operation,{target:q.get(\"target\"),mode:q.get(\"mode\")});\n return {status:303,location:\"/permissions\"};\n }catch{\n return {status:400,message:\"Grant rejected: verify directory and permissions\"};\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"permissions-dashboard.js","sourceRoot":"","sources":["../../src/permissions-dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage } from \"node:http\";\nimport type { GrantsConfig,PostResult,WorkspaceGrants } from \"./contracts.js\";\nimport { isSafeInteger } from \"./validation.js\";\n// Local owner-facing permissions console. No scripts, cookies or remote MCP\n// tool capable of elevating its own grants.\nconst e=(s: unknown)=>String(s??\"\").replace(/[&<>\"']/g,c=>\n ({\"&\":\"&\",\"<\":\"<\",\">\":\">\",'\"':\""\",\"'\":\"'\"}[c]!));\nexport const permissionHeaders=Object.freeze({\n \"cache-control\":\"no-store\",\"x-content-type-options\":\"nosniff\",\n \"referrer-policy\":\"no-referrer\",\n \"content-security-policy\":\"default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'\"\n});\nconst selector=(grant: unknown,op: string,label: string,warning=\"\")=>\n '<form method=\"post\" action=\"/permissions\">'+\n '<input type=\"hidden\" name=\"op\" value=\"'+e(op)+'\">'+\n '<label>设备所有者管理密钥 <input name=\"token\" type=\"password\" required autocomplete=\"off\"></label> '+\n '<button type=\"submit\">'+e(label)+'</button>'+\n (warning?'<small>'+e(warning)+'</small>':\"\")+\n '</form>';\nexport function renderPermissionDashboard({project,home,config,warning=\"\",nativeAccessEnabled=false}: {project:{path:string;mode:string};home:string;config:GrantsConfig;warning?:string;nativeAccessEnabled?:boolean}){\n const rows=config.custom.map(grant=>\n '<tr><td><code>'+e(grant.path)+'</code></td><td>'+e(grant.mode)+\n '</td><td><form method=\"post\" action=\"/permissions\">'+\n '<input type=\"hidden\" name=\"op\" value=\"revoke\">'+\n '<input type=\"hidden\" name=\"target\" value=\"'+e(grant.path)+'\">'+\n '<input type=\"password\" name=\"token\" autocomplete=\"off\" required placeholder=\"管理密钥\">'+\n '<button type=\"submit\">撤销</button></form></td></tr>').join(\"\");\n return '<!doctype html><html lang=\"zh-CN\"><head>'+\n '<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">'+\n '<title>Starmux · Workspace Permissions</title>'+\n '<style>body{font:15px system-ui,-apple-system,sans-serif;max-width:940px;margin:30px auto;padding:0 20px;color:#233045;background:#f6f7fa}'+\n 'section{background:white;border:1px solid #ddd;padding:20px;margin:15px 0;border-radius:12px}'+\n 'table{border-collapse:collapse;width:100%}td,th{padding:10px;border-bottom:1px solid #ddd;text-align:left}'+\n 'input,select,button{font:inherit;padding:6px;margin:4px}input[name=target]{width:90%;max-width:550px}'+\n 'small{display:block;color:#657083}code{overflow-wrap:anywhere}button{cursor:pointer}</style></head><body>'+\n '<h1>Starmux · 目录授权</h1>'+\n '<p>本页在 Mac 本机管理 Gateway 权限。远程 AI 不可自行增加权限。'+\n '增加或撤销授权后,下一次工具调用立即使用新权限。</p>'+\n (warning?'<section role=\"alert\">'+e(warning)+'</section>':\"\")+\n (nativeAccessEnabled?'<section role=\"alert\"><h2>可信开发模式 · 完整本地执行权限</h2>'+\n '<p>此设备所有者启用了原生 Desktop Commander 工具透传。'+\n '以下 Home/外部/自定义目录开关<strong>不限制原生 DC 文件和 Shell 工具</strong>;'+\n '可访问范围由 Desktop Commander 和 macOS 用户账户决定。'+\n '开关仅在切回受限工作区模式后生效。请勿将此服务暴露给未经认证的客户端。</p>'+\n '</section>':\"\")+\n '<section><h2>工作目录 · 读写范围</h2><code>'+e(project.path)+'</code>'+\n '<p>工作模式:<b>'+e(project.mode)+'</b>。该项目沿用原有 Gateway 安全规则。</p></section>'+\n '<section><h2>Home · 只读</h2><code>'+e(home)+'</code>'+\n '<p>当前:<b>'+(config.homeRead?\"已开启只读扫描\":\"未授权\")+'</b></p>'+\n selector(null,config.homeRead?\"home-disable\":\"home-enable\",\n config.homeRead?\"关闭 Home 读取\":\"允许 Home 只读\",\n \"默认仍保护 SSH、密钥、凭据及配置路径。\")+'</section>'+\n '<section><h2>Home 外 · 只读</h2>'+\n '<p>当前:<b>'+(config.outsideRead?\"已开启只读扫描\":\"未授权\")+'</b></p>'+\n selector(null,config.outsideRead?\"outside-disable\":\"outside-enable\",\n config.outsideRead?\"关闭 Home 外读取\":\"允许 Home 外只读\",\n \"不会授予删除、任意 Shell 或写操作。\")+'</section>'+\n '<section><h2>自定义目录 · 单独授权</h2>'+\n '<table><thead><tr><th>目录</th><th>权限</th><th>操作</th></tr></thead><tbody>'+\n (rows||'<tr><td colspan=\"3\">暂无自定义授权</td></tr>')+'</tbody></table>'+\n '<h3>新增或修改</h3><form method=\"post\" action=\"/permissions\">'+\n '<input type=\"hidden\" name=\"op\" value=\"add\">'+\n '<label>绝对路径 <input type=\"text\" name=\"target\" required placeholder=\"/Volumes/Disk/Folder\"></label>'+\n '<label>权限 <select name=\"mode\"><option value=\"read-only\">只读</option>'+\n '<option value=\"read-write\">读写</option></select></label>'+\n '<label>管理密钥 <input type=\"password\" name=\"token\" required autocomplete=\"off\"></label>'+\n '<button type=\"submit\">确认授权</button></form></section>'+\n '<p><a href=\"/dashboard\">返回运行 Dashboard</a> · '+\n '<a href=\"/filters\">工具黑名单</a></p>'+\n '<small>授权管理密钥仅在 Mac 本地终端显示;不可交给远程 Agent。'+\n '本页不改变 trusted-project 的同 UID Shell 风险。</small></body></html>';\n}\nexport async function applyPermissionPost(req: IncomingMessage,grants: WorkspaceGrants,host: string|undefined): Promise<PostResult>{\n if(req.headers.origin!==\"http://\"+host||\n (req.headers[\"content-type\"]||\"\").split(\";\")[0]!==\"application/x-www-form-urlencoded\")\n return {status:403,message:\"Owner authorization required\"};\n const size=Number(req.headers[\"content-length\"]||0);\n if(!isSafeInteger(size)||size>4096||size<1)\n return {status:413,message:\"Invalid request\"};\n let body=\"\";\n for await(const chunk of req){\n body+=chunk.toString(\"utf8\");\n if(Buffer.byteLength(body)>4096)return {status:413,message:\"Invalid request\"};\n }\n const q=new URLSearchParams(body);\n for(const key of q.keys())\n if(![\"op\",\"target\",\"mode\",\"token\"].includes(key)||q.getAll(key).length!==1)\n return {status:400,message:\"Invalid form\"};\n if(!await grants.authorized(q.get(\"token\")))\n return {status:403,message:\"Owner authorization required\"};\n const operation=q.get(\"op\")??\"\";\n if(![\"home-enable\",\"home-disable\",\"outside-enable\",\"outside-disable\",\"add\",\"revoke\"]\n .includes(operation))return {status:400,message:\"Invalid operation\"};\n if(operation===\"add\"&&![\"read-only\",\"read-write\"].includes(q.get(\"mode\")??\"\"))\n return {status:400,message:\"Invalid mode\"};\n try{\n await grants.update(operation,{target:q.get(\"target\"),mode:q.get(\"mode\")});\n return {status:303,location:\"/permissions\"};\n }catch{\n return {status:400,message:\"Grant rejected: verify directory and permissions\"};\n }\n}\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
2
|
import { fileURLToPath } from 'node:url';
|
|
4
3
|
// This module executes from <installation>/dist/src, never directly as TS.
|
|
5
4
|
export const packageRoot = fileURLToPath(new URL('../../', import.meta.url));
|
|
6
5
|
export const cliEntry = (name = 'starmux') => path.join(packageRoot, 'bin', name + '.mjs');
|
|
7
|
-
//
|
|
8
|
-
export const desktopCommanderEntry =
|
|
6
|
+
// Vendored zero-telemetry build of Desktop Commander bundled inside dist/vendor.
|
|
7
|
+
export const desktopCommanderEntry = path.join(packageRoot, 'dist', 'vendor', 'desktop-commander', 'dist', 'index.js');
|
|
8
|
+
export const desktopCommanderPackageJson = path.join(packageRoot, 'dist', 'vendor', 'desktop-commander', 'package.json');
|
|
9
9
|
export const fixtureRoot = path.join(packageRoot, 'test', 'fixtures');
|
|
10
10
|
export const legacyCliEntry = () => path.join(packageRoot, 'dist/src/cli/mac-mcp-gateway.js');
|
|
11
11
|
//# sourceMappingURL=runtime-paths.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-paths.js","sourceRoot":"","sources":["../../src/runtime-paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,
|
|
1
|
+
{"version":3,"file":"runtime-paths.js","sourceRoot":"","sources":["../../src/runtime-paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAI,GAAG,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;AAC3F,iFAAiF;AACjF,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AACvH,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,EAAE,cAAc,CAAC,CAAC;AACzH,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AAEtE,MAAM,CAAC,MAAM,cAAc,GAAC,GAAE,EAAE,CAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAC,iCAAiC,CAAC,CAAC","sourcesContent":["import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n// This module executes from <installation>/dist/src, never directly as TS.\nexport const packageRoot = fileURLToPath(new URL('../../', import.meta.url));\nexport const cliEntry = (name = 'starmux') => path.join(packageRoot, 'bin', name + '.mjs');\n// Vendored zero-telemetry build of Desktop Commander bundled inside dist/vendor.\nexport const desktopCommanderEntry = path.join(packageRoot, 'dist', 'vendor', 'desktop-commander', 'dist', 'index.js');\nexport const desktopCommanderPackageJson = path.join(packageRoot, 'dist', 'vendor', 'desktop-commander', 'package.json');\nexport const fixtureRoot = path.join(packageRoot, 'test', 'fixtures');\n\nexport const legacyCliEntry=()=>path.join(packageRoot,'dist/src/cli/mac-mcp-gateway.js');\n"]}
|
|
@@ -126,7 +126,7 @@ async function migrateRegistry() {
|
|
|
126
126
|
export async function migrate(source, yes = false, json = false) {
|
|
127
127
|
if (!source) {
|
|
128
128
|
const registry = await exists(path.join(oldRoot(), 'registry.json'));
|
|
129
|
-
const legacy = await migrateLegacy();
|
|
129
|
+
const legacy = { ...await migrateLegacy(), message: 'Choose a source, then run starmux migrate setup or starmux migrate live. Use -y for non-interactive confirmation. Running legacy instances using these stores will be stopped; original files are retained.' };
|
|
130
130
|
process.stdout.write(JSON.stringify({ registry: registry ? path.join(oldRoot(), 'registry.json') : null, legacy, next: 'starmux migrate registry|setup|live [-y]' }, null, 2) + '\n');
|
|
131
131
|
return;
|
|
132
132
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"starmux-migration.js","sourceRoot":"","sources":["../../src/starmux-migration.ts"],"names":[],"mappings":"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","sourcesContent":["import {homedir} from 'node:os';\nimport path from 'node:path';\nimport {readdir,rename,rm,rmdir,lstat,mkdir} from 'node:fs/promises';\nimport {createInterface} from 'node:readline/promises';\nimport {createPrivateDirectory} from './platform.js';\nimport {instanceRequest} from './instance-runtime.js';\nimport {createAccessControl} from './access-control.js';\nimport {migrateLegacy} from './instance-migration.js';\nimport {atomicPrivate,configTemplate,exists,instancesRoot,parseConfig,readRegistry,readTrusted,registrySchema,sourceRevision,fingerprint,validateConfig,withLock} from './instance-store.js';\n\nconst oldRoot=()=>path.join(homedir(),'.config','mac-mcp-gateway');\n// Only recover locks positively identified as belonging to a dead migration process.\nasync function migrationLock<T>(file:string,action:()=>Promise<T>):Promise<T>{\n const marker=path.join(file,'starmux-migration-owner.json');\n if(await exists(marker)){\n const recovery=file+'.starmux-recovery';await mkdir(recovery,{mode:0o700});\n try{\n const owner=JSON.parse(await readTrusted(marker)) as {pid:number;source:string};\n if(!Number.isSafeInteger(owner.pid)||owner.pid<2||owner.source!==oldRoot())throw Error('Unrecognized migration lock.');\n let alive=true;try{process.kill(owner.pid,0);}catch(error){if((error as NodeJS.ErrnoException).code==='ESRCH')alive=false;else throw error;}\n if(!alive)await rm(file,{recursive:true});\n }finally{await rmdir(recovery);}\n }\n return withLock(file,async()=>{\n await atomicPrivate(marker,JSON.stringify({pid:process.pid,source:oldRoot()}));\n return action();\n });\n}\nasync function copyPrivate(source:string,target:string){\n const info=await lstat(source);\n if(info.isSymbolicLink())throw Error('Migration refuses symbolic links: '+source);\n if(info.isDirectory()){\n createPrivateDirectory(target);\n for(const name of await readdir(source)){\n if(name.endsWith('.lock')||name.endsWith('-lock')||name==='last-runtime.json')continue;\n await copyPrivate(path.join(source,name),path.join(target,name));\n }\n }else await atomicPrivate(target,await readTrusted(source,true,32*1024*1024));\n}\nasync function migrateRegistry(){\n const source=oldRoot(),target=instancesRoot(),stage=target+'.migration';\n if(await exists(target)&&(await readdir(target)).length)throw Error('Migration destination already exists; no files were overwritten.');\n return migrationLock(path.join(source,'.registry-lock'),async()=>{\n const registry=registrySchema.parse(JSON.parse(await readTrusted(path.join(source,'registry.json'))));\n if(new Set(registry.instances.map(i=>i.id)).size!==registry.instances.length||new Set(registry.instances.map(i=>i.name)).size!==registry.instances.length)throw Error('Invalid duplicate identities in old registry.');\n for(const instance of registry.instances){\n const managed=path.join(source,'instances',instance.id,'config.jsonc');\n if(instance.source.kind==='managed'&&instance.source.path!==managed)throw Error('Invalid old managed source path.');\n await validateConfig(parseConfig(await readTrusted(instance.source.path,instance.source.kind==='managed')));\n }\n if(await exists(stage)){\n const marker=JSON.parse(await readTrusted(path.join(stage,'migration.json'))) as {source?:string};\n if(marker.source!==source)throw Error('Unrecognized migration staging directory.');\n await rm(stage,{recursive:true});\n }\n createPrivateDirectory(stage);\n await atomicPrivate(path.join(stage,'migration.json'),JSON.stringify({source,result:'PREPARING',oldFilesRetained:true}));\n for(const instance of registry.instances){\n const stopped=await instanceRequest(instance,'stop');\n if(stopped&&stopped.phase!=='stopped')throw Error('Could not stop old instance '+instance.name);\n // Wait until the old supervisor has released its control identity.\n for(let n=0;n<100;n++){\n try{if(!await instanceRequest(instance,'status'))break;}catch{break;}\n if(n===99)throw Error('Old supervisor is still exiting: '+instance.name);\n await new Promise(r=>setTimeout(r,100));\n }\n const from=path.join(source,'instances',instance.id),to=path.join(stage,'instances',instance.id);\n await migrationLock(path.join(from,'state','access-v1.json.lock'),async()=>{await copyPrivate(from,to);});\n if(instance.source.kind==='managed')instance.source.path=path.join(target,'instances',instance.id,'config.jsonc');\n const accessFile=path.join(to,'state','access-v1.json');\n if(await exists(accessFile))await (await createAccessControl(accessFile)).snapshot();\n else if(instance.requiredAccess!=='ingress'||await exists(accessFile+'.initialized'))throw Error('Missing old authentication state: '+instance.name);\n const snapshot=path.join(to,'snapshots','active.json');\n if(await exists(snapshot)){\n const active=JSON.parse(await readTrusted(snapshot)) as {config:unknown;sourcePath:string;revision:string;effectiveRevision:string};\n const config=await validateConfig(parseConfig(JSON.stringify(active.config)));\n active.sourcePath=instance.source.path;\n active.revision=sourceRevision(instance.source.path,configTemplate(config));\n active.effectiveRevision=fingerprint(JSON.stringify({...config,$schema:undefined}));\n await atomicPrivate(snapshot,JSON.stringify(active));\n }\n }\n if(await exists(path.join(source,'gateway.schema.json')))await copyPrivate(path.join(source,'gateway.schema.json'),path.join(stage,'gateway.schema.json'));\n await atomicPrivate(path.join(stage,'registry.json'),JSON.stringify(registry,null,2));\n await atomicPrivate(path.join(stage,'migration.json'),JSON.stringify({source,result:'MIGRATED_NOT_STARTED',oldFilesRetained:true,at:new Date().toISOString()}));\n if(await exists(target))await rmdir(target);\n await rename(stage,target);\n return {result:'MIGRATED_NOT_STARTED',instances:registry.instances.length,oldFilesRetained:true};\n });\n}\nexport async function migrate(source:string|undefined,yes=false,json=false){\n if(!source){\n const registry=await exists(path.join(oldRoot(),'registry.json'));\n const legacy=await migrateLegacy();\n process.stdout.write(JSON.stringify({registry:registry?path.join(oldRoot(),'registry.json'):null,legacy,next:'starmux migrate registry|setup|live [-y]'},null,2)+'\\n');return;\n }\n if(!['registry','setup','live'].includes(source))throw Error('Choose registry, setup or live.');\n if(source!=='registry'&&await exists(path.join(oldRoot(),'registry.json')))throw Error('Old registry found. Migrate registry to preserve existing identities.');\n if(!yes){\n if(!process.stdin.isTTY)throw Error('Migration stops the selected old gateway. Re-run with -y to confirm.');\n const rl=createInterface({input:process.stdin,output:process.stderr});\n try{if(!/^y(es)?$/i.test((await rl.question('Migrate '+source+' and stop its old processes? Originals will be retained. [y/N] ')).trim()))throw Error('Cancelled.');}finally{rl.close();}\n }\n let result:unknown;\n if(source==='registry')result=await migrateRegistry();\n else {\n const marker=path.join(instancesRoot(),'migration-in-progress.json');\n if(await exists(instancesRoot())&&(await readdir(instancesRoot())).length){\n if(!await exists(marker))throw Error('Migration destination already exists.');\n const previous=JSON.parse(await readTrusted(marker)) as {source:string};\n if(previous.source!==source||(await readRegistry()).instances.length)throw Error('Migration destination already has instances.');\n }\n createPrivateDirectory(instancesRoot());await atomicPrivate(marker,JSON.stringify({source}));\n result=await migrateLegacy(source,true);\n await atomicPrivate(path.join(instancesRoot(),'migration.json'),JSON.stringify({source,result:'MIGRATED_NOT_STARTED',oldFilesRetained:true}));await rm(marker);\n }\n if(json)process.stdout.write(JSON.stringify(result,null,2)+'\\n');else process.stdout.write('Migrated. Originals retained. Start with starmux up.\\n');\n}\n"]}
|
|
1
|
+
{"version":3,"file":"starmux-migration.js","sourceRoot":"","sources":["../../src/starmux-migration.ts"],"names":[],"mappings":"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","sourcesContent":["import {homedir} from 'node:os';\nimport path from 'node:path';\nimport {readdir,rename,rm,rmdir,lstat,mkdir} from 'node:fs/promises';\nimport {createInterface} from 'node:readline/promises';\nimport {createPrivateDirectory} from './platform.js';\nimport {instanceRequest} from './instance-runtime.js';\nimport {createAccessControl} from './access-control.js';\nimport {migrateLegacy} from './instance-migration.js';\nimport {atomicPrivate,configTemplate,exists,instancesRoot,parseConfig,readRegistry,readTrusted,registrySchema,sourceRevision,fingerprint,validateConfig,withLock} from './instance-store.js';\n\nconst oldRoot=()=>path.join(homedir(),'.config','mac-mcp-gateway');\n// Only recover locks positively identified as belonging to a dead migration process.\nasync function migrationLock<T>(file:string,action:()=>Promise<T>):Promise<T>{\n const marker=path.join(file,'starmux-migration-owner.json');\n if(await exists(marker)){\n const recovery=file+'.starmux-recovery';await mkdir(recovery,{mode:0o700});\n try{\n const owner=JSON.parse(await readTrusted(marker)) as {pid:number;source:string};\n if(!Number.isSafeInteger(owner.pid)||owner.pid<2||owner.source!==oldRoot())throw Error('Unrecognized migration lock.');\n let alive=true;try{process.kill(owner.pid,0);}catch(error){if((error as NodeJS.ErrnoException).code==='ESRCH')alive=false;else throw error;}\n if(!alive)await rm(file,{recursive:true});\n }finally{await rmdir(recovery);}\n }\n return withLock(file,async()=>{\n await atomicPrivate(marker,JSON.stringify({pid:process.pid,source:oldRoot()}));\n return action();\n });\n}\nasync function copyPrivate(source:string,target:string){\n const info=await lstat(source);\n if(info.isSymbolicLink())throw Error('Migration refuses symbolic links: '+source);\n if(info.isDirectory()){\n createPrivateDirectory(target);\n for(const name of await readdir(source)){\n if(name.endsWith('.lock')||name.endsWith('-lock')||name==='last-runtime.json')continue;\n await copyPrivate(path.join(source,name),path.join(target,name));\n }\n }else await atomicPrivate(target,await readTrusted(source,true,32*1024*1024));\n}\nasync function migrateRegistry(){\n const source=oldRoot(),target=instancesRoot(),stage=target+'.migration';\n if(await exists(target)&&(await readdir(target)).length)throw Error('Migration destination already exists; no files were overwritten.');\n return migrationLock(path.join(source,'.registry-lock'),async()=>{\n const registry=registrySchema.parse(JSON.parse(await readTrusted(path.join(source,'registry.json'))));\n if(new Set(registry.instances.map(i=>i.id)).size!==registry.instances.length||new Set(registry.instances.map(i=>i.name)).size!==registry.instances.length)throw Error('Invalid duplicate identities in old registry.');\n for(const instance of registry.instances){\n const managed=path.join(source,'instances',instance.id,'config.jsonc');\n if(instance.source.kind==='managed'&&instance.source.path!==managed)throw Error('Invalid old managed source path.');\n await validateConfig(parseConfig(await readTrusted(instance.source.path,instance.source.kind==='managed')));\n }\n if(await exists(stage)){\n const marker=JSON.parse(await readTrusted(path.join(stage,'migration.json'))) as {source?:string};\n if(marker.source!==source)throw Error('Unrecognized migration staging directory.');\n await rm(stage,{recursive:true});\n }\n createPrivateDirectory(stage);\n await atomicPrivate(path.join(stage,'migration.json'),JSON.stringify({source,result:'PREPARING',oldFilesRetained:true}));\n for(const instance of registry.instances){\n const stopped=await instanceRequest(instance,'stop');\n if(stopped&&stopped.phase!=='stopped')throw Error('Could not stop old instance '+instance.name);\n // Wait until the old supervisor has released its control identity.\n for(let n=0;n<100;n++){\n try{if(!await instanceRequest(instance,'status'))break;}catch{break;}\n if(n===99)throw Error('Old supervisor is still exiting: '+instance.name);\n await new Promise(r=>setTimeout(r,100));\n }\n const from=path.join(source,'instances',instance.id),to=path.join(stage,'instances',instance.id);\n await migrationLock(path.join(from,'state','access-v1.json.lock'),async()=>{await copyPrivate(from,to);});\n if(instance.source.kind==='managed')instance.source.path=path.join(target,'instances',instance.id,'config.jsonc');\n const accessFile=path.join(to,'state','access-v1.json');\n if(await exists(accessFile))await (await createAccessControl(accessFile)).snapshot();\n else if(instance.requiredAccess!=='ingress'||await exists(accessFile+'.initialized'))throw Error('Missing old authentication state: '+instance.name);\n const snapshot=path.join(to,'snapshots','active.json');\n if(await exists(snapshot)){\n const active=JSON.parse(await readTrusted(snapshot)) as {config:unknown;sourcePath:string;revision:string;effectiveRevision:string};\n const config=await validateConfig(parseConfig(JSON.stringify(active.config)));\n active.sourcePath=instance.source.path;\n active.revision=sourceRevision(instance.source.path,configTemplate(config));\n active.effectiveRevision=fingerprint(JSON.stringify({...config,$schema:undefined}));\n await atomicPrivate(snapshot,JSON.stringify(active));\n }\n }\n if(await exists(path.join(source,'gateway.schema.json')))await copyPrivate(path.join(source,'gateway.schema.json'),path.join(stage,'gateway.schema.json'));\n await atomicPrivate(path.join(stage,'registry.json'),JSON.stringify(registry,null,2));\n await atomicPrivate(path.join(stage,'migration.json'),JSON.stringify({source,result:'MIGRATED_NOT_STARTED',oldFilesRetained:true,at:new Date().toISOString()}));\n if(await exists(target))await rmdir(target);\n await rename(stage,target);\n return {result:'MIGRATED_NOT_STARTED',instances:registry.instances.length,oldFilesRetained:true};\n });\n}\nexport async function migrate(source:string|undefined,yes=false,json=false){\n if(!source){\n const registry=await exists(path.join(oldRoot(),'registry.json'));\n const legacy={...await migrateLegacy(),message:'Choose a source, then run starmux migrate setup or starmux migrate live. Use -y for non-interactive confirmation. Running legacy instances using these stores will be stopped; original files are retained.'};\n process.stdout.write(JSON.stringify({registry:registry?path.join(oldRoot(),'registry.json'):null,legacy,next:'starmux migrate registry|setup|live [-y]'},null,2)+'\\n');return;\n }\n if(!['registry','setup','live'].includes(source))throw Error('Choose registry, setup or live.');\n if(source!=='registry'&&await exists(path.join(oldRoot(),'registry.json')))throw Error('Old registry found. Migrate registry to preserve existing identities.');\n if(!yes){\n if(!process.stdin.isTTY)throw Error('Migration stops the selected old gateway. Re-run with -y to confirm.');\n const rl=createInterface({input:process.stdin,output:process.stderr});\n try{if(!/^y(es)?$/i.test((await rl.question('Migrate '+source+' and stop its old processes? Originals will be retained. [y/N] ')).trim()))throw Error('Cancelled.');}finally{rl.close();}\n }\n let result:unknown;\n if(source==='registry')result=await migrateRegistry();\n else {\n const marker=path.join(instancesRoot(),'migration-in-progress.json');\n if(await exists(instancesRoot())&&(await readdir(instancesRoot())).length){\n if(!await exists(marker))throw Error('Migration destination already exists.');\n const previous=JSON.parse(await readTrusted(marker)) as {source:string};\n if(previous.source!==source||(await readRegistry()).instances.length)throw Error('Migration destination already has instances.');\n }\n createPrivateDirectory(instancesRoot());await atomicPrivate(marker,JSON.stringify({source}));\n result=await migrateLegacy(source,true);\n await atomicPrivate(path.join(instancesRoot(),'migration.json'),JSON.stringify({source,result:'MIGRATED_NOT_STARTED',oldFilesRetained:true}));await rm(marker);\n }\n if(json)process.stdout.write(JSON.stringify(result,null,2)+'\\n');else process.stdout.write('Migrated. Originals retained. Start with starmux up.\\n');\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 Eduard Ruzga and Desktop Commander Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threadpool bootstrap. MUST be the first import in index.ts.
|
|
3
|
+
*
|
|
4
|
+
* Every fs operation (read_file, write_file, edit_block, config/history/log
|
|
5
|
+
* persistence) runs on libuv's threadpool, which defaults to only 4 threads.
|
|
6
|
+
* Under heavy parallel load — e.g. several agents reading/writing files on a
|
|
7
|
+
* slow or cloud-synced filesystem — 4 stalled operations exhaust the pool, and
|
|
8
|
+
* because a stalled syscall keeps its thread until the OS returns (a JS-level
|
|
9
|
+
* timeout does not cancel it), every subsequent fs op queues for minutes. That
|
|
10
|
+
* surfaced as multi-minute tool-call hangs under parallel `claude -p` load.
|
|
11
|
+
*
|
|
12
|
+
* Raising the pool size gives enough headroom that a burst of slow reads no
|
|
13
|
+
* longer starves the rest. libuv reads UV_THREADPOOL_SIZE only when the pool is
|
|
14
|
+
* first initialized (on first submitted work), so this assignment has to happen
|
|
15
|
+
* before ANY threadpool work — hence "first import". A user-provided value is
|
|
16
|
+
* always respected.
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_THREADPOOL_SIZE = 16;
|
|
19
|
+
if (!process.env.UV_THREADPOOL_SIZE) {
|
|
20
|
+
process.env.UV_THREADPOOL_SIZE = String(DEFAULT_THREADPOOL_SIZE);
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { configManager } from './config-manager.js';
|
|
3
|
+
import { capture } from "./utils/capture.js";
|
|
4
|
+
class CommandManager {
|
|
5
|
+
getBaseCommand(command) {
|
|
6
|
+
return command.split(' ')[0].toLowerCase().trim();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The command name of each command in a command line (chained, piped, in $(), ...).
|
|
10
|
+
* asTyped: each first word as typed instead of its base name, for telemetry,
|
|
11
|
+
* which then replaces a path whole (a base name alone can't be told from one).
|
|
12
|
+
*/
|
|
13
|
+
extractCommands(commandString, asTyped = false) {
|
|
14
|
+
try {
|
|
15
|
+
// Trim any leading/trailing whitespace
|
|
16
|
+
commandString = commandString.trim();
|
|
17
|
+
// Define command separators - these are the operators that can chain commands
|
|
18
|
+
const separators = [';', '&&', '||', '|', '&'];
|
|
19
|
+
// This will store our extracted commands
|
|
20
|
+
const commands = [];
|
|
21
|
+
// Split by common separators while preserving quotes
|
|
22
|
+
let inQuote = false;
|
|
23
|
+
let quoteChar = '';
|
|
24
|
+
let currentCmd = '';
|
|
25
|
+
let escaped = false;
|
|
26
|
+
for (let i = 0; i < commandString.length; i++) {
|
|
27
|
+
const char = commandString[i];
|
|
28
|
+
// Handle escape characters
|
|
29
|
+
if (char === '\\' && !escaped) {
|
|
30
|
+
escaped = true;
|
|
31
|
+
currentCmd += char;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// If this character is escaped, just add it
|
|
35
|
+
if (escaped) {
|
|
36
|
+
escaped = false;
|
|
37
|
+
currentCmd += char;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
// Handle quotes (both single and double)
|
|
41
|
+
if ((char === '"' || char === "'") && !inQuote) {
|
|
42
|
+
inQuote = true;
|
|
43
|
+
quoteChar = char;
|
|
44
|
+
currentCmd += char;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
else if (char === quoteChar && inQuote) {
|
|
48
|
+
inQuote = false;
|
|
49
|
+
quoteChar = '';
|
|
50
|
+
currentCmd += char;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Handle $() command substitution even inside quotes (fixes blocklist bypass)
|
|
54
|
+
if (char === '$' && i + 1 < commandString.length && commandString[i + 1] === '(') {
|
|
55
|
+
const startIndex = i;
|
|
56
|
+
let openParens = 1;
|
|
57
|
+
let j = i + 2; // skip past $(
|
|
58
|
+
while (j < commandString.length && openParens > 0) {
|
|
59
|
+
if (commandString[j] === '(')
|
|
60
|
+
openParens++;
|
|
61
|
+
if (commandString[j] === ')')
|
|
62
|
+
openParens--;
|
|
63
|
+
j++;
|
|
64
|
+
}
|
|
65
|
+
if (j <= commandString.length && openParens === 0) {
|
|
66
|
+
const subContent = commandString.substring(i + 2, j - 1);
|
|
67
|
+
const subCommands = this.extractCommands(subContent, asTyped);
|
|
68
|
+
commands.push(...subCommands);
|
|
69
|
+
i = j - 1;
|
|
70
|
+
if (!inQuote) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
currentCmd += commandString.substring(startIndex, j);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Handle backtick command substitution even inside quotes
|
|
80
|
+
if (char === '`') {
|
|
81
|
+
const startIndex = i;
|
|
82
|
+
let j = i + 1;
|
|
83
|
+
while (j < commandString.length && commandString[j] !== '`') {
|
|
84
|
+
j++;
|
|
85
|
+
}
|
|
86
|
+
if (j < commandString.length) {
|
|
87
|
+
const subContent = commandString.substring(i + 1, j);
|
|
88
|
+
const subCommands = this.extractCommands(subContent, asTyped);
|
|
89
|
+
commands.push(...subCommands);
|
|
90
|
+
i = j;
|
|
91
|
+
if (!inQuote) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
currentCmd += commandString.substring(startIndex, j + 1);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// If we're inside quotes, just add the character
|
|
101
|
+
if (inQuote) {
|
|
102
|
+
currentCmd += char;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// Handle subshells - if we see an opening parenthesis, we need to find its matching closing parenthesis
|
|
106
|
+
if (char === '(') {
|
|
107
|
+
// Find the matching closing parenthesis
|
|
108
|
+
let openParens = 1;
|
|
109
|
+
let j = i + 1;
|
|
110
|
+
while (j < commandString.length && openParens > 0) {
|
|
111
|
+
if (commandString[j] === '(')
|
|
112
|
+
openParens++;
|
|
113
|
+
if (commandString[j] === ')')
|
|
114
|
+
openParens--;
|
|
115
|
+
j++;
|
|
116
|
+
}
|
|
117
|
+
// Skip to after the closing parenthesis only if properly balanced
|
|
118
|
+
if (j <= commandString.length && openParens === 0) {
|
|
119
|
+
const subshellContent = commandString.substring(i + 1, j - 1);
|
|
120
|
+
// Recursively extract commands from the subshell
|
|
121
|
+
const subCommands = this.extractCommands(subshellContent, asTyped);
|
|
122
|
+
commands.push(...subCommands);
|
|
123
|
+
// Move position past the subshell
|
|
124
|
+
i = j - 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Check for separators
|
|
129
|
+
let isSeparator = false;
|
|
130
|
+
for (const separator of separators) {
|
|
131
|
+
if (commandString.startsWith(separator, i)) {
|
|
132
|
+
// We found a separator - extract the command before it
|
|
133
|
+
if (currentCmd.trim()) {
|
|
134
|
+
const baseCommand = this.extractBaseCommand(currentCmd.trim(), asTyped);
|
|
135
|
+
if (baseCommand)
|
|
136
|
+
commands.push(baseCommand);
|
|
137
|
+
}
|
|
138
|
+
// Move past the separator
|
|
139
|
+
i += separator.length - 1;
|
|
140
|
+
currentCmd = '';
|
|
141
|
+
isSeparator = true;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (!isSeparator) {
|
|
146
|
+
currentCmd += char;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Don't forget to add the last command
|
|
150
|
+
if (currentCmd.trim()) {
|
|
151
|
+
const baseCommand = this.extractBaseCommand(currentCmd.trim(), asTyped);
|
|
152
|
+
if (baseCommand)
|
|
153
|
+
commands.push(baseCommand);
|
|
154
|
+
}
|
|
155
|
+
// Remove duplicates and return
|
|
156
|
+
return [...new Set(commands)];
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
// If anything goes wrong, log the error but return the basic command to not break execution
|
|
160
|
+
capture('server_request_error', {
|
|
161
|
+
error: 'Error extracting commands'
|
|
162
|
+
});
|
|
163
|
+
const baseCmd = this.extractBaseCommand(commandString, asTyped);
|
|
164
|
+
return baseCmd ? [baseCmd] : [];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// This extracts the actual command name from a command string (asTyped: see extractCommands)
|
|
168
|
+
extractBaseCommand(commandStr, asTyped = false) {
|
|
169
|
+
try {
|
|
170
|
+
// Remove environment variables (patterns like KEY=value)
|
|
171
|
+
const withoutEnvVars = commandStr.replace(/\w+=\S+\s*/g, '').trim();
|
|
172
|
+
// If nothing remains after removing env vars, return null
|
|
173
|
+
if (!withoutEnvVars)
|
|
174
|
+
return null;
|
|
175
|
+
// Get the first token (the command)
|
|
176
|
+
const tokens = withoutEnvVars.split(/\s+/);
|
|
177
|
+
let firstToken = null;
|
|
178
|
+
// Find the first valid token (skip variables)
|
|
179
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
180
|
+
const token = tokens[i];
|
|
181
|
+
// Skip dollar-prefixed tokens (variables) but not $() command substitutions
|
|
182
|
+
if (token.startsWith('$') && !token.startsWith('$(')) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
// Check if it starts with special characters like ( that might indicate it's not a regular command
|
|
186
|
+
if (token[0] === '(') {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
firstToken = token;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
// No valid command token found
|
|
193
|
+
if (!firstToken) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
// handle $() command substitution - extract the inner command
|
|
197
|
+
if (firstToken.startsWith('$(') && firstToken.endsWith(')')) {
|
|
198
|
+
const inner = firstToken.slice(2, -1).trim();
|
|
199
|
+
if (inner) {
|
|
200
|
+
const innerTokens = inner.split(/\s+/);
|
|
201
|
+
return (asTyped ? innerTokens[0] : path.basename(innerTokens[0])).toLowerCase();
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
// strip path prefix so /usr/bin/sudo gets caught as "sudo"
|
|
206
|
+
const baseName = asTyped ? firstToken : path.basename(firstToken);
|
|
207
|
+
return baseName.toLowerCase();
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
capture('Error extracting base command');
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async validateCommand(command) {
|
|
215
|
+
try {
|
|
216
|
+
// Get blocked commands from config
|
|
217
|
+
const config = await configManager.getConfig();
|
|
218
|
+
const blockedCommands = config.blockedCommands || [];
|
|
219
|
+
// Extract all commands from the command string
|
|
220
|
+
const allCommands = this.extractCommands(command);
|
|
221
|
+
// If there are no commands extracted, fall back to base command
|
|
222
|
+
if (allCommands.length === 0) {
|
|
223
|
+
const baseCommand = this.getBaseCommand(command);
|
|
224
|
+
return !blockedCommands.includes(baseCommand);
|
|
225
|
+
}
|
|
226
|
+
// Check if any of the extracted commands are in the blocked list
|
|
227
|
+
for (const cmd of allCommands) {
|
|
228
|
+
if (blockedCommands.includes(cmd)) {
|
|
229
|
+
return false; // Command is blocked
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// No commands were blocked
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
console.error('Error validating command:', error);
|
|
237
|
+
capture('server_validate_command_error', {
|
|
238
|
+
error: error instanceof Error ? error.message : String(error)
|
|
239
|
+
});
|
|
240
|
+
// Fail closed: deny the command if validation encounters an error.
|
|
241
|
+
// This prevents a config read failure from bypassing all command filtering.
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
export const commandManager = new CommandManager();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Single source of truth for user-editable configuration fields.
|
|
2
|
+
export const CONFIG_FIELD_DEFINITIONS = {
|
|
3
|
+
blockedCommands: {
|
|
4
|
+
label: 'Blocked Commands',
|
|
5
|
+
description: 'This is your personal safety blocklist. If a command appears here, Desktop Commander will refuse to run it even if a prompt asks for it. Add risky commands you never want executed by mistake.',
|
|
6
|
+
valueType: 'array',
|
|
7
|
+
},
|
|
8
|
+
allowedDirectories: {
|
|
9
|
+
label: 'Allowed Folders',
|
|
10
|
+
description: 'These are the folders Desktop Commander is allowed to read and edit. Think of this as a permission list. Keeping it small is safer. If this list is empty, Desktop Commander can access your entire filesystem.',
|
|
11
|
+
valueType: 'array',
|
|
12
|
+
},
|
|
13
|
+
defaultShell: {
|
|
14
|
+
label: 'Default Shell',
|
|
15
|
+
description: 'This is the shell used for new command sessions (for example /bin/bash or /bin/zsh). Only change this if you know your environment requires a specific shell.',
|
|
16
|
+
valueType: 'string',
|
|
17
|
+
},
|
|
18
|
+
telemetryEnabled: {
|
|
19
|
+
label: 'Anonymous Telemetry',
|
|
20
|
+
description: 'When on, Desktop Commander sends anonymous usage information that helps improve product quality. When off, no telemetry data is sent.',
|
|
21
|
+
valueType: 'boolean',
|
|
22
|
+
},
|
|
23
|
+
fileReadLineLimit: {
|
|
24
|
+
label: 'File Read Limit',
|
|
25
|
+
description: 'Maximum number of lines returned from a file in one read action. Lower numbers keep responses short and safer; higher numbers return more text at once.',
|
|
26
|
+
valueType: 'number',
|
|
27
|
+
},
|
|
28
|
+
fileWriteLineLimit: {
|
|
29
|
+
label: 'File Write Limit',
|
|
30
|
+
description: 'Maximum number of lines that can be written in one edit operation. This helps prevent accidental oversized writes and keeps file changes predictable.',
|
|
31
|
+
valueType: 'number',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export const CONFIG_FIELD_KEYS = Object.keys(CONFIG_FIELD_DEFINITIONS);
|
|
35
|
+
export function isConfigFieldKey(value) {
|
|
36
|
+
return Object.prototype.hasOwnProperty.call(CONFIG_FIELD_DEFINITIONS, value);
|
|
37
|
+
}
|