@tencent-ai/agent-sdk 0.3.206 → 0.3.210

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/cli/CHANGELOG.md +61 -0
  2. package/cli/builtin/workflows/deep-research.workflow.js +77 -20
  3. package/cli/dist/codebuddy-headless.js +294 -246
  4. package/cli/dist/web-ui/assets/index-CH6e4vvI.css +32 -0
  5. package/cli/dist/web-ui/assets/index-GcU4pZms.js +1102 -0
  6. package/cli/dist/web-ui/index.html +2 -2
  7. package/cli/dist/web-ui/sw.js +1 -1
  8. package/cli/package.json +4 -4
  9. package/cli/product.cloudhosted.json +2 -2
  10. package/cli/product.internal.json +2 -2
  11. package/cli/product.ioa.json +2 -2
  12. package/cli/product.json +3 -3
  13. package/cli/product.selfhosted.json +2 -2
  14. package/cli/vendor/native-builds.json +18 -0
  15. package/cli/vendor/shim/broker-ipc-client.cjs +158 -0
  16. package/cli/vendor/shim/brokered-bin/codebuddy-toybox-dispatch +381 -0
  17. package/cli/vendor/shim/brokered-sandbox-bash-env.sh +10 -0
  18. package/cli/vendor/shim/genie-safe-delete.cjs +4 -781
  19. package/cli/vendor/shim/native/binding.gyp +13 -0
  20. package/cli/vendor/shim/native/brokered_sandbox_native.c +293 -0
  21. package/cli/vendor/shim/native/darwin-arm64/brokered-sandbox-native.node +0 -0
  22. package/cli/vendor/shim/native/darwin-x64/brokered-sandbox-native.node +0 -0
  23. package/cli/vendor/shim/node-brokered-fs-shim.cjs +932 -0
  24. package/cli/vendor/shim/node-language-shim.cjs +35 -0
  25. package/cli/vendor/shim/node-safe-delete-shim.cjs +810 -0
  26. package/cli/vendor/shim/safe-bin/safe-delete-bash-env.sh +1 -1
  27. package/cli/vendor/shim/safe-bin/safe-delete-common.sh +95 -34
  28. package/cli/vendor/shim/safe-delete-broker-delete.cjs +166 -0
  29. package/cli/vendor/shim/shell-runtime-bash-env.sh +8 -0
  30. package/cli/vendor/shim/sitecustomize.py +551 -22
  31. package/cli/vendor/toybox-macos/LICENSE +12 -0
  32. package/cli/vendor/toybox-macos/toybox +0 -0
  33. package/cli/vendor/toybox-macos/toybox.sb +19 -0
  34. package/cli/vendor/zsh-macos/LICENSE +37 -0
  35. package/cli/vendor/zsh-macos/bin/zsh +0 -0
  36. package/package.json +1 -1
  37. package/cli/dist/web-ui/assets/index-BZjhFKbY.js +0 -1051
  38. package/cli/dist/web-ui/assets/index-C5EhSt3C.css +0 -32
@@ -15,10 +15,10 @@
15
15
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
16
16
  <meta name="mobile-web-app-capable" content="yes">
17
17
  <link rel="apple-touch-icon" href="/pwa-icon.svg">
18
- <script type="module" crossorigin src="/assets/index-BZjhFKbY.js"></script>
18
+ <script type="module" crossorigin src="/assets/index-GcU4pZms.js"></script>
19
19
  <link rel="modulepreload" crossorigin href="/assets/markdown-Ce2Umeb2.js">
20
20
  <link rel="modulepreload" crossorigin href="/assets/vendor-DpYitQz5.js">
21
- <link rel="stylesheet" crossorigin href="/assets/index-C5EhSt3C.css">
21
+ <link rel="stylesheet" crossorigin href="/assets/index-CH6e4vvI.css">
22
22
  <link rel="manifest" href="/manifest.webmanifest"></head>
23
23
  <body class="bg-bg-primary text-text-primary min-h-screen overscroll-none">
24
24
  <div id="root"></div>
