@use-aistack/cli 0.6.2 → 0.6.3
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/index.js +141 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
+
// src/version.ts
|
|
7
|
+
var CLI_VERSION = true ? "0.6.3" : "0.0.0-dev";
|
|
8
|
+
|
|
6
9
|
// src/api.ts
|
|
7
10
|
var BASE_URL = process.env.AISTACK_URL || "https://aistack.to";
|
|
8
11
|
async function request(path3, options = {}) {
|
|
@@ -34,7 +37,12 @@ function failure(what, res) {
|
|
|
34
37
|
async function authStart(machineName) {
|
|
35
38
|
const res = await request("/api/cli/auth/start", {
|
|
36
39
|
method: "POST",
|
|
37
|
-
|
|
40
|
+
// `cliVersion` rides along so `cli_login_completed` can report which
|
|
41
|
+
// version linked the machine (#78). The server carries it on the pending
|
|
42
|
+
// session and reads it at the token exchange.
|
|
43
|
+
body: JSON.stringify(
|
|
44
|
+
machineName ? { machineName, cliVersion: CLI_VERSION } : { cliVersion: CLI_VERSION }
|
|
45
|
+
)
|
|
38
46
|
});
|
|
39
47
|
if (!res.ok) throw failure("Auth start failed", res);
|
|
40
48
|
return res.json();
|
|
@@ -2750,7 +2758,11 @@ function emptyScanStats() {
|
|
|
2750
2758
|
filesRead: 0,
|
|
2751
2759
|
filesSkippedByMtime: 0,
|
|
2752
2760
|
filesSkippedAsDuplicate: 0,
|
|
2753
|
-
filesUnreadable: 0
|
|
2761
|
+
filesUnreadable: 0,
|
|
2762
|
+
filesForeign: 0,
|
|
2763
|
+
foreignOriginators: /* @__PURE__ */ new Map(),
|
|
2764
|
+
unreadableFiles: [],
|
|
2765
|
+
filesZstdUnsupported: 0
|
|
2754
2766
|
};
|
|
2755
2767
|
}
|
|
2756
2768
|
|
|
@@ -3042,11 +3054,12 @@ async function scan2(agg, opts = {}) {
|
|
|
3042
3054
|
} catch {
|
|
3043
3055
|
resolved = file;
|
|
3044
3056
|
}
|
|
3045
|
-
|
|
3057
|
+
const dedupKey = resolved.endsWith(".zst") ? resolved.slice(0, -".zst".length) : resolved;
|
|
3058
|
+
if (visited.has(dedupKey)) {
|
|
3046
3059
|
stats.filesSkippedAsDuplicate++;
|
|
3047
3060
|
continue;
|
|
3048
3061
|
}
|
|
3049
|
-
visited.add(
|
|
3062
|
+
visited.add(dedupKey);
|
|
3050
3063
|
if (opts.sinceMs !== void 0) {
|
|
3051
3064
|
try {
|
|
3052
3065
|
const st = await stat3(file);
|
|
@@ -3060,11 +3073,20 @@ async function scan2(agg, opts = {}) {
|
|
|
3060
3073
|
agg.files++;
|
|
3061
3074
|
stats.filesRead++;
|
|
3062
3075
|
if (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
} catch {
|
|
3076
|
+
const outcome = ingestWithRetry(agg, file, opts);
|
|
3077
|
+
if (!outcome.ok) {
|
|
3066
3078
|
stats.filesUnreadable++;
|
|
3067
3079
|
stats.filesRead--;
|
|
3080
|
+
if (outcome.reason === "zstd-unsupported") stats.filesZstdUnsupported++;
|
|
3081
|
+
stats.unreadableFiles.push({
|
|
3082
|
+
path: path2.relative(root, file),
|
|
3083
|
+
reason: outcome.reason
|
|
3084
|
+
});
|
|
3085
|
+
} else if (!outcome.genuine) {
|
|
3086
|
+
stats.filesForeign++;
|
|
3087
|
+
stats.filesRead--;
|
|
3088
|
+
const seen = stats.foreignOriginators.get(outcome.originator) ?? 0;
|
|
3089
|
+
stats.foreignOriginators.set(outcome.originator, seen + 1);
|
|
3068
3090
|
}
|
|
3069
3091
|
}
|
|
3070
3092
|
}
|
|
@@ -3079,29 +3101,81 @@ async function exists3(p8) {
|
|
|
3079
3101
|
return false;
|
|
3080
3102
|
}
|
|
3081
3103
|
}
|
|
3082
|
-
function
|
|
3104
|
+
function errorClass(e) {
|
|
3105
|
+
const code = e?.code;
|
|
3106
|
+
if (typeof code === "string" && code.length > 0) return code;
|
|
3107
|
+
return e instanceof Error ? e.constructor.name : "unknown";
|
|
3108
|
+
}
|
|
3109
|
+
var readError = (reason) => Object.assign(new Error(reason), { code: reason });
|
|
3110
|
+
function ingestWithRetry(agg, file, opts) {
|
|
3111
|
+
try {
|
|
3112
|
+
return ingestFile2(agg, file, opts);
|
|
3113
|
+
} catch (e) {
|
|
3114
|
+
if (errorClass(e) === "ENOENT" && !file.endsWith(".zst")) {
|
|
3115
|
+
try {
|
|
3116
|
+
return ingestFile2(agg, `${file}.zst`, opts);
|
|
3117
|
+
} catch (e2) {
|
|
3118
|
+
return { ok: false, reason: errorClass(e2) };
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
return { ok: false, reason: errorClass(e) };
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
function ingestFile2(agg, file, opts) {
|
|
3125
|
+
const readFile = opts.readFileImpl ?? readFileSync9;
|
|
3083
3126
|
let text;
|
|
3084
3127
|
if (file.endsWith(".zst")) {
|
|
3085
|
-
if (zstdDecompress === null)
|
|
3086
|
-
|
|
3128
|
+
if (zstdDecompress === null) throw readError("zstd-unsupported");
|
|
3129
|
+
const raw = readFile(file);
|
|
3130
|
+
try {
|
|
3131
|
+
text = zstdDecompress(
|
|
3132
|
+
Buffer.isBuffer(raw) ? raw : Buffer.from(raw)
|
|
3133
|
+
).toString("utf8");
|
|
3134
|
+
} catch {
|
|
3135
|
+
throw readError("zstd-corrupt");
|
|
3087
3136
|
}
|
|
3088
|
-
text = zstdDecompress(readFileSync9(file)).toString("utf8");
|
|
3089
3137
|
} else {
|
|
3090
|
-
text =
|
|
3138
|
+
text = readFile(file).toString("utf8");
|
|
3091
3139
|
}
|
|
3092
|
-
const
|
|
3140
|
+
const records = [];
|
|
3141
|
+
let nonEmptyLines = 0;
|
|
3142
|
+
let parseErrors = 0;
|
|
3093
3143
|
for (const line of text.split("\n")) {
|
|
3094
3144
|
if (!line) continue;
|
|
3095
|
-
|
|
3096
|
-
let rec;
|
|
3145
|
+
nonEmptyLines++;
|
|
3097
3146
|
try {
|
|
3098
|
-
|
|
3147
|
+
records.push(JSON.parse(line));
|
|
3099
3148
|
} catch {
|
|
3100
|
-
|
|
3101
|
-
continue;
|
|
3149
|
+
parseErrors++;
|
|
3102
3150
|
}
|
|
3103
|
-
ingestLine(agg, rec, state, sinceMs);
|
|
3104
3151
|
}
|
|
3152
|
+
const verdict = classifyRollout(records);
|
|
3153
|
+
if (!verdict.genuine) return { ok: true, ...verdict };
|
|
3154
|
+
agg.lines += nonEmptyLines;
|
|
3155
|
+
agg.parseErrors += parseErrors;
|
|
3156
|
+
const state = createFileState();
|
|
3157
|
+
for (const rec of records) ingestLine(agg, rec, state, opts.sinceMs);
|
|
3158
|
+
return { ok: true, genuine: true };
|
|
3159
|
+
}
|
|
3160
|
+
function classifyRollout(records) {
|
|
3161
|
+
let originator = null;
|
|
3162
|
+
let sawTurnContext = false;
|
|
3163
|
+
let genuine = records.length > 0;
|
|
3164
|
+
for (const [i, raw] of records.entries()) {
|
|
3165
|
+
const rec = asObj(raw);
|
|
3166
|
+
const type = rec ? asStr(rec.type) : null;
|
|
3167
|
+
const payload = rec ? asObj(rec.payload) : null;
|
|
3168
|
+
if (i === 0 && type !== "session_meta") genuine = false;
|
|
3169
|
+
if (type === "session_meta" && payload && originator === null) {
|
|
3170
|
+
originator = asStr(payload.originator);
|
|
3171
|
+
} else if (type === "turn_context") {
|
|
3172
|
+
sawTurnContext = true;
|
|
3173
|
+
} else if (type === "event_msg" && payload && asStr(payload.type) === "token_count" && !sawTurnContext) {
|
|
3174
|
+
genuine = false;
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
if (genuine) return { genuine: true };
|
|
3178
|
+
return { genuine: false, originator: originator ?? "(none)" };
|
|
3105
3179
|
}
|
|
3106
3180
|
function readConfiguredMcpServers(agg, configFile) {
|
|
3107
3181
|
const file = configFile ?? path2.join(codexHome2(), "config.toml");
|
|
@@ -3370,11 +3444,12 @@ function mergeKeptPrivate(halves) {
|
|
|
3370
3444
|
}
|
|
3371
3445
|
return out;
|
|
3372
3446
|
}
|
|
3373
|
-
function buildSyncBody(built, syncConfig) {
|
|
3447
|
+
function buildSyncBody(built, syncConfig, autoSync) {
|
|
3374
3448
|
const payloads = built.map((b) => b.payload);
|
|
3375
|
-
|
|
3449
|
+
const base = autoSync ? { payloads, autoSync } : { payloads };
|
|
3450
|
+
if (!syncConfig.reviewKeptPrivate) return base;
|
|
3376
3451
|
return {
|
|
3377
|
-
|
|
3452
|
+
...base,
|
|
3378
3453
|
keptPrivate: mergeKeptPrivate(built.map((b) => b.keptPrivate))
|
|
3379
3454
|
};
|
|
3380
3455
|
}
|
|
@@ -3470,7 +3545,32 @@ function harnessLabel(name) {
|
|
|
3470
3545
|
if (name === "codex") return "Codex";
|
|
3471
3546
|
return name;
|
|
3472
3547
|
}
|
|
3473
|
-
|
|
3548
|
+
var UNREADABLE_FILES_SHOWN = 5;
|
|
3549
|
+
function scanNoteLines(stats, label) {
|
|
3550
|
+
const out = [];
|
|
3551
|
+
const shown = stats.unreadableFiles.slice(0, UNREADABLE_FILES_SHOWN);
|
|
3552
|
+
for (const f of shown) {
|
|
3553
|
+
out.push(` ${f.path} (${f.reason})`);
|
|
3554
|
+
}
|
|
3555
|
+
if (stats.unreadableFiles.length > shown.length) {
|
|
3556
|
+
out.push(
|
|
3557
|
+
` ...${stats.unreadableFiles.length - shown.length} more`
|
|
3558
|
+
);
|
|
3559
|
+
}
|
|
3560
|
+
if (stats.filesZstdUnsupported > 0) {
|
|
3561
|
+
out.push(
|
|
3562
|
+
` ${stats.filesZstdUnsupported} compressed rollout${stats.filesZstdUnsupported === 1 ? "" : "s"} need Node 22.15 or newer`
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
3565
|
+
if (stats.filesForeign > 0) {
|
|
3566
|
+
const origins = [...stats.foreignOriginators].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([name, n]) => n > 1 ? `${name} \xD7${n}` : name).join(", ");
|
|
3567
|
+
out.push(
|
|
3568
|
+
`skipped ${stats.filesForeign} file${stats.filesForeign === 1 ? "" : "s"} not written by ${label} \u2014 left out (originators: ${origins})`
|
|
3569
|
+
);
|
|
3570
|
+
}
|
|
3571
|
+
return out;
|
|
3572
|
+
}
|
|
3573
|
+
function payloadBlock(payload, showHeader, stats) {
|
|
3474
3574
|
const out = [];
|
|
3475
3575
|
if (showHeader) {
|
|
3476
3576
|
out.push(
|
|
@@ -3493,6 +3593,9 @@ function payloadBlock(payload, showHeader) {
|
|
|
3493
3593
|
`coverage ${cov.filesUnreadable} files unreadable \xB7 ${cov.linesFailed} lines failed \u2014 this reading is a floor`
|
|
3494
3594
|
);
|
|
3495
3595
|
}
|
|
3596
|
+
if (stats) {
|
|
3597
|
+
out.push(...scanNoteLines(stats, harnessLabel(payload.harness.name)));
|
|
3598
|
+
}
|
|
3496
3599
|
out.push("");
|
|
3497
3600
|
out.push("models");
|
|
3498
3601
|
for (const m of payload.models) {
|
|
@@ -3524,7 +3627,8 @@ function buildGateSummary(ctx) {
|
|
|
3524
3627
|
);
|
|
3525
3628
|
}
|
|
3526
3629
|
for (const payload of payloads) {
|
|
3527
|
-
|
|
3630
|
+
const stats = ctx.scanStats?.[payload.harness.name];
|
|
3631
|
+
out.push(...payloadBlock(payload, payloads.length > 1, stats));
|
|
3528
3632
|
out.push("");
|
|
3529
3633
|
}
|
|
3530
3634
|
if (out[out.length - 1] === "") out.pop();
|
|
@@ -3550,6 +3654,12 @@ function buildGateSummary(ctx) {
|
|
|
3550
3654
|
out.push(" they stay on this machine");
|
|
3551
3655
|
}
|
|
3552
3656
|
}
|
|
3657
|
+
if (body.autoSync !== void 0) {
|
|
3658
|
+
out.push("");
|
|
3659
|
+
out.push(
|
|
3660
|
+
`auto-sync ${body.autoSync.enabled ? `on, about every ${body.autoSync.frequencyHours}h` : "off"}`
|
|
3661
|
+
);
|
|
3662
|
+
}
|
|
3553
3663
|
if (source === "bundled") {
|
|
3554
3664
|
out.push("");
|
|
3555
3665
|
out.push(
|
|
@@ -3577,9 +3687,11 @@ async function stageSync(deps) {
|
|
|
3577
3687
|
...token ? { token } : {}
|
|
3578
3688
|
});
|
|
3579
3689
|
const built = [];
|
|
3690
|
+
const scanStats = {};
|
|
3580
3691
|
const sinceMs = windowStartMs(now, windowDays);
|
|
3581
3692
|
for (const adapter of await adapters()) {
|
|
3582
3693
|
const { aggregate, stats } = await adapter.scan({ sinceMs });
|
|
3694
|
+
scanStats[adapter.name] = stats;
|
|
3583
3695
|
built.push(
|
|
3584
3696
|
buildPayload({
|
|
3585
3697
|
aggregate,
|
|
@@ -3593,7 +3705,8 @@ async function stageSync(deps) {
|
|
|
3593
3705
|
})
|
|
3594
3706
|
);
|
|
3595
3707
|
}
|
|
3596
|
-
const
|
|
3708
|
+
const settings = (deps.getSettingsImpl ?? getSettings)();
|
|
3709
|
+
const body = buildSyncBody(built, config, settings.autoSync);
|
|
3597
3710
|
const bodyJson = JSON.stringify(body);
|
|
3598
3711
|
const keptPrivate = mergeKeptPrivate(built.map((b) => b.keptPrivate));
|
|
3599
3712
|
const ctx = {
|
|
@@ -3601,7 +3714,8 @@ async function stageSync(deps) {
|
|
|
3601
3714
|
keptPrivate,
|
|
3602
3715
|
config,
|
|
3603
3716
|
source,
|
|
3604
|
-
baseUrl: deps.baseUrl
|
|
3717
|
+
baseUrl: deps.baseUrl,
|
|
3718
|
+
scanStats
|
|
3605
3719
|
};
|
|
3606
3720
|
let blockedReason = null;
|
|
3607
3721
|
if (built.length === 0) {
|
|
@@ -4072,7 +4186,7 @@ function runStdioSyncServer(deps) {
|
|
|
4072
4186
|
|
|
4073
4187
|
// src/index.ts
|
|
4074
4188
|
var program = new Command();
|
|
4075
|
-
program.name("aistack").description("Measure and share your AI stack from your terminal").version(
|
|
4189
|
+
program.name("aistack").description("Measure and share your AI stack from your terminal").version(CLI_VERSION);
|
|
4076
4190
|
program.command("login").description("Authenticate with AI Stack").action(loginCommand);
|
|
4077
4191
|
program.command("collect").description("Scan and upload AI config files from your project").option("--no-global", "Exclude global config files (~/.claude, etc.)").action((options) => collectCommand({ global: options.global ?? true }));
|
|
4078
4192
|
program.command("create").description("Download and write your stack's AI config files").action(createCommand);
|