@afffun/codexbot 1.0.74 → 1.0.76
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/package.json
CHANGED
|
@@ -3,36 +3,11 @@ import fs from 'node:fs/promises';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
|
-
import readline from 'node:readline/promises';
|
|
7
6
|
|
|
8
7
|
import { buildInstallLicenseClaimUrl, getDefaultDistributionConfig } from './config_service.mjs';
|
|
8
|
+
import { createPrompter, promptUntilValid, trim } from './input_contract_service.mjs';
|
|
9
9
|
import { resolveInstalledControlLayout } from './installed_layout_service.mjs';
|
|
10
10
|
|
|
11
|
-
function trim(value, fallback = '') {
|
|
12
|
-
const text = String(value ?? '').trim();
|
|
13
|
-
return text || String(fallback ?? '').trim();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function createPrompter({ stdin, stdout } = {}) {
|
|
17
|
-
const input = stdin || process.stdin;
|
|
18
|
-
const output = stdout || process.stdout;
|
|
19
|
-
if (!input?.isTTY || !output?.isTTY) {
|
|
20
|
-
return {
|
|
21
|
-
interactive: false,
|
|
22
|
-
ask: async () => '',
|
|
23
|
-
close: async () => {},
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
const rl = readline.createInterface({ input, output });
|
|
27
|
-
return {
|
|
28
|
-
interactive: true,
|
|
29
|
-
ask: async (prompt) => trim(await rl.question(prompt)),
|
|
30
|
-
close: async () => {
|
|
31
|
-
rl.close();
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
11
|
function runChildCapture(spawnFn, command, args, options = {}) {
|
|
37
12
|
return new Promise((resolve, reject) => {
|
|
38
13
|
const child = spawnFn(command, args, {
|
|
@@ -128,9 +103,13 @@ export function createNpmDistributionActivationService(deps = {}) {
|
|
|
128
103
|
if (Boolean(options.nonInteractive) || !prompter.interactive) {
|
|
129
104
|
throw new Error('license key is required');
|
|
130
105
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
106
|
+
return promptUntilValid(prompter, {
|
|
107
|
+
logger,
|
|
108
|
+
prompt: 'License key: ',
|
|
109
|
+
normalize: trim,
|
|
110
|
+
validate: (value) => Boolean(value),
|
|
111
|
+
invalidMessage: 'License key is required.',
|
|
112
|
+
});
|
|
134
113
|
} finally {
|
|
135
114
|
await prompter.close().catch(() => {});
|
|
136
115
|
}
|
package/src/args_service.mjs
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return String(value ?? '').trim();
|
|
3
|
-
}
|
|
1
|
+
import { normalizeConnectorChoice, trim } from './input_contract_service.mjs';
|
|
4
2
|
|
|
5
3
|
function readOptionValue(argv, index, name) {
|
|
6
4
|
if (index + 1 >= argv.length) {
|
|
@@ -94,7 +92,10 @@ function parseBootstrapOptions(argv = []) {
|
|
|
94
92
|
continue;
|
|
95
93
|
}
|
|
96
94
|
if (token === '--connector') {
|
|
97
|
-
options.connector = readOptionValue(argv, i, token);
|
|
95
|
+
options.connector = normalizeConnectorChoice(readOptionValue(argv, i, token));
|
|
96
|
+
if (!options.connector) {
|
|
97
|
+
throw new Error('invalid value for --connector (expected telegram, wecom, or skip)');
|
|
98
|
+
}
|
|
98
99
|
i += 1;
|
|
99
100
|
continue;
|
|
100
101
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import readline from 'node:readline/promises';
|
|
2
|
+
|
|
3
|
+
export function trim(value, fallback = '') {
|
|
4
|
+
const text = String(value ?? '').trim();
|
|
5
|
+
return text || String(fallback ?? '').trim();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function normalizeConnectorChoice(value) {
|
|
9
|
+
const raw = trim(value).toLowerCase();
|
|
10
|
+
if (raw === '1') return 'telegram';
|
|
11
|
+
if (raw === '2') return 'wecom';
|
|
12
|
+
if (raw === '3') return 'skip';
|
|
13
|
+
if (raw === 'telegram') return 'telegram';
|
|
14
|
+
if (raw === 'wecom') return 'wecom';
|
|
15
|
+
if (raw === 'skip' || raw === 'none') return 'skip';
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function normalizeList(input) {
|
|
20
|
+
return Array.from(new Set(
|
|
21
|
+
String(input || '')
|
|
22
|
+
.split(/[,\n]/)
|
|
23
|
+
.map((item) => trim(item))
|
|
24
|
+
.filter(Boolean),
|
|
25
|
+
));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function normalizeYesNo(input, fallback = false) {
|
|
29
|
+
const raw = trim(input).toLowerCase();
|
|
30
|
+
if (!raw) return Boolean(fallback);
|
|
31
|
+
if (['y', 'yes', '1', 'true'].includes(raw)) return true;
|
|
32
|
+
if (['n', 'no', '0', 'false'].includes(raw)) return false;
|
|
33
|
+
return Boolean(fallback);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createPrompter({ stdin, stdout } = {}) {
|
|
37
|
+
const input = stdin || process.stdin;
|
|
38
|
+
const output = stdout || process.stdout;
|
|
39
|
+
if (!input?.isTTY || !output?.isTTY) {
|
|
40
|
+
return {
|
|
41
|
+
interactive: false,
|
|
42
|
+
ask: async () => '',
|
|
43
|
+
close: async () => {},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const rl = readline.createInterface({ input, output });
|
|
47
|
+
return {
|
|
48
|
+
interactive: true,
|
|
49
|
+
ask: async (prompt) => trim(await rl.question(prompt)),
|
|
50
|
+
close: async () => {
|
|
51
|
+
rl.close();
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function promptUntilValid(prompter, {
|
|
57
|
+
logger = console,
|
|
58
|
+
prompt,
|
|
59
|
+
normalize = trim,
|
|
60
|
+
validate = (value) => Boolean(value),
|
|
61
|
+
invalidMessage = 'Invalid input. Please try again.',
|
|
62
|
+
} = {}) {
|
|
63
|
+
while (true) {
|
|
64
|
+
const value = normalize(await prompter.ask(prompt));
|
|
65
|
+
if (validate(value)) return value;
|
|
66
|
+
logger?.log?.(invalidMessage);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,36 +1,23 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import
|
|
4
|
+
import {
|
|
5
|
+
createPrompter,
|
|
6
|
+
normalizeConnectorChoice,
|
|
7
|
+
normalizeList,
|
|
8
|
+
normalizeYesNo,
|
|
9
|
+
promptUntilValid,
|
|
10
|
+
trim,
|
|
11
|
+
} from './input_contract_service.mjs';
|
|
5
12
|
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (raw === 'wecom') return 'wecom';
|
|
15
|
-
if (raw === 'skip' || raw === 'none') return 'skip';
|
|
16
|
-
return '';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function normalizeList(input) {
|
|
20
|
-
return Array.from(new Set(
|
|
21
|
-
String(input || '')
|
|
22
|
-
.split(/[,\n]/)
|
|
23
|
-
.map((item) => trim(item))
|
|
24
|
-
.filter(Boolean),
|
|
25
|
-
));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function normalizeYesNo(input, fallback = false) {
|
|
29
|
-
const raw = trim(input).toLowerCase();
|
|
30
|
-
if (!raw) return Boolean(fallback);
|
|
31
|
-
if (['y', 'yes', '1', 'true'].includes(raw)) return true;
|
|
32
|
-
if (['n', 'no', '0', 'false'].includes(raw)) return false;
|
|
33
|
-
return Boolean(fallback);
|
|
13
|
+
async function promptForConnectorChoice(prompter, logger) {
|
|
14
|
+
return promptUntilValid(prompter, {
|
|
15
|
+
logger,
|
|
16
|
+
prompt: 'Choose connector [1=telegram, 2=wecom, 3=skip] (default: 3): ',
|
|
17
|
+
normalize: normalizeConnectorChoice,
|
|
18
|
+
validate: (value) => Boolean(value),
|
|
19
|
+
invalidMessage: 'Invalid connector selection. Enter 1, 2, 3, telegram, wecom, or skip.',
|
|
20
|
+
});
|
|
34
21
|
}
|
|
35
22
|
|
|
36
23
|
function buildConnectorCapabilities(type) {
|
|
@@ -211,26 +198,6 @@ function buildWecomSeed(nowIso, {
|
|
|
211
198
|
};
|
|
212
199
|
}
|
|
213
200
|
|
|
214
|
-
function createPrompter({ stdin, stdout } = {}) {
|
|
215
|
-
const input = stdin || process.stdin;
|
|
216
|
-
const output = stdout || process.stdout;
|
|
217
|
-
if (!input?.isTTY || !output?.isTTY) {
|
|
218
|
-
return {
|
|
219
|
-
interactive: false,
|
|
220
|
-
ask: async () => '',
|
|
221
|
-
close: async () => {},
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
const rl = readline.createInterface({ input, output });
|
|
225
|
-
return {
|
|
226
|
-
interactive: true,
|
|
227
|
-
ask: async (prompt) => trim(await rl.question(prompt)),
|
|
228
|
-
close: async () => {
|
|
229
|
-
rl.close();
|
|
230
|
-
},
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
201
|
async function ensureReadableFile(fsPromises, filePath, label) {
|
|
235
202
|
const target = trim(filePath);
|
|
236
203
|
if (!target) return '';
|
|
@@ -325,7 +292,7 @@ export function createNpmDistributionInstallSeedService(deps = {}) {
|
|
|
325
292
|
}
|
|
326
293
|
if (!connector && !nonInteractive) {
|
|
327
294
|
logger.log('==> Remote control channel setup');
|
|
328
|
-
connector =
|
|
295
|
+
connector = await promptForConnectorChoice(prompter, logger);
|
|
329
296
|
}
|
|
330
297
|
if (!connector) connector = 'skip';
|
|
331
298
|
|
|
@@ -338,8 +305,28 @@ export function createNpmDistributionInstallSeedService(deps = {}) {
|
|
|
338
305
|
}
|
|
339
306
|
|
|
340
307
|
if (connector === 'telegram') {
|
|
341
|
-
const telegramBotToken = trim(options.telegramBotToken || (
|
|
342
|
-
|
|
308
|
+
const telegramBotToken = trim(options.telegramBotToken || (
|
|
309
|
+
!nonInteractive
|
|
310
|
+
? await promptUntilValid(prompter, {
|
|
311
|
+
logger,
|
|
312
|
+
prompt: 'Telegram bot token: ',
|
|
313
|
+
normalize: trim,
|
|
314
|
+
validate: (value) => Boolean(value),
|
|
315
|
+
invalidMessage: 'Telegram bot token is required.',
|
|
316
|
+
})
|
|
317
|
+
: ''
|
|
318
|
+
));
|
|
319
|
+
const telegramChatIds = normalizeList(options.telegramChatIds || (
|
|
320
|
+
!nonInteractive
|
|
321
|
+
? await promptUntilValid(prompter, {
|
|
322
|
+
logger,
|
|
323
|
+
prompt: 'Telegram allowed chat IDs (comma-separated): ',
|
|
324
|
+
normalize: normalizeList,
|
|
325
|
+
validate: (value) => Array.isArray(value) && value.length > 0,
|
|
326
|
+
invalidMessage: 'Provide at least one Telegram chat ID.',
|
|
327
|
+
})
|
|
328
|
+
: ''
|
|
329
|
+
));
|
|
343
330
|
if (!telegramBotToken || telegramChatIds.length === 0) {
|
|
344
331
|
throw new Error('Telegram connector requires bot token and at least one allowed chat ID');
|
|
345
332
|
}
|
|
@@ -356,8 +343,28 @@ export function createNpmDistributionInstallSeedService(deps = {}) {
|
|
|
356
343
|
result.summary.connector = 'telegram';
|
|
357
344
|
result.summary.connectorSeeded = true;
|
|
358
345
|
} else if (connector === 'wecom') {
|
|
359
|
-
const wecomBotId = trim(options.wecomBotId || (
|
|
360
|
-
|
|
346
|
+
const wecomBotId = trim(options.wecomBotId || (
|
|
347
|
+
!nonInteractive
|
|
348
|
+
? await promptUntilValid(prompter, {
|
|
349
|
+
logger,
|
|
350
|
+
prompt: 'WeCom bot ID: ',
|
|
351
|
+
normalize: trim,
|
|
352
|
+
validate: (value) => Boolean(value),
|
|
353
|
+
invalidMessage: 'WeCom bot ID is required.',
|
|
354
|
+
})
|
|
355
|
+
: ''
|
|
356
|
+
));
|
|
357
|
+
const wecomSecret = trim(options.wecomSecret || (
|
|
358
|
+
!nonInteractive
|
|
359
|
+
? await promptUntilValid(prompter, {
|
|
360
|
+
logger,
|
|
361
|
+
prompt: 'WeCom bot secret: ',
|
|
362
|
+
normalize: trim,
|
|
363
|
+
validate: (value) => Boolean(value),
|
|
364
|
+
invalidMessage: 'WeCom bot secret is required.',
|
|
365
|
+
})
|
|
366
|
+
: ''
|
|
367
|
+
));
|
|
361
368
|
const wecomTencentDocsApiKey = trim(options.wecomTencentDocsApiKey || (!nonInteractive ? await prompter.ask('Tencent Docs API key (optional, blank to skip): ') : ''));
|
|
362
369
|
if (!wecomBotId || !wecomSecret) {
|
|
363
370
|
throw new Error('WeCom connector requires bot ID and secret');
|