@@ -1 +1 @@
1
- if(!self.define){let e,s={};const o=(o,n)=>(o=new URL(o+".js",n).href,s[o]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=o,e.onload=s,document.head.appendChild(e)}else e=o,importScripts(o),s()}).then(()=>{let e=s[o];if(!e)throw new Error(`Module ${o} didn’t register its module`);return e}));self.define=(n,i)=>{const l=e||("document"in self?document.currentScript.src:"")||location.href;if(s[l])return;let r={};const t=e=>o(e,l),f={module:{uri:l},exports:r,require:t};s[l]=Promise.all(n.map(e=>f[e]||t(e))).then(e=>(i(...e),r))}}define(["./workbox-fed2bdfe"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"sandbox_proxy.html",revision:"e54bd087bf8b90d87d84c3cd8f2c22e9"},{url:"pwa-icon.svg",revision:"673adddf1548f59682764258005b6034"},{url:"logo.svg",revision:"269aa921a3f446f8e17843c89ed7bf35"},{url:"index.html",revision:"869ce4f2b6e61c0e3ace1acc6d98be76"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/vendor-DpYitQz5.js",revision:null},{url:"assets/octicons-CaZ_fok2.woff2",revision:null},{url:"assets/mfixx-CpAhKOZz.woff2",revision:null},{url:"assets/markdown-Ce2Umeb2.js",revision:null},{url:"assets/index-DixJ0kqk.js",revision:null},{url:"assets/index-C5EhSt3C.css",revision:null},{url:"assets/index-BZjhFKbY.js",revision:null},{url:"assets/fontawesome-B-jkhYfk.woff2",revision:null},{url:"assets/file-icons-C0jOugUK.woff2",revision:null},{url:"assets/devopicons-QN4QXivI.woff2",revision:null},{url:"logo.svg",revision:"269aa921a3f446f8e17843c89ed7bf35"},{url:"pwa-icon.svg",revision:"673adddf1548f59682764258005b6034"},{url:"manifest.webmanifest",revision:"7a003d0094122eb3e607566c757e9885"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api/,/^\/gateway/,/^\/docs\//]})),e.registerRoute(/^https:\/\/fonts\.googleapis\.com/,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[]}),"GET"),e.registerRoute(/^https:\/\/fonts\.gstatic\.com/,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:20,maxAgeSeconds:31536e3})]}),"GET")});
1
+ if(!self.define){let e,s={};const o=(o,n)=>(o=new URL(o+".js",n).href,s[o]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=o,e.onload=s,document.head.appendChild(e)}else e=o,importScripts(o),s()}).then(()=>{let e=s[o];if(!e)throw new Error(`Module ${o} didn’t register its module`);return e}));self.define=(n,i)=>{const l=e||("document"in self?document.currentScript.src:"")||location.href;if(s[l])return;let r={};const t=e=>o(e,l),f={module:{uri:l},exports:r,require:t};s[l]=Promise.all(n.map(e=>f[e]||t(e))).then(e=>(i(...e),r))}}define(["./workbox-fed2bdfe"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"sandbox_proxy.html",revision:"e54bd087bf8b90d87d84c3cd8f2c22e9"},{url:"pwa-icon.svg",revision:"673adddf1548f59682764258005b6034"},{url:"logo.svg",revision:"269aa921a3f446f8e17843c89ed7bf35"},{url:"index.html",revision:"dca9a6f91dad44d4bb8b35848dac2f0d"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/vendor-DpYitQz5.js",revision:null},{url:"assets/octicons-CaZ_fok2.woff2",revision:null},{url:"assets/mfixx-CpAhKOZz.woff2",revision:null},{url:"assets/markdown-Ce2Umeb2.js",revision:null},{url:"assets/index-GcU4pZms.js",revision:null},{url:"assets/index-DixJ0kqk.js",revision:null},{url:"assets/index-CH6e4vvI.css",revision:null},{url:"assets/fontawesome-B-jkhYfk.woff2",revision:null},{url:"assets/file-icons-C0jOugUK.woff2",revision:null},{url:"assets/devopicons-QN4QXivI.woff2",revision:null},{url:"logo.svg",revision:"269aa921a3f446f8e17843c89ed7bf35"},{url:"pwa-icon.svg",revision:"673adddf1548f59682764258005b6034"},{url:"manifest.webmanifest",revision:"7a003d0094122eb3e607566c757e9885"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api/,/^\/gateway/,/^\/docs\//]})),e.registerRoute(/^https:\/\/fonts\.googleapis\.com/,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[]}),"GET"),e.registerRoute(/^https:\/\/fonts\.gstatic\.com/,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:20,maxAgeSeconds:31536e3})]}),"GET")});
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-ai/codebuddy-code",
3
- "version": "2.118.2",
3
+ "version": "2.119.3",
4
4
  "description": "Use CodeBuddy, Tencent's AI assistant, right from your terminal. CodeBuddy can understand your codebase, edit files, run terminal commands, and handle entire workflows for you.",
5
5
  "main": "lib/node/index.js",
6
6
  "typings": "lib/node/index.d.ts",
@@ -18,9 +18,9 @@
18
18
  "@lydell/node-pty-linux-x64": "1.2.0-beta.12",
19
19
  "@lydell/node-pty-win32-arm64": "1.2.0-beta.12",
20
20
  "@lydell/node-pty-win32-x64": "1.2.0-beta.12",
21
- "@tencent-ai/sandbox-cli-darwin-arm64": "5.3.0-beta.5",
22
- "@tencent-ai/sandbox-cli-darwin-x64": "5.3.0-beta.5",
23
- "@tencent-ai/sandbox-cli-win32-x64": "5.3.0-beta.5"
21
+ "@tencent-ai/sandbox-cli-darwin-arm64": "5.3.0-beta.8",
22
+ "@tencent-ai/sandbox-cli-darwin-x64": "5.3.0-beta.8",
23
+ "@tencent-ai/sandbox-cli-win32-x64": "5.3.0-beta.10"
24
24
  },
25
25
  "keywords": [
26
26
  "codebuddy",
@@ -827,6 +827,6 @@
827
827
  "SelectImage": true,
828
828
  "SkipToolCallSupportCheck": true
829
829
  },
830
- "commit": "06f198e82ac0259e3eb8aac37520ea990418c35b",
831
- "date": "2026-07-09T13:59:53.585Z"
830
+ "commit": "72cfe535d28e0d79457fa34b0536105b21714c74",
831
+ "date": "2026-07-12T11:36:03.437Z"
832
832
  }
@@ -781,6 +781,6 @@
781
781
  }
782
782
  }
783
783
  },
784
- "commit": "06f198e82ac0259e3eb8aac37520ea990418c35b",
785
- "date": "2026-07-09T13:59:53.583Z"
784
+ "commit": "72cfe535d28e0d79457fa34b0536105b21714c74",
785
+ "date": "2026-07-12T11:36:03.435Z"
786
786
  }
@@ -1189,6 +1189,6 @@
1189
1189
  }
1190
1190
  }
1191
1191
  },
