skydive-cli 0.4.0-beta.1029 → 0.4.0-beta.1050

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/js/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as setActiveWorkspace, S as listWorkspaces, T as version, _ as themes, b as getActiveWorkspaceId, o as brandHelpArt, t as installCrashHandler, w as name, x as getSessionIdentity, y as ensureActiveOrganization } from "./install-C_N51msl.mjs";
2
+ import { C as ensureActiveOrganization, D as setActiveWorkspace, E as listWorkspaces, O as name, T as getSessionIdentity, a as installCrashHandler, k as version, t as maybeStartProfiling, u as brandHelpArt, w as getActiveWorkspaceId, x as themes } from "./profiler-Dsf_RNXl.mjs";
3
3
  import { A as getStoredApiKeyWorkspaceName, C as getLastSeenVersion, F as resolveManagementAuth, I as resolveSession, M as resolveAppUrl, N as resolveChatAuth, O as getShareMachineDefault, P as resolveConfig, R as saveConfig, S as getConfigPath, T as getPromptHistoryPath, V as setLastSeenVersion, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getUpdateCheckDisabled, k as getStoredApiKeyId, v as DEFAULT_API_URL, w as getPreference, x as deleteConfig, z as saveSession } from "./print-CiaU2ygh.mjs";
4
4
  import { n as printError, r as printTable, t as output } from "./output-DYzzdXYV.mjs";
5
5
  import { n as createRestClient, t as HttpError } from "./rest-CG7VpQ56.mjs";
@@ -1505,7 +1505,7 @@ const importCommand = {
1505
1505
  process.exit(1);
1506
1506
  }
1507
1507
  }
1508
- const { runChat } = await import("./boot-BSHn51iw.mjs");
1508
+ const { runChat } = await import("./boot-D7c-DOE9.mjs");
1509
1509
  await runChat({
1510
1510
  appUrl,
1511
1511
  sessionToken: session.value.sessionToken,
@@ -1917,7 +1917,7 @@ const chatCommand = {
1917
1917
  sessionToken: auth.value.token,
1918
1918
  agentSelector: argv.agent ?? null
1919
1919
  });
1920
- const { runChat } = await import("./boot-BSHn51iw.mjs");
1920
+ const { runChat } = await import("./boot-D7c-DOE9.mjs");
1921
1921
  await runChat({
1922
1922
  appUrl: auth.value.appUrl,
1923
1923
  sessionToken: auth.value.token,
@@ -2276,7 +2276,7 @@ const switchCommand = {
2276
2276
  printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
2277
2277
  process.exit(1);
2278
2278
  }
2279
- const { runWorkspacePicker } = await import("./boot-BSHn51iw.mjs");
2279
+ const { runWorkspacePicker } = await import("./boot-D7c-DOE9.mjs");
2280
2280
  await runWorkspacePicker(session);
2281
2281
  return;
2282
2282
  }
@@ -3881,6 +3881,7 @@ function maybePrintWhatsNew({ stderrIsTTY = process.stderr.isTTY ?? false, nonIn
3881
3881
  //#endregion
3882
3882
  //#region src/bin.ts
3883
3883
  installCrashHandler();
3884
+ maybeStartProfiling(hideBin(process.argv), version);
3884
3885
  if (process.argv.includes(UPDATE_WORKER_FLAG)) {
3885
3886
  await runUpdateCheckWorker();
3886
3887
  process.exit(0);
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as setActiveWorkspace, S as listWorkspaces, a as WORDMARK, b as getActiveWorkspaceId, c as applyTheme, d as noColorRequested, f as theme, g as themeVersion, h as themeModeFromColorFgBg, i as MARK_CELLS, l as findTheme, m as themeMode, n as buildCrashReport, p as themeForMode, r as writeCrashReport, s as DEFAULT_THEME_ID, t as installCrashHandler, u as monoTheme, v as themesForMode } from "./install-C_N51msl.mjs";
2
+ import { D as setActiveWorkspace, E as listWorkspaces, S as themesForMode, _ as themeForMode, a as installCrashHandler, b as themeVersion, c as MARK_CELLS, d as DEFAULT_THEME_ID, f as applyTheme, g as theme, h as noColorRequested, i as writeArtifact, l as WORDMARK, m as monoTheme, n as profilingEnabled, o as buildCrashReport, p as findTheme, r as record, s as writeCrashReport, v as themeMode, w as getActiveWorkspaceId, y as themeModeFromColorFgBg } from "./profiler-Dsf_RNXl.mjs";
3
3
  import { B as saveTheme, D as getSavedTheme, E as getReviewStateDir, L as resolveWebUrl, S as getConfigPath, c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams, v as DEFAULT_API_URL, y as DEFAULT_APP_URL } from "./print-CiaU2ygh.mjs";
4
4
  import { a as errorMessage, i as sendErrorMessage, n as createRestClient, o as isRecord, r as errorDetail, t as HttpError } from "./rest-CG7VpQ56.mjs";
5
5
  import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-CwfG1BTR.mjs";
@@ -19,7 +19,7 @@ import { connect, createConnection } from "node:net";
19
19
  import { access, appendFile, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
20
20
  import { MarkdownRenderable, RenderableEvents, StyledText, SyntaxStyle, addDefaultParsers, createCliRenderer, decodePasteBytes, detectLinks, fg, link } from "@opentui/core";
21
21
  import { createRoot, extend, useKeyboard, usePaste, useRenderer, useTerminalDimensions } from "@opentui/react";
22
- import { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
22
+ import { Profiler, createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
23
23
  import { create } from "zustand";
24
24
  import { Fragment, jsx, jsxs } from "@opentui/react/jsx-runtime";
25
25
  import fuzzysort from "fuzzysort";
@@ -1842,6 +1842,9 @@ function FilterableAgentList({ agents, scope, onPick }) {
1842
1842
  fg: theme.muted,
1843
1843
  children: "search "
1844
1844
  }), /* @__PURE__ */ jsx("input", {
1845
+ textColor: theme.fg,
1846
+ focusedTextColor: theme.fg,
1847
+ placeholderColor: theme.dim,
1845
1848
  placeholder: "type to filter…",
1846
1849
  focused: true,
1847
1850
  width: Math.max(10, width - 12),
@@ -2040,6 +2043,9 @@ function AgentCreateScreen() {
2040
2043
  flexShrink: 0
2041
2044
  },
2042
2045
  children: /* @__PURE__ */ jsx("input", {
2046
+ textColor: theme.fg,
2047
+ focusedTextColor: theme.fg,
2048
+ placeholderColor: theme.dim,
2043
2049
  placeholder: "Agent name",
2044
2050
  onInput: (value) => {
2045
2051
  setName(value);
@@ -2742,6 +2748,9 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
2742
2748
  fg: theme.muted,
2743
2749
  children: "search "
2744
2750
  }), /* @__PURE__ */ jsx("input", {
2751
+ textColor: theme.fg,
2752
+ focusedTextColor: theme.fg,
2753
+ placeholderColor: theme.dim,
2745
2754
  placeholder: "type to filter…",
2746
2755
  focused: confirmId === null,
2747
2756
  width: Math.max(10, width - 12),
@@ -6055,6 +6064,9 @@ function HelpModal({ onClose }) {
6055
6064
  fg: theme.muted,
6056
6065
  children: "search "
6057
6066
  }), /* @__PURE__ */ jsx("input", {
6067
+ textColor: theme.fg,
6068
+ focusedTextColor: theme.fg,
6069
+ placeholderColor: theme.dim,
6058
6070
  placeholder: "type to filter…",
6059
6071
  focused: true,
6060
6072
  width: Math.max(10, width - 12),
@@ -6618,6 +6630,9 @@ function FilterableModelList({ models, currentModel, modelLocked, agentId, onSel
6618
6630
  fg: theme.muted,
6619
6631
  children: "search "
6620
6632
  }), /* @__PURE__ */ jsx("input", {
6633
+ textColor: theme.fg,
6634
+ focusedTextColor: theme.fg,
6635
+ placeholderColor: theme.dim,
6621
6636
  placeholder: "type to filter…",
6622
6637
  focused: true,
6623
6638
  width: Math.max(10, width - 12),
@@ -7562,6 +7577,9 @@ function ReviewFileSearch({ query, onQueryChange, placeholder = "filter files…
7562
7577
  fg: theme.accent,
7563
7578
  children: "/ "
7564
7579
  }), /* @__PURE__ */ jsx("input", {
7580
+ textColor: theme.fg,
7581
+ focusedTextColor: theme.fg,
7582
+ placeholderColor: theme.dim,
7565
7583
  placeholder,
7566
7584
  focused: true,
7567
7585
  width: "100%",
@@ -8281,6 +8299,9 @@ function WorkspaceFilesTab({ agentId, viewportWidth, viewportHeight, active, she
8281
8299
  height: 3
8282
8300
  },
8283
8301
  children: /* @__PURE__ */ jsx("input", {
8302
+ textColor: theme.fg,
8303
+ focusedTextColor: theme.fg,
8304
+ placeholderColor: theme.dim,
8284
8305
  placeholder: "Leave a comment…",
8285
8306
  focused: true,
8286
8307
  width: Math.max(8, width - 10),
@@ -8808,6 +8829,9 @@ function ReviewPane({ agentId, conversationId, items, viewportWidth, viewportHei
8808
8829
  height: 3
8809
8830
  },
8810
8831
  children: /* @__PURE__ */ jsx("input", {
8832
+ textColor: theme.fg,
8833
+ focusedTextColor: theme.fg,
8834
+ placeholderColor: theme.dim,
8811
8835
  placeholder: "Leave a comment…",
8812
8836
  focused: true,
8813
8837
  width: Math.max(8, sourceWidth - 8),
@@ -11439,6 +11463,9 @@ function ChatScreen({ agent, conversation }) {
11439
11463
  },
11440
11464
  children: /* @__PURE__ */ jsx("textarea", {
11441
11465
  ref: composerRef,
11466
+ textColor: theme.fg,
11467
+ focusedTextColor: theme.fg,
11468
+ placeholderColor: theme.dim,
11442
11469
  placeholder: forking ? "forking into a new chat…" : `message ${agent.name}…`,
11443
11470
  keyBindings: composerKeyBindings,
11444
11471
  onContentChange: handleComposerChange,
@@ -11571,12 +11598,18 @@ function CredentialPromptBox({ prompt, height, onInput, onSubmit }) {
11571
11598
  ]
11572
11599
  }),
11573
11600
  field.secret ? /* @__PURE__ */ jsx("input", {
11601
+ textColor: theme.fg,
11602
+ focusedTextColor: theme.fg,
11603
+ placeholderColor: theme.dim,
11574
11604
  value: MASK_CHAR.repeat(prompt.current.length),
11575
11605
  placeholder: field.placeholder ?? "",
11576
11606
  onInput,
11577
11607
  onSubmit,
11578
11608
  focused: !prompt.submitting
11579
11609
  }, field.key) : /* @__PURE__ */ jsx("input", {
11610
+ textColor: theme.fg,
11611
+ focusedTextColor: theme.fg,
11612
+ placeholderColor: theme.dim,
11580
11613
  placeholder: field.placeholder ?? "",
11581
11614
  onInput,
11582
11615
  onSubmit,
@@ -12284,6 +12317,97 @@ async function settleBootThemeMode({ source, applyThemeMode, graceMs = THEME_QUE
12284
12317
  if (answered) applyThemeMode(answered);
12285
12318
  }
12286
12319
 
12320
+ //#endregion
12321
+ //#region src/profiling/tui.ts
12322
+ /**
12323
+ * TUI-side profiling instrumentation. Everything here rides on the shared
12324
+ * profile directory created in bin.ts (see profiler.ts) and is inert unless
12325
+ * SKYDIVE_PROFILE is set.
12326
+ */
12327
+ /**
12328
+ * Renderer options to merge into createCliRenderer when profiling:
12329
+ * gatherStats makes OpenTUI retain per-frame timings (frame time, cells
12330
+ * updated, native render + stdout write time) that getStats() exposes.
12331
+ */
12332
+ function profilingRendererOptions() {
12333
+ return profilingEnabled() ? { gatherStats: true } : {};
12334
+ }
12335
+ /**
12336
+ * Samples renderer stats periodically and dumps the full series at exit.
12337
+ * The periodic samples give the timeline shape (fps over the session); the
12338
+ * final dump carries the complete frame-time distribution.
12339
+ */
12340
+ function startRendererStatsCapture(renderer) {
12341
+ if (!profilingEnabled()) return;
12342
+ const sample = () => {
12343
+ const s = renderer.getStats();
12344
+ record("render", {
12345
+ fps: s.fps,
12346
+ frameCount: s.frameCount,
12347
+ averageFrameTime: s.averageFrameTime,
12348
+ maxFrameTime: s.maxFrameTime,
12349
+ cellsUpdated: s.cellsUpdated,
12350
+ nativeRenderTime: s.nativeRenderTime ?? null,
12351
+ nativeStdoutWriteTime: s.nativeStdoutWriteTime ?? null
12352
+ });
12353
+ };
12354
+ const interval = setInterval(sample, 1e3);
12355
+ interval.unref?.();
12356
+ process.on("exit", () => {
12357
+ clearInterval(interval);
12358
+ const s = renderer.getStats();
12359
+ writeArtifact("render.json", {
12360
+ fps: s.fps,
12361
+ frameCount: s.frameCount,
12362
+ frameTimes: s.frameTimes,
12363
+ averageFrameTime: s.averageFrameTime,
12364
+ minFrameTime: s.minFrameTime,
12365
+ maxFrameTime: s.maxFrameTime,
12366
+ cellsUpdated: s.cellsUpdated,
12367
+ averageCellsUpdated: s.averageCellsUpdated
12368
+ });
12369
+ });
12370
+ }
12371
+ /**
12372
+ * Records zustand store transitions. Subscribes to the whole store and
12373
+ * logs a compact diff-flavored event per change: the screen kind always,
12374
+ * plus which top-level keys changed. Gives an agent the "what was the UI
12375
+ * doing at t=X" axis without serializing message payloads.
12376
+ */
12377
+ function startStateCapture(store) {
12378
+ if (!profilingEnabled()) return;
12379
+ store.subscribe((state, prev) => {
12380
+ const prevEntries = new Map(Object.entries(prev));
12381
+ const changed = [];
12382
+ let screenKind = null;
12383
+ for (const [key, value] of Object.entries(state)) {
12384
+ if (typeof value === "function") continue;
12385
+ if (value !== prevEntries.get(key)) changed.push(key);
12386
+ if (key === "screen" && typeof value === "object" && value !== null && "kind" in value && typeof value.kind === "string") screenKind = value.kind;
12387
+ }
12388
+ if (changed.length === 0) return;
12389
+ record("state", {
12390
+ screen: screenKind,
12391
+ changed
12392
+ });
12393
+ });
12394
+ }
12395
+ /**
12396
+ * React Profiler onRender callback for the <Profiler> wrapping <App>.
12397
+ * One event per commit: which subtree, mount/update, and actual vs base
12398
+ * duration (actualDuration >> baseDuration means memoization is losing).
12399
+ */
12400
+ function onRenderCommit(id, phase, actualDuration, baseDuration, startTime, commitTime) {
12401
+ record("commits", {
12402
+ id,
12403
+ phase,
12404
+ actualDuration,
12405
+ baseDuration,
12406
+ startTime,
12407
+ commitTime
12408
+ });
12409
+ }
12410
+
12287
12411
  //#endregion
12288
12412
  //#region src/chat/boot.tsx
12289
12413
  /**
@@ -12296,7 +12420,12 @@ async function runChat({ appUrl, sessionToken, authKind = "session", shareMachin
12296
12420
  await registerGrammars();
12297
12421
  const watchdog = startMemoryWatchdog();
12298
12422
  installCrashHandler({ memoryProvider: () => watchdog.snapshot() });
12299
- const renderer = await createCliRenderer({ exitOnCtrlC: false });
12423
+ const renderer = await createCliRenderer({
12424
+ exitOnCtrlC: false,
12425
+ ...profilingRendererOptions()
12426
+ });
12427
+ startRendererStatsCapture(renderer);
12428
+ startStateCapture(useStore);
12300
12429
  extend({ markdown: LinkifiedMarkdownRenderable });
12301
12430
  useStore.setState({
12302
12431
  authKind,
@@ -12316,16 +12445,20 @@ async function runChat({ appUrl, sessionToken, authKind = "session", shareMachin
12316
12445
  source: renderer,
12317
12446
  applyThemeMode: (mode) => useStore.getState().setThemeMode(mode)
12318
12447
  });
12319
- createRoot(renderer).render(/* @__PURE__ */ jsx(App, {
12320
- appUrl,
12321
- sessionToken,
12322
- authKind,
12323
- shareMachine,
12324
- promptHistoryPath,
12325
- notifications,
12326
- agentSelector: agentSelector ?? null,
12327
- conversationId: conversationId ?? null,
12328
- newConversation
12448
+ createRoot(renderer).render(/* @__PURE__ */ jsx(Profiler, {
12449
+ id: "app",
12450
+ onRender: onRenderCommit,
12451
+ children: /* @__PURE__ */ jsx(App, {
12452
+ appUrl,
12453
+ sessionToken,
12454
+ authKind,
12455
+ shareMachine,
12456
+ promptHistoryPath,
12457
+ notifications,
12458
+ agentSelector: agentSelector ?? null,
12459
+ conversationId: conversationId ?? null,
12460
+ newConversation
12461
+ })
12329
12462
  }));
12330
12463
  }
12331
12464
  /** Open the same workspace picker used by the initial agent screen. */
@@ -8,7 +8,7 @@ import fs from "node:fs";
8
8
 
9
9
  //#region package.json
10
10
  var name = "skydive-cli";
11
- var version$1 = "0.4.0-beta.1029";
11
+ var version$1 = "0.4.0-beta.1050";
12
12
 
13
13
  //#endregion
14
14
  //#region src/auth/organization.ts
@@ -1057,4 +1057,211 @@ function printFatalNotice(file, memory) {
1057
1057
  }
1058
1058
 
1059
1059
  //#endregion
1060
- export { setActiveWorkspace as C, listWorkspaces as S, version$1 as T, themes as _, WORDMARK as a, getActiveWorkspaceId as b, applyTheme as c, noColorRequested as d, theme as f, themeVersion as g, themeModeFromColorFgBg as h, MARK_CELLS as i, findTheme as l, themeMode as m, buildCrashReport as n, brandHelpArt as o, themeForMode as p, writeCrashReport as r, DEFAULT_THEME_ID as s, installCrashHandler as t, monoTheme as u, themesForMode as v, name as w, getSessionIdentity as x, ensureActiveOrganization as y };
1060
+ //#region src/profiling/profiler.ts
1061
+ /**
1062
+ * Opt-in session profiling. When SKYDIVE_PROFILE is set, an invocation
1063
+ * records everything needed to reconstruct what happened performance-wise —
1064
+ * designed so the whole directory can be handed to an agent (or a human)
1065
+ * and read without special tooling:
1066
+ *
1067
+ * network.ndjson every fetch: method, url, status, timing, content type
1068
+ * state.ndjson TUI state transitions (screen changes, store updates)
1069
+ * commits.ndjson React commits: which subtree rendered, when, how long
1070
+ * render.ndjson OpenTUI renderer samples: fps, frame times, cells drawn
1071
+ * render.json final renderer stats dump (full frame-time series)
1072
+ * cpu-<pid>.cpuprofile V8 CPU profile (Node-run commands only)
1073
+ * meta-<pid>.json argv, versions, runtime, exit code, wall time
1074
+ *
1075
+ * Every ndjson event carries a wall-clock `t` (epoch ms) and `pid`, so
1076
+ * records from the Node parent and the Bun-re-exec'd chat TUI land in the
1077
+ * same files and stay correlatable on one clock. The profile directory is
1078
+ * created by the first process and shared with children through
1079
+ * SKYDIVE_PROFILE_DIR (the chat re-exec inherits the environment).
1080
+ *
1081
+ * Inert unless SKYDIVE_PROFILE is set: no patched fetch, no subscriptions,
1082
+ * no inspector session.
1083
+ */
1084
+ const ENV_FLAG = "SKYDIVE_PROFILE";
1085
+ const ENV_DIR = "SKYDIVE_PROFILE_DIR";
1086
+ let activeDir = null;
1087
+ let startedAtMs = 0;
1088
+ /**
1089
+ * Buffered event lines per stream, flushed asynchronously. Events are
1090
+ * appended to an in-memory buffer and written with fs.promises off the
1091
+ * hot path, so recording never blocks the TUI's event loop — high-rate
1092
+ * streams (React commits, renderer samples) stay cheap. Whatever is
1093
+ * still buffered when the process exits is drained synchronously in the
1094
+ * exit handler, where async I/O would never flush.
1095
+ */
1096
+ const pendingLines = /* @__PURE__ */ new Map();
1097
+ let flushScheduled = false;
1098
+ let flushing = Promise.resolve();
1099
+ function drainBuffersSync() {
1100
+ if (activeDir === null) return;
1101
+ for (const [stream, lines] of pendingLines) {
1102
+ if (lines.length === 0) continue;
1103
+ pendingLines.set(stream, []);
1104
+ try {
1105
+ fs.appendFileSync(path.join(activeDir, `${stream}.ndjson`), lines.join("\n") + "\n");
1106
+ } catch (_error) {}
1107
+ }
1108
+ }
1109
+ function scheduleFlush() {
1110
+ if (flushScheduled || activeDir === null) return;
1111
+ flushScheduled = true;
1112
+ setTimeout(() => {
1113
+ flushScheduled = false;
1114
+ flushing = flushing.then(async () => {
1115
+ if (activeDir === null) return;
1116
+ for (const [stream, lines] of pendingLines) {
1117
+ if (lines.length === 0) continue;
1118
+ pendingLines.set(stream, []);
1119
+ try {
1120
+ await fs.promises.appendFile(path.join(activeDir, `${stream}.ndjson`), lines.join("\n") + "\n");
1121
+ } catch (_error) {}
1122
+ }
1123
+ });
1124
+ }, 100).unref?.();
1125
+ }
1126
+ function profilingEnabled() {
1127
+ const v = process.env[ENV_FLAG];
1128
+ return v !== void 0 && v !== "" && v !== "0";
1129
+ }
1130
+ function sanitizeToken(token) {
1131
+ return token.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
1132
+ }
1133
+ /**
1134
+ * Appends one event to a stream file. Buffered and written asynchronously
1135
+ * so recording never blocks the event loop; the exit handler drains any
1136
+ * remainder synchronously so abrupt exits still keep their events.
1137
+ */
1138
+ function record(stream, event) {
1139
+ if (activeDir === null) return;
1140
+ const line = JSON.stringify({
1141
+ t: Date.now(),
1142
+ pid: process.pid,
1143
+ ...event
1144
+ });
1145
+ const lines = pendingLines.get(stream);
1146
+ if (lines === void 0) pendingLines.set(stream, [line]);
1147
+ else lines.push(line);
1148
+ scheduleFlush();
1149
+ }
1150
+ /** Writes a JSON artifact (non-append) into the profile directory. */
1151
+ function writeArtifact(name, data) {
1152
+ if (activeDir === null) return;
1153
+ try {
1154
+ fs.writeFileSync(path.join(activeDir, name), JSON.stringify(data, null, 2));
1155
+ } catch (_error) {}
1156
+ }
1157
+ /**
1158
+ * Patches globalThis.fetch to record request timing. A wrapper (not
1159
+ * undici's diagnostics_channel) because chat re-execs under Bun, where
1160
+ * fetch is Bun-native and undici events never fire; the wrapper behaves
1161
+ * identically in both runtimes.
1162
+ */
1163
+ function installFetchRecorder() {
1164
+ const original = globalThis.fetch;
1165
+ const wrapped = async (...args) => {
1166
+ const [input, init] = args;
1167
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1168
+ const method = init?.method ?? (input instanceof Request ? input.method : "GET");
1169
+ const started = Date.now();
1170
+ try {
1171
+ const res = await original(...args);
1172
+ const contentType = res.headers.get("content-type") ?? "";
1173
+ record("network", {
1174
+ method,
1175
+ url,
1176
+ status: res.status,
1177
+ durationMs: Date.now() - started,
1178
+ contentType,
1179
+ streamed: contentType.includes("text/event-stream")
1180
+ });
1181
+ return res;
1182
+ } catch (err) {
1183
+ record("network", {
1184
+ method,
1185
+ url,
1186
+ status: 0,
1187
+ durationMs: Date.now() - started,
1188
+ error: err instanceof Error ? err.message : String(err)
1189
+ });
1190
+ throw err;
1191
+ }
1192
+ };
1193
+ globalThis.fetch = Object.assign(wrapped, original);
1194
+ }
1195
+ function isBunRuntime() {
1196
+ return typeof process !== "undefined" && "bun" in process.versions;
1197
+ }
1198
+ /**
1199
+ * Starts the V8 CPU profiler via node:inspector. Node-run commands only —
1200
+ * Bun does not implement the inspector Profiler domain, so the chat TUI
1201
+ * skips this artifact.
1202
+ */
1203
+ function startCpuProfile() {
1204
+ if (isBunRuntime()) return;
1205
+ (async () => {
1206
+ try {
1207
+ const { Session } = await import("node:inspector/promises");
1208
+ const session = new Session();
1209
+ session.connect();
1210
+ await session.post("Profiler.enable");
1211
+ await session.post("Profiler.start");
1212
+ process.once("beforeExit", () => {
1213
+ (async () => {
1214
+ try {
1215
+ const { profile } = await session.post("Profiler.stop");
1216
+ writeArtifact(`cpu-${process.pid}.cpuprofile`, profile);
1217
+ session.disconnect();
1218
+ } catch (_error) {}
1219
+ })();
1220
+ });
1221
+ } catch (_error) {}
1222
+ })();
1223
+ }
1224
+ /**
1225
+ * Activates profiling for this process if SKYDIVE_PROFILE is set. Creates
1226
+ * the profile directory (or joins the one a parent process created),
1227
+ * patches fetch, starts the CPU profiler, and registers the exit-time
1228
+ * meta dump. Call once, as early as possible.
1229
+ */
1230
+ function maybeStartProfiling(argv, cliVersion) {
1231
+ if (!profilingEnabled() || activeDir !== null) return;
1232
+ const inherited = process.env[ENV_DIR];
1233
+ if (inherited !== void 0 && inherited !== "") activeDir = inherited;
1234
+ else {
1235
+ const flagValue = process.env[ENV_FLAG] ?? "1";
1236
+ const command = argv.find((a) => !a.startsWith("-")) ?? "chat";
1237
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
1238
+ activeDir = flagValue === "1" || flagValue.toLowerCase() === "true" ? path.resolve(process.cwd(), `skydive-profile-${sanitizeToken(command)}-${stamp}`) : path.resolve(process.cwd(), flagValue);
1239
+ process.env[ENV_DIR] = activeDir;
1240
+ }
1241
+ try {
1242
+ fs.mkdirSync(activeDir, { recursive: true });
1243
+ } catch (_error) {
1244
+ activeDir = null;
1245
+ return;
1246
+ }
1247
+ startedAtMs = Date.now();
1248
+ installFetchRecorder();
1249
+ startCpuProfile();
1250
+ process.on("exit", (code) => {
1251
+ drainBuffersSync();
1252
+ writeArtifact(`meta-${process.pid}.json`, {
1253
+ argv,
1254
+ cliVersion,
1255
+ runtime: isBunRuntime() ? `bun ${process.versions["bun"]}` : `node ${process.version}`,
1256
+ platform: process.platform,
1257
+ pid: process.pid,
1258
+ exitCode: code,
1259
+ startedAt: new Date(startedAtMs).toISOString(),
1260
+ wallTimeMs: Date.now() - startedAtMs
1261
+ });
1262
+ if (process.env[ENV_DIR] === activeDir && inherited === void 0) process.stderr.write(`\nprofile written to ${activeDir}\n`);
1263
+ });
1264
+ }
1265
+
1266
+ //#endregion
1267
+ export { ensureActiveOrganization as C, setActiveWorkspace as D, listWorkspaces as E, name as O, themesForMode as S, getSessionIdentity as T, themeForMode as _, installCrashHandler as a, themeVersion as b, MARK_CELLS as c, DEFAULT_THEME_ID as d, applyTheme as f, theme as g, noColorRequested as h, writeArtifact as i, version$1 as k, WORDMARK as l, monoTheme as m, profilingEnabled as n, buildCrashReport as o, findTheme as p, record as r, writeCrashReport as s, maybeStartProfiling as t, brandHelpArt as u, themeMode as v, getActiveWorkspaceId as w, themes as x, themeModeFromColorFgBg as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.4.0-beta.1029",
3
+ "version": "0.4.0-beta.1050",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",