@ait-co/console-cli 0.1.39 → 0.1.40
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.en.md +1 -31
- package/README.md +1 -31
- package/dist/cli.mjs +15 -559
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -9,7 +9,6 @@ import { checkbox, confirm, editor, input, password, select } from "@inquirer/pr
|
|
|
9
9
|
import { imageSize } from "image-size";
|
|
10
10
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
11
11
|
import { constants, createReadStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
|
-
import { createInterface } from "node:readline/promises";
|
|
13
12
|
import { promisify } from "node:util";
|
|
14
13
|
import { createHash } from "node:crypto";
|
|
15
14
|
//#region src/api/http.ts
|
|
@@ -3611,7 +3610,7 @@ function serviceStatusFor(entry) {
|
|
|
3611
3610
|
}
|
|
3612
3611
|
const POLL_MIN_INTERVAL_SEC = 30;
|
|
3613
3612
|
const POLL_MAX_INTERVAL_SEC = 3600;
|
|
3614
|
-
const statusCommand$
|
|
3613
|
+
const statusCommand$1 = defineCommand({
|
|
3615
3614
|
meta: {
|
|
3616
3615
|
name: "status",
|
|
3617
3616
|
description: "Show the derived review state of a mini-app (under-review / rejected / approved)."
|
|
@@ -5721,7 +5720,7 @@ const appCommand = defineCommand({
|
|
|
5721
5720
|
}),
|
|
5722
5721
|
ls: lsCommand$4,
|
|
5723
5722
|
show: showCommand$2,
|
|
5724
|
-
status: statusCommand$
|
|
5723
|
+
status: statusCommand$1,
|
|
5725
5724
|
ratings: ratingsCommand,
|
|
5726
5725
|
reports: reportsCommand,
|
|
5727
5726
|
bundles: bundlesCommand,
|
|
@@ -9185,557 +9184,6 @@ const noticesCommand = defineCommand({
|
|
|
9185
9184
|
}
|
|
9186
9185
|
});
|
|
9187
9186
|
//#endregion
|
|
9188
|
-
//#region src/version.ts
|
|
9189
|
-
function resolveVersion() {
|
|
9190
|
-
try {
|
|
9191
|
-
const injected = globalThis.AITCC_VERSION;
|
|
9192
|
-
if (typeof injected === "string" && injected.length > 0) return injected;
|
|
9193
|
-
} catch {}
|
|
9194
|
-
try {
|
|
9195
|
-
return "0.1.39";
|
|
9196
|
-
} catch {}
|
|
9197
|
-
return "0.0.0-dev";
|
|
9198
|
-
}
|
|
9199
|
-
const VERSION = resolveVersion();
|
|
9200
|
-
//#endregion
|
|
9201
|
-
//#region src/telemetry/state.ts
|
|
9202
|
-
/**
|
|
9203
|
-
* Telemetry consent state + anon_id I/O for console-cli.
|
|
9204
|
-
*
|
|
9205
|
-
* Storage: ~/.config/aitcc/telemetry.json (0600, XDG-aware via configDir())
|
|
9206
|
-
* Consistent with devtools' localStorage schema names where applicable.
|
|
9207
|
-
*/
|
|
9208
|
-
/** Current policy version. Bump whenever the privacy policy changes. */
|
|
9209
|
-
const CURRENT_POLICY_VERSION = "2026-05-18";
|
|
9210
|
-
function telemetryFilePath() {
|
|
9211
|
-
return join(configDir(), "telemetry.json");
|
|
9212
|
-
}
|
|
9213
|
-
async function readStateFile() {
|
|
9214
|
-
let raw;
|
|
9215
|
-
try {
|
|
9216
|
-
raw = await readFile(telemetryFilePath(), "utf8");
|
|
9217
|
-
} catch {
|
|
9218
|
-
return null;
|
|
9219
|
-
}
|
|
9220
|
-
let parsed;
|
|
9221
|
-
try {
|
|
9222
|
-
parsed = JSON.parse(raw);
|
|
9223
|
-
} catch {
|
|
9224
|
-
return null;
|
|
9225
|
-
}
|
|
9226
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
9227
|
-
const obj = parsed;
|
|
9228
|
-
if (obj.schemaVersion !== 1) return null;
|
|
9229
|
-
if (obj.consent !== "granted" && obj.consent !== "denied" && obj.consent !== "undecided") return null;
|
|
9230
|
-
if (typeof obj.policyVersion !== "string") return null;
|
|
9231
|
-
return {
|
|
9232
|
-
schemaVersion: 1,
|
|
9233
|
-
consent: obj.consent,
|
|
9234
|
-
policyVersion: obj.policyVersion,
|
|
9235
|
-
...typeof obj.anonId === "string" ? { anonId: obj.anonId } : {},
|
|
9236
|
-
...typeof obj.tier0LastSent === "string" ? { tier0LastSent: obj.tier0LastSent } : {},
|
|
9237
|
-
...obj.tier0OptOut === true ? { tier0OptOut: true } : {}
|
|
9238
|
-
};
|
|
9239
|
-
}
|
|
9240
|
-
async function writeStateFile(state) {
|
|
9241
|
-
const path = telemetryFilePath();
|
|
9242
|
-
await mkdir(dirname(path), {
|
|
9243
|
-
recursive: true,
|
|
9244
|
-
mode: 448
|
|
9245
|
-
});
|
|
9246
|
-
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
9247
|
-
try {
|
|
9248
|
-
await writeFile(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
9249
|
-
await rename(tmp, path);
|
|
9250
|
-
} catch (err) {
|
|
9251
|
-
await unlink(tmp).catch(() => {});
|
|
9252
|
-
throw err;
|
|
9253
|
-
}
|
|
9254
|
-
}
|
|
9255
|
-
/** Read the raw consent from disk. Returns 'undecided' if no file. */
|
|
9256
|
-
async function readConsentState() {
|
|
9257
|
-
const s = await readStateFile();
|
|
9258
|
-
if (!s) return "undecided";
|
|
9259
|
-
return s.consent;
|
|
9260
|
-
}
|
|
9261
|
-
/**
|
|
9262
|
-
* Resolve effective consent with policy-version bump rule:
|
|
9263
|
-
* - Previously 'granted' but on an old policy version → revert to 'undecided'
|
|
9264
|
-
* - Previously 'denied' on any version → stay 'denied'
|
|
9265
|
-
*/
|
|
9266
|
-
async function resolveEffectiveConsent() {
|
|
9267
|
-
const s = await readStateFile();
|
|
9268
|
-
if (!s) return "undecided";
|
|
9269
|
-
if (s.consent === "granted") {
|
|
9270
|
-
if (s.policyVersion !== "2026-05-18") return "undecided";
|
|
9271
|
-
return "granted";
|
|
9272
|
-
}
|
|
9273
|
-
return s.consent;
|
|
9274
|
-
}
|
|
9275
|
-
/**
|
|
9276
|
-
* Returns the stored anon_id, or generates + persists a new UUID v4.
|
|
9277
|
-
* Once generated it is never overwritten except after a successful deleteMyData call.
|
|
9278
|
-
*/
|
|
9279
|
-
async function getOrCreateAnonId() {
|
|
9280
|
-
const s = await readStateFile();
|
|
9281
|
-
if (s?.anonId) return s.anonId;
|
|
9282
|
-
const id = crypto.randomUUID();
|
|
9283
|
-
await writeStateFile({
|
|
9284
|
-
...s ?? {
|
|
9285
|
-
schemaVersion: 1,
|
|
9286
|
-
consent: "undecided",
|
|
9287
|
-
policyVersion: "2026-05-18"
|
|
9288
|
-
},
|
|
9289
|
-
anonId: id
|
|
9290
|
-
});
|
|
9291
|
-
return id;
|
|
9292
|
-
}
|
|
9293
|
-
async function acceptConsent() {
|
|
9294
|
-
await writeStateFile({
|
|
9295
|
-
schemaVersion: 1,
|
|
9296
|
-
consent: "granted",
|
|
9297
|
-
policyVersion: CURRENT_POLICY_VERSION,
|
|
9298
|
-
anonId: (await readStateFile())?.anonId ?? crypto.randomUUID()
|
|
9299
|
-
});
|
|
9300
|
-
}
|
|
9301
|
-
async function denyConsent() {
|
|
9302
|
-
const s = await readStateFile();
|
|
9303
|
-
await writeStateFile({
|
|
9304
|
-
schemaVersion: 1,
|
|
9305
|
-
consent: "denied",
|
|
9306
|
-
policyVersion: CURRENT_POLICY_VERSION,
|
|
9307
|
-
...s?.anonId ? { anonId: s.anonId } : {}
|
|
9308
|
-
});
|
|
9309
|
-
}
|
|
9310
|
-
/** Returns true if Tier 0 pings are permanently opted out. */
|
|
9311
|
-
async function isTier0OptedOut() {
|
|
9312
|
-
return (await readStateFile())?.tier0OptOut === true;
|
|
9313
|
-
}
|
|
9314
|
-
/** Permanently opt out of Tier 0 pings (sets tier0OptOut: true). */
|
|
9315
|
-
async function setTier0OptOut(optOut) {
|
|
9316
|
-
const current = await readStateFile() ?? {
|
|
9317
|
-
schemaVersion: 1,
|
|
9318
|
-
consent: "undecided",
|
|
9319
|
-
policyVersion: "2026-05-18"
|
|
9320
|
-
};
|
|
9321
|
-
if (optOut) await writeStateFile({
|
|
9322
|
-
...current,
|
|
9323
|
-
tier0OptOut: true
|
|
9324
|
-
});
|
|
9325
|
-
else {
|
|
9326
|
-
const { tier0OptOut: _removed, ...rest } = current;
|
|
9327
|
-
await writeStateFile(rest);
|
|
9328
|
-
}
|
|
9329
|
-
}
|
|
9330
|
-
/**
|
|
9331
|
-
* Returns the ISO date (YYYY-MM-DD) of the last sent Tier 0 ping, or null.
|
|
9332
|
-
*/
|
|
9333
|
-
async function getTier0LastSent() {
|
|
9334
|
-
return (await readStateFile())?.tier0LastSent ?? null;
|
|
9335
|
-
}
|
|
9336
|
-
/**
|
|
9337
|
-
* Record that a Tier 0 ping was sent today (ISO date marker).
|
|
9338
|
-
*/
|
|
9339
|
-
async function markTier0Sent(date) {
|
|
9340
|
-
await writeStateFile({
|
|
9341
|
-
...await readStateFile() ?? {
|
|
9342
|
-
schemaVersion: 1,
|
|
9343
|
-
consent: "undecided",
|
|
9344
|
-
policyVersion: "2026-05-18"
|
|
9345
|
-
},
|
|
9346
|
-
tier0LastSent: date
|
|
9347
|
-
});
|
|
9348
|
-
}
|
|
9349
|
-
/**
|
|
9350
|
-
* Delete data: send DELETE /e?anon_id=... to the server (if we have an id),
|
|
9351
|
-
* then rotate local anon_id so subsequent events are unlinkable.
|
|
9352
|
-
*/
|
|
9353
|
-
async function deleteMyData(endpoint) {
|
|
9354
|
-
const s = await readStateFile();
|
|
9355
|
-
if (!s?.anonId) return false;
|
|
9356
|
-
try {
|
|
9357
|
-
if (!(await fetch(`${endpoint}/e?anon_id=${encodeURIComponent(s.anonId)}`, { method: "DELETE" })).ok) return false;
|
|
9358
|
-
await writeStateFile({
|
|
9359
|
-
...s,
|
|
9360
|
-
anonId: crypto.randomUUID()
|
|
9361
|
-
});
|
|
9362
|
-
return true;
|
|
9363
|
-
} catch {
|
|
9364
|
-
return false;
|
|
9365
|
-
}
|
|
9366
|
-
}
|
|
9367
|
-
//#endregion
|
|
9368
|
-
//#region src/telemetry/send.ts
|
|
9369
|
-
/**
|
|
9370
|
-
* Telemetry send — fire-and-forget with one retry.
|
|
9371
|
-
*
|
|
9372
|
-
* Tier 0 rules:
|
|
9373
|
-
* 1. No consent check — fires regardless of opt-in state (unless opted out via tier0OptOut).
|
|
9374
|
-
* 2. No anon_id — server generates a daily hash.
|
|
9375
|
-
* 3. 5 s timeout, no retry. Drops silently on any failure.
|
|
9376
|
-
*
|
|
9377
|
-
* Tier 1 rules:
|
|
9378
|
-
* 1. If consent ≠ "granted" — drop silently.
|
|
9379
|
-
* 2. POST event as JSON with 5 s timeout.
|
|
9380
|
-
* 3. On network error or non-2xx: retry ONCE after 2 s. On second failure: drop.
|
|
9381
|
-
* 4. Meta is capped at 256 bytes (JSON-serialized); oversized meta is dropped.
|
|
9382
|
-
* 5. All calls are non-blocking — caller never awaits send().
|
|
9383
|
-
*/
|
|
9384
|
-
/** Meta size cap per server contract (JSON bytes). */
|
|
9385
|
-
const META_BYTE_CAP = 256;
|
|
9386
|
-
function sanitizeMeta(meta) {
|
|
9387
|
-
if (meta === void 0) return void 0;
|
|
9388
|
-
const serialized = JSON.stringify(meta);
|
|
9389
|
-
if (new TextEncoder().encode(serialized).byteLength > META_BYTE_CAP) return void 0;
|
|
9390
|
-
return meta;
|
|
9391
|
-
}
|
|
9392
|
-
async function doFetch(endpoint, payload) {
|
|
9393
|
-
const controller = new AbortController();
|
|
9394
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
9395
|
-
try {
|
|
9396
|
-
return (await fetch(`${endpoint}/e`, {
|
|
9397
|
-
method: "POST",
|
|
9398
|
-
headers: { "Content-Type": "application/json" },
|
|
9399
|
-
body: JSON.stringify(payload),
|
|
9400
|
-
signal: controller.signal
|
|
9401
|
-
})).ok;
|
|
9402
|
-
} catch {
|
|
9403
|
-
return false;
|
|
9404
|
-
} finally {
|
|
9405
|
-
clearTimeout(timeoutId);
|
|
9406
|
-
}
|
|
9407
|
-
}
|
|
9408
|
-
function delay(ms) {
|
|
9409
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9410
|
-
}
|
|
9411
|
-
/** Retry delay in ms — injectable for tests. */
|
|
9412
|
-
let RETRY_DELAY_MS = 2e3;
|
|
9413
|
-
/**
|
|
9414
|
-
* Send a Tier 1 telemetry event. Drops silently if consent is not 'granted'.
|
|
9415
|
-
* Returns a Promise but callers should NOT await it — fire-and-forget only.
|
|
9416
|
-
*/
|
|
9417
|
-
async function send(endpoint, event, version, meta) {
|
|
9418
|
-
if (await readConsentState() !== "granted") return;
|
|
9419
|
-
const sanitized = sanitizeMeta(meta);
|
|
9420
|
-
const payload = {
|
|
9421
|
-
tier: 1,
|
|
9422
|
-
source: "console-cli",
|
|
9423
|
-
event,
|
|
9424
|
-
anon_id: await getOrCreateAnonId(),
|
|
9425
|
-
version,
|
|
9426
|
-
ts: Date.now(),
|
|
9427
|
-
...sanitized !== void 0 ? { meta: sanitized } : {}
|
|
9428
|
-
};
|
|
9429
|
-
if (await doFetch(endpoint, payload)) return;
|
|
9430
|
-
await delay(RETRY_DELAY_MS);
|
|
9431
|
-
await doFetch(endpoint, payload);
|
|
9432
|
-
}
|
|
9433
|
-
/**
|
|
9434
|
-
* Send a Tier 0 anonymous daily ping.
|
|
9435
|
-
* - No anon_id (server generates a daily hash from IP+UA).
|
|
9436
|
-
* - No retry. 5 s timeout. Fire-and-forget.
|
|
9437
|
-
* - Callers should NOT await this.
|
|
9438
|
-
*/
|
|
9439
|
-
async function sendTier0Ping(endpoint, version) {
|
|
9440
|
-
const payload = {
|
|
9441
|
-
tier: 0,
|
|
9442
|
-
source: "console-cli",
|
|
9443
|
-
version,
|
|
9444
|
-
platform: process.platform
|
|
9445
|
-
};
|
|
9446
|
-
const controller = new AbortController();
|
|
9447
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
9448
|
-
try {
|
|
9449
|
-
await fetch(`${endpoint}/e`, {
|
|
9450
|
-
method: "POST",
|
|
9451
|
-
headers: { "Content-Type": "application/json" },
|
|
9452
|
-
body: JSON.stringify(payload),
|
|
9453
|
-
signal: controller.signal
|
|
9454
|
-
});
|
|
9455
|
-
} catch {} finally {
|
|
9456
|
-
clearTimeout(timeoutId);
|
|
9457
|
-
}
|
|
9458
|
-
}
|
|
9459
|
-
//#endregion
|
|
9460
|
-
//#region src/telemetry/index.ts
|
|
9461
|
-
/**
|
|
9462
|
-
* Telemetry client — internal to @ait-co/console-cli.
|
|
9463
|
-
*
|
|
9464
|
-
* Usage: import { trackInvocation, trackTier0Ping } from './telemetry/index.js'
|
|
9465
|
-
*
|
|
9466
|
-
* Tier 0 (opt-out): anonymous daily ping. Fires on every invocation; client-side
|
|
9467
|
-
* daily dedupe via tier0LastSent. Respects AITCC_TELEMETRY=off, --no-telemetry,
|
|
9468
|
-
* and permanent tier0OptOut flag.
|
|
9469
|
-
*
|
|
9470
|
-
* Tier 1 (opt-in): detailed events. First invocation on a TTY prompts the user;
|
|
9471
|
-
* non-TTY (CI) defaults to deny.
|
|
9472
|
-
*
|
|
9473
|
-
* Endpoint override for staging: AITCC_TELEMETRY_ENV=staging
|
|
9474
|
-
* (or automatically when VERSION contains '-dev').
|
|
9475
|
-
*/
|
|
9476
|
-
function resolveEndpoint() {
|
|
9477
|
-
if (process.env.AITCC_TELEMETRY_ENV === "staging") return "https://t-staging.aitc.dev";
|
|
9478
|
-
if (VERSION.includes("-dev")) return "https://t-staging.aitc.dev";
|
|
9479
|
-
return "https://t.aitc.dev";
|
|
9480
|
-
}
|
|
9481
|
-
const TELEMETRY_ENDPOINT = resolveEndpoint();
|
|
9482
|
-
/** Returns true if this is the very first run (telemetry.json does not exist). */
|
|
9483
|
-
function isFirstRun() {
|
|
9484
|
-
return !existsSync(telemetryFilePath());
|
|
9485
|
-
}
|
|
9486
|
-
/**
|
|
9487
|
-
* Prompt for consent on TTY. Defaults to deny on any non-TTY or error.
|
|
9488
|
-
* Called once per install (no file yet) when stdin/stdout are both TTYs.
|
|
9489
|
-
*/
|
|
9490
|
-
async function promptConsent() {
|
|
9491
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
9492
|
-
await denyConsent();
|
|
9493
|
-
return;
|
|
9494
|
-
}
|
|
9495
|
-
const rl = createInterface({
|
|
9496
|
-
input: process.stdin,
|
|
9497
|
-
output: process.stdout
|
|
9498
|
-
});
|
|
9499
|
-
try {
|
|
9500
|
-
process.stderr.write([
|
|
9501
|
-
"",
|
|
9502
|
-
"aitcc 사용 개선을 위해 익명 사용 통계를 수집해도 될까요?",
|
|
9503
|
-
" · 개인 식별 정보 없음 · 랜덤 익명 ID만 사용 · 언제든 off 가능",
|
|
9504
|
-
" 자세한 내용: https://docs.aitc.dev/privacy",
|
|
9505
|
-
""
|
|
9506
|
-
].join("\n"));
|
|
9507
|
-
if ((await rl.question("보내도 될까요? [y/N] ")).trim().toLowerCase() === "y") await acceptConsent();
|
|
9508
|
-
else await denyConsent();
|
|
9509
|
-
} catch {
|
|
9510
|
-
await denyConsent();
|
|
9511
|
-
} finally {
|
|
9512
|
-
rl.close();
|
|
9513
|
-
}
|
|
9514
|
-
}
|
|
9515
|
-
/**
|
|
9516
|
-
* Check whether telemetry is globally disabled via environment or CLI flag.
|
|
9517
|
-
* Accepts the parsed --no-telemetry flag value from argv.
|
|
9518
|
-
*/
|
|
9519
|
-
function isTelemetryGloballyDisabled(noTelemetryFlag) {
|
|
9520
|
-
if (noTelemetryFlag) return true;
|
|
9521
|
-
const env = process.env.AITCC_TELEMETRY;
|
|
9522
|
-
if (env !== void 0 && env.toLowerCase() === "off") return true;
|
|
9523
|
-
return false;
|
|
9524
|
-
}
|
|
9525
|
-
/**
|
|
9526
|
-
* Send a Tier 0 anonymous daily ping (fire-and-forget).
|
|
9527
|
-
*
|
|
9528
|
-
* Skips if:
|
|
9529
|
-
* - AITCC_TELEMETRY=off or --no-telemetry flag
|
|
9530
|
-
* - tier0OptOut === true in the state file
|
|
9531
|
-
* - already sent today (tier0LastSent === today's ISO date)
|
|
9532
|
-
*
|
|
9533
|
-
* On success, records today's date in tier0LastSent for client-side daily dedupe.
|
|
9534
|
-
* The server also deduplicates server-side via KV, so this is an extra client guard.
|
|
9535
|
-
*/
|
|
9536
|
-
async function trackTier0Ping(noTelemetryFlag = false) {
|
|
9537
|
-
try {
|
|
9538
|
-
if (isTelemetryGloballyDisabled(noTelemetryFlag)) return;
|
|
9539
|
-
if (await isTier0OptedOut()) return;
|
|
9540
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9541
|
-
if (await getTier0LastSent() === today) return;
|
|
9542
|
-
sendTier0Ping(TELEMETRY_ENDPOINT, VERSION);
|
|
9543
|
-
await markTier0Sent(today);
|
|
9544
|
-
} catch {}
|
|
9545
|
-
}
|
|
9546
|
-
/** True only on the first invocation after a fresh install. */
|
|
9547
|
-
async function isNewInstall() {
|
|
9548
|
-
const markerPath = `${telemetryFilePath()}.install`;
|
|
9549
|
-
if (existsSync(markerPath)) return false;
|
|
9550
|
-
try {
|
|
9551
|
-
await writeFile(markerPath, "1", { mode: 384 });
|
|
9552
|
-
return true;
|
|
9553
|
-
} catch {
|
|
9554
|
-
return false;
|
|
9555
|
-
}
|
|
9556
|
-
}
|
|
9557
|
-
/**
|
|
9558
|
-
* Called at CLI entry point with the resolved top-level command name.
|
|
9559
|
-
* Handles first-run Tier 1 consent prompt, install detection, and Tier 1 event send.
|
|
9560
|
-
* Fire-and-forget: do NOT await this.
|
|
9561
|
-
*
|
|
9562
|
-
* Note: Tier 0 ping is sent separately via trackTier0Ping() before this call.
|
|
9563
|
-
*/
|
|
9564
|
-
async function trackInvocation(command, noTelemetryFlag = false) {
|
|
9565
|
-
try {
|
|
9566
|
-
if (isTelemetryGloballyDisabled(noTelemetryFlag)) return;
|
|
9567
|
-
if (isFirstRun()) await promptConsent();
|
|
9568
|
-
if (await resolveEffectiveConsent() !== "granted") return;
|
|
9569
|
-
if (await isNewInstall()) send(TELEMETRY_ENDPOINT, "cli_install", VERSION, {
|
|
9570
|
-
platform: process.platform,
|
|
9571
|
-
arch: process.arch
|
|
9572
|
-
});
|
|
9573
|
-
send(TELEMETRY_ENDPOINT, "cli_invoked", VERSION, { command });
|
|
9574
|
-
} catch {}
|
|
9575
|
-
}
|
|
9576
|
-
const telemetryCommand = defineCommand({
|
|
9577
|
-
meta: {
|
|
9578
|
-
name: "telemetry",
|
|
9579
|
-
description: "Manage anonymous usage telemetry."
|
|
9580
|
-
},
|
|
9581
|
-
subCommands: {
|
|
9582
|
-
status: defineCommand({
|
|
9583
|
-
meta: {
|
|
9584
|
-
name: "status",
|
|
9585
|
-
description: "Show current telemetry status for both Tier 0 and Tier 1."
|
|
9586
|
-
},
|
|
9587
|
-
args: { json: {
|
|
9588
|
-
type: "boolean",
|
|
9589
|
-
description: "Emit machine-readable JSON.",
|
|
9590
|
-
default: false
|
|
9591
|
-
} },
|
|
9592
|
-
async run({ args }) {
|
|
9593
|
-
const [tier1Consent, tier0OptOut, tier0LastSent, anonId] = await Promise.all([
|
|
9594
|
-
resolveEffectiveConsent(),
|
|
9595
|
-
isTier0OptedOut(),
|
|
9596
|
-
getTier0LastSent(),
|
|
9597
|
-
resolveEffectiveConsent().then((c) => c === "granted" ? getOrCreateAnonId() : null)
|
|
9598
|
-
]);
|
|
9599
|
-
const filePath = telemetryFilePath();
|
|
9600
|
-
const tier0Status = tier0OptOut ? "off (opted out)" : "on";
|
|
9601
|
-
const tier0Display = tier0LastSent ? `${tier0Status} (last sent: ${tier0LastSent})` : tier0Status;
|
|
9602
|
-
if (args.json) {
|
|
9603
|
-
process.stdout.write(`${JSON.stringify({
|
|
9604
|
-
ok: true,
|
|
9605
|
-
tier0: {
|
|
9606
|
-
status: tier0OptOut ? "opted-out" : "on",
|
|
9607
|
-
lastSent: tier0LastSent ?? null
|
|
9608
|
-
},
|
|
9609
|
-
tier1: {
|
|
9610
|
-
consent: tier1Consent,
|
|
9611
|
-
policyVersion: CURRENT_POLICY_VERSION,
|
|
9612
|
-
...anonId ? { anonId } : {}
|
|
9613
|
-
},
|
|
9614
|
-
endpoint: TELEMETRY_ENDPOINT,
|
|
9615
|
-
filePath
|
|
9616
|
-
})}\n`);
|
|
9617
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
9618
|
-
}
|
|
9619
|
-
process.stdout.write("Telemetry status\n");
|
|
9620
|
-
process.stdout.write(` Tier 0 (anonymous daily ping): ${tier0Display}\n`);
|
|
9621
|
-
process.stdout.write(` Tier 1 (opt-in events): ${tier1Consent} (policyVersion: ${CURRENT_POLICY_VERSION})\n`);
|
|
9622
|
-
process.stdout.write(`\nEndpoint: ${TELEMETRY_ENDPOINT}\n`);
|
|
9623
|
-
if (anonId) process.stdout.write(`Anon ID: ${anonId}\n`);
|
|
9624
|
-
process.stdout.write(`State file: ${filePath}\n`);
|
|
9625
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
9626
|
-
}
|
|
9627
|
-
}),
|
|
9628
|
-
enable: defineCommand({
|
|
9629
|
-
meta: {
|
|
9630
|
-
name: "enable",
|
|
9631
|
-
description: "Enable anonymous usage telemetry (opt-in)."
|
|
9632
|
-
},
|
|
9633
|
-
args: { json: {
|
|
9634
|
-
type: "boolean",
|
|
9635
|
-
description: "Emit machine-readable JSON.",
|
|
9636
|
-
default: false
|
|
9637
|
-
} },
|
|
9638
|
-
async run({ args }) {
|
|
9639
|
-
await acceptConsent();
|
|
9640
|
-
const anonId = await getOrCreateAnonId();
|
|
9641
|
-
if (args.json) process.stdout.write(`${JSON.stringify({
|
|
9642
|
-
ok: true,
|
|
9643
|
-
consent: "granted",
|
|
9644
|
-
anonId
|
|
9645
|
-
})}\n`);
|
|
9646
|
-
else {
|
|
9647
|
-
process.stdout.write("Telemetry enabled. Thank you!\n");
|
|
9648
|
-
process.stdout.write(`Anon ID: ${anonId}\n`);
|
|
9649
|
-
}
|
|
9650
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
9651
|
-
}
|
|
9652
|
-
}),
|
|
9653
|
-
disable: defineCommand({
|
|
9654
|
-
meta: {
|
|
9655
|
-
name: "disable",
|
|
9656
|
-
description: "Disable anonymous usage telemetry."
|
|
9657
|
-
},
|
|
9658
|
-
args: { json: {
|
|
9659
|
-
type: "boolean",
|
|
9660
|
-
description: "Emit machine-readable JSON.",
|
|
9661
|
-
default: false
|
|
9662
|
-
} },
|
|
9663
|
-
async run({ args }) {
|
|
9664
|
-
await denyConsent();
|
|
9665
|
-
if (args.json) process.stdout.write(`${JSON.stringify({
|
|
9666
|
-
ok: true,
|
|
9667
|
-
consent: "denied"
|
|
9668
|
-
})}\n`);
|
|
9669
|
-
else process.stdout.write("Telemetry disabled.\n");
|
|
9670
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
9671
|
-
}
|
|
9672
|
-
}),
|
|
9673
|
-
delete: defineCommand({
|
|
9674
|
-
meta: {
|
|
9675
|
-
name: "delete",
|
|
9676
|
-
description: "Delete your telemetry data from the server and rotate the local anon_id."
|
|
9677
|
-
},
|
|
9678
|
-
args: { json: {
|
|
9679
|
-
type: "boolean",
|
|
9680
|
-
description: "Emit machine-readable JSON.",
|
|
9681
|
-
default: false
|
|
9682
|
-
} },
|
|
9683
|
-
async run({ args }) {
|
|
9684
|
-
const beforeConsent = await readConsentState();
|
|
9685
|
-
const ok = await deleteMyData(TELEMETRY_ENDPOINT);
|
|
9686
|
-
if (args.json) process.stdout.write(`${JSON.stringify({
|
|
9687
|
-
ok,
|
|
9688
|
-
...ok ? {} : { reason: beforeConsent === "undecided" ? "no-data" : "server-error" }
|
|
9689
|
-
})}\n`);
|
|
9690
|
-
else if (ok) process.stdout.write("Deletion request sent. Your data has been removed and a new anon ID assigned.\n");
|
|
9691
|
-
else if (beforeConsent === "undecided") process.stdout.write("No telemetry data to delete (telemetry was never enabled).\n");
|
|
9692
|
-
else process.stderr.write("Deletion request failed. Please try again or contact the maintainers.\n");
|
|
9693
|
-
return exitAfterFlush(ok || beforeConsent === "undecided" ? ExitCode.Ok : ExitCode.NetworkError);
|
|
9694
|
-
}
|
|
9695
|
-
}),
|
|
9696
|
-
"tier0-off": defineCommand({
|
|
9697
|
-
meta: {
|
|
9698
|
-
name: "tier0-off",
|
|
9699
|
-
description: "Permanently opt out of the Tier 0 anonymous daily ping."
|
|
9700
|
-
},
|
|
9701
|
-
args: { json: {
|
|
9702
|
-
type: "boolean",
|
|
9703
|
-
description: "Emit machine-readable JSON.",
|
|
9704
|
-
default: false
|
|
9705
|
-
} },
|
|
9706
|
-
async run({ args }) {
|
|
9707
|
-
await setTier0OptOut(true);
|
|
9708
|
-
if (args.json) process.stdout.write(`${JSON.stringify({
|
|
9709
|
-
ok: true,
|
|
9710
|
-
tier0: { status: "opted-out" }
|
|
9711
|
-
})}\n`);
|
|
9712
|
-
else process.stdout.write("Tier 0 anonymous ping disabled. No daily pings will be sent.\n");
|
|
9713
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
9714
|
-
}
|
|
9715
|
-
}),
|
|
9716
|
-
"tier0-on": defineCommand({
|
|
9717
|
-
meta: {
|
|
9718
|
-
name: "tier0-on",
|
|
9719
|
-
description: "Re-enable the Tier 0 anonymous daily ping after a previous tier0-off."
|
|
9720
|
-
},
|
|
9721
|
-
args: { json: {
|
|
9722
|
-
type: "boolean",
|
|
9723
|
-
description: "Emit machine-readable JSON.",
|
|
9724
|
-
default: false
|
|
9725
|
-
} },
|
|
9726
|
-
async run({ args }) {
|
|
9727
|
-
await setTier0OptOut(false);
|
|
9728
|
-
if (args.json) process.stdout.write(`${JSON.stringify({
|
|
9729
|
-
ok: true,
|
|
9730
|
-
tier0: { status: "on" }
|
|
9731
|
-
})}\n`);
|
|
9732
|
-
else process.stdout.write("Tier 0 anonymous ping re-enabled.\n");
|
|
9733
|
-
return exitAfterFlush(ExitCode.Ok);
|
|
9734
|
-
}
|
|
9735
|
-
})
|
|
9736
|
-
}
|
|
9737
|
-
});
|
|
9738
|
-
//#endregion
|
|
9739
9187
|
//#region src/github.ts
|
|
9740
9188
|
const REPO_OWNER = "apps-in-toss-community";
|
|
9741
9189
|
const REPO_NAME = "console-cli";
|
|
@@ -9869,6 +9317,19 @@ function sha256OfFile(path) {
|
|
|
9869
9317
|
});
|
|
9870
9318
|
}
|
|
9871
9319
|
//#endregion
|
|
9320
|
+
//#region src/version.ts
|
|
9321
|
+
function resolveVersion() {
|
|
9322
|
+
try {
|
|
9323
|
+
const injected = globalThis.AITCC_VERSION;
|
|
9324
|
+
if (typeof injected === "string" && injected.length > 0) return injected;
|
|
9325
|
+
} catch {}
|
|
9326
|
+
try {
|
|
9327
|
+
return "0.1.40";
|
|
9328
|
+
} catch {}
|
|
9329
|
+
return "0.0.0-dev";
|
|
9330
|
+
}
|
|
9331
|
+
const VERSION = resolveVersion();
|
|
9332
|
+
//#endregion
|
|
9872
9333
|
//#region src/commands/upgrade.ts
|
|
9873
9334
|
const execFileP = promisify(execFile);
|
|
9874
9335
|
function isStandaloneBinary() {
|
|
@@ -11016,15 +10477,10 @@ const main = defineCommand({
|
|
|
11016
10477
|
keys: keysCommand,
|
|
11017
10478
|
notices: noticesCommand,
|
|
11018
10479
|
me: meCommand,
|
|
11019
|
-
telemetry: telemetryCommand,
|
|
11020
10480
|
completion: completionCommand
|
|
11021
10481
|
}
|
|
11022
10482
|
});
|
|
11023
10483
|
cleanupStaleUpgradeArtifacts().catch(() => {});
|
|
11024
|
-
const _telemetryCmd = process.argv.slice(2).find((a) => !a.startsWith("-")) ?? "(none)";
|
|
11025
|
-
const _noTelemetry = process.argv.includes("--no-telemetry");
|
|
11026
|
-
trackTier0Ping(_noTelemetry);
|
|
11027
|
-
trackInvocation(_telemetryCmd, _noTelemetry);
|
|
11028
10484
|
runMain(main);
|
|
11029
10485
|
//#endregion
|
|
11030
10486
|
export {};
|