@glassanalytics/cli 0.1.1 → 0.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/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import { parseArgs } from "util";
6
6
  // src/commands.ts
7
7
  import { spawnSync } from "child_process";
8
8
  import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
9
- import { dirname as dirname2, relative } from "path";
9
+ import { dirname as dirname2, join as join2, relative } from "path";
10
10
 
11
11
  // src/output.ts
12
12
  var ExitCode = {
@@ -165,7 +165,14 @@ var Api = class {
165
165
  method,
166
166
  headers,
167
167
  ...body !== void 0 ? { body: JSON.stringify(body) } : {}
168
- }).catch((e) => fail(ExitCode.ERROR, `network error: ${String(e)}`));
168
+ }).catch(
169
+ (e) => fail(
170
+ ExitCode.ERROR,
171
+ `couldn't reach the Glass control plane at ${this.apiHost} (${String(
172
+ e?.message ?? e
173
+ )}) \u2014 check the host is correct/reachable, or pass --api-host <url> (or set GLASS_API_HOST)`
174
+ )
175
+ );
169
176
  const text = await res.text();
170
177
  const json = text ? JSON.parse(text) : {};
171
178
  if (res.status === 401 || res.status === 403) fail(ExitCode.AUTH, "unauthorized", json);
@@ -180,6 +187,8 @@ var Api = class {
180
187
  import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
181
188
  import { homedir } from "os";
182
189
  import { dirname, join } from "path";
190
+ var DEFAULT_API_HOST = "https://glass-control-plane.isnit.workers.dev";
191
+ var DEFAULT_INGEST_HOST = "https://glass-ingest.isnit.workers.dev";
183
192
  var PROJECT_FILE = join(process.cwd(), "glass.json");
184
193
  var CRED_FILE = join(homedir(), ".glass", "credentials.json");
185
194
  function readProject() {
@@ -195,8 +204,8 @@ function readProject() {
195
204
  return {
196
205
  project_id: envProject ?? file?.project_id ?? "",
197
206
  ingest_key: envIngest ?? file?.ingest_key ?? "",
198
- ingest_host: process.env.GLASS_INGEST_HOST ?? file?.ingest_host ?? "https://in.glass.dev",
199
- api_host: process.env.GLASS_API_HOST ?? file?.api_host ?? "https://api.glass.dev",
207
+ ingest_host: process.env.GLASS_INGEST_HOST ?? file?.ingest_host ?? DEFAULT_INGEST_HOST,
208
+ api_host: process.env.GLASS_API_HOST ?? file?.api_host ?? DEFAULT_API_HOST,
200
209
  ...file?.claim_url ? { claim_url: file.claim_url } : {}
201
210
  };
202
211
  }
@@ -251,13 +260,13 @@ async function init(flags) {
251
260
  process.stdout.write("Use `glass init --force` to provision a new project, or `glass install` to wire the SDK.\n");
252
261
  });
253
262
  }
254
- const apiHost = flags["api-host"] ?? "https://api.glass.dev";
263
+ const apiHost = flags["api-host"] ?? DEFAULT_API_HOST;
255
264
  const api = new Api(apiHost);
256
265
  const res = await api.request("POST", "/v1/projects/provision", { name: flags.name ?? void 0 });
257
266
  writeProject({
258
267
  project_id: res.project_id,
259
268
  ingest_key: res.ingest_key,
260
- ingest_host: flags["ingest-host"] ?? "https://in.glass.dev",
269
+ ingest_host: flags["ingest-host"] ?? DEFAULT_INGEST_HOST,
261
270
  api_host: apiHost,
262
271
  claim_url: res.claim_url
263
272
  });
@@ -293,32 +302,16 @@ function detectPackageManager() {
293
302
  if (existsSync("bun.lockb")) return { cmd: "bun", args: ["add"] };
294
303
  return { cmd: "npm", args: ["install"] };
295
304
  }
