skydive-cli 0.5.0-beta.12 → 0.5.0-beta.14

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,12 +1,12 @@
1
1
  #!/usr/bin/env node
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-CZIaF_t9.mjs";
3
- import { A as getStoredApiKeyId, B as saveConfig, C as getDefaultAgent, E as getPromptHistoryPath, F as resolveChatAuth, I as resolveConfig, L as resolveManagementAuth, M as getUpdateCheckDisabled, P as resolveAppUrl, R as resolveSession, S as getConfigPath, T as getPreference, U as setLastSeenVersion, V as saveSession, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getStoredApiKeyWorkspaceName, k as getShareMachineDefault, v as DEFAULT_API_URL, w as getLastSeenVersion, x as deleteConfig } from "./print-ClPkgR9z.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-COGO4LN8.mjs";
3
+ import { A as getStoredApiKeyId, B as saveConfig, C as getDefaultAgent, E as getPromptHistoryPath, F as resolveChatAuth, I as resolveConfig, L as resolveManagementAuth, M as getUpdateCheckDisabled, P as resolveAppUrl, R as resolveSession, S as getConfigPath, T as getPreference, U as setLastSeenVersion, V as saveSession, _ as API_KEY_PREFIX, b as PREFERENCES, g as API_KEY_FAMILY_PREFIX, h as API_KEYS_URL, i as resolveAgent, j as getStoredApiKeyWorkspaceName, k as getShareMachineDefault, v as DEFAULT_API_URL, w as getLastSeenVersion, x as deleteConfig } from "./print-riiY5Gly.mjs";
4
4
  import { n as printError, r as printTable, t as output } from "./output-DYzzdXYV.mjs";
5
5
  import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
6
- import { t as createRestClient } from "./rest-I3imNduB.mjs";
6
+ import { t as createRestClient } from "./rest-CLrHVkxh.mjs";
7
7
  import "./billing-blocked-2wju4gC_.mjs";
8
- import { r as resolveMachineIdentity } from "./client-DabRpc_T.mjs";
9
- import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-DLY2NmVU.mjs";
8
+ import { r as resolveMachineIdentity } from "./client-DbqRBquD.mjs";
9
+ import { i as queryDaemonStatus, l as PORTAL_DAEMON_FLAG, n as ensureDaemonRunning, s as stopDaemon, u as daemonPaths } from "./daemon-CL25CwjW.mjs";
10
10
  import { i as grantPortalAccess, n as fetchPortalDevices, o as registerPortalDevice, r as findThisDevice, s as revokePortalAccess } from "./api-DG5W6iwx.mjs";
11
11
  import { t as SandboxStream } from "./client-c4c5MmgN.mjs";
12
12
  import { hideBin } from "yargs/helpers";
@@ -1517,7 +1517,7 @@ const importCommand = {
1517
1517
  process.exit(1);
1518
1518
  }
1519
1519
  }
1520
- const { runChat } = await import("./boot-DNe3CikP.mjs");
1520
+ const { runChat } = await import("./boot-CJKq8rvL.mjs");
1521
1521
  await runChat({
1522
1522
  appUrl,
1523
1523
  sessionToken: session.value.sessionToken,
@@ -1545,7 +1545,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1545
1545
  printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
1546
1546
  process.exit(1);
1547
1547
  }
1548
- const { connectMachineShare } = await import("./print-share-CKLPmsg0.mjs");
1548
+ const { connectMachineShare } = await import("./print-share-BH9gvCls.mjs");
1549
1549
  const machineShare = await connectMachineShare({
1550
1550
  appUrl,
1551
1551
  sessionToken: session.value.sessionToken,
@@ -1553,7 +1553,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1553
1553
  });
1554
1554
  const extra = (argv.print ?? "").trim();
1555
1555
  const prompt = buildImportSeedPrompt(process.cwd()) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
1556
- const { runPrint } = await import("./print-Bo06tvfV.mjs");
1556
+ const { runPrint } = await import("./print-DTx2HyDY.mjs");
1557
1557
  try {
1558
1558
  const result = await runPrint({
1559
1559
  appUrl,
@@ -1930,7 +1930,7 @@ const chatCommand = {
1930
1930
  sessionToken: auth.value.token,
1931
1931
  agentSelector: argv.agent ?? null
1932
1932
  });
1933
- const { runChat } = await import("./boot-DNe3CikP.mjs");
1933
+ const { runChat } = await import("./boot-CJKq8rvL.mjs");
1934
1934
  await runChat({
1935
1935
  appUrl: auth.value.appUrl,
1936
1936
  sessionToken: auth.value.token,
@@ -1998,7 +1998,7 @@ async function runPrintMode({ argv, appUrl }) {
1998
1998
  printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
1999
1999
  process.exit(1);
2000
2000
  }
2001
- const { runPrint, readStdin } = await import("./print-Bo06tvfV.mjs");
2001
+ const { runPrint, readStdin } = await import("./print-DTx2HyDY.mjs");
2002
2002
  await ensureAgentWorkspace({
2003
2003
  appUrl,
2004
2004
  sessionToken: auth.value.token,
@@ -2023,7 +2023,7 @@ async function runPrintMode({ argv, appUrl }) {
2023
2023
  process.exit(1);
2024
2024
  }
2025
2025
  } else {
2026
- const { connectMachineShare } = await import("./print-share-CKLPmsg0.mjs");
2026
+ const { connectMachineShare } = await import("./print-share-BH9gvCls.mjs");
2027
2027
  machineShare = await connectMachineShare({
2028
2028
  appUrl: auth.value.appUrl,
2029
2029
  sessionToken: auth.value.token,
@@ -2087,7 +2087,7 @@ const getCommand$1 = {
2087
2087
  printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
2088
2088
  process.exit(1);
2089
2089
  }
2090
- const { messageGet } = await import("./print-Bo06tvfV.mjs");
2090
+ const { messageGet } = await import("./print-DTx2HyDY.mjs");
2091
2091
  try {
2092
2092
  const result = await messageGet({
2093
2093
  appUrl: auth.value.appUrl,
@@ -2290,7 +2290,7 @@ const switchCommand = {
2290
2290
  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.");
2291
2291
  process.exit(1);
2292
2292
  }
2293
- const { runWorkspacePicker } = await import("./boot-DNe3CikP.mjs");
2293
+ const { runWorkspacePicker } = await import("./boot-CJKq8rvL.mjs");
2294
2294
  await runWorkspacePicker(session);
2295
2295
  return;
2296
2296
  }
@@ -2339,7 +2339,7 @@ const workspaceCommand = {
2339
2339
  */
2340
2340
  async function resolveGrantTarget(argv) {
2341
2341
  const session = requireSession(argv);
2342
- const [{ devices, agents }, identity] = await Promise.all([fetchPortalDevices(session), resolveMachineIdentity()]);
2342
+ const [{ devices, agents }, identity] = await Promise.all([fetchPortalDevices(session), resolveMachineIdentity(null)]);
2343
2343
  return {
2344
2344
  session,
2345
2345
  agent: resolveAgent(agents, argv.agent),
@@ -2368,8 +2368,8 @@ const openCommand = {
2368
2368
  const session = requireSession(argv);
2369
2369
  const agent = argv.agent ? resolveAgent((await fetchPortalDevices(session)).agents, argv.agent) : null;
2370
2370
  const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
2371
- const { machineName } = await resolveMachineIdentity();
2372
- const { PortalDaemonClient } = await import("./daemon-client-DnhKkk1Y.mjs");
2371
+ const { machineName } = await resolveMachineIdentity(null);
2372
+ const { PortalDaemonClient } = await import("./daemon-client-DEyOdfdA.mjs");
2373
2373
  let lastLine = "";
2374
2374
  let signalConnected;
2375
2375
  const connected = new Promise((resolve) => {
@@ -2468,7 +2468,7 @@ const statusCommand = {
2468
2468
  console.log("No machines registered. Run `skydive portal open` to register this one.");
2469
2469
  return;
2470
2470
  }
2471
- const { headers, rows } = buildDeviceTable(devices, agents, (await resolveMachineIdentity()).machineName);
2471
+ const { headers, rows } = buildDeviceTable(devices, agents, (await resolveMachineIdentity(null)).machineName);
2472
2472
  printTable(headers, rows);
2473
2473
  }
2474
2474
  };
@@ -2679,8 +2679,8 @@ const sandboxCommand = {
2679
2679
  }).example("skydive sandbox --agent grace", "Live terminal (Ctrl-] detaches)").example("skydive sandbox --agent grace -- tail -n 50 /tmp/harness.log", "One-shot command (use `--` so its flags reach the sandbox)").example("skydive sandbox --agent grace -- sh -c 'ls /tmp | wc -l'", "Shell features go through an explicit `sh -c`"),
2680
2680
  handler: async (argv) => {
2681
2681
  const session = requireSession(argv);
2682
- const { createRestClient } = await import("./rest-D29qNkto.mjs");
2683
- const { resolveAgent } = await import("./print-Bo06tvfV.mjs");
2682
+ const { createRestClient } = await import("./rest-DsdPAE8K.mjs");
2683
+ const { resolveAgent } = await import("./print-DTx2HyDY.mjs");
2684
2684
  const client = createRestClient({
2685
2685
  appUrl: session.appUrl,
2686
2686
  sessionToken: session.sessionToken
@@ -2837,8 +2837,8 @@ const fsEditCommand = {
2837
2837
  handler: async (argv) => {
2838
2838
  const session = requireSession(argv);
2839
2839
  const remotePath = argv.path;
2840
- const { createRestClient } = await import("./rest-D29qNkto.mjs");
2841
- const { resolveAgent } = await import("./print-Bo06tvfV.mjs");
2840
+ const { createRestClient } = await import("./rest-DsdPAE8K.mjs");
2841
+ const { resolveAgent } = await import("./print-DTx2HyDY.mjs");
2842
2842
  const client = createRestClient({
2843
2843
  appUrl: session.appUrl,
2844
2844
  sessionToken: session.sessionToken
@@ -4215,7 +4215,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
4215
4215
  process.exit(0);
4216
4216
  }
4217
4217
  if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
4218
- const { runPortalDaemon } = await import("./daemon-BuAxg1_H.mjs");
4218
+ const { runPortalDaemon } = await import("./daemon-DjsUMczp.mjs");
4219
4219
  runPortalDaemon(process.argv);
4220
4220
  } else runCli();
4221
4221
  function runCli() {
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
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-CZIaF_t9.mjs";
3
- import { D as getReviewStateDir, H as saveTheme, N as recordDefaultAgent, O as getSavedTheme, 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, z as resolveWebUrl } from "./print-ClPkgR9z.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-COGO4LN8.mjs";
3
+ import { D as getReviewStateDir, H as saveTheme, N as recordDefaultAgent, O as getSavedTheme, 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, z as resolveWebUrl } from "./print-riiY5Gly.mjs";
4
4
  import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
5
- import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-I3imNduB.mjs";
5
+ import { a as isRecord, i as errorMessage, n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-CLrHVkxh.mjs";
6
6
  import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-2wju4gC_.mjs";
7
- import { t as PortalClient } from "./client-DabRpc_T.mjs";
8
- import "./daemon-DLY2NmVU.mjs";
7
+ import { t as PortalClient } from "./client-DbqRBquD.mjs";
8
+ import "./daemon-CL25CwjW.mjs";
9
9
  import "./api-DG5W6iwx.mjs";
10
10
  import { t as SandboxStream } from "./client-c4c5MmgN.mjs";
11
- import { t as PortalDaemonClient } from "./daemon-client-CG6KWbdI.mjs";
11
+ import { t as PortalDaemonClient } from "./daemon-client-k8ICifN2.mjs";
12
12
  import { t as runRawPtyPassthrough } from "./raw-pty-DY4KelZW.mjs";
13
13
  import * as os$1 from "node:os";
14
14
  import { homedir, platform, release, tmpdir } from "node:os";
@@ -386,6 +386,8 @@ function createPortalClient(opts) {
386
386
  deviceToken: null
387
387
  }),
388
388
  resolveCwd: () => process.cwd(),
389
+ persistedMachineName: null,
390
+ onMachineName: () => {},
389
391
  onState: (state) => opts.onState({
390
392
  status: state.status,
391
393
  machineName: state.machineName,
@@ -2841,15 +2843,20 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
2841
2843
  children: [marker, truncate("+ new conversation", rowWidth)]
2842
2844
  }, "new");
2843
2845
  const archived = isArchived(row.hit.item);
2846
+ const unread = row.hit.item.unread === true;
2844
2847
  const { title, detail, tag } = fitRowText(singleLine(row.hit.item.title ?? "(untitled)"), `${archived ? "archived · " : ""}${previewLine(row.hit.item)}`, rowWidth, coAgentsTag(row.hit.item, agent.id));
2848
+ const titleText = /* @__PURE__ */ jsx(FuzzyText, {
2849
+ text: title,
2850
+ indexes: row.hit.item.title ? row.hit.highlights[0] : null
2851
+ });
2845
2852
  return /* @__PURE__ */ jsxs("text", {
2846
2853
  fg: archived && !selected ? theme.dim : fg,
2847
2854
  children: [
2848
- marker,
2849
- /* @__PURE__ */ jsx(FuzzyText, {
2850
- text: title,
2851
- indexes: row.hit.item.title ? row.hit.highlights[0] : null
2852
- }),
2855
+ !selected && unread ? /* @__PURE__ */ jsx("span", {
2856
+ fg: theme.accent,
2857
+ children: "● "
2858
+ }) : marker,
2859
+ unread ? /* @__PURE__ */ jsx("b", { children: titleText }) : titleText,
2853
2860
  tag ? /* @__PURE__ */ jsxs("span", {
2854
2861
  fg: theme.muted,
2855
2862
  children: [" ", tag]
@@ -10738,6 +10745,8 @@ function ChatScreen({ agent, conversation }) {
10738
10745
  const menuHighlightClamped = Math.min(menuHighlight, Math.max(0, menuCommands.length - 1));
10739
10746
  const runRef = useRef(run);
10740
10747
  runRef.current = run;
10748
+ const conversationIdRef = useRef(conversationId);
10749
+ conversationIdRef.current = conversationId;
10741
10750
  const itemsRef = useRef(items);
10742
10751
  itemsRef.current = items;
10743
10752
  const attachmentsRef = useRef(attachments);
@@ -10907,6 +10916,15 @@ function ChatScreen({ agent, conversation }) {
10907
10916
  initialConversationId,
10908
10917
  agentHost
10909
10918
  ]);
10919
+ const markRead = useCallback(() => {
10920
+ const id = conversationIdRef.current;
10921
+ if (!rest || !id) return;
10922
+ rest.markConversationRead({ conversationId: id }).catch(() => {});
10923
+ }, [rest]);
10924
+ useEffect(() => {
10925
+ if (!conversationId) return;
10926
+ markRead();
10927
+ }, [conversationId, markRead]);
10910
10928
  const attachToRun = useCallback((runId, runAgentName, runStartedAtMs) => {
10911
10929
  if (!rest) return;
10912
10930
  seenRunsRef.current.add(runId);
@@ -10959,6 +10977,7 @@ function ChatScreen({ agent, conversation }) {
10959
10977
  attachments: m.attachments
10960
10978
  } : m));
10961
10979
  setRun({ kind: "idle" });
10980
+ markRead();
10962
10981
  agentHost.transition({
10963
10982
  state: "idle",
10964
10983
  notification: {
@@ -10980,7 +10999,8 @@ function ChatScreen({ agent, conversation }) {
10980
10999
  }, [
10981
11000
  rest,
10982
11001
  agent.name,
10983
- agentHost
11002
+ agentHost,
11003
+ markRead
10984
11004
  ]);
10985
11005
  const multiAgent = useMemo(() => conversationAgentNames(items, agent.name), [items, agent.name]).size > 1;
10986
11006
  const transcriptWindow = useMemo(() => windowItems(items), [items]);
@@ -2,11 +2,14 @@
2
2
  import { a as mintPortalDeviceToken, c as unifyLegacyCliGrants, i as grantPortalAccess, n as fetchPortalDevices, r as findThisDevice } from "./api-DG5W6iwx.mjs";
3
3
  import os from "node:os";
4
4
  import { z } from "zod";
5
- import { execFile, spawn } from "node:child_process";
5
+ import * as childProcess from "node:child_process";
6
+ import { spawn } from "node:child_process";
6
7
  import net from "node:net";
7
8
  import { WebSocket, createWebSocketStream } from "ws";
8
9
 
9
10
  //#region ../portal-daemon/src/machine.ts
11
+ const SCUTIL_TIMEOUT_MS = 2e3;
12
+ const SCUTIL_ATTEMPTS = 3;
10
13
  /**
11
14
  * Identity this machine registers under on the portal.
12
15
  *
@@ -15,38 +18,74 @@ import { WebSocket, createWebSocketStream } from "ws";
15
18
  * row, so a grant applies to the machine no matter which surface the user
16
19
  * shared from. The name must therefore match what the desktop app registers —
17
20
  * on macOS that is `scutil --get LocalHostName` (the Rust client's own
18
- * derivation, stabler than the network-assigned hostname), with the plain
19
- * hostname as the fallback for other platforms or a failed read.
21
+ * derivation, stabler than the network-assigned hostname).
22
+ *
23
+ * The machine name is a durable IDENTITY, not a fresh network reading: every
24
+ * grant hangs off the device row keyed by this name, so if it changes the
25
+ * device flips to a brand-new row with ZERO grants and every agent re-prompts
26
+ * across every open CLI. `os.hostname()` is NOT durable — macOS rewrites it
27
+ * from DHCP/DNS per network — so it must never be allowed to silently replace
28
+ * a name we already registered under. Resolution order:
29
+ *
30
+ * 1. `scutil --get LocalHostName` (retried) — the stable, correct source.
31
+ * 2. `previous` — the name we last successfully registered under, persisted
32
+ * by the daemon. Preferred over the volatile hostname so a transient
33
+ * `scutil` failure keeps our existing identity (and grants) intact.
34
+ * 3. `os.hostname()` — last resort for a machine that has NEVER resolved a
35
+ * name (first run, non-macOS, scutil permanently unavailable).
20
36
  *
21
37
  * Historically the CLI suffixed `-cli` to keep a device distinct from the
22
38
  * desktop's; `unifyLegacyCliGrants` migrates grants off those rows.
23
39
  */
24
- async function resolveMachineIdentity() {
25
- const fallback = fallbackHost();
26
- if (process.platform !== "darwin") return {
27
- machineName: fallback,
28
- friendlyName: fallback
40
+ async function resolveMachineIdentity(previous) {
41
+ const persisted = previous?.trim() || null;
42
+ if (process.platform !== "darwin") {
43
+ const machineName = persisted ?? fallbackHost();
44
+ return {
45
+ machineName,
46
+ friendlyName: machineName,
47
+ machineNameSource: persisted ? "persisted" : "hostname-fallback"
48
+ };
49
+ }
50
+ const local = await scutilRead("LocalHostName");
51
+ if (local) return {
52
+ machineName: local,
53
+ friendlyName: await scutilRead("ComputerName") ?? local,
54
+ machineNameSource: "scutil"
55
+ };
56
+ if (persisted) return {
57
+ machineName: persisted,
58
+ friendlyName: persisted,
59
+ machineNameSource: "persisted"
29
60
  };
30
- const local = await scutilRead("LocalHostName") ?? fallback;
61
+ const fallback = fallbackHost();
31
62
  return {
32
- machineName: local,
33
- friendlyName: await scutilRead("ComputerName") ?? local
63
+ machineName: fallback,
64
+ friendlyName: fallback,
65
+ machineNameSource: "hostname-fallback"
34
66
  };
35
67
  }
36
68
  function fallbackHost() {
37
69
  return (os.hostname() || "machine").trim().replace(/\.local$/i, "") || "machine";
38
70
  }
39
71
  function scutilRead(key) {
40
- return new Promise((resolve) => {
41
- execFile("/usr/sbin/scutil", ["--get", key], { timeout: 2e3 }, (error, stdout) => {
42
- if (error) {
43
- resolve(null);
44
- return;
45
- }
46
- const value = stdout.trim();
47
- resolve(value.length > 0 ? value : null);
72
+ return attempt(0);
73
+ function attempt(n) {
74
+ return new Promise((resolve) => {
75
+ childProcess.execFile("/usr/sbin/scutil", ["--get", key], { timeout: SCUTIL_TIMEOUT_MS }, (error, stdout) => {
76
+ if (error) {
77
+ if (n + 1 < SCUTIL_ATTEMPTS) {
78
+ resolve(attempt(n + 1));
79
+ return;
80
+ }
81
+ resolve(null);
82
+ return;
83
+ }
84
+ const value = stdout.trim();
85
+ resolve(value.length > 0 ? value : null);
86
+ });
48
87
  });
49
- });
88
+ }
50
89
  }
51
90
  const INHERITED_ENV = [
52
91
  "HOME",
@@ -550,9 +589,10 @@ var PortalClient = class {
550
589
  }
551
590
  async connectLoop() {
552
591
  if (!this.machineName) {
553
- const identity = await resolveMachineIdentity();
592
+ const identity = await resolveMachineIdentity(this.opts.persistedMachineName);
554
593
  this.machineName = identity.machineName;
555
594
  this.friendlyName = identity.friendlyName;
595
+ this.opts.onMachineName(identity.machineName, identity.machineNameSource);
556
596
  }
557
597
  let backoff = INITIAL_BACKOFF_MS;
558
598
  while (this.enabled && !this.disposed) {
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { t as PortalClient } from "./client-DbqRBquD.mjs";
3
+ import "./api-DG5W6iwx.mjs";
4
+
5
+ export { PortalClient };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as isRecord, t as PortalClient } from "./client-DabRpc_T.mjs";
2
+ import { n as isRecord, t as PortalClient } from "./client-DbqRBquD.mjs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { z } from "zod";
@@ -20,7 +20,7 @@ import { access, appendFile, mkdir, readFile, unlink, writeFile } from "node:fs/
20
20
  * the commit time of the built tree is one monotonic clock they share.
21
21
  */
22
22
  function portalDaemonBuild() {
23
- return "1786600530";
23
+ return "1786604432";
24
24
  }
25
25
  /**
26
26
  * Whether a client carrying `mine` should replace a running daemon carrying
@@ -365,6 +365,7 @@ var PortalDaemon = class {
365
365
  conns = /* @__PURE__ */ new Set();
366
366
  cwds = /* @__PURE__ */ new Map();
367
367
  declined = /* @__PURE__ */ new Set();
368
+ persistedMachineName = null;
368
369
  fallbackCwd;
369
370
  idleTimer = null;
370
371
  sessionToken = null;
@@ -499,12 +500,28 @@ var PortalDaemon = class {
499
500
  deviceToken: this.deviceToken
500
501
  }),
501
502
  resolveCwd: (conversationId) => this.resolveCwd(conversationId),
503
+ persistedMachineName: this.persistedMachineName,
504
+ onMachineName: (name, source) => this.onMachineName(name, source),
502
505
  onState: (state) => {
503
506
  this.lastState = state;
504
507
  this.broadcastState();
505
508
  }
506
509
  });
507
510
  }
511
+ /**
512
+ * The identity resolved on connect. Persist the name so a later scutil
513
+ * failure reuses it instead of adopting the volatile hostname, and log the
514
+ * source — a `hostname-fallback` after we'd previously registered under
515
+ * scutil is the tell that a flap just orphaned this machine's grants.
516
+ */
517
+ onMachineName(name, source) {
518
+ if (source === "hostname-fallback" && this.persistedMachineName) this.logInfo(`portal identity WARNING: fell back to hostname "${name}" but had previously registered as "${this.persistedMachineName}" — grants may be orphaned`);
519
+ else this.logInfo(`portal identity: "${name}" (source=${source})`);
520
+ if (name && name !== this.persistedMachineName) {
521
+ this.persistedMachineName = name;
522
+ this.persistState();
523
+ }
524
+ }
508
525
  /** The cwd an exec for `conversationId` runs in. */
509
526
  resolveCwd(conversationId) {
510
527
  if (conversationId) {
@@ -538,7 +555,14 @@ var PortalDaemon = class {
538
555
  /** Append a line to the daemon log file (best-effort, for post-hoc debugging). */
539
556
  logError(context, error) {
540
557
  const message = error instanceof Error ? error.message : String(error);
541
- const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${context}: ${message}\n`;
558
+ this.logLine(`${context}: ${message}`);
559
+ }
560
+ /** Append an informational line to the daemon log file (best-effort). */
561
+ logInfo(message) {
562
+ this.logLine(message);
563
+ }
564
+ logLine(message) {
565
+ const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${message}\n`;
542
566
  appendFile(this.paths.logPath, line).catch((_error) => {});
543
567
  }
544
568
  /** Close the listener without exiting the process (tests own the process). */
@@ -598,6 +622,7 @@ var PortalDaemon = class {
598
622
  if (Array.isArray(parsed.declined)) {
599
623
  for (const id of parsed.declined) if (typeof id === "string") this.declined.add(id);
600
624
  }
625
+ if (typeof parsed.machineName === "string" && parsed.machineName) this.persistedMachineName = parsed.machineName;
601
626
  }
602
627
  } catch (_error) {}
603
628
  }
@@ -605,7 +630,8 @@ var PortalDaemon = class {
605
630
  const state = {
606
631
  version: LOCAL_PROTOCOL_VERSION,
607
632
  cwds: Object.fromEntries(this.cwds),
608
- declined: [...this.declined]
633
+ declined: [...this.declined],
634
+ machineName: this.persistedMachineName ?? void 0
609
635
  };
610
636
  writeFile(this.paths.statePath, JSON.stringify(state)).catch((error) => {
611
637
  this.logError("persistState failed", error);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import "./client-DabRpc_T.mjs";
3
- import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-DLY2NmVU.mjs";
2
+ import "./client-DbqRBquD.mjs";
3
+ import { a as runPortalDaemon, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-CL25CwjW.mjs";
4
4
  import "./api-DG5W6iwx.mjs";
5
5
 
6
6
  export { runPortalDaemon };
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import "./client-DbqRBquD.mjs";
3
+ import "./daemon-CL25CwjW.mjs";
4
+ import "./api-DG5W6iwx.mjs";
5
+ import { t as PortalDaemonClient } from "./daemon-client-k8ICifN2.mjs";
6
+
7
+ export { PortalDaemonClient };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-DLY2NmVU.mjs";
2
+ import { c as LOCAL_PROTOCOL_VERSION, d as encodeLine, f as makeLineParser, n as ensureDaemonRunning, p as parseDaemonMessage, u as daemonPaths } from "./daemon-CL25CwjW.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { connect } from "node:net";
5
5
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-ClPkgR9z.mjs";
3
- import "./rest-I3imNduB.mjs";
2
+ import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-riiY5Gly.mjs";
3
+ import "./rest-CLrHVkxh.mjs";
4
4
  import "./billing-blocked-2wju4gC_.mjs";
5
5
 
6
6
  export { messageGet, readStdin, resolveAgent, runPrint };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
3
- import { a as isRecord, i as errorMessage, r as sendErrorMessage, t as createRestClient } from "./rest-I3imNduB.mjs";
3
+ import { a as isRecord, i as errorMessage, r as sendErrorMessage, t as createRestClient } from "./rest-CLrHVkxh.mjs";
4
4
  import { i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError } from "./billing-blocked-2wju4gC_.mjs";
5
5
  import path from "node:path";
6
6
  import Conf from "conf";
@@ -14,7 +14,7 @@ import { n as printError } from "./output-DYzzdXYV.mjs";
14
14
  * the reply (and to --json).
15
15
  */
16
16
  async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
17
- const { PortalClient } = await import("./client-BuU34IVE.mjs");
17
+ const { PortalClient } = await import("./client-hH2PJL8y.mjs");
18
18
  let signalConnected;
19
19
  const connected = new Promise((resolve) => {
20
20
  signalConnected = resolve;
@@ -26,6 +26,8 @@ async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
26
26
  deviceToken: null
27
27
  }),
28
28
  resolveCwd: () => process.cwd(),
29
+ persistedMachineName: null,
30
+ onMachineName: () => {},
29
31
  onState: (state) => {
30
32
  if (state.status === "connected") signalConnected();
31
33
  if (state.status === "error") console.error(`portal: connection error: ${state.error ?? "unknown"}; retrying`);
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { S as getConfigPath } from "./print-ClPkgR9z.mjs";
2
+ import { S as getConfigPath } from "./print-riiY5Gly.mjs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { err, ok } from "neverthrow";
@@ -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.5.0-beta.12";
11
+ var version$1 = "0.5.0-beta.14";
12
12
 
13
13
  //#endregion
14
14
  //#region src/auth/organization.ts
@@ -329,6 +329,9 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
329
329
  setConversationArchived: async ({ conversationId, archived }) => {
330
330
  await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/archive`, { archived }, z.object({ archived: z.boolean() }));
331
331
  },
332
+ markConversationRead: async ({ conversationId }) => {
333
+ await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/read`, {}, z.object({ read: z.boolean() }));
334
+ },
332
335
  renameConversation: async ({ conversationId, title }) => {
333
336
  await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}`, { title }, z.object({ conversation: z.object({ id: z.string() }) }), "PATCH");
334
337
  },
@@ -469,6 +472,7 @@ const conversationSummarySchema = z.object({
469
472
  channel: z.string().nullable(),
470
473
  channelLabel: z.string().nullable(),
471
474
  viewerArchivedAt: z.string().nullable().optional(),
475
+ unread: z.boolean().optional(),
472
476
  agent: conversationAgentSchema,
473
477
  agents: z.array(conversationAgentSchema).optional()
474
478
  });
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as HttpError } from "./http-error-DzyrsLAZ.mjs";
3
- import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-I3imNduB.mjs";
3
+ import { n as errorDetail, r as sendErrorMessage, t as createRestClient } from "./rest-CLrHVkxh.mjs";
4
4
  import "./billing-blocked-2wju4gC_.mjs";
5
5
 
6
6
  export { createRestClient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.5.0-beta.12",
3
+ "version": "0.5.0-beta.14",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as PortalClient } from "./client-DabRpc_T.mjs";
3
- import "./api-DG5W6iwx.mjs";
4
-
5
- export { PortalClient };
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-DabRpc_T.mjs";
3
- import "./daemon-DLY2NmVU.mjs";
4
- import "./api-DG5W6iwx.mjs";
5
- import { t as PortalDaemonClient } from "./daemon-client-CG6KWbdI.mjs";
6
-
7
- export { PortalDaemonClient };