2g 0.2.0 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"cli-chunk.js","sources":["../../src/commands/clean/index.ts","../../src/commands/shared.ts","../../src/commands/export/chromeTrace.ts","../../src/commands/export/opentelemetry.ts","../../src/commands/export/index.ts","../../src/commands/ps/index.ts","../../src/commands/tap/index.ts","../../src/cli.ts"],"sourcesContent":["import { parseArgs } from 'node:util';\n\nimport { cleanExitedSessionsSync, cleanStaleSessionsSync } from '../../clean';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\n\nexport function runCleanCli(args: string[] = []) {\n const options = parseCleanOptions(args);\n if (options.json) redirectConsoleToStderr();\n const removed = options.all\n ? cleanExitedSessionsSync()\n : cleanStaleSessionsSync();\n process.stdout.write(\n options.json\n ? `${JSON.stringify({ removed })}\\n`\n : `Removed ${removed} ${options.all ? 'exited' : 'stale'} sessions.\\n`\n );\n}\n\nfunction parseCleanOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n options: {\n all: { type: 'boolean', short: 'a' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n return {\n all: parsed.values.all === true,\n json: parsed.values.json === true,\n };\n}\n\nexport function printCleanHelp() {\n process.stdout.write(\n [\n 'Usage: 2g clean',\n '',\n 'Removes stale exited sessions past the retention window.',\n '',\n 'Options:',\n ' --all, -a Remove all exited sessions',\n ' --json',\n '',\n ].join('\\n')\n );\n}\n","import fs from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Readable } from 'node:stream';\nimport { parseArgs } from 'node:util';\n\nimport type { ListedSession } from '../tap';\nimport {\n compileEventFilter,\n matchesTapOptions,\n parseEventLine,\n parseSince,\n parseDuration as parseDurationValue,\n} from '../utils/eventFilter';\nimport type { ParsedEvent } from '../types';\n\nexport const tapArgOptions = {\n since: { type: 'string' },\n filter: { type: 'string', multiple: true },\n spans: { type: 'boolean' },\n debug: { type: 'boolean' },\n tail: { type: 'boolean' },\n} as const;\n\nexport interface SharedTapOptions {\n since?: string;\n filter?: string[];\n spans?: boolean;\n debug?: boolean;\n follow: boolean;\n}\n\nexport function formatSessionProcessName(session: ListedSession) {\n return session.version\n ? `${session.command} (v${session.version}, PID ${session.pid})`\n : `${session.command} (PID ${session.pid})`;\n}\n\nexport async function* readJsonlFile(file: string) {\n yield* readJsonlStream(fs.createReadStream(file, { encoding: 'utf8' }));\n}\n\nexport async function* readJsonlStdin() {\n yield* readJsonlStream(process.stdin);\n}\n\nexport async function* readJsonlStream(stream: Readable) {\n const rl = createInterface({ input: stream });\n for await (const line of rl) {\n if (!line) continue;\n // Lossless parse; the debug decision belongs to filterEvents\n const event = parseEventLine(line, { debug: true });\n if (event) yield event;\n }\n}\n\nexport async function* filterEvents(\n events: AsyncIterable<ParsedEvent> | Iterable<ParsedEvent>,\n options: Pick<SharedTapOptions, 'since' | 'filter' | 'spans' | 'debug'>\n) {\n const eventFilter = compileEventFilter(options.filter);\n const since = parseSince(options.since);\n for await (const event of events) {\n if (\n matchesTapOptions(\n event,\n {\n filter: options.filter,\n since,\n spans: options.spans,\n debug: options.debug,\n },\n eventFilter\n )\n )\n yield event;\n }\n}\n\nexport function parseSharedTapOptions(values: {\n since?: string;\n filter?: string[];\n spans?: boolean;\n debug?: boolean;\n tail?: boolean;\n}): SharedTapOptions {\n return {\n since: values.since,\n filter: values.filter\n ?.flatMap(value => value.split(','))\n .map(item => item.trim())\n .filter(Boolean),\n spans: values.spans === true,\n debug: values.debug === true,\n follow: values.tail === true,\n };\n}\n\nexport function parseHelp(args: string[]) {\n return (\n parseArgs({\n args,\n allowPositionals: true,\n strict: false,\n options: {\n help: { type: 'boolean', short: 'h' },\n },\n }).values.help === true\n );\n}\n\nexport function parseJsonFlag(args: string[]) {\n return parseArgs({\n args,\n allowPositionals: true,\n options: {\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n }).values;\n}\n\nexport function parseDuration(value?: string) {\n if (value == null || value === '') return undefined;\n const duration = parseDurationValue(value);\n if (duration == null) throw new Error(`Invalid duration: ${value}`);\n return duration;\n}\n","import type { ParsedEvent } from '../../types';\n\nexport interface ConvertToChromeTraceOptions {\n processName?: string;\n pid?: number;\n}\n\nexport interface TraceFile {\n traceEvents: TraceEvent[];\n metadata: {\n source: '2g';\n version: string;\n convertedAt: string;\n startTime: string;\n startTimestampMs: number;\n };\n}\n\nexport type TraceEvent = TraceComplete | TraceInstant | TraceMetadata;\n\nexport interface TraceComplete {\n ph: 'X';\n name: string;\n cat: string;\n pid: number;\n tid: number;\n ts: number;\n dur: number;\n args?: Record<string, unknown>;\n}\n\nexport interface TraceInstant {\n ph: 'i';\n name: string;\n cat: string;\n pid: number;\n tid: number;\n ts: number;\n s: 'g' | 'p' | 't';\n args?: Record<string, unknown>;\n}\n\nexport interface TraceMetadata {\n ph: 'M';\n name: 'process_name' | 'thread_name' | 'thread_sort_index';\n pid: number;\n tid?: number;\n args: Record<string, unknown>;\n}\n\nconst STRIP_SUFFIXES = [':started', ':done', ':failed'];\n\nexport async function convertToChromeTrace(\n events: Iterable<ParsedEvent> | AsyncIterable<ParsedEvent>,\n options: ConvertToChromeTraceOptions = {}\n): Promise<TraceFile> {\n const converter = new TraceConverter(options);\n for await (const event of events) {\n converter.add(event);\n }\n return converter.toTraceFile();\n}\n\ninterface PendingSpan {\n track: string;\n name: string;\n cat: string;\n ts: number;\n dur: number;\n args?: Record<string, unknown>;\n lane: number;\n}\n\ninterface PendingInstant {\n track: string;\n name: string;\n cat: string;\n ts: number;\n args?: Record<string, unknown>;\n}\n\ntype PendingEvent =\n | ({ kind: 'span' } & PendingSpan)\n | ({ kind: 'instant' } & PendingInstant);\n\nexport class TraceConverter {\n #pending: PendingEvent[] = [];\n #tracks = new Map<string, string>();\n #pid: number;\n #processName: string;\n #version = '0.1.0';\n\n constructor(options: ConvertToChromeTraceOptions = {}) {\n this.#pid = options.pid ?? 1;\n this.#processName = options.processName ?? '2g';\n }\n\n add(event: ParsedEvent) {\n if (event._e === 'root:init') {\n if (typeof event.version === 'string') {\n this.#version = event.version;\n if (this.#processName === '2g') {\n this.#processName = `2g (v${event.version})`;\n }\n }\n return;\n }\n\n const parsed = splitEventName(event._e);\n const track = this.#getTrack(\n parsed.category,\n typeof event._w === 'string' ? event._w : undefined\n );\n const args = extractArgs(event);\n\n if (typeof event._d === 'number') {\n this.#pending.push({\n kind: 'span',\n track,\n name: stripSuffix(parsed.name),\n cat: parsed.category,\n ts: Math.round((event._t - event._d) * 1000),\n dur: Math.round(event._d * 1000),\n args,\n lane: 0,\n });\n } else {\n this.#pending.push({\n kind: 'instant',\n track,\n name: stripSuffix(parsed.name),\n cat: parsed.category,\n ts: Math.round(event._t * 1000),\n args,\n });\n }\n }\n\n toTraceFile(): TraceFile {\n let base = this.#pending.length ? Infinity : 0;\n for (const event of this.#pending) {\n if (event.ts < base) base = event.ts;\n }\n const traceEvents: TraceEvent[] = [\n {\n ph: 'M',\n name: 'process_name',\n pid: this.#pid,\n args: { name: this.#processName },\n },\n ];\n\n const tids = new Map<string, number>();\n let nextTid = 1;\n for (const [track, displayName] of this.#tracks) {\n const spans: PendingSpan[] = [];\n for (const event of this.#pending) {\n if (event.kind === 'span' && event.track === track) spans.push(event);\n }\n const laneCount = Math.max(assignLanes(spans), 1);\n for (let lane = 0; lane < laneCount; lane++) {\n const tid = nextTid++;\n tids.set(`${track}\\0${lane}`, tid);\n traceEvents.push(\n {\n ph: 'M',\n name: 'thread_name',\n pid: this.#pid,\n tid,\n args: {\n name: lane === 0 ? displayName : `${displayName} #${lane + 1}`,\n },\n },\n {\n ph: 'M',\n name: 'thread_sort_index',\n pid: this.#pid,\n tid,\n args: { sort_index: tid },\n }\n );\n }\n }\n\n for (const event of this.#pending) {\n const lane = event.kind === 'span' ? event.lane : 0;\n const tid = tids.get(`${event.track}\\0${lane}`)!;\n if (event.kind === 'span') {\n traceEvents.push({\n ph: 'X',\n name: event.name,\n cat: event.cat,\n pid: this.#pid,\n tid,\n ts: event.ts - base,\n dur: event.dur,\n args: event.args,\n });\n } else {\n traceEvents.push({\n ph: 'i',\n name: event.name,\n cat: event.cat,\n pid: this.#pid,\n tid,\n ts: event.ts - base,\n s: 't',\n args: event.args,\n });\n }\n }\n\n return {\n traceEvents,\n metadata: {\n source: '2g',\n version: this.#version,\n convertedAt: new Date().toISOString(),\n startTime: new Date(base / 1000).toISOString(),\n startTimestampMs: base / 1000,\n },\n };\n }\n\n #getTrack(category: string, worker?: string) {\n const key = worker ? `${category}:${worker}` : category;\n if (!this.#tracks.has(key)) {\n this.#tracks.set(key, worker ? `${category} ${worker}` : category);\n }\n return key;\n }\n}\n\n// 'X' events on one tid nest by containment; partial overlap is invalid.\nfunction assignLanes(spans: PendingSpan[]) {\n const order = [...spans].sort(\n (a, b) => a.ts - b.ts || b.ts + b.dur - (a.ts + a.dur)\n );\n const lanes: PendingSpan[][] = [];\n for (const span of order) {\n const end = span.ts + span.dur;\n let lane = -1;\n for (let index = 0; index < lanes.length; index++) {\n const stack = lanes[index];\n while (\n stack.length &&\n stack[stack.length - 1].ts + stack[stack.length - 1].dur <= span.ts\n ) {\n stack.pop();\n }\n const top = stack[stack.length - 1];\n if (!top || top.ts + top.dur >= end) {\n stack.push(span);\n lane = index;\n break;\n }\n }\n if (lane < 0) {\n lanes.push([span]);\n lane = lanes.length - 1;\n }\n span.lane = lane;\n }\n return lanes.length;\n}\n\nfunction splitEventName(value: string) {\n const index = value.indexOf(':');\n if (index < 1) return { category: 'uncategorized', name: value };\n return {\n category: value.slice(0, index),\n name: value.slice(index + 1),\n };\n}\n\nfunction stripSuffix(name: string) {\n for (const suffix of STRIP_SUFFIXES) {\n if (name.endsWith(suffix)) return name.slice(0, -suffix.length);\n }\n return name;\n}\n\nfunction extractArgs(event: ParsedEvent) {\n const args: Record<string, unknown> = {};\n for (const key of Object.keys(event)) {\n if (key.startsWith('_')) continue;\n args[key] = event[key];\n }\n return Object.keys(args).length ? args : undefined;\n}\n","import crypto from 'node:crypto';\n\nimport type { ParsedEvent } from '../../types';\n\nexport interface ConvertToOpenTelemetryOptions {\n processName?: string;\n pid?: number;\n}\n\nexport interface OpenTelemetryFile {\n resourceSpans: OpenTelemetryResourceSpan[];\n}\n\ninterface OpenTelemetryResourceSpan {\n resource: {\n attributes: OpenTelemetryAttribute[];\n };\n scopeSpans: Array<{\n scope: {\n name: string;\n };\n spans: OpenTelemetrySpan[];\n }>;\n}\n\ninterface OpenTelemetrySpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeUnixNano: string;\n endTimeUnixNano: string;\n attributes?: OpenTelemetryAttribute[];\n events?: OpenTelemetrySpanEvent[];\n}\n\ninterface OpenTelemetrySpanEvent {\n timeUnixNano: string;\n name: string;\n attributes?: OpenTelemetryAttribute[];\n}\n\ninterface OpenTelemetryAttribute {\n key: string;\n value: OpenTelemetryAnyValue;\n}\n\ntype OpenTelemetryAnyValue =\n | { stringValue: string }\n | { intValue: string }\n | { doubleValue: number }\n | { boolValue: boolean }\n | { arrayValue: { values: OpenTelemetryAnyValue[] } }\n | { kvlistValue: { values: OpenTelemetryAttribute[] } };\n\nconst STRIP_SUFFIXES = [':started', ':done', ':failed'];\n\nexport async function convertToOpenTelemetry(\n events: Iterable<ParsedEvent> | AsyncIterable<ParsedEvent>,\n options: ConvertToOpenTelemetryOptions = {}\n): Promise<OpenTelemetryFile> {\n const converter = new OpenTelemetryConverter(options);\n for await (const event of events) {\n converter.add(event);\n }\n return converter.toFile();\n}\n\nclass OpenTelemetryConverter {\n readonly #traceId = crypto.randomBytes(16).toString('hex');\n readonly #sessionSpanId = createSpanId();\n readonly #spans: OpenTelemetrySpan[] = [];\n readonly #events: OpenTelemetrySpanEvent[] = [];\n #processName: string;\n #version = '0.1.0';\n #startTimeUnixNano: string | undefined;\n #endTimeUnixNano: string | undefined;\n\n constructor(private readonly options: ConvertToOpenTelemetryOptions) {\n this.#processName = options.processName ?? '2g';\n }\n\n add(event: ParsedEvent) {\n const eventTime = unixNano(event._t);\n this.#startTimeUnixNano =\n this.#startTimeUnixNano == null ||\n BigInt(eventTime) < BigInt(this.#startTimeUnixNano)\n ? eventTime\n : this.#startTimeUnixNano;\n this.#endTimeUnixNano =\n this.#endTimeUnixNano == null ||\n BigInt(eventTime) > BigInt(this.#endTimeUnixNano)\n ? eventTime\n : this.#endTimeUnixNano;\n\n if (event._e === 'root:init') {\n if (typeof event.version === 'string') this.#version = event.version;\n if (this.#processName === '2g' && typeof event.version === 'string') {\n this.#processName = `2g (v${event.version})`;\n }\n return;\n }\n\n if (typeof event._d === 'number') {\n this.#spans.push(this.#createSpan(event));\n } else {\n this.#events.push(this.#createEvent(event));\n }\n }\n\n toFile(): OpenTelemetryFile {\n const startTimeUnixNano = this.#startTimeUnixNano ?? unixNano(Date.now());\n const endTimeUnixNano = this.#endTimeUnixNano ?? startTimeUnixNano;\n return {\n resourceSpans: [\n {\n resource: {\n attributes: attributes({\n 'service.name': this.#processName,\n 'service.version': this.#version,\n 'process.pid': this.options.pid,\n 'telemetry.sdk.name': '2g',\n 'telemetry.sdk.language': 'nodejs',\n }),\n },\n scopeSpans: [\n {\n scope: { name: '2g' },\n spans: [\n {\n traceId: this.#traceId,\n spanId: this.#sessionSpanId,\n name: this.#processName,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: attributes({\n 'event_log.kind': 'session',\n }),\n events: this.#events.length ? this.#events : undefined,\n },\n ...this.#spans,\n ],\n },\n ],\n },\n ],\n };\n }\n\n #createSpan(event: ParsedEvent): OpenTelemetrySpan {\n const parsed = splitEventName(event._e);\n const startTimeUnixNano = unixNano(event._t - event._d!);\n const endTimeUnixNano = unixNano(event._t);\n return {\n traceId: this.#traceId,\n spanId: createSpanId(),\n parentSpanId: this.#sessionSpanId,\n name: parsed ? stripSuffix(parsed.name) : event._e,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: eventAttributes(event, parsed),\n };\n }\n\n #createEvent(event: ParsedEvent): OpenTelemetrySpanEvent {\n const parsed = splitEventName(event._e);\n return {\n timeUnixNano: unixNano(event._t),\n name: parsed ? stripSuffix(parsed.name) : event._e,\n attributes: eventAttributes(event, parsed),\n };\n }\n}\n\nfunction eventAttributes(\n event: ParsedEvent,\n parsed: ReturnType<typeof splitEventName>\n) {\n const values: Record<string, unknown> = {\n 'event.name': event._e,\n };\n if (parsed) {\n values['event.category'] = parsed.category;\n values['event.action'] = parsed.name;\n }\n if (typeof event._w === 'string') values['event.worker.id'] = event._w;\n\n for (const key of Object.keys(event)) {\n if (\n key === '_e' ||\n key === '_t' ||\n key === '_d' ||\n key === '_l' ||\n key === '_w'\n )\n continue;\n values[`event_log.${key}`] = event[key];\n }\n\n return attributes(values);\n}\n\nfunction attributes(values: Record<string, unknown>): OpenTelemetryAttribute[] {\n const result: OpenTelemetryAttribute[] = [];\n for (const [key, value] of Object.entries(values)) {\n const converted = anyValue(value);\n if (converted) result.push({ key, value: converted });\n }\n return result;\n}\n\nfunction anyValue(value: unknown): OpenTelemetryAnyValue | null {\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'boolean') return { boolValue: value };\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) return null;\n return Number.isInteger(value)\n ? { intValue: String(value) }\n : { doubleValue: value };\n }\n if (Array.isArray(value)) {\n return {\n arrayValue: {\n values: value.flatMap(item => {\n const converted = anyValue(item);\n return converted ? [converted] : [];\n }),\n },\n };\n }\n if (value && typeof value === 'object') {\n return {\n kvlistValue: {\n values: attributes(value as Record<string, unknown>),\n },\n };\n }\n return null;\n}\n\nfunction createSpanId() {\n return crypto.randomBytes(8).toString('hex');\n}\n\nfunction unixNano(timeMs: number) {\n return String(BigInt(Math.round(timeMs * 1_000_000)));\n}\n\nfunction splitEventName(value: string) {\n const index = value.indexOf(':');\n if (index < 1) return null;\n return {\n category: value.slice(0, index),\n name: value.slice(index + 1),\n };\n}\n\nfunction stripSuffix(name: string) {\n for (const suffix of STRIP_SUFFIXES) {\n if (name.endsWith(suffix)) return name.slice(0, -suffix.length);\n }\n return name;\n}\n","import fs from 'node:fs';\nimport { parseArgs } from 'node:util';\n\nimport { resolveSession, tap } from '../../tap';\nimport type { ParsedEvent } from '../../types';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport {\n filterEvents,\n formatSessionProcessName,\n parseHelp,\n parseDuration,\n parseSharedTapOptions,\n readJsonlFile,\n readJsonlStdin,\n tapArgOptions,\n} from '../shared';\nimport { convertToChromeTrace } from './chromeTrace';\nimport { convertToOpenTelemetry } from './opentelemetry';\n\ntype ExportFormat = 'chrome-trace' | 'opentelemetry';\nconst DEFAULT_IDLE_TIMEOUT = 30_000;\n\nexport async function runExportCli(args: string[]) {\n if (parseHelp(args)) {\n printExportHelp();\n return;\n }\n const options = parseExportOptions(args);\n if (options.json) redirectConsoleToStderr();\n let events: AsyncIterable<ParsedEvent> | ParsedEvent[];\n let processName: string | undefined;\n let exportPid: number | undefined;\n\n if (options.selector) {\n const session = await resolveSession(options.selector);\n processName = formatSessionProcessName(session);\n exportPid = session.pid;\n events = tap(session.sessionDir, {\n follow: options.follow,\n debug: options.debug,\n timeout: options.timeout,\n idleTimeout: options.follow ? options.idleTimeout : undefined,\n });\n } else if (options.inputFile) {\n events = readJsonlFile(options.inputFile);\n } else {\n events = readJsonlStdin();\n }\n\n const filteredEvents = filterEvents(events, options);\n const file =\n options.format === 'opentelemetry'\n ? await convertToOpenTelemetry(filteredEvents, {\n pid: exportPid,\n processName,\n })\n : await convertToChromeTrace(filteredEvents, {\n pid: exportPid,\n processName,\n });\n const output = JSON.stringify(file, null, 2);\n if (options.output) fs.writeFileSync(options.output, `${output}\\n`);\n else process.stdout.write(`${output}\\n`);\n}\n\nfunction parseExportOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n ...tapArgOptions,\n input: { type: 'string' },\n output: { type: 'string', short: 'o' },\n format: { type: 'string' },\n json: { type: 'boolean' },\n timeout: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const tapOptions = parseSharedTapOptions(parsed.values);\n const timeout = parseDuration(parsed.values.timeout);\n\n const format = parsed.values.format ?? 'chrome-trace';\n if (!isExportFormat(format)) throw new Error(`Unsupported format: ${format}`);\n if (parsed.positionals[0] && parsed.values.input)\n throw new Error('Use either a session selector or --input, not both');\n if (timeout != null && !tapOptions.follow)\n throw new Error('Use --timeout only with --tail');\n\n return {\n selector: parsed.positionals[0],\n inputFile: parsed.values.input,\n output: parsed.values.output,\n format,\n since: tapOptions.since,\n filter: tapOptions.filter,\n spans: tapOptions.spans,\n debug: tapOptions.debug,\n follow: tapOptions.follow,\n timeout,\n idleTimeout: DEFAULT_IDLE_TIMEOUT,\n json: parsed.values.json ?? false,\n };\n}\n\nfunction isExportFormat(value: string): value is ExportFormat {\n return value === 'chrome-trace' || value === 'opentelemetry';\n}\n\nfunction printExportHelp() {\n process.stdout.write(\n [\n 'Usage: 2g export [selector]',\n '',\n 'Exports session events to Chrome Trace or OTLP JSON.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command; a running',\n ' session wins ties',\n ' --input <file> Read JSONL events from a file',\n ' -o, --output <file> Write output to a file',\n ' --format <format> chrome-trace or opentelemetry',\n ' --json Print JSON output',\n ' --since <time> Duration ago (5m, 90s), Unix time, or ISO date',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Only span events (log.span() pairs carrying a _d',\n ' duration); a plain \"ms\" payload field is not a span',\n ' --debug Include debug-level events',\n ' --tail Follow live events after replaying history',\n ' --timeout <duration> Stop tailing after an absolute duration',\n ' Tail also stops after 30s without new events',\n '',\n ].join('\\n')\n );\n}\n","import { parseArgs } from 'node:util';\n\nimport { listSessions } from '../../tap';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\n\nexport async function runPsCli(args: string[] = []) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n active: { type: 'boolean', short: 'a' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n if (parsed.values.json) redirectConsoleToStderr();\n const sessions = (\n await listSessions({ selector: parsed.positionals[0] })\n ).filter(session => !parsed.values.active || session.alive);\n if (parsed.values.json) {\n process.stdout.write(`${JSON.stringify(sessions, null, 2)}\\n`);\n return;\n }\n\n process.stdout.write('PID\\tSTATUS\\tSTARTED\\tCOMMAND\\tCWD\\n');\n for (const session of sessions) {\n process.stdout.write(\n `${escapeTsv(session.pid)}\\t${escapeTsv(\n session.alive ? 'alive' : 'exited'\n )}\\t${escapeTsv(\n new Date(session.startedAt).toISOString()\n )}\\t${escapeTsv(session.command)}\\t${escapeTsv(session.cwd)}\\n`\n );\n }\n}\n\nfunction escapeTsv(value: unknown) {\n return String(value)\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\t/g, '\\\\t')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\n/g, '\\\\n');\n}\n\nexport function printPsHelp() {\n process.stdout.write(\n [\n 'Usage: 2g ps [selector]',\n '',\n 'Lists recorded sessions, newest first; alive means still running.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command',\n ' --active, -a Show only active sessions',\n ' --json Print sessions as JSON',\n '',\n ].join('\\n')\n );\n}\n","import { parseArgs } from 'node:util';\n\nimport { resolveSession, tap } from '../../tap';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport { formatPrettyEvent, shouldColorizeStream } from '../../utils/pretty';\nimport { parseHelp, parseSharedTapOptions, tapArgOptions } from '../shared';\n\nexport async function runTapCli(args: string[]) {\n if (parseHelp(args)) {\n printTapHelp();\n return;\n }\n const options = parseTapOptions(args);\n if (options.json) redirectConsoleToStderr();\n const session = await resolveSession(options.selector);\n const colorizePretty = shouldColorizeStream(process.stdout);\n for await (const event of tap(session.sessionDir, options)) {\n if (options.pretty) {\n process.stdout.write(\n `${formatPrettyEvent(event, {\n colorize: colorizePretty,\n stream: process.stdout,\n })}\\n`\n );\n } else {\n process.stdout.write(`${JSON.stringify(event)}\\n`);\n }\n }\n}\n\nfunction parseTapOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n ...tapArgOptions,\n format: { type: 'string' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const tapOptions = parseSharedTapOptions(parsed.values);\n if (parsed.values.json && parsed.values.format === 'pretty')\n throw new Error('Use either --json or --format pretty, not both');\n\n return {\n selector: parsed.positionals[0],\n since: tapOptions.since,\n follow: tapOptions.follow,\n filter: tapOptions.filter,\n spans: tapOptions.spans,\n debug: tapOptions.debug,\n json: parsed.values.json ?? false,\n pretty: parsed.values.json ? false : parsed.values.format === 'pretty',\n };\n}\n\nfunction printTapHelp() {\n process.stdout.write(\n [\n 'Usage: 2g tap [selector]',\n '',\n 'Replays retained session history as JSONL, oldest first.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command; a running',\n ' session wins ties',\n ' --since <time> Duration ago (5m, 90s), Unix time, or ISO date',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Only span events (log.span() pairs carrying a _d',\n ' duration); a plain \"ms\" payload field is not a span',\n ' --debug Include debug-level events',\n ' --format pretty Print a readable line per event',\n ' --tail Follow live events after replaying history; runs',\n ' until killed (use export with a timeout for a',\n ' bounded follow)',\n '',\n ].join('\\n')\n );\n}\n","#!/usr/bin/env node\nimport { fileURLToPath } from 'node:url';\n\nimport { printCleanHelp, runCleanCli } from './commands/clean';\nimport { runExportCli } from './commands/export';\nimport { printPsHelp, runPsCli } from './commands/ps';\nimport { runTapCli } from './commands/tap';\nimport { parseHelp } from './commands/shared';\n\nexport async function main(argv = process.argv.slice(2)) {\n const [command, ...args] = argv;\n\n if (!command || command === '--help' || command === '-h') {\n printHelp();\n return 0;\n }\n\n switch (command) {\n case 'typegen': {\n const { runTypegenCli } = await import('./commands/typegen');\n runTypegenCli(args);\n return 0;\n }\n case 'ps':\n if (parseHelp(args)) {\n printPsHelp();\n return 0;\n }\n await runPsCli(args);\n return 0;\n case 'tap':\n await runTapCli(args);\n return 0;\n case 'clean':\n if (parseHelp(args)) {\n printCleanHelp();\n return 0;\n }\n runCleanCli(args);\n return 0;\n case 'export':\n await runExportCli(args);\n return 0;\n default:\n throw new Error(`Unknown command: ${command}`);\n }\n}\n\nfunction printHelp() {\n process.stdout.write(\n [\n 'Usage: 2g <command>',\n '',\n 'Reads structured JSONL event logs that instrumented CLIs record during runs',\n 'into the system temp dir, with no setup: run a command, then tap it here.',\n '',\n 'Commands:',\n ' ps [selector] List recorded sessions',\n ' --active, -a (running only) --json',\n '',\n ' tap [selector] Replay session events as JSONL, oldest first',\n ' --since <time> --filter <pattern> --spans --debug --format pretty',\n ' --tail (follow live events after replay; runs until killed)',\n '',\n ' export [selector] Export events to Chrome Trace or OTLP JSON',\n ' --input <jsonl-file> -o, --output <file> --json',\n ' --format chrome-trace|opentelemetry',\n ' --since <time> --filter <pattern> --spans --debug',\n ' --tail --timeout <duration> (absolute; tail also stops after 30s idle)',\n '',\n ' typegen List every event name/payload a project can emit',\n ' -p, --project <tsconfig> -o, --output <file> --format dts|json --json',\n '',\n ' clean Remove stale sessions',\n ' --all, -a (remove all exited sessions) --json',\n '',\n 'Typical use:',\n ' 2g ps -a # what is running now?',\n ' 2g tap \"expo start\" --tail # follow live events',\n ' 2g tap \"expo start\" --since 5m --format pretty # recent history, readable',\n ' 2g tap \"expo start\" --filter metro:bundling # narrow to one area',\n ' 2g export \"expo start\" -o trace.json # flame chart of a run',\n '',\n 'Selectors match sessions by substring of PID, CWD, or command. If several',\n 'match, and exactly one of them is running, it is chosen; otherwise 2g errors',\n 'and lists the candidates.',\n '',\n '--filter takes comma-separated event-name prefixes matched on whole',\n 'segments: \"category\" and \"category:kind\" both match \"category:kind:test\";',\n '* matches anything. Discover names by tapping unfiltered or via typegen.',\n '',\n '--since accepts a duration ago (5m, 90s, 1h30m), Unix time, or ISO date.',\n '--spans keeps only span events (log.span() pairs with a \"_d\" duration in ms).',\n '',\n 'Events are single JSON lines: {\"_e\":\"category:kind\",\"_t\":<epoch ms>,...}.',\n '\"_d\" is a span duration, \"_w\" a worker id; debug events need --debug.',\n '',\n 'Set LOG_EVENTS=<file|fd> on an instrumented command to redirect its events;',\n 'LOG_DEBUG=<filter> also mirrors matching events to stderr.',\n '',\n 'Run 2g <command> --help for per-command help.',\n '',\n ].join('\\n')\n );\n}\n\nif (isCliEntrypoint()) {\n main()\n .then(code => {\n process.exitCode = code;\n })\n .catch(error => {\n process.stderr.write(\n `${error instanceof Error ? error.message : String(error)}\\n`\n );\n process.exitCode = 1;\n });\n}\n\nfunction isCliEntrypoint() {\n try {\n return process.argv[1] === fileURLToPath(import.meta.url);\n } catch {\n return false;\n }\n}\n"],"names":["runCleanCli","args","options","parseCleanOptions","parsed","parseArgs","all","type","short","json","help","values","redirectConsoleToStderr","removed","cleanExitedSessionsSync","cleanStaleSessionsSync","process","stdout","write","JSON","stringify","tapArgOptions","since","filter","multiple","spans","debug","tail","async","readJsonlStream","stream","rl","createInterface","input","line","event","parseEventLine","parseSharedTapOptions","flatMap","value","split","map","item","trim","Boolean","follow","parseHelp","allowPositionals","strict","STRIP_SUFFIXES","TraceConverter","pending","tracks","Map","pid","processName","version","constructor","this","add","_e","splitEventName","index","indexOf","category","name","slice","track","getTrack","_w","undefined","extractArgs","key","Object","keys","startsWith","length","_d","push","kind","stripSuffix","cat","ts","Math","round","_t","dur","lane","toTraceFile","base","Infinity","traceEvents","ph","tids","nextTid","displayName","laneCount","max","assignLanes","tid","set","sort_index","get","s","metadata","source","convertedAt","Date","toISOString","startTime","startTimestampMs","worker","has","order","sort","a","b","lanes","span","end","stack","pop","top","suffix","endsWith","OpenTelemetryConverter","traceId","crypto","randomBytes","toString","sessionSpanId","createSpanId","events","startTimeUnixNano","endTimeUnixNano","eventTime","unixNano","BigInt","createSpan","createEvent","toFile","now","resourceSpans","resource","attributes","scopeSpans","scope","spanId","parentSpanId","eventAttributes","timeUnixNano","result","entries","converted","anyValue","stringValue","boolValue","Number","isFinite","isInteger","intValue","String","doubleValue","Array","isArray","arrayValue","kvlistValue","timeMs","runExportCli","printExportHelp","join","parseExportOptions","output","format","timeout","tapOptions","parseDuration","duration","parseDurationValue","Error","isExportFormat","positionals","selector","inputFile","idleTimeout","exportPid","session","resolveSession","formatSessionProcessName","command","tap","sessionDir","readJsonlFile","file","fs","createReadStream","encoding","readJsonlStdin","stdin","filteredEvents","filterEvents","eventFilter","compileEventFilter","parseSince","matchesTapOptions","convertToOpenTelemetry","converter","convertToChromeTrace","writeFileSync","escapeTsv","replace","runTapCli","printTapHelp","parseTapOptions","pretty","colorizePretty","shouldColorizeStream","formatPrettyEvent","colorize","main","argv","printHelp","runTypegenCli","Promise","resolve","then","require","printPsHelp","runPsCli","active","sessions","listSessions","alive","startedAt","cwd","printCleanHelp","isCliEntrypoint","fileURLToPath","import","code","exitCode","catch","error","stderr","message"],"mappings":"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"}
1
+ {"version":3,"file":"cli-chunk.js","sources":["../../src/commands/clean/index.ts","../../src/commands/shared.ts","../../src/commands/export/chromeTrace.ts","../../src/commands/export/opentelemetry.ts","../../src/commands/export/index.ts","../../src/commands/ps/index.ts","../../src/commands/tap/index.ts","../../src/cli.ts"],"sourcesContent":["import { parseArgs } from 'node:util';\n\nimport { cleanExitedSessionsSync, cleanStaleSessionsSync } from '../../clean';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\n\nexport function runCleanCli(args: string[] = []) {\n const options = parseCleanOptions(args);\n if (options.json) redirectConsoleToStderr();\n const removed = options.all\n ? cleanExitedSessionsSync()\n : cleanStaleSessionsSync();\n process.stdout.write(\n options.json\n ? `${JSON.stringify({ removed })}\\n`\n : `Removed ${removed} ${options.all ? 'exited' : 'stale'} sessions.\\n`\n );\n}\n\nfunction parseCleanOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n options: {\n all: { type: 'boolean', short: 'a' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n return {\n all: parsed.values.all === true,\n json: parsed.values.json === true,\n };\n}\n\nexport function printCleanHelp() {\n process.stdout.write(\n [\n 'Usage: 2g clean',\n '',\n 'Removes stale exited sessions past the retention window.',\n '',\n 'Options:',\n ' --all, -a Remove all exited sessions',\n ' --json',\n '',\n ].join('\\n')\n );\n}\n","import fs from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { Readable } from 'node:stream';\nimport { parseArgs } from 'node:util';\n\nimport type { ListedSession } from '../tap';\nimport {\n compileEventFilter,\n matchesTapOptions,\n parseEventLine,\n parseSince,\n parseDuration as parseDurationValue,\n} from '../utils/eventFilter';\nimport type { ParsedEvent } from '../types';\n\nexport const tapArgOptions = {\n since: { type: 'string' },\n filter: { type: 'string', multiple: true },\n spans: { type: 'boolean' },\n debug: { type: 'boolean' },\n tail: { type: 'boolean' },\n} as const;\n\nexport interface SharedTapOptions {\n since?: string;\n filter?: string[];\n spans?: boolean;\n debug?: boolean;\n follow: boolean;\n}\n\nexport function formatSessionProcessName(session: ListedSession) {\n return session.version\n ? `${session.command} (v${session.version}, PID ${session.pid})`\n : `${session.command} (PID ${session.pid})`;\n}\n\nexport async function* readJsonlFile(file: string) {\n yield* readJsonlStream(fs.createReadStream(file, { encoding: 'utf8' }));\n}\n\nexport async function* readJsonlStdin() {\n yield* readJsonlStream(process.stdin);\n}\n\nexport async function* readJsonlStream(stream: Readable) {\n const rl = createInterface({ input: stream });\n for await (const line of rl) {\n if (!line) continue;\n // Lossless parse; the debug decision belongs to filterEvents\n const event = parseEventLine(line, { debug: true });\n if (event) yield event;\n }\n}\n\nexport async function* filterEvents(\n events: AsyncIterable<ParsedEvent> | Iterable<ParsedEvent>,\n options: Pick<SharedTapOptions, 'since' | 'filter' | 'spans' | 'debug'>\n) {\n const eventFilter = compileEventFilter(options.filter);\n const since = parseSince(options.since);\n for await (const event of events) {\n if (\n matchesTapOptions(\n event,\n {\n filter: options.filter,\n since,\n spans: options.spans,\n debug: options.debug,\n },\n eventFilter\n )\n )\n yield event;\n }\n}\n\nexport function parseSharedTapOptions(values: {\n since?: string;\n filter?: string[];\n spans?: boolean;\n debug?: boolean;\n tail?: boolean;\n}): SharedTapOptions {\n return {\n since: values.since,\n filter: values.filter\n ?.flatMap(value => value.split(','))\n .map(item => item.trim())\n .filter(Boolean),\n spans: values.spans === true,\n debug: values.debug === true,\n follow: values.tail === true,\n };\n}\n\nexport function parseHelp(args: string[]) {\n return (\n parseArgs({\n args,\n allowPositionals: true,\n strict: false,\n options: {\n help: { type: 'boolean', short: 'h' },\n },\n }).values.help === true\n );\n}\n\nexport function parseJsonFlag(args: string[]) {\n return parseArgs({\n args,\n allowPositionals: true,\n options: {\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n }).values;\n}\n\nexport function parseDuration(value?: string) {\n if (value == null || value === '') return undefined;\n const duration = parseDurationValue(value);\n if (duration == null) throw new Error(`Invalid duration: ${value}`);\n return duration;\n}\n","import type { ParsedEvent } from '../../types';\n\nexport interface ConvertToChromeTraceOptions {\n processName?: string;\n pid?: number;\n}\n\nexport interface TraceFile {\n traceEvents: TraceEvent[];\n metadata: {\n source: '2g';\n version: string;\n convertedAt: string;\n startTime: string;\n startTimestampMs: number;\n };\n}\n\nexport type TraceEvent = TraceComplete | TraceInstant | TraceMetadata;\n\nexport interface TraceComplete {\n ph: 'X';\n name: string;\n cat: string;\n pid: number;\n tid: number;\n ts: number;\n dur: number;\n args?: Record<string, unknown>;\n}\n\nexport interface TraceInstant {\n ph: 'i';\n name: string;\n cat: string;\n pid: number;\n tid: number;\n ts: number;\n s: 'g' | 'p' | 't';\n args?: Record<string, unknown>;\n}\n\nexport interface TraceMetadata {\n ph: 'M';\n name: 'process_name' | 'thread_name' | 'thread_sort_index';\n pid: number;\n tid?: number;\n args: Record<string, unknown>;\n}\n\nconst STRIP_SUFFIXES = [':started', ':done', ':failed'];\n\nexport async function convertToChromeTrace(\n events: Iterable<ParsedEvent> | AsyncIterable<ParsedEvent>,\n options: ConvertToChromeTraceOptions = {}\n): Promise<TraceFile> {\n const converter = new TraceConverter(options);\n for await (const event of events) {\n converter.add(event);\n }\n return converter.toTraceFile();\n}\n\ninterface PendingSpan {\n track: string;\n name: string;\n cat: string;\n ts: number;\n dur: number;\n args?: Record<string, unknown>;\n lane: number;\n}\n\ninterface PendingInstant {\n track: string;\n name: string;\n cat: string;\n ts: number;\n args?: Record<string, unknown>;\n}\n\ntype PendingEvent =\n | ({ kind: 'span' } & PendingSpan)\n | ({ kind: 'instant' } & PendingInstant);\n\nexport class TraceConverter {\n #pending: PendingEvent[] = [];\n #tracks = new Map<string, string>();\n #pid: number;\n #processName: string;\n #version = '0.1.0';\n\n constructor(options: ConvertToChromeTraceOptions = {}) {\n this.#pid = options.pid ?? 1;\n this.#processName = options.processName ?? '2g';\n }\n\n add(event: ParsedEvent) {\n if (event._e === 'root:init') {\n if (typeof event.version === 'string') {\n this.#version = event.version;\n if (this.#processName === '2g') {\n this.#processName = `2g (v${event.version})`;\n }\n }\n return;\n }\n\n const parsed = splitEventName(event._e);\n const track = this.#getTrack(\n parsed.category,\n typeof event._w === 'string' ? event._w : undefined\n );\n const args = extractArgs(event);\n\n if (typeof event._d === 'number') {\n this.#pending.push({\n kind: 'span',\n track,\n name: stripSuffix(parsed.name),\n cat: parsed.category,\n ts: Math.round((event._t - event._d) * 1000),\n dur: Math.round(event._d * 1000),\n args,\n lane: 0,\n });\n } else {\n this.#pending.push({\n kind: 'instant',\n track,\n name: stripSuffix(parsed.name),\n cat: parsed.category,\n ts: Math.round(event._t * 1000),\n args,\n });\n }\n }\n\n toTraceFile(): TraceFile {\n let base = this.#pending.length ? Infinity : 0;\n for (const event of this.#pending) {\n if (event.ts < base) base = event.ts;\n }\n const traceEvents: TraceEvent[] = [\n {\n ph: 'M',\n name: 'process_name',\n pid: this.#pid,\n args: { name: this.#processName },\n },\n ];\n\n const tids = new Map<string, number>();\n let nextTid = 1;\n for (const [track, displayName] of this.#tracks) {\n const spans: PendingSpan[] = [];\n for (const event of this.#pending) {\n if (event.kind === 'span' && event.track === track) spans.push(event);\n }\n const laneCount = Math.max(assignLanes(spans), 1);\n for (let lane = 0; lane < laneCount; lane++) {\n const tid = nextTid++;\n tids.set(`${track}\\0${lane}`, tid);\n traceEvents.push(\n {\n ph: 'M',\n name: 'thread_name',\n pid: this.#pid,\n tid,\n args: {\n name: lane === 0 ? displayName : `${displayName} #${lane + 1}`,\n },\n },\n {\n ph: 'M',\n name: 'thread_sort_index',\n pid: this.#pid,\n tid,\n args: { sort_index: tid },\n }\n );\n }\n }\n\n for (const event of this.#pending) {\n const lane = event.kind === 'span' ? event.lane : 0;\n const tid = tids.get(`${event.track}\\0${lane}`)!;\n if (event.kind === 'span') {\n traceEvents.push({\n ph: 'X',\n name: event.name,\n cat: event.cat,\n pid: this.#pid,\n tid,\n ts: event.ts - base,\n dur: event.dur,\n args: event.args,\n });\n } else {\n traceEvents.push({\n ph: 'i',\n name: event.name,\n cat: event.cat,\n pid: this.#pid,\n tid,\n ts: event.ts - base,\n s: 't',\n args: event.args,\n });\n }\n }\n\n return {\n traceEvents,\n metadata: {\n source: '2g',\n version: this.#version,\n convertedAt: new Date().toISOString(),\n startTime: new Date(base / 1000).toISOString(),\n startTimestampMs: base / 1000,\n },\n };\n }\n\n #getTrack(category: string, worker?: string) {\n const key = worker ? `${category}:${worker}` : category;\n if (!this.#tracks.has(key)) {\n this.#tracks.set(key, worker ? `${category} ${worker}` : category);\n }\n return key;\n }\n}\n\n// 'X' events on one tid nest by containment; partial overlap is invalid.\nfunction assignLanes(spans: PendingSpan[]) {\n const order = [...spans].sort(\n (a, b) => a.ts - b.ts || b.ts + b.dur - (a.ts + a.dur)\n );\n const lanes: PendingSpan[][] = [];\n for (const span of order) {\n const end = span.ts + span.dur;\n let lane = -1;\n for (let index = 0; index < lanes.length; index++) {\n const stack = lanes[index];\n while (\n stack.length &&\n stack[stack.length - 1].ts + stack[stack.length - 1].dur <= span.ts\n ) {\n stack.pop();\n }\n const top = stack[stack.length - 1];\n if (!top || top.ts + top.dur >= end) {\n stack.push(span);\n lane = index;\n break;\n }\n }\n if (lane < 0) {\n lanes.push([span]);\n lane = lanes.length - 1;\n }\n span.lane = lane;\n }\n return lanes.length;\n}\n\nfunction splitEventName(value: string) {\n const index = value.indexOf(':');\n if (index < 1) return { category: 'uncategorized', name: value };\n return {\n category: value.slice(0, index),\n name: value.slice(index + 1),\n };\n}\n\nfunction stripSuffix(name: string) {\n for (const suffix of STRIP_SUFFIXES) {\n if (name.endsWith(suffix)) return name.slice(0, -suffix.length);\n }\n return name;\n}\n\nfunction extractArgs(event: ParsedEvent) {\n const args: Record<string, unknown> = {};\n for (const key of Object.keys(event)) {\n if (key.startsWith('_')) continue;\n args[key] = event[key];\n }\n return Object.keys(args).length ? args : undefined;\n}\n","import crypto from 'node:crypto';\n\nimport type { ParsedEvent } from '../../types';\n\nexport interface ConvertToOpenTelemetryOptions {\n processName?: string;\n pid?: number;\n}\n\nexport interface OpenTelemetryFile {\n resourceSpans: OpenTelemetryResourceSpan[];\n}\n\ninterface OpenTelemetryResourceSpan {\n resource: {\n attributes: OpenTelemetryAttribute[];\n };\n scopeSpans: Array<{\n scope: {\n name: string;\n };\n spans: OpenTelemetrySpan[];\n }>;\n}\n\ninterface OpenTelemetrySpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n startTimeUnixNano: string;\n endTimeUnixNano: string;\n attributes?: OpenTelemetryAttribute[];\n events?: OpenTelemetrySpanEvent[];\n}\n\ninterface OpenTelemetrySpanEvent {\n timeUnixNano: string;\n name: string;\n attributes?: OpenTelemetryAttribute[];\n}\n\ninterface OpenTelemetryAttribute {\n key: string;\n value: OpenTelemetryAnyValue;\n}\n\ntype OpenTelemetryAnyValue =\n | { stringValue: string }\n | { intValue: string }\n | { doubleValue: number }\n | { boolValue: boolean }\n | { arrayValue: { values: OpenTelemetryAnyValue[] } }\n | { kvlistValue: { values: OpenTelemetryAttribute[] } };\n\nconst STRIP_SUFFIXES = [':started', ':done', ':failed'];\n\nexport async function convertToOpenTelemetry(\n events: Iterable<ParsedEvent> | AsyncIterable<ParsedEvent>,\n options: ConvertToOpenTelemetryOptions = {}\n): Promise<OpenTelemetryFile> {\n const converter = new OpenTelemetryConverter(options);\n for await (const event of events) {\n converter.add(event);\n }\n return converter.toFile();\n}\n\nclass OpenTelemetryConverter {\n readonly #traceId = crypto.randomBytes(16).toString('hex');\n readonly #sessionSpanId = createSpanId();\n readonly #spans: OpenTelemetrySpan[] = [];\n readonly #events: OpenTelemetrySpanEvent[] = [];\n #processName: string;\n #version = '0.1.0';\n #startTimeUnixNano: string | undefined;\n #endTimeUnixNano: string | undefined;\n\n constructor(private readonly options: ConvertToOpenTelemetryOptions) {\n this.#processName = options.processName ?? '2g';\n }\n\n add(event: ParsedEvent) {\n const eventTime = unixNano(event._t);\n this.#startTimeUnixNano =\n this.#startTimeUnixNano == null ||\n BigInt(eventTime) < BigInt(this.#startTimeUnixNano)\n ? eventTime\n : this.#startTimeUnixNano;\n this.#endTimeUnixNano =\n this.#endTimeUnixNano == null ||\n BigInt(eventTime) > BigInt(this.#endTimeUnixNano)\n ? eventTime\n : this.#endTimeUnixNano;\n\n if (event._e === 'root:init') {\n if (typeof event.version === 'string') this.#version = event.version;\n if (this.#processName === '2g' && typeof event.version === 'string') {\n this.#processName = `2g (v${event.version})`;\n }\n return;\n }\n\n if (typeof event._d === 'number') {\n this.#spans.push(this.#createSpan(event));\n } else {\n this.#events.push(this.#createEvent(event));\n }\n }\n\n toFile(): OpenTelemetryFile {\n const startTimeUnixNano = this.#startTimeUnixNano ?? unixNano(Date.now());\n const endTimeUnixNano = this.#endTimeUnixNano ?? startTimeUnixNano;\n return {\n resourceSpans: [\n {\n resource: {\n attributes: attributes({\n 'service.name': this.#processName,\n 'service.version': this.#version,\n 'process.pid': this.options.pid,\n 'telemetry.sdk.name': '2g',\n 'telemetry.sdk.language': 'nodejs',\n }),\n },\n scopeSpans: [\n {\n scope: { name: '2g' },\n spans: [\n {\n traceId: this.#traceId,\n spanId: this.#sessionSpanId,\n name: this.#processName,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: attributes({\n 'event_log.kind': 'session',\n }),\n events: this.#events.length ? this.#events : undefined,\n },\n ...this.#spans,\n ],\n },\n ],\n },\n ],\n };\n }\n\n #createSpan(event: ParsedEvent): OpenTelemetrySpan {\n const parsed = splitEventName(event._e);\n const startTimeUnixNano = unixNano(event._t - event._d!);\n const endTimeUnixNano = unixNano(event._t);\n return {\n traceId: this.#traceId,\n spanId: createSpanId(),\n parentSpanId: this.#sessionSpanId,\n name: parsed ? stripSuffix(parsed.name) : event._e,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: eventAttributes(event, parsed),\n };\n }\n\n #createEvent(event: ParsedEvent): OpenTelemetrySpanEvent {\n const parsed = splitEventName(event._e);\n return {\n timeUnixNano: unixNano(event._t),\n name: parsed ? stripSuffix(parsed.name) : event._e,\n attributes: eventAttributes(event, parsed),\n };\n }\n}\n\nfunction eventAttributes(\n event: ParsedEvent,\n parsed: ReturnType<typeof splitEventName>\n) {\n const values: Record<string, unknown> = {\n 'event.name': event._e,\n };\n if (parsed) {\n values['event.category'] = parsed.category;\n values['event.action'] = parsed.name;\n }\n if (typeof event._w === 'string') values['event.worker.id'] = event._w;\n\n for (const key of Object.keys(event)) {\n if (\n key === '_e' ||\n key === '_t' ||\n key === '_d' ||\n key === '_l' ||\n key === '_w'\n )\n continue;\n values[`event_log.${key}`] = event[key];\n }\n\n return attributes(values);\n}\n\nfunction attributes(values: Record<string, unknown>): OpenTelemetryAttribute[] {\n const result: OpenTelemetryAttribute[] = [];\n for (const [key, value] of Object.entries(values)) {\n const converted = anyValue(value);\n if (converted) result.push({ key, value: converted });\n }\n return result;\n}\n\nfunction anyValue(value: unknown): OpenTelemetryAnyValue | null {\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'boolean') return { boolValue: value };\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) return null;\n return Number.isInteger(value)\n ? { intValue: String(value) }\n : { doubleValue: value };\n }\n if (Array.isArray(value)) {\n return {\n arrayValue: {\n values: value.flatMap(item => {\n const converted = anyValue(item);\n return converted ? [converted] : [];\n }),\n },\n };\n }\n if (value && typeof value === 'object') {\n return {\n kvlistValue: {\n values: attributes(value as Record<string, unknown>),\n },\n };\n }\n return null;\n}\n\nfunction createSpanId() {\n return crypto.randomBytes(8).toString('hex');\n}\n\nfunction unixNano(timeMs: number) {\n return String(BigInt(Math.round(timeMs * 1_000_000)));\n}\n\nfunction splitEventName(value: string) {\n const index = value.indexOf(':');\n if (index < 1) return null;\n return {\n category: value.slice(0, index),\n name: value.slice(index + 1),\n };\n}\n\nfunction stripSuffix(name: string) {\n for (const suffix of STRIP_SUFFIXES) {\n if (name.endsWith(suffix)) return name.slice(0, -suffix.length);\n }\n return name;\n}\n","import fs from 'node:fs';\nimport { parseArgs } from 'node:util';\n\nimport { resolveSession, tap } from '../../tap';\nimport type { ParsedEvent } from '../../types';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport {\n filterEvents,\n formatSessionProcessName,\n parseHelp,\n parseDuration,\n parseSharedTapOptions,\n readJsonlFile,\n readJsonlStdin,\n tapArgOptions,\n} from '../shared';\nimport { convertToChromeTrace } from './chromeTrace';\nimport { convertToOpenTelemetry } from './opentelemetry';\n\ntype ExportFormat = 'chrome-trace' | 'opentelemetry';\nconst DEFAULT_IDLE_TIMEOUT = 30_000;\n\nexport async function runExportCli(args: string[]) {\n if (parseHelp(args)) {\n printExportHelp();\n return;\n }\n const options = parseExportOptions(args);\n if (options.json) redirectConsoleToStderr();\n let events: AsyncIterable<ParsedEvent> | ParsedEvent[];\n let processName: string | undefined;\n let exportPid: number | undefined;\n\n if (options.selector) {\n const session = await resolveSession(options.selector);\n processName = formatSessionProcessName(session);\n exportPid = session.pid;\n events = tap(session.sessionDir, {\n follow: options.follow,\n debug: options.debug,\n timeout: options.timeout,\n idleTimeout: options.follow ? options.idleTimeout : undefined,\n });\n } else if (options.inputFile) {\n events = readJsonlFile(options.inputFile);\n } else {\n events = readJsonlStdin();\n }\n\n const filteredEvents = filterEvents(events, options);\n const file =\n options.format === 'opentelemetry'\n ? await convertToOpenTelemetry(filteredEvents, {\n pid: exportPid,\n processName,\n })\n : await convertToChromeTrace(filteredEvents, {\n pid: exportPid,\n processName,\n });\n const output = JSON.stringify(file, null, 2);\n if (options.output) fs.writeFileSync(options.output, `${output}\\n`);\n else process.stdout.write(`${output}\\n`);\n}\n\nfunction parseExportOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n ...tapArgOptions,\n input: { type: 'string' },\n output: { type: 'string', short: 'o' },\n format: { type: 'string' },\n json: { type: 'boolean' },\n timeout: { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const tapOptions = parseSharedTapOptions(parsed.values);\n const timeout = parseDuration(parsed.values.timeout);\n\n const format = parsed.values.format ?? 'chrome-trace';\n if (!isExportFormat(format)) throw new Error(`Unsupported format: ${format}`);\n if (parsed.positionals[0] && parsed.values.input)\n throw new Error('Use either a session selector or --input, not both');\n if (timeout != null && !tapOptions.follow)\n throw new Error('Use --timeout only with --tail');\n\n return {\n selector: parsed.positionals[0],\n inputFile: parsed.values.input,\n output: parsed.values.output,\n format,\n since: tapOptions.since,\n filter: tapOptions.filter,\n spans: tapOptions.spans,\n debug: tapOptions.debug,\n follow: tapOptions.follow,\n timeout,\n idleTimeout: DEFAULT_IDLE_TIMEOUT,\n json: parsed.values.json ?? false,\n };\n}\n\nfunction isExportFormat(value: string): value is ExportFormat {\n return value === 'chrome-trace' || value === 'opentelemetry';\n}\n\nfunction printExportHelp() {\n process.stdout.write(\n [\n 'Usage: 2g export [selector]',\n '',\n 'Exports session events to Chrome Trace or OTLP JSON.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command; a running',\n ' session wins ties',\n ' --input <file> Read JSONL events from a file',\n ' -o, --output <file> Write output to a file',\n ' --format <format> chrome-trace or opentelemetry',\n ' --json Print JSON output',\n ' --since <time> Duration ago (5m, 90s), Unix time, or ISO date',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Keep only span events (log.span() pairs with a _d',\n ' duration); omit to include point events too. A',\n ' plain \"ms\" payload field is not a span',\n ' --debug Include debug events (only recorded when the',\n ' command ran with LOG_DEBUG set)',\n ' --tail Follow live events after replaying history',\n ' --timeout <duration> Stop tailing after an absolute duration',\n ' Tail also stops after 30s without new events',\n '',\n ].join('\\n')\n );\n}\n","import { parseArgs } from 'node:util';\n\nimport { listSessions } from '../../tap';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\n\nexport async function runPsCli(args: string[] = []) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n active: { type: 'boolean', short: 'a' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n if (parsed.values.json) redirectConsoleToStderr();\n const sessions = (\n await listSessions({ selector: parsed.positionals[0] })\n ).filter(session => !parsed.values.active || session.alive);\n if (parsed.values.json) {\n process.stdout.write(`${JSON.stringify(sessions, null, 2)}\\n`);\n return;\n }\n\n process.stdout.write('PID\\tSTATUS\\tSTARTED\\tCOMMAND\\tCWD\\n');\n for (const session of sessions) {\n process.stdout.write(\n `${escapeTsv(session.pid)}\\t${escapeTsv(\n session.alive ? 'alive' : 'exited'\n )}\\t${escapeTsv(\n new Date(session.startedAt).toISOString()\n )}\\t${escapeTsv(session.command)}\\t${escapeTsv(session.cwd)}\\n`\n );\n }\n}\n\nfunction escapeTsv(value: unknown) {\n return String(value)\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\t/g, '\\\\t')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\n/g, '\\\\n');\n}\n\nexport function printPsHelp() {\n process.stdout.write(\n [\n 'Usage: 2g ps [selector]',\n '',\n 'Lists recorded sessions, newest first; alive means still running.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command',\n ' --active, -a Show only active sessions',\n ' --json Print sessions as JSON',\n '',\n ].join('\\n')\n );\n}\n","import { parseArgs } from 'node:util';\n\nimport { resolveSession, tap } from '../../tap';\nimport { redirectConsoleToStderr } from '../../utils/redirectConsole';\nimport { formatPrettyEvent, shouldColorizeStream } from '../../utils/pretty';\nimport { parseHelp, parseSharedTapOptions, tapArgOptions } from '../shared';\n\nexport async function runTapCli(args: string[]) {\n if (parseHelp(args)) {\n printTapHelp();\n return;\n }\n const options = parseTapOptions(args);\n if (options.json) redirectConsoleToStderr();\n const session = await resolveSession(options.selector);\n const colorizePretty = shouldColorizeStream(process.stdout);\n for await (const event of tap(session.sessionDir, options)) {\n if (options.pretty) {\n process.stdout.write(\n `${formatPrettyEvent(event, {\n colorize: colorizePretty,\n stream: process.stdout,\n })}\\n`\n );\n } else {\n process.stdout.write(`${JSON.stringify(event)}\\n`);\n }\n }\n}\n\nfunction parseTapOptions(args: string[]) {\n const parsed = parseArgs({\n args,\n allowPositionals: true,\n options: {\n ...tapArgOptions,\n format: { type: 'string' },\n json: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n const tapOptions = parseSharedTapOptions(parsed.values);\n if (parsed.values.json && parsed.values.format === 'pretty')\n throw new Error('Use either --json or --format pretty, not both');\n\n return {\n selector: parsed.positionals[0],\n since: tapOptions.since,\n follow: tapOptions.follow,\n filter: tapOptions.filter,\n spans: tapOptions.spans,\n debug: tapOptions.debug,\n json: parsed.values.json ?? false,\n pretty: parsed.values.json ? false : parsed.values.format === 'pretty',\n };\n}\n\nfunction printTapHelp() {\n process.stdout.write(\n [\n 'Usage: 2g tap [selector]',\n '',\n 'Replays retained session history as JSONL, oldest first.',\n '',\n 'Options:',\n ' selector Substring of PID, CWD, or command; a running',\n ' session wins ties',\n ' --since <time> Duration ago (5m, 90s), Unix time, or ISO date',\n ' --filter <pattern> Event name prefix (whole segments), e.g. category,',\n ' category:kind; * wildcards; comma-separated',\n ' --spans Keep only span events (log.span() pairs with a _d',\n ' duration); omit to include point events too. A',\n ' plain \"ms\" payload field is not a span',\n ' --debug Include debug events (only recorded when the',\n ' command ran with LOG_DEBUG set)',\n ' --format pretty Print a readable line per event',\n ' --tail Follow live events after replaying history; runs',\n ' until killed (use export with a timeout for a',\n ' bounded follow)',\n '',\n ].join('\\n')\n );\n}\n","#!/usr/bin/env node\nimport { fileURLToPath } from 'node:url';\n\nimport { printCleanHelp, runCleanCli } from './commands/clean';\nimport { runExportCli } from './commands/export';\nimport { printPsHelp, runPsCli } from './commands/ps';\nimport { runTapCli } from './commands/tap';\nimport { parseHelp } from './commands/shared';\n\nexport async function main(argv = process.argv.slice(2)) {\n const [command, ...args] = argv;\n\n if (!command || command === '--help' || command === '-h') {\n printHelp();\n return 0;\n }\n\n switch (command) {\n case 'typegen': {\n const { runTypegenCli } = await import('./commands/typegen');\n runTypegenCli(args);\n return 0;\n }\n case 'ps':\n if (parseHelp(args)) {\n printPsHelp();\n return 0;\n }\n await runPsCli(args);\n return 0;\n case 'tap':\n await runTapCli(args);\n return 0;\n case 'clean':\n if (parseHelp(args)) {\n printCleanHelp();\n return 0;\n }\n runCleanCli(args);\n return 0;\n case 'export':\n await runExportCli(args);\n return 0;\n default:\n throw new Error(`Unknown command: ${command}`);\n }\n}\n\nfunction printHelp() {\n process.stdout.write(\n [\n 'Usage: 2g <command>',\n '',\n 'Reads structured JSONL event logs that instrumented CLIs record during runs',\n 'into the system temp dir, with no setup: run a command, then tap it here.',\n '',\n 'Commands:',\n ' ps [selector] List recorded sessions',\n ' --active, -a (running only) --json',\n '',\n ' tap [selector] Replay session events as JSONL, oldest first',\n ' --since <time> --filter <pattern> --spans --debug --format pretty',\n ' --tail (follow live events after replay; runs until killed)',\n '',\n ' export [selector] Export events to Chrome Trace or OTLP JSON',\n ' --input <jsonl-file> -o, --output <file> --json',\n ' --format chrome-trace|opentelemetry',\n ' --since <time> --filter <pattern> --spans --debug',\n ' --tail --timeout <duration> (absolute; tail also stops after 30s idle)',\n '',\n ' typegen List every event name/payload a project can emit',\n ' -p, --project <tsconfig> -o, --output <file> --format dts|json --json',\n '',\n ' clean Remove stale sessions',\n ' --all, -a (remove all exited sessions) --json',\n '',\n 'Typical use:',\n ' 2g ps -a # what is running now?',\n ' 2g tap \"expo start\" --tail # follow live events',\n ' 2g tap \"expo start\" --since 5m --format pretty # recent history, readable',\n ' 2g tap \"expo start\" --filter metro:bundling # narrow to one area',\n ' 2g export \"expo start\" -o trace.json # flame chart of a run',\n '',\n 'Selectors match sessions by substring of PID, CWD, or command. If several',\n 'match, and exactly one of them is running, it is chosen; otherwise 2g errors',\n 'and lists the candidates.',\n '',\n '--filter takes comma-separated event-name prefixes matched on whole',\n 'segments: \"category\" and \"category:kind\" both match \"category:kind:test\";',\n '* matches anything. Discover names by tapping unfiltered or via typegen.',\n '',\n '--since accepts a duration ago (5m, 90s, 1h30m), Unix time, or ISO date.',\n '--spans keeps only span events (log.span() pairs with a \"_d\" duration in',\n 'ms); without it, point events are included alongside spans.',\n '',\n 'Events are single JSON lines: {\"_e\":\"category:kind\",\"_t\":<epoch ms>,...}.',\n '\"_d\" is a span duration, \"_w\" a worker id; debug events need --debug.',\n '',\n 'Set LOG_EVENTS=<file|fd> on an instrumented command to redirect its events.',\n 'Debug events are only recorded when the command runs with LOG_DEBUG set.',\n '',\n 'Pass LOG_DEBUG=<filter> when debugging; it also mirrors matching events to',\n 'stderr. Reading them back needs --debug.',\n '',\n 'Run 2g <command> --help for per-command help.',\n '',\n ].join('\\n')\n );\n}\n\nif (isCliEntrypoint()) {\n main()\n .then(code => {\n process.exitCode = code;\n })\n .catch(error => {\n process.stderr.write(\n `${error instanceof Error ? error.message : String(error)}\\n`\n );\n process.exitCode = 1;\n });\n}\n\nfunction isCliEntrypoint() {\n try {\n return process.argv[1] === fileURLToPath(import.meta.url);\n } catch {\n return false;\n }\n}\n"],"names":["runCleanCli","args","options","parseCleanOptions","parsed","parseArgs","all","type","short","json","help","values","redirectConsoleToStderr","removed","cleanExitedSessionsSync","cleanStaleSessionsSync","process","stdout","write","JSON","stringify","tapArgOptions","since","filter","multiple","spans","debug","tail","async","readJsonlStream","stream","rl","createInterface","input","line","event","parseEventLine","parseSharedTapOptions","flatMap","value","split","map","item","trim","Boolean","follow","parseHelp","allowPositionals","strict","STRIP_SUFFIXES","TraceConverter","pending","tracks","Map","pid","processName","version","constructor","this","add","_e","splitEventName","index","indexOf","category","name","slice","track","getTrack","_w","undefined","extractArgs","key","Object","keys","startsWith","length","_d","push","kind","stripSuffix","cat","ts","Math","round","_t","dur","lane","toTraceFile","base","Infinity","traceEvents","ph","tids","nextTid","displayName","laneCount","max","assignLanes","tid","set","sort_index","get","s","metadata","source","convertedAt","Date","toISOString","startTime","startTimestampMs","worker","has","order","sort","a","b","lanes","span","end","stack","pop","top","suffix","endsWith","OpenTelemetryConverter","traceId","crypto","randomBytes","toString","sessionSpanId","createSpanId","events","startTimeUnixNano","endTimeUnixNano","eventTime","unixNano","BigInt","createSpan","createEvent","toFile","now","resourceSpans","resource","attributes","scopeSpans","scope","spanId","parentSpanId","eventAttributes","timeUnixNano","result","entries","converted","anyValue","stringValue","boolValue","Number","isFinite","isInteger","intValue","String","doubleValue","Array","isArray","arrayValue","kvlistValue","timeMs","runExportCli","printExportHelp","join","parseExportOptions","output","format","timeout","tapOptions","parseDuration","duration","parseDurationValue","Error","isExportFormat","positionals","selector","inputFile","idleTimeout","exportPid","session","resolveSession","formatSessionProcessName","command","tap","sessionDir","readJsonlFile","file","fs","createReadStream","encoding","readJsonlStdin","stdin","filteredEvents","filterEvents","eventFilter","compileEventFilter","parseSince","matchesTapOptions","convertToOpenTelemetry","converter","convertToChromeTrace","writeFileSync","escapeTsv","replace","runTapCli","printTapHelp","parseTapOptions","pretty","colorizePretty","shouldColorizeStream","formatPrettyEvent","colorize","main","argv","printHelp","runTypegenCli","Promise","resolve","then","require","printPsHelp","runPsCli","active","sessions","listSessions","alive","startedAt","cwd","printCleanHelp","isCliEntrypoint","fileURLToPath","import","code","exitCode","catch","error","stderr","message"],"mappings":"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"}
@@ -2,15 +2,15 @@ import { fileURLToPath as e } from "node:url";
2
2
 
3
3
  import { parseArgs as t } from "node:util";
4
4
 
5
- import { a as n, b as s, c as o, d as r, m as i, e as a, p as l } from "./sessionSockets-chunk.mjs";
5
+ import { a as n, b as s, c as o, d as i, m as r, e as a, p as l } from "./sessionSockets-chunk.mjs";
6
6
 
7
7
  import { r as p, s as c, f as u } from "./pretty-chunk.mjs";
8
8
 
9
9
  import d from "node:fs";
10
10
 
11
- import { r as f, t as m, l as g } from "./tap-chunk.mjs";
11
+ import { r as f, t as m, l as h } from "./tap-chunk.mjs";
12
12
 
13
- import { createInterface as h } from "node:readline";
13
+ import { createInterface as g } from "node:readline";
14
14
 
15
15
  import v from "node:crypto";
16
16
 
@@ -40,10 +40,10 @@ function runCleanCli(e = []) {
40
40
  if (o.json) {
41
41
  p();
42
42
  }
43
- const r = o.all ? n() : s();
43
+ const i = o.all ? n() : s();
44
44
  process.stdout.write(o.json ? `${JSON.stringify({
45
- removed: r
46
- })}\n` : `Removed ${r} ${o.all ? "exited" : "stale"} sessions.\n`);
45
+ removed: i
46
+ })}\n` : `Removed ${i} ${o.all ? "exited" : "stale"} sessions.\n`);
47
47
  }
48
48
 
49
49
  const y = {
@@ -66,7 +66,7 @@ const y = {
66
66
  };
67
67
 
68
68
  async function* readJsonlStream(e) {
69
- const t = h({
69
+ const t = g({
70
70
  input: e
71
71
  });
72
72
  for await (const e of t) {
@@ -141,7 +141,7 @@ class TraceConverter {
141
141
  name: e.slice(t + 1)
142
142
  };
143
143
  }(e._e);
144
- const n = this.#r(t.category, "string" == typeof e._w ? e._w : void 0);
144
+ const n = this.#i(t.category, "string" == typeof e._w ? e._w : void 0);
145
145
  const s = function extractArgs(e) {
146
146
  const t = {};
147
147
  for (const n of Object.keys(e)) {
@@ -192,31 +192,31 @@ class TraceConverter {
192
192
  const n = new Map;
193
193
  let s = 1;
194
194
  for (const [e, o] of this.#t) {
195
- const r = [];
195
+ const i = [];
196
196
  for (const t of this.#e) {
197
197
  if ("span" === t.kind && t.track === e) {
198
- r.push(t);
198
+ i.push(t);
199
199
  }
200
200
  }
201
- const i = Math.max(assignLanes(r), 1);
202
- for (let r = 0; r < i; r++) {
203
- const i = s++;
204
- n.set(`${e}\0${r}`, i);
201
+ const r = Math.max(assignLanes(i), 1);
202
+ for (let i = 0; i < r; i++) {
203
+ const r = s++;
204
+ n.set(`${e}\0${i}`, r);
205
205
  t.push({
206
206
  ph: "M",
207
207
  name: "thread_name",
208
208
  pid: this.#n,
209
- tid: i,
209
+ tid: r,
210
210
  args: {
211
- name: 0 === r ? o : `${o} #${r + 1}`
211
+ name: 0 === i ? o : `${o} #${i + 1}`
212
212
  }
213
213
  }, {
214
214
  ph: "M",
215
215
  name: "thread_sort_index",
216
216
  pid: this.#n,
217
- tid: i,
217
+ tid: r,
218
218
  args: {
219
- sort_index: i
219
+ sort_index: r
220
220
  }
221
221
  });
222
222
  }
@@ -258,7 +258,7 @@ class TraceConverter {
258
258
  }
259
259
  };
260
260
  }
261
- #r(e, t) {
261
+ #i(e, t) {
262
262
  const n = t ? `${e}:${t}` : e;
263
263
  if (!this.#t.has(n)) {
264
264
  this.#t.set(n, t ? `${e} ${t}` : e);
@@ -274,13 +274,13 @@ function assignLanes(e) {
274
274
  const t = e.ts + e.dur;
275
275
  let s = -1;
276
276
  for (let o = 0; o < n.length; o++) {
277
- const r = n[o];
278
- while (r.length && r[r.length - 1].ts + r[r.length - 1].dur <= e.ts) {
279
- r.pop();
277
+ const i = n[o];
278
+ while (i.length && i[i.length - 1].ts + i[i.length - 1].dur <= e.ts) {
279
+ i.pop();
280
280
  }
281
- const i = r[r.length - 1];
282
- if (!i || i.ts + i.dur >= t) {
283
- r.push(e);
281
+ const r = i[i.length - 1];
282
+ if (!r || r.ts + r.dur >= t) {
283
+ i.push(e);
284
284
  s = o;
285
285
  break;
286
286
  }
@@ -306,7 +306,7 @@ function stripSuffix$1(e) {
306
306
  const b = [ ":started", ":done", ":failed" ];
307
307
 
308
308
  class OpenTelemetryConverter {
309
- #i=v.randomBytes(16).toString("hex");
309
+ #r=v.randomBytes(16).toString("hex");
310
310
  #a=createSpanId();
311
311
  #l=[];
312
312
  #p=[];
@@ -356,7 +356,7 @@ class OpenTelemetryConverter {
356
356
  name: "2g"
357
357
  },
358
358
  spans: [ {
359
- traceId: this.#i,
359
+ traceId: this.#r,
360
360
  spanId: this.#a,
361
361
  name: this.#s,
362
362
  startTimeUnixNano: e,
@@ -375,7 +375,7 @@ class OpenTelemetryConverter {
375
375
  const n = unixNano(e._t - e._d);
376
376
  const s = unixNano(e._t);
377
377
  return {
378
- traceId: this.#i,
378
+ traceId: this.#r,
379
379
  spanId: createSpanId(),
380
380
  parentSpanId: this.#a,
381
381
  name: t ? stripSuffix(t.name) : e._e,
@@ -500,7 +500,7 @@ function stripSuffix(e) {
500
500
  async function runExportCli(e) {
501
501
  if (parseHelp(e)) {
502
502
  !function printExportHelp() {
503
- process.stdout.write([ "Usage: 2g export [selector]", "", "Exports session events to Chrome Trace or OTLP JSON.", "", "Options:", " selector Substring of PID, CWD, or command; a running", " session wins ties", " --input <file> Read JSONL events from a file", " -o, --output <file> Write output to a file", " --format <format> chrome-trace or opentelemetry", " --json Print JSON output", " --since <time> Duration ago (5m, 90s), Unix time, or ISO date", " --filter <pattern> Event name prefix (whole segments), e.g. category,", " category:kind; * wildcards; comma-separated", " --spans Only span events (log.span() pairs carrying a _d", ' duration); a plain "ms" payload field is not a span', " --debug Include debug-level events", " --tail Follow live events after replaying history", " --timeout <duration> Stop tailing after an absolute duration", " Tail also stops after 30s without new events", "" ].join("\n"));
503
+ process.stdout.write([ "Usage: 2g export [selector]", "", "Exports session events to Chrome Trace or OTLP JSON.", "", "Options:", " selector Substring of PID, CWD, or command; a running", " session wins ties", " --input <file> Read JSONL events from a file", " -o, --output <file> Write output to a file", " --format <format> chrome-trace or opentelemetry", " --json Print JSON output", " --since <time> Duration ago (5m, 90s), Unix time, or ISO date", " --filter <pattern> Event name prefix (whole segments), e.g. category,", " category:kind; * wildcards; comma-separated", " --spans Keep only span events (log.span() pairs with a _d", " duration); omit to include point events too. A", ' plain "ms" payload field is not a span', " --debug Include debug events (only recorded when the", " command ran with LOG_DEBUG set)", " --tail Follow live events after replaying history", " --timeout <duration> Stop tailing after an absolute duration", " Tail also stops after 30s without new events", "" ].join("\n"));
504
504
  }();
505
505
  return;
506
506
  }
@@ -543,11 +543,11 @@ async function runExportCli(e) {
543
543
  }
544
544
  return t;
545
545
  }(n.values.timeout);
546
- const r = n.values.format ?? "chrome-trace";
546
+ const i = n.values.format ?? "chrome-trace";
547
547
  if (!function isExportFormat(e) {
548
548
  return "chrome-trace" === e || "opentelemetry" === e;
549
- }(r)) {
550
- throw new Error(`Unsupported format: ${r}`);
549
+ }(i)) {
550
+ throw new Error(`Unsupported format: ${i}`);
551
551
  }
552
552
  if (n.positionals[0] && n.values.input) {
553
553
  throw new Error("Use either a session selector or --input, not both");
@@ -559,7 +559,7 @@ async function runExportCli(e) {
559
559
  selector: n.positionals[0],
560
560
  inputFile: n.values.input,
561
561
  output: n.values.output,
562
- format: r,
562
+ format: i,
563
563
  since: s.since,
564
564
  filter: s.filter,
565
565
  spans: s.spans,
@@ -601,9 +601,9 @@ async function runExportCli(e) {
601
601
  }
602
602
  const u = async function* filterEvents(e, t) {
603
603
  const n = o(t.filter);
604
- const s = r(t.since);
604
+ const s = i(t.since);
605
605
  for await (const o of e) {
606
- if (i(o, {
606
+ if (r(o, {
607
607
  filter: t.filter,
608
608
  since: s,
609
609
  spans: t.spans,
@@ -613,7 +613,7 @@ async function runExportCli(e) {
613
613
  }
614
614
  }
615
615
  }(s, n);
616
- const g = "opentelemetry" === n.format ? await async function convertToOpenTelemetry(e, t = {}) {
616
+ const h = "opentelemetry" === n.format ? await async function convertToOpenTelemetry(e, t = {}) {
617
617
  const n = new OpenTelemetryConverter(t);
618
618
  for await (const t of e) {
619
619
  n.add(t);
@@ -632,11 +632,11 @@ async function runExportCli(e) {
632
632
  pid: c,
633
633
  processName: l
634
634
  });
635
- const h = JSON.stringify(g, null, 2);
635
+ const g = JSON.stringify(h, null, 2);
636
636
  if (n.output) {
637
- d.writeFileSync(n.output, `${h}\n`);
637
+ d.writeFileSync(n.output, `${g}\n`);
638
638
  } else {
639
- process.stdout.write(`${h}\n`);
639
+ process.stdout.write(`${g}\n`);
640
640
  }
641
641
  }
642
642
 
@@ -647,7 +647,7 @@ function escapeTsv(e) {
647
647
  async function runTapCli(e) {
648
648
  if (parseHelp(e)) {
649
649
  !function printTapHelp() {
650
- process.stdout.write([ "Usage: 2g tap [selector]", "", "Replays retained session history as JSONL, oldest first.", "", "Options:", " selector Substring of PID, CWD, or command; a running", " session wins ties", " --since <time> Duration ago (5m, 90s), Unix time, or ISO date", " --filter <pattern> Event name prefix (whole segments), e.g. category,", " category:kind; * wildcards; comma-separated", " --spans Only span events (log.span() pairs carrying a _d", ' duration); a plain "ms" payload field is not a span', " --debug Include debug-level events", " --format pretty Print a readable line per event", " --tail Follow live events after replaying history; runs", " until killed (use export with a timeout for a", " bounded follow)", "" ].join("\n"));
650
+ process.stdout.write([ "Usage: 2g tap [selector]", "", "Replays retained session history as JSONL, oldest first.", "", "Options:", " selector Substring of PID, CWD, or command; a running", " session wins ties", " --since <time> Duration ago (5m, 90s), Unix time, or ISO date", " --filter <pattern> Event name prefix (whole segments), e.g. category,", " category:kind; * wildcards; comma-separated", " --spans Keep only span events (log.span() pairs with a _d", " duration); omit to include point events too. A", ' plain "ms" payload field is not a span', " --debug Include debug events (only recorded when the", " command ran with LOG_DEBUG set)", " --format pretty Print a readable line per event", " --tail Follow live events after replaying history; runs", " until killed (use export with a timeout for a", " bounded follow)", "" ].join("\n"));
651
651
  }();
652
652
  return;
653
653
  }
@@ -705,7 +705,7 @@ async function main(e = process.argv.slice(2)) {
705
705
  const [n, ...s] = e;
706
706
  if (!n || "--help" === n || "-h" === n) {
707
707
  !function printHelp() {
708
- process.stdout.write([ "Usage: 2g <command>", "", "Reads structured JSONL event logs that instrumented CLIs record during runs", "into the system temp dir, with no setup: run a command, then tap it here.", "", "Commands:", " ps [selector] List recorded sessions", " --active, -a (running only) --json", "", " tap [selector] Replay session events as JSONL, oldest first", " --since <time> --filter <pattern> --spans --debug --format pretty", " --tail (follow live events after replay; runs until killed)", "", " export [selector] Export events to Chrome Trace or OTLP JSON", " --input <jsonl-file> -o, --output <file> --json", " --format chrome-trace|opentelemetry", " --since <time> --filter <pattern> --spans --debug", " --tail --timeout <duration> (absolute; tail also stops after 30s idle)", "", " typegen List every event name/payload a project can emit", " -p, --project <tsconfig> -o, --output <file> --format dts|json --json", "", " clean Remove stale sessions", " --all, -a (remove all exited sessions) --json", "", "Typical use:", " 2g ps -a # what is running now?", ' 2g tap "expo start" --tail # follow live events', ' 2g tap "expo start" --since 5m --format pretty # recent history, readable', ' 2g tap "expo start" --filter metro:bundling # narrow to one area', ' 2g export "expo start" -o trace.json # flame chart of a run', "", "Selectors match sessions by substring of PID, CWD, or command. If several", "match, and exactly one of them is running, it is chosen; otherwise 2g errors", "and lists the candidates.", "", "--filter takes comma-separated event-name prefixes matched on whole", 'segments: "category" and "category:kind" both match "category:kind:test";', "* matches anything. Discover names by tapping unfiltered or via typegen.", "", "--since accepts a duration ago (5m, 90s, 1h30m), Unix time, or ISO date.", '--spans keeps only span events (log.span() pairs with a "_d" duration in ms).', "", 'Events are single JSON lines: {"_e":"category:kind","_t":<epoch ms>,...}.', '"_d" is a span duration, "_w" a worker id; debug events need --debug.', "", "Set LOG_EVENTS=<file|fd> on an instrumented command to redirect its events;", "LOG_DEBUG=<filter> also mirrors matching events to stderr.", "", "Run 2g <command> --help for per-command help.", "" ].join("\n"));
708
+ process.stdout.write([ "Usage: 2g <command>", "", "Reads structured JSONL event logs that instrumented CLIs record during runs", "into the system temp dir, with no setup: run a command, then tap it here.", "", "Commands:", " ps [selector] List recorded sessions", " --active, -a (running only) --json", "", " tap [selector] Replay session events as JSONL, oldest first", " --since <time> --filter <pattern> --spans --debug --format pretty", " --tail (follow live events after replay; runs until killed)", "", " export [selector] Export events to Chrome Trace or OTLP JSON", " --input <jsonl-file> -o, --output <file> --json", " --format chrome-trace|opentelemetry", " --since <time> --filter <pattern> --spans --debug", " --tail --timeout <duration> (absolute; tail also stops after 30s idle)", "", " typegen List every event name/payload a project can emit", " -p, --project <tsconfig> -o, --output <file> --format dts|json --json", "", " clean Remove stale sessions", " --all, -a (remove all exited sessions) --json", "", "Typical use:", " 2g ps -a # what is running now?", ' 2g tap "expo start" --tail # follow live events', ' 2g tap "expo start" --since 5m --format pretty # recent history, readable', ' 2g tap "expo start" --filter metro:bundling # narrow to one area', ' 2g export "expo start" -o trace.json # flame chart of a run', "", "Selectors match sessions by substring of PID, CWD, or command. If several", "match, and exactly one of them is running, it is chosen; otherwise 2g errors", "and lists the candidates.", "", "--filter takes comma-separated event-name prefixes matched on whole", 'segments: "category" and "category:kind" both match "category:kind:test";', "* matches anything. Discover names by tapping unfiltered or via typegen.", "", "--since accepts a duration ago (5m, 90s, 1h30m), Unix time, or ISO date.", '--spans keeps only span events (log.span() pairs with a "_d" duration in', "ms); without it, point events are included alongside spans.", "", 'Events are single JSON lines: {"_e":"category:kind","_t":<epoch ms>,...}.', '"_d" is a span duration, "_w" a worker id; debug events need --debug.', "", "Set LOG_EVENTS=<file|fd> on an instrumented command to redirect its events.", "Debug events are only recorded when the command runs with LOG_DEBUG set.", "", "Pass LOG_DEBUG=<filter> when debugging; it also mirrors matching events to", "stderr. Reading them back needs --debug.", "", "Run 2g <command> --help for per-command help.", "" ].join("\n"));
709
709
  }();
710
710
  return 0;
711
711
  }
@@ -745,7 +745,7 @@ async function main(e = process.argv.slice(2)) {
745
745
  if (n.values.json) {
746
746
  p();
747
747
  }
748
- const s = (await g({
748
+ const s = (await h({
749
749
  selector: n.positionals[0]
750
750
  })).filter(e => !n.values.active || e.alive);
751
751
  if (n.values.json) {