@token-dashboard/typeless-usage-uploader 0.1.1 → 0.1.4
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 +5 -30
- package/dist/bin/typeless-usage-uploader.js +4 -4
- package/package.json +3 -10
- package/dist/cli.js +0 -430
- package/dist/collector.js +0 -495
- package/dist/constants.js +0 -43
- package/dist/launchd.js +0 -154
- package/dist/runtime-config.js +0 -158
- package/dist/state-db.js +0 -280
- package/dist/utils.js +0 -52
package/README.md
CHANGED
|
@@ -1,35 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# typeless-usage-uploader removed
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This package is deprecated and no longer provides a standalone uploader.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- `~/Library/Application Support/Typeless/typeless.db`
|
|
8
|
-
- `~/Library/Application Support/Typeless/app-storage.json`
|
|
9
|
-
|
|
10
|
-
采集端只上传明细记录的时间、字符长度和 Typeless 账号身份,不上传 `refined_text`、音频、窗口标题、URL、debug 信息、referral code 或公钥。
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
typeless-usage-uploader init --backend-url http://localhost:8086
|
|
14
|
-
typeless-usage-uploader status
|
|
15
|
-
typeless-usage-uploader logs
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
`init` 默认会询问是否登录后自动启动;也可以用参数显式控制:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
typeless-usage-uploader init --backend-url http://localhost:8086 --auto-start
|
|
22
|
-
typeless-usage-uploader init --backend-url http://localhost:8086 --no-auto-start
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
如果之前开启了登录自动启动,重新执行 `init --no-auto-start` 会保存配置、移除
|
|
26
|
-
`~/Library/LaunchAgents` 下的自启动 plist,并重启当前后台服务。
|
|
27
|
-
|
|
28
|
-
开发环境:
|
|
5
|
+
Use the unified uploader instead:
|
|
29
6
|
|
|
30
7
|
```bash
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
npm run build
|
|
34
|
-
npm run init
|
|
8
|
+
npx -y @token-dashboard/usage-uploader@latest init
|
|
9
|
+
token-usage-uploader typeless status
|
|
35
10
|
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
console.error('typeless-usage-uploader has been removed.');
|
|
3
|
+
console.error('Please use: npx -y @token-dashboard/usage-uploader@latest init');
|
|
4
|
+
console.error('Then run: token-usage-uploader typeless status');
|
|
5
|
+
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@token-dashboard/typeless-usage-uploader",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Typeless
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Deprecated Typeless usage uploader CLI tombstone",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"typeless-usage-uploader": "dist/bin/typeless-usage-uploader.js"
|
|
@@ -13,14 +13,7 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "node scripts/build.mjs",
|
|
15
15
|
"prepublishOnly": "npm run build",
|
|
16
|
-
"test": "
|
|
17
|
-
"init": "node ./bin/typeless-usage-uploader.js --config-file $HOME/.typeless-usage-uploader-dev/config.json init --backend-url http://localhost:8086",
|
|
18
|
-
"start": "node ./bin/typeless-usage-uploader.js --config-file $HOME/.typeless-usage-uploader-dev/config.json start",
|
|
19
|
-
"stop": "node ./bin/typeless-usage-uploader.js --config-file $HOME/.typeless-usage-uploader-dev/config.json stop",
|
|
20
|
-
"restart": "node ./bin/typeless-usage-uploader.js --config-file $HOME/.typeless-usage-uploader-dev/config.json restart",
|
|
21
|
-
"status": "node ./bin/typeless-usage-uploader.js --config-file $HOME/.typeless-usage-uploader-dev/config.json status",
|
|
22
|
-
"logs": "node ./bin/typeless-usage-uploader.js --config-file $HOME/.typeless-usage-uploader-dev/config.json logs",
|
|
23
|
-
"clear": "node ./bin/typeless-usage-uploader.js --config-file $HOME/.typeless-usage-uploader-dev/config.json clear"
|
|
16
|
+
"test": "npm run build"
|
|
24
17
|
},
|
|
25
18
|
"engines": {
|
|
26
19
|
"node": ">=22.13.0"
|
package/dist/cli.js
DELETED
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import readline from 'node:readline/promises';
|
|
4
|
-
import { stdin as input, stdout as output } from 'node:process';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { TypelessUsageUploader } from './collector.js';
|
|
7
|
-
import {
|
|
8
|
-
CLI_NAME,
|
|
9
|
-
DEFAULT_BACKEND_URL,
|
|
10
|
-
DEFAULT_CONFIG_FILE,
|
|
11
|
-
PRODUCT_NAME,
|
|
12
|
-
} from './constants.js';
|
|
13
|
-
import { LaunchdServiceManager } from './launchd.js';
|
|
14
|
-
import { formatStatusOutput, mergeRuntimeConfig, saveRuntimeConfig } from './runtime-config.js';
|
|
15
|
-
import { StateDb } from './state-db.js';
|
|
16
|
-
|
|
17
|
-
const HELP_TEXT = `${PRODUCT_NAME}
|
|
18
|
-
|
|
19
|
-
Usage:
|
|
20
|
-
${CLI_NAME} init [--backend-url <url>] [--interval 300] [--yes] [--auto-start|--no-auto-start]
|
|
21
|
-
${CLI_NAME} clear [--yes]
|
|
22
|
-
${CLI_NAME} start
|
|
23
|
-
${CLI_NAME} stop
|
|
24
|
-
${CLI_NAME} restart
|
|
25
|
-
${CLI_NAME} status
|
|
26
|
-
${CLI_NAME} logs [--lines 100]
|
|
27
|
-
${CLI_NAME} uninstall
|
|
28
|
-
|
|
29
|
-
Common options:
|
|
30
|
-
--config-file <path> Runtime config path. Default: ${DEFAULT_CONFIG_FILE}
|
|
31
|
-
--backend-url <url> Dashboard backend URL
|
|
32
|
-
--interval <seconds> Scan interval in seconds
|
|
33
|
-
--typeless-db <path> Typeless typeless.db path
|
|
34
|
-
--typeless-storage <path> Typeless app-storage.json path
|
|
35
|
-
--state-db <path> Local SQLite state DB path
|
|
36
|
-
--auto-start Start automatically when you log in
|
|
37
|
-
--no-auto-start Do not start automatically when you log in
|
|
38
|
-
--yes Skip interactive prompts
|
|
39
|
-
--lines <n> Number of log lines for service logs
|
|
40
|
-
-h, --help Show help
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
class CliOperationalError extends Error {
|
|
44
|
-
constructor(message) {
|
|
45
|
-
super(message);
|
|
46
|
-
this.name = 'CliOperationalError';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const cliDeps = {
|
|
51
|
-
promptConfirm,
|
|
52
|
-
createUploader(runtime) {
|
|
53
|
-
return new TypelessUsageUploader({
|
|
54
|
-
stateDbPath: runtime.stateDbPath,
|
|
55
|
-
backendUrl: runtime.backendUrl,
|
|
56
|
-
typelessDbPath: runtime.typelessDbPath,
|
|
57
|
-
typelessStoragePath: runtime.typelessStoragePath,
|
|
58
|
-
intervalSeconds: runtime.intervalSeconds,
|
|
59
|
-
persistentCollectorIdPath: runtime.persistentCollectorIdPath,
|
|
60
|
-
});
|
|
61
|
-
},
|
|
62
|
-
createServiceManager(runtime) {
|
|
63
|
-
return new LaunchdServiceManager(runtime);
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export function parseCliArgs(argv) {
|
|
68
|
-
const options = {
|
|
69
|
-
configFile: DEFAULT_CONFIG_FILE,
|
|
70
|
-
interval: undefined,
|
|
71
|
-
yes: false,
|
|
72
|
-
lines: 100,
|
|
73
|
-
};
|
|
74
|
-
const positionals = [];
|
|
75
|
-
|
|
76
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
77
|
-
const token = argv[index];
|
|
78
|
-
if (!token.startsWith('-')) {
|
|
79
|
-
positionals.push(token);
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (token === '-h' || token === '--help') {
|
|
83
|
-
options.help = true;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (token === '--yes') {
|
|
87
|
-
options.yes = true;
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (token === '--auto-start') {
|
|
91
|
-
options.autoStartOnLogin = true;
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (token === '--no-auto-start') {
|
|
95
|
-
options.autoStartOnLogin = false;
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const [key, inlineValue] = token.split('=', 2);
|
|
100
|
-
const takesValue = new Set([
|
|
101
|
-
'--config-file',
|
|
102
|
-
'--backend-url',
|
|
103
|
-
'--interval',
|
|
104
|
-
'--typeless-db',
|
|
105
|
-
'--typeless-storage',
|
|
106
|
-
'--state-db',
|
|
107
|
-
'--lines',
|
|
108
|
-
]);
|
|
109
|
-
if (!takesValue.has(key)) throw new Error(`Unknown argument: ${token}`);
|
|
110
|
-
const value = inlineValue ?? argv[++index];
|
|
111
|
-
if (value == null || value.startsWith('-')) throw new Error(`Missing value for ${key}`);
|
|
112
|
-
assignOption(options, key, value);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
command: positionals[0] ?? null,
|
|
117
|
-
subcommand: positionals[1] ?? null,
|
|
118
|
-
extraPositionals: positionals.slice(2),
|
|
119
|
-
options,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function assignOption(options, key, value) {
|
|
124
|
-
switch (key) {
|
|
125
|
-
case '--config-file':
|
|
126
|
-
options.configFile = value;
|
|
127
|
-
break;
|
|
128
|
-
case '--backend-url':
|
|
129
|
-
options.backendUrl = value;
|
|
130
|
-
break;
|
|
131
|
-
case '--interval':
|
|
132
|
-
options.interval = parsePositiveInt(value, '--interval');
|
|
133
|
-
break;
|
|
134
|
-
case '--typeless-db':
|
|
135
|
-
options.typelessDbPath = value;
|
|
136
|
-
break;
|
|
137
|
-
case '--typeless-storage':
|
|
138
|
-
options.typelessStoragePath = value;
|
|
139
|
-
break;
|
|
140
|
-
case '--state-db':
|
|
141
|
-
options.stateDbPath = value;
|
|
142
|
-
break;
|
|
143
|
-
case '--lines':
|
|
144
|
-
options.lines = parsePositiveInt(value, '--lines');
|
|
145
|
-
break;
|
|
146
|
-
default:
|
|
147
|
-
throw new Error(`Unknown argument: ${key}`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function parsePositiveInt(value, label) {
|
|
152
|
-
const parsed = Number.parseInt(String(value), 10);
|
|
153
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
154
|
-
throw new Error(`${label} must be a positive integer`);
|
|
155
|
-
}
|
|
156
|
-
return parsed;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function runtimeOverrides(options) {
|
|
160
|
-
const overrides = {};
|
|
161
|
-
if (options.backendUrl !== undefined) overrides.backendUrl = options.backendUrl;
|
|
162
|
-
if (options.interval !== undefined) overrides.intervalSeconds = options.interval;
|
|
163
|
-
if (options.typelessDbPath !== undefined) overrides.typelessDbPath = options.typelessDbPath;
|
|
164
|
-
if (options.typelessStoragePath !== undefined) {
|
|
165
|
-
overrides.typelessStoragePath = options.typelessStoragePath;
|
|
166
|
-
}
|
|
167
|
-
if (options.stateDbPath !== undefined) overrides.stateDbPath = options.stateDbPath;
|
|
168
|
-
if (options.autoStartOnLogin !== undefined) {
|
|
169
|
-
overrides.autoStartOnLogin = options.autoStartOnLogin;
|
|
170
|
-
}
|
|
171
|
-
return overrides;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function printHelp() {
|
|
175
|
-
console.log(HELP_TEXT);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async function promptValue(label, defaultValue) {
|
|
179
|
-
const rl = readline.createInterface({ input, output });
|
|
180
|
-
try {
|
|
181
|
-
const suffix = defaultValue ? ` [${defaultValue}]` : '';
|
|
182
|
-
const answer = (await rl.question(`${label}${suffix}: `)).trim();
|
|
183
|
-
return answer || defaultValue || null;
|
|
184
|
-
} finally {
|
|
185
|
-
rl.close();
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async function promptConfirm(label, defaultYes = false) {
|
|
190
|
-
const defaultValue = defaultYes ? 'y' : 'n';
|
|
191
|
-
const answer = await promptValue(
|
|
192
|
-
`${label} (${defaultYes ? 'Y/n' : 'y/N'})`,
|
|
193
|
-
defaultValue,
|
|
194
|
-
);
|
|
195
|
-
return /^(y|yes)$/i.test(String(answer ?? '').trim());
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async function resolveAutoStartOnLogin(runtime, options) {
|
|
199
|
-
if (options.autoStartOnLogin !== undefined) {
|
|
200
|
-
return options.autoStartOnLogin;
|
|
201
|
-
}
|
|
202
|
-
if (options.yes) {
|
|
203
|
-
return runtime.autoStartOnLogin;
|
|
204
|
-
}
|
|
205
|
-
return cliDeps.promptConfirm(
|
|
206
|
-
'Start automatically when you log in on this Mac?',
|
|
207
|
-
runtime.autoStartOnLogin,
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function currentEntryFile() {
|
|
212
|
-
const distBin = fileURLToPath(new URL('./bin/typeless-usage-uploader.js', import.meta.url));
|
|
213
|
-
if (fs.existsSync(distBin)) return distBin;
|
|
214
|
-
return fileURLToPath(new URL('../bin/typeless-usage-uploader.js', import.meta.url));
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
async function runInit(options) {
|
|
218
|
-
const overrides = runtimeOverrides(options);
|
|
219
|
-
if (!overrides.backendUrl) overrides.backendUrl = DEFAULT_BACKEND_URL;
|
|
220
|
-
let runtime = mergeRuntimeConfig(options.configFile, overrides);
|
|
221
|
-
runtime.entryFile = currentEntryFile();
|
|
222
|
-
runtime.autoStartOnLogin = await resolveAutoStartOnLogin(runtime, options);
|
|
223
|
-
runtime = saveRuntimeConfig(runtime);
|
|
224
|
-
|
|
225
|
-
const uploader = cliDeps.createUploader(runtime);
|
|
226
|
-
try {
|
|
227
|
-
const scanResult = await uploader.scanTypeless();
|
|
228
|
-
scanResult.batchesQueued += uploader.stateDb.sealStaleBatches(true);
|
|
229
|
-
await uploader.flushPendingBatches({ failFast: false });
|
|
230
|
-
const manager = cliDeps.createServiceManager(runtime);
|
|
231
|
-
try {
|
|
232
|
-
manager.start();
|
|
233
|
-
console.log(`${PRODUCT_NAME} initialized and started.`);
|
|
234
|
-
console.log(
|
|
235
|
-
runtime.autoStartOnLogin
|
|
236
|
-
? 'Login auto-start is enabled.'
|
|
237
|
-
: 'Login auto-start is disabled for future logins.',
|
|
238
|
-
);
|
|
239
|
-
} catch (error) {
|
|
240
|
-
console.log(`${PRODUCT_NAME} initialized. Service start skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
241
|
-
}
|
|
242
|
-
console.log(`Backend: ${runtime.backendUrl}`);
|
|
243
|
-
console.log(`Install root: ${runtime.installRoot}`);
|
|
244
|
-
console.log(`Config file: ${runtime.configFile}`);
|
|
245
|
-
console.log(`Collector ID: ${uploader.identity.collectorId}`);
|
|
246
|
-
console.log(
|
|
247
|
-
`Initial scan: account=${scanResult.accountFound ? 'yes' : 'no'} db=${scanResult.dbFound ? 'yes' : 'no'} changed=${scanResult.changedEvents} deleted=${scanResult.tombstoneEvents}`,
|
|
248
|
-
);
|
|
249
|
-
} finally {
|
|
250
|
-
uploader.close();
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function loadQueueStats(stateDbPath) {
|
|
255
|
-
if (!fs.existsSync(stateDbPath)) {
|
|
256
|
-
return {
|
|
257
|
-
collectorId: null,
|
|
258
|
-
bufferingBatchCount: 0,
|
|
259
|
-
pendingBatchCount: 0,
|
|
260
|
-
retryingBatchCount: 0,
|
|
261
|
-
queuedEvents: 0,
|
|
262
|
-
oldestPendingAgeSeconds: null,
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
const stateDb = new StateDb(stateDbPath);
|
|
266
|
-
try {
|
|
267
|
-
const identity = stateDb.getIdentity();
|
|
268
|
-
return {
|
|
269
|
-
collectorId: identity.collectorId ?? null,
|
|
270
|
-
...stateDb.getQueueStats(),
|
|
271
|
-
};
|
|
272
|
-
} finally {
|
|
273
|
-
stateDb.close();
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function printStatus(runtime, status) {
|
|
278
|
-
const payload = {
|
|
279
|
-
...formatStatusOutput(runtime, status),
|
|
280
|
-
...loadQueueStats(runtime.stateDbPath),
|
|
281
|
-
};
|
|
282
|
-
const lines = [
|
|
283
|
-
['Config exists', payload.configExists ? 'yes' : 'no'],
|
|
284
|
-
['Loaded', payload.loaded ? 'yes' : 'no'],
|
|
285
|
-
['Running', payload.running ? 'yes' : 'no'],
|
|
286
|
-
['Auto start on login', payload.autoStartOnLogin ? 'yes' : 'no'],
|
|
287
|
-
['PID', payload.pid ?? '-'],
|
|
288
|
-
['State', payload.state ?? '-'],
|
|
289
|
-
['Last exit code', payload.lastExitCode ?? '-'],
|
|
290
|
-
['Collector ID', payload.collectorId ?? '-'],
|
|
291
|
-
['Upload URL', payload.backendUrl ? `${payload.backendUrl}/typeless-usage/upload` : '-'],
|
|
292
|
-
['Register URL', payload.backendUrl ? `${payload.backendUrl}/typeless-usage/collectors/register` : '-'],
|
|
293
|
-
['Interval', `${payload.intervalSeconds}s`],
|
|
294
|
-
['Typeless DB', payload.typelessDbPath],
|
|
295
|
-
['Typeless storage', payload.typelessStoragePath],
|
|
296
|
-
['Buffering batches', payload.bufferingBatchCount],
|
|
297
|
-
['Pending batches', payload.pendingBatchCount],
|
|
298
|
-
['Retrying batches', payload.retryingBatchCount],
|
|
299
|
-
['Queued events', payload.queuedEvents],
|
|
300
|
-
[
|
|
301
|
-
'Oldest pending age',
|
|
302
|
-
payload.oldestPendingAgeSeconds == null
|
|
303
|
-
? '-'
|
|
304
|
-
: `${payload.oldestPendingAgeSeconds}s`,
|
|
305
|
-
],
|
|
306
|
-
['Config file', payload.configFile],
|
|
307
|
-
['State DB', payload.stateDbPath],
|
|
308
|
-
['Stdout log', payload.stdoutLogPath],
|
|
309
|
-
['Stderr log', payload.stderrLogPath],
|
|
310
|
-
['Service plist', payload.plistPath],
|
|
311
|
-
['LaunchAgent plist', payload.launchAgentPath],
|
|
312
|
-
['Label', payload.label],
|
|
313
|
-
];
|
|
314
|
-
for (const [label, value] of lines) console.log(`${label}: ${value}`);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function ensureInitialized(runtime) {
|
|
318
|
-
if (!fs.existsSync(runtime.configFile) || !runtime.entryFile) {
|
|
319
|
-
throw new CliOperationalError(
|
|
320
|
-
`Uploader is not initialized yet. Run \`${CLI_NAME} init\` first.`,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
async function runClear(options) {
|
|
326
|
-
const runtime = mergeRuntimeConfig(options.configFile, runtimeOverrides(options));
|
|
327
|
-
const uploader = cliDeps.createUploader(runtime);
|
|
328
|
-
try {
|
|
329
|
-
uploader.resetBackfillState();
|
|
330
|
-
} finally {
|
|
331
|
-
uploader.close();
|
|
332
|
-
}
|
|
333
|
-
console.log('Local Typeless scan state has been cleared.');
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function runLifecycleCommand(action, options) {
|
|
337
|
-
const runtime = mergeRuntimeConfig(options.configFile, runtimeOverrides(options));
|
|
338
|
-
const manager = cliDeps.createServiceManager(runtime);
|
|
339
|
-
switch (action) {
|
|
340
|
-
case 'start':
|
|
341
|
-
ensureInitialized(runtime);
|
|
342
|
-
manager.start();
|
|
343
|
-
console.log('Service started.');
|
|
344
|
-
break;
|
|
345
|
-
case 'stop':
|
|
346
|
-
manager.stop();
|
|
347
|
-
console.log('Service stopped.');
|
|
348
|
-
break;
|
|
349
|
-
case 'restart':
|
|
350
|
-
ensureInitialized(runtime);
|
|
351
|
-
manager.restart();
|
|
352
|
-
console.log('Service restarted.');
|
|
353
|
-
break;
|
|
354
|
-
case 'uninstall':
|
|
355
|
-
manager.uninstall();
|
|
356
|
-
console.log('Service uninstalled.');
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
async function runInternalWorker(options) {
|
|
362
|
-
const runtime = mergeRuntimeConfig(options.configFile, runtimeOverrides(options));
|
|
363
|
-
const uploader = cliDeps.createUploader(runtime);
|
|
364
|
-
try {
|
|
365
|
-
console.log(
|
|
366
|
-
`[run] backend=${runtime.backendUrl ?? '-'} interval=${runtime.intervalSeconds}s typeless_db=${runtime.typelessDbPath}`,
|
|
367
|
-
);
|
|
368
|
-
await uploader.watch();
|
|
369
|
-
} finally {
|
|
370
|
-
uploader.close();
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function tailFile(filePath, lines) {
|
|
375
|
-
if (!fs.existsSync(filePath)) return [];
|
|
376
|
-
return fs.readFileSync(filePath, 'utf8')
|
|
377
|
-
.split(/\r?\n/)
|
|
378
|
-
.filter((line, index, array) => !(index === array.length - 1 && line === ''))
|
|
379
|
-
.slice(-lines);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
export async function main(argv = process.argv.slice(2)) {
|
|
383
|
-
const parsed = parseCliArgs(argv);
|
|
384
|
-
const { command, subcommand, extraPositionals, options } = parsed;
|
|
385
|
-
if (options.help || !command) {
|
|
386
|
-
printHelp();
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
if (subcommand || extraPositionals.length) {
|
|
390
|
-
throw new Error('Unexpected extra arguments.');
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
switch (command) {
|
|
394
|
-
case 'init':
|
|
395
|
-
await runInit(options);
|
|
396
|
-
return;
|
|
397
|
-
case 'clear':
|
|
398
|
-
await runClear(options);
|
|
399
|
-
return;
|
|
400
|
-
case 'start':
|
|
401
|
-
case 'stop':
|
|
402
|
-
case 'restart':
|
|
403
|
-
case 'uninstall':
|
|
404
|
-
runLifecycleCommand(command, options);
|
|
405
|
-
return;
|
|
406
|
-
case 'status': {
|
|
407
|
-
const runtime = mergeRuntimeConfig(options.configFile, runtimeOverrides(options));
|
|
408
|
-
const manager = cliDeps.createServiceManager(runtime);
|
|
409
|
-
let status;
|
|
410
|
-
try {
|
|
411
|
-
status = manager.status();
|
|
412
|
-
} catch {
|
|
413
|
-
status = { loaded: false, running: false, pid: null, state: null, lastExitCode: null };
|
|
414
|
-
}
|
|
415
|
-
printStatus(runtime, status);
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
case 'logs': {
|
|
419
|
-
const runtime = mergeRuntimeConfig(options.configFile, runtimeOverrides(options));
|
|
420
|
-
for (const line of tailFile(runtime.stdoutLogPath, options.lines)) console.log(line);
|
|
421
|
-
for (const line of tailFile(runtime.stderrLogPath, options.lines)) console.error(line);
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
case 'run':
|
|
425
|
-
await runInternalWorker(options);
|
|
426
|
-
return;
|
|
427
|
-
default:
|
|
428
|
-
throw new Error(`Unknown command: ${command}`);
|
|
429
|
-
}
|
|
430
|
-
}
|