w-dispatch-ai 1.0.34 → 1.0.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/w-dispatch-ai.umd.js +2 -2
- package/dist/w-dispatch-ai.umd.js.map +1 -1
- package/docs/WDispatchAi.mjs.html +1 -1
- package/docs/adapters.mjs.html +1 -1
- package/docs/budgetFor.mjs.html +1 -1
- package/docs/buildValidator.mjs.html +1 -1
- package/docs/castPintOr.mjs.html +1 -1
- package/docs/dfTimeoutMs.mjs.html +1 -1
- package/docs/dispatchAi.mjs.html +1 -1
- package/docs/dispatchAiFallback.mjs.html +1 -1
- package/docs/dispatchAiWkf.mjs.html +1 -1
- package/docs/dispatchAntigravity.mjs.html +1 -1
- package/docs/dispatchApiOpenaiCompat.mjs.html +1 -1
- package/docs/dispatchApiOpenaiResponses.mjs.html +1 -1
- package/docs/dispatchApiTypesafeSystemone.mjs.html +1 -1
- package/docs/dispatchClaude.mjs.html +13 -3
- package/docs/dispatchCodex.mjs.html +1 -1
- package/docs/dispatchOpencode.mjs.html +43 -11
- package/docs/getCliArgs.mjs.html +1 -1
- package/docs/getErrorResult.mjs.html +1 -1
- package/docs/getErrorType.mjs.html +1 -1
- package/docs/global.html +7 -7
- package/docs/index.html +1 -1
- package/docs/quota_dfQuotaTimeoutMs.mjs.html +1 -1
- package/docs/quota_fetchQuotaJson.mjs.html +1 -1
- package/docs/quota_fromCodexUsageHttp.mjs.html +1 -1
- package/docs/quota_getQuotaAntigravity.mjs.html +1 -1
- package/docs/quota_getQuotaClaude.mjs.html +1 -1
- package/docs/quota_getQuotaCodex.mjs.html +1 -1
- package/docs/quota_readJsonOrNull.mjs.html +1 -1
- package/docs/quota_toQuotaLabel.mjs.html +1 -1
- package/docs/quota_toQuotaResult.mjs.html +1 -1
- package/docs/quota_toQuotaScopedLabel.mjs.html +1 -1
- package/docs/quota_toQuotaWindow.mjs.html +1 -1
- package/docs/readEnvFile.mjs.html +1 -1
- package/docs/resolveProviders.mjs.html +1 -1
- package/docs/wkf_callAiWithFallback.mjs.html +2 -2
- package/docs/wkf_createFileStore.mjs.html +1 -1
- package/docs/wkf_createUsageCounter.mjs.html +1 -1
- package/docs/wkf_extractJsonLoose.mjs.html +1 -1
- package/docs/wkf_noSideEffectPrefix.mjs.html +4 -3
- package/docs/wkf_runFanout.mjs.html +1 -1
- package/docs/wkf_runFanoutPipeline.mjs.html +1 -1
- package/docs/wkf_runRolePipeline.mjs.html +1 -1
- package/docs/wkf_salvageTruncatedArray.mjs.html +1 -1
- package/package.json +1 -1
- package/src/dispatchClaude.mjs +12 -2
- package/src/dispatchOpencode.mjs +41 -9
- package/src/providers.mjs +6 -1
- package/src/wkf/callAiWithFallback.mjs +1 -1
- package/src/wkf/noSideEffectPrefix.mjs +3 -2
- package/test/tools/fakeCliForTest.mjs +1 -0
- package/test/unit-dispatchOpencode.test.mjs +21 -0
|
@@ -53,8 +53,9 @@
|
|
|
53
53
|
// 本套件早已修正, 形成「套件修了、消費端沒跟上」的分岔。抽成獨立模組後
|
|
54
54
|
// 所有路徑引用同一來源, 措辭再修正時全體同步。
|
|
55
55
|
//
|
|
56
|
-
// 【為何需要此前綴】agentic CLI
|
|
57
|
-
//
|
|
56
|
+
// 【為何需要此前綴】agentic CLI自帶檔案讀寫工具, 且cwd不是隔離邊界(可用絕對路徑
|
|
57
|
+
// 寫到cwd外; 另opencode曾因沿用父進程之PWD而在啟動目錄作業, 2026-09-23已於
|
|
58
|
+
// dispatchOpencode修正, 見該檔檔頭), 實測會「順手」把結果另存孤兒檔,
|
|
58
59
|
// 甚至可能覆蓋呼叫端資料本體(殷鑑: 2026-08-10執行任務歷史.md遭AI覆寫、
|
|
59
60
|
// 評比腳本繞過前綴又產生根目錄孤兒檔); 各家CLI的權限機制彼此不同(codex有sandbox、
|
|
60
61
|
// claude有--disallowedTools、opencode有config.permission),
|
|
@@ -108,7 +109,7 @@ export default NO_SIDE_EFFECT
|
|
|
108
109
|
<br class="clear">
|
|
109
110
|
|
|
110
111
|
<footer>
|
|
111
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026
|
|
112
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026 18:02:52 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
112
113
|
</footer>
|
|
113
114
|
|
|
114
115
|
<script>prettyPrint();</script>
|
|
@@ -217,7 +217,7 @@ export { defaultIntegratePrompt }
|
|
|
217
217
|
<br class="clear">
|
|
218
218
|
|
|
219
219
|
<footer>
|
|
220
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026
|
|
220
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026 18:02:52 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
221
221
|
</footer>
|
|
222
222
|
|
|
223
223
|
<script>prettyPrint();</script>
|
|
@@ -161,7 +161,7 @@ export default runFanoutPipeline
|
|
|
161
161
|
<br class="clear">
|
|
162
162
|
|
|
163
163
|
<footer>
|
|
164
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026
|
|
164
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026 18:02:52 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
165
165
|
</footer>
|
|
166
166
|
|
|
167
167
|
<script>prettyPrint();</script>
|
|
@@ -183,7 +183,7 @@ export default runRolePipeline
|
|
|
183
183
|
<br class="clear">
|
|
184
184
|
|
|
185
185
|
<footer>
|
|
186
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026
|
|
186
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026 18:02:52 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
187
187
|
</footer>
|
|
188
188
|
|
|
189
189
|
<script>prettyPrint();</script>
|
|
@@ -154,7 +154,7 @@ export default salvageTruncatedArray
|
|
|
154
154
|
<br class="clear">
|
|
155
155
|
|
|
156
156
|
<footer>
|
|
157
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026
|
|
157
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Wed Sep 23 2026 18:02:52 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
158
158
|
</footer>
|
|
159
159
|
|
|
160
160
|
<script>prettyPrint();</script>
|
package/package.json
CHANGED
package/src/dispatchClaude.mjs
CHANGED
|
@@ -19,6 +19,16 @@ import dfTimeoutMs from './dfTimeoutMs.mjs'
|
|
|
19
19
|
//
|
|
20
20
|
// 【認證】沿用Claude Code既有登入狀態(帳號層級),不需也不支援逐次注入API key,
|
|
21
21
|
// 故本轉接器無key參數——輪替時它是「一個獨立的供應商」而非「另一把金鑰」。
|
|
22
|
+
//
|
|
23
|
+
// 【勿帶--bare, 並留意其未來成為-p預設(2026-09-23查官方headless文件)】文件原文:
|
|
24
|
+
// 「--bare is the recommended mode for scripted and SDK calls, and will become the default for -p in a
|
|
25
|
+
// future release」且「In bare mode, Claude Code never reads OAuth credentials or the system keychain」。
|
|
26
|
+
// 本轉接器靠訂閱登入(OAuth), 故extraArgs勿帶--bare(會直接認證失敗); 若日後某版把-p預設改為bare,
|
|
27
|
+
// 所有訂閱條目會一併失效——徵狀為非零離開碼, 失敗訊息印在stdout而非stderr(官方: 執行期失敗如未登入
|
|
28
|
+
// 以result印在stdout)。2.1.280尚無退出bare之旗標, 無法預先處理; 每次升級Claude Code後跑一次claude
|
|
29
|
+
// 條目即可偵測, 屆時再查該版之退出方式調整此處。
|
|
30
|
+
// 另兩點同日查證: stdin傳入之prompt上限10MB(超過即非零離開, 見@param prompt);
|
|
31
|
+
// 2.1.280起Pro/Team Standard方案未給model時預設由Sonnet改為Opus(只影響未帶--model之呼叫)。
|
|
22
32
|
|
|
23
33
|
|
|
24
34
|
//預設值
|
|
@@ -41,10 +51,10 @@ let OWN_KEYS = ['exe', 'model', 'skipPermissions', 'extraArgs', 'input']
|
|
|
41
51
|
* 可給予skipPermissions為false保留CLI權限閘門;
|
|
42
52
|
* 本函數不會reject,一律以結果物件之ok與error欄位回報成敗
|
|
43
53
|
*
|
|
44
|
-
* @param {String} prompt 輸入提示詞字串,一律以stdin傳入子進程
|
|
54
|
+
* @param {String} prompt 輸入提示詞字串,一律以stdin傳入子進程(Claude Code之stdin上限10MB,超過即非零離開)
|
|
45
55
|
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
46
56
|
* @param {String} [opt.exe='claude'] 輸入claude執行檔名稱或絕對路徑字串,給予名稱時由execCli自系統PATH解析,預設'claude'
|
|
47
|
-
* @param {String} [opt.model=''] 輸入模型別名或模型ID字串,例如'claude-opus-5-5'(全名, 固定版本)、'opus'或'sonnet'(別名, 隨CLI指向最新版),預設''代表不帶`--model
|
|
57
|
+
* @param {String} [opt.model=''] 輸入模型別名或模型ID字串,例如'claude-opus-5-5'(全名, 固定版本)、'opus'或'sonnet'(別名, 隨CLI指向最新版),預設''代表不帶`--model`旗標,由CLI依方案決定(2.1.280起Pro/Team Standard亦預設Opus)
|
|
48
58
|
* @param {Boolean} [opt.skipPermissions=true] 輸入是否帶`--dangerously-skip-permissions`旗標布林值,false代表保留CLI權限閘門,預設true
|
|
49
59
|
* @param {Array} [opt.extraArgs=[]] 輸入額外命令列旗標字串陣列,將接於固定旗標之後,預設[]
|
|
50
60
|
* @param {Number} [opt.timeoutMs=300000] 輸入逾時毫秒正整數,逾時將強制關閉子進程及其子孫程序,全套件統一預設300000
|
package/src/dispatchOpencode.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'path'
|
|
1
2
|
import get from 'lodash-es/get.js'
|
|
2
3
|
import omit from 'lodash-es/omit.js'
|
|
3
4
|
import isobj from 'wsemi/src/isobj.mjs'
|
|
@@ -22,16 +23,30 @@ import dfTimeoutMs from './dfTimeoutMs.mjs'
|
|
|
22
23
|
// `if (process.env.OPENCODE_AUTH_CONTENT) return JSON.parse(...)`, 解析成功即整份取代, 不與auth.json合併;
|
|
23
24
|
// 空字串為falsy、非法JSON會落回讀auth.json。auth.json位於<XDG_DATA_HOME|~/.local/share>/opencode/。
|
|
24
25
|
//
|
|
25
|
-
// 【本轉接器只驗證過opencode v1(實測至1.18.
|
|
26
|
-
// opencode已另發布v2(
|
|
27
|
-
// 官方〈Migrate from V1〉明載「OpenCode 1 and OpenCode 2 both use the opencode
|
|
28
|
-
// installed side by side by default... the V2 curl installer replaces the V1 binary
|
|
29
|
-
// v2
|
|
26
|
+
// 【本轉接器只驗證過opencode v1(實測至1.18.32), v2尚未支援(2026-09-22查證, 2026-09-23補④)】
|
|
27
|
+
// opencode已另發布v2(2026-09-23已至2.0.15, npm套件@opencode/cli, bin另含opencode2; 文件自成一棵樹
|
|
28
|
+
// https://opencode.ai/v2/docs), 官方〈Migrate from V1〉明載「OpenCode 1 and OpenCode 2 both use the opencode
|
|
29
|
+
// command and are no longer installed side by side by default... the V2 curl installer replaces the V1 binary」
|
|
30
|
+
// ——即升級v2會取代同名執行檔。v1之npm套件opencode-ai(1.18.32)仍為latest且未標deprecated。
|
|
31
|
+
// v2仍有`opencode run`(文件列為自動化用途)與`--agent`, 但至少四處與本轉接器之假設不同, 升級前須逐一實測:
|
|
30
32
|
// ① 模型旗標: v2文件一律寫`--model provider/model`, 本轉接器用`-m`(v2是否保留為別名未驗證);
|
|
31
|
-
// ② provider設定形狀: v2改為`providers.<id>.package
|
|
33
|
+
// ② provider設定形狀: v2改為`providers.<id>.package`(單數provider→providers, npm→package並加aisdk:前綴),
|
|
32
34
|
// 與本轉接器經OPENCODE_CONFIG_CONTENT注入之v1形狀(provider.<id>.npm)不同;
|
|
33
|
-
// ③ 金鑰注入: v2憑證走/connect與新憑證庫, OPENCODE_AUTH_CONTENT
|
|
34
|
-
//
|
|
35
|
+
// ③ 金鑰注入: v2憑證走/connect與新憑證庫, OPENCODE_AUTH_CONTENT是否仍被讀取未驗證;
|
|
36
|
+
// ④ 權限設定形狀: v2改為單一有序`permissions`陣列, 動作亦改名(bash→shell、task→subagent、write/patch→edit),
|
|
37
|
+
// providers.mjs之OC_READONLY({edit:'deny', bash:'ask'})為v1形狀, 在v2不會被認得——唯讀鎖等同失效,
|
|
38
|
+
// 升v2前須以金絲雀(要求寫檔, 驗未落地)重驗防寫, 不可假設仍有效。
|
|
39
|
+
// 故本機若升v2, 請先以單一呼叫實測上述四項再調整本檔; 條目端可先以exe指定v1執行檔路徑過渡。
|
|
40
|
+
//
|
|
41
|
+
// 【cwd須同步PWD環境變數(2026-09-23實測後新增)】opencode run決定session目錄之原始碼為
|
|
42
|
+
// `const root = Filesystem.resolve(process.env.PWD ?? process.cwd())`(packages/opencode/src/cli/cmd/run.ts),
|
|
43
|
+
// 即「繼承來的PWD優先於子進程真實cwd」。Git Bash與Linux/macOS之shell皆會設PWD, 故呼叫端給opt.cwd而
|
|
44
|
+
// 父進程PWD指向他處時, opencode會在父進程目錄作業: 讀相對路徑落空(回NOTFOUND且ok:true, 靜默失敗),
|
|
45
|
+
// 允許寫檔時檔案落在父進程目錄(本專案曾因此於根目錄留下金絲雀孤兒檔, 當時誤判為「以git根目錄為準」)。
|
|
46
|
+
// 實測(cwd指向探針目錄, 父PWD=專案根): 不處理→working directory為專案根、讀MARKER.txt失敗;
|
|
47
|
+
// 注入PWD=cwd→正確; 帶--dir亦正確。採注入PWD: 不動命令列(與--attach等extraArgs無衝突), 語意即shell
|
|
48
|
+
// 於該目錄啟動程式時之環境; 且以本函數之值蓋過opt.env之PWD(呼叫端常整份展開process.env, 若讓其覆寫
|
|
49
|
+
// 則缺陷靜默回歸)。claude與codex以同組探針實測皆遵循spawn cwd, 不受影響, 故只在本轉接器處理。
|
|
35
50
|
//
|
|
36
51
|
// 【Zen免費層閘門與config注入(2026-09-18實測)】opencode自2026-09-17起限制免費模型只准在opencode本體內使用
|
|
37
52
|
// (403 FreeTierError: free tier can only be used from within OpenCode)。CLI本身可通, 但經OPENCODE_CONFIG_CONTENT
|
|
@@ -39,6 +54,14 @@ import dfTimeoutMs from './dfTimeoutMs.mjs'
|
|
|
39
54
|
// 少了bash即被判非opencode而403; 改permission.bash:'ask'則通過, 且非互動run下ask自動拒絕, 仍為機械防寫。
|
|
40
55
|
// 詳見providers.mjs檔頭【Zen免費層閘門】與OC_READONLY常數。
|
|
41
56
|
//
|
|
57
|
+
// 【Zen免費層429限流時run不會結束(2026-09-23實測, 1.18.32)】REST探測0.7s即回429 FreeUsageLimitError
|
|
58
|
+
// (Rate limit exceeded), 但`opencode run`收到同一錯誤後不退出: 預設stderr只有session標頭, 直到呼叫端逾時;
|
|
59
|
+
// 帶--print-logs --log-level DEBUG可見`stream error ... AI_APICallError: Rate limit exceeded`, 其後再無輸出。
|
|
60
|
+
// 不經本套件直接跑原始CLI亦同, 非本轉接器所致。對本套件之意義: 此情形以TIMEOUT回報(errorType 'timeout'),
|
|
61
|
+
// dispatchAiFallback內建之TIMEOUT冷卻觸發可涵蓋, 但每次嘗試都耗滿timeoutMs——免費oc:條目宜以
|
|
62
|
+
// resolveProviders之patch給較短timeoutMs; 需在stderr看到字樣(供coolDetect)時於extraArgs加
|
|
63
|
+
// --print-logs --log-level ERROR。
|
|
64
|
+
//
|
|
42
65
|
// 【不讀auth.json之匿名呼叫: useStoredAuth:false(2026-09-17實測後新增)】
|
|
43
66
|
// 未注入金鑰時opencode會自動沿用auth.json之登入; 該帳號工作區若未開某免費模型(如union-alpha),
|
|
44
67
|
// 就回`Error: Model is disabled`, 而同一模型在「無auth.json」之機器上匿名可用——結果隨執行機器而異。
|
|
@@ -95,7 +118,7 @@ let OWN_KEYS = ['exe', 'model', 'key', 'provider', 'useStoredAuth', 'agent', 'co
|
|
|
95
118
|
* @param {Array} [opt.extraArgs=[]] 輸入額外命令列旗標字串陣列,將接於固定旗標之後,預設[]
|
|
96
119
|
* @param {Object} [opt.env=undefined] 輸入本次調用額外注入之環境變數物件,同時給予key與provider時會再併入OPENCODE_AUTH_CONTENT,預設undefined
|
|
97
120
|
* @param {Number} [opt.timeoutMs=300000] 輸入逾時毫秒正整數,逾時將強制關閉子進程及其子孫程序,全套件統一預設300000
|
|
98
|
-
* @param {String} [opt.cwd=process.cwd()] 輸入子進程工作目錄字串,預設process.cwd()
|
|
121
|
+
* @param {String} [opt.cwd=process.cwd()] 輸入子進程工作目錄字串,預設process.cwd();其絕對路徑並同步注入環境變數PWD(opencode以繼承之PWD優先於真實cwd決定session目錄,見檔頭),呼叫端env內之PWD會被覆寫
|
|
99
122
|
* @param {String|Function} [opt.validate=undefined] 輸入stdout驗證規則字串或自訂驗證函數,規則字串支援'nonempty'、'json'、'min:100',多規則可用逗號串接,預設undefined代表不驗證
|
|
100
123
|
* @param {Number} [opt.maxRetries=0] 輸入失敗後最大重試次數非負整數,預設0
|
|
101
124
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件,內含ok(是否成功布林值)、stdout(標準輸出字串)、stderr(標準錯誤字串)、code(離開碼)、error(錯誤訊息字串,成功時為空字串)、durationMs(耗時毫秒)、attempts(實際嘗試次數),本函數不會reject
|
|
@@ -214,6 +237,15 @@ async function dispatchOpencode(prompt, opt = {}) {
|
|
|
214
237
|
}
|
|
215
238
|
}
|
|
216
239
|
|
|
240
|
+
//PWD同步為有效cwd之絕對路徑(有給cwd則解析之, 否則process.cwd(), 與execCli之spawn cwd同源),
|
|
241
|
+
//以本值蓋過呼叫端env之PWD——opencode以繼承之PWD優先於真實cwd決定session目錄(見檔頭【cwd須同步PWD】)
|
|
242
|
+
let cwdEff = get(opt, 'cwd', null)
|
|
243
|
+
cwdEff = isestr(cwdEff) ? path.resolve(cwdEff) : process.cwd()
|
|
244
|
+
env = {
|
|
245
|
+
...env,
|
|
246
|
+
PWD: cwdEff,
|
|
247
|
+
}
|
|
248
|
+
|
|
217
249
|
//timeoutMs, 無效回退全套件統一預設(dfTimeoutMs=300000), 各轉接器一致令呼叫方無須記多套數字
|
|
218
250
|
let timeoutMs = castPintOr(get(opt, 'timeoutMs', null), dfTimeoutMs)
|
|
219
251
|
|
package/src/providers.mjs
CHANGED
|
@@ -164,7 +164,8 @@
|
|
|
164
164
|
// bash:'ask' 而非'deny'——2026-09-18實測: Zen免費層閘門以「bash工具是否存在」判定是否為opencode本體,
|
|
165
165
|
// deny會把bash自工具清單移除而被判非opencode(403 FreeTierError), ask則工具仍在;
|
|
166
166
|
// 而`opencode run`為非互動, ask一律自動拒絕(stderr: The user rejected permission),
|
|
167
|
-
// 金絲雀實測寫檔與shell建檔皆未落地,
|
|
167
|
+
// 金絲雀實測寫檔與shell建檔皆未落地, 故仍為機械鎖(2026-09-23於1.18.32重驗仍自動拒絕)。
|
|
168
|
+
// 注意呼叫端勿另傳--auto(2026-09-23實測: 帶上即放行ask, 模型以node寫檔落地)。
|
|
168
169
|
// agnes/poolside走opencode但非Zen免費層, 不受閘門影響, 為對稱亦用同一鎖(bash:ask之拒絕行為相同)。
|
|
169
170
|
let OC_READONLY = { edit: 'deny', bash: 'ask' }
|
|
170
171
|
|
|
@@ -179,6 +180,10 @@ let OC_READONLY = { edit: 'deny', bash: 'ask' }
|
|
|
179
180
|
// --strict-mcp-config 排除所有MCP工具(--tools不管MCP, 單用時claude.ai Docs之寫入工具仍在)。
|
|
180
181
|
// 同日實測: 工具清單恰為Glob,Grep,Read; 要求寫檔(含指明可用PowerShell)未落地; 讀檔正常作答。
|
|
181
182
|
// 代價: 失去WebFetch/WebSearch等網路讀取工具; 需要時於條目覆寫extraArgs自行加入。
|
|
183
|
+
// 邊界(2026-09-23查官方permissions文件): 此鎖只管模型可用之工具; 資料夾未受信任時-p仍會執行該專案
|
|
184
|
+
// .claude/settings.json之hooks與env(官方列為「Used」)。於不信任之目錄派工可於條目另加
|
|
185
|
+
// '--setting-sources user'(同日實測可與本鎖及skip並用); 更嚴之'--restricted'須搭配skipPermissions:false
|
|
186
|
+
// (restricted拒絕bypassPermissions, 同日實測帶skip即報錯)。勿帶--bare(不讀OAuth登入, 見dispatchClaude檔頭)。
|
|
182
187
|
let CLAUDE_READONLY = ['--tools', 'Read,Glob,Grep', '--strict-mcp-config']
|
|
183
188
|
|
|
184
189
|
|
|
@@ -23,7 +23,7 @@ import NO_SIDE_EFFECT from './noSideEffectPrefix.mjs'
|
|
|
23
23
|
// 與金鑰無關之失敗而「整組跳過換下一家」(不換組內金鑰——同模型換金鑰仍是
|
|
24
24
|
// 同樣的產出習慣); 端點不穩而偶發空回的模型, 以maxRetries調高令同鍵重試。
|
|
25
25
|
//
|
|
26
|
-
// 【防寫檔前綴】agentic CLI
|
|
26
|
+
// 【防寫檔前綴】agentic CLI之cwd不是隔離邊界(可用絕對路徑寫到cwd外),
|
|
27
27
|
// 故預設在prompt前掛「禁止建檔」約束(實測有效); 不需要時傳promptPrefix:''關閉。
|
|
28
28
|
// 措辭已移至獨立模組wkf/noSideEffectPrefix.mjs(單一來源)——不經本層、
|
|
29
29
|
// 直接呼叫dispatchAiFallback的呼叫端亦應引用同一份, 措辭修正時全體同步;
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
// 本套件早已修正, 形成「套件修了、消費端沒跟上」的分岔。抽成獨立模組後
|
|
7
7
|
// 所有路徑引用同一來源, 措辭再修正時全體同步。
|
|
8
8
|
//
|
|
9
|
-
// 【為何需要此前綴】agentic CLI
|
|
10
|
-
//
|
|
9
|
+
// 【為何需要此前綴】agentic CLI自帶檔案讀寫工具, 且cwd不是隔離邊界(可用絕對路徑
|
|
10
|
+
// 寫到cwd外; 另opencode曾因沿用父進程之PWD而在啟動目錄作業, 2026-09-23已於
|
|
11
|
+
// dispatchOpencode修正, 見該檔檔頭), 實測會「順手」把結果另存孤兒檔,
|
|
11
12
|
// 甚至可能覆蓋呼叫端資料本體(殷鑑: 2026-08-10執行任務歷史.md遭AI覆寫、
|
|
12
13
|
// 評比腳本繞過前綴又產生根目錄孤兒檔); 各家CLI的權限機制彼此不同(codex有sandbox、
|
|
13
14
|
// claude有--disallowedTools、opencode有config.permission),
|
|
@@ -61,6 +61,7 @@ process.stdin.on('end', () => {
|
|
|
61
61
|
OPENCODE_AUTH_CONTENT: process.env.OPENCODE_AUTH_CONTENT || '',
|
|
62
62
|
OPENCODE_CONFIG_CONTENT: process.env.OPENCODE_CONFIG_CONTENT || '',
|
|
63
63
|
FAKE_ENV: process.env.FAKE_ENV || '',
|
|
64
|
+
PWD: process.env.PWD || '',
|
|
64
65
|
},
|
|
65
66
|
}
|
|
66
67
|
let end = () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import assert from 'assert'
|
|
2
|
+
import path from 'path'
|
|
2
3
|
import dispatchOpencode from '../src/dispatchOpencode.mjs'
|
|
3
4
|
import createFakeCli from './tools/fakeCliForTest.mjs'
|
|
4
5
|
|
|
@@ -182,6 +183,26 @@ describe('dispatchOpencode', function() {
|
|
|
182
183
|
assert.strict.deepEqual(r, rr)
|
|
183
184
|
})
|
|
184
185
|
|
|
186
|
+
it('PWD同步為有效cwd之絕對路徑: 有給cwd(絕對或相對)即解析之且等於子進程真實cwd, 未給則為process.cwd()', async function() {
|
|
187
|
+
let t1 = await dispatchOpencode('abc', { exe: fake.exe, cwd: fake.fd })
|
|
188
|
+
let t2 = await dispatchOpencode('abc', { exe: fake.exe, cwd: path.relative(process.cwd(), fake.fd) })
|
|
189
|
+
let t3 = await dispatchOpencode('abc', { exe: fake.exe })
|
|
190
|
+
let o1 = JSON.parse(t1.stdout)
|
|
191
|
+
let o2 = JSON.parse(t2.stdout)
|
|
192
|
+
let o3 = JSON.parse(t3.stdout)
|
|
193
|
+
let r = [o1.env.PWD, o1.env.PWD === o1.cwd, o2.env.PWD, o3.env.PWD, o3.env.PWD === o3.cwd]
|
|
194
|
+
let rr = [path.resolve(fake.fd), true, path.resolve(fake.fd), process.cwd(), true]
|
|
195
|
+
assert.strict.deepEqual(r, rr)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('呼叫端env內之PWD(含整份展開process.env之寫法)被有效cwd覆寫, 其餘變數保留', async function() {
|
|
199
|
+
let t = await dispatchOpencode('abc', { exe: fake.exe, cwd: fake.fd, env: { ...process.env, PWD: 'C:/somewhere/else', FAKE_ENV: 'x' } })
|
|
200
|
+
let o = JSON.parse(t.stdout)
|
|
201
|
+
let r = [o.env.PWD, o.env.FAKE_ENV]
|
|
202
|
+
let rr = [path.resolve(fake.fd), 'x']
|
|
203
|
+
assert.strict.deepEqual(r, rr)
|
|
204
|
+
})
|
|
205
|
+
|
|
185
206
|
it('prompt以stdin傳入, 中文與多行皆完整保留', async function() {
|
|
186
207
|
let prompt = '第一行中文\n第二行 with "quote" & <sym>\n第三行'
|
|
187
208
|
let t = await dispatchOpencode(prompt, { exe: fake.exe })
|