1192
- "commit": "06f198e82ac0259e3eb8aac37520ea990418c35b",
1193
- "date": "2026-07-09T13:59:53.584Z"
1192
+ "commit": "72cfe535d28e0d79457fa34b0536105b21714c74",
1193
+ "date": "2026-07-12T11:36:03.476Z"
1194
1194
  }
package/cli/product.json CHANGED
@@ -781,7 +781,7 @@
781
781
  },
782
782
  {
783
783
  "name": "tool-skill-description",
784
- "template": "Execute a skill within the main conversation\n\nWhen users ask you to perform tasks, check if any of the available skills below match. Skills provide specialized capabilities and domain knowledge.\n\nWhen users reference a \"slash command\" or \"/<something>\" (e.g., \"/commit\", \"/review-pr\"), they are referring to a skill. Use this tool to invoke it.\n\nHow to invoke:\n- Use this tool with the skill name and optional arguments\n- Examples:\n - `skill: \"pdf\"` - invoke the pdf skill\n - `skill: \"commit\", args: \"-m 'Fix bug'\"` - invoke with arguments\n - `skill: \"ms-office-suite:pdf\"` - invoke using fully qualified name\n\nImportant:\n- When a skill matches the user's request, this is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task\n- NEVER mention a skill without actually calling this tool\n- Do not invoke a skill that is already running\n- Do not use this tool for built-in CLI commands (like /help, /clear, etc.)\n- If you see a <command-name> tag in the current conversation turn, the skill has ALREADY been loaded - follow the instructions directly instead of calling this tool again\n\n<available_skills>\n{%- if skills and skills.length > 0 -%}\n{%- for skill in skills %}\n- {{skill.name}}: {% if skill.truncatedDescription == undefined %}{{skill.description}}{% elif skill.truncatedDescription %}{{skill.truncatedDescription}}{% endif %} (location: {{skill.location}})\n{%- endfor -%}\n{%- endif %}\n</available_skills>\n{%- if skillsOverview and skillsOverview.truncationMode != 'none' %}\n{%- if skillsOverview.omittedCount > 0 %}\n<!-- Showing {{skillsOverview.shownCount}} of {{skillsOverview.totalCount}} skills due to token limits -->\n{%- if skillsOverview.scanDirs and skillsOverview.scanDirs.length > 0 %}\nMore skills are available in these directories. Each subdirectory name is a skill name (containing a SKILL.md file). Discover them as needed, then invoke by name with this tool:\n{%- for dir in skillsOverview.scanDirs %}\n - {{dir.path}}\n{%- endfor %}\n{%- endif %}\n{%- else %}\n<!-- Descriptions for some skills have been trimmed to fit token limits. All listed names remain callable. -->\n{%- endif %}\n{%- endif %}\n"
784
+ "template": "Execute a skill within the main conversation\n\nWhen users ask you to perform tasks, check if any of the available skills below match. Skills provide specialized capabilities and domain knowledge.\n\nWhen users reference a \"slash command\" or \"/<something>\" (e.g., \"/commit\", \"/review-pr\"), they are referring to a skill. Use this tool to invoke it.\n\nHow to invoke:\n- Use this tool with the skill name and optional arguments\n- Examples:\n - `skill: \"pdf\"` - invoke the pdf skill\n - `skill: \"commit\", args: \"-m 'Fix bug'\"` - invoke with arguments\n - `skill: \"ms-office-suite:pdf\"` - invoke using fully qualified name\n\nImportant:\n- When a skill matches the user's request, this is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task\n- NEVER mention a skill without actually calling this tool\n- Do not invoke a skill that is already running\n- Do not use this tool for built-in CLI commands (like /help, /clear, etc.)\n- If you see a <command-name> tag in the current conversation turn, the skill has ALREADY been loaded - follow the instructions directly instead of calling this tool again\n- If the name matches a deferred tool listed in <available_deferred_tools> (e.g., \"Workflow\"), do NOT use this Skill tool — use ToolSearch + DeferExecuteTool instead\n\n<available_skills>\n{%- if skills and skills.length > 0 -%}\n{%- for skill in skills %}\n- {{skill.name}}: {% if skill.truncatedDescription == undefined %}{{skill.description}}{% elif skill.truncatedDescription %}{{skill.truncatedDescription}}{% endif %} (location: {{skill.location}})\n{%- endfor -%}\n{%- endif %}\n</available_skills>\n{%- if skillsOverview and skillsOverview.truncationMode != 'none' %}\n{%- if skillsOverview.omittedCount > 0 %}\n<!-- Showing {{skillsOverview.shownCount}} of {{skillsOverview.totalCount}} skills due to token limits -->\n{%- if skillsOverview.scanDirs and skillsOverview.scanDirs.length > 0 %}\nMore skills are available in these directories. Each subdirectory name is a skill name (containing a SKILL.md file). Discover them as needed, then invoke by name with this tool:\n{%- for dir in skillsOverview.scanDirs %}\n - {{dir.path}}\n{%- endfor %}\n{%- endif %}\n{%- else %}\n<!-- Descriptions for some skills have been trimmed to fit token limits. All listed names remain callable. -->\n{%- endif %}\n{%- endif %}\n"
785
785
  },
786
786
  {
787
787
  "name": "tool-skillmanage-description",
@@ -1765,6 +1765,6 @@
1765
1765
  "deferLoading": true
1766
1766
  }
1767
1767
  ],