296
- function snippetFor(framework, project) {
297
- if (framework === "node") {
298
- return {
299
- pkg: "@glassanalytics/node",
300
- file: "glass.ts",
301
- code: `import { GlassNode } from '@glassanalytics/node';
302
-
303
- export const glass = new GlassNode({ projectKey: '${project.ingest_key}', ingestHost: '${project.ingest_host}' });
304
- `
305
- };
305
+ function findNextAppLayout() {
306
+ for (const p of ["app/layout.tsx", "app/layout.jsx", "src/app/layout.tsx", "src/app/layout.jsx"]) {
307
+ if (existsSync(p)) return p;
306
308
  }
307
- return {
308
- pkg: "@glassanalytics/browser",
309
- file: "glass.ts",
310
- code: `import glass from '@glassanalytics/browser';
311
-
312
- glass.init({ projectKey: '${project.ingest_key}', ingestHost: '${project.ingest_host}' });
313
-
314
- export default glass;
315
- `
316
- };
309
+ return null;
317
310
  }
318
311
  function entryCandidates(framework) {
319
312
  switch (framework) {
320
313
  case "next":
321
- return ["app/layout.tsx", "app/layout.jsx", "src/app/layout.tsx", "src/app/layout.jsx"];
314
+ return ["pages/_app.tsx", "pages/_app.jsx", "src/pages/_app.tsx", "src/pages/_app.jsx"];
322
315
  case "react":
323
316
  case "vite":
324
317
  return ["src/main.tsx", "src/main.jsx", "src/index.tsx", "src/index.jsx", "src/main.ts", "src/index.ts"];
@@ -328,57 +321,200 @@ function entryCandidates(framework) {
328
321
  return [];
329
322
  }
330
323
  }
331
- function injectEntryPoint(framework, glassFile) {
324
+ function alreadyWired() {
325
+ const files = [
326
+ "glass.ts",
327
+ findNextAppLayout(),
328
+ ...entryCandidates("next"),
329
+ ...entryCandidates("react")
330
+ ].filter((f) => !!f && existsSync(f));
331
+ return files.some((f) => {
332
+ try {
333
+ return /@glassanalytics\/(browser|node)|glass\.init\(|GlassProvider/.test(readFileSync2(f, "utf8"));
334
+ } catch {
335
+ return false;
336
+ }
337
+ });
338
+ }
339
+ var NEXT_PROVIDER_SRC = `'use client';
340
+
341
+ import { useEffect, useRef } from 'react';
342
+ import glass from '@glassanalytics/browser';
343
+
344
+ const KEY = process.env.NEXT_PUBLIC_GLASS_PROJECT_KEY;
345
+ const INGEST_HOST = process.env.NEXT_PUBLIC_GLASS_INGEST_HOST;
346
+ const API_HOST = process.env.NEXT_PUBLIC_GLASS_API_HOST;
347
+
348
+ /**
349
+ * Initialises Glass on the client. The SDK auto-captures pageviews (incl. App
350
+ * Router navigations), autocapture, errors and session replay after init().
351
+ */
352
+ export function GlassProvider() {
353
+ const started = useRef(false);
354
+ useEffect(() => {
355
+ if (started.current || !KEY || !INGEST_HOST) return;
356
+ started.current = true;
357
+ glass.init({ projectKey: KEY, ingestHost: INGEST_HOST, apiHost: API_HOST });
358
+ }, []);
359
+ return null;
360
+ }
361
+
362
+ export default GlassProvider;
363
+ `;
364
+ function upsertEnv(file, vars) {
365
+ let src = "";
366
+ try {
367
+ src = readFileSync2(file, "utf8");
368
+ } catch {
369
+ src = "";
370
+ }
371
+ let out = src;
372
+ let changed = false;
373
+ for (const [k, v] of Object.entries(vars)) {
374
+ if (new RegExp(`^${k}=`, "m").test(out)) continue;
375
+ if (out.length && !out.endsWith("\n")) out += "\n";
376
+ out += `${k}=${v}
377
+ `;
378
+ changed = true;
379
+ }
380
+ if (changed) writeFileSync2(file, out);
381
+ return { file, wrote: changed };
382
+ }
383
+ function wireNextAppRouter(layout) {
384
+ const providerFile = join2(dirname2(layout), "glass-provider.tsx");
385
+ if (!existsSync(providerFile)) writeFileSync2(providerFile, NEXT_PROVIDER_SRC);
386
+ let src = readFileSync2(layout, "utf8");
387
+ const importLine = "import { GlassProvider } from './glass-provider';\n";
388
+ let injectedImport = false;
389
+ if (!src.includes("./glass-provider")) {
390
+ const directive = /^\s*(['"])use (client|server)\1;?\s*\n/.exec(src);
391
+ src = directive ? src.slice(0, directive[0].length) + importLine + src.slice(directive[0].length) : importLine + src;
392
+ injectedImport = true;
393
+ }
394
+ let injectedRender = false;
395
+ if (!src.includes("<GlassProvider")) {
396
+ const bodyOpen = /(<body[^>]*>)/;
397
+ if (bodyOpen.test(src)) {
398
+ src = src.replace(bodyOpen, "$1\n <GlassProvider />");
399
+ injectedRender = true;
400
+ }
401
+ }
402
+ writeFileSync2(layout, src);
403
+ return { provider: providerFile, injectedImport, injectedRender };
404
+ }
405
+ function wireBareImport(framework, project) {
406
+ const file = "glass.ts";
407
+ const code = framework === "node" ? `import { GlassNode } from '@glassanalytics/node';
408
+
409
+ export const glass = new GlassNode({
410
+ projectKey: process.env.GLASS_INGEST_KEY ?? '${project.ingest_key}',
411
+ ingestHost: process.env.GLASS_INGEST_HOST ?? '${project.ingest_host}',
412
+ });
413
+ ` : `import glass from '@glassanalytics/browser';
414
+
415
+ glass.init({ projectKey: '${project.ingest_key}', ingestHost: '${project.ingest_host}', apiHost: '${project.api_host}' });
416
+
417
+ export default glass;
418
+ `;
419
+ let wroteFile = false;
420
+ if (!existsSync(file)) {
421
+ writeFileSync2(file, code);
422
+ wroteFile = true;
423
+ }
332
424
  for (const entry of entryCandidates(framework)) {
333
425
  if (!existsSync(entry)) continue;
334
426
  const src = readFileSync2(entry, "utf8");
335
- let spec = relative(dirname2(entry), glassFile).replace(/\.(ts|tsx|js|jsx)$/, "");
427
+ let spec = relative(dirname2(entry), file).replace(/\.(ts|tsx|js|jsx)$/, "");
336
428
  if (!spec.startsWith(".")) spec = `./${spec}`;
337
- if (src.includes(`'${spec}'`) || src.includes(`"${spec}"`)) return { entry, injected: false };
429
+ if (src.includes(`'${spec}'`) || src.includes(`"${spec}"`)) return { file, wroteFile, entry, injected: false };
338
430
  const importLine = `import '${spec}';
339
431
  `;
340
432
  const directive = /^\s*(['"])use (client|server)\1;?\s*\n/.exec(src);
341
433
  const next = directive ? src.slice(0, directive[0].length) + importLine + src.slice(directive[0].length) : importLine + src;
342
434
  writeFileSync2(entry, next);
343
- return { entry, injected: true };
435
+ return { file, wroteFile, entry, injected: true };
344
436
  }
345
- return { entry: null, injected: false };
437
+ return { file, wroteFile, entry: null, injected: false };
346
438
  }
347
439
  function install(flags) {
348
440
  const project = requireProject();
349
441
  const framework = flags.framework ?? detectFramework();
350
- const { pkg, file, code } = snippetFor(framework, project);
442
+ const pkg = framework === "node" ? "@glassanalytics/node" : "@glassanalytics/browser";
443
+ if (alreadyWired() && flags.force !== true) {
444
+ return ok({ framework, package: pkg, already_wired: true }, () => {
445
+ process.stdout.write("Glass already appears to be wired in this project \u2014 nothing to do.\n");
446
+ process.stdout.write("Re-run with `--force` to wire it again anyway.\n");
447
+ });
448
+ }
351
449
  let installed = false;
352
450
  if (flags["no-install"] !== true) {
353
451
  const pm = detectPackageManager();
354
- const r = spawnSync(pm.cmd, [...pm.args, pkg], { stdio: "inherit" });
452
+ const args = [...pm.args, pkg];
453
+ if (typeof flags.registry === "string" && flags.registry) args.push(`--registry=${flags.registry}`);
454
+ const r = spawnSync(pm.cmd, args, { stdio: "inherit" });
355
455
  installed = r.status === 0;
356
- if (!installed && r.error) {
357
- return fail(ExitCode.ERROR, `failed to run ${pm.cmd}: ${String(r.error.message)} \u2014 install ${pkg} manually`);
456
+ if (!installed) {
457
+ const detail = r.error ? String(r.error.message) : `exit ${r.status}`;
458
+ return fail(
459
+ ExitCode.ERROR,
460
+ `failed to install ${pkg} via ${pm.cmd} (${detail}) \u2014 install it manually, or retry with \`--registry https://registry.npmjs.org/\` if you're behind a private registry`
461
+ );
358
462
  }
359
463
  }
360
- let wrote = false;
361
- if (!existsSync(file)) {
362
- writeFileSync2(file, code);
363
- wrote = true;
464
+ const appLayout = framework === "next" ? findNextAppLayout() : null;
465
+ if (appLayout) {
466
+ const env = upsertEnv(".env.local", {
467
+ NEXT_PUBLIC_GLASS_PROJECT_KEY: project.ingest_key,
468
+ NEXT_PUBLIC_GLASS_INGEST_HOST: project.ingest_host,
469
+ NEXT_PUBLIC_GLASS_API_HOST: project.api_host
470
+ });
471
+ const w2 = wireNextAppRouter(appLayout);
472
+ return ok(
473
+ {
474
+ framework: "next-app",
475
+ package: pkg,
476
+ installed,
477
+ provider: w2.provider,
478
+ env_file: env.file,
479
+ wrote_env: env.wrote,
480
+ layout: appLayout,
481
+ injected: w2.injectedImport,
482
+ rendered: w2.injectedRender
483
+ },
484
+ () => {
485
+ process.stdout.write("Detected: Next.js (App Router)\n");
486
+ process.stdout.write(installed ? `Installed ${pkg}.
487
+ ` : `Skipped install \u2014 add ${pkg} yourself.
488
+ `);
489
+ process.stdout.write(`Wrote ${w2.provider}${env.wrote ? ` and Glass keys to ${env.file}` : ""}.
490
+ `);
491
+ if (w2.injectedRender) process.stdout.write(`Rendered <GlassProvider /> in ${appLayout}.
492
+ `);
493
+ else process.stdout.write(`Couldn't auto-insert <GlassProvider /> \u2014 add it inside <body> in ${appLayout}.
494
+ `);
495
+ }
496
+ );
364
497
  }
365
- const { entry, injected } = injectEntryPoint(framework, file);
366
- return ok({ framework, package: pkg, installed, file, wrote_file: wrote, entry, injected, snippet: code }, () => {
367
- process.stdout.write(`Detected: ${framework}
498
+ const w = wireBareImport(framework, project);
499
+ return ok(
500
+ { framework, package: pkg, installed, file: w.file, wrote_file: w.wroteFile, entry: w.entry, injected: w.injected },
501
+ () => {
502
+ process.stdout.write(`Detected: ${framework}${framework === "next" ? " (Pages Router)" : ""}
368
503
  `);
369
- process.stdout.write(installed ? `Installed ${pkg}.
504
+ process.stdout.write(installed ? `Installed ${pkg}.
370
505
  ` : `Skipped install \u2014 add ${pkg} yourself.
371
506
  `);
372
- process.stdout.write(wrote ? `Wrote ${file}.
373
- ` : `${file} already exists; left untouched.
507
+ process.stdout.write(w.wroteFile ? `Wrote ${w.file}.
508
+ ` : `${w.file} already exists; left untouched.
374
509
  `);
375
- if (injected && entry) process.stdout.write(`Wired ${file} into ${entry}.
510
+ if (w.injected && w.entry) process.stdout.write(`Wired ${w.file} into ${w.entry}.
376
511
  `);
377
- else if (entry) process.stdout.write(`${entry} already imports ${file}.
512
+ else if (w.entry) process.stdout.write(`${w.entry} already imports ${w.file}.
378
513
  `);
379
- else process.stdout.write(`No entry point found \u2014 import "./${file.replace(/\.(ts|tsx)$/, "")}" yourself.
514
+ else process.stdout.write(`No entry point found \u2014 import "./${w.file.replace(/\.(ts|tsx)$/, "")}" in your app entry yourself.
380
515
  `);
381
- });
516
+ }
517
+ );
382
518
  }
383
519
  async function status(flags) {
384
520
  const { api } = authedApi(flags);
@@ -426,7 +562,7 @@ async function claim(flags) {
426
562
  }
427
563
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
428
564
  async function login(flags) {
429
- const apiHost = flags["api-host"] ?? readProject()?.api_host ?? "https://api.glass.dev";
565
+ const apiHost = flags["api-host"] ?? readProject()?.api_host ?? DEFAULT_API_HOST;
430
566
  const provided = flags.token;
431
567
  if (provided) {
432
568
  const creds = readCredentials();
@@ -486,14 +622,14 @@ async function whoami(flags) {
486
622
  const project = readProject();
487
623
  const token = resolveToken(flags, project?.project_id);
488
624
  if (!token) fail(ExitCode.AUTH, "not logged in \u2014 run `glass login` or set GLASS_TOKEN");
489
- const api = new Api(project?.api_host ?? flags["api-host"] ?? "https://api.glass.dev", token, project?.project_id);
625
+ const api = new Api(project?.api_host ?? flags["api-host"] ?? DEFAULT_API_HOST, token, project?.project_id);
490
626
  return ok(await api.request("GET", "/v1/me"));
491
627
  }
492
628
  async function link(flags, positionals = []) {
493
629
  const projectId = flags.project ?? positionals[0];
494
630
  if (!projectId) fail(ExitCode.USAGE, "link needs a project id: `glass link <id>`");
495
631
  const existing = readProject();
496
- const apiHost = flags["api-host"] ?? existing?.api_host ?? "https://api.glass.dev";
632
+ const apiHost = flags["api-host"] ?? existing?.api_host ?? DEFAULT_API_HOST;
497
633
  const token = resolveToken(flags, projectId);
498
634
  if (!token) fail(ExitCode.AUTH, "no credentials \u2014 run `glass login` or set GLASS_TOKEN");
499
635
  const api = new Api(apiHost, token, projectId);
@@ -501,7 +637,7 @@ async function link(flags, positionals = []) {
501
637
  writeProject({
502
638
  project_id: projectId,
503
639
  ingest_key: proj.publishableKey ?? existing?.ingest_key ?? "",
504
- ingest_host: proj.ingestHost ?? existing?.ingest_host ?? "https://in.glass.dev",
640
+ ingest_host: proj.ingestHost ?? existing?.ingest_host ?? DEFAULT_INGEST_HOST,
505
641
  api_host: apiHost
506
642
  });
507
643
  return ok({ linked: projectId }, () => process.stdout.write(`Linked glass.json to ${projectId}.
@@ -643,7 +779,7 @@ var HELP = `glass <command> [options]
643
779
 
644
780
  Setup
645
781
  init [--force] Provision a project with no signup (writes glass.json)
646
- install [--framework F] [--no-install] Install + wire the SDK
782
+ install [--framework F] [--no-install] [--registry URL] [--force] Install + wire the SDK
647
783
  status Show whether events are being received
648
784
  doctor Diagnose setup problems
649
785
  claim Get the claim URL to keep your data
@@ -668,7 +804,7 @@ Manage
668
804
 
669
805
  Global: --json --csv --ndjson --api-host --ingest-host
670
806
  --max-scan <500MB|1GB> --yes/--no-input (auth: --token | GLASS_TOKEN)
671
- Env: GLASS_TOKEN GLASS_PROJECT GLASS_INGEST_KEY GLASS_API_HOST`;
807
+ Env: GLASS_TOKEN GLASS_PROJECT GLASS_INGEST_KEY GLASS_API_HOST GLASS_INGEST_HOST`;
672
808
  async function main() {
673
809
  const argv = process.argv.slice(2);
674
810
  const command = argv[0];
@@ -714,7 +850,8 @@ async function main() {
714
850
  "budget-r2sql": { type: "string" },
715
851
  token: { type: "string" },
716
852
  "api-host": { type: "string" },
717
- "ingest-host": { type: "string" }
853
+ "ingest-host": { type: "string" },
854
+ registry: { type: "string" }
718
855
  }
719
856
  });
720
857
  const flags = values;
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/commands.ts","../src/output.ts","../src/api.ts","../src/config.ts"],"sourcesContent":["import { parseArgs } from 'node:util';\nimport * as cmd from './commands.js';\nimport { ExitCode, fail, setFormat, setJsonMode } from './output.js';\n\n/**\n * `glass` — the agent-friendly terminal surface (`CLI.md`). Setup in one command\n * (`glass init`, no signup), then the same typed query primitives the dashboard\n * uses. `--json` + stable exit codes make it scriptable by humans and agents.\n */\n\nconst HELP = `glass <command> [options]\n\nSetup\n init [--force] Provision a project with no signup (writes glass.json)\n install [--framework F] [--no-install] Install + wire the SDK\n status Show whether events are being received\n doctor Diagnose setup problems\n claim Get the claim URL to keep your data\n login [--token T] Store a Clerk session for human-scoped access\n logout Clear the stored session\n whoami Show the current principal (GET /v1/me)\n link --project ID Point glass.json at another project you can access\n\nQuery (all accept --last 7d, --json, --confirm, --max-scan <MB>)\n trend --event E [--bucket day] [--breakdown B] [--aggregate count|unique_users|sum]\n funnel --steps a,b,c | --id <def> [--window 1d]\n retention --id <def> --return E [--periods 8]\n segment --group-by a,b [--event E]\n errors\n replays [--limit 50]\n query --query '<json>' | -f <file> | (piped stdin)\n\nManage\n define --file spec.json | --kind funnel --steps a,b | --list\n tokens list | create [--budget-r2sql 1GB/day] | revoke <id>\n link <project-id>\n\nGlobal: --json --csv --ndjson --api-host --ingest-host\n --max-scan <500MB|1GB> --yes/--no-input (auth: --token | GLASS_TOKEN)\nEnv: GLASS_TOKEN GLASS_PROJECT GLASS_INGEST_KEY GLASS_API_HOST`;\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n const command = argv[0];\n const sub = argv[1] && !argv[1].startsWith('-') ? argv[1] : undefined;\n\n const { values, positionals } = parseArgs({\n args: argv.slice(1),\n allowPositionals: true,\n options: {\n json: { type: 'boolean' },\n csv: { type: 'boolean' },\n ndjson: { type: 'boolean' },\n confirm: { type: 'boolean' },\n reissue: { type: 'boolean' },\n force: { type: 'boolean' },\n 'no-install': { type: 'boolean' },\n yes: { type: 'boolean' },\n 'no-input': { type: 'boolean' },\n list: { type: 'boolean' },\n 'max-scan': { type: 'string' },\n last: { type: 'string' },\n event: { type: 'string' },\n bucket: { type: 'string' },\n breakdown: { type: 'string' },\n aggregate: { type: 'string' },\n steps: { type: 'string' },\n window: { type: 'string' },\n scope: { type: 'string' },\n kind: { type: 'string' },\n id: { type: 'string' },\n return: { type: 'string' },\n periods: { type: 'string' },\n 'group-by': { type: 'string' },\n group: { type: 'string' },\n status: { type: 'string' },\n limit: { type: 'string' },\n user: { type: 'string' },\n query: { type: 'string' },\n file: { type: 'string', short: 'f' },\n name: { type: 'string' },\n role: { type: 'string' },\n framework: { type: 'string' },\n project: { type: 'string' },\n 'budget-r2sql': { type: 'string' },\n token: { type: 'string' },\n 'api-host': { type: 'string' },\n 'ingest-host': { type: 'string' },\n },\n });\n\n const flags = values as Record<string, string | boolean>;\n if (flags.json) setJsonMode(true);\n else if (flags.ndjson) setFormat('ndjson');\n else if (flags.csv) setFormat('csv');\n\n switch (command) {\n case 'init':\n return void (await cmd.init(flags));\n case 'install':\n return void cmd.install(flags);\n case 'status':\n return void (await cmd.status(flags));\n case 'doctor':\n return void (await cmd.doctor(flags));\n case 'claim':\n return void (await cmd.claim(flags));\n case 'login':\n return void (await cmd.login(flags));\n case 'logout':\n return void cmd.logout(flags);\n case 'whoami':\n return void (await cmd.whoami(flags));\n case 'link':\n return void (await cmd.link(flags, positionals));\n case 'trend':\n return void (await cmd.trend(flags));\n case 'funnel':\n return void (await cmd.funnel(flags));\n case 'retention':\n return void (await cmd.retention(flags));\n case 'segment':\n return void (await cmd.segment(flags));\n case 'errors':\n return void (await cmd.errors(flags));\n case 'replays':\n return void (await cmd.replays(flags));\n case 'query':\n return void (await cmd.query(flags));\n case 'define':\n return void (await cmd.define(flags));\n case 'tokens':\n // positionals[0] is the sub-action; pass the remainder (e.g. token id).\n return void (await cmd.tokens(sub ?? 'list', flags, positionals.slice(1)));\n case 'help':\n case undefined:\n case '--help':\n case '-h':\n process.stdout.write(`${HELP}\\n`);\n process.exit(ExitCode.OK);\n break;\n default:\n fail(ExitCode.USAGE, `unknown command: ${command}\\n\\n${HELP}`);\n }\n}\n\nmain().catch((e) => fail(ExitCode.ERROR, String(e?.message ?? e)));\n","import { spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, relative } from 'node:path';\nimport type { GlassQuery } from '@glassanalytics/core';\nimport { Api } from './api.js';\nimport { readCredentials, readProject, tokenFor, writeCredentials, writeProject } from './config.js';\nimport { ExitCode, fail, ok, parseBytes } from './output.js';\n\ntype Flags = Record<string, string | boolean>;\n\nfunction requireProject() {\n const project = readProject();\n if (!project) fail(ExitCode.USAGE, 'no glass.json found — run `glass init` first');\n return project;\n}\n\n/**\n * Token precedence (`CLI.md` §2): explicit --token > GLASS_TOKEN env > stored\n * per-project token > stored Clerk session. Lets CI inject a token without\n * touching the filesystem.\n */\nfunction resolveToken(flags: Flags, projectId?: string): string | undefined {\n if (typeof flags.token === 'string' && flags.token) return flags.token;\n if (process.env.GLASS_TOKEN) return process.env.GLASS_TOKEN;\n if (projectId) {\n const t = tokenFor(projectId);\n if (t) return t;\n }\n return readCredentials().session;\n}\n\nfunction authedApi(flags: Flags = {}) {\n const project = requireProject();\n const token = resolveToken(flags, project.project_id);\n if (!token) fail(ExitCode.AUTH, 'no credentials — run `glass init`, `glass login`, or set GLASS_TOKEN');\n return { api: new Api(project.api_host, token, project.project_id), project };\n}\n\n// --- glass init (zero-auth provisioning) -------------------------------------\n\nexport async function init(flags: Flags): Promise<never> {\n // Idempotent: don't re-provision if a project is already linked here.\n const existing = readProject();\n if (existing && flags.force !== true) {\n return ok({ project_id: existing.project_id, linked: true }, () => {\n process.stdout.write(`Already linked to ${existing.project_id} (glass.json present).\\n`);\n process.stdout.write('Use `glass init --force` to provision a new project, or `glass install` to wire the SDK.\\n');\n });\n }\n\n const apiHost = (flags['api-host'] as string) ?? 'https://api.glass.dev';\n const api = new Api(apiHost);\n const res = await api.request<{\n project_id: string;\n ingest_key: string;\n provisioning_token: string;\n claim_url: string;\n }>('POST', '/v1/projects/provision', { name: (flags.name as string) ?? undefined });\n\n writeProject({\n project_id: res.project_id,\n ingest_key: res.ingest_key,\n ingest_host: (flags['ingest-host'] as string) ?? 'https://in.glass.dev',\n api_host: apiHost,\n claim_url: res.claim_url,\n });\n const creds = readCredentials();\n creds.tokens[res.project_id] = res.provisioning_token;\n writeCredentials(creds);\n\n return ok(res, () => {\n process.stdout.write(`Created project ${res.project_id}\\n`);\n process.stdout.write('Wrote glass.json and ~/.glass/credentials.json (0600)\\n');\n process.stdout.write(`\\nClaim it to keep your data:\\n ${res.claim_url}\\n`);\n process.stdout.write('\\nNext: `glass install` to wire up the SDK.\\n');\n });\n}\n\n// --- glass install (framework detection) -------------------------------------\n\ntype Framework = 'next' | 'react' | 'vite' | 'node' | 'browser';\n\nfunction detectFramework(): Framework {\n try {\n const pkg = JSON.parse(readFileSync('package.json', 'utf8')) as { dependencies?: Record<string, string>; devDependencies?: Record<string, string> };\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps.next) return 'next';\n if (deps.express || deps.fastify || deps.hono) return 'node';\n if (deps.react) return 'react';\n if (deps.vite) return 'vite';\n } catch {\n /* no package.json — assume browser */\n }\n return 'browser';\n}\n\n/** Pick the package manager from the lockfile present in cwd. */\nfunction detectPackageManager(): { cmd: string; args: string[] } {\n if (existsSync('pnpm-lock.yaml')) return { cmd: 'pnpm', args: ['add'] };\n if (existsSync('yarn.lock')) return { cmd: 'yarn', args: ['add'] };\n if (existsSync('bun.lockb')) return { cmd: 'bun', args: ['add'] };\n return { cmd: 'npm', args: ['install'] };\n}\n\nfunction snippetFor(framework: Framework, project: { ingest_key: string; ingest_host: string }): { file: string; code: string; pkg: string } {\n if (framework === 'node') {\n return {\n pkg: '@glassanalytics/node',\n file: 'glass.ts',\n code: `import { GlassNode } from '@glassanalytics/node';\\n\\nexport const glass = new GlassNode({ projectKey: '${project.ingest_key}', ingestHost: '${project.ingest_host}' });\\n`,\n };\n }\n return {\n pkg: '@glassanalytics/browser',\n file: 'glass.ts',\n code: `import glass from '@glassanalytics/browser';\\n\\nglass.init({ projectKey: '${project.ingest_key}', ingestHost: '${project.ingest_host}' });\\n\\nexport default glass;\\n`,\n };\n}\n\n/** Candidate entry-point files per framework, in preference order. */\nfunction entryCandidates(framework: Framework): string[] {\n switch (framework) {\n case 'next':\n return ['app/layout.tsx', 'app/layout.jsx', 'src/app/layout.tsx', 'src/app/layout.jsx'];\n case 'react':\n case 'vite':\n return ['src/main.tsx', 'src/main.jsx', 'src/index.tsx', 'src/index.jsx', 'src/main.ts', 'src/index.ts'];\n case 'node':\n return ['src/index.ts', 'src/server.ts', 'index.ts', 'server.ts', 'src/main.ts'];\n default:\n return [];\n }\n}\n\n/**\n * Inject `import '<glass>'` into the app's entry point so the SDK actually\n * initialises (`CLI.md` §3.1) — writing `glass.ts` alone wires nothing. We only\n * touch a file that exists and doesn't already import it, and we never reorder\n * existing code (the import is prepended, after a leading \"use client\" if present).\n */\nfunction injectEntryPoint(framework: Framework, glassFile: string): { entry: string | null; injected: boolean } {\n for (const entry of entryCandidates(framework)) {\n if (!existsSync(entry)) continue;\n const src = readFileSync(entry, 'utf8');\n let spec = relative(dirname(entry), glassFile).replace(/\\.(ts|tsx|js|jsx)$/, '');\n if (!spec.startsWith('.')) spec = `./${spec}`;\n if (src.includes(`'${spec}'`) || src.includes(`\"${spec}\"`)) return { entry, injected: false };\n const importLine = `import '${spec}';\\n`;\n // Preserve a leading directive (\"use client\"/\"use server\") as the first line.\n const directive = /^\\s*(['\"])use (client|server)\\1;?\\s*\\n/.exec(src);\n const next = directive\n ? src.slice(0, directive[0].length) + importLine + src.slice(directive[0].length)\n : importLine + src;\n writeFileSync(entry, next);\n return { entry, injected: true };\n }\n return { entry: null, injected: false };\n}\n\nexport function install(flags: Flags): never {\n const project = requireProject();\n const framework = ((flags.framework as Framework) ?? detectFramework()) as Framework;\n const { pkg, file, code } = snippetFor(framework, project);\n\n // 1. Install the SDK dependency (fixed argv — never a shell string).\n let installed = false;\n if (flags['no-install'] !== true) {\n const pm = detectPackageManager();\n const r = spawnSync(pm.cmd, [...pm.args, pkg], { stdio: 'inherit' });\n installed = r.status === 0;\n if (!installed && r.error) {\n return fail(ExitCode.ERROR, `failed to run ${pm.cmd}: ${String(r.error.message)} — install ${pkg} manually`);\n }\n }\n\n // 2. Write the wiring file if absent (never clobber existing app code).\n let wrote = false;\n if (!existsSync(file)) {\n writeFileSync(file, code);\n wrote = true;\n }\n\n // 3. Inject the import into the framework entry point so init actually runs.\n const { entry, injected } = injectEntryPoint(framework, file);\n\n return ok({ framework, package: pkg, installed, file, wrote_file: wrote, entry, injected, snippet: code }, () => {\n process.stdout.write(`Detected: ${framework}\\n`);\n process.stdout.write(installed ? `Installed ${pkg}.\\n` : `Skipped install — add ${pkg} yourself.\\n`);\n process.stdout.write(wrote ? `Wrote ${file}.\\n` : `${file} already exists; left untouched.\\n`);\n if (injected && entry) process.stdout.write(`Wired ${file} into ${entry}.\\n`);\n else if (entry) process.stdout.write(`${entry} already imports ${file}.\\n`);\n else process.stdout.write(`No entry point found — import \"./${file.replace(/\\.(ts|tsx)$/, '')}\" yourself.\\n`);\n });\n}\n\n// --- status / doctor ----------------------------------------------------------\n\nexport async function status(flags: Flags): Promise<never> {\n const { api } = authedApi(flags);\n const res = await api.request('GET', '/v1/ingest/status');\n return ok(res);\n}\n\nexport async function doctor(flags: Flags): Promise<never> {\n const project = readProject();\n const checks: { name: string; ok: boolean; detail?: string }[] = [];\n checks.push({ name: 'glass.json present', ok: !!project });\n if (project) {\n const tok = resolveToken(flags, project.project_id);\n checks.push({ name: 'credentials present', ok: !!tok });\n try {\n const api = new Api(project.api_host, tok, project.project_id);\n const s = await api.request<{ receiving: boolean }>('GET', '/v1/ingest/status');\n checks.push({ name: 'events received', ok: s.receiving, detail: s.receiving ? undefined : 'no events in last 24h' });\n } catch {\n checks.push({ name: 'control plane reachable', ok: false });\n }\n }\n const allOk = checks.every((c) => c.ok);\n return allOk ? ok({ checks }) : fail(ExitCode.ERROR, 'doctor found problems', { checks });\n}\n\n// --- claim / login ------------------------------------------------------------\n\nexport async function claim(flags: Flags): Promise<never> {\n const project = requireProject();\n\n // Default: show the claim URL stored at `glass init` WITHOUT contacting the\n // control plane (so we never invalidate a link the user may already be using).\n if (flags.reissue !== true && project.claim_url) {\n return ok({ claim_url: project.claim_url, reissued: false }, () =>\n process.stdout.write(`Claim URL:\\n ${project.claim_url}\\n\\n(Use \\`glass claim --reissue\\` to mint a fresh single-use link.)\\n`),\n );\n }\n\n // `--reissue` (or no stored URL): mint a fresh single-use claim link.\n const token = tokenFor(project.project_id);\n if (!token) fail(ExitCode.AUTH, 'no provisioning token for this project');\n const api = new Api(project.api_host, token);\n const res = await api.request<{ claim_url: string }>('POST', `/v1/projects/${project.project_id}/claim-url`);\n // Persist the new link so subsequent `glass claim` shows it without reissuing.\n writeProject({ ...project, claim_url: res.claim_url });\n return ok({ ...res, reissued: true }, () => process.stdout.write(`Claim URL (reissued):\\n ${res.claim_url}\\n`));\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/**\n * `glass login` (`CLI.md` §2). `--token <session>` stores a session directly\n * (CI/non-interactive); otherwise runs the OAuth-style device flow: start a\n * grant, print the verification URL + short user-code, then poll until a\n * signed-in human approves it in the dashboard.\n */\nexport async function login(flags: Flags): Promise<never> {\n const apiHost = (flags['api-host'] as string) ?? readProject()?.api_host ?? 'https://api.glass.dev';\n\n // Non-interactive path: store an explicitly-provided session.\n const provided = flags.token as string | undefined;\n if (provided) {\n const creds = readCredentials();\n creds.session = provided;\n writeCredentials(creds);\n return ok({ message: 'Logged in. Session stored in ~/.glass/credentials.json' }, () =>\n process.stdout.write('Logged in. Session stored in ~/.glass/credentials.json\\n'),\n );\n }\n\n // The device flow needs a human to approve in a browser; in non-interactive\n // mode (`--no-input`/`--yes`, or CI) fail fast and ask for a token instead.\n if (flags['no-input'] === true || flags.yes === true || (!process.stdin.isTTY && !process.stdout.isTTY)) {\n return fail(ExitCode.AUTH, 'login is interactive — pass `--token <session>` or set GLASS_TOKEN for non-interactive use');\n }\n\n // Device flow.\n const api = new Api(apiHost);\n const grant = await api.request<{\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete?: string;\n interval: number;\n expires_in: number;\n }>('POST', '/v1/cli/device/start');\n\n if (!flags.json) {\n process.stdout.write('\\nTo sign in, open:\\n');\n process.stdout.write(` ${grant.verification_uri_complete ?? grant.verification_uri}\\n\\n`);\n process.stdout.write(`and enter the code: ${grant.user_code}\\n\\n`);\n process.stdout.write('Waiting for approval…\\n');\n }\n\n const deadline = Date.now() + grant.expires_in * 1000;\n const intervalMs = Math.max(1, grant.interval) * 1000;\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n let resp: { session?: string; status?: string } | null = null;\n try {\n resp = await api.request<{ session?: string; status?: string }>('POST', '/v1/cli/device/token', {\n device_code: grant.device_code,\n });\n } catch {\n // 202 (pending) / 410 (expired) surface as request errors depending on the\n // client; treat transient failures as \"keep polling\" until the deadline.\n continue;\n }\n if (resp?.session) {\n const creds = readCredentials();\n creds.session = resp.session;\n writeCredentials(creds);\n return ok({ message: 'Logged in. Session stored in ~/.glass/credentials.json' }, () =>\n process.stdout.write('\\nLogged in. Session stored in ~/.glass/credentials.json\\n'),\n );\n }\n }\n return fail(ExitCode.AUTH, 'device authorization timed out — run `glass login` again');\n}\n\nexport function logout(_flags: Flags): never {\n const creds = readCredentials();\n creds.session = undefined;\n writeCredentials(creds);\n return ok({ message: 'Logged out. Cleared stored session.' }, () => process.stdout.write('Logged out.\\n'));\n}\n\nexport async function whoami(flags: Flags): Promise<never> {\n const project = readProject();\n const token = resolveToken(flags, project?.project_id);\n if (!token) fail(ExitCode.AUTH, 'not logged in — run `glass login` or set GLASS_TOKEN');\n const api = new Api(project?.api_host ?? (flags['api-host'] as string) ?? 'https://api.glass.dev', token, project?.project_id);\n return ok(await api.request('GET', '/v1/me'));\n}\n\n/** Switch the active project in glass.json after verifying access. */\nexport async function link(flags: Flags, positionals: string[] = []): Promise<never> {\n // Accept both `glass link <id>` and `glass link --project <id>`.\n const projectId = (flags.project as string | undefined) ?? positionals[0];\n if (!projectId) fail(ExitCode.USAGE, 'link needs a project id: `glass link <id>`');\n const existing = readProject();\n const apiHost = (flags['api-host'] as string) ?? existing?.api_host ?? 'https://api.glass.dev';\n const token = resolveToken(flags, projectId);\n if (!token) fail(ExitCode.AUTH, 'no credentials — run `glass login` or set GLASS_TOKEN');\n const api = new Api(apiHost, token, projectId);\n const proj = await api.request<{ id: string; publishableKey?: string; ingestHost?: string }>('GET', '/v1/project');\n writeProject({\n project_id: projectId,\n ingest_key: proj.publishableKey ?? existing?.ingest_key ?? '',\n ingest_host: proj.ingestHost ?? existing?.ingest_host ?? 'https://in.glass.dev',\n api_host: apiHost,\n });\n return ok({ linked: projectId }, () => process.stdout.write(`Linked glass.json to ${projectId}.\\n`));\n}\n\n// --- query + sugar ------------------------------------------------------------\n\n/**\n * `glass query` source resolution (`CLI.md` §4): a GlassQuery can come from\n * `--query '<json>'`, `--file/-f <path>`, or stdin (so it composes in a pipe:\n * `cat q.json | glass query --json`). Exactly one source is used, in that order.\n */\nexport async function query(flags: Flags): Promise<never> {\n let raw: string | undefined;\n if (typeof flags.query === 'string' && flags.query) raw = flags.query;\n else if (typeof flags.file === 'string' && flags.file) raw = readFileSync(flags.file, 'utf8');\n else if (!process.stdin.isTTY) raw = readFileSync(0, 'utf8').trim() || undefined;\n\n if (!raw) fail(ExitCode.USAGE, \"query needs --query '<json>', --file <path>, or piped stdin\");\n let parsed: GlassQuery;\n try {\n parsed = JSON.parse(raw) as GlassQuery;\n } catch (e) {\n return fail(ExitCode.USAGE, `query is not valid JSON: ${String((e as Error).message)}`);\n }\n return runQuery(parsed, flags);\n}\n\nexport async function runQuery(query: GlassQuery, flags: Flags): Promise<never> {\n const { api } = authedApi(flags);\n const body: Record<string, unknown> = { query, confirm: flags.confirm === true };\n if (flags['max-scan']) {\n // Accept a bare number (legacy: MB) or a unit string (`500MB`, `1GB`).\n const raw = String(flags['max-scan']);\n const bytes = /^[0-9]*\\.?[0-9]+$/.test(raw.trim())\n ? Number(raw) * 1024 * 1024\n : parseBytes(raw);\n if (bytes == null) fail(ExitCode.USAGE, `invalid --max-scan: ${raw} (try 500MB or 1GB)`);\n body.max_scan_bytes = bytes;\n }\n const res = await api.request('POST', '/v1/query', body);\n return ok(res);\n}\n\nfunction parseTime(flags: Flags) {\n return { last: (flags.last as string) ?? '7d' };\n}\n\nexport async function trend(flags: Flags): Promise<never> {\n return runQuery(\n {\n v: 1,\n kind: 'trend',\n time: parseTime(flags),\n measure: { aggregate: (flags.aggregate as 'count' | 'unique_users' | 'sum') ?? 'count', ...(flags.event ? { event: flags.event as string } : {}) },\n bucket: (flags.bucket as 'hour' | 'day' | 'week' | 'month') ?? 'day',\n ...(flags.breakdown ? { breakdown: flags.breakdown as string } : {}),\n },\n flags,\n );\n}\n\nexport async function funnel(flags: Flags): Promise<never> {\n const steps = String(flags.steps ?? '').split(',').map((e) => ({ event: e.trim() })).filter((s) => s.event);\n if (flags.id) return runQuery({ v: 1, kind: 'funnel', time: parseTime(flags), definition_id: flags.id as string }, flags);\n if (steps.length < 2) fail(ExitCode.USAGE, 'funnel needs --steps a,b,c (>=2) or --id <definition>');\n return runQuery({ v: 1, kind: 'funnel', time: parseTime(flags), steps, ...(flags.window ? { window: flags.window as string } : {}) }, flags);\n}\n\nexport async function retention(flags: Flags): Promise<never> {\n if (!flags.id) fail(ExitCode.USAGE, 'retention needs --id <definition>');\n return runQuery({ v: 1, kind: 'retention', time: parseTime(flags), definition_id: flags.id as string, return_event: (flags.return as string) ?? '$pageview', periods: Number(flags.periods ?? 8) }, flags);\n}\n\nexport async function segment(flags: Flags): Promise<never> {\n const groupBy = String(flags['group-by'] ?? '').split(',').map((s) => s.trim()).filter(Boolean);\n if (groupBy.length === 0) fail(ExitCode.USAGE, 'segment needs --group-by a,b');\n return runQuery({ v: 1, kind: 'segmentation', time: parseTime(flags), group_by: groupBy, ...(flags.event ? { event: flags.event as string } : {}) }, flags);\n}\n\nexport async function errors(flags: Flags): Promise<never> {\n return runQuery(\n {\n v: 1,\n kind: 'errors',\n time: parseTime(flags),\n limit: Number(flags.limit ?? 50),\n ...(flags.group ? { group: flags.group as string } : {}),\n ...(flags.status ? { status: flags.status as 'unresolved' | 'resolved' | 'ignored' } : {}),\n },\n flags,\n );\n}\n\nexport async function replays(flags: Flags): Promise<never> {\n return runQuery(\n {\n v: 1,\n kind: 'replays',\n time: parseTime(flags),\n limit: Number(flags.limit ?? 50),\n ...(flags.user ? { session_filters: [{ property: 'user_id', op: 'eq' as const, value: flags.user as string }] } : {}),\n },\n flags,\n );\n}\n\n// --- define / tokens ----------------------------------------------------------\n\nexport async function define(flags: Flags): Promise<never> {\n const { api } = authedApi(flags);\n\n // `define --list` — show existing definitions.\n if (flags.list) return ok(await api.request('GET', '/v1/definitions'));\n\n // Flag-based sugar: build a spec from flags without writing JSON.\n let spec: unknown;\n if (flags.kind === 'funnel') {\n const steps = String(flags.steps ?? '').split(',').map((e) => ({ event: e.trim() })).filter((s) => s.event);\n if (steps.length < 2) fail(ExitCode.USAGE, 'define --kind funnel needs --steps a,b,c (>=2)');\n spec = { v: 1, kind: 'funnel', name: (flags.name as string) ?? 'funnel', steps, scope: (flags.scope as string) ?? 'within-session', ...(flags.window ? { window: flags.window as string } : {}) };\n } else if (flags.kind === 'metric') {\n spec = { v: 1, kind: 'metric', name: (flags.name as string) ?? 'metric', aggregate: (flags.aggregate as string) ?? 'count', bucket: (flags.bucket as string) ?? 'day', ...(flags.event ? { event: flags.event as string } : {}) };\n } else {\n const raw = flags.file ? readFileSync(flags.file as string, 'utf8') : readFileSync(0, 'utf8');\n spec = JSON.parse(raw);\n }\n const res = await api.request('POST', '/v1/definitions', { spec });\n return ok(res);\n}\n\nexport async function tokens(action: string, flags: Flags, positionals: string[] = []): Promise<never> {\n const { api } = authedApi(flags);\n if (action === 'list') return ok(await api.request('GET', '/v1/tokens'));\n if (action === 'create') {\n let budget: number | undefined;\n if (flags['budget-r2sql']) {\n // Accept human units + an optional `/day` rate suffix (`1GB/day`).\n const parsed = parseBytes(String(flags['budget-r2sql']));\n if (parsed == null) fail(ExitCode.USAGE, `invalid --budget-r2sql: ${flags['budget-r2sql']} (try 1GB/day)`);\n budget = parsed;\n }\n const res = await api.request<{ id: string; token: string; prefix: string }>('POST', '/v1/tokens', {\n name: (flags.name as string) ?? 'agent',\n role: (flags.role as string) ?? 'read',\n ...(budget != null ? { budget_r2sql_bytes_per_day: budget } : {}),\n });\n // The secret is shown ONCE — print it verbatim (not through the redactor).\n return ok(res, () => {\n process.stdout.write(`Created token ${res.id}\\n\\n ${res.token}\\n\\nThis is shown once. Store it now (e.g. as GLASS_TOKEN).\\n`);\n });\n }\n if (action === 'revoke') {\n // Accept both `glass tokens revoke <id>` and `--id <id>`.\n const id = (flags.id as string | undefined) ?? positionals[0];\n if (!id) fail(ExitCode.USAGE, 'tokens revoke needs a token id: `glass tokens revoke <id>`');\n return ok(await api.request('DELETE', `/v1/tokens/${id}`));\n }\n return fail(ExitCode.USAGE, 'tokens <list|create|revoke>');\n}\n","/**\n * Stable, scriptable output (`CLI.md` §5). The CLI is built for agents as much\n * as humans: `--json` emits a uniform envelope and EXIT CODES are stable so a\n * caller can branch on them without parsing prose.\n */\n\nexport const ExitCode = {\n OK: 0,\n ERROR: 1,\n USAGE: 2,\n AUTH: 3,\n COST_GATE: 4,\n BUDGET: 5,\n} as const;\nexport type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];\n\ntype OutputFormat = 'human' | 'json' | 'csv' | 'ndjson';\nlet format: OutputFormat = 'human';\nexport function setFormat(f: OutputFormat): void {\n format = f;\n}\n/** Back-compat shim for `--json`. */\nexport function setJsonMode(on: boolean): void {\n if (on) format = 'json';\n}\n\n/** Mask token-like secrets in human output so they're never accidentally logged. */\nconst SECRET_RE =\n /\\b(glass_(?:sk|pt)_[A-Za-z0-9]+)\\b|\\b(eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,})\\b/g;\nexport function redact(text: string): string {\n return text.replace(SECRET_RE, (m) => `${m.slice(0, 12)}…<redacted>`);\n}\n\n/** Flatten an array of flat records to CSV; falls back to JSON for non-tabular data. */\nexport function toCsv(data: unknown): string | null {\n const rows = Array.isArray(data) ? data : extractRows(data);\n if (!rows || rows.length === 0) return null;\n const cols = [...new Set(rows.flatMap((r) => Object.keys(r)))];\n const esc = (v: unknown) => {\n const s = v == null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);\n return /[\",\\n]/.test(s) ? `\"${s.replace(/\"/g, '\"\"')}\"` : s;\n };\n return [\n cols.join(','),\n ...rows.map((r) => cols.map((c) => esc((r as Record<string, unknown>)[c])).join(',')),\n ].join('\\n');\n}\n\n/**\n * Parse a human byte budget into bytes (`CLI.md` §4): accepts plain numbers and\n * unit suffixes (`500MB`, `1GB`, `2 TiB`), with an optional `/day` rate suffix\n * that we strip (the API stores per-day budgets). Returns null on garbage.\n */\nconst BYTE_UNITS: Record<string, number> = {\n b: 1,\n kb: 1e3,\n mb: 1e6,\n gb: 1e9,\n tb: 1e12,\n kib: 1024,\n mib: 1024 ** 2,\n gib: 1024 ** 3,\n tib: 1024 ** 4,\n};\nexport function parseBytes(input: string): number | null {\n const s = input.trim().toLowerCase().replace(/\\/(day|d|hr|hour|h)$/, '');\n const m = /^([0-9]*\\.?[0-9]+)\\s*([a-z]+)?$/.exec(s);\n if (!m) return null;\n const value = Number(m[1]);\n if (!Number.isFinite(value)) return null;\n if (!m[2]) return value; // bare number = bytes\n const unit = BYTE_UNITS[m[2]];\n if (!unit) return null;\n return Math.round(value * unit);\n}\n\n/** Find the first array-of-objects field (e.g. {rows:[...]}, {issues:[...]}). */\nexport function extractRows(data: unknown): Record<string, unknown>[] | null {\n if (Array.isArray(data)) return data as Record<string, unknown>[];\n if (data && typeof data === 'object') {\n for (const v of Object.values(data)) {\n if (Array.isArray(v) && v.every((x) => x && typeof x === 'object'))\n return v as Record<string, unknown>[];\n }\n }\n return null;\n}\n\n/**\n * Render an array of flat records as an aligned ASCII table for human output.\n * Returns null when the data isn't tabular so callers can fall back to JSON.\n */\nexport function humanTable(data: unknown): string | null {\n const rows = extractRows(data);\n if (!rows || rows.length === 0) return null;\n const cols = [...new Set(rows.flatMap((r) => Object.keys(r)))];\n if (cols.length === 0) return null;\n const cell = (v: unknown) => {\n const s = v == null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);\n return redact(s).replace(/\\n/g, ' ');\n };\n const widths = cols.map((c) =>\n Math.max(c.length, ...rows.map((r) => cell((r as Record<string, unknown>)[c]).length)),\n );\n const line = (cells: string[]) => cells.map((s, i) => s.padEnd(widths[i] ?? 0)).join(' ').trimEnd();\n const header = line(cols);\n const sep = widths.map((w) => '-'.repeat(w)).join(' ');\n const body = rows.map((r) => line(cols.map((c) => cell((r as Record<string, unknown>)[c]))));\n return [header, sep, ...body].join('\\n');\n}\n\n/**\n * One-line provenance badge for a query result (`UI.md` ResultBadge parity):\n * surfaces whether a number is exact or sampled, and its source, so an agent\n * reading stdout knows how much to trust it. Returns null when not applicable.\n */\nexport function resultBadge(data: unknown): string | null {\n if (!data || typeof data !== 'object') return null;\n const d = data as Record<string, unknown>;\n if (typeof d.exact !== 'boolean' && !d.source && !d.sampled) return null;\n const parts: string[] = [];\n parts.push(d.exact === false ? 'sampled' : 'exact');\n if (d.source) parts.push(String(d.source));\n const sampled = d.sampled as { interval?: number } | undefined;\n if (sampled?.interval && sampled.interval > 1) parts.push(`1/${sampled.interval}`);\n return `[${parts.join(' · ')}]`;\n}\n\nexport function ok(data: unknown, human?: () => void): never {\n if (format === 'json') {\n process.stdout.write(`${JSON.stringify({ ok: true, data }, null, 2)}\\n`);\n } else if (format === 'ndjson') {\n const rows = extractRows(data) ?? [data];\n for (const r of rows) process.stdout.write(`${JSON.stringify(r)}\\n`);\n } else if (format === 'csv') {\n const csv = toCsv(data);\n process.stdout.write(csv ? `${csv}\\n` : `${JSON.stringify(data, null, 2)}\\n`);\n } else if (human) {\n human();\n } else {\n const table = humanTable(data);\n if (table) {\n const badge = resultBadge(data);\n process.stdout.write(`${table}\\n`);\n if (badge) process.stdout.write(`${badge}\\n`);\n } else {\n process.stdout.write(`${redact(JSON.stringify(data, null, 2))}\\n`);\n }\n }\n process.exit(ExitCode.OK);\n}\n\nexport function fail(code: ExitCodeValue, message: string, extra?: unknown): never {\n if (format === 'json' || format === 'ndjson') {\n process.stdout.write(\n `${JSON.stringify({ ok: false, error: { code, message, ...(extra ? { extra } : {}) } }, null, 2)}\\n`,\n );\n } else {\n process.stderr.write(`glass: ${message}\\n`);\n if (extra) process.stderr.write(`${redact(JSON.stringify(extra, null, 2))}\\n`);\n }\n process.exit(code);\n}\n","import { ExitCode, fail } from './output.js';\n\n/**\n * Thin control-plane client. Maps HTTP status to the CLI's stable exit codes so\n * `--json` callers (agents) can branch deterministically (`CLI.md` §5):\n * 401/403 → AUTH(3), 402 → COST_GATE(4), 429 → BUDGET(5).\n */\nexport class Api {\n constructor(\n private apiHost: string,\n private token?: string,\n private projectId?: string,\n ) {}\n\n async request<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this.token) headers.Authorization = `Bearer ${this.token}`;\n if (this.projectId) headers['X-Glass-Project'] = this.projectId;\n\n const res = await fetch(`${this.apiHost}${path}`, {\n method,\n headers,\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n }).catch((e) => fail(ExitCode.ERROR, `network error: ${String(e)}`));\n\n const text = await res.text();\n const json = text ? (JSON.parse(text) as T) : ({} as T);\n\n if (res.status === 401 || res.status === 403) fail(ExitCode.AUTH, 'unauthorized', json);\n if (res.status === 402) fail(ExitCode.COST_GATE, 'cost gate: re-run with --confirm', json);\n if (res.status === 429) fail(ExitCode.BUDGET, 'budget or rate limit exceeded', json);\n if (!res.ok) fail(ExitCode.ERROR, `request failed (${res.status})`, json);\n return json;\n }\n}\n","import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n/**\n * Local config (`CLI.md` §2/§3). Project pointer in `glass.json` (committable,\n * contains only the write-only publishable key); SECRETS in\n * `~/.glass/credentials.json` with `0600` perms, never in the repo.\n */\n\nexport interface ProjectFile {\n project_id: string;\n ingest_key: string;\n ingest_host: string;\n api_host: string;\n /** The claim URL minted at provisioning (single-use); shown by `glass claim`. */\n claim_url?: string;\n}\n\nexport interface Credentials {\n /** provisioning/agent tokens keyed by project_id. */\n tokens: Record<string, string>;\n /** Clerk session from `glass login` (device flow). */\n session?: string;\n}\n\nconst PROJECT_FILE = join(process.cwd(), 'glass.json');\nconst CRED_FILE = join(homedir(), '.glass', 'credentials.json');\n\n/**\n * Read the project pointer, with environment overrides layered on top\n * (`CLI.md` §2): `GLASS_PROJECT` selects/overrides the project id and\n * `GLASS_INGEST_KEY` the publishable key — so CI can run without a committed\n * `glass.json`. If neither the file nor `GLASS_PROJECT` is present, returns null.\n */\nexport function readProject(): ProjectFile | null {\n let file: ProjectFile | null = null;\n try {\n file = JSON.parse(readFileSync(PROJECT_FILE, 'utf8')) as ProjectFile;\n } catch {\n file = null;\n }\n\n const envProject = process.env.GLASS_PROJECT;\n const envIngest = process.env.GLASS_INGEST_KEY;\n if (!file && !envProject) return null;\n\n return {\n project_id: envProject ?? file?.project_id ?? '',\n ingest_key: envIngest ?? file?.ingest_key ?? '',\n ingest_host: process.env.GLASS_INGEST_HOST ?? file?.ingest_host ?? 'https://in.glass.dev',\n api_host: process.env.GLASS_API_HOST ?? file?.api_host ?? 'https://api.glass.dev',\n ...(file?.claim_url ? { claim_url: file.claim_url } : {}),\n };\n}\n\nexport function writeProject(p: ProjectFile): void {\n writeFileSync(PROJECT_FILE, `${JSON.stringify(p, null, 2)}\\n`);\n}\n\nexport function readCredentials(): Credentials {\n try {\n return JSON.parse(readFileSync(CRED_FILE, 'utf8')) as Credentials;\n } catch {\n return { tokens: {} };\n }\n}\n\nexport function writeCredentials(c: Credentials): void {\n mkdirSync(dirname(CRED_FILE), { recursive: true, mode: 0o700 });\n writeFileSync(CRED_FILE, `${JSON.stringify(c, null, 2)}\\n`, { mode: 0o600 });\n chmodSync(CRED_FILE, 0o600); // enforce even if the file pre-existed\n}\n\nexport function tokenFor(projectId: string): string | undefined {\n return readCredentials().tokens[projectId];\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","dirname","readFileSync","dirname","writeFileSync","query"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands.ts","../src/output.ts","../src/api.ts","../src/config.ts"],"sourcesContent":["import { parseArgs } from 'node:util';\nimport * as cmd from './commands.js';\nimport { ExitCode, fail, setFormat, setJsonMode } from './output.js';\n\n/**\n * `glass` — the agent-friendly terminal surface (`CLI.md`). Setup in one command\n * (`glass init`, no signup), then the same typed query primitives the dashboard\n * uses. `--json` + stable exit codes make it scriptable by humans and agents.\n */\n\nconst HELP = `glass <command> [options]\n\nSetup\n init [--force] Provision a project with no signup (writes glass.json)\n install [--framework F] [--no-install] [--registry URL] [--force] Install + wire the SDK\n status Show whether events are being received\n doctor Diagnose setup problems\n claim Get the claim URL to keep your data\n login [--token T] Store a Clerk session for human-scoped access\n logout Clear the stored session\n whoami Show the current principal (GET /v1/me)\n link --project ID Point glass.json at another project you can access\n\nQuery (all accept --last 7d, --json, --confirm, --max-scan <MB>)\n trend --event E [--bucket day] [--breakdown B] [--aggregate count|unique_users|sum]\n funnel --steps a,b,c | --id <def> [--window 1d]\n retention --id <def> --return E [--periods 8]\n segment --group-by a,b [--event E]\n errors\n replays [--limit 50]\n query --query '<json>' | -f <file> | (piped stdin)\n\nManage\n define --file spec.json | --kind funnel --steps a,b | --list\n tokens list | create [--budget-r2sql 1GB/day] | revoke <id>\n link <project-id>\n\nGlobal: --json --csv --ndjson --api-host --ingest-host\n --max-scan <500MB|1GB> --yes/--no-input (auth: --token | GLASS_TOKEN)\nEnv: GLASS_TOKEN GLASS_PROJECT GLASS_INGEST_KEY GLASS_API_HOST GLASS_INGEST_HOST`;\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n const command = argv[0];\n const sub = argv[1] && !argv[1].startsWith('-') ? argv[1] : undefined;\n\n const { values, positionals } = parseArgs({\n args: argv.slice(1),\n allowPositionals: true,\n options: {\n json: { type: 'boolean' },\n csv: { type: 'boolean' },\n ndjson: { type: 'boolean' },\n confirm: { type: 'boolean' },\n reissue: { type: 'boolean' },\n force: { type: 'boolean' },\n 'no-install': { type: 'boolean' },\n yes: { type: 'boolean' },\n 'no-input': { type: 'boolean' },\n list: { type: 'boolean' },\n 'max-scan': { type: 'string' },\n last: { type: 'string' },\n event: { type: 'string' },\n bucket: { type: 'string' },\n breakdown: { type: 'string' },\n aggregate: { type: 'string' },\n steps: { type: 'string' },\n window: { type: 'string' },\n scope: { type: 'string' },\n kind: { type: 'string' },\n id: { type: 'string' },\n return: { type: 'string' },\n periods: { type: 'string' },\n 'group-by': { type: 'string' },\n group: { type: 'string' },\n status: { type: 'string' },\n limit: { type: 'string' },\n user: { type: 'string' },\n query: { type: 'string' },\n file: { type: 'string', short: 'f' },\n name: { type: 'string' },\n role: { type: 'string' },\n framework: { type: 'string' },\n project: { type: 'string' },\n 'budget-r2sql': { type: 'string' },\n token: { type: 'string' },\n 'api-host': { type: 'string' },\n 'ingest-host': { type: 'string' },\n registry: { type: 'string' },\n },\n });\n\n const flags = values as Record<string, string | boolean>;\n if (flags.json) setJsonMode(true);\n else if (flags.ndjson) setFormat('ndjson');\n else if (flags.csv) setFormat('csv');\n\n switch (command) {\n case 'init':\n return void (await cmd.init(flags));\n case 'install':\n return void cmd.install(flags);\n case 'status':\n return void (await cmd.status(flags));\n case 'doctor':\n return void (await cmd.doctor(flags));\n case 'claim':\n return void (await cmd.claim(flags));\n case 'login':\n return void (await cmd.login(flags));\n case 'logout':\n return void cmd.logout(flags);\n case 'whoami':\n return void (await cmd.whoami(flags));\n case 'link':\n return void (await cmd.link(flags, positionals));\n case 'trend':\n return void (await cmd.trend(flags));\n case 'funnel':\n return void (await cmd.funnel(flags));\n case 'retention':\n return void (await cmd.retention(flags));\n case 'segment':\n return void (await cmd.segment(flags));\n case 'errors':\n return void (await cmd.errors(flags));\n case 'replays':\n return void (await cmd.replays(flags));\n case 'query':\n return void (await cmd.query(flags));\n case 'define':\n return void (await cmd.define(flags));\n case 'tokens':\n // positionals[0] is the sub-action; pass the remainder (e.g. token id).\n return void (await cmd.tokens(sub ?? 'list', flags, positionals.slice(1)));\n case 'help':\n case undefined:\n case '--help':\n case '-h':\n process.stdout.write(`${HELP}\\n`);\n process.exit(ExitCode.OK);\n break;\n default:\n fail(ExitCode.USAGE, `unknown command: ${command}\\n\\n${HELP}`);\n }\n}\n\nmain().catch((e) => fail(ExitCode.ERROR, String(e?.message ?? e)));\n","import { spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport type { GlassQuery } from '@glassanalytics/core';\nimport { Api } from './api.js';\nimport {\n DEFAULT_API_HOST,\n DEFAULT_INGEST_HOST,\n readCredentials,\n readProject,\n tokenFor,\n writeCredentials,\n writeProject,\n} from './config.js';\nimport { ExitCode, fail, ok, parseBytes } from './output.js';\n\ntype Flags = Record<string, string | boolean>;\n\nfunction requireProject() {\n const project = readProject();\n if (!project) fail(ExitCode.USAGE, 'no glass.json found — run `glass init` first');\n return project;\n}\n\n/**\n * Token precedence (`CLI.md` §2): explicit --token > GLASS_TOKEN env > stored\n * per-project token > stored Clerk session. Lets CI inject a token without\n * touching the filesystem.\n */\nfunction resolveToken(flags: Flags, projectId?: string): string | undefined {\n if (typeof flags.token === 'string' && flags.token) return flags.token;\n if (process.env.GLASS_TOKEN) return process.env.GLASS_TOKEN;\n if (projectId) {\n const t = tokenFor(projectId);\n if (t) return t;\n }\n return readCredentials().session;\n}\n\nfunction authedApi(flags: Flags = {}) {\n const project = requireProject();\n const token = resolveToken(flags, project.project_id);\n if (!token) fail(ExitCode.AUTH, 'no credentials — run `glass init`, `glass login`, or set GLASS_TOKEN');\n return { api: new Api(project.api_host, token, project.project_id), project };\n}\n\n// --- glass init (zero-auth provisioning) -------------------------------------\n\nexport async function init(flags: Flags): Promise<never> {\n // Idempotent: don't re-provision if a project is already linked here.\n const existing = readProject();\n if (existing && flags.force !== true) {\n return ok({ project_id: existing.project_id, linked: true }, () => {\n process.stdout.write(`Already linked to ${existing.project_id} (glass.json present).\\n`);\n process.stdout.write('Use `glass init --force` to provision a new project, or `glass install` to wire the SDK.\\n');\n });\n }\n\n const apiHost = (flags['api-host'] as string) ?? DEFAULT_API_HOST;\n const api = new Api(apiHost);\n const res = await api.request<{\n project_id: string;\n ingest_key: string;\n provisioning_token: string;\n claim_url: string;\n }>('POST', '/v1/projects/provision', { name: (flags.name as string) ?? undefined });\n\n writeProject({\n project_id: res.project_id,\n ingest_key: res.ingest_key,\n ingest_host: (flags['ingest-host'] as string) ?? DEFAULT_INGEST_HOST,\n api_host: apiHost,\n claim_url: res.claim_url,\n });\n const creds = readCredentials();\n creds.tokens[res.project_id] = res.provisioning_token;\n writeCredentials(creds);\n\n return ok(res, () => {\n process.stdout.write(`Created project ${res.project_id}\\n`);\n process.stdout.write('Wrote glass.json and ~/.glass/credentials.json (0600)\\n');\n process.stdout.write(`\\nClaim it to keep your data:\\n ${res.claim_url}\\n`);\n process.stdout.write('\\nNext: `glass install` to wire up the SDK.\\n');\n });\n}\n\n// --- glass install (framework detection) -------------------------------------\n\ntype Framework = 'next' | 'react' | 'vite' | 'node' | 'browser';\n\nfunction detectFramework(): Framework {\n try {\n const pkg = JSON.parse(readFileSync('package.json', 'utf8')) as { dependencies?: Record<string, string>; devDependencies?: Record<string, string> };\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps.next) return 'next';\n if (deps.express || deps.fastify || deps.hono) return 'node';\n if (deps.react) return 'react';\n if (deps.vite) return 'vite';\n } catch {\n /* no package.json — assume browser */\n }\n return 'browser';\n}\n\n/** Pick the package manager from the lockfile present in cwd. */\nfunction detectPackageManager(): { cmd: string; args: string[] } {\n if (existsSync('pnpm-lock.yaml')) return { cmd: 'pnpm', args: ['add'] };\n if (existsSync('yarn.lock')) return { cmd: 'yarn', args: ['add'] };\n if (existsSync('bun.lockb')) return { cmd: 'bun', args: ['add'] };\n return { cmd: 'npm', args: ['install'] };\n}\n\n/** Next App Router root layout, if this is an App Router project. */\nfunction findNextAppLayout(): string | null {\n for (const p of ['app/layout.tsx', 'app/layout.jsx', 'src/app/layout.tsx', 'src/app/layout.jsx']) {\n if (existsSync(p)) return p;\n }\n return null;\n}\n\n/** Bare-import entry points for non-App-Router client apps + Node servers. */\nfunction entryCandidates(framework: Framework): string[] {\n switch (framework) {\n case 'next': // Pages Router (App Router is handled separately).\n return ['pages/_app.tsx', 'pages/_app.jsx', 'src/pages/_app.tsx', 'src/pages/_app.jsx'];\n case 'react':\n case 'vite':\n return ['src/main.tsx', 'src/main.jsx', 'src/index.tsx', 'src/index.jsx', 'src/main.ts', 'src/index.ts'];\n case 'node':\n return ['src/index.ts', 'src/server.ts', 'index.ts', 'server.ts', 'src/main.ts'];\n default:\n return [];\n }\n}\n\n/** True if any candidate entry already wires Glass (idempotency guard). */\nfunction alreadyWired(): boolean {\n const files = [\n 'glass.ts',\n findNextAppLayout(),\n ...entryCandidates('next'),\n ...entryCandidates('react'),\n ].filter((f): f is string => !!f && existsSync(f));\n return files.some((f) => {\n try {\n return /@glassanalytics\\/(browser|node)|glass\\.init\\(|GlassProvider/.test(readFileSync(f, 'utf8'));\n } catch {\n return false;\n }\n });\n}\n\n/** The client provider component that actually initialises Glass in the browser. */\nconst NEXT_PROVIDER_SRC = `'use client';\n\nimport { useEffect, useRef } from 'react';\nimport glass from '@glassanalytics/browser';\n\nconst KEY = process.env.NEXT_PUBLIC_GLASS_PROJECT_KEY;\nconst INGEST_HOST = process.env.NEXT_PUBLIC_GLASS_INGEST_HOST;\nconst API_HOST = process.env.NEXT_PUBLIC_GLASS_API_HOST;\n\n/**\n * Initialises Glass on the client. The SDK auto-captures pageviews (incl. App\n * Router navigations), autocapture, errors and session replay after init().\n */\nexport function GlassProvider() {\n const started = useRef(false);\n useEffect(() => {\n if (started.current || !KEY || !INGEST_HOST) return;\n started.current = true;\n glass.init({ projectKey: KEY, ingestHost: INGEST_HOST, apiHost: API_HOST });\n }, []);\n return null;\n}\n\nexport default GlassProvider;\n`;\n\n/** Append missing KEY=VALUE lines to an env file; never clobbers existing values. */\nfunction upsertEnv(file: string, vars: Record<string, string>): { file: string; wrote: boolean } {\n let src = '';\n try {\n src = readFileSync(file, 'utf8');\n } catch {\n src = '';\n }\n let out = src;\n let changed = false;\n for (const [k, v] of Object.entries(vars)) {\n if (new RegExp(`^${k}=`, 'm').test(out)) continue; // respect an existing value\n if (out.length && !out.endsWith('\\n')) out += '\\n';\n out += `${k}=${v}\\n`;\n changed = true;\n }\n if (changed) writeFileSync(file, out);\n return { file, wrote: changed };\n}\n\n/**\n * Wire Glass into a Next App Router layout the ONLY way that runs client-side:\n * a `\"use client\"` provider component rendered inside <body>. A bare\n * side-effect import in a Server Component would never execute in the browser.\n */\nfunction wireNextAppRouter(layout: string): {\n provider: string;\n injectedImport: boolean;\n injectedRender: boolean;\n} {\n const providerFile = join(dirname(layout), 'glass-provider.tsx');\n if (!existsSync(providerFile)) writeFileSync(providerFile, NEXT_PROVIDER_SRC);\n\n let src = readFileSync(layout, 'utf8');\n const importLine = \"import { GlassProvider } from './glass-provider';\\n\";\n let injectedImport = false;\n if (!src.includes('./glass-provider')) {\n const directive = /^\\s*(['\"])use (client|server)\\1;?\\s*\\n/.exec(src);\n src = directive\n ? src.slice(0, directive[0].length) + importLine + src.slice(directive[0].length)\n : importLine + src;\n injectedImport = true;\n }\n\n let injectedRender = false;\n if (!src.includes('<GlassProvider')) {\n const bodyOpen = /(<body[^>]*>)/;\n if (bodyOpen.test(src)) {\n src = src.replace(bodyOpen, '$1\\n <GlassProvider />');\n injectedRender = true;\n }\n }\n\n writeFileSync(layout, src);\n return { provider: providerFile, injectedImport, injectedRender };\n}\n\n/**\n * Bare side-effect wiring for non-App-Router client apps (Pages Router, Vite,\n * CRA) and Node servers — a top-level `import './glass'` in the client/server\n * entry is correct there. Writes glass.ts (env-driven for the browser) if absent.\n */\nfunction wireBareImport(\n framework: Framework,\n project: { ingest_key: string; ingest_host: string; api_host: string },\n): { file: string; wroteFile: boolean; entry: string | null; injected: boolean } {\n const file = 'glass.ts';\n const code =\n framework === 'node'\n ? `import { GlassNode } from '@glassanalytics/node';\\n\\nexport const glass = new GlassNode({\\n projectKey: process.env.GLASS_INGEST_KEY ?? '${project.ingest_key}',\\n ingestHost: process.env.GLASS_INGEST_HOST ?? '${project.ingest_host}',\\n});\\n`\n : `import glass from '@glassanalytics/browser';\\n\\nglass.init({ projectKey: '${project.ingest_key}', ingestHost: '${project.ingest_host}', apiHost: '${project.api_host}' });\\n\\nexport default glass;\\n`;\n\n let wroteFile = false;\n if (!existsSync(file)) {\n writeFileSync(file, code);\n wroteFile = true;\n }\n\n for (const entry of entryCandidates(framework)) {\n if (!existsSync(entry)) continue;\n const src = readFileSync(entry, 'utf8');\n let spec = relative(dirname(entry), file).replace(/\\.(ts|tsx|js|jsx)$/, '');\n if (!spec.startsWith('.')) spec = `./${spec}`;\n if (src.includes(`'${spec}'`) || src.includes(`\"${spec}\"`)) return { file, wroteFile, entry, injected: false };\n const importLine = `import '${spec}';\\n`;\n const directive = /^\\s*(['\"])use (client|server)\\1;?\\s*\\n/.exec(src);\n const next = directive\n ? src.slice(0, directive[0].length) + importLine + src.slice(directive[0].length)\n : importLine + src;\n writeFileSync(entry, next);\n return { file, wroteFile, entry, injected: true };\n }\n return { file, wroteFile, entry: null, injected: false };\n}\n\nexport function install(flags: Flags): never {\n const project = requireProject();\n const framework = ((flags.framework as Framework) ?? detectFramework()) as Framework;\n const pkg = framework === 'node' ? '@glassanalytics/node' : '@glassanalytics/browser';\n\n // Idempotency: don't add a second, conflicting integration.\n if (alreadyWired() && flags.force !== true) {\n return ok({ framework, package: pkg, already_wired: true }, () => {\n process.stdout.write('Glass already appears to be wired in this project — nothing to do.\\n');\n process.stdout.write('Re-run with `--force` to wire it again anyway.\\n');\n });\n }\n\n // 1. Install the SDK dependency (fixed argv — never a shell string). A\n // `--registry` passthrough helps past enterprise/mirror registries.\n let installed = false;\n if (flags['no-install'] !== true) {\n const pm = detectPackageManager();\n const args = [...pm.args, pkg];\n if (typeof flags.registry === 'string' && flags.registry) args.push(`--registry=${flags.registry}`);\n const r = spawnSync(pm.cmd, args, { stdio: 'inherit' });\n installed = r.status === 0;\n if (!installed) {\n const detail = r.error ? String(r.error.message) : `exit ${r.status}`;\n return fail(\n ExitCode.ERROR,\n `failed to install ${pkg} via ${pm.cmd} (${detail}) — install it manually, or retry with \\`--registry https://registry.npmjs.org/\\` if you're behind a private registry`,\n );\n }\n }\n\n // 2. Wire it up the correct way for the detected framework.\n const appLayout = framework === 'next' ? findNextAppLayout() : null;\n\n if (appLayout) {\n const env = upsertEnv('.env.local', {\n NEXT_PUBLIC_GLASS_PROJECT_KEY: project.ingest_key,\n NEXT_PUBLIC_GLASS_INGEST_HOST: project.ingest_host,\n NEXT_PUBLIC_GLASS_API_HOST: project.api_host,\n });\n const w = wireNextAppRouter(appLayout);\n return ok(\n {\n framework: 'next-app',\n package: pkg,\n installed,\n provider: w.provider,\n env_file: env.file,\n wrote_env: env.wrote,\n layout: appLayout,\n injected: w.injectedImport,\n rendered: w.injectedRender,\n },\n () => {\n process.stdout.write('Detected: Next.js (App Router)\\n');\n process.stdout.write(installed ? `Installed ${pkg}.\\n` : `Skipped install — add ${pkg} yourself.\\n`);\n process.stdout.write(`Wrote ${w.provider}${env.wrote ? ` and Glass keys to ${env.file}` : ''}.\\n`);\n if (w.injectedRender) process.stdout.write(`Rendered <GlassProvider /> in ${appLayout}.\\n`);\n else process.stdout.write(`Couldn't auto-insert <GlassProvider /> — add it inside <body> in ${appLayout}.\\n`);\n },\n );\n }\n\n const w = wireBareImport(framework, project);\n return ok(\n { framework, package: pkg, installed, file: w.file, wrote_file: w.wroteFile, entry: w.entry, injected: w.injected },\n () => {\n process.stdout.write(`Detected: ${framework}${framework === 'next' ? ' (Pages Router)' : ''}\\n`);\n process.stdout.write(installed ? `Installed ${pkg}.\\n` : `Skipped install — add ${pkg} yourself.\\n`);\n process.stdout.write(w.wroteFile ? `Wrote ${w.file}.\\n` : `${w.file} already exists; left untouched.\\n`);\n if (w.injected && w.entry) process.stdout.write(`Wired ${w.file} into ${w.entry}.\\n`);\n else if (w.entry) process.stdout.write(`${w.entry} already imports ${w.file}.\\n`);\n else process.stdout.write(`No entry point found — import \"./${w.file.replace(/\\.(ts|tsx)$/, '')}\" in your app entry yourself.\\n`);\n },\n );\n}\n\n// --- status / doctor ----------------------------------------------------------\n\nexport async function status(flags: Flags): Promise<never> {\n const { api } = authedApi(flags);\n const res = await api.request('GET', '/v1/ingest/status');\n return ok(res);\n}\n\nexport async function doctor(flags: Flags): Promise<never> {\n const project = readProject();\n const checks: { name: string; ok: boolean; detail?: string }[] = [];\n checks.push({ name: 'glass.json present', ok: !!project });\n if (project) {\n const tok = resolveToken(flags, project.project_id);\n checks.push({ name: 'credentials present', ok: !!tok });\n try {\n const api = new Api(project.api_host, tok, project.project_id);\n const s = await api.request<{ receiving: boolean }>('GET', '/v1/ingest/status');\n checks.push({ name: 'events received', ok: s.receiving, detail: s.receiving ? undefined : 'no events in last 24h' });\n } catch {\n checks.push({ name: 'control plane reachable', ok: false });\n }\n }\n const allOk = checks.every((c) => c.ok);\n return allOk ? ok({ checks }) : fail(ExitCode.ERROR, 'doctor found problems', { checks });\n}\n\n// --- claim / login ------------------------------------------------------------\n\nexport async function claim(flags: Flags): Promise<never> {\n const project = requireProject();\n\n // Default: show the claim URL stored at `glass init` WITHOUT contacting the\n // control plane (so we never invalidate a link the user may already be using).\n if (flags.reissue !== true && project.claim_url) {\n return ok({ claim_url: project.claim_url, reissued: false }, () =>\n process.stdout.write(`Claim URL:\\n ${project.claim_url}\\n\\n(Use \\`glass claim --reissue\\` to mint a fresh single-use link.)\\n`),\n );\n }\n\n // `--reissue` (or no stored URL): mint a fresh single-use claim link.\n const token = tokenFor(project.project_id);\n if (!token) fail(ExitCode.AUTH, 'no provisioning token for this project');\n const api = new Api(project.api_host, token);\n const res = await api.request<{ claim_url: string }>('POST', `/v1/projects/${project.project_id}/claim-url`);\n // Persist the new link so subsequent `glass claim` shows it without reissuing.\n writeProject({ ...project, claim_url: res.claim_url });\n return ok({ ...res, reissued: true }, () => process.stdout.write(`Claim URL (reissued):\\n ${res.claim_url}\\n`));\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/**\n * `glass login` (`CLI.md` §2). `--token <session>` stores a session directly\n * (CI/non-interactive); otherwise runs the OAuth-style device flow: start a\n * grant, print the verification URL + short user-code, then poll until a\n * signed-in human approves it in the dashboard.\n */\nexport async function login(flags: Flags): Promise<never> {\n const apiHost = (flags['api-host'] as string) ?? readProject()?.api_host ?? DEFAULT_API_HOST;\n\n // Non-interactive path: store an explicitly-provided session.\n const provided = flags.token as string | undefined;\n if (provided) {\n const creds = readCredentials();\n creds.session = provided;\n writeCredentials(creds);\n return ok({ message: 'Logged in. Session stored in ~/.glass/credentials.json' }, () =>\n process.stdout.write('Logged in. Session stored in ~/.glass/credentials.json\\n'),\n );\n }\n\n // The device flow needs a human to approve in a browser; in non-interactive\n // mode (`--no-input`/`--yes`, or CI) fail fast and ask for a token instead.\n if (flags['no-input'] === true || flags.yes === true || (!process.stdin.isTTY && !process.stdout.isTTY)) {\n return fail(ExitCode.AUTH, 'login is interactive — pass `--token <session>` or set GLASS_TOKEN for non-interactive use');\n }\n\n // Device flow.\n const api = new Api(apiHost);\n const grant = await api.request<{\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete?: string;\n interval: number;\n expires_in: number;\n }>('POST', '/v1/cli/device/start');\n\n if (!flags.json) {\n process.stdout.write('\\nTo sign in, open:\\n');\n process.stdout.write(` ${grant.verification_uri_complete ?? grant.verification_uri}\\n\\n`);\n process.stdout.write(`and enter the code: ${grant.user_code}\\n\\n`);\n process.stdout.write('Waiting for approval…\\n');\n }\n\n const deadline = Date.now() + grant.expires_in * 1000;\n const intervalMs = Math.max(1, grant.interval) * 1000;\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n let resp: { session?: string; status?: string } | null = null;\n try {\n resp = await api.request<{ session?: string; status?: string }>('POST', '/v1/cli/device/token', {\n device_code: grant.device_code,\n });\n } catch {\n // 202 (pending) / 410 (expired) surface as request errors depending on the\n // client; treat transient failures as \"keep polling\" until the deadline.\n continue;\n }\n if (resp?.session) {\n const creds = readCredentials();\n creds.session = resp.session;\n writeCredentials(creds);\n return ok({ message: 'Logged in. Session stored in ~/.glass/credentials.json' }, () =>\n process.stdout.write('\\nLogged in. Session stored in ~/.glass/credentials.json\\n'),\n );\n }\n }\n return fail(ExitCode.AUTH, 'device authorization timed out — run `glass login` again');\n}\n\nexport function logout(_flags: Flags): never {\n const creds = readCredentials();\n creds.session = undefined;\n writeCredentials(creds);\n return ok({ message: 'Logged out. Cleared stored session.' }, () => process.stdout.write('Logged out.\\n'));\n}\n\nexport async function whoami(flags: Flags): Promise<never> {\n const project = readProject();\n const token = resolveToken(flags, project?.project_id);\n if (!token) fail(ExitCode.AUTH, 'not logged in — run `glass login` or set GLASS_TOKEN');\n const api = new Api(project?.api_host ?? (flags['api-host'] as string) ?? DEFAULT_API_HOST, token, project?.project_id);\n return ok(await api.request('GET', '/v1/me'));\n}\n\n/** Switch the active project in glass.json after verifying access. */\nexport async function link(flags: Flags, positionals: string[] = []): Promise<never> {\n // Accept both `glass link <id>` and `glass link --project <id>`.\n const projectId = (flags.project as string | undefined) ?? positionals[0];\n if (!projectId) fail(ExitCode.USAGE, 'link needs a project id: `glass link <id>`');\n const existing = readProject();\n const apiHost = (flags['api-host'] as string) ?? existing?.api_host ?? DEFAULT_API_HOST;\n const token = resolveToken(flags, projectId);\n if (!token) fail(ExitCode.AUTH, 'no credentials — run `glass login` or set GLASS_TOKEN');\n const api = new Api(apiHost, token, projectId);\n const proj = await api.request<{ id: string; publishableKey?: string; ingestHost?: string }>('GET', '/v1/project');\n writeProject({\n project_id: projectId,\n ingest_key: proj.publishableKey ?? existing?.ingest_key ?? '',\n ingest_host: proj.ingestHost ?? existing?.ingest_host ?? DEFAULT_INGEST_HOST,\n api_host: apiHost,\n });\n return ok({ linked: projectId }, () => process.stdout.write(`Linked glass.json to ${projectId}.\\n`));\n}\n\n// --- query + sugar ------------------------------------------------------------\n\n/**\n * `glass query` source resolution (`CLI.md` §4): a GlassQuery can come from\n * `--query '<json>'`, `--file/-f <path>`, or stdin (so it composes in a pipe:\n * `cat q.json | glass query --json`). Exactly one source is used, in that order.\n */\nexport async function query(flags: Flags): Promise<never> {\n let raw: string | undefined;\n if (typeof flags.query === 'string' && flags.query) raw = flags.query;\n else if (typeof flags.file === 'string' && flags.file) raw = readFileSync(flags.file, 'utf8');\n else if (!process.stdin.isTTY) raw = readFileSync(0, 'utf8').trim() || undefined;\n\n if (!raw) fail(ExitCode.USAGE, \"query needs --query '<json>', --file <path>, or piped stdin\");\n let parsed: GlassQuery;\n try {\n parsed = JSON.parse(raw) as GlassQuery;\n } catch (e) {\n return fail(ExitCode.USAGE, `query is not valid JSON: ${String((e as Error).message)}`);\n }\n return runQuery(parsed, flags);\n}\n\nexport async function runQuery(query: GlassQuery, flags: Flags): Promise<never> {\n const { api } = authedApi(flags);\n const body: Record<string, unknown> = { query, confirm: flags.confirm === true };\n if (flags['max-scan']) {\n // Accept a bare number (legacy: MB) or a unit string (`500MB`, `1GB`).\n const raw = String(flags['max-scan']);\n const bytes = /^[0-9]*\\.?[0-9]+$/.test(raw.trim())\n ? Number(raw) * 1024 * 1024\n : parseBytes(raw);\n if (bytes == null) fail(ExitCode.USAGE, `invalid --max-scan: ${raw} (try 500MB or 1GB)`);\n body.max_scan_bytes = bytes;\n }\n const res = await api.request('POST', '/v1/query', body);\n return ok(res);\n}\n\nfunction parseTime(flags: Flags) {\n return { last: (flags.last as string) ?? '7d' };\n}\n\nexport async function trend(flags: Flags): Promise<never> {\n return runQuery(\n {\n v: 1,\n kind: 'trend',\n time: parseTime(flags),\n measure: { aggregate: (flags.aggregate as 'count' | 'unique_users' | 'sum') ?? 'count', ...(flags.event ? { event: flags.event as string } : {}) },\n bucket: (flags.bucket as 'hour' | 'day' | 'week' | 'month') ?? 'day',\n ...(flags.breakdown ? { breakdown: flags.breakdown as string } : {}),\n },\n flags,\n );\n}\n\nexport async function funnel(flags: Flags): Promise<never> {\n const steps = String(flags.steps ?? '').split(',').map((e) => ({ event: e.trim() })).filter((s) => s.event);\n if (flags.id) return runQuery({ v: 1, kind: 'funnel', time: parseTime(flags), definition_id: flags.id as string }, flags);\n if (steps.length < 2) fail(ExitCode.USAGE, 'funnel needs --steps a,b,c (>=2) or --id <definition>');\n return runQuery({ v: 1, kind: 'funnel', time: parseTime(flags), steps, ...(flags.window ? { window: flags.window as string } : {}) }, flags);\n}\n\nexport async function retention(flags: Flags): Promise<never> {\n if (!flags.id) fail(ExitCode.USAGE, 'retention needs --id <definition>');\n return runQuery({ v: 1, kind: 'retention', time: parseTime(flags), definition_id: flags.id as string, return_event: (flags.return as string) ?? '$pageview', periods: Number(flags.periods ?? 8) }, flags);\n}\n\nexport async function segment(flags: Flags): Promise<never> {\n const groupBy = String(flags['group-by'] ?? '').split(',').map((s) => s.trim()).filter(Boolean);\n if (groupBy.length === 0) fail(ExitCode.USAGE, 'segment needs --group-by a,b');\n return runQuery({ v: 1, kind: 'segmentation', time: parseTime(flags), group_by: groupBy, ...(flags.event ? { event: flags.event as string } : {}) }, flags);\n}\n\nexport async function errors(flags: Flags): Promise<never> {\n return runQuery(\n {\n v: 1,\n kind: 'errors',\n time: parseTime(flags),\n limit: Number(flags.limit ?? 50),\n ...(flags.group ? { group: flags.group as string } : {}),\n ...(flags.status ? { status: flags.status as 'unresolved' | 'resolved' | 'ignored' } : {}),\n },\n flags,\n );\n}\n\nexport async function replays(flags: Flags): Promise<never> {\n return runQuery(\n {\n v: 1,\n kind: 'replays',\n time: parseTime(flags),\n limit: Number(flags.limit ?? 50),\n ...(flags.user ? { session_filters: [{ property: 'user_id', op: 'eq' as const, value: flags.user as string }] } : {}),\n },\n flags,\n );\n}\n\n// --- define / tokens ----------------------------------------------------------\n\nexport async function define(flags: Flags): Promise<never> {\n const { api } = authedApi(flags);\n\n // `define --list` — show existing definitions.\n if (flags.list) return ok(await api.request('GET', '/v1/definitions'));\n\n // Flag-based sugar: build a spec from flags without writing JSON.\n let spec: unknown;\n if (flags.kind === 'funnel') {\n const steps = String(flags.steps ?? '').split(',').map((e) => ({ event: e.trim() })).filter((s) => s.event);\n if (steps.length < 2) fail(ExitCode.USAGE, 'define --kind funnel needs --steps a,b,c (>=2)');\n spec = { v: 1, kind: 'funnel', name: (flags.name as string) ?? 'funnel', steps, scope: (flags.scope as string) ?? 'within-session', ...(flags.window ? { window: flags.window as string } : {}) };\n } else if (flags.kind === 'metric') {\n spec = { v: 1, kind: 'metric', name: (flags.name as string) ?? 'metric', aggregate: (flags.aggregate as string) ?? 'count', bucket: (flags.bucket as string) ?? 'day', ...(flags.event ? { event: flags.event as string } : {}) };\n } else {\n const raw = flags.file ? readFileSync(flags.file as string, 'utf8') : readFileSync(0, 'utf8');\n spec = JSON.parse(raw);\n }\n const res = await api.request('POST', '/v1/definitions', { spec });\n return ok(res);\n}\n\nexport async function tokens(action: string, flags: Flags, positionals: string[] = []): Promise<never> {\n const { api } = authedApi(flags);\n if (action === 'list') return ok(await api.request('GET', '/v1/tokens'));\n if (action === 'create') {\n let budget: number | undefined;\n if (flags['budget-r2sql']) {\n // Accept human units + an optional `/day` rate suffix (`1GB/day`).\n const parsed = parseBytes(String(flags['budget-r2sql']));\n if (parsed == null) fail(ExitCode.USAGE, `invalid --budget-r2sql: ${flags['budget-r2sql']} (try 1GB/day)`);\n budget = parsed;\n }\n const res = await api.request<{ id: string; token: string; prefix: string }>('POST', '/v1/tokens', {\n name: (flags.name as string) ?? 'agent',\n role: (flags.role as string) ?? 'read',\n ...(budget != null ? { budget_r2sql_bytes_per_day: budget } : {}),\n });\n // The secret is shown ONCE — print it verbatim (not through the redactor).\n return ok(res, () => {\n process.stdout.write(`Created token ${res.id}\\n\\n ${res.token}\\n\\nThis is shown once. Store it now (e.g. as GLASS_TOKEN).\\n`);\n });\n }\n if (action === 'revoke') {\n // Accept both `glass tokens revoke <id>` and `--id <id>`.\n const id = (flags.id as string | undefined) ?? positionals[0];\n if (!id) fail(ExitCode.USAGE, 'tokens revoke needs a token id: `glass tokens revoke <id>`');\n return ok(await api.request('DELETE', `/v1/tokens/${id}`));\n }\n return fail(ExitCode.USAGE, 'tokens <list|create|revoke>');\n}\n","/**\n * Stable, scriptable output (`CLI.md` §5). The CLI is built for agents as much\n * as humans: `--json` emits a uniform envelope and EXIT CODES are stable so a\n * caller can branch on them without parsing prose.\n */\n\nexport const ExitCode = {\n OK: 0,\n ERROR: 1,\n USAGE: 2,\n AUTH: 3,\n COST_GATE: 4,\n BUDGET: 5,\n} as const;\nexport type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];\n\ntype OutputFormat = 'human' | 'json' | 'csv' | 'ndjson';\nlet format: OutputFormat = 'human';\nexport function setFormat(f: OutputFormat): void {\n format = f;\n}\n/** Back-compat shim for `--json`. */\nexport function setJsonMode(on: boolean): void {\n if (on) format = 'json';\n}\n\n/** Mask token-like secrets in human output so they're never accidentally logged. */\nconst SECRET_RE =\n /\\b(glass_(?:sk|pt)_[A-Za-z0-9]+)\\b|\\b(eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,})\\b/g;\nexport function redact(text: string): string {\n return text.replace(SECRET_RE, (m) => `${m.slice(0, 12)}…<redacted>`);\n}\n\n/** Flatten an array of flat records to CSV; falls back to JSON for non-tabular data. */\nexport function toCsv(data: unknown): string | null {\n const rows = Array.isArray(data) ? data : extractRows(data);\n if (!rows || rows.length === 0) return null;\n const cols = [...new Set(rows.flatMap((r) => Object.keys(r)))];\n const esc = (v: unknown) => {\n const s = v == null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);\n return /[\",\\n]/.test(s) ? `\"${s.replace(/\"/g, '\"\"')}\"` : s;\n };\n return [\n cols.join(','),\n ...rows.map((r) => cols.map((c) => esc((r as Record<string, unknown>)[c])).join(',')),\n ].join('\\n');\n}\n\n/**\n * Parse a human byte budget into bytes (`CLI.md` §4): accepts plain numbers and\n * unit suffixes (`500MB`, `1GB`, `2 TiB`), with an optional `/day` rate suffix\n * that we strip (the API stores per-day budgets). Returns null on garbage.\n */\nconst BYTE_UNITS: Record<string, number> = {\n b: 1,\n kb: 1e3,\n mb: 1e6,\n gb: 1e9,\n tb: 1e12,\n kib: 1024,\n mib: 1024 ** 2,\n gib: 1024 ** 3,\n tib: 1024 ** 4,\n};\nexport function parseBytes(input: string): number | null {\n const s = input.trim().toLowerCase().replace(/\\/(day|d|hr|hour|h)$/, '');\n const m = /^([0-9]*\\.?[0-9]+)\\s*([a-z]+)?$/.exec(s);\n if (!m) return null;\n const value = Number(m[1]);\n if (!Number.isFinite(value)) return null;\n if (!m[2]) return value; // bare number = bytes\n const unit = BYTE_UNITS[m[2]];\n if (!unit) return null;\n return Math.round(value * unit);\n}\n\n/** Find the first array-of-objects field (e.g. {rows:[...]}, {issues:[...]}). */\nexport function extractRows(data: unknown): Record<string, unknown>[] | null {\n if (Array.isArray(data)) return data as Record<string, unknown>[];\n if (data && typeof data === 'object') {\n for (const v of Object.values(data)) {\n if (Array.isArray(v) && v.every((x) => x && typeof x === 'object'))\n return v as Record<string, unknown>[];\n }\n }\n return null;\n}\n\n/**\n * Render an array of flat records as an aligned ASCII table for human output.\n * Returns null when the data isn't tabular so callers can fall back to JSON.\n */\nexport function humanTable(data: unknown): string | null {\n const rows = extractRows(data);\n if (!rows || rows.length === 0) return null;\n const cols = [...new Set(rows.flatMap((r) => Object.keys(r)))];\n if (cols.length === 0) return null;\n const cell = (v: unknown) => {\n const s = v == null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);\n return redact(s).replace(/\\n/g, ' ');\n };\n const widths = cols.map((c) =>\n Math.max(c.length, ...rows.map((r) => cell((r as Record<string, unknown>)[c]).length)),\n );\n const line = (cells: string[]) => cells.map((s, i) => s.padEnd(widths[i] ?? 0)).join(' ').trimEnd();\n const header = line(cols);\n const sep = widths.map((w) => '-'.repeat(w)).join(' ');\n const body = rows.map((r) => line(cols.map((c) => cell((r as Record<string, unknown>)[c]))));\n return [header, sep, ...body].join('\\n');\n}\n\n/**\n * One-line provenance badge for a query result (`UI.md` ResultBadge parity):\n * surfaces whether a number is exact or sampled, and its source, so an agent\n * reading stdout knows how much to trust it. Returns null when not applicable.\n */\nexport function resultBadge(data: unknown): string | null {\n if (!data || typeof data !== 'object') return null;\n const d = data as Record<string, unknown>;\n if (typeof d.exact !== 'boolean' && !d.source && !d.sampled) return null;\n const parts: string[] = [];\n parts.push(d.exact === false ? 'sampled' : 'exact');\n if (d.source) parts.push(String(d.source));\n const sampled = d.sampled as { interval?: number } | undefined;\n if (sampled?.interval && sampled.interval > 1) parts.push(`1/${sampled.interval}`);\n return `[${parts.join(' · ')}]`;\n}\n\nexport function ok(data: unknown, human?: () => void): never {\n if (format === 'json') {\n process.stdout.write(`${JSON.stringify({ ok: true, data }, null, 2)}\\n`);\n } else if (format === 'ndjson') {\n const rows = extractRows(data) ?? [data];\n for (const r of rows) process.stdout.write(`${JSON.stringify(r)}\\n`);\n } else if (format === 'csv') {\n const csv = toCsv(data);\n process.stdout.write(csv ? `${csv}\\n` : `${JSON.stringify(data, null, 2)}\\n`);\n } else if (human) {\n human();\n } else {\n const table = humanTable(data);\n if (table) {\n const badge = resultBadge(data);\n process.stdout.write(`${table}\\n`);\n if (badge) process.stdout.write(`${badge}\\n`);\n } else {\n process.stdout.write(`${redact(JSON.stringify(data, null, 2))}\\n`);\n }\n }\n process.exit(ExitCode.OK);\n}\n\nexport function fail(code: ExitCodeValue, message: string, extra?: unknown): never {\n if (format === 'json' || format === 'ndjson') {\n process.stdout.write(\n `${JSON.stringify({ ok: false, error: { code, message, ...(extra ? { extra } : {}) } }, null, 2)}\\n`,\n );\n } else {\n process.stderr.write(`glass: ${message}\\n`);\n if (extra) process.stderr.write(`${redact(JSON.stringify(extra, null, 2))}\\n`);\n }\n process.exit(code);\n}\n","import { ExitCode, fail } from './output.js';\n\n/**\n * Thin control-plane client. Maps HTTP status to the CLI's stable exit codes so\n * `--json` callers (agents) can branch deterministically (`CLI.md` §5):\n * 401/403 → AUTH(3), 402 → COST_GATE(4), 429 → BUDGET(5).\n */\nexport class Api {\n constructor(\n private apiHost: string,\n private token?: string,\n private projectId?: string,\n ) {}\n\n async request<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (this.token) headers.Authorization = `Bearer ${this.token}`;\n if (this.projectId) headers['X-Glass-Project'] = this.projectId;\n\n const res = await fetch(`${this.apiHost}${path}`, {\n method,\n headers,\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n }).catch((e) =>\n fail(\n ExitCode.ERROR,\n `couldn't reach the Glass control plane at ${this.apiHost} (${String(\n (e as Error)?.message ?? e,\n )}) — check the host is correct/reachable, or pass --api-host <url> (or set GLASS_API_HOST)`,\n ),\n );\n\n const text = await res.text();\n const json = text ? (JSON.parse(text) as T) : ({} as T);\n\n if (res.status === 401 || res.status === 403) fail(ExitCode.AUTH, 'unauthorized', json);\n if (res.status === 402) fail(ExitCode.COST_GATE, 'cost gate: re-run with --confirm', json);\n if (res.status === 429) fail(ExitCode.BUDGET, 'budget or rate limit exceeded', json);\n if (!res.ok) fail(ExitCode.ERROR, `request failed (${res.status})`, json);\n return json;\n }\n}\n","import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n/**\n * Local config (`CLI.md` §2/§3). Project pointer in `glass.json` (committable,\n * contains only the write-only publishable key); SECRETS in\n * `~/.glass/credentials.json` with `0600` perms, never in the repo.\n */\n\nexport interface ProjectFile {\n project_id: string;\n ingest_key: string;\n ingest_host: string;\n api_host: string;\n /** The claim URL minted at provisioning (single-use); shown by `glass claim`. */\n claim_url?: string;\n}\n\nexport interface Credentials {\n /** provisioning/agent tokens keyed by project_id. */\n tokens: Record<string, string>;\n /** Clerk session from `glass login` (device flow). */\n session?: string;\n}\n\n/**\n * Default Glass hosts. These are the live production Workers; a bare\n * `glass init` / `install` works with no flags. Override per-invocation with\n * `--api-host` / `--ingest-host` or the `GLASS_API_HOST` / `GLASS_INGEST_HOST`\n * env vars (self-hosters point these at their own deployment).\n */\nexport const DEFAULT_API_HOST = 'https://glass-control-plane.isnit.workers.dev';\nexport const DEFAULT_INGEST_HOST = 'https://glass-ingest.isnit.workers.dev';\n\nconst PROJECT_FILE = join(process.cwd(), 'glass.json');\nconst CRED_FILE = join(homedir(), '.glass', 'credentials.json');\n\n/**\n * Read the project pointer, with environment overrides layered on top\n * (`CLI.md` §2): `GLASS_PROJECT` selects/overrides the project id and\n * `GLASS_INGEST_KEY` the publishable key — so CI can run without a committed\n * `glass.json`. If neither the file nor `GLASS_PROJECT` is present, returns null.\n */\nexport function readProject(): ProjectFile | null {\n let file: ProjectFile | null = null;\n try {\n file = JSON.parse(readFileSync(PROJECT_FILE, 'utf8')) as ProjectFile;\n } catch {\n file = null;\n }\n\n const envProject = process.env.GLASS_PROJECT;\n const envIngest = process.env.GLASS_INGEST_KEY;\n if (!file && !envProject) return null;\n\n return {\n project_id: envProject ?? file?.project_id ?? '',\n ingest_key: envIngest ?? file?.ingest_key ?? '',\n ingest_host: process.env.GLASS_INGEST_HOST ?? file?.ingest_host ?? DEFAULT_INGEST_HOST,\n api_host: process.env.GLASS_API_HOST ?? file?.api_host ?? DEFAULT_API_HOST,\n ...(file?.claim_url ? { claim_url: file.claim_url } : {}),\n };\n}\n\nexport function writeProject(p: ProjectFile): void {\n writeFileSync(PROJECT_FILE, `${JSON.stringify(p, null, 2)}\\n`);\n}\n\nexport function readCredentials(): Credentials {\n try {\n return JSON.parse(readFileSync(CRED_FILE, 'utf8')) as Credentials;\n } catch {\n return { tokens: {} };\n }\n}\n\nexport function writeCredentials(c: Credentials): void {\n mkdirSync(dirname(CRED_FILE), { recursive: true, mode: 0o700 });\n writeFileSync(CRED_FILE, `${JSON.stringify(c, null, 2)}\\n`, { mode: 0o600 });\n chmodSync(CRED_FILE, 0o600); // enforce even if the file pre-existed\n}\n\nexport function tokenFor(projectId: string): string | undefined {\n return readCredentials().tokens[projectId];\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","dirname","join","readFileSync","writeFileSync","join","dirname","w","query"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassanalytics/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Glass CLI — zero-auth provisioning, SDK install, and typed querying from the terminal. Agent-friendly: --json + stable exit codes.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",