ccgauge 1.1.0 → 1.1.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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-build-manifest.json +42 -42
- package/.next/standalone/.next/app-path-routes-manifest.json +6 -6
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/blocks/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/export/usage/route.js +1 -1
- package/.next/standalone/.next/server/app/api/export/usage/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/pricing/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/scan/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/usage/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/models/page.js +1 -1
- package/.next/standalone/.next/server/app/models/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/sessions/page.js +1 -1
- package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page.js +2 -2
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/usage/page.js +1 -1
- package/.next/standalone/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +6 -6
- package/.next/standalone/.next/server/chunks/125.js +1 -1
- package/.next/standalone/.next/server/chunks/567.js +3 -3
- package/.next/standalone/.next/server/chunks/716.js +1 -1
- package/.next/standalone/.next/server/chunks/98.js +1 -1
- package/.next/standalone/.next/server/functions-config-manifest.json +1 -1
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/148-edf90b0918345dc2.js +1 -0
- package/.next/standalone/.next/static/chunks/app/layout-af71188a257674b5.js +1 -0
- package/.next/standalone/.next/static/chunks/app/page-1a147d12fca0b184.js +1 -0
- package/.next/standalone/.next/static/chunks/app/usage/{page-7fcc2a2d931307d5.js → page-051223f62647aadc.js} +1 -1
- package/.next/standalone/.next/static/css/fabb40b2545c70dd.css +5 -0
- package/.next/standalone/package.json +8 -2
- package/.next/standalone/public/codex-logo.webp +0 -0
- package/CHANGELOG.md +160 -0
- package/README.md +136 -384
- package/README.zh-CN.md +146 -398
- package/bin/cli.mjs +30 -190
- package/dist/mcp/server.mjs +11 -11
- package/dist/report/index.mjs +36 -3127
- package/package.json +8 -2
- package/.next/standalone/.next/static/chunks/148-f2cba0b76260b8d3.js +0 -1
- package/.next/standalone/.next/static/chunks/app/layout-0adb4fc0305adf29.js +0 -1
- package/.next/standalone/.next/static/chunks/app/page-3cda7f70ecf5017a.js +0 -1
- package/.next/standalone/.next/static/css/bde47638beb0c717.css +0 -3
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/constants.js +0 -10
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/fontkit/index.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/format-available-values.js +0 -9
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/fetch-css-from-google-fonts.js +0 -28
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/fetch-font-file.js +0 -24
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/fetch-resource.js +0 -46
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/find-font-files-in-css.js +0 -34
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/font-data.json +0 -17669
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/get-fallback-font-override-metrics.js +0 -62
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/get-font-axes.js +0 -66
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/get-google-fonts-url.js +0 -55
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/get-proxy-agent.js +0 -23
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/google-fonts-metadata.js +0 -8
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/loader.js +0 -175
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/retry.js +0 -18
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/sort-fonts-variant-values.js +0 -26
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/google/validate-google-font-function-call.js +0 -101
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/local/get-fallback-metrics-from-font-file.js +0 -85
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/local/loader.js +0 -78
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/local/pick-font-file-for-fallback-generation.js +0 -85
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/local/validate-local-font-function-call.js +0 -66
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/dist/next-font-error.js +0 -11
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/google/loader.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/local/loader.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/@next/font/package.json +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/amphtml-validator/index.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/amphtml-validator/package.json +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/amphtml-validator/validator_wasm.js +0 -2672
- package/.next/standalone/node_modules/next/dist/compiled/babel/bundle.js +0 -227
- package/.next/standalone/node_modules/next/dist/compiled/babel/code-frame.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/core-lib-block-hoist-plugin.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/core-lib-config.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/core-lib-normalize-file.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/core-lib-normalize-opts.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/core-lib-plugin-pass.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/core.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/generator.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/package.json +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/parser.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/plugin-syntax-jsx.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/plugin-transform-define.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/plugin-transform-modules-commonjs.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/preset-typescript.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/traverse.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel/types.js +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel-packages/package.json +0 -1
- package/.next/standalone/node_modules/next/dist/compiled/babel-packages/packages-bundle.js +0 -335
- package/.next/standalone/node_modules/next/dist/server/capsize-font-metrics.json +0 -181516
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-darwin-arm64/LICENSE +0 -191
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-darwin-arm64/lib/sharp-darwin-arm64.node +0 -0
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-darwin-arm64/package.json +0 -40
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64/lib/index.js +0 -1
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64/lib/libvips-cpp.8.17.3.dylib +0 -0
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64/package.json +0 -36
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64/versions.json +0 -30
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/channel.js +0 -177
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/colour.js +0 -195
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/composite.js +0 -212
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/constructor.js +0 -499
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/index.js +0 -16
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/input.js +0 -809
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/is.js +0 -143
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/libvips.js +0 -207
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/operation.js +0 -1016
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/output.js +0 -1666
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/resize.js +0 -595
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/sharp.js +0 -121
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/utility.js +0 -291
- package/.next/standalone/node_modules/next/node_modules/sharp/package.json +0 -202
- package/.next/standalone/public/codex-logo.png +0 -0
- /package/.next/standalone/.next/static/{jncTEohJB76Iq9TUm3G21 → EQqRlXV5HyaCYSZWOVllH}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{jncTEohJB76Iq9TUm3G21 → EQqRlXV5HyaCYSZWOVllH}/_ssgManifest.js +0 -0
package/bin/cli.mjs
CHANGED
|
@@ -13,11 +13,6 @@ const __dirname = dirname(__filename);
|
|
|
13
13
|
const packageRoot = resolve(__dirname, '..');
|
|
14
14
|
const pkg = require(join(packageRoot, 'package.json'));
|
|
15
15
|
|
|
16
|
-
// commander is needed before we even parse argv to know which subcommand
|
|
17
|
-
// the user asked for, so we load it eagerly. `get-port` (only used by
|
|
18
|
-
// start / restart) and `open` (only by start / open) are deferred so
|
|
19
|
-
// short-lived commands like `mcp`, `status`, `logs`, `report`, `--version`
|
|
20
|
-
// don't pay their import cost (~20-30 ms cold-start each).
|
|
21
16
|
const { Command } = await import('commander');
|
|
22
17
|
async function loadGetPort() {
|
|
23
18
|
const mod = await import('get-port');
|
|
@@ -38,13 +33,7 @@ const COMMAND_NAMES = new Set([
|
|
|
38
33
|
'start', 'stop', 'restart', 'status', 'open', 'logs', 'mcp',
|
|
39
34
|
'report', 'doctor',
|
|
40
35
|
]);
|
|
41
|
-
|
|
42
|
-
// indicates whether the option consumes the following positional as a
|
|
43
|
-
// value. Used by `normalizeArgv` to decide whether `ccgauge -p 3000`
|
|
44
|
-
// should be treated as the bare-`start` shortcut. Anything outside this
|
|
45
|
-
// set (e.g. `-r` / `--range` from `report`) makes us bail out and let
|
|
46
|
-
// commander surface its own "unknown option" against the root program
|
|
47
|
-
// — which lists subcommands. Keep in sync with `addStartOptions`.
|
|
36
|
+
|
|
48
37
|
const START_OPTIONS = new Map([
|
|
49
38
|
['-p', true], ['--port', true],
|
|
50
39
|
['-H', true], ['--host', true],
|
|
@@ -65,18 +54,6 @@ function buildUrl(host, port) {
|
|
|
65
54
|
return `http://${browserHost(host)}:${port}`;
|
|
66
55
|
}
|
|
67
56
|
|
|
68
|
-
/** Decide whether to emit ANSI colour. Precedence (high → low):
|
|
69
|
-
* 1. `forceOff` (commander's `--no-color`) → off.
|
|
70
|
-
* 2. `NO_COLOR` env var (NO_COLOR.org convention) → off.
|
|
71
|
-
* 3. `FORCE_COLOR` env var → on (covers tee / pipes / CI).
|
|
72
|
-
* 4. stdout TTY check → on.
|
|
73
|
-
* 5. else → off.
|
|
74
|
-
*
|
|
75
|
-
* We take `forceOff` as an explicit boolean rather than reading
|
|
76
|
-
* `opts.color` directly because commander gives `.option('--no-color')`
|
|
77
|
-
* a default of `true` — there's no in-band way to distinguish "user
|
|
78
|
-
* didn't say anything" from "user passed --color". Callers translate
|
|
79
|
-
* their own option to `forceOff: opts.color === false`. */
|
|
80
57
|
function shouldUseColor({ forceOff = false } = {}) {
|
|
81
58
|
if (forceOff) return false;
|
|
82
59
|
if (process.env.NO_COLOR) return false;
|
|
@@ -107,26 +84,16 @@ function addStartOptions(cmd) {
|
|
|
107
84
|
.option('--log <path>', 'background log file', DEFAULT_LOG_FILE);
|
|
108
85
|
}
|
|
109
86
|
|
|
110
|
-
// Browser-open policy:
|
|
111
|
-
// - foreground: open by default; --no-open disables.
|
|
112
|
-
// - background: never auto-open. Use `ccgauge open` after start.
|
|
113
87
|
function shouldOpenBrowser(opts) {
|
|
114
88
|
if (opts.background) return false;
|
|
115
89
|
return opts.open !== false;
|
|
116
90
|
}
|
|
117
91
|
|
|
118
|
-
// For `restart`: when the user did not explicitly pass an option, fall back
|
|
119
|
-
// to whatever the previous background run was using.
|
|
120
92
|
async function inheritFromState(opts, cmd) {
|
|
121
93
|
const prev = await readState();
|
|
122
94
|
if (!prev) return { ...opts };
|
|
123
95
|
const isDefault = (key) => cmd.getOptionValueSource(key) === 'default';
|
|
124
|
-
|
|
125
|
-
// reports its source as `undefined` when unset and `'cli'` when the
|
|
126
|
-
// user typed it — including `--dir ""` which means "clear override".
|
|
127
|
-
// Using truthy-on-opts.dir would conflate "unset" and "explicit empty"
|
|
128
|
-
// and re-inherit the old dataDir, defeating the whole point of
|
|
129
|
-
// letting users switch back to the default Claude path on restart.
|
|
96
|
+
|
|
130
97
|
const isProvided = (key) => cmd.getOptionValueSource(key) === 'cli';
|
|
131
98
|
const merged = { ...opts };
|
|
132
99
|
if (isDefault('port') && prev.port) merged.port = String(prev.port);
|
|
@@ -229,40 +196,27 @@ addReportOptions(program.command('report').description('print a formatted usage
|
|
|
229
196
|
|
|
230
197
|
await program.parseAsync(normalizeArgv(process.argv));
|
|
231
198
|
|
|
232
|
-
/** Implements `ccgauge` (no subcommand) as a shortcut for `ccgauge start`,
|
|
233
|
-
* but only when every flag we see belongs to `start`. If the user typed
|
|
234
|
-
* something that looks like a `report` / `mcp` flag without the
|
|
235
|
-
* subcommand (e.g. `ccgauge -r 7d`), we leave argv alone so commander
|
|
236
|
-
* surfaces "unknown option" against the root program — which lists the
|
|
237
|
-
* available subcommands. Without this discrimination commander would
|
|
238
|
-
* complain about `start: unknown option -r`, which is the wrong hint. */
|
|
239
199
|
function normalizeArgv(argv) {
|
|
240
200
|
const args = argv.slice(2);
|
|
241
|
-
|
|
242
|
-
// we deliberately do NOT early-return on `args.length === 0`. The flag
|
|
243
|
-
// walk below is a no-op for an empty argv, so we fall through to the
|
|
244
|
-
// final `[argv[0], argv[1], 'start']` injection.
|
|
201
|
+
|
|
245
202
|
if (args.includes('--help') || args.includes('-h') || args.includes('-V') || args.includes('--version')) {
|
|
246
203
|
return argv;
|
|
247
204
|
}
|
|
248
|
-
|
|
205
|
+
|
|
249
206
|
if (args.length > 0 && COMMAND_NAMES.has(args[0])) return argv;
|
|
250
|
-
|
|
207
|
+
|
|
251
208
|
for (let i = 0; i < args.length; i += 1) {
|
|
252
209
|
const arg = args[i];
|
|
253
210
|
if (arg === '--') break;
|
|
254
211
|
if (!arg.startsWith('-')) {
|
|
255
|
-
|
|
256
|
-
// out at the root rather than after silently picking `start`.
|
|
212
|
+
|
|
257
213
|
return argv;
|
|
258
214
|
}
|
|
259
|
-
|
|
215
|
+
|
|
260
216
|
const eqIdx = arg.indexOf('=');
|
|
261
217
|
const flag = eqIdx >= 0 ? arg.slice(0, eqIdx) : arg;
|
|
262
218
|
if (!START_OPTIONS.has(flag)) return argv;
|
|
263
|
-
|
|
264
|
-
// the next token is the value — skip it so we don't re-check it as a
|
|
265
|
-
// flag.
|
|
219
|
+
|
|
266
220
|
if (START_OPTIONS.get(flag) === true && eqIdx < 0) i += 1;
|
|
267
221
|
}
|
|
268
222
|
return [argv[0], argv[1], 'start', ...args];
|
|
@@ -281,13 +235,7 @@ async function start(opts) {
|
|
|
281
235
|
async function startForeground(standaloneEntry, opts) {
|
|
282
236
|
const port = await resolvePort(opts);
|
|
283
237
|
const env = makeServerEnv(opts, port);
|
|
284
|
-
|
|
285
|
-
// node script — it doesn't use the IPC channel. `fork` would open one
|
|
286
|
-
// anyway, and since Next never calls `process.disconnect()`, the
|
|
287
|
-
// parent could never exit cleanly without `process.exit()` racing the
|
|
288
|
-
// child's shutdown. `spawn` matches the background path and lets us
|
|
289
|
-
// hand the child our stdio directly. `--quiet` muffles both streams
|
|
290
|
-
// (Next's warnings go to stderr, so muting only stdout misses them).
|
|
238
|
+
|
|
291
239
|
const stdio = opts.quiet
|
|
292
240
|
? ['ignore', 'ignore', 'ignore']
|
|
293
241
|
: 'inherit';
|
|
@@ -309,15 +257,10 @@ async function startForeground(standaloneEntry, opts) {
|
|
|
309
257
|
process.exit(1);
|
|
310
258
|
});
|
|
311
259
|
|
|
312
|
-
// Forward our signals to the child, then wait for it to exit so its
|
|
313
|
-
// teardown is observable rather than racing `process.exit()`. The
|
|
314
|
-
// child propagates the exit code back via the `exit` event below.
|
|
315
260
|
function forward(signal) {
|
|
316
261
|
return () => {
|
|
317
262
|
if (!child.killed) child.kill(signal);
|
|
318
|
-
|
|
319
|
-
// child ignores the signal for some reason, a second Ctrl+C will
|
|
320
|
-
// re-enter and the OS eventually escalates.
|
|
263
|
+
|
|
321
264
|
};
|
|
322
265
|
}
|
|
323
266
|
process.on('SIGINT', forward('SIGINT'));
|
|
@@ -327,9 +270,7 @@ async function startForeground(standaloneEntry, opts) {
|
|
|
327
270
|
});
|
|
328
271
|
|
|
329
272
|
child.on('exit', (code, signal) => {
|
|
330
|
-
|
|
331
|
-
// standard). Plain numeric exit → forward as-is. `code === null`
|
|
332
|
-
// happens when only `signal` is set.
|
|
273
|
+
|
|
333
274
|
if (typeof code === 'number') process.exit(code);
|
|
334
275
|
else process.exit(signal ? 128 : 0);
|
|
335
276
|
});
|
|
@@ -355,14 +296,13 @@ async function startBackground(standaloneEntry, opts) {
|
|
|
355
296
|
env,
|
|
356
297
|
detached: true,
|
|
357
298
|
stdio: ['ignore', out, err],
|
|
358
|
-
|
|
359
|
-
// detached background child. No-op on macOS/Linux.
|
|
299
|
+
|
|
360
300
|
windowsHide: true,
|
|
361
301
|
});
|
|
362
302
|
child.unref();
|
|
363
|
-
|
|
364
|
-
try { closeSync(out); } catch {
|
|
365
|
-
try { closeSync(err); } catch {
|
|
303
|
+
|
|
304
|
+
try { closeSync(out); } catch { }
|
|
305
|
+
try { closeSync(err); } catch { }
|
|
366
306
|
|
|
367
307
|
const url = buildUrl(opts.host, port);
|
|
368
308
|
try {
|
|
@@ -373,10 +313,7 @@ async function startBackground(standaloneEntry, opts) {
|
|
|
373
313
|
const exited = await waitForProcessExit(child.pid, 2_000);
|
|
374
314
|
if (!exited) safeKill(child.pid, 'SIGKILL');
|
|
375
315
|
}
|
|
376
|
-
|
|
377
|
-
// by a sibling that survived getPort's check, etc.) is in the log
|
|
378
|
-
// file that the spawned child writes to. Surface the tail so users
|
|
379
|
-
// don't have to discover `ccgauge logs` themselves.
|
|
316
|
+
|
|
380
317
|
const tail = await tailLog(logFile, 5);
|
|
381
318
|
const tailNote = tail ? `\nLast log lines (${logFile}):\n${tail}\n` : '';
|
|
382
319
|
throw new Error(`failed to start background service: ${startErr.message}${tailNote}`);
|
|
@@ -390,11 +327,7 @@ async function startBackground(standaloneEntry, opts) {
|
|
|
390
327
|
logFile,
|
|
391
328
|
startedAt: new Date().toISOString(),
|
|
392
329
|
bootId: bootId(),
|
|
393
|
-
|
|
394
|
-
// `process.title = 'next-server (vX.Y.Z)'` on boot, so `ps` will
|
|
395
|
-
// show "next-server" in the command column. Pinning that as the
|
|
396
|
-
// identity marker means a recycled PID belonging to some other
|
|
397
|
-
// node process won't pass the identity check in isProcessRunning.
|
|
330
|
+
|
|
398
331
|
cmdMarker: 'next-server',
|
|
399
332
|
packageRoot,
|
|
400
333
|
dataDir: opts.dir ? String(opts.dir) : null,
|
|
@@ -433,15 +366,7 @@ async function status(opts) {
|
|
|
433
366
|
const payload = state
|
|
434
367
|
? { running, ...state }
|
|
435
368
|
: { running: false };
|
|
436
|
-
|
|
437
|
-
// - Plain text → systemd-style 3 when not running, so
|
|
438
|
-
// `if ccgauge status; then …` works in shell.
|
|
439
|
-
// - `--json` → always 0; the consumer is a script that should read
|
|
440
|
-
// `payload.running` from the JSON. Non-zero here would break
|
|
441
|
-
// pipelines like `ccgauge status --json | jq` under `set -e`.
|
|
442
|
-
// Number inlined (no const) because this function sits below the
|
|
443
|
-
// file's top-level `await program.parseAsync(...)` — a const there
|
|
444
|
-
// would be in the TDZ when commander invokes the action handler.
|
|
369
|
+
|
|
445
370
|
if (opts.json) {
|
|
446
371
|
console.log(JSON.stringify(payload, null, 2));
|
|
447
372
|
return;
|
|
@@ -485,16 +410,6 @@ async function logs(opts) {
|
|
|
485
410
|
await followLog(logFile, content.length);
|
|
486
411
|
}
|
|
487
412
|
|
|
488
|
-
/** Shared "build artifact missing" template. Used by start / report /
|
|
489
|
-
* mcp so all three give the same diagnostic shape and so future
|
|
490
|
-
* artifacts only need a one-line registration here. Three install
|
|
491
|
-
* sources mean three different remediation hints:
|
|
492
|
-
*
|
|
493
|
-
* - `npm`: the tarball should already contain this — reinstall is the
|
|
494
|
-
* move. We also print `node -v` / `ccgauge -v` so issue reports
|
|
495
|
-
* don't lose those.
|
|
496
|
-
* - `source`: a `pnpm build` (or the per-artifact target) is missing.
|
|
497
|
-
* - `dev`: there's no built artifact in dev mode — point at `pnpm dev`. */
|
|
498
413
|
function missingArtifactError({ artifactName, expectedPath, buildCmd, devCmd }) {
|
|
499
414
|
const lines = [
|
|
500
415
|
'',
|
|
@@ -567,11 +482,7 @@ async function report(opts) {
|
|
|
567
482
|
console.error(`[ccgauge] error: report failed: ${(err && err.message) || err}`);
|
|
568
483
|
process.exit(1);
|
|
569
484
|
}
|
|
570
|
-
|
|
571
|
-
// For a one-shot report we explicitly exit once stdout is drained.
|
|
572
|
-
// Use the write() return value rather than chaining a `drain` listener
|
|
573
|
-
// after the fact: if drain fires between the write and the listener
|
|
574
|
-
// attach, we'd hang forever waiting for an event that already happened.
|
|
485
|
+
|
|
575
486
|
const flushed = process.stdout.write(payload);
|
|
576
487
|
if (flushed) {
|
|
577
488
|
process.exit(0);
|
|
@@ -590,9 +501,6 @@ async function startMcp(opts = {}) {
|
|
|
590
501
|
});
|
|
591
502
|
}
|
|
592
503
|
|
|
593
|
-
// --check: don't actually run the JSON-RPC server — load the bundle,
|
|
594
|
-
// boot the indexer, print one line per provider, and exit. Lets users
|
|
595
|
-
// verify their install without wiring up an MCP client.
|
|
596
504
|
if (opts.check) {
|
|
597
505
|
const mod = await import(pathToFileURL(bundle).href);
|
|
598
506
|
if (typeof mod.printCheck !== 'function') {
|
|
@@ -603,21 +511,6 @@ async function startMcp(opts = {}) {
|
|
|
603
511
|
process.exit(typeof code === 'number' ? code : 0);
|
|
604
512
|
}
|
|
605
513
|
|
|
606
|
-
// Run the bundled MCP server **in this process** — the bundle exposes a
|
|
607
|
-
// top-level `runStdioServer()` so we just import + invoke it. Spawning a
|
|
608
|
-
// second Node process here is wasted memory/latency (LLM clients already
|
|
609
|
-
// spawn `ccgauge mcp` per conversation), and forwarding signals across
|
|
610
|
-
// processes is brittle (e.g. SIGHUP isn't covered by the old shim).
|
|
611
|
-
//
|
|
612
|
-
// CRITICAL: `runStdioServer()` resolves as soon as
|
|
613
|
-
// `server.connect(transport)` finishes the JSON-RPC handshake setup —
|
|
614
|
-
// the long-running stdio listener is what holds the process alive
|
|
615
|
-
// afterwards. We must NOT call `process.exit(0)` after the await
|
|
616
|
-
// returns, or we'd kill the process before any client `initialize`
|
|
617
|
-
// can land. The dist/mcp/server.mjs direct-entry path gets this right
|
|
618
|
-
// (entry.ts uses `.catch()` only); the CLI in-process wrapper used to
|
|
619
|
-
// exit here too — a regression introduced when we moved off the
|
|
620
|
-
// spawn-a-child design. Don't reintroduce.
|
|
621
514
|
try {
|
|
622
515
|
const mod = await import(pathToFileURL(bundle).href);
|
|
623
516
|
if (typeof mod.runStdioServer !== 'function') {
|
|
@@ -625,25 +518,13 @@ async function startMcp(opts = {}) {
|
|
|
625
518
|
process.exit(1);
|
|
626
519
|
}
|
|
627
520
|
await mod.runStdioServer();
|
|
628
|
-
|
|
629
|
-
// alive. Just let the event loop run; the transport calls
|
|
630
|
-
// process.exit(0) when stdin closes (see `shutdown` in
|
|
631
|
-
// lib/mcp/server.ts), which is the LLM client disconnecting.
|
|
521
|
+
|
|
632
522
|
} catch (err) {
|
|
633
523
|
console.error('[ccgauge-mcp] error: failed to start:', err?.message ?? err);
|
|
634
524
|
process.exit(1);
|
|
635
525
|
}
|
|
636
526
|
}
|
|
637
527
|
|
|
638
|
-
/** `ccgauge doctor` — print everything we'd ask the user to gather when
|
|
639
|
-
* debugging a "why doesn't it work" report, in one place:
|
|
640
|
-
* - version / platform
|
|
641
|
-
* - env vars that influence ccgauge behaviour
|
|
642
|
-
* - on-disk build artifacts (each can be rebuilt independently)
|
|
643
|
-
* - background service state (if any)
|
|
644
|
-
* - delegated MCP `--check` for indexer + per-provider scan stats
|
|
645
|
-
* Output is plain text (no colour) so it's easy to paste into a GitHub
|
|
646
|
-
* issue. */
|
|
647
528
|
async function doctor() {
|
|
648
529
|
const lines = [];
|
|
649
530
|
lines.push(`ccgauge: v${pkg.version ?? '?'}`);
|
|
@@ -697,12 +578,8 @@ async function doctor() {
|
|
|
697
578
|
}
|
|
698
579
|
lines.push('');
|
|
699
580
|
|
|
700
|
-
// Print everything we've accumulated so the indexer probe's output
|
|
701
|
-
// appears below it (printCheck writes to stdout synchronously).
|
|
702
581
|
for (const l of lines) console.log(l);
|
|
703
582
|
|
|
704
|
-
// Delegate to the MCP bundle's printCheck() for indexer / providers
|
|
705
|
-
// detail — same info `ccgauge mcp --check` shows, in the same format.
|
|
706
583
|
const mcpBundle = join(packageRoot, 'dist', 'mcp', 'server.mjs');
|
|
707
584
|
if (!existsSync(mcpBundle)) {
|
|
708
585
|
console.log('indexer: (MCP bundle missing; rebuild to run the indexer probe)');
|
|
@@ -712,9 +589,7 @@ async function doctor() {
|
|
|
712
589
|
const mod = await import(pathToFileURL(mcpBundle).href);
|
|
713
590
|
if (typeof mod.printCheck === 'function') {
|
|
714
591
|
const code = await mod.printCheck();
|
|
715
|
-
|
|
716
|
-
// otherwise. Anything that printCheck couldn't decide we treat as
|
|
717
|
-
// success — doctor is a diagnostic tool, not a gate.
|
|
592
|
+
|
|
718
593
|
process.exit(typeof code === 'number' ? code : 0);
|
|
719
594
|
}
|
|
720
595
|
process.exit(0);
|
|
@@ -729,11 +604,7 @@ async function resolvePort(opts) {
|
|
|
729
604
|
if (!Number.isInteger(preferred) || preferred <= 0 || preferred > 65535) {
|
|
730
605
|
throw new Error(`invalid port: ${opts.port}`);
|
|
731
606
|
}
|
|
732
|
-
|
|
733
|
-
// 65535), then 0 (let the OS pick an ephemeral port). For unusually high
|
|
734
|
-
// preferred values (e.g. 65530) the +N candidates are clamped by the
|
|
735
|
-
// filter, leaving just the preferred + ephemeral fallback — that's still
|
|
736
|
-
// correct, just narrower.
|
|
607
|
+
|
|
737
608
|
const candidates = opts.strictPort
|
|
738
609
|
? preferred
|
|
739
610
|
: [preferred, ...Array.from({ length: 19 }, (_, i) => preferred + i + 1).filter((p) => p <= 65535), 0];
|
|
@@ -758,10 +629,6 @@ function makeServerEnv(opts, port) {
|
|
|
758
629
|
return env;
|
|
759
630
|
}
|
|
760
631
|
|
|
761
|
-
/** Read the last `lines` non-empty lines of a log file. Used as a hint
|
|
762
|
-
* in error messages when the spawned background server fails to come
|
|
763
|
-
* up — the actual root cause (port conflict, parse error, etc.) is in
|
|
764
|
-
* the log file and we'd rather not make the user go fishing for it. */
|
|
765
632
|
async function tailLog(logFile, lines = 5) {
|
|
766
633
|
try {
|
|
767
634
|
const content = await readFile(logFile, 'utf8');
|
|
@@ -795,13 +662,10 @@ async function tryOpen(url) {
|
|
|
795
662
|
const openBrowser = await loadOpenBrowser();
|
|
796
663
|
await openBrowser(url);
|
|
797
664
|
} catch {
|
|
798
|
-
|
|
665
|
+
|
|
799
666
|
}
|
|
800
667
|
}
|
|
801
668
|
|
|
802
|
-
/** Build a small palette of ANSI escapes that collapse to '' when colour
|
|
803
|
-
* is off, so the same template literal works for both modes without an
|
|
804
|
-
* if/else per line. */
|
|
805
669
|
function ansiPalette(useColor) {
|
|
806
670
|
const seq = (...codes) => (useColor ? `\x1b[${codes.join(';')}m` : '');
|
|
807
671
|
return {
|
|
@@ -852,12 +716,9 @@ async function readState() {
|
|
|
852
716
|
const raw = await readFile(STATE_FILE, 'utf8');
|
|
853
717
|
const parsed = JSON.parse(raw);
|
|
854
718
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null;
|
|
855
|
-
|
|
719
|
+
|
|
856
720
|
if (parsed.version !== STATE_VERSION) return null;
|
|
857
|
-
|
|
858
|
-
// `restart` all assume these have the right shape. A hand-edited
|
|
859
|
-
// state.json with the right `version` but garbage in `pid` could
|
|
860
|
-
// otherwise crash `safeKill()` or `isProcessRunning()`.
|
|
721
|
+
|
|
861
722
|
if (typeof parsed.pid !== 'number' || !Number.isInteger(parsed.pid) || parsed.pid <= 0) {
|
|
862
723
|
return null;
|
|
863
724
|
}
|
|
@@ -879,22 +740,10 @@ async function removeState() {
|
|
|
879
740
|
await rm(STATE_FILE, { force: true });
|
|
880
741
|
}
|
|
881
742
|
|
|
882
|
-
/** Best-effort approximation of system boot time, in ms since the epoch.
|
|
883
|
-
* Used as a `bootId` on persisted state so we can reject stale PIDs
|
|
884
|
-
* after a reboot (PID space gets recycled and would otherwise let us
|
|
885
|
-
* SIGTERM an unrelated process that happens to inherit the old PID). */
|
|
886
743
|
function bootId() {
|
|
887
744
|
return Math.floor(Date.now() - os.uptime() * 1000);
|
|
888
745
|
}
|
|
889
746
|
|
|
890
|
-
/** Optional identity check on top of the existing `process.kill(pid, 0)`.
|
|
891
|
-
* Pass `state` to additionally verify:
|
|
892
|
-
* 1. We're still on the same boot (uptime hasn't reset).
|
|
893
|
-
* 2. `ps -p <pid> -o command=` mentions `state.cmdMarker` — i.e. that
|
|
894
|
-
* pid actually points at *our* dashboard child and not some other
|
|
895
|
-
* process that re-inherited the PID.
|
|
896
|
-
* Failures of (2) are tolerated (`ps` missing, sandboxed exec, etc.)
|
|
897
|
-
* so we don't false-negative on minimal containers. */
|
|
898
747
|
function isProcessRunning(pid, state) {
|
|
899
748
|
if (!pid) return false;
|
|
900
749
|
try {
|
|
@@ -903,8 +752,7 @@ function isProcessRunning(pid, state) {
|
|
|
903
752
|
return false;
|
|
904
753
|
}
|
|
905
754
|
if (!state) return true;
|
|
906
|
-
|
|
907
|
-
// same boot (os.uptime() has float jitter), so use a generous window.
|
|
755
|
+
|
|
908
756
|
if (typeof state.bootId === 'number') {
|
|
909
757
|
if (Math.abs(bootId() - state.bootId) > 60_000) return false;
|
|
910
758
|
}
|
|
@@ -917,12 +765,7 @@ function isProcessRunning(pid, state) {
|
|
|
917
765
|
});
|
|
918
766
|
if (!out.includes(state.cmdMarker)) return false;
|
|
919
767
|
} catch {
|
|
920
|
-
|
|
921
|
-
// back to the kill(0) result we already have. We deliberately do
|
|
922
|
-
// NOT fail closed here: a missing `ps` is more common than a PID
|
|
923
|
-
// collision (PID reuse requires a reboot or wraparound), so the
|
|
924
|
-
// false-negative cost of "treat as not-running" outweighs the
|
|
925
|
-
// false-positive of "treat as running".
|
|
768
|
+
|
|
926
769
|
}
|
|
927
770
|
}
|
|
928
771
|
return true;
|
|
@@ -954,12 +797,9 @@ async function followLog(logFile, offset) {
|
|
|
954
797
|
busy = true;
|
|
955
798
|
try {
|
|
956
799
|
const s = await stat(logFile);
|
|
957
|
-
if (s.size < cursor) cursor = 0;
|
|
800
|
+
if (s.size < cursor) cursor = 0;
|
|
958
801
|
if (s.size === cursor) return;
|
|
959
|
-
|
|
960
|
-
// chunk written *after* stat returns would slip into this read,
|
|
961
|
-
// and the next tick (which starts at `s.size`) would replay it.
|
|
962
|
-
// `end` is inclusive, hence the -1.
|
|
802
|
+
|
|
963
803
|
const endAt = s.size - 1;
|
|
964
804
|
await new Promise((res, rej) => {
|
|
965
805
|
const stream = createReadStream(logFile, {
|
|
@@ -975,7 +815,7 @@ async function followLog(logFile, offset) {
|
|
|
975
815
|
stream.on('error', rej);
|
|
976
816
|
});
|
|
977
817
|
} catch {
|
|
978
|
-
|
|
818
|
+
|
|
979
819
|
} finally {
|
|
980
820
|
busy = false;
|
|
981
821
|
}
|