1768
- "commit": "06f198e82ac0259e3eb8aac37520ea990418c35b",
1769
- "date": "2026-07-09T13:59:53.599Z"
1768
+ "commit": "72cfe535d28e0d79457fa34b0536105b21714c74",
1769
+ "date": "2026-07-12T11:36:03.447Z"
1770
1770
  }
@@ -342,6 +342,6 @@
342
342
  "ScheduledTasks": true,
343
343
  "SkipToolCallSupportCheck": true
344
344
  },
345
- "commit": "06f198e82ac0259e3eb8aac37520ea990418c35b",
346
- "date": "2026-07-09T13:59:53.582Z"
345
+ "commit": "72cfe535d28e0d79457fa34b0536105b21714c74",
346
+ "date": "2026-07-12T11:36:03.494Z"
347
347
  }
@@ -0,0 +1,18 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "sourceRepository": "tsbx-macos",
4
+ "artifacts": {
5
+ "toybox-macos/toybox": {
6
+ "sourceCommit": "bb6dd7af9ecd79ed62b70cfdf64fd24464db7525",
7
+ "buildCommand": "./build-toybox-macos.sh",
8
+ "platform": "macos",
9
+ "archs": ["x86_64", "arm64"]
10
+ },
11
+ "zsh-macos/bin/zsh": {
12
+ "sourceCommit": "bb6dd7af9ecd79ed62b70cfdf64fd24464db7525",
13
+ "buildCommand": "cd zsh-macos && ./build-zsh-macos.sh",
14
+ "platform": "macos",
15
+ "archs": ["x86_64", "arm64"]
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Neutral broker IPC client for vendor shims.
3
+ *
4
+ * Owns JSON-line socket transport only. Runtime-specific request semantics and
5
+ * response classification stay in the caller.
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const net = require('net');
11
+ const { spawnSync } = require('child_process');
12
+
13
+ const DEFAULT_TIMEOUT_MS = 5_000;
14
+
15
+ function brokerTimeoutMs() {
16
+ const parsed = Number.parseInt(process.env.CODEBUDDY_SANDBOX_BROKER_TIMEOUT_MS || '', 10);
17
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS;
18
+ }
19
+
20
+ function brokerSocketPath() {
21
+ return process.env.CODEBUDDY_SANDBOX_BROKER_IPC_ADDRESS;
22
+ }
23
+
24
+ function parseBrokerResponse(line) {
25
+ try {
26
+ return JSON.parse(String(line || '').trim());
27
+ } catch (error) {
28
+ throw new Error(`Invalid broker response: ${error && error.message ? error.message : error}`);
29
+ }
30
+ }
31
+
32
+ function sendBrokerRequest(request, options = {}) {
33
+ const socketPath = options.socketPath || brokerSocketPath();
34
+ const timeoutMs = options.timeoutMs || brokerTimeoutMs();
35
+ return new Promise((resolve, reject) => {
36
+ if (!socketPath) {
37
+ reject(new Error('Broker socket unavailable'));
38
+ return;
39
+ }
40
+
41
+ let settled = false;
42
+ let buffer = '';
43
+ const socket = net.createConnection(socketPath);
44
+ const finish = (error, response) => {
45
+ if (settled) { return; }
46
+ settled = true;
47
+ clearTimeout(timer);
48
+ socket.destroy();
49
+ if (error) { reject(error); } else { resolve(response); }
50
+ };
51
+ const timer = setTimeout(() => finish(new Error('Broker request timed out')), timeoutMs);
52
+
53
+ socket.setEncoding('utf8');
54
+ socket.on('connect', () => {
55
+ socket.write(`${JSON.stringify(request)}\n`);
56
+ });
57
+ socket.on('data', chunk => {
58
+ buffer += chunk;
59
+ const newlineIndex = buffer.indexOf('\n');
60
+ if (newlineIndex === -1) { return; }
61
+ try {
62
+ finish(undefined, parseBrokerResponse(buffer.slice(0, newlineIndex)));
63
+ } catch (error) {
64
+ finish(error);
65
+ }
66
+ });
67
+ socket.on('error', error => finish(error));
68
+ socket.on('end', () => {
69
+ if (!settled) {
70
+ finish(new Error('Broker closed without a response'));
71
+ }
72
+ });
73
+ });
74
+ }
75
+
76
+ function sendBrokerRequestSync(request, options = {}) {
77
+ const socketPath = options.socketPath || brokerSocketPath();
78
+ if (!socketPath) {
79
+ throw new Error('Broker socket unavailable');
80
+ }
81
+
82
+ const timeoutMs = options.timeoutMs || brokerTimeoutMs();
83
+ const line = JSON.stringify(request);
84
+ const nativeAddon = options.nativeAddon;
85
+ if (nativeAddon && typeof nativeAddon.requestBrokerSync === 'function') {
86
+ return parseBrokerResponse(nativeAddon.requestBrokerSync(socketPath, line, timeoutMs));
87
+ }
88
+
89
+ const nodeBin = process.env.CODEBUDDY_NODE_BIN || process.execPath;
90
+ const result = spawnSync(nodeBin, [
91
+ __filename,
92
+ '__request-sync',
93
+ socketPath,
94
+ line,
95
+ String(timeoutMs),
96
+ ], {
97
+ encoding: 'utf8',
98
+ timeout: timeoutMs + 1_000,
99
+ windowsHide: true,
100
+ env: {
101
+ ...process.env,
102
+ NODE_OPTIONS: '',
103
+ },
104
+ });
105
+
106
+ if (result.error) {
107
+ throw result.error;
108
+ }
109
+ if (result.status !== 0) {
110
+ throw new Error((result.stderr || '').trim() || `Broker sync helper exited with ${result.status}`);
111
+ }
112
+ return parseBrokerResponse(result.stdout);
113
+ }
114
+
115
+ function runSyncRequestCli(argv) {
116
+ const [, , mode, socketPath, line, timeoutValue] = argv;
117
+ if (mode !== '__request-sync') {
118
+ process.stderr.write('[broker-ipc-client] unsupported mode\n');
119
+ process.exit(2);
120
+ }
121
+ const timeoutMs = Number.parseInt(timeoutValue || '', 10) || DEFAULT_TIMEOUT_MS;
122
+ let settled = false;
123
+ let buffer = '';
124
+ const socket = net.createConnection(socketPath);
125
+ const finish = (code, message) => {
126
+ if (settled) { return; }
127
+ settled = true;
128
+ clearTimeout(timer);
129
+ socket.destroy();
130
+ if (message) {
131
+ (code === 0 ? process.stdout : process.stderr).write(message);
132
+ }
133
+ process.exit(code);
134
+ };
135
+ const timer = setTimeout(() => finish(124, 'Broker request timed out'), timeoutMs);
136
+ socket.setEncoding('utf8');
137
+ socket.on('connect', () => socket.write(`${line}\n`));
138
+ socket.on('data', chunk => {
139
+ buffer += chunk;
140
+ const newlineIndex = buffer.indexOf('\n');
141
+ if (newlineIndex !== -1) {
142
+ finish(0, buffer.slice(0, newlineIndex));
143
+ }
144
+ });
145
+ socket.on('error', error => finish(1, error && error.message ? error.message : String(error)));
146
+ socket.on('end', () => finish(2, 'Broker closed without a response'));
147
+ }
148
+
149
+ module.exports = {
150
+ brokerTimeoutMs,
151
+ parseBrokerResponse,
152
+ sendBrokerRequest,
153
+ sendBrokerRequestSync,
154
+ };
155
+
156
+ if (require.main === module) {
157
+ runSyncRequestCli(process.argv);
158
+ }
@@ -0,0 +1,381 @@
1
+ #!/usr/bin/env sh
2
+
3
+ __cb_app="${0##*/}"
4
+ __cb_toybox="${CODEBUDDY_TOYBOX_BIN:-}"
5
+ __cb_brokered_dir="${CODEBUDDY_BROKERED_BIN_DIR:-${0%/*}}"
6
+ __cb_broker_client="${CODEBUDDY_BROKER_IPC_CLIENT:-${__cb_brokered_dir%/brokered-bin}/broker-ipc-client.cjs}"
7
+
8
+ __cb_cleanup() { /bin/rm -f "$__cb_err" 2>/dev/null || true; }
9
+
10
+ __cb_should_fallback() { case "$1" in *"Unknown command "*|*"Unknown option "*|*"unknown option "*|*"unrecognized option "*) return 0 ;; *) return 1 ;; esac; }
11
+
12
+ __cb_path_without_brokered_dir() {
13
+ __cb_old_ifs="$IFS"
14
+ IFS=:; __cb_new_path=
15
+ for __cb_dir in ${PATH:-}; do
16
+ [ "$__cb_dir" = "$__cb_brokered_dir" ] && continue
17
+ __cb_new_path="${__cb_new_path:+$__cb_new_path:}$__cb_dir"
18
+ done
19
+ IFS="$__cb_old_ifs"
20
+ printf '%s' "$__cb_new_path"
21
+ }
22
+
23
+ __cb_fallback() {
24
+ case "$__cb_app" in
25
+ rm|rmdir|unlink) [ ! -x "${CODEBUDDY_SAFE_DELETE_BIN_DIR:-}/${__cb_app}" ] || BASH_ENV= exec "${CODEBUDDY_SAFE_DELETE_BIN_DIR}/${__cb_app}" "$@" ;;
26
+ esac
27
+
28
+ __cb_fallback_path="$(__cb_path_without_brokered_dir)"
29
+ [ -z "$__cb_fallback_path" ] && return 127
30
+ PATH="$__cb_fallback_path" exec "$__cb_app" "$@"
31
+ }
32
+
33
+ if [ "${CODEBUDDY_TOYBOX_DISPATCH_ACTIVE:-}" = "$__cb_app" ]; then
34
+ unset CODEBUDDY_TOYBOX_DISPATCH_ACTIVE
35
+ __cb_fallback "$@"
36
+ exit $?
37
+ fi
38
+
39
+ __cb_prebackup_path() {
40
+ [ -n "${1:-}" ] || return 0
41
+ [ -f "$1" ] || return 0
42
+ [ -n "${CODEBUDDY_SANDBOX_BROKER_IPC_ADDRESS:-}" ] || return 0
43
+ [ -n "${CODEBUDDY_SANDBOX_BROKER_SESSION_ID:-}" ] || return 0
44
+ [ -n "${CODEBUDDY_SANDBOX_FILE_TOKEN_OPERATION_COMMAND:-}" ] || return 0
45
+ [ -f "$__cb_broker_client" ] || return 0
46
+
47
+ NODE_OPTIONS= "${CODEBUDDY_NODE_BIN:-node}" - "$__cb_broker_client" "$1" <<'NODE'
48
+ const { sendBrokerRequestSync } = require(process.argv[2]);
49
+ const response = sendBrokerRequestSync({
50
+ command: process.env.CODEBUDDY_SANDBOX_FILE_TOKEN_OPERATION_COMMAND,
51
+ operation: 'write',
52
+ path: process.argv[3],
53
+ sessionId: process.env.CODEBUDDY_SANDBOX_BROKER_SESSION_ID,
54
+ toolCallId: process.env.CODEBUDDY_SANDBOX_BROKER_TOOL_CALL_ID,
55
+ brokerTraceId: process.env.CODEBUDDY_SANDBOX_BROKER_TRACE_ID,
56
+ runtime: 'toybox-dispatch',
57
+ });
58
+ if (response && (response.ok === true || response.decision === 'sandbox')) {
59
+ process.exit(0);
60
+ }
61
+ process.stderr.write(response && response.error ? response.error : 'Brokered write preflight denied');
62
+ process.exit(13);
63
+ NODE
64
+ }
65
+
66
+ __cb_prebackup_tree() {
67
+ [ -n "${1:-}" ] || return 0
68
+ [ -d "$1" ] || return 0
69
+ [ -n "${CODEBUDDY_SANDBOX_BROKER_IPC_ADDRESS:-}" ] || return 0
70
+ [ -n "${CODEBUDDY_SANDBOX_BROKER_SESSION_ID:-}" ] || return 0
71
+ [ -n "${CODEBUDDY_SANDBOX_FILE_TOKEN_OPERATION_COMMAND:-}" ] || return 0
72
+ [ -f "$__cb_broker_client" ] || return 0
73
+
74
+ NODE_OPTIONS= "${CODEBUDDY_NODE_BIN:-node}" - "$__cb_broker_client" "$1" <<'NODE'
75
+ const fs = require('fs');
76
+ const path = require('path');
77
+ const { sendBrokerRequestSync } = require(process.argv[2]);
78
+
79
+ function prebackupFile(filePath) {
80
+ const response = sendBrokerRequestSync({
81
+ command: process.env.CODEBUDDY_SANDBOX_FILE_TOKEN_OPERATION_COMMAND,
82
+ operation: 'write',
83
+ path: filePath,
84
+ sessionId: process.env.CODEBUDDY_SANDBOX_BROKER_SESSION_ID,
85
+ toolCallId: process.env.CODEBUDDY_SANDBOX_BROKER_TOOL_CALL_ID,
86
+ brokerTraceId: process.env.CODEBUDDY_SANDBOX_BROKER_TRACE_ID,
87
+ runtime: 'toybox-dispatch',
88
+ });
89
+ if (response && (response.ok === true || response.decision === 'sandbox')) {
90
+ return;
91
+ }
92
+ process.stderr.write(response && response.error ? response.error : 'Brokered write preflight denied');
93
+ process.exit(13);
94
+ }
95
+
96
+ function walk(dir) {
97
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
98
+ const entryPath = path.join(dir, entry.name);
99
+ if (entry.isDirectory()) {
100
+ walk(entryPath);
101
+ } else if (entry.isFile()) {
102
+ prebackupFile(entryPath);
103
+ }
104
+ }
105
+ }
106
+
107
+ walk(process.argv[3]);
108
+ NODE
109
+ }
110
+
111
+ __cb_prebackup_tree_or_file() {
112
+ if [ -f "$1" ]; then
113
+ __cb_prebackup_path "$1"
114
+ return $?
115
+ fi
116
+ if [ -d "$1" ]; then
117
+ __cb_prebackup_tree "$1"
118
+ return $?
119
+ fi
120
+ return 0
121
+ }
122
+
123
+ __cb_prebackup_truncate() {
124
+ __cb_after_options=
125
+ __cb_skip_next=
126
+ for __cb_arg in "$@"; do
127
+ if [ -n "$__cb_skip_next" ]; then __cb_skip_next=; continue; fi
128
+ if [ -n "$__cb_after_options" ]; then __cb_prebackup_path "$__cb_arg" || return $?; continue; fi
129
+ case "$__cb_arg" in
130
+ --) __cb_after_options=1 ;;
131
+ -s|--size) __cb_skip_next=1 ;;
132
+ --size=*) ;;
133
+ -*) ;;
134
+ *) __cb_prebackup_path "$__cb_arg" || return $? ;;
135
+ esac
136
+ done
137
+ }
138
+
139
+ __cb_prebackup_dd() {
140
+ for __cb_arg in "$@"; do
141
+ case "$__cb_arg" in
142
+ of=*) __cb_prebackup_path "${__cb_arg#of=}" || return $? ;;
143
+ esac
144
+ done
145
+ }
146
+
147
+ __cb_prebackup_tee() {
148
+ __cb_after_options=
149
+ for __cb_arg in "$@"; do
150
+ if [ -n "$__cb_after_options" ]; then __cb_prebackup_path "$__cb_arg" || return $?; continue; fi
151
+ case "$__cb_arg" in
152
+ --) __cb_after_options=1 ;;
153
+ -*) ;;
154
+ *) __cb_prebackup_path "$__cb_arg" || return $? ;;
155
+ esac
156
+ done
157
+ }
158
+
159
+ __cb_prebackup_last_operand() {
160
+ __cb_after_options=
161
+ __cb_last=
162
+ for __cb_arg in "$@"; do
163
+ if [ -n "$__cb_after_options" ]; then __cb_last="$__cb_arg"; continue; fi
164
+ case "$__cb_arg" in
165
+ --) __cb_after_options=1 ;;
166
+ -*) ;;
167
+ *) __cb_last="$__cb_arg" ;;
168
+ esac
169
+ done
170
+ [ -z "$__cb_last" ] || __cb_prebackup_tree_or_file "$__cb_last"
171
+ }
172
+
173
+ __cb_cp_target_for_source() {
174
+ __cb_src="$1"
175
+ __cb_dest="$2"
176
+ if [ -d "$__cb_dest" ] && [ -z "${__cb_cp_no_target_dir:-}" ]; then
177
+ __cb_base="${__cb_src%/}"
178
+ __cb_base="${__cb_base##*/}"
179
+ [ -z "$__cb_base" ] || __cb_prebackup_tree_or_file "$__cb_dest/$__cb_base"
180
+ return $?
181
+ fi
182
+ __cb_prebackup_tree_or_file "$__cb_dest"
183
+ }
184
+
185
+ __cb_prebackup_cp() {
186
+ __cb_after_options=
187
+ __cb_skip_next=
188
+ __cb_cp_no_target_dir=
189
+ __cb_cp_target_dir=
190
+ __cb_cp_count=0
191
+ __cb_cp_dest=
192
+ for __cb_arg in "$@"; do
193
+ if [ "$__cb_skip_next" = "target-dir" ]; then __cb_cp_target_dir="$__cb_arg"; __cb_skip_next=; continue; fi
194
+ if [ -n "$__cb_after_options" ]; then __cb_cp_count=$((__cb_cp_count + 1)); __cb_cp_dest="$__cb_arg"; continue; fi
195
+ case "$__cb_arg" in
196
+ --) __cb_after_options=1 ;;
197
+ -t|--target-directory) __cb_skip_next=target-dir ;;
198
+ --target-directory=*) __cb_cp_target_dir="${__cb_arg#*=}" ;;
199
+ -T|--no-target-directory) __cb_cp_no_target_dir=1 ;;
200
+ -*) ;;
201
+ *) __cb_cp_count=$((__cb_cp_count + 1)); __cb_cp_dest="$__cb_arg" ;;
202
+ esac
203
+ done
204
+
205
+ if [ -n "$__cb_cp_target_dir" ]; then
206
+ __cb_after_options=
207
+ __cb_skip_next=
208
+ for __cb_arg in "$@"; do
209
+ if [ "$__cb_skip_next" = "target-dir" ]; then __cb_skip_next=; continue; fi
210
+ if [ -n "$__cb_after_options" ]; then __cb_cp_target_for_source "$__cb_arg" "$__cb_cp_target_dir" || return $?; continue; fi
211
+ case "$__cb_arg" in
212
+ --) __cb_after_options=1 ;;
213
+ -t|--target-directory) __cb_skip_next=target-dir ;;
214
+ --target-directory=*|-T|--no-target-directory|-*) ;;
215
+ *) __cb_cp_target_for_source "$__cb_arg" "$__cb_cp_target_dir" || return $? ;;
216
+ esac
217
+ done
218
+ return 0
219
+ fi
220
+
221
+ [ "$__cb_cp_count" -ge 2 ] || return 0
222
+ __cb_after_options=
223
+ __cb_index=0
224
+ for __cb_arg in "$@"; do
225
+ if [ -n "$__cb_after_options" ]; then
226
+ __cb_index=$((__cb_index + 1))
227
+ [ "$__cb_index" -ge "$__cb_cp_count" ] || __cb_cp_target_for_source "$__cb_arg" "$__cb_cp_dest" || return $?
228
+ continue
229
+ fi
230
+ case "$__cb_arg" in
231
+ --) __cb_after_options=1 ;;
232
+ -t|--target-directory) __cb_skip_next=target-dir ;;
233
+ --target-directory=*|-T|--no-target-directory|-*) ;;
234
+ *)
235
+ __cb_index=$((__cb_index + 1))
236
+ [ "$__cb_index" -ge "$__cb_cp_count" ] || __cb_cp_target_for_source "$__cb_arg" "$__cb_cp_dest" || return $?
237
+ ;;
238
+ esac
239
+ done
240
+ }
241
+
242
+ __cb_prebackup_targets() {
243
+ case "$__cb_app" in
244
+ truncate) __cb_prebackup_truncate "$@" ;;
245
+ tee) __cb_prebackup_tee "$@" ;;
246
+ cp) __cb_prebackup_cp "$@" ;;
247
+ dd) __cb_prebackup_dd "$@" ;;
248
+ esac
249
+ }
250
+
251
+ __cb_host_op_ln() {
252
+ [ "$__cb_app" = "ln" ] || return 125
253
+ [ -n "${CODEBUDDY_SANDBOX_BROKER_IPC_ADDRESS:-}" ] || return 125
254
+ [ -n "${CODEBUDDY_SANDBOX_BROKER_SESSION_ID:-}" ] || return 125
255
+ [ -n "${CODEBUDDY_SANDBOX_HOST_FILE_OPERATION_COMMAND:-}" ] || return 125
256
+ [ -f "$__cb_broker_client" ] || return 125
257
+
258
+ # Only simple ln [-s] SRC DEST is dispatched; other flags fall back to toybox.
259
+ NODE_OPTIONS= "${CODEBUDDY_NODE_BIN:-node}" - "$__cb_broker_client" "$@" <<'NODE'
260
+ const fs = require('fs');
261
+ const path = require('path');
262
+ const { sendBrokerRequestSync } = require(process.argv[2]);
263
+
264
+ const args = process.argv.slice(3);
265
+ let symbolic = false;
266
+ let afterOptions = false;
267
+ const operands = [];
268
+
269
+ for (const arg of args) {
270
+ if (afterOptions) {
271
+ operands.push(arg);
272
+ continue;
273
+ }
274
+ if (arg === '--') {
275
+ afterOptions = true;
276
+ continue;
277
+ }
278
+ if (arg === '-s' || arg === '--symbolic') {
279
+ symbolic = true;
280
+ continue;
281
+ }
282
+ if (arg.startsWith('-')) {
283
+ process.exit(125);
284
+ }
285
+ operands.push(arg);
286
+ }
287
+
288
+ if (operands.length !== 2) {
289
+ process.exit(125);
290
+ }
291
+
292
+ const [fromArg, toArg] = operands;
293
+ const toPath = path.resolve(process.cwd(), toArg);
294
+ const fromPath = symbolic ? fromArg : path.resolve(process.cwd(), fromArg);
295
+ let linkPath = toPath;
296
+ try {
297
+ if (fs.statSync(toPath).isDirectory()) {
298
+ linkPath = path.join(toPath, path.basename(fromArg));
299
+ }
300
+ } catch {
301
+ linkPath = toPath;
302
+ }
303
+ const response = sendBrokerRequestSync({
304
+ command: process.env.CODEBUDDY_SANDBOX_HOST_FILE_OPERATION_COMMAND,
305
+ operation: 'link',
306
+ from: fromPath,
307
+ to: linkPath,
308
+ symbolic,
309
+ sessionId: process.env.CODEBUDDY_SANDBOX_BROKER_SESSION_ID,
310
+ toolCallId: process.env.CODEBUDDY_SANDBOX_BROKER_TOOL_CALL_ID,
311
+ brokerTraceId: process.env.CODEBUDDY_SANDBOX_BROKER_TRACE_ID,
312
+ runtime: 'toybox-dispatch',
313
+ });
314
+
315
+ if (response && response.ok === true) {
316
+ process.exit(0);
317
+ }
318
+ if (response && response.decision === 'sandbox') {
319
+ process.exit(125);
320
+ }
321
+ process.stderr.write(response && response.error ? response.error : 'Brokered link operation denied');
322
+ process.exit(13);
323
+ NODE
324
+ }
325
+
326
+ # rm/rmdir/unlink 在沙箱模式下由 toybox 原生实现直接执行,不会经过
327
+ # safe-bin/rm,因此批量删除审批(safe-delete-bulk-guard.cjs)在这里补一次,
328
+ # 与 node-safe-delete-shim.cjs / sitecustomize.py 里的调用方式保持一致。
329
+ __cb_bulk_guard_delete_check() {
330
+ case "$__cb_app" in
331
+ rm|rmdir|unlink) ;;
332
+ *) return 0 ;;
333
+ esac
334
+ __cb_bg_common_sh="${__cb_brokered_dir%/brokered-bin}/safe-bin/safe-delete-common.sh"
335
+ [ -f "$__cb_bg_common_sh" ] || return 0
336
+
337
+ __cb_bg_targets=()
338
+ __cb_bg_after_dd=
339
+ for __cb_bg_arg in "$@"; do
340
+ if [ -n "$__cb_bg_after_dd" ]; then
341
+ __cb_bg_targets+=("$__cb_bg_arg")
342
+ continue
343
+ fi
344
+ case "$__cb_bg_arg" in
345
+ --) __cb_bg_after_dd=1 ;;
346
+ -*) ;;
347
+ *) __cb_bg_targets+=("$__cb_bg_arg") ;;
348
+ esac
349
+ done
350
+ [ "${#__cb_bg_targets[@]}" -gt 0 ] || return 0
351
+
352
+ # shellcheck source=/dev/null
353
+ . "$__cb_bg_common_sh"
354
+ safe_delete_bulk_guard_non_tmp_check "${__cb_bg_targets[@]}"
355
+ }
356
+
357
+ if [ -z "$__cb_toybox" ] || [ ! -x "$__cb_toybox" ]; then __cb_fallback "$@"; exit $?; fi
358
+
359
+ __cb_bulk_guard_delete_check "$@" || exit $?
360
+ __cb_prebackup_targets "$@" || exit $?
361
+ __cb_host_op_ln "$@"
362
+ __cb_host_op_status=$?
363
+ if [ "$__cb_host_op_status" -eq 0 ]; then exit 0; fi
364
+ if [ "$__cb_host_op_status" -ne 125 ]; then exit "$__cb_host_op_status"; fi
365
+
366
+ __cb_err="$(/usr/bin/mktemp "${TMPDIR:-/tmp}/codebuddy-toybox.XXXXXX" 2>/dev/null || mktemp "${TMPDIR:-/tmp}/codebuddy-toybox.XXXXXX" 2>/dev/null)"
367
+ if [ -z "${__cb_err:-}" ]; then CODEBUDDY_TOYBOX_DISPATCH_ACTIVE="$__cb_app" exec "$__cb_toybox" "$__cb_app" "$@"; fi
368
+
369
+ CODEBUDDY_TOYBOX_DISPATCH_ACTIVE="$__cb_app" "$__cb_toybox" "$__cb_app" "$@" 2>"$__cb_err"
370
+ __cb_status=$?
371
+ if [ "$__cb_status" -eq 0 ]; then [ ! -s "$__cb_err" ] || /bin/cat "$__cb_err" >&2 2>/dev/null || true; __cb_cleanup; exit 0; fi
372
+
373
+ __cb_msg="$(/bin/cat "$__cb_err" 2>/dev/null || true)"
374
+ if __cb_should_fallback "$__cb_msg"; then
375
+ __cb_cleanup; __cb_fallback "$@"
376
+ exit $?
377
+ fi
378
+
379
+ [ -z "$__cb_msg" ] || printf '%s' "$__cb_msg" >&2
380
+ __cb_cleanup
381
+ exit "$__cb_status"