w-dispatch-ai 1.0.1 → 1.0.2

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.
@@ -0,0 +1,173 @@
1
+ import assert from 'assert'
2
+ import dispatchAntigravity from '../src/dispatchAntigravity.mjs'
3
+ import createFakeCli from './tools/fakeCliForTest.mjs'
4
+
5
+
6
+ describe('dispatchAntigravity', function() {
7
+
8
+ let fake = null
9
+
10
+ before(function() {
11
+ fake = createFakeCli('fake-agy')
12
+ })
13
+
14
+ after(function() {
15
+ if (fake) {
16
+ fake.clean()
17
+ }
18
+ })
19
+
20
+ it('prompt非有效字串時回傳錯誤結果物件且不reject', async function() {
21
+ let r = []
22
+ for (let prompt of [null, undefined, '', 123, {}, []]) {
23
+ let t = await dispatchAntigravity(prompt)
24
+ r.push([t.ok, t.error, t.attempts])
25
+ }
26
+ let rr = [
27
+ [false, 'prompt must be a non-empty string', 0],
28
+ [false, 'prompt must be a non-empty string', 0],
29
+ [false, 'prompt must be a non-empty string', 0],
30
+ [false, 'prompt must be a non-empty string', 0],
31
+ [false, 'prompt must be a non-empty string', 0],
32
+ [false, 'prompt must be a non-empty string', 0],
33
+ ]
34
+ assert.strict.deepEqual(r, rr)
35
+ })
36
+
37
+ it('prompt超過30000字元時回傳prompt too long且不spawn', async function() {
38
+ let t = await dispatchAntigravity('x'.repeat(30001), { exe: fake.exe })
39
+ let r = [t.ok, t.error.indexOf('prompt too long (30001 chars > 30000)') === 0, t.attempts]
40
+ let rr = [false, true, 0]
41
+ assert.strict.deepEqual(r, rr)
42
+ })
43
+
44
+ it('prompt恰為30000字元時可正常spawn', async function() {
45
+ let prompt = 'x'.repeat(30000)
46
+ let t = await dispatchAntigravity(prompt, { exe: fake.exe })
47
+ let o = JSON.parse(t.stdout)
48
+ let r = [t.ok, o.args[o.args.length - 1] === prompt]
49
+ let rr = [true, true]
50
+ assert.strict.deepEqual(r, rr)
51
+ })
52
+
53
+ it('轉義膨脹突破命令列上限時由try/catch兜底回傳錯誤結果物件不reject', async function() {
54
+ //29000個引號長度過前置檢查, 但spawn轉義後約58000字元 → 同步拋ENAMETOOLONG
55
+ let t = await dispatchAntigravity('"'.repeat(29000), { exe: fake.exe })
56
+ let r = [t.ok, t.error.includes('ENAMETOOLONG'), t.attempts]
57
+ let rr = [false, true, 0]
58
+ assert.strict.deepEqual(r, rr)
59
+ })
60
+
61
+ it('預設帶--dangerously-skip-permissions與--print-timeout, prompt為--print之值且位於最後', async function() {
62
+ let t = await dispatchAntigravity('abc', { exe: fake.exe })
63
+ let o = JSON.parse(t.stdout)
64
+ let r = [t.ok, o.args]
65
+ let rr = [true, ['--dangerously-skip-permissions', '--print-timeout', '270s', '--print', 'abc']]
66
+ assert.strict.deepEqual(r, rr)
67
+ })
68
+
69
+ it('prompt不走stdin(agy由--print取得, 塞stdin會進互動模式)', async function() {
70
+ let t = await dispatchAntigravity('abc', { exe: fake.exe })
71
+ let o = JSON.parse(t.stdout)
72
+ let r = [o.stdin, o.args.includes('abc')]
73
+ let rr = ['', true]
74
+ assert.strict.deepEqual(r, rr)
75
+ })
76
+
77
+ it('有給model時帶--model旗標', async function() {
78
+ let t = await dispatchAntigravity('abc', { exe: fake.exe, model: 'gemini-3.6-flash-low' })
79
+ let o = JSON.parse(t.stdout)
80
+ let r = o.args
81
+ let rr = ['--dangerously-skip-permissions', '--print-timeout', '270s', '--model', 'gemini-3.6-flash-low', '--print', 'abc']
82
+ assert.strict.deepEqual(r, rr)
83
+ })
84
+
85
+ it('有給effort時帶--effort旗標', async function() {
86
+ let t = await dispatchAntigravity('abc', { exe: fake.exe, model: 'gemini-3.1-pro', effort: 'low' })
87
+ let o = JSON.parse(t.stdout)
88
+ let r = o.args
89
+ let rr = ['--dangerously-skip-permissions', '--print-timeout', '270s', '--model', 'gemini-3.1-pro', '--effort', 'low', '--print', 'abc']
90
+ assert.strict.deepEqual(r, rr)
91
+ })
92
+
93
+ it('skipPermissions為false時不帶--dangerously-skip-permissions旗標', async function() {
94
+ let t = await dispatchAntigravity('abc', { exe: fake.exe, skipPermissions: false })
95
+ let o = JSON.parse(t.stdout)
96
+ let r = [t.ok, o.args]
97
+ let rr = [true, ['--print-timeout', '270s', '--print', 'abc']]
98
+ assert.strict.deepEqual(r, rr)
99
+ })
100
+
101
+ it('printTimeout由timeoutMs推導並預留30秒緩衝, 下限30秒', async function() {
102
+ let r = []
103
+ for (let timeoutMs of [600000, 120000, 5000, undefined]) {
104
+ let opt = { exe: fake.exe }
105
+ if (timeoutMs !== undefined) {
106
+ opt.timeoutMs = timeoutMs
107
+ }
108
+ let t = await dispatchAntigravity('abc', opt)
109
+ let o = JSON.parse(t.stdout)
110
+ let i = o.args.indexOf('--print-timeout')
111
+ r.push(o.args[i + 1])
112
+ }
113
+ let rr = ['570s', '90s', '30s', '270s'] //600-30, 120-30, max(30,5-30), 預設300-30
114
+ assert.strict.deepEqual(r, rr)
115
+ })
116
+
117
+ it('printTimeout明確給定時不推導', async function() {
118
+ let t = await dispatchAntigravity('abc', { exe: fake.exe, printTimeout: '10m', timeoutMs: 600000 })
119
+ let o = JSON.parse(t.stdout)
120
+ let i = o.args.indexOf('--print-timeout')
121
+ let r = o.args[i + 1]
122
+ let rr = '10m'
123
+ assert.strict.deepEqual(r, rr)
124
+ })
125
+
126
+ it('addDirs逐項展開為--add-dir並濾除非有效字串', async function() {
127
+ let t = await dispatchAntigravity('abc', { exe: fake.exe, addDirs: ['c:/a', null, 'c:/b', 123] })
128
+ let o = JSON.parse(t.stdout)
129
+ let r = o.args
130
+ let rr = ['--dangerously-skip-permissions', '--print-timeout', '270s', '--add-dir', 'c:/a', '--add-dir', 'c:/b', '--print', 'abc']
131
+ assert.strict.deepEqual(r, rr)
132
+ })
133
+
134
+ it('extraArgs接於固定旗標之後且於--print之前', async function() {
135
+ let t = await dispatchAntigravity('abc', { exe: fake.exe, extraArgs: ['--output-format', 'json'] })
136
+ let o = JSON.parse(t.stdout)
137
+ let r = o.args
138
+ let rr = ['--dangerously-skip-permissions', '--print-timeout', '270s', '--output-format', 'json', '--print', 'abc']
139
+ assert.strict.deepEqual(r, rr)
140
+ })
141
+
142
+ it('中文與多行prompt於--print值內完整保留', async function() {
143
+ let prompt = '第一行中文\n第二行 with "quote" & <sym>\n第三行'
144
+ let t = await dispatchAntigravity(prompt, { exe: fake.exe })
145
+ let o = JSON.parse(t.stdout)
146
+ let r = o.args[o.args.length - 1] === prompt
147
+ let rr = true
148
+ assert.strict.deepEqual(r, rr)
149
+ })
150
+
151
+ it('執行檔不存在時回傳ENOENT且不重試', async function() {
152
+ let t = await dispatchAntigravity('abc', { exe: 'agy-not-exist-for-test', maxRetries: 3 })
153
+ let r = [t.ok, t.error.includes('ENOENT'), t.attempts]
154
+ let rr = [false, true, 1]
155
+ assert.strict.deepEqual(r, rr)
156
+ })
157
+
158
+ it('離開碼非0時回傳失敗(如model與effort衝突時agy回exit 1)', async function() {
159
+ let t = await dispatchAntigravity('abc', { exe: fake.exe, extraArgs: ['--fake-exit=1', '--fake-stderr=conflicts with --effort'] })
160
+ let r = [t.ok, t.code, t.error, t.stderr]
161
+ let rr = [false, 1, 'Exit code 1', 'conflicts with --effort']
162
+ assert.strict.deepEqual(r, rr)
163
+ })
164
+
165
+ it('cwd等execCli設定原樣轉傳', async function() {
166
+ let t = await dispatchAntigravity('abc', { exe: fake.exe, cwd: process.cwd() })
167
+ let o = JSON.parse(t.stdout)
168
+ let r = [t.ok, o.cwd === process.cwd()]
169
+ let rr = [true, true]
170
+ assert.strict.deepEqual(r, rr)
171
+ })
172
+
173
+ })