@kopai/cli 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,7 +35,7 @@ kopai logs search --service my-api --limit 20
35
35
  kopai metrics discover
36
36
  ```
37
37
 
38
- By default connects to `http://localhost:8000/signals`.
38
+ By default connects to `http://localhost:8000`.
39
39
 
40
40
  ## Configuration
41
41
 
@@ -54,7 +54,7 @@ Create `.kopairc` in project root or home directory:
54
54
 
55
55
  ```json
56
56
  {
57
- "url": "https://your-kopai-server.com/signals",
57
+ "url": "https://your-kopai-server.com",
58
58
  "token": "your-token"
59
59
  }
60
60
  ```
@@ -154,6 +154,31 @@ kopai metrics search --type Sum --attr "host=server1"
154
154
 
155
155
  **Fields:** TimeUnix, StartTimeUnix, MetricType, MetricName, MetricDescription, MetricUnit, ServiceName, Value, Count, Sum, Min, Max, Attributes, ResourceAttributes, ScopeName, ScopeAttributes, Exemplars, BucketCounts, ExplicitBounds
156
156
 
157
+ ### dashboards
158
+
159
+ #### `dashboards schema`
160
+
161
+ Print the UI tree component schema (for AI agents).
162
+
163
+ ```bash
164
+ kopai dashboards schema
165
+ ```
166
+
167
+ #### `dashboards create`
168
+
169
+ Create a dashboard. Reads uiTree JSON from stdin.
170
+
171
+ ```bash
172
+ echo '{"uiTree":{...},"metadata":{}}' | kopai dashboards create --name "My Dashboard" --tree-version "0.5.0" --json
173
+ ```
174
+
175
+ **Required flags:**
176
+
177
+ - `--name <name>` - Dashboard display name
178
+ - `--tree-version <semver>` - UI tree version (e.g. `"0.5.0"`)
179
+
180
+ Stdin JSON should contain `uiTree` (required) and optionally `metadata`. If the JSON has no `uiTree` key, the entire object is treated as the uiTree.
181
+
157
182
  #### `metrics discover`
158
183
 
159
184
  List available metrics.
package/dist/index.mjs CHANGED
@@ -9,7 +9,7 @@ import { createInterface } from "node:readline";
9
9
  //#region src/config.ts
10
10
  const CONFIG_FILENAME = ".kopairc";
11
11
  const TOKEN_PREFIX_LENGTH = 10;
12
- const DEFAULT_URL$1 = "http://localhost:8000/signals";
12
+ const DEFAULT_URL$1 = "http://localhost:8000";
13
13
  /** Owner read+write only (rw-------). Used for files containing secrets. */
14
14
  const OWNER_READ_WRITE = 384;
15
15
  function loadConfigFile(path) {
@@ -73,11 +73,16 @@ function removeConfigToken(targetPath) {
73
73
  function withConnectionOptions(cmd) {
74
74
  return cmd.option("--url <url>", "API base URL").option("--token <token>", "Auth token").option("-c, --config <path>", "Config file path").option("--timeout <ms>", "Request timeout");
75
75
  }
76
- const DEFAULT_URL = "http://localhost:8000/signals";
77
- function createClient(opts) {
76
+ const DEFAULT_URL = "http://localhost:8000";
77
+ function resolveConnectionOpts(opts) {
78
78
  const fileConfig = loadConfig(opts.config);
79
- const url = opts.url ?? fileConfig.url ?? DEFAULT_URL;
80
- const token = opts.token ?? fileConfig.token;
79
+ return {
80
+ url: (opts.url ?? fileConfig.url ?? DEFAULT_URL).replace(/\/signals\/?$/, "").replace(/\/$/, ""),
81
+ token: opts.token ?? fileConfig.token
82
+ };
83
+ }
84
+ function createClient(opts) {
85
+ const { url, token } = resolveConnectionOpts(opts);
81
86
  const timeout = opts.timeout != null ? parseInt(String(opts.timeout), 10) : void 0;
82
87
  return new KopaiClient({
83
88
  baseUrl: url,
@@ -299,6 +304,60 @@ function collect(value, previous) {
299
304
  return previous.concat([value]);
300
305
  }
301
306
 
307
+ //#endregion
308
+ //#region src/commands/dashboards.ts
309
+ async function readStdin() {
310
+ const chunks = [];
311
+ for await (const chunk of process.stdin) chunks.push(chunk);
312
+ return Buffer.concat(chunks).toString("utf-8");
313
+ }
314
+ function createDashboardsCommand() {
315
+ const dashboards = new Command("dashboards").description("Manage dashboards");
316
+ withConnectionOptions(dashboards.command("schema").description("Print UI tree schema for AI agents")).action(async (opts) => {
317
+ try {
318
+ const { url, token } = resolveConnectionOpts(opts);
319
+ const headers = {};
320
+ if (token) headers["Authorization"] = `Bearer ${token}`;
321
+ const response = await fetch(`${url}/dashboards/schema`, { headers });
322
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
323
+ const schema = await response.text();
324
+ process.stdout.write(schema);
325
+ } catch (err) {
326
+ outputError(err, false);
327
+ process.exit(1);
328
+ }
329
+ });
330
+ withConnectionOptions(dashboards.command("create").description("Create a dashboard (reads uiTree JSON from stdin)").requiredOption("--name <name>", "Dashboard name").requiredOption("--tree-version <semver>", "UI tree version (semver)").option("-j, --json", "JSON output").option("-t, --table", "Table output").addHelpText("after", `
331
+ Example:
332
+ $ echo '{"uiTree":{"root":"s1","elements":{"s1":{"key":"s1","type":"Stack","props":{"direction":"vertical","gap":"md","align":null},"children":[],"parentKey":""}}}}' | kopai dashboards create --name "My Dashboard" --tree-version "0.5.0" --json`)).action(async (opts) => {
333
+ const isJson = opts.json ?? false;
334
+ try {
335
+ const client = createClient(opts);
336
+ const raw = await readStdin();
337
+ let body;
338
+ try {
339
+ body = JSON.parse(raw);
340
+ } catch (e) {
341
+ if (e instanceof SyntaxError) {
342
+ process.stderr.write(`Invalid JSON input: ${e.message}\n`);
343
+ process.exit(2);
344
+ }
345
+ throw e;
346
+ }
347
+ output(await client.createDashboard({
348
+ name: opts.name,
349
+ uiTreeVersion: opts.treeVersion,
350
+ uiTree: body.uiTree ?? body,
351
+ metadata: body.metadata
352
+ }), { format: detectFormat(opts.json, opts.table) });
353
+ } catch (err) {
354
+ outputError(err, isJson);
355
+ process.exit(1);
356
+ }
357
+ });
358
+ return dashboards;
359
+ }
360
+
302
361
  //#endregion
303
362
  //#region src/commands/login.ts
304
363
  function readTokenFromStdin() {
@@ -373,16 +432,16 @@ function createWhoamiCommand() {
373
432
 
374
433
  //#endregion
375
434
  //#region package.json
376
- var version = "0.6.0";
435
+ var version = "0.7.0";
377
436
 
378
437
  //#endregion
379
438
  //#region src/index.ts
380
439
  const program = new Command();
381
- program.name("@kopai/cli").description("|--k> kopai - Query OpenTelemetry data").version(version).addCommand(createTracesCommand()).addCommand(createLogsCommand()).addCommand(createMetricsCommand()).addCommand(createLoginCommand()).addCommand(createLogoutCommand()).addCommand(createWhoamiCommand()).addHelpText("after", `
440
+ program.name("@kopai/cli").description("|--k> kopai - Query OpenTelemetry data").version(version).addCommand(createTracesCommand()).addCommand(createLogsCommand()).addCommand(createMetricsCommand()).addCommand(createDashboardsCommand()).addCommand(createLoginCommand()).addCommand(createLogoutCommand()).addCommand(createWhoamiCommand()).addHelpText("after", `
382
441
  Examples:
383
442
  $ kopai traces search # localhost:8000 (default, for @kopai/app running locally)
384
- $ kopai traces search --url https://example.com/signals # remote instance
385
- $ kopai logs search --url https://example.com/signals --token kpi_… # with auth`);
443
+ $ kopai traces search --url https://example.com # remote instance
444
+ $ kopai logs search --url https://example.com --token kpi_… # with auth`);
386
445
  program.parse();
387
446
 
388
447
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["DEFAULT_URL","collect","collect","DEFAULT_URL","DEFAULT_URL","pkg.version"],"sources":["../src/config.ts","../src/client.ts","../src/output.ts","../src/commands/traces.ts","../src/commands/logs.ts","../src/commands/metrics.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../package.json","../src/index.ts"],"sourcesContent":["import { readFileSync, existsSync, writeFileSync, chmodSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport interface Config {\n url?: string;\n token?: string;\n}\n\nexport const CONFIG_FILENAME = \".kopairc\";\nexport const TOKEN_PREFIX_LENGTH = 10;\nexport const DEFAULT_URL = \"http://localhost:8000/signals\";\n\n/** Owner read+write only (rw-------). Used for files containing secrets. */\nconst OWNER_READ_WRITE = 0o600;\n\nfunction loadConfigFile(path: string): Config | null {\n if (!existsSync(path)) return null;\n try {\n const content = readFileSync(path, \"utf-8\");\n return JSON.parse(content) as Config;\n } catch {\n return null;\n }\n}\n\nexport function loadConfig(configPath?: string): Config {\n // Priority: --config flag > ./.kopairc > ~/.kopairc\n const paths = configPath\n ? [configPath]\n : [join(process.cwd(), CONFIG_FILENAME), join(homedir(), CONFIG_FILENAME)];\n\n for (const path of paths) {\n const config = loadConfigFile(path);\n if (config) return config;\n }\n\n return {};\n}\n\nexport function resolveConfigPath(global: boolean): string {\n return global\n ? join(homedir(), CONFIG_FILENAME)\n : join(process.cwd(), CONFIG_FILENAME);\n}\n\nexport function saveConfig(updates: Partial<Config>, targetPath: string): void {\n let existing: Config = {};\n if (existsSync(targetPath)) {\n try {\n const content = readFileSync(targetPath, \"utf-8\");\n existing = JSON.parse(content) as Config;\n } catch {\n // ignore parse errors, overwrite\n }\n }\n const merged = { ...existing, ...updates };\n writeFileSync(targetPath, JSON.stringify(merged, null, 2) + \"\\n\", {\n encoding: \"utf-8\",\n mode: OWNER_READ_WRITE,\n });\n chmodSync(targetPath, OWNER_READ_WRITE);\n}\n\nexport function removeConfigToken(targetPath: string): boolean {\n if (!existsSync(targetPath)) return false;\n try {\n const content = readFileSync(targetPath, \"utf-8\");\n const config = JSON.parse(content) as Config;\n if (!config.token) return false;\n delete config.token;\n writeFileSync(targetPath, JSON.stringify(config, null, 2) + \"\\n\", {\n encoding: \"utf-8\",\n mode: OWNER_READ_WRITE,\n });\n } catch {\n return false;\n }\n try {\n chmodSync(targetPath, OWNER_READ_WRITE);\n } catch {\n // chmod failure does not affect the successful token removal\n }\n return true;\n}\n","import type { Command } from \"commander\";\nimport { KopaiClient } from \"@kopai/sdk\";\nimport { loadConfig } from \"./config.js\";\n\nexport function withConnectionOptions<T extends Command>(cmd: T): T {\n return cmd\n .option(\"--url <url>\", \"API base URL\")\n .option(\"--token <token>\", \"Auth token\")\n .option(\"-c, --config <path>\", \"Config file path\")\n .option(\"--timeout <ms>\", \"Request timeout\") as T;\n}\n\nexport interface ClientOptions {\n config?: string;\n url?: string;\n token?: string;\n timeout?: number;\n}\n\nconst DEFAULT_URL = \"http://localhost:8000/signals\";\n\nexport function createClient(opts: ClientOptions): KopaiClient {\n const fileConfig = loadConfig(opts.config);\n\n const url = opts.url ?? fileConfig.url ?? DEFAULT_URL;\n const token = opts.token ?? fileConfig.token;\n\n const timeout =\n opts.timeout != null ? parseInt(String(opts.timeout), 10) : undefined;\n\n return new KopaiClient({\n baseUrl: url,\n token,\n timeout: Number.isNaN(timeout) ? undefined : timeout,\n });\n}\n\nexport function parseAttributes(attrs?: string[]): Record<string, string> {\n if (!attrs || attrs.length === 0) return {};\n const result: Record<string, string> = {};\n for (const attr of attrs) {\n const idx = attr.indexOf(\"=\");\n if (idx === -1) {\n console.error(`Invalid attribute format: ${attr}. Use key=value`);\n process.exit(2);\n }\n result[attr.slice(0, idx)] = attr.slice(idx + 1);\n }\n return result;\n}\n","export type OutputFormat = \"json\" | \"table\";\n\nexport interface OutputOptions {\n format: OutputFormat;\n fields?: string[];\n}\n\nexport function detectFormat(json?: boolean, table?: boolean): OutputFormat {\n if (json) return \"json\";\n if (table) return \"table\";\n // Default: table for TTY, JSON for pipes\n return process.stdout.isTTY ? \"table\" : \"json\";\n}\n\nfunction countAttributes(attr: { values: Record<string, string[]> }): number {\n return Object.keys(attr.values).length;\n}\n\nfunction filterFields<T extends object>(data: T, fields: string[]): Partial<T> {\n return Object.fromEntries(\n Object.entries(data).filter(([key]) => fields.includes(key))\n ) as Partial<T>;\n}\n\nexport function parseFields(fieldsStr?: string): string[] | undefined {\n if (!fieldsStr) return undefined;\n return fieldsStr.split(\",\").map((f) => f.trim());\n}\n\nfunction isMetricsDiscoverData(data: unknown[]): data is Array<{\n attributes: { values: Record<string, string[]> };\n resourceAttributes: { values: Record<string, string[]> };\n}> {\n return (\n data.length > 0 &&\n typeof data[0] === \"object\" &&\n data[0] !== null &&\n \"attributes\" in data[0] &&\n \"resourceAttributes\" in data[0] &&\n typeof (data[0] as Record<string, unknown>).attributes === \"object\" &&\n (data[0] as Record<string, unknown>).attributes !== null &&\n \"values\" in ((data[0] as Record<string, unknown>).attributes as object)\n );\n}\n\nexport function output<T>(data: T, opts: OutputOptions): void {\n let outputData = data;\n\n // Apply field filtering\n if (opts.fields && opts.fields.length > 0) {\n if (Array.isArray(data)) {\n outputData = data.map((item) =>\n typeof item === \"object\" && item !== null\n ? filterFields(item as object, opts.fields!)\n : item\n ) as T;\n } else if (typeof data === \"object\" && data !== null) {\n outputData = filterFields(data as object, opts.fields) as T;\n }\n }\n\n if (opts.format === \"json\") {\n console.log(JSON.stringify(outputData, null, 2));\n } else {\n if (Array.isArray(outputData)) {\n if (outputData.length === 0) {\n console.log(\"No results found.\");\n } else if (!opts.fields && isMetricsDiscoverData(outputData)) {\n const transformed = outputData.map((m) => ({\n name: m.name,\n type: m.type,\n unit: m.unit,\n description: m.description,\n attrs: countAttributes(m.attributes),\n resourceAttrs: countAttributes(m.resourceAttributes),\n }));\n console.table(transformed);\n console.log(\"\\nUse --json for full attribute details.\");\n } else {\n console.table(outputData);\n }\n } else {\n console.log(outputData);\n }\n }\n}\n\nexport function outputError(error: unknown, json: boolean): void {\n const err =\n error instanceof Error\n ? { name: error.name, message: error.message }\n : { message: String(error) };\n\n if (json) {\n console.error(JSON.stringify({ error: err }));\n } else {\n console.error(`Error: ${err.message}`);\n }\n}\n","import { Command } from \"commander\";\nimport {\n createClient,\n parseAttributes,\n withConnectionOptions,\n type ClientOptions,\n} from \"../client.js\";\nimport { detectFormat, output, outputError, parseFields } from \"../output.js\";\n\ninterface TracesGetOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n}\n\ninterface TracesSearchOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n limit?: string;\n traceId?: string;\n spanId?: string;\n parentSpanId?: string;\n service?: string;\n spanName?: string;\n spanKind?: string;\n statusCode?: string;\n scope?: string;\n timestampMin?: string;\n timestampMax?: string;\n durationMin?: string;\n durationMax?: string;\n spanAttr?: string[];\n resourceAttr?: string[];\n sort?: string;\n}\n\nexport function createTracesCommand(): Command {\n const traces = new Command(\"traces\").description(\"Query traces\");\n\n withConnectionOptions(\n traces\n .command(\"get\")\n .description(\"Get all spans for a trace by ID\")\n .argument(\"<traceId>\", \"Trace ID\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n ).action(async (traceId: string, opts: TracesGetOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const spans = await client.getTrace(traceId);\n output(spans, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n withConnectionOptions(\n traces\n .command(\"search\")\n .description(\"Search traces\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n .option(\"-l, --limit <n>\", \"Max results\")\n .option(\"--trace-id <id>\", \"Filter by trace ID\")\n .option(\"--span-id <id>\", \"Filter by span ID\")\n .option(\"--parent-span-id <id>\", \"Filter by parent span ID\")\n .option(\"-s, --service <name>\", \"Filter by service name\")\n .option(\"--span-name <name>\", \"Filter by span name\")\n .option(\"--span-kind <kind>\", \"Filter by span kind\")\n .option(\"--status-code <code>\", \"Filter by status code\")\n .option(\"--scope <name>\", \"Filter by scope name\")\n .option(\"--timestamp-min <ns>\", \"Min timestamp (nanoseconds)\")\n .option(\"--timestamp-max <ns>\", \"Max timestamp (nanoseconds)\")\n .option(\"--duration-min <ns>\", \"Min duration (nanoseconds)\")\n .option(\"--duration-max <ns>\", \"Max duration (nanoseconds)\")\n .option(\n \"--span-attr <key=value>\",\n \"Span attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--resource-attr <key=value>\",\n \"Resource attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\"--sort <order>\", \"Sort order (ASC|DESC)\")\n ).action(async (opts: TracesSearchOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n\n const filter = {\n traceId: opts.traceId,\n spanId: opts.spanId,\n parentSpanId: opts.parentSpanId,\n serviceName: opts.service,\n spanName: opts.spanName,\n spanKind: opts.spanKind,\n statusCode: opts.statusCode,\n scopeName: opts.scope,\n timestampMin: opts.timestampMin,\n timestampMax: opts.timestampMax,\n durationMin: opts.durationMin,\n durationMax: opts.durationMax,\n spanAttributes: parseAttributes(opts.spanAttr),\n resourceAttributes: parseAttributes(opts.resourceAttr),\n limit,\n sortOrder: opts.sort as \"ASC\" | \"DESC\" | undefined,\n };\n\n const result = await client.searchTracesPage(filter);\n output(result.data, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n return traces;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return previous.concat([value]);\n}\n","import { Command } from \"commander\";\nimport {\n createClient,\n parseAttributes,\n withConnectionOptions,\n type ClientOptions,\n} from \"../client.js\";\nimport { detectFormat, output, outputError, parseFields } from \"../output.js\";\n\ninterface LogsSearchOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n limit?: string;\n traceId?: string;\n spanId?: string;\n service?: string;\n scope?: string;\n severityText?: string;\n severityMin?: string;\n severityMax?: string;\n body?: string;\n timestampMin?: string;\n timestampMax?: string;\n logAttr?: string[];\n resourceAttr?: string[];\n scopeAttr?: string[];\n sort?: string;\n}\n\nexport function createLogsCommand(): Command {\n const logs = new Command(\"logs\").description(\"Query logs\");\n\n withConnectionOptions(\n logs\n .command(\"search\")\n .description(\"Search logs\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n .option(\"-l, --limit <n>\", \"Max results\")\n .option(\"--trace-id <id>\", \"Filter by trace ID\")\n .option(\"--span-id <id>\", \"Filter by span ID\")\n .option(\"-s, --service <name>\", \"Filter by service name\")\n .option(\"--scope <name>\", \"Filter by scope name\")\n .option(\"--severity-text <level>\", \"Filter by severity text\")\n .option(\"--severity-min <n>\", \"Min severity number\")\n .option(\"--severity-max <n>\", \"Max severity number\")\n .option(\"-b, --body <text>\", \"Filter by body contains\")\n .option(\"--timestamp-min <ns>\", \"Min timestamp (nanoseconds)\")\n .option(\"--timestamp-max <ns>\", \"Max timestamp (nanoseconds)\")\n .option(\n \"--log-attr <key=value>\",\n \"Log attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--resource-attr <key=value>\",\n \"Resource attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--scope-attr <key=value>\",\n \"Scope attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\"--sort <order>\", \"Sort order (ASC|DESC)\")\n ).action(async (opts: LogsSearchOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n\n const filter = {\n traceId: opts.traceId,\n spanId: opts.spanId,\n serviceName: opts.service,\n scopeName: opts.scope,\n severityText: opts.severityText,\n severityNumberMin: opts.severityMin\n ? parseInt(opts.severityMin, 10)\n : undefined,\n severityNumberMax: opts.severityMax\n ? parseInt(opts.severityMax, 10)\n : undefined,\n bodyContains: opts.body,\n timestampMin: opts.timestampMin,\n timestampMax: opts.timestampMax,\n logAttributes: parseAttributes(opts.logAttr),\n resourceAttributes: parseAttributes(opts.resourceAttr),\n scopeAttributes: parseAttributes(opts.scopeAttr),\n limit,\n sortOrder: opts.sort as \"ASC\" | \"DESC\" | undefined,\n };\n\n const result = await client.searchLogsPage(filter);\n output(result.data, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n return logs;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return previous.concat([value]);\n}\n","import { Command } from \"commander\";\nimport {\n createClient,\n parseAttributes,\n withConnectionOptions,\n type ClientOptions,\n} from \"../client.js\";\nimport { detectFormat, output, outputError, parseFields } from \"../output.js\";\n\ninterface MetricsSearchOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n limit?: string;\n type: string;\n name?: string;\n service?: string;\n scope?: string;\n timeMin?: string;\n timeMax?: string;\n attr?: string[];\n resourceAttr?: string[];\n scopeAttr?: string[];\n sort?: string;\n}\n\ninterface MetricsDiscoverOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n}\n\nexport function createMetricsCommand(): Command {\n const metrics = new Command(\"metrics\").description(\"Query metrics\");\n\n withConnectionOptions(\n metrics\n .command(\"search\")\n .description(\"Search metrics\")\n .requiredOption(\n \"--type <type>\",\n \"Metric type (Gauge|Sum|Histogram|ExponentialHistogram|Summary)\"\n )\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n .option(\"-l, --limit <n>\", \"Max results\")\n .option(\"-n, --name <name>\", \"Filter by metric name\")\n .option(\"-s, --service <name>\", \"Filter by service name\")\n .option(\"--scope <name>\", \"Filter by scope name\")\n .option(\"--time-min <ns>\", \"Min time (nanoseconds)\")\n .option(\"--time-max <ns>\", \"Max time (nanoseconds)\")\n .option(\n \"-a, --attr <key=value>\",\n \"Attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--resource-attr <key=value>\",\n \"Resource attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--scope-attr <key=value>\",\n \"Scope attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\"--sort <order>\", \"Sort order (ASC|DESC)\")\n ).action(async (opts: MetricsSearchOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n\n const filter = {\n metricType: opts.type as\n | \"Gauge\"\n | \"Sum\"\n | \"Histogram\"\n | \"ExponentialHistogram\"\n | \"Summary\",\n metricName: opts.name,\n serviceName: opts.service,\n scopeName: opts.scope,\n timeUnixMin: opts.timeMin,\n timeUnixMax: opts.timeMax,\n attributes: parseAttributes(opts.attr),\n resourceAttributes: parseAttributes(opts.resourceAttr),\n scopeAttributes: parseAttributes(opts.scopeAttr),\n limit,\n sortOrder: opts.sort as \"ASC\" | \"DESC\" | undefined,\n };\n\n const result = await client.searchMetricsPage(filter);\n output(result.data, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n withConnectionOptions(\n metrics\n .command(\"discover\")\n .description(\"List available metrics\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n ).action(async (opts: MetricsDiscoverOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const result = await client.discoverMetrics();\n output(result.metrics, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n return metrics;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return previous.concat([value]);\n}\n","import { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n saveConfig,\n resolveConfigPath,\n TOKEN_PREFIX_LENGTH,\n DEFAULT_URL,\n type Config,\n} from \"../config.js\";\n\nfunction readTokenFromStdin(): Promise<string> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n rl.question(\"Token: \", (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nexport function createLoginCommand(): Command {\n return new Command(\"login\")\n .description(\"Save an API token to .kopairc\")\n .option(\"--url <url>\", \"API base URL to save alongside token\")\n .option(\"--global\", \"Write to ~/.kopairc instead of ./.kopairc\")\n .action(async (opts: { url?: string; global?: boolean }) => {\n const token = await readTokenFromStdin();\n if (!token) {\n console.error(\"Token cannot be empty.\");\n process.exitCode = 2;\n return;\n }\n\n const updates: Partial<Config> = {\n token,\n url: opts.url ?? DEFAULT_URL,\n };\n\n const targetPath = resolveConfigPath(opts.global ?? false);\n saveConfig(updates, targetPath);\n\n // Warn if in a git repo and .kopairc is not gitignored\n if (!opts.global) {\n const gitDir = join(process.cwd(), \".git\");\n if (existsSync(gitDir)) {\n const gitignorePath = join(process.cwd(), \".gitignore\");\n let isIgnored = false;\n if (existsSync(gitignorePath)) {\n const content = readFileSync(gitignorePath, \"utf-8\");\n isIgnored = content\n .split(\"\\n\")\n .some((line) => line.trim() === \".kopairc\");\n }\n if (!isIgnored) {\n console.error(\n \"Warning: .kopairc is not in .gitignore. Add it to avoid committing secrets.\"\n );\n }\n }\n }\n\n console.log(\n `Logged in. Token: ${token.slice(0, TOKEN_PREFIX_LENGTH)}...`\n );\n });\n}\n","import { Command } from \"commander\";\nimport { removeConfigToken, resolveConfigPath } from \"../config.js\";\n\nexport function createLogoutCommand(): Command {\n return new Command(\"logout\")\n .description(\"Remove saved API token from .kopairc\")\n .option(\"--global\", \"Remove from ~/.kopairc instead of ./.kopairc\")\n .action((opts: { global?: boolean }) => {\n const targetPath = resolveConfigPath(opts.global ?? false);\n const removed = removeConfigToken(targetPath);\n\n if (removed) {\n console.log(\"Logged out.\");\n } else {\n console.log(\"Not logged in.\");\n }\n });\n}\n","import { Command } from \"commander\";\nimport { KopaiClient } from \"@kopai/sdk\";\nimport { loadConfig, TOKEN_PREFIX_LENGTH, DEFAULT_URL } from \"../config.js\";\n\nexport function createWhoamiCommand(): Command {\n return new Command(\"whoami\")\n .description(\"Show current authentication status\")\n .option(\"-c, --config <path>\", \"Config file path\")\n .action(async (opts: { config?: string }) => {\n const config = loadConfig(opts.config);\n\n if (!config.token) {\n console.log(\"Not logged in.\");\n return;\n }\n\n console.log(`Token: ${config.token.slice(0, TOKEN_PREFIX_LENGTH)}...`);\n console.log(`URL: ${config.url ?? `${DEFAULT_URL} (default)`}`);\n\n // Try to validate token against server\n try {\n const client = new KopaiClient({\n baseUrl: config.url ?? DEFAULT_URL,\n token: config.token,\n });\n await client.searchTracesPage({ limit: 1 });\n console.log(\"Token is valid.\");\n } catch (err: unknown) {\n const status =\n err && typeof err === \"object\" && \"status\" in err\n ? (err as { status: number }).status\n : undefined;\n if (status === 401) {\n console.log(\"Token is invalid or expired.\");\n } else {\n console.log(\"Could not reach server to validate token.\");\n }\n process.exitCode = 1;\n }\n });\n}\n","","#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport { createTracesCommand } from \"./commands/traces.js\";\nimport { createLogsCommand } from \"./commands/logs.js\";\nimport { createMetricsCommand } from \"./commands/metrics.js\";\nimport { createLoginCommand } from \"./commands/login.js\";\nimport { createLogoutCommand } from \"./commands/logout.js\";\nimport { createWhoamiCommand } from \"./commands/whoami.js\";\nimport pkg from \"../package.json\" with { type: \"json\" };\n\nconst program = new Command();\n\nprogram\n .name(\"@kopai/cli\")\n .description(\"|--k> kopai - Query OpenTelemetry data\")\n .version(pkg.version)\n .addCommand(createTracesCommand())\n .addCommand(createLogsCommand())\n .addCommand(createMetricsCommand())\n .addCommand(createLoginCommand())\n .addCommand(createLogoutCommand())\n .addCommand(createWhoamiCommand())\n .addHelpText(\n \"after\",\n `\nExamples:\n $ kopai traces search # localhost:8000 (default, for @kopai/app running locally)\n $ kopai traces search --url https://example.com/signals # remote instance\n $ kopai logs search --url https://example.com/signals --token kpi_… # with auth`\n );\n\nprogram.parse();\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["DEFAULT_URL","collect","collect","DEFAULT_URL","DEFAULT_URL","pkg.version"],"sources":["../src/config.ts","../src/client.ts","../src/output.ts","../src/commands/traces.ts","../src/commands/logs.ts","../src/commands/metrics.ts","../src/commands/dashboards.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../package.json","../src/index.ts"],"sourcesContent":["import { readFileSync, existsSync, writeFileSync, chmodSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport interface Config {\n url?: string;\n token?: string;\n}\n\nexport const CONFIG_FILENAME = \".kopairc\";\nexport const TOKEN_PREFIX_LENGTH = 10;\nexport const DEFAULT_URL = \"http://localhost:8000\";\n\n/** Owner read+write only (rw-------). Used for files containing secrets. */\nconst OWNER_READ_WRITE = 0o600;\n\nfunction loadConfigFile(path: string): Config | null {\n if (!existsSync(path)) return null;\n try {\n const content = readFileSync(path, \"utf-8\");\n return JSON.parse(content) as Config;\n } catch {\n return null;\n }\n}\n\nexport function loadConfig(configPath?: string): Config {\n // Priority: --config flag > ./.kopairc > ~/.kopairc\n const paths = configPath\n ? [configPath]\n : [join(process.cwd(), CONFIG_FILENAME), join(homedir(), CONFIG_FILENAME)];\n\n for (const path of paths) {\n const config = loadConfigFile(path);\n if (config) return config;\n }\n\n return {};\n}\n\nexport function resolveConfigPath(global: boolean): string {\n return global\n ? join(homedir(), CONFIG_FILENAME)\n : join(process.cwd(), CONFIG_FILENAME);\n}\n\nexport function saveConfig(updates: Partial<Config>, targetPath: string): void {\n let existing: Config = {};\n if (existsSync(targetPath)) {\n try {\n const content = readFileSync(targetPath, \"utf-8\");\n existing = JSON.parse(content) as Config;\n } catch {\n // ignore parse errors, overwrite\n }\n }\n const merged = { ...existing, ...updates };\n writeFileSync(targetPath, JSON.stringify(merged, null, 2) + \"\\n\", {\n encoding: \"utf-8\",\n mode: OWNER_READ_WRITE,\n });\n chmodSync(targetPath, OWNER_READ_WRITE);\n}\n\nexport function removeConfigToken(targetPath: string): boolean {\n if (!existsSync(targetPath)) return false;\n try {\n const content = readFileSync(targetPath, \"utf-8\");\n const config = JSON.parse(content) as Config;\n if (!config.token) return false;\n delete config.token;\n writeFileSync(targetPath, JSON.stringify(config, null, 2) + \"\\n\", {\n encoding: \"utf-8\",\n mode: OWNER_READ_WRITE,\n });\n } catch {\n return false;\n }\n try {\n chmodSync(targetPath, OWNER_READ_WRITE);\n } catch {\n // chmod failure does not affect the successful token removal\n }\n return true;\n}\n","import type { Command } from \"commander\";\nimport { KopaiClient } from \"@kopai/sdk\";\nimport { loadConfig } from \"./config.js\";\n\nexport function withConnectionOptions<T extends Command>(cmd: T): T {\n return cmd\n .option(\"--url <url>\", \"API base URL\")\n .option(\"--token <token>\", \"Auth token\")\n .option(\"-c, --config <path>\", \"Config file path\")\n .option(\"--timeout <ms>\", \"Request timeout\") as T;\n}\n\nexport interface ClientOptions {\n config?: string;\n url?: string;\n token?: string;\n timeout?: number;\n}\n\nconst DEFAULT_URL = \"http://localhost:8000\";\n\nexport interface ConnectionOpts {\n url: string;\n token: string | undefined;\n}\n\nexport function resolveConnectionOpts(opts: ClientOptions): ConnectionOpts {\n const fileConfig = loadConfig(opts.config);\n const raw = opts.url ?? fileConfig.url ?? DEFAULT_URL;\n const url = raw.replace(/\\/signals\\/?$/, \"\").replace(/\\/$/, \"\");\n return {\n url,\n token: opts.token ?? fileConfig.token,\n };\n}\n\nexport function createClient(opts: ClientOptions): KopaiClient {\n const { url, token } = resolveConnectionOpts(opts);\n\n const timeout =\n opts.timeout != null ? parseInt(String(opts.timeout), 10) : undefined;\n\n return new KopaiClient({\n baseUrl: url,\n token,\n timeout: Number.isNaN(timeout) ? undefined : timeout,\n });\n}\n\nexport function parseAttributes(attrs?: string[]): Record<string, string> {\n if (!attrs || attrs.length === 0) return {};\n const result: Record<string, string> = {};\n for (const attr of attrs) {\n const idx = attr.indexOf(\"=\");\n if (idx === -1) {\n console.error(`Invalid attribute format: ${attr}. Use key=value`);\n process.exit(2);\n }\n result[attr.slice(0, idx)] = attr.slice(idx + 1);\n }\n return result;\n}\n","export type OutputFormat = \"json\" | \"table\";\n\nexport interface OutputOptions {\n format: OutputFormat;\n fields?: string[];\n}\n\nexport function detectFormat(json?: boolean, table?: boolean): OutputFormat {\n if (json) return \"json\";\n if (table) return \"table\";\n // Default: table for TTY, JSON for pipes\n return process.stdout.isTTY ? \"table\" : \"json\";\n}\n\nfunction countAttributes(attr: { values: Record<string, string[]> }): number {\n return Object.keys(attr.values).length;\n}\n\nfunction filterFields<T extends object>(data: T, fields: string[]): Partial<T> {\n return Object.fromEntries(\n Object.entries(data).filter(([key]) => fields.includes(key))\n ) as Partial<T>;\n}\n\nexport function parseFields(fieldsStr?: string): string[] | undefined {\n if (!fieldsStr) return undefined;\n return fieldsStr.split(\",\").map((f) => f.trim());\n}\n\nfunction isMetricsDiscoverData(data: unknown[]): data is Array<{\n attributes: { values: Record<string, string[]> };\n resourceAttributes: { values: Record<string, string[]> };\n}> {\n return (\n data.length > 0 &&\n typeof data[0] === \"object\" &&\n data[0] !== null &&\n \"attributes\" in data[0] &&\n \"resourceAttributes\" in data[0] &&\n typeof (data[0] as Record<string, unknown>).attributes === \"object\" &&\n (data[0] as Record<string, unknown>).attributes !== null &&\n \"values\" in ((data[0] as Record<string, unknown>).attributes as object)\n );\n}\n\nexport function output<T>(data: T, opts: OutputOptions): void {\n let outputData = data;\n\n // Apply field filtering\n if (opts.fields && opts.fields.length > 0) {\n if (Array.isArray(data)) {\n outputData = data.map((item) =>\n typeof item === \"object\" && item !== null\n ? filterFields(item as object, opts.fields!)\n : item\n ) as T;\n } else if (typeof data === \"object\" && data !== null) {\n outputData = filterFields(data as object, opts.fields) as T;\n }\n }\n\n if (opts.format === \"json\") {\n console.log(JSON.stringify(outputData, null, 2));\n } else {\n if (Array.isArray(outputData)) {\n if (outputData.length === 0) {\n console.log(\"No results found.\");\n } else if (!opts.fields && isMetricsDiscoverData(outputData)) {\n const transformed = outputData.map((m) => ({\n name: m.name,\n type: m.type,\n unit: m.unit,\n description: m.description,\n attrs: countAttributes(m.attributes),\n resourceAttrs: countAttributes(m.resourceAttributes),\n }));\n console.table(transformed);\n console.log(\"\\nUse --json for full attribute details.\");\n } else {\n console.table(outputData);\n }\n } else {\n console.log(outputData);\n }\n }\n}\n\nexport function outputError(error: unknown, json: boolean): void {\n const err =\n error instanceof Error\n ? { name: error.name, message: error.message }\n : { message: String(error) };\n\n if (json) {\n console.error(JSON.stringify({ error: err }));\n } else {\n console.error(`Error: ${err.message}`);\n }\n}\n","import { Command } from \"commander\";\nimport {\n createClient,\n parseAttributes,\n withConnectionOptions,\n type ClientOptions,\n} from \"../client.js\";\nimport { detectFormat, output, outputError, parseFields } from \"../output.js\";\n\ninterface TracesGetOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n}\n\ninterface TracesSearchOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n limit?: string;\n traceId?: string;\n spanId?: string;\n parentSpanId?: string;\n service?: string;\n spanName?: string;\n spanKind?: string;\n statusCode?: string;\n scope?: string;\n timestampMin?: string;\n timestampMax?: string;\n durationMin?: string;\n durationMax?: string;\n spanAttr?: string[];\n resourceAttr?: string[];\n sort?: string;\n}\n\nexport function createTracesCommand(): Command {\n const traces = new Command(\"traces\").description(\"Query traces\");\n\n withConnectionOptions(\n traces\n .command(\"get\")\n .description(\"Get all spans for a trace by ID\")\n .argument(\"<traceId>\", \"Trace ID\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n ).action(async (traceId: string, opts: TracesGetOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const spans = await client.getTrace(traceId);\n output(spans, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n withConnectionOptions(\n traces\n .command(\"search\")\n .description(\"Search traces\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n .option(\"-l, --limit <n>\", \"Max results\")\n .option(\"--trace-id <id>\", \"Filter by trace ID\")\n .option(\"--span-id <id>\", \"Filter by span ID\")\n .option(\"--parent-span-id <id>\", \"Filter by parent span ID\")\n .option(\"-s, --service <name>\", \"Filter by service name\")\n .option(\"--span-name <name>\", \"Filter by span name\")\n .option(\"--span-kind <kind>\", \"Filter by span kind\")\n .option(\"--status-code <code>\", \"Filter by status code\")\n .option(\"--scope <name>\", \"Filter by scope name\")\n .option(\"--timestamp-min <ns>\", \"Min timestamp (nanoseconds)\")\n .option(\"--timestamp-max <ns>\", \"Max timestamp (nanoseconds)\")\n .option(\"--duration-min <ns>\", \"Min duration (nanoseconds)\")\n .option(\"--duration-max <ns>\", \"Max duration (nanoseconds)\")\n .option(\n \"--span-attr <key=value>\",\n \"Span attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--resource-attr <key=value>\",\n \"Resource attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\"--sort <order>\", \"Sort order (ASC|DESC)\")\n ).action(async (opts: TracesSearchOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n\n const filter = {\n traceId: opts.traceId,\n spanId: opts.spanId,\n parentSpanId: opts.parentSpanId,\n serviceName: opts.service,\n spanName: opts.spanName,\n spanKind: opts.spanKind,\n statusCode: opts.statusCode,\n scopeName: opts.scope,\n timestampMin: opts.timestampMin,\n timestampMax: opts.timestampMax,\n durationMin: opts.durationMin,\n durationMax: opts.durationMax,\n spanAttributes: parseAttributes(opts.spanAttr),\n resourceAttributes: parseAttributes(opts.resourceAttr),\n limit,\n sortOrder: opts.sort as \"ASC\" | \"DESC\" | undefined,\n };\n\n const result = await client.searchTracesPage(filter);\n output(result.data, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n return traces;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return previous.concat([value]);\n}\n","import { Command } from \"commander\";\nimport {\n createClient,\n parseAttributes,\n withConnectionOptions,\n type ClientOptions,\n} from \"../client.js\";\nimport { detectFormat, output, outputError, parseFields } from \"../output.js\";\n\ninterface LogsSearchOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n limit?: string;\n traceId?: string;\n spanId?: string;\n service?: string;\n scope?: string;\n severityText?: string;\n severityMin?: string;\n severityMax?: string;\n body?: string;\n timestampMin?: string;\n timestampMax?: string;\n logAttr?: string[];\n resourceAttr?: string[];\n scopeAttr?: string[];\n sort?: string;\n}\n\nexport function createLogsCommand(): Command {\n const logs = new Command(\"logs\").description(\"Query logs\");\n\n withConnectionOptions(\n logs\n .command(\"search\")\n .description(\"Search logs\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n .option(\"-l, --limit <n>\", \"Max results\")\n .option(\"--trace-id <id>\", \"Filter by trace ID\")\n .option(\"--span-id <id>\", \"Filter by span ID\")\n .option(\"-s, --service <name>\", \"Filter by service name\")\n .option(\"--scope <name>\", \"Filter by scope name\")\n .option(\"--severity-text <level>\", \"Filter by severity text\")\n .option(\"--severity-min <n>\", \"Min severity number\")\n .option(\"--severity-max <n>\", \"Max severity number\")\n .option(\"-b, --body <text>\", \"Filter by body contains\")\n .option(\"--timestamp-min <ns>\", \"Min timestamp (nanoseconds)\")\n .option(\"--timestamp-max <ns>\", \"Max timestamp (nanoseconds)\")\n .option(\n \"--log-attr <key=value>\",\n \"Log attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--resource-attr <key=value>\",\n \"Resource attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--scope-attr <key=value>\",\n \"Scope attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\"--sort <order>\", \"Sort order (ASC|DESC)\")\n ).action(async (opts: LogsSearchOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n\n const filter = {\n traceId: opts.traceId,\n spanId: opts.spanId,\n serviceName: opts.service,\n scopeName: opts.scope,\n severityText: opts.severityText,\n severityNumberMin: opts.severityMin\n ? parseInt(opts.severityMin, 10)\n : undefined,\n severityNumberMax: opts.severityMax\n ? parseInt(opts.severityMax, 10)\n : undefined,\n bodyContains: opts.body,\n timestampMin: opts.timestampMin,\n timestampMax: opts.timestampMax,\n logAttributes: parseAttributes(opts.logAttr),\n resourceAttributes: parseAttributes(opts.resourceAttr),\n scopeAttributes: parseAttributes(opts.scopeAttr),\n limit,\n sortOrder: opts.sort as \"ASC\" | \"DESC\" | undefined,\n };\n\n const result = await client.searchLogsPage(filter);\n output(result.data, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n return logs;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return previous.concat([value]);\n}\n","import { Command } from \"commander\";\nimport {\n createClient,\n parseAttributes,\n withConnectionOptions,\n type ClientOptions,\n} from \"../client.js\";\nimport { detectFormat, output, outputError, parseFields } from \"../output.js\";\n\ninterface MetricsSearchOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n limit?: string;\n type: string;\n name?: string;\n service?: string;\n scope?: string;\n timeMin?: string;\n timeMax?: string;\n attr?: string[];\n resourceAttr?: string[];\n scopeAttr?: string[];\n sort?: string;\n}\n\ninterface MetricsDiscoverOptions extends ClientOptions {\n json?: boolean;\n table?: boolean;\n fields?: string;\n}\n\nexport function createMetricsCommand(): Command {\n const metrics = new Command(\"metrics\").description(\"Query metrics\");\n\n withConnectionOptions(\n metrics\n .command(\"search\")\n .description(\"Search metrics\")\n .requiredOption(\n \"--type <type>\",\n \"Metric type (Gauge|Sum|Histogram|ExponentialHistogram|Summary)\"\n )\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n .option(\"-l, --limit <n>\", \"Max results\")\n .option(\"-n, --name <name>\", \"Filter by metric name\")\n .option(\"-s, --service <name>\", \"Filter by service name\")\n .option(\"--scope <name>\", \"Filter by scope name\")\n .option(\"--time-min <ns>\", \"Min time (nanoseconds)\")\n .option(\"--time-max <ns>\", \"Max time (nanoseconds)\")\n .option(\n \"-a, --attr <key=value>\",\n \"Attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--resource-attr <key=value>\",\n \"Resource attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\n \"--scope-attr <key=value>\",\n \"Scope attribute filter (repeatable)\",\n collect,\n []\n )\n .option(\"--sort <order>\", \"Sort order (ASC|DESC)\")\n ).action(async (opts: MetricsSearchOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n\n const filter = {\n metricType: opts.type as\n | \"Gauge\"\n | \"Sum\"\n | \"Histogram\"\n | \"ExponentialHistogram\"\n | \"Summary\",\n metricName: opts.name,\n serviceName: opts.service,\n scopeName: opts.scope,\n timeUnixMin: opts.timeMin,\n timeUnixMax: opts.timeMax,\n attributes: parseAttributes(opts.attr),\n resourceAttributes: parseAttributes(opts.resourceAttr),\n scopeAttributes: parseAttributes(opts.scopeAttr),\n limit,\n sortOrder: opts.sort as \"ASC\" | \"DESC\" | undefined,\n };\n\n const result = await client.searchMetricsPage(filter);\n output(result.data, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n withConnectionOptions(\n metrics\n .command(\"discover\")\n .description(\"List available metrics\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .option(\"-f, --fields <fields>\", \"Comma-separated fields to include\")\n ).action(async (opts: MetricsDiscoverOptions) => {\n const format = detectFormat(opts.json, opts.table);\n const fields = parseFields(opts.fields);\n try {\n const client = createClient(opts);\n const result = await client.discoverMetrics();\n output(result.metrics, { format, fields });\n } catch (err) {\n outputError(err, format === \"json\");\n process.exit(1);\n }\n });\n\n return metrics;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return previous.concat([value]);\n}\n","import { Command } from \"commander\";\nimport {\n createClient,\n resolveConnectionOpts,\n withConnectionOptions,\n type ClientOptions,\n} from \"../client.js\";\nimport { detectFormat, output, outputError } from \"../output.js\";\n\ninterface DashboardCreateOptions extends ClientOptions {\n name: string;\n treeVersion: string;\n json?: boolean;\n table?: boolean;\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString(\"utf-8\");\n}\n\nexport function createDashboardsCommand(): Command {\n const dashboards = new Command(\"dashboards\").description(\"Manage dashboards\");\n\n withConnectionOptions(\n dashboards\n .command(\"schema\")\n .description(\"Print UI tree schema for AI agents\")\n ).action(async (opts: ClientOptions) => {\n try {\n const { url, token } = resolveConnectionOpts(opts);\n const headers: Record<string, string> = {};\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n const response = await fetch(`${url}/dashboards/schema`, { headers });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n const schema = await response.text();\n process.stdout.write(schema);\n } catch (err) {\n outputError(err, false);\n process.exit(1);\n }\n });\n\n withConnectionOptions(\n dashboards\n .command(\"create\")\n .description(\"Create a dashboard (reads uiTree JSON from stdin)\")\n .requiredOption(\"--name <name>\", \"Dashboard name\")\n .requiredOption(\"--tree-version <semver>\", \"UI tree version (semver)\")\n .option(\"-j, --json\", \"JSON output\")\n .option(\"-t, --table\", \"Table output\")\n .addHelpText(\n \"after\",\n `\nExample:\n $ echo '{\"uiTree\":{\"root\":\"s1\",\"elements\":{\"s1\":{\"key\":\"s1\",\"type\":\"Stack\",\"props\":{\"direction\":\"vertical\",\"gap\":\"md\",\"align\":null},\"children\":[],\"parentKey\":\"\"}}}}' | kopai dashboards create --name \"My Dashboard\" --tree-version \"0.5.0\" --json`\n )\n ).action(async (opts: DashboardCreateOptions) => {\n const isJson = opts.json ?? false;\n try {\n const client = createClient(opts);\n const raw = await readStdin();\n let body: Record<string, unknown>;\n try {\n body = JSON.parse(raw) as Record<string, unknown>;\n } catch (e) {\n if (e instanceof SyntaxError) {\n process.stderr.write(`Invalid JSON input: ${e.message}\\n`);\n process.exit(2);\n }\n throw e;\n }\n\n const result = await client.createDashboard({\n name: opts.name,\n uiTreeVersion: opts.treeVersion,\n uiTree: (body.uiTree ?? body) as Record<string, unknown>,\n metadata: body.metadata as Record<string, unknown> | undefined,\n });\n\n const format = detectFormat(opts.json, opts.table);\n output(result, { format });\n } catch (err) {\n outputError(err, isJson);\n process.exit(1);\n }\n });\n\n return dashboards;\n}\n","import { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n saveConfig,\n resolveConfigPath,\n TOKEN_PREFIX_LENGTH,\n DEFAULT_URL,\n type Config,\n} from \"../config.js\";\n\nfunction readTokenFromStdin(): Promise<string> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n rl.question(\"Token: \", (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nexport function createLoginCommand(): Command {\n return new Command(\"login\")\n .description(\"Save an API token to .kopairc\")\n .option(\"--url <url>\", \"API base URL to save alongside token\")\n .option(\"--global\", \"Write to ~/.kopairc instead of ./.kopairc\")\n .action(async (opts: { url?: string; global?: boolean }) => {\n const token = await readTokenFromStdin();\n if (!token) {\n console.error(\"Token cannot be empty.\");\n process.exitCode = 2;\n return;\n }\n\n const updates: Partial<Config> = {\n token,\n url: opts.url ?? DEFAULT_URL,\n };\n\n const targetPath = resolveConfigPath(opts.global ?? false);\n saveConfig(updates, targetPath);\n\n // Warn if in a git repo and .kopairc is not gitignored\n if (!opts.global) {\n const gitDir = join(process.cwd(), \".git\");\n if (existsSync(gitDir)) {\n const gitignorePath = join(process.cwd(), \".gitignore\");\n let isIgnored = false;\n if (existsSync(gitignorePath)) {\n const content = readFileSync(gitignorePath, \"utf-8\");\n isIgnored = content\n .split(\"\\n\")\n .some((line) => line.trim() === \".kopairc\");\n }\n if (!isIgnored) {\n console.error(\n \"Warning: .kopairc is not in .gitignore. Add it to avoid committing secrets.\"\n );\n }\n }\n }\n\n console.log(\n `Logged in. Token: ${token.slice(0, TOKEN_PREFIX_LENGTH)}...`\n );\n });\n}\n","import { Command } from \"commander\";\nimport { removeConfigToken, resolveConfigPath } from \"../config.js\";\n\nexport function createLogoutCommand(): Command {\n return new Command(\"logout\")\n .description(\"Remove saved API token from .kopairc\")\n .option(\"--global\", \"Remove from ~/.kopairc instead of ./.kopairc\")\n .action((opts: { global?: boolean }) => {\n const targetPath = resolveConfigPath(opts.global ?? false);\n const removed = removeConfigToken(targetPath);\n\n if (removed) {\n console.log(\"Logged out.\");\n } else {\n console.log(\"Not logged in.\");\n }\n });\n}\n","import { Command } from \"commander\";\nimport { KopaiClient } from \"@kopai/sdk\";\nimport { loadConfig, TOKEN_PREFIX_LENGTH, DEFAULT_URL } from \"../config.js\";\n\nexport function createWhoamiCommand(): Command {\n return new Command(\"whoami\")\n .description(\"Show current authentication status\")\n .option(\"-c, --config <path>\", \"Config file path\")\n .action(async (opts: { config?: string }) => {\n const config = loadConfig(opts.config);\n\n if (!config.token) {\n console.log(\"Not logged in.\");\n return;\n }\n\n console.log(`Token: ${config.token.slice(0, TOKEN_PREFIX_LENGTH)}...`);\n console.log(`URL: ${config.url ?? `${DEFAULT_URL} (default)`}`);\n\n // Try to validate token against server\n try {\n const client = new KopaiClient({\n baseUrl: config.url ?? DEFAULT_URL,\n token: config.token,\n });\n await client.searchTracesPage({ limit: 1 });\n console.log(\"Token is valid.\");\n } catch (err: unknown) {\n const status =\n err && typeof err === \"object\" && \"status\" in err\n ? (err as { status: number }).status\n : undefined;\n if (status === 401) {\n console.log(\"Token is invalid or expired.\");\n } else {\n console.log(\"Could not reach server to validate token.\");\n }\n process.exitCode = 1;\n }\n });\n}\n","","#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport { createTracesCommand } from \"./commands/traces.js\";\nimport { createLogsCommand } from \"./commands/logs.js\";\nimport { createMetricsCommand } from \"./commands/metrics.js\";\nimport { createDashboardsCommand } from \"./commands/dashboards.js\";\nimport { createLoginCommand } from \"./commands/login.js\";\nimport { createLogoutCommand } from \"./commands/logout.js\";\nimport { createWhoamiCommand } from \"./commands/whoami.js\";\nimport pkg from \"../package.json\" with { type: \"json\" };\n\nconst program = new Command();\n\nprogram\n .name(\"@kopai/cli\")\n .description(\"|--k> kopai - Query OpenTelemetry data\")\n .version(pkg.version)\n .addCommand(createTracesCommand())\n .addCommand(createLogsCommand())\n .addCommand(createMetricsCommand())\n .addCommand(createDashboardsCommand())\n .addCommand(createLoginCommand())\n .addCommand(createLogoutCommand())\n .addCommand(createWhoamiCommand())\n .addHelpText(\n \"after\",\n `\nExamples:\n $ kopai traces search # localhost:8000 (default, for @kopai/app running locally)\n $ kopai traces search --url https://example.com # remote instance\n $ kopai logs search --url https://example.com --token kpi_… # with auth`\n );\n\nprogram.parse();\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kopai/cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "CLI for querying OpenTelemetry data from Kopai - traces, logs, and metrics",
5
5
  "keywords": [
6
6
  "opentelemetry",
@@ -36,8 +36,8 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "commander": "^14.0.3",
39
- "@kopai/core": "0.6.0",
40
- "@kopai/sdk": "0.3.1"
39
+ "@kopai/core": "0.7.0",
40
+ "@kopai/sdk": "0.4.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "tsdown": "^0.20.3",