@standardagents/code 0.9.3 → 0.9.5

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import os9, { homedir } from 'os';
2
+ import os10, { homedir } from 'os';
3
3
  import path3 from 'path';
4
4
  import readline2 from 'readline/promises';
5
5
  import { stdout, stdin } from 'process';
@@ -1112,7 +1112,7 @@ function detailSuffix(tool, result) {
1112
1112
  const lines = result.split("\n").length;
1113
1113
  return ` (${lines} line${lines === 1 ? "" : "s"})`;
1114
1114
  }
1115
- var LOG_DIR = path3.join(os9.homedir(), ".standardagents", "process-logs");
1115
+ var LOG_DIR = path3.join(os10.homedir(), ".standardagents", "process-logs");
1116
1116
  var KEY2 = "bg_processes";
1117
1117
  function isAlive(pid) {
1118
1118
  try {
@@ -1189,7 +1189,7 @@ var ProcessRegistry = class {
1189
1189
  }
1190
1190
  };
1191
1191
  function configFile() {
1192
- return process.env.STANDARDAGENTS_MCP_CONFIG || path3.join(os9.homedir(), ".standardagents", "mcp.json");
1192
+ return process.env.STANDARDAGENTS_MCP_CONFIG || path3.join(os10.homedir(), ".standardagents", "mcp.json");
1193
1193
  }
1194
1194
  function loadMcpConfig() {
1195
1195
  try {
@@ -1491,7 +1491,7 @@ var HostTools = class {
1491
1491
  }
1492
1492
  }
1493
1493
  const hash = crypto.createHash("sha256").update(JSON.stringify(files)).digest("hex").slice(0, 12);
1494
- const skillDir = path3.join(os9.tmpdir(), "standardcode-skills", `${skill}-${hash}`);
1494
+ const skillDir = path3.join(os10.tmpdir(), "standardcode-skills", `${skill}-${hash}`);
1495
1495
  for (const f of files) {
1496
1496
  const dest = path3.resolve(skillDir, f.path);
1497
1497
  if (path3.relative(skillDir, dest).startsWith("..")) {
@@ -2352,6 +2352,23 @@ var ExecutionSession = class {
2352
2352
  }
2353
2353
  };
2354
2354
 
2355
+ // src/theme.ts
2356
+ function detectLight() {
2357
+ const forced = (process.env.STANDARD_CODE_THEME || "").toLowerCase();
2358
+ if (forced === "light") return true;
2359
+ if (forced === "dark") return false;
2360
+ const fgbg = process.env.COLORFGBG;
2361
+ if (fgbg) {
2362
+ const parts = fgbg.split(";");
2363
+ const bg = Number(parts[parts.length - 1]);
2364
+ if (!Number.isNaN(bg)) return bg === 7 || bg === 15 || bg >= 230;
2365
+ }
2366
+ return false;
2367
+ }
2368
+ var isLightTheme = detectLight();
2369
+ var themeWhite = isLightTheme ? "\x1B[30m" : "\x1B[97m";
2370
+ var themeGray = isLightTheme ? "\x1B[38;5;244m" : "\x1B[90m";
2371
+
2355
2372
  // src/stream.ts
2356
2373
  var MessageStream = class {
2357
2374
  constructor(api, threadId, hooks) {
@@ -2816,7 +2833,7 @@ function fromFile(filePath) {
2816
2833
  }
2817
2834
  }
2818
2835
  async function readDarwin() {
2819
- const tmp = path3.join(os9.tmpdir(), `sc-clip-${process.pid}-${Date.now()}.png`);
2836
+ const tmp = path3.join(os10.tmpdir(), `sc-clip-${process.pid}-${Date.now()}.png`);
2820
2837
  const script = [
2821
2838
  `set d to the clipboard as \xABclass PNGf\xBB`,
2822
2839
  `set f to open for access POSIX file "${tmp}" with write permission`,
@@ -2853,7 +2870,7 @@ async function readLinux() {
2853
2870
  return null;
2854
2871
  }
2855
2872
  async function readWindows() {
2856
- const tmp = path3.join(os9.tmpdir(), `sc-clip-${process.pid}-${Date.now()}.png`);
2873
+ const tmp = path3.join(os10.tmpdir(), `sc-clip-${process.pid}-${Date.now()}.png`);
2857
2874
  const ps = [
2858
2875
  "Add-Type -AssemblyName System.Windows.Forms;",
2859
2876
  "$img = [System.Windows.Forms.Clipboard]::GetImage();",
@@ -3071,7 +3088,7 @@ var C = {
3071
3088
  red: "\x1B[31m",
3072
3089
  blue: "\x1B[34m",
3073
3090
  magenta: "\x1B[35m",
3074
- gray: "\x1B[90m",
3091
+ gray: themeGray,
3075
3092
  teal: "\x1B[38;5;37m"
3076
3093
  };
3077
3094
  var FRAMES = ["\u28F7", "\u28EF", "\u28DF", "\u287F", "\u28BF", "\u28FB", "\u28FD", "\u28FE"];
@@ -3225,6 +3242,11 @@ var Tui = class _Tui {
3225
3242
  /** Enter while the `[⚙ n bg]` badge is selected — opens the bg process panel. */
3226
3243
  onBgBadge = () => {
3227
3244
  };
3245
+ /** Fired (deduped) when the input draft text changes — index.ts debounce-writes
3246
+ * it into the shared cross-surface presence buffer. */
3247
+ onDraftChange = () => {
3248
+ };
3249
+ lastDraftSeen = "";
3228
3250
  onQuit = () => process.exit(0);
3229
3251
  levelListeners = [];
3230
3252
  /**
@@ -3935,6 +3957,13 @@ var Tui = class _Tui {
3935
3957
  */
3936
3958
  renderBottom() {
3937
3959
  if (!this.started || this.takeoverHandler) return;
3960
+ if (this.inputBuffer !== this.lastDraftSeen) {
3961
+ this.lastDraftSeen = this.inputBuffer;
3962
+ try {
3963
+ this.onDraftChange(this.inputBuffer);
3964
+ } catch {
3965
+ }
3966
+ }
3938
3967
  if (this.resizePending) return;
3939
3968
  const cols2 = process.stdout.columns || 80;
3940
3969
  this.moveToRegionTop();
@@ -4920,7 +4949,7 @@ function renderMarkdown(src, cols2 = 80) {
4920
4949
  }
4921
4950
  return out;
4922
4951
  }
4923
- var DIR = path3.join(os9.homedir(), ".standardagents");
4952
+ var DIR = path3.join(os10.homedir(), ".standardagents");
4924
4953
  var FILE = path3.join(DIR, "credentials");
4925
4954
  function normalizeEndpoint(endpoint) {
4926
4955
  let e = endpoint.trim();
@@ -5054,7 +5083,7 @@ function relaxTlsForLocalEndpoint(endpoint) {
5054
5083
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
5055
5084
  return true;
5056
5085
  }
5057
- var dir = () => path3.join(os9.homedir(), ".standardagents");
5086
+ var dir = () => path3.join(os10.homedir(), ".standardagents");
5058
5087
  var file = () => path3.join(dir(), "machine.json");
5059
5088
  function loadMachineIdentity() {
5060
5089
  try {
@@ -5088,6 +5117,9 @@ function machineIdFromDaemonClientId(clientId) {
5088
5117
  var KEY_PREFIX = "standardcode.machine.";
5089
5118
  var CMD_SUFFIX = ".cmd";
5090
5119
  var NAME_SUFFIX = ".name";
5120
+ var ICON_SUFFIX = ".icon";
5121
+ var FSREQ_SUFFIX = ".fsreq";
5122
+ var FSRES_SUFFIX = ".fsres";
5091
5123
  var DAEMON_ONLINE_WINDOW_MS = 90 * 1e3;
5092
5124
  function machineKey(machineId) {
5093
5125
  return `${KEY_PREFIX}${machineId}`;
@@ -5098,6 +5130,41 @@ function commandKey(machineId) {
5098
5130
  function nameKey(machineId) {
5099
5131
  return `${KEY_PREFIX}${machineId}${NAME_SUFFIX}`;
5100
5132
  }
5133
+ function iconKey(machineId) {
5134
+ return `${KEY_PREFIX}${machineId}${ICON_SUFFIX}`;
5135
+ }
5136
+ function fsRequestKey(machineId) {
5137
+ return `${KEY_PREFIX}${machineId}${FSREQ_SUFFIX}`;
5138
+ }
5139
+ function fsResponseKey(machineId) {
5140
+ return `${KEY_PREFIX}${machineId}${FSRES_SUFFIX}`;
5141
+ }
5142
+ function parseFsRequest(value) {
5143
+ if (!value || typeof value !== "object") return null;
5144
+ const r = value;
5145
+ const op = r.op === "mkdir" ? "mkdir" : r.op === "list" ? "list" : null;
5146
+ if (typeof r.nonce !== "string" || !op || typeof r.path !== "string") return null;
5147
+ return {
5148
+ nonce: r.nonce,
5149
+ op,
5150
+ path: r.path,
5151
+ name: typeof r.name === "string" ? r.name : void 0,
5152
+ show_hidden: r.show_hidden === true,
5153
+ requested_at: typeof r.requested_at === "number" ? r.requested_at : Date.now()
5154
+ };
5155
+ }
5156
+ async function readFsRequest(api, machineId) {
5157
+ return parseFsRequest(await api.userKvGet(fsRequestKey(machineId)));
5158
+ }
5159
+ async function writeFsResponse(api, machineId, res) {
5160
+ await api.userKvSet(fsResponseKey(machineId), { ...res, responded_at: Date.now() });
5161
+ }
5162
+ function machineIcon(record) {
5163
+ if (record.icon && record.icon.trim()) return record.icon.trim();
5164
+ if (record.platform === "darwin") return "\u{1F4BB}";
5165
+ if (record.platform === "win32") return "\u{1F5A5}\uFE0F";
5166
+ return "\u{1F5B3}";
5167
+ }
5101
5168
  function machineDisplayName(record) {
5102
5169
  return record.name?.trim() || "" || (record.hostname || "") || (record.id || "") || "machine";
5103
5170
  }
@@ -5133,28 +5200,45 @@ async function loadMachines(api) {
5133
5200
  const entries = await api.userKvList(KEY_PREFIX);
5134
5201
  const records = [];
5135
5202
  const names = /* @__PURE__ */ new Map();
5203
+ const icons = /* @__PURE__ */ new Map();
5136
5204
  for (const e of entries) {
5137
5205
  const rest = e.key.slice(KEY_PREFIX.length);
5138
- if (rest.endsWith(CMD_SUFFIX)) continue;
5206
+ if (rest.endsWith(CMD_SUFFIX) || rest.endsWith(FSREQ_SUFFIX) || rest.endsWith(FSRES_SUFFIX)) continue;
5139
5207
  if (rest.endsWith(NAME_SUFFIX)) {
5140
5208
  const id = rest.slice(0, -NAME_SUFFIX.length);
5141
5209
  if (typeof e.value === "string" && e.value.trim()) names.set(id, e.value.trim());
5142
5210
  continue;
5143
5211
  }
5212
+ if (rest.endsWith(ICON_SUFFIX)) {
5213
+ const id = rest.slice(0, -ICON_SUFFIX.length);
5214
+ if (typeof e.value === "string" && e.value.trim()) icons.set(id, e.value.trim());
5215
+ continue;
5216
+ }
5144
5217
  const rec = parseMachineRecord(e.value);
5145
5218
  if (rec) records.push(rec);
5146
5219
  }
5147
5220
  for (const rec of records) {
5148
5221
  const override = names.get(rec.id);
5149
5222
  if (override) rec.name = override;
5223
+ const icon = icons.get(rec.id);
5224
+ if (icon) rec.icon = icon;
5150
5225
  }
5151
5226
  return records;
5152
5227
  }
5228
+ async function getMachineIcon(api, machineId) {
5229
+ const v = await api.userKvGet(iconKey(machineId));
5230
+ return typeof v === "string" && v.trim() ? v.trim() : null;
5231
+ }
5232
+ async function setMachineIcon(api, machineId, icon) {
5233
+ const trimmed = icon.trim();
5234
+ await api.userKvSet(iconKey(machineId), trimmed || null);
5235
+ }
5153
5236
  async function loadMachine(api, machineId) {
5154
5237
  const rec = await loadRawMachine(api, machineId);
5155
5238
  if (!rec) return null;
5156
- const override = await getMachineName(api, machineId);
5239
+ const [override, icon] = await Promise.all([getMachineName(api, machineId), getMachineIcon(api, machineId)]);
5157
5240
  if (override) rec.name = override;
5241
+ if (icon) rec.icon = icon;
5158
5242
  return rec;
5159
5243
  }
5160
5244
  function daemonOnline(record, now = Date.now()) {
@@ -5164,8 +5248,8 @@ function newRecord(identity) {
5164
5248
  const now = Date.now();
5165
5249
  return {
5166
5250
  id: identity.machine_id,
5167
- name: os9.hostname(),
5168
- hostname: os9.hostname(),
5251
+ name: os10.hostname(),
5252
+ hostname: os10.hostname(),
5169
5253
  platform: process.platform,
5170
5254
  arch: process.arch,
5171
5255
  version: readVersion() || void 0,
@@ -5178,7 +5262,7 @@ function newRecord(identity) {
5178
5262
  async function updateOwnMachineRecord(api, identity, mutate) {
5179
5263
  const existing = await loadRawMachine(api, identity.machine_id);
5180
5264
  const record = existing ?? newRecord(identity);
5181
- record.hostname = os9.hostname();
5265
+ record.hostname = os10.hostname();
5182
5266
  record.platform = process.platform;
5183
5267
  record.arch = process.arch;
5184
5268
  record.version = readVersion() || record.version;
@@ -5263,16 +5347,16 @@ async function clearMachineCommands(api, machineId, appliedIds) {
5263
5347
  async function applyMachineCommand(api, identity, cmd) {
5264
5348
  switch (cmd.kind) {
5265
5349
  case "add_project": {
5266
- const path12 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
5267
- if (!path12) return "add_project: ignored (no path)";
5268
- await registerProject(api, identity, path12);
5269
- return `added project ${path12}`;
5350
+ const path13 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
5351
+ if (!path13) return "add_project: ignored (no path)";
5352
+ await registerProject(api, identity, path13);
5353
+ return `added project ${path13}`;
5270
5354
  }
5271
5355
  case "remove_project": {
5272
- const path12 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
5273
- if (!path12) return "remove_project: ignored (no path)";
5274
- await unregisterProject(api, identity, path12);
5275
- return `removed project ${path12}`;
5356
+ const path13 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
5357
+ if (!path13) return "remove_project: ignored (no path)";
5358
+ await unregisterProject(api, identity, path13);
5359
+ return `removed project ${path13}`;
5276
5360
  }
5277
5361
  case "update":
5278
5362
  return "update requested";
@@ -5334,6 +5418,114 @@ async function awaitApprovalViaRelay(api, threadId, request, options = {}) {
5334
5418
  });
5335
5419
  }
5336
5420
  }
5421
+ var PROJECT_MARKERS = [
5422
+ ".git",
5423
+ "package.json",
5424
+ "pyproject.toml",
5425
+ "Cargo.toml",
5426
+ "go.mod",
5427
+ "pom.xml",
5428
+ "build.gradle",
5429
+ "Gemfile",
5430
+ "composer.json",
5431
+ "requirements.txt",
5432
+ ".standardagents"
5433
+ ];
5434
+ var MAX_ENTRIES2 = 500;
5435
+ function resolveBrowsePath(input3) {
5436
+ const home = os10.homedir();
5437
+ let p = (input3 ?? "").trim();
5438
+ if (!p) return home;
5439
+ if (p === "~") return home;
5440
+ if (p.startsWith("~/")) p = path3.join(home, p.slice(2));
5441
+ return path3.resolve(p);
5442
+ }
5443
+ function markers(dirPath) {
5444
+ let repo = false;
5445
+ let project = false;
5446
+ for (const marker of PROJECT_MARKERS) {
5447
+ let hit = false;
5448
+ try {
5449
+ hit = fs4.existsSync(path3.join(dirPath, marker));
5450
+ } catch {
5451
+ hit = false;
5452
+ }
5453
+ if (!hit) continue;
5454
+ project = true;
5455
+ if (marker === ".git") repo = true;
5456
+ if (repo) break;
5457
+ }
5458
+ return { project, repo };
5459
+ }
5460
+ function browseDirectory(input3, opts = {}) {
5461
+ const home = os10.homedir();
5462
+ const abs = resolveBrowsePath(input3);
5463
+ const parent = path3.dirname(abs);
5464
+ const base = {
5465
+ path: abs,
5466
+ parent: parent === abs ? null : parent,
5467
+ home
5468
+ };
5469
+ let dirents;
5470
+ try {
5471
+ const stat = fs4.statSync(abs);
5472
+ if (!stat.isDirectory()) {
5473
+ return { ...base, entries: [], truncated: false, error: "Not a directory" };
5474
+ }
5475
+ dirents = fs4.readdirSync(abs, { withFileTypes: true });
5476
+ } catch (err) {
5477
+ const code = err?.code;
5478
+ const message = code === "EACCES" || code === "EPERM" ? "Permission denied" : code === "ENOENT" ? "Folder not found" : "Could not read this folder";
5479
+ return { ...base, entries: [], truncated: false, error: message };
5480
+ }
5481
+ const showHidden = !!opts.showHidden;
5482
+ const rawDirs = [];
5483
+ const rawFiles = [];
5484
+ for (const d of dirents) {
5485
+ const name = d.name;
5486
+ if (!showHidden && name.startsWith(".")) continue;
5487
+ let isDir = d.isDirectory();
5488
+ if (d.isSymbolicLink()) {
5489
+ try {
5490
+ isDir = fs4.statSync(path3.join(abs, name)).isDirectory();
5491
+ } catch {
5492
+ isDir = false;
5493
+ }
5494
+ }
5495
+ if (isDir) {
5496
+ const { project, repo } = markers(path3.join(abs, name));
5497
+ rawDirs.push({ name, dir: true, project: project || void 0, repo: repo || void 0 });
5498
+ } else {
5499
+ rawFiles.push({ name, dir: false });
5500
+ }
5501
+ }
5502
+ const cmp = (a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase());
5503
+ rawDirs.sort(cmp);
5504
+ rawFiles.sort(cmp);
5505
+ const all = [...rawDirs, ...rawFiles];
5506
+ const truncated = all.length > MAX_ENTRIES2;
5507
+ return { ...base, entries: truncated ? all.slice(0, MAX_ENTRIES2) : all, truncated };
5508
+ }
5509
+ function mkdirDirectory(parentInput, name, opts = {}) {
5510
+ const parent = resolveBrowsePath(parentInput);
5511
+ const clean2 = (name ?? "").trim();
5512
+ const listParent = () => browseDirectory(parent, opts);
5513
+ if (!clean2 || clean2 === "." || clean2 === ".." || clean2.includes("/") || clean2.includes("\\") || clean2.includes("\0")) {
5514
+ return { ...listParent(), error: "Invalid folder name." };
5515
+ }
5516
+ const target = path3.join(parent, clean2);
5517
+ if (path3.dirname(target) !== parent) {
5518
+ return { ...listParent(), error: "Invalid folder name." };
5519
+ }
5520
+ try {
5521
+ fs4.mkdirSync(target, { recursive: false });
5522
+ } catch (err) {
5523
+ const code = err?.code;
5524
+ const message = code === "EEXIST" ? "A folder with that name already exists." : code === "EACCES" || code === "EPERM" ? "Permission denied." : code === "ENOENT" ? "The parent folder no longer exists." : "Could not create the folder.";
5525
+ return { ...listParent(), error: message };
5526
+ }
5527
+ return listParent();
5528
+ }
5337
5529
  var PKG_NAME = "@standardagents/code";
5338
5530
  var REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PKG_NAME)}`;
5339
5531
  var CACHE_REL_DIR = ".config/standardagents-cli";
@@ -5514,12 +5706,13 @@ function runUpdate(pm) {
5514
5706
  // src/daemon.ts
5515
5707
  var HEARTBEAT_MS = 3e4;
5516
5708
  var COMMAND_POLL_MS = 8e3;
5709
+ var FS_POLL_MS = 1e3;
5517
5710
  var RECLAIM_PROBE_MS = 2 * 6e4;
5518
5711
  var UPDATE_CHECK_MS = 6 * 60 * 6e4;
5519
5712
  var SWEEP_MS = 10 * 6e4;
5520
5713
  var MAX_WORKERS = 30;
5521
5714
  var LOG_MAX_BYTES = 1e6;
5522
- var LOG_FILE = path3.join(os9.homedir(), ".standardagents", "daemon.log");
5715
+ var LOG_FILE = path3.join(os10.homedir(), ".standardagents", "daemon.log");
5523
5716
  function daemonLog(line) {
5524
5717
  try {
5525
5718
  fs4.mkdirSync(path3.dirname(LOG_FILE), { recursive: true });
@@ -5538,7 +5731,7 @@ function pathFromTags(tags) {
5538
5731
  const tag = tags.find((t) => t.startsWith("path:"));
5539
5732
  if (!tag) return null;
5540
5733
  const raw = tag.slice("path:".length);
5541
- return raw.replace(/^~(?=\/|$)/, os9.homedir());
5734
+ return raw.replace(/^~(?=\/|$)/, os10.homedir());
5542
5735
  }
5543
5736
  var ThreadWorker = class {
5544
5737
  constructor(api, identity, machineName, threadId, projectDir, createdAt) {
@@ -5670,7 +5863,7 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
5670
5863
  process.exit(1);
5671
5864
  }
5672
5865
  let displayName = machineDisplayName(await loadMachine(api, identity.machine_id).catch(() => null) ?? {
5673
- hostname: os9.hostname(),
5866
+ hostname: os10.hostname(),
5674
5867
  id: identity.machine_id
5675
5868
  });
5676
5869
  const applied = consumeAppliedUpdate(version);
@@ -5832,6 +6025,31 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
5832
6025
  };
5833
6026
  void drainCommands();
5834
6027
  const commandTimer = setInterval(() => void drainCommands(), COMMAND_POLL_MS);
6028
+ let lastFsNonce = null;
6029
+ let fsBusy = false;
6030
+ const drainFsRequests = async () => {
6031
+ if (fsBusy) return;
6032
+ fsBusy = true;
6033
+ try {
6034
+ const req = await readFsRequest(api, identity.machine_id).catch(() => null);
6035
+ if (!req || req.nonce === lastFsNonce) return;
6036
+ lastFsNonce = req.nonce;
6037
+ let ok = true;
6038
+ let result;
6039
+ let error;
6040
+ try {
6041
+ result = req.op === "mkdir" ? mkdirDirectory(req.path, req.name ?? "", { showHidden: req.show_hidden }) : browseDirectory(req.path, { showHidden: req.show_hidden });
6042
+ } catch (err) {
6043
+ ok = false;
6044
+ error = err instanceof Error ? err.message : "browse failed";
6045
+ }
6046
+ await writeFsResponse(api, identity.machine_id, { nonce: req.nonce, ok, result, error }).catch(() => {
6047
+ });
6048
+ } finally {
6049
+ fsBusy = false;
6050
+ }
6051
+ };
6052
+ const fsTimer = setInterval(() => void drainFsRequests(), FS_POLL_MS);
5835
6053
  const sweeper = setInterval(() => {
5836
6054
  if (updateReady && ![...workers.values()].some((w) => w.busy)) {
5837
6055
  daemonLog("restarting to apply the installed update");
@@ -5845,6 +6063,7 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
5845
6063
  clearInterval(reclaim);
5846
6064
  clearInterval(updateTimer);
5847
6065
  clearInterval(commandTimer);
6066
+ clearInterval(fsTimer);
5848
6067
  clearInterval(sweeper);
5849
6068
  for (const worker of workers.values()) worker.stop();
5850
6069
  events.close();
@@ -5902,8 +6121,8 @@ function run2(cmd, args) {
5902
6121
  const output4 = `${res.stdout ?? ""}${res.stderr ?? ""}`.trim();
5903
6122
  return { ok: res.status === 0, output: output4 };
5904
6123
  }
5905
- var plistPath = () => path3.join(os9.homedir(), "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
5906
- var unitPath = () => path3.join(os9.homedir(), ".config", "systemd", "user", SYSTEMD_UNIT);
6124
+ var plistPath = () => path3.join(os10.homedir(), "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
6125
+ var unitPath = () => path3.join(os10.homedir(), ".config", "systemd", "user", SYSTEMD_UNIT);
5907
6126
  var xmlEscape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5908
6127
  function installService(command, endpoint) {
5909
6128
  if (process.platform === "darwin") return installLaunchd(command, endpoint);
@@ -5915,7 +6134,7 @@ function installService(command, endpoint) {
5915
6134
  };
5916
6135
  }
5917
6136
  function installLaunchd(command, endpoint) {
5918
- const logDir = path3.join(os9.homedir(), ".standardagents");
6137
+ const logDir = path3.join(os10.homedir(), ".standardagents");
5919
6138
  fs4.mkdirSync(logDir, { recursive: true });
5920
6139
  fs4.mkdirSync(path3.dirname(plistPath()), { recursive: true });
5921
6140
  const envEntries = [
@@ -5988,10 +6207,10 @@ WantedBy=default.target
5988
6207
  if (!enable.ok) {
5989
6208
  return { ok: false, detail: `systemctl enable failed: ${enable.output}` };
5990
6209
  }
5991
- const linger = run2("loginctl", ["enable-linger", os9.userInfo().username]);
6210
+ const linger = run2("loginctl", ["enable-linger", os10.userInfo().username]);
5992
6211
  return {
5993
6212
  ok: true,
5994
- detail: `systemd user unit installed (${unitPath()})` + (linger.ok ? ", lingering enabled" : ` \u2014 enable lingering manually: sudo loginctl enable-linger ${os9.userInfo().username}`)
6213
+ detail: `systemd user unit installed (${unitPath()})` + (linger.ok ? ", lingering enabled" : ` \u2014 enable lingering manually: sudo loginctl enable-linger ${os10.userInfo().username}`)
5995
6214
  };
5996
6215
  }
5997
6216
  function uninstallService() {
@@ -6119,7 +6338,7 @@ async function installCommand(endpointFlag) {
6119
6338
  const api = await ensureSignedIn(endpoint);
6120
6339
  const identity = loadMachineIdentity();
6121
6340
  const existing = await loadMachine(api, identity.machine_id).catch(() => null);
6122
- const suggested = machineDisplayName(existing ?? { hostname: os9.hostname(), id: identity.machine_id });
6341
+ const suggested = machineDisplayName(existing ?? { hostname: os10.hostname(), id: identity.machine_id });
6123
6342
  const rl = readline2.createInterface({ input: stdin, output: stdout });
6124
6343
  const answer = (await rl.question(
6125
6344
  `${c2.bold}Machine name${c2.reset} ${c2.dim}(shown in the session picker)${c2.reset} [${suggested}]: `
@@ -6179,7 +6398,7 @@ async function statusCommand() {
6179
6398
  const cred = getCredential(endpoint);
6180
6399
  if (!cred) {
6181
6400
  stdout.write(
6182
- `${c2.bold}Machine:${c2.reset} ${os9.hostname()} ${c2.dim}(${identity.machine_id})${c2.reset}
6401
+ `${c2.bold}Machine:${c2.reset} ${os10.hostname()} ${c2.dim}(${identity.machine_id})${c2.reset}
6183
6402
  `
6184
6403
  );
6185
6404
  stdout.write(`${c2.bold}Account:${c2.reset} ${c2.yellow}not signed in to ${endpoint}${c2.reset}
@@ -6190,7 +6409,7 @@ async function statusCommand() {
6190
6409
  const api = new ApiClient(endpoint, cred.access_token);
6191
6410
  const record = await loadMachine(api, identity.machine_id).catch(() => null);
6192
6411
  stdout.write(
6193
- `${c2.bold}Machine:${c2.reset} ${machineDisplayName(record ?? { hostname: os9.hostname(), id: identity.machine_id })} ${c2.dim}(${identity.machine_id})${c2.reset}
6412
+ `${c2.bold}Machine:${c2.reset} ${machineDisplayName(record ?? { hostname: os10.hostname(), id: identity.machine_id })} ${c2.dim}(${identity.machine_id})${c2.reset}
6194
6413
  `
6195
6414
  );
6196
6415
  if (!record) {
@@ -6224,7 +6443,7 @@ async function projectCommand(action, target) {
6224
6443
  const api = await ensureSignedIn(endpoint);
6225
6444
  const identity = loadMachineIdentity();
6226
6445
  const record = await loadMachine(api, identity.machine_id).catch(() => null);
6227
- const displayName = machineDisplayName(record ?? { hostname: os9.hostname(), id: identity.machine_id });
6446
+ const displayName = machineDisplayName(record ?? { hostname: os10.hostname(), id: identity.machine_id });
6228
6447
  if (action === "add") {
6229
6448
  await registerProject(api, identity, dir2);
6230
6449
  stdout.write(`${c2.green}\u2713${c2.reset} Registered ${dir2} for remote sessions on ${displayName}.
@@ -6284,10 +6503,10 @@ var c3 = {
6284
6503
  reset: "\x1B[0m",
6285
6504
  dim: "\x1B[2m",
6286
6505
  bold: "\x1B[1m",
6287
- white: "\x1B[97m",
6506
+ white: themeWhite,
6288
6507
  cyan: "\x1B[36m",
6289
6508
  green: "\x1B[32m",
6290
- gray: "\x1B[90m",
6509
+ gray: themeGray,
6291
6510
  magenta: "\x1B[35m",
6292
6511
  yellow: "\x1B[33m",
6293
6512
  red: "\x1B[31m",
@@ -6424,7 +6643,7 @@ ${c3.teal}\u25C7${c3.reset} ${c3.dim}Standard Code \u2014 see you soon.${c3.rese
6424
6643
  `);
6425
6644
  }
6426
6645
  function printWelcome(endpoint, projectDir) {
6427
- const home = os9.homedir();
6646
+ const home = os10.homedir();
6428
6647
  const dir2 = projectDir.startsWith(home) ? "~" + projectDir.slice(home.length) : projectDir;
6429
6648
  const host = endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
6430
6649
  const version = readVersion();
@@ -6492,7 +6711,7 @@ async function main() {
6492
6711
  const endpointOverride = cliArgs.promptEndpoint || typeof endpointArg === "string" && endpointArg.trim() !== "";
6493
6712
  const dirArg = cliArgs.dir;
6494
6713
  const projectDir = path3.resolve(dirArg || process.cwd());
6495
- const machine = os9.hostname();
6714
+ const machine = os10.hostname();
6496
6715
  const reader = { rl: null };
6497
6716
  let handoffClosing = false;
6498
6717
  let preflightArmed = false;
@@ -6677,7 +6896,7 @@ ${c3.dim}Press Control-C again to exit${c3.reset}
6677
6896
  void registerProject(api, identity, projectDir).catch(() => {
6678
6897
  });
6679
6898
  const tui = new Tui(1);
6680
- const home = os9.homedir();
6899
+ const home = os10.homedir();
6681
6900
  const tildeDir = projectDir.startsWith(home) ? "~" + projectDir.slice(home.length) : projectDir;
6682
6901
  const shortDir = tildeDir.length > 38 ? "\u2026" + tildeDir.slice(-37) : tildeDir;
6683
6902
  const session = { mode: "local", identity };
@@ -6691,9 +6910,9 @@ ${c3.dim}Press Control-C again to exit${c3.reset}
6691
6910
  const where = await tui.select(
6692
6911
  `${c3.bold}${gradientText("Where should this session run?")}${c3.reset} ${c3.dim}\u2191\u2193 \xB7 enter \xB7 esc${c3.reset}`,
6693
6912
  [
6694
- { label: `This machine \u2014 ${shortDir}`, hint: "tools run locally", value: null },
6913
+ { label: `\u{1F5A5}\uFE0F This machine \u2014 ${shortDir}`, hint: "tools run locally", value: null },
6695
6914
  ...remoteTargets.map((m) => ({
6696
- label: m.name,
6915
+ label: `${machineIcon(m)} ${m.name}`,
6697
6916
  hint: `${m.hostname} \xB7 daemon online${m.daemon ? ` \xB7 v${m.daemon.version}` : ""}`,
6698
6917
  value: m
6699
6918
  }))
@@ -7429,7 +7648,24 @@ ${c3.gray}Close another session (its slot frees within ~90s), then resend your m
7429
7648
  ]);
7430
7649
  const history = await loadHistory(api, threadId, historySeedThreadId);
7431
7650
  tui.setHistory(history);
7651
+ const PRESENCE_KEY = "presence.latest_draft";
7652
+ const presenceSurfaceId = `cli:${Math.random().toString(36).slice(2, 10)}`;
7653
+ let presenceTimer;
7654
+ const clearPresence = () => {
7655
+ if (presenceTimer) clearTimeout(presenceTimer);
7656
+ void api.kvSet(threadId, PRESENCE_KEY, { text: "", images: [], surface: "cli", surface_id: presenceSurfaceId, updated_at: Date.now() }).catch(() => {
7657
+ });
7658
+ };
7659
+ tui.onDraftChange = (textVal) => {
7660
+ if (presenceTimer) clearTimeout(presenceTimer);
7661
+ presenceTimer = setTimeout(() => {
7662
+ const t = textVal.trim();
7663
+ void api.kvSet(threadId, PRESENCE_KEY, { text: t ? textVal : "", images: [], surface: "cli", surface_id: presenceSurfaceId, updated_at: Date.now() }).catch(() => {
7664
+ });
7665
+ }, 400);
7666
+ };
7432
7667
  tui.onSubmit = (text, images) => {
7668
+ clearPresence();
7433
7669
  const trimmed = text.trimStart();
7434
7670
  if (trimmed.startsWith("!") && !trimmed.startsWith("!!")) {
7435
7671
  const command = trimmed.slice(1).trim();
@@ -7757,7 +7993,7 @@ async function runMachinesMenu(tui, api, self) {
7757
7993
  const daemonBit = m.daemon ? online ? "daemon online" : "daemon offline" : "no daemon";
7758
7994
  const nproj = Object.keys(m.projects).length;
7759
7995
  return {
7760
- label: `${m.name}${isSelf ? " (this machine)" : ""}`,
7996
+ label: `${machineIcon(m)} ${m.name}${isSelf ? " (this machine)" : ""}`,
7761
7997
  hint: `${m.hostname} \xB7 ${m.platform}/${m.arch} \xB7 v${m.version ?? "?"} \xB7 ${daemonBit} \xB7 ${nproj} project${nproj === 1 ? "" : "s"}`,
7762
7998
  value: m.id
7763
7999
  };
@@ -7772,7 +8008,8 @@ async function manageMachine(tui, api, self, machine) {
7772
8008
  const online = daemonOnline(machine);
7773
8009
  const canRunCommands = isSelf || !!machine.daemon;
7774
8010
  const options = [
7775
- { label: "Rename", value: "rename" }
8011
+ { label: "Rename", value: "rename" },
8012
+ { label: "Set icon", hint: machine.icon || "default", value: "icon" }
7776
8013
  ];
7777
8014
  if (canRunCommands) {
7778
8015
  options.push(
@@ -7790,8 +8027,22 @@ async function manageMachine(tui, api, self, machine) {
7790
8027
  `${c3.dim}${machine.name}'s daemon is offline \u2014 queued changes apply when it next comes online.${c3.reset}`
7791
8028
  );
7792
8029
  }
7793
- const action = await tui.select(`${c3.bold}${machine.name}${c3.reset}`, options);
8030
+ const action = await tui.select(`${c3.bold}${machineIcon(machine)} ${machine.name}${c3.reset}`, options);
7794
8031
  if (!action || action === "back") return;
8032
+ if (action === "icon") {
8033
+ const current = machine.icon ?? "";
8034
+ const emoji = await tui.prompt(
8035
+ `${c3.bold}Icon for ${machine.name}${c3.reset} ${c3.dim}(paste an emoji, blank to reset)${c3.reset}`,
8036
+ current
8037
+ );
8038
+ if (emoji !== null) {
8039
+ const trimmed = emoji.trim();
8040
+ await setMachineIcon(api, machine.id, trimmed);
8041
+ machine.icon = trimmed || void 0;
8042
+ tui.print(`${c3.green}\u2713${c3.reset} icon ${trimmed ? `set to ${trimmed}` : "reset"} for ${machine.name}`);
8043
+ }
8044
+ return manageMachine(tui, api, self, machine);
8045
+ }
7795
8046
  const dispatch = async (kind, args) => {
7796
8047
  if (isSelf) {
7797
8048
  await applyMachineCommand(api, self, { kind, args});
@@ -7833,12 +8084,12 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
7833
8084
  );
7834
8085
  if (!picked) return;
7835
8086
  if (picked === ADD) {
7836
- const path12 = await tui.prompt(
8087
+ const path13 = await tui.prompt(
7837
8088
  `Absolute project path on ${machine.name}`,
7838
8089
  machine.id === self.machine_id ? process.cwd() : "/home/you/project"
7839
8090
  );
7840
- if (!path12 || !path12.trim()) return;
7841
- const trimmed = path12.trim();
8091
+ if (!path13 || !path13.trim()) return;
8092
+ const trimmed = path13.trim();
7842
8093
  if (!trimmed.startsWith("/") && !trimmed.startsWith("~")) {
7843
8094
  tui.print(`${c3.yellow}Use an absolute path (starting with / or ~).${c3.reset}`);
7844
8095
  return;