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.
- package/CHANGELOG.md +14 -0
- package/README.md +4 -4
- package/dist/2g.js +6 -9
- package/dist/2g.js.map +1 -1
- package/dist/2g.mjs +62 -65
- package/dist/2g.mjs.map +1 -1
- package/dist/chunks/cli-chunk.js +3 -3
- package/dist/chunks/cli-chunk.js.map +1 -1
- package/dist/chunks/cli-chunk.mjs +42 -42
- package/dist/chunks/cli-chunk.mjs.map +1 -1
- package/dist/chunks/types-chunk.d.ts +1 -4
- package/package.json +1 -1
|
@@ -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
|
|
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
|
|
11
|
+
import { r as f, t as m, l as h } from "./tap-chunk.mjs";
|
|
12
12
|
|
|
13
|
-
import { createInterface as
|
|
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
|
|
43
|
+
const i = o.all ? n() : s();
|
|
44
44
|
process.stdout.write(o.json ? `${JSON.stringify({
|
|
45
|
-
removed:
|
|
46
|
-
})}\n` : `Removed ${
|
|
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 =
|
|
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.#
|
|
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
|
|
195
|
+
const i = [];
|
|
196
196
|
for (const t of this.#e) {
|
|
197
197
|
if ("span" === t.kind && t.track === e) {
|
|
198
|
-
|
|
198
|
+
i.push(t);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
-
const
|
|
202
|
-
for (let
|
|
203
|
-
const
|
|
204
|
-
n.set(`${e}\0${
|
|
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:
|
|
209
|
+
tid: r,
|
|
210
210
|
args: {
|
|
211
|
-
name: 0 ===
|
|
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:
|
|
217
|
+
tid: r,
|
|
218
218
|
args: {
|
|
219
|
-
sort_index:
|
|
219
|
+
sort_index: r
|
|
220
220
|
}
|
|
221
221
|
});
|
|
222
222
|
}
|
|
@@ -258,7 +258,7 @@ class TraceConverter {
|
|
|
258
258
|
}
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
|
-
#
|
|
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
|
|
278
|
-
while (
|
|
279
|
-
|
|
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
|
|
282
|
-
if (!
|
|
283
|
-
|
|
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
|
-
#
|
|
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.#
|
|
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.#
|
|
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
|
|
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
|
|
546
|
+
const i = n.values.format ?? "chrome-trace";
|
|
547
547
|
if (!function isExportFormat(e) {
|
|
548
548
|
return "chrome-trace" === e || "opentelemetry" === e;
|
|
549
|
-
}(
|
|
550
|
-
throw new Error(`Unsupported format: ${
|
|
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:
|
|
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 =
|
|
604
|
+
const s = i(t.since);
|
|
605
605
|
for await (const o of e) {
|
|
606
|
-
if (
|
|
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
|
|
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
|
|
635
|
+
const g = JSON.stringify(h, null, 2);
|
|
636
636
|
if (n.output) {
|
|
637
|
-
d.writeFileSync(n.output, `${
|
|
637
|
+
d.writeFileSync(n.output, `${g}\n`);
|
|
638
638
|
} else {
|
|
639
|
-
process.stdout.write(`${
|
|
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
|
|
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).
|
|
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
|
|
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) {
|