claude-yes 1.28.0 → 1.30.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/dist/claude-yes.js +20037 -442
- package/dist/cli.js +20037 -442
- package/dist/cli.js.map +353 -9
- package/dist/codex-yes.js +20037 -442
- package/dist/copilot-yes.js +20037 -442
- package/dist/cursor-yes.js +20037 -442
- package/dist/gemini-yes.js +20037 -442
- package/dist/grok-yes.js +20037 -442
- package/dist/index.js +18433 -379
- package/dist/index.js.map +311 -7
- package/dist/qwen-yes.js +20037 -442
- package/package.json +15 -11
- package/ts/ReadyManager.spec.ts +72 -0
- package/ts/catcher.spec.ts +260 -0
- package/ts/catcher.ts +35 -0
- package/ts/cli.ts +24 -6
- package/ts/idleWaiter.spec.ts +55 -0
- package/ts/index.ts +65 -21
- package/ts/parseCliArgs.ts +8 -6
- package/ts/removeControlCharacters.spec.ts +74 -0
- package/ts/utils.spec.ts +169 -0
- package/ts/yesLog.spec.ts +74 -0
- package/ts/yesLog.ts +1 -1
- package/ts/tryCatch.ts +0 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-yes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
"import": "./dist/index.js",
|
|
34
34
|
"types": "./ts/index.ts"
|
|
35
35
|
},
|
|
36
|
-
"main": "dist/index.js",
|
|
37
36
|
"module": "ts/index.ts",
|
|
38
37
|
"types": "./ts/index.ts",
|
|
39
38
|
"bin": {
|
|
@@ -51,15 +50,19 @@
|
|
|
51
50
|
},
|
|
52
51
|
"files": [
|
|
53
52
|
"ts/*.ts",
|
|
54
|
-
"dist"
|
|
53
|
+
"dist",
|
|
54
|
+
"scripts"
|
|
55
55
|
],
|
|
56
56
|
"scripts": {
|
|
57
|
-
"build": "bun build
|
|
57
|
+
"build": "bun run build:index && bun run build:cli",
|
|
58
58
|
"postbuild": "bun ./ts/postbuild.ts",
|
|
59
|
-
"
|
|
59
|
+
"build:cli": "bun build ts/cli.ts --outdir=dist --target=node --sourcemap --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
60
|
+
"build:index": "bun build ts/index.ts --outdir=dist --target=node --sourcemap --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
61
|
+
"demo": "bun run build && bun link && claude-yes -- demo",
|
|
62
|
+
"dev": "bun ts/index.ts",
|
|
60
63
|
"fmt": "bunx @biomejs/biome check --fix && bunx sort-package-json",
|
|
61
64
|
"prepack": "bun run build",
|
|
62
|
-
"prepare": "bunx husky",
|
|
65
|
+
"prepare": "bunx husky && bun run build",
|
|
63
66
|
"test": "bun test --coverage"
|
|
64
67
|
},
|
|
65
68
|
"lint-staged": {
|
|
@@ -88,7 +91,11 @@
|
|
|
88
91
|
]
|
|
89
92
|
},
|
|
90
93
|
"dependencies": {
|
|
91
|
-
"
|
|
94
|
+
"@types/ms": "^2.1.0",
|
|
95
|
+
"bun": "^1.3.1",
|
|
96
|
+
"bun-pty": "^0.3.2",
|
|
97
|
+
"from-node-stream": "^0.0.11",
|
|
98
|
+
"ms": "^2.1.3"
|
|
92
99
|
},
|
|
93
100
|
"devDependencies": {
|
|
94
101
|
"@biomejs/biome": "^2.2.5",
|
|
@@ -101,9 +108,7 @@
|
|
|
101
108
|
"@types/node": "^24.0.10",
|
|
102
109
|
"@types/yargs": "^17.0.33",
|
|
103
110
|
"cpu-wait": "^0.0.10",
|
|
104
|
-
"enhanced-ms": "^4.1.0",
|
|
105
111
|
"execa": "^9.6.0",
|
|
106
|
-
"from-node-stream": "^0.0.11",
|
|
107
112
|
"husky": "^9.1.7",
|
|
108
113
|
"lint-staged": "^16.1.4",
|
|
109
114
|
"p-map": "^7.0.3",
|
|
@@ -114,12 +119,11 @@
|
|
|
114
119
|
"strip-ansi-control-characters": "^2.0.0",
|
|
115
120
|
"terminal-render": "^1.2.0",
|
|
116
121
|
"tsa-composer": "^3.0.2",
|
|
117
|
-
"tsx": "^4.20.3",
|
|
118
122
|
"vitest": "^3.2.4",
|
|
119
123
|
"yargs": "^18.0.0"
|
|
120
124
|
},
|
|
121
125
|
"peerDependencies": {
|
|
122
|
-
"node-pty": "^1.
|
|
126
|
+
"node-pty": "^1.1.0-beta38",
|
|
123
127
|
"typescript": "^5.8.3"
|
|
124
128
|
}
|
|
125
129
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { ReadyManager } from './ReadyManager';
|
|
3
|
+
|
|
4
|
+
describe('ReadyManager', () => {
|
|
5
|
+
it('should start in not ready state', () => {
|
|
6
|
+
const manager = new ReadyManager();
|
|
7
|
+
expect(manager.wait()).toBeInstanceOf(Promise);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should resolve wait when ready is called', async () => {
|
|
11
|
+
const manager = new ReadyManager();
|
|
12
|
+
const waitPromise = manager.wait();
|
|
13
|
+
|
|
14
|
+
manager.ready();
|
|
15
|
+
|
|
16
|
+
await expect(waitPromise).resolves.toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should resolve immediately if already ready', async () => {
|
|
20
|
+
const manager = new ReadyManager();
|
|
21
|
+
manager.ready();
|
|
22
|
+
|
|
23
|
+
const result = manager.wait();
|
|
24
|
+
expect(result).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should handle multiple waiters', async () => {
|
|
28
|
+
const manager = new ReadyManager();
|
|
29
|
+
const wait1 = manager.wait();
|
|
30
|
+
const wait2 = manager.wait();
|
|
31
|
+
const wait3 = manager.wait();
|
|
32
|
+
|
|
33
|
+
manager.ready();
|
|
34
|
+
|
|
35
|
+
await Promise.all([
|
|
36
|
+
expect(wait1).resolves.toBeUndefined(),
|
|
37
|
+
expect(wait2).resolves.toBeUndefined(),
|
|
38
|
+
expect(wait3).resolves.toBeUndefined(),
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should reset to not ready when unready is called', async () => {
|
|
43
|
+
const manager = new ReadyManager();
|
|
44
|
+
manager.ready();
|
|
45
|
+
manager.unready();
|
|
46
|
+
|
|
47
|
+
expect(manager.wait()).toBeInstanceOf(Promise);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle ready with no waiting queue', () => {
|
|
51
|
+
const manager = new ReadyManager();
|
|
52
|
+
manager.ready(); // Should not throw even if no one is waiting
|
|
53
|
+
expect(manager.wait()).toBeUndefined(); // Should be ready now
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should handle multiple ready/unready cycles', async () => {
|
|
57
|
+
const manager = new ReadyManager();
|
|
58
|
+
|
|
59
|
+
// First cycle
|
|
60
|
+
const wait1 = manager.wait();
|
|
61
|
+
manager.ready();
|
|
62
|
+
await wait1;
|
|
63
|
+
|
|
64
|
+
// Reset
|
|
65
|
+
manager.unready();
|
|
66
|
+
|
|
67
|
+
// Second cycle
|
|
68
|
+
const wait2 = manager.wait();
|
|
69
|
+
manager.ready();
|
|
70
|
+
await expect(wait2).resolves.toBeUndefined();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { catcher } from './catcher';
|
|
3
|
+
|
|
4
|
+
describe('catcher', () => {
|
|
5
|
+
describe('curried overload', () => {
|
|
6
|
+
it('should return a function when called with only catchFn', () => {
|
|
7
|
+
const catchFn = () => 'error';
|
|
8
|
+
const result = catcher(catchFn);
|
|
9
|
+
expect(typeof result).toBe('function');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should catch errors and call catchFn with error, function, and args', () => {
|
|
13
|
+
let catchedError: unknown;
|
|
14
|
+
let catchedFn: unknown;
|
|
15
|
+
let catchedArgs: unknown[];
|
|
16
|
+
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
17
|
+
catchedError = error;
|
|
18
|
+
catchedFn = fn;
|
|
19
|
+
catchedArgs = args;
|
|
20
|
+
return 'caught';
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let calledArgs: unknown[] = [];
|
|
24
|
+
const errorFn = (...args: unknown[]) => {
|
|
25
|
+
calledArgs = args;
|
|
26
|
+
throw new Error('test error');
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const wrappedFn = catcher(catchFn)(errorFn);
|
|
30
|
+
const result = wrappedFn('arg1', 'arg2');
|
|
31
|
+
|
|
32
|
+
expect(result).toBe('caught');
|
|
33
|
+
expect(catchedError).toBeInstanceOf(Error);
|
|
34
|
+
expect(catchedFn).toBe(errorFn);
|
|
35
|
+
expect(catchedArgs).toEqual(['arg1', 'arg2']);
|
|
36
|
+
expect(calledArgs).toEqual(['arg1', 'arg2']);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return normal result when no error occurs', () => {
|
|
40
|
+
let catchCalled = false;
|
|
41
|
+
const catchFn = () => {
|
|
42
|
+
catchCalled = true;
|
|
43
|
+
return 'error';
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let calledArgs: unknown[] = [];
|
|
47
|
+
const normalFn = (...args: unknown[]) => {
|
|
48
|
+
calledArgs = args;
|
|
49
|
+
return 'success';
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const wrappedFn = catcher(catchFn)(normalFn);
|
|
53
|
+
const result = wrappedFn('arg1', 'arg2');
|
|
54
|
+
|
|
55
|
+
expect(result).toBe('success');
|
|
56
|
+
expect(catchCalled).toBe(false);
|
|
57
|
+
expect(calledArgs).toEqual(['arg1', 'arg2']);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('direct overload', () => {
|
|
62
|
+
it('should catch errors and call catchFn with error, function, and args directly', () => {
|
|
63
|
+
let catchedError: unknown;
|
|
64
|
+
let catchedFn: unknown;
|
|
65
|
+
let catchedArgs: unknown[];
|
|
66
|
+
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
67
|
+
catchedError = error;
|
|
68
|
+
catchedFn = fn;
|
|
69
|
+
catchedArgs = args;
|
|
70
|
+
return 'caught';
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let calledArgs: unknown[] = [];
|
|
74
|
+
const errorFn = (...args: unknown[]) => {
|
|
75
|
+
calledArgs = args;
|
|
76
|
+
throw new Error('test error');
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const wrappedFn = catcher(catchFn, errorFn);
|
|
80
|
+
const result = wrappedFn('arg1', 'arg2');
|
|
81
|
+
|
|
82
|
+
expect(result).toBe('caught');
|
|
83
|
+
expect(catchedError).toBeInstanceOf(Error);
|
|
84
|
+
expect(catchedFn).toBe(errorFn);
|
|
85
|
+
expect(catchedArgs).toEqual(['arg1', 'arg2']);
|
|
86
|
+
expect(calledArgs).toEqual(['arg1', 'arg2']);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should return normal result when no error occurs directly', () => {
|
|
90
|
+
let catchCalled = false;
|
|
91
|
+
const catchFn = () => {
|
|
92
|
+
catchCalled = true;
|
|
93
|
+
return 'error';
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
let calledArgs: unknown[] = [];
|
|
97
|
+
const normalFn = (...args: unknown[]) => {
|
|
98
|
+
calledArgs = args;
|
|
99
|
+
return 'success';
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const wrappedFn = catcher(catchFn, normalFn);
|
|
103
|
+
const result = wrappedFn('arg1', 'arg2');
|
|
104
|
+
|
|
105
|
+
expect(result).toBe('success');
|
|
106
|
+
expect(catchCalled).toBe(false);
|
|
107
|
+
expect(calledArgs).toEqual(['arg1', 'arg2']);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('error handling', () => {
|
|
112
|
+
it('should handle different error types and pass function context', () => {
|
|
113
|
+
const results: unknown[] = [];
|
|
114
|
+
const functions: unknown[] = [];
|
|
115
|
+
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
116
|
+
results.push(error);
|
|
117
|
+
functions.push(fn);
|
|
118
|
+
return 'handled';
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// String error
|
|
122
|
+
const stringErrorFn = () => {
|
|
123
|
+
throw 'string error';
|
|
124
|
+
};
|
|
125
|
+
const wrappedStringFn = catcher(catchFn, stringErrorFn);
|
|
126
|
+
expect(wrappedStringFn()).toBe('handled');
|
|
127
|
+
expect(results[0]).toBe('string error');
|
|
128
|
+
expect(functions[0]).toBe(stringErrorFn);
|
|
129
|
+
|
|
130
|
+
// Object error
|
|
131
|
+
const objectError = { message: 'object error' };
|
|
132
|
+
const objectErrorFn = () => {
|
|
133
|
+
throw objectError;
|
|
134
|
+
};
|
|
135
|
+
const wrappedObjectFn = catcher(catchFn, objectErrorFn);
|
|
136
|
+
expect(wrappedObjectFn()).toBe('handled');
|
|
137
|
+
expect(results[1]).toBe(objectError);
|
|
138
|
+
expect(functions[1]).toBe(objectErrorFn);
|
|
139
|
+
|
|
140
|
+
// null error
|
|
141
|
+
const nullErrorFn = () => {
|
|
142
|
+
throw null;
|
|
143
|
+
};
|
|
144
|
+
const wrappedNullFn = catcher(catchFn, nullErrorFn);
|
|
145
|
+
expect(wrappedNullFn()).toBe('handled');
|
|
146
|
+
expect(results[2]).toBe(null);
|
|
147
|
+
expect(functions[2]).toBe(nullErrorFn);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should preserve function parameters and pass them to catchFn', () => {
|
|
151
|
+
let caughtError: unknown;
|
|
152
|
+
let caughtFn: unknown;
|
|
153
|
+
let caughtArgs: unknown[];
|
|
154
|
+
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
155
|
+
caughtError = error;
|
|
156
|
+
caughtFn = fn;
|
|
157
|
+
caughtArgs = args;
|
|
158
|
+
return 'caught';
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
let testArgs: [number, string, boolean] | undefined;
|
|
162
|
+
const testFn = (a: number, b: string, c: boolean) => {
|
|
163
|
+
testArgs = [a, b, c];
|
|
164
|
+
if (a > 5) throw new Error('too big');
|
|
165
|
+
return `${a}-${b}-${c}`;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const wrappedFn = catcher(catchFn, testFn);
|
|
169
|
+
|
|
170
|
+
// Normal execution
|
|
171
|
+
expect(wrappedFn(3, 'test', true)).toBe('3-test-true');
|
|
172
|
+
expect(testArgs).toEqual([3, 'test', true]);
|
|
173
|
+
|
|
174
|
+
// Error execution
|
|
175
|
+
expect(wrappedFn(10, 'error', false)).toBe('caught');
|
|
176
|
+
expect(testArgs).toEqual([10, 'error', false]);
|
|
177
|
+
expect(caughtError).toBeInstanceOf(Error);
|
|
178
|
+
expect(caughtFn).toBe(testFn);
|
|
179
|
+
expect(caughtArgs).toEqual([10, 'error', false]);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should handle functions with no parameters', () => {
|
|
183
|
+
let caughtError: unknown;
|
|
184
|
+
let caughtFn: unknown;
|
|
185
|
+
let caughtArgs: unknown[];
|
|
186
|
+
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
187
|
+
caughtError = error;
|
|
188
|
+
caughtFn = fn;
|
|
189
|
+
caughtArgs = args;
|
|
190
|
+
return 'no params caught';
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
let called = false;
|
|
194
|
+
const noParamsFn = () => {
|
|
195
|
+
called = true;
|
|
196
|
+
throw new Error('no params error');
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const wrappedFn = catcher(catchFn, noParamsFn);
|
|
200
|
+
const result = wrappedFn();
|
|
201
|
+
|
|
202
|
+
expect(result).toBe('no params caught');
|
|
203
|
+
expect(called).toBe(true);
|
|
204
|
+
expect(caughtError).toBeInstanceOf(Error);
|
|
205
|
+
expect(caughtFn).toBe(noParamsFn);
|
|
206
|
+
expect(caughtArgs).toEqual([]);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should handle functions returning different types', () => {
|
|
210
|
+
const catchFn = () => null;
|
|
211
|
+
|
|
212
|
+
// Function returning number
|
|
213
|
+
const numberFn = catcher(catchFn, () => 42);
|
|
214
|
+
expect(numberFn()).toBe(42);
|
|
215
|
+
|
|
216
|
+
// Function returning object
|
|
217
|
+
const obj = { key: 'value' };
|
|
218
|
+
const objectFn = catcher(catchFn, () => obj);
|
|
219
|
+
expect(objectFn()).toBe(obj);
|
|
220
|
+
|
|
221
|
+
// Function returning undefined
|
|
222
|
+
const undefinedFn = catcher(catchFn, () => undefined);
|
|
223
|
+
expect(undefinedFn()).toBeUndefined();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('type safety', () => {
|
|
228
|
+
it('should maintain function signature', () => {
|
|
229
|
+
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) =>
|
|
230
|
+
'error';
|
|
231
|
+
const originalFn = (a: number, b: string): string => `${a}-${b}`;
|
|
232
|
+
|
|
233
|
+
const wrappedFn = catcher(catchFn, originalFn);
|
|
234
|
+
|
|
235
|
+
// This should be type-safe
|
|
236
|
+
const result: string = wrappedFn(1, 'test');
|
|
237
|
+
expect(result).toBe('1-test');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should pass function reference and arguments to catchFn', () => {
|
|
241
|
+
let capturedFn: unknown;
|
|
242
|
+
let capturedArgs: unknown[];
|
|
243
|
+
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
244
|
+
capturedFn = fn;
|
|
245
|
+
capturedArgs = args;
|
|
246
|
+
return 'handled';
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const testFn = (x: number, y: string) => {
|
|
250
|
+
throw new Error('test');
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const wrappedFn = catcher(catchFn, testFn);
|
|
254
|
+
wrappedFn(42, 'hello');
|
|
255
|
+
|
|
256
|
+
expect(capturedFn).toBe(testFn);
|
|
257
|
+
expect(capturedArgs).toEqual([42, 'hello']);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
package/ts/catcher.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// curried overload
|
|
2
|
+
export function catcher<F extends (...args: any[]) => any, R>(
|
|
3
|
+
catchFn: (error: unknown, fn: F, ...args: Parameters<F>) => R,
|
|
4
|
+
): (fn: F) => (...args: Parameters<F>) => ReturnType<F> | R;
|
|
5
|
+
|
|
6
|
+
// direct overload
|
|
7
|
+
export function catcher<F extends (...args: any[]) => any, R>(
|
|
8
|
+
catchFn: (error: unknown, fn: F, ...args: Parameters<F>) => R,
|
|
9
|
+
fn: F,
|
|
10
|
+
): (...args: Parameters<F>) => ReturnType<F> | R;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A utility function to wrap another function with a try-catch block.
|
|
14
|
+
* If an error occurs during the execution of the function, the provided
|
|
15
|
+
* catchFn is called with the error, the original function, and its arguments.
|
|
16
|
+
*
|
|
17
|
+
* This function supports both direct invocation and curried usage.
|
|
18
|
+
*
|
|
19
|
+
* @param catchFn - The function to call when an error occurs.
|
|
20
|
+
* @param fn - The function to wrap (optional for curried usage).
|
|
21
|
+
* @returns A new function that wraps the original function with error handling.
|
|
22
|
+
*/
|
|
23
|
+
export function catcher<F extends (...args: any[]) => any, R>(
|
|
24
|
+
catchFn: (error: unknown, fn: F, ...args: Parameters<F>) => R,
|
|
25
|
+
fn?: F,
|
|
26
|
+
) {
|
|
27
|
+
if (!fn) return (fn: F) => catcher(catchFn, fn) as any;
|
|
28
|
+
return (...args: Parameters<F>) => {
|
|
29
|
+
try {
|
|
30
|
+
return fn(...args);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
return catchFn(error, fn, ...args);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
package/ts/cli.ts
CHANGED
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import DIE from 'phpdie';
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
import cliYesConfig from '../cli-yes.config';
|
|
4
|
+
|
|
5
|
+
// if node-pty is not installed, re-run with bun
|
|
6
|
+
// const hasNodePty = !!await import('node-pty').catch(() => null);
|
|
7
|
+
if (!globalThis.Bun) {
|
|
8
|
+
// run with same arguments in Bun if not already
|
|
9
|
+
console.log('Re-running with Bun...', process.argv);
|
|
10
|
+
(await import('child_process')).spawnSync(
|
|
11
|
+
'node_modules/.bin/bun',
|
|
12
|
+
[process.argv[1]!, '--', ...process.argv.slice(2)],
|
|
13
|
+
{ stdio: 'inherit' },
|
|
14
|
+
);
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
// check and fix bun-pty on some systems
|
|
18
|
+
if (globalThis.Bun) console.log('Bun detected, using bun-pty');
|
|
19
|
+
// await import("./fix-pty.js")
|
|
20
|
+
|
|
21
|
+
// console.log('Running', process.argv);
|
|
22
|
+
|
|
23
|
+
// Import the CLI module
|
|
24
|
+
const { default: cliYes, parseCliArgs } = await import('./');
|
|
5
25
|
|
|
6
26
|
// Parse CLI arguments
|
|
7
27
|
const config = parseCliArgs(process.argv);
|
|
8
28
|
|
|
9
29
|
// Validate CLI name
|
|
10
|
-
if (!config.cli)
|
|
11
|
-
DIE
|
|
12
|
-
}
|
|
30
|
+
if (!config.cli)
|
|
31
|
+
DIE`missing cli def, available clis: ${Object.keys((await cliYesConfig).clis).join(', ')}`;
|
|
13
32
|
|
|
14
|
-
// console.clear();
|
|
15
33
|
if (config.verbose) {
|
|
16
34
|
process.env.VERBOSE = 'true'; // enable verbose logging in yesLog.ts
|
|
17
35
|
console.log(config);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { IdleWaiter } from './idleWaiter';
|
|
3
|
+
|
|
4
|
+
describe('IdleWaiter', () => {
|
|
5
|
+
it('should initialize with current time', () => {
|
|
6
|
+
const waiter = new IdleWaiter();
|
|
7
|
+
expect(waiter.lastActivityTime).toBeCloseTo(Date.now(), -2);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should update lastActivityTime when ping is called', () => {
|
|
11
|
+
const waiter = new IdleWaiter();
|
|
12
|
+
const initialTime = waiter.lastActivityTime;
|
|
13
|
+
|
|
14
|
+
// Wait a small amount
|
|
15
|
+
const start = Date.now();
|
|
16
|
+
while (Date.now() - start < 10) {
|
|
17
|
+
// busy wait
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
waiter.ping();
|
|
21
|
+
expect(waiter.lastActivityTime).toBeGreaterThan(initialTime);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should return this when ping is called for chaining', () => {
|
|
25
|
+
const waiter = new IdleWaiter();
|
|
26
|
+
expect(waiter.ping()).toBe(waiter);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should resolve wait immediately when already idle', async () => {
|
|
30
|
+
const waiter = new IdleWaiter();
|
|
31
|
+
|
|
32
|
+
// Wait enough time to be considered idle
|
|
33
|
+
const start = Date.now();
|
|
34
|
+
while (Date.now() - start < 50) {
|
|
35
|
+
// busy wait
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// This should resolve quickly since enough time has passed
|
|
39
|
+
const waitPromise = waiter.wait(10);
|
|
40
|
+
await expect(waitPromise).resolves.toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should respect custom check interval', () => {
|
|
44
|
+
const waiter = new IdleWaiter();
|
|
45
|
+
waiter.checkInterval = 200;
|
|
46
|
+
|
|
47
|
+
expect(waiter.checkInterval).toBe(200);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should have ping method that chains', () => {
|
|
51
|
+
const waiter = new IdleWaiter();
|
|
52
|
+
const result = waiter.ping().ping().ping();
|
|
53
|
+
expect(result).toBe(waiter);
|
|
54
|
+
});
|
|
55
|
+
});
|