flyspace-cli 0.1.30 → 0.1.31

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.
Files changed (2) hide show
  1. package/dist/cli.js +300 -222
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -642,6 +642,26 @@ async function requestHostDb(req) {
642
642
  send2({ type: "db", id, req });
643
643
  });
644
644
  }
645
+ var pendingUrls = /* @__PURE__ */ new Map();
646
+ function handleAttachmentUrlResult(id, url) {
647
+ pendingUrls.get(id)?.(url);
648
+ pendingUrls.delete(id);
649
+ }
650
+ async function requestAttachmentUrl(key) {
651
+ const send2 = await whenConnected();
652
+ const id = randomUUID();
653
+ return new Promise((resolve7, reject) => {
654
+ const timer2 = setTimeout(() => {
655
+ pendingUrls.delete(id);
656
+ reject(new Error("Data API did not answer in time"));
657
+ }, REQUEST_TIMEOUT_MS);
658
+ pendingUrls.set(id, (url) => {
659
+ clearTimeout(timer2);
660
+ resolve7(url);
661
+ });
662
+ send2({ type: "attachment-url", id, key });
663
+ });
664
+ }
645
665
  var BODYLESS_STATUS = /* @__PURE__ */ new Set([101, 204, 205, 304]);
646
666
  function dbFetch(send2) {
647
667
  return async (input2, init2) => {
@@ -861,14 +881,17 @@ function isServiceInstalled(profile) {
861
881
  return false;
862
882
  }
863
883
  function serviceBinaryMissing(file) {
884
+ const bin = serviceBinary(file);
885
+ return !!bin && !existsSync3(bin);
886
+ }
887
+ function serviceBinary(file) {
864
888
  let text;
865
889
  try {
866
890
  text = readFileSync5(file, "utf-8");
867
891
  } catch {
868
- return false;
892
+ return void 0;
869
893
  }
870
- const bin = text.match(/<key>ProgramArguments<\/key>\s*<array>\s*<string>([^<]+)<\/string>/)?.[1] ?? text.match(/^ExecStart=(\S+)/m)?.[1];
871
- return !!bin && !existsSync3(bin);
894
+ return text.match(/<key>ProgramArguments<\/key>\s*<array>\s*<string>([^<]+)<\/string>/)?.[1] ?? text.match(/^ExecStart=(\S+)/m)?.[1];
872
895
  }
873
896
  function profileServiceFile(profile) {
874
897
  return platform() === "darwin" ? plistPath(profile) : systemdUnitPath(profile);
@@ -1835,7 +1858,7 @@ async function runAuthCmd(args) {
1835
1858
  }
1836
1859
 
1837
1860
  // ../api/src/version.ts
1838
- var CLI_VERSION = true ? "0.1.30" : null;
1861
+ var CLI_VERSION = true ? "0.1.31" : null;
1839
1862
  function isOlderVersion(a, b) {
1840
1863
  const parse = (v) => v.replace(/^v/, "").split("-")[0].split(".").map(Number);
1841
1864
  const [pa, pb] = [parse(a), parse(b)];
@@ -2381,8 +2404,8 @@ async function runProfiles(args = []) {
2381
2404
 
2382
2405
  // src/commands/upgrade.ts
2383
2406
  import { execSync as execSync6 } from "child_process";
2384
- import { realpathSync as realpathSync3 } from "fs";
2385
- import { join as join11 } from "path";
2407
+ import { existsSync as existsSync9, readFileSync as readFileSync10, realpathSync as realpathSync3 } from "fs";
2408
+ import { dirname as dirname5, join as join11 } from "path";
2386
2409
  function restartInstalledProfiles() {
2387
2410
  if (isHostServiceInstalled()) {
2388
2411
  console.log(restartHostService() ? "\nHost restarted." : "\nHost restart failed; run: flyspace setup --start-host --restart");
@@ -2402,36 +2425,68 @@ Restarting ${installed.length} instance(s)...`);
2402
2425
  }
2403
2426
  }
2404
2427
  }
2405
- function npmInstallCommand() {
2406
- let prefix;
2428
+ function installTarget(entry) {
2429
+ const home2 = flyspaceHome(entry) ?? (entry.endsWith("/bin/flyspace") ? dirname5(dirname5(entry)) : void 0);
2430
+ if (home2 && existsSync9(join11(home2, "cli", "lib", "node_modules", "flyspace-cli"))) {
2431
+ return { prefix: join11(home2, "cli"), nodeBin: join11(home2, "node", "bin") };
2432
+ }
2433
+ try {
2434
+ const real = realpathSync3(entry);
2435
+ const at = real.indexOf(`/lib/node_modules/${"flyspace-cli"}/`);
2436
+ if (at > 0) return { prefix: real.slice(0, at), nodeBin: join11(real.slice(0, at), "bin") };
2437
+ } catch {
2438
+ }
2439
+ return void 0;
2440
+ }
2441
+ function installTargets() {
2442
+ const files = [
2443
+ ...isHostServiceInstalled() ? [hostServiceFile()] : [],
2444
+ ...listProfiles().filter(isServiceInstalled).map(profileServiceFile)
2445
+ ];
2446
+ const entries2 = [process.argv[1] ?? "", ...files.map(serviceBinary).filter((b) => !!b)];
2447
+ const byPrefix = /* @__PURE__ */ new Map();
2448
+ for (const target of entries2.map(installTarget)) {
2449
+ if (target && !byPrefix.has(target.prefix)) byPrefix.set(target.prefix, target);
2450
+ }
2451
+ return [...byPrefix.values()];
2452
+ }
2453
+ function installedVersion(target) {
2407
2454
  try {
2408
- const entry = realpathSync3(process.argv[1] ?? "");
2409
- const at = entry.indexOf(`/lib/node_modules/${"flyspace-cli"}/`);
2410
- if (at > 0) prefix = entry.slice(0, at);
2455
+ const pkg = join11(target.prefix, "lib", "node_modules", "flyspace-cli", "package.json");
2456
+ return JSON.parse(readFileSync10(pkg, "utf-8")).version;
2411
2457
  } catch {
2458
+ return void 0;
2412
2459
  }
2413
- const global = `-g${prefix ? ` --prefix "${prefix}"` : ""}`;
2460
+ }
2461
+ function npmInstallCommand(target) {
2462
+ const global = `-g${target ? ` --prefix "${target.prefix}"` : ""}`;
2414
2463
  return `npm install ${global} --ignore-scripts ${"flyspace-cli"}@latest && npm rebuild ${global} better-sqlite3`;
2415
2464
  }
2416
- function npmEnv() {
2465
+ function npmEnv(target) {
2417
2466
  const home2 = flyspaceHome();
2418
- return home2 ? { ...process.env, PATH: `${join11(home2, "node", "bin")}:${process.env.PATH ?? ""}` } : process.env;
2467
+ const nodeBin = target?.nodeBin ?? (home2 ? join11(home2, "node", "bin") : void 0);
2468
+ return nodeBin ? { ...process.env, PATH: `${nodeBin}:${process.env.PATH ?? ""}` } : process.env;
2419
2469
  }
2420
2470
  async function runUpgrade() {
2421
- const current = "0.1.30";
2471
+ const current = "0.1.31";
2422
2472
  console.log(`Current version: ${current}`);
2423
2473
  console.log("Checking for updates...\n");
2424
2474
  try {
2425
2475
  const latest2 = execSync6(`npm view ${"flyspace-cli"} version`, { encoding: "utf-8", env: npmEnv() }).trim();
2426
- if (latest2 === current) {
2476
+ const stale = installTargets().filter((t) => installedVersion(t) !== latest2);
2477
+ if (stale.length === 0) {
2427
2478
  console.log(`Already on the latest version (${current}).`);
2428
2479
  return;
2429
2480
  }
2430
2481
  console.log(`New version available: ${latest2}`);
2431
- console.log("Upgrading...\n");
2432
- execSync6(npmInstallCommand(), { stdio: "inherit", env: npmEnv() });
2482
+ for (const target of stale) {
2483
+ console.log(`
2484
+ Upgrading ${target.prefix} (${installedVersion(target) ?? "unknown"})...
2485
+ `);
2486
+ execSync6(npmInstallCommand(target), { stdio: "inherit", env: npmEnv(target) });
2487
+ }
2433
2488
  console.log(`
2434
- Upgraded from ${current} to ${latest2}.`);
2489
+ Upgraded to ${latest2}.`);
2435
2490
  restartInstalledProfiles();
2436
2491
  console.log("\nReload your shell to use the new version:");
2437
2492
  console.log(" source ~/.zshrc # or source ~/.bashrc");
@@ -2447,13 +2502,13 @@ You can upgrade manually with: ${npmInstallCommand()}`);
2447
2502
  import { WebClient } from "@slack/web-api";
2448
2503
 
2449
2504
  // src/automations/config-file.ts
2450
- import { readFileSync as readFileSync10, existsSync as existsSync9 } from "fs";
2505
+ import { readFileSync as readFileSync11, existsSync as existsSync10 } from "fs";
2451
2506
  import yaml from "js-yaml";
2452
2507
  var EMPTY_CONFIG = { schedules: [], triggers: [], mention_triggers: [] };
2453
2508
  function loadAutomationsFile() {
2454
2509
  const filePath = paths.AUTOMATIONS_FILE;
2455
- if (!existsSync9(filePath)) return { ...EMPTY_CONFIG };
2456
- const raw = readFileSync10(filePath, "utf-8");
2510
+ if (!existsSync10(filePath)) return { ...EMPTY_CONFIG };
2511
+ const raw = readFileSync11(filePath, "utf-8");
2457
2512
  if (!raw.trim()) return { ...EMPTY_CONFIG };
2458
2513
  const parsed = yaml.load(raw);
2459
2514
  return {
@@ -2809,7 +2864,7 @@ async function runAutomationCmd(args) {
2809
2864
  }
2810
2865
 
2811
2866
  // src/commands/usage.ts
2812
- import { existsSync as existsSync10 } from "fs";
2867
+ import { existsSync as existsSync11 } from "fs";
2813
2868
  var HELP4 = `
2814
2869
  Usage: flyspace usage [options]
2815
2870
 
@@ -2936,7 +2991,7 @@ async function runUsage(args) {
2936
2991
  return;
2937
2992
  }
2938
2993
  const { days, by, explain, json } = parsed;
2939
- if (!existsSync10(paths.DB_FILE)) {
2994
+ if (!existsSync11(paths.DB_FILE)) {
2940
2995
  console.log("No usage recorded yet (the database does not exist).");
2941
2996
  return;
2942
2997
  }
@@ -3051,13 +3106,13 @@ async function runRestart() {
3051
3106
 
3052
3107
  // src/commands/logs.ts
3053
3108
  import { spawn as spawn3 } from "child_process";
3054
- import { existsSync as existsSync11 } from "fs";
3109
+ import { existsSync as existsSync12 } from "fs";
3055
3110
  import { join as join12 } from "path";
3056
3111
  async function runLogs(args) {
3057
3112
  const showErrors = args.includes("--error") || args.includes("-e");
3058
3113
  const filename = showErrors ? "custie-error.log" : "custie.log";
3059
3114
  const logFile = join12(paths.LOG_DIR, filename);
3060
- if (!existsSync11(logFile)) {
3115
+ if (!existsSync12(logFile)) {
3061
3116
  console.error(`Log file not found: ${logFile}`);
3062
3117
  console.error("Is the service installed? Run `flyspace install` first.");
3063
3118
  process.exit(1);
@@ -3078,7 +3133,7 @@ async function runLogs(args) {
3078
3133
 
3079
3134
  // src/commands/slack.ts
3080
3135
  import { execFileSync as execFileSync3 } from "child_process";
3081
- import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
3136
+ import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
3082
3137
  import { resolve as resolve4 } from "path";
3083
3138
  import { tmpdir as tmpdir2, homedir as homedir6 } from "os";
3084
3139
  import { input, confirm as confirm4, select, checkbox } from "@inquirer/prompts";
@@ -5164,7 +5219,7 @@ async function threadReplies(client2, nameOrId, ts2) {
5164
5219
  async function postMessage(client2, args) {
5165
5220
  const channelArg = getArg2(args, "--channel");
5166
5221
  const textFilePath = getArg2(args, "--text-file");
5167
- const text = textFilePath ? readFileSync11(textFilePath, "utf8") : getArg2(args, "--text");
5222
+ const text = textFilePath ? readFileSync12(textFilePath, "utf8") : getArg2(args, "--text");
5168
5223
  const threadTs = getArg2(args, "--thread");
5169
5224
  const markdown = args.includes("--markdown");
5170
5225
  const broadcast = args.includes("--broadcast") && Boolean(threadTs);
@@ -5262,7 +5317,7 @@ function defaultClaudeConfigDir() {
5262
5317
  return process.env.CLAUDE_CONFIG_DIR || resolve4(homedir6(), ".claude");
5263
5318
  }
5264
5319
  function hasClaudeCredentials(configDir2) {
5265
- if (existsSync12(resolve4(configDir2, ".credentials.json"))) {
5320
+ if (existsSync13(resolve4(configDir2, ".credentials.json"))) {
5266
5321
  return true;
5267
5322
  }
5268
5323
  if (process.platform === "darwin") {
@@ -5319,7 +5374,7 @@ async function ensureClaudeCodeReady() {
5319
5374
  }
5320
5375
  function getManifestYaml(botName) {
5321
5376
  const manifestPath = resolve4(paths.PACKAGE_ROOT, "slack-app-manifest.yml");
5322
- const raw = readFileSync11(manifestPath, "utf-8");
5377
+ const raw = readFileSync12(manifestPath, "utf-8");
5323
5378
  return raw.replace(/name: Custie/g, `name: ${botName}`);
5324
5379
  }
5325
5380
  async function stepPersonalise() {
@@ -5748,10 +5803,10 @@ function printNextSteps() {
5748
5803
  console.log(" Run \x1B[1mflyspace service install\x1B[0m\n");
5749
5804
  }
5750
5805
  function copyDefaultPrompt(botName) {
5751
- if (!existsSync12(paths.PROMPT_FILE)) {
5806
+ if (!existsSync13(paths.PROMPT_FILE)) {
5752
5807
  const defaultPrompt = resolve4(paths.PACKAGE_ROOT, "system.default.md");
5753
- if (existsSync12(defaultPrompt)) {
5754
- const content = readFileSync11(defaultPrompt, "utf-8").replace(/\{\{botName\}\}/g, botName);
5808
+ if (existsSync13(defaultPrompt)) {
5809
+ const content = readFileSync12(defaultPrompt, "utf-8").replace(/\{\{botName\}\}/g, botName);
5755
5810
  writeFileSync7(paths.PROMPT_FILE, content);
5756
5811
  success4(`Default prompt copied to ${paths.PROMPT_FILE}`);
5757
5812
  }
@@ -5764,7 +5819,7 @@ async function runSlackSetup(args) {
5764
5819
  const prereq = await ensureClaudeCodeReady();
5765
5820
  if (!prereq) return;
5766
5821
  const { claudeConfigDir } = prereq;
5767
- if (existsSync12(paths.CONFIG_FILE)) {
5822
+ if (existsSync13(paths.CONFIG_FILE)) {
5768
5823
  const reconfigure = await confirm4({
5769
5824
  message: `Config already exists at ${paths.CONFIG_FILE}. Reconfigure?`,
5770
5825
  default: false
@@ -6075,10 +6130,10 @@ async function runChatCmd(args) {
6075
6130
  }
6076
6131
 
6077
6132
  // src/commands/memory.ts
6078
- import { readFileSync as readFileSync13 } from "fs";
6133
+ import { readFileSync as readFileSync14 } from "fs";
6079
6134
 
6080
6135
  // src/store/claude-memory-import.ts
6081
- import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync12 } from "fs";
6136
+ import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync13 } from "fs";
6082
6137
  import { homedir as homedir7 } from "os";
6083
6138
  import { basename as basename5, join as join14 } from "path";
6084
6139
  import dotenv from "dotenv";
@@ -6088,7 +6143,7 @@ function claudeMemoryDir(cwd, configDir2) {
6088
6143
  function profileClaudeConfigDirFor(profile) {
6089
6144
  const dir = profile === "default" ? paths.BASE_CONFIG_DIR : join14(paths.PROFILES_DIR, profile);
6090
6145
  const file = join14(dir, "config.env");
6091
- const env = existsSync13(file) ? dotenv.parse(readFileSync12(file)) : {};
6146
+ const env = existsSync14(file) ? dotenv.parse(readFileSync13(file)) : {};
6092
6147
  return env["CLAUDE_CONFIG_DIR"]?.trim() || join14(homedir7(), ".claude");
6093
6148
  }
6094
6149
  var TYPE_MAP = {
@@ -6134,11 +6189,11 @@ function parseClaudeMemory(fileName, text, channelId) {
6134
6189
  return input2;
6135
6190
  }
6136
6191
  async function importClaudeMemories(dir, channelId, opts = {}) {
6137
- if (!existsSync13(dir)) throw new Error(`No Claude memory directory at ${dir}`);
6192
+ if (!existsSync14(dir)) throw new Error(`No Claude memory directory at ${dir}`);
6138
6193
  const files = readdirSync3(dir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md").sort();
6139
6194
  const results = [];
6140
6195
  for (const file of files) {
6141
- const parsed = parseClaudeMemory(file, readFileSync12(join14(dir, file), "utf-8"), channelId);
6196
+ const parsed = parseClaudeMemory(file, readFileSync13(join14(dir, file), "utf-8"), channelId);
6142
6197
  if ("skip" in parsed) {
6143
6198
  results.push({ file, outcome: "skipped", reason: parsed.skip });
6144
6199
  continue;
@@ -6250,7 +6305,7 @@ ${memory.body}`);
6250
6305
  );
6251
6306
  }
6252
6307
  if (!isMemoryType(type)) throw new Error(`--type must be one of ${MEMORY_TYPES.join(", ")}`);
6253
- const body = bodyArg === "-" ? readFileSync13(0, "utf-8") : bodyArg;
6308
+ const body = bodyArg === "-" ? readFileSync14(0, "utf-8") : bodyArg;
6254
6309
  const thread = getArg4(rest, "--thread");
6255
6310
  const { created, changed } = await saveMemory(
6256
6311
  target,
@@ -6349,12 +6404,12 @@ async function runMcpCmd(args) {
6349
6404
 
6350
6405
  // src/commands/dashboard.ts
6351
6406
  import { spawn as spawn8, spawnSync } from "child_process";
6352
- import { existsSync as existsSync32 } from "fs";
6407
+ import { existsSync as existsSync33 } from "fs";
6353
6408
  import { platform as platform7 } from "os";
6354
6409
  import { join as join29 } from "path";
6355
6410
 
6356
6411
  // src/reconcile.ts
6357
- import { existsSync as existsSync14, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
6412
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
6358
6413
  async function boundWorkspaces2() {
6359
6414
  return (await boundWorkspaces())?.workspaces ?? null;
6360
6415
  }
@@ -6365,12 +6420,12 @@ async function servedWorkspaces2() {
6365
6420
  async function reconcileProfile(profile) {
6366
6421
  return runWithProfile(profile, async () => {
6367
6422
  const log5 = [];
6368
- const hadConfigDir = existsSync14(paths.CONFIG_DIR);
6369
- const hadDataDir = existsSync14(paths.DATA_DIR);
6423
+ const hadConfigDir = existsSync15(paths.CONFIG_DIR);
6424
+ const hadDataDir = existsSync15(paths.DATA_DIR);
6370
6425
  ensureDirs();
6371
6426
  if (!hadConfigDir) log5.push(`config dir created (${paths.CONFIG_DIR})`);
6372
6427
  if (!hadDataDir) log5.push(`data dir created (${paths.DATA_DIR})`);
6373
- if (!existsSync14(paths.CONFIG_FILE)) {
6428
+ if (!existsSync15(paths.CONFIG_FILE)) {
6374
6429
  writeFileSync8(paths.CONFIG_FILE, `BOT_NAME=${profile}
6375
6430
  `);
6376
6431
  log5.push(`config.env written (${paths.CONFIG_FILE})`);
@@ -6527,7 +6582,7 @@ var requireDocsAccess = async (c, next) => {
6527
6582
  import { Hono as Hono29 } from "hono";
6528
6583
 
6529
6584
  // ../api/src/dashboard/public-share-routes.ts
6530
- import { existsSync as existsSync16, readFileSync as readFileSync15, statSync as statSync3 } from "fs";
6585
+ import { existsSync as existsSync17, readFileSync as readFileSync16, statSync as statSync3 } from "fs";
6531
6586
  import { join as join16 } from "path";
6532
6587
  import { Hono as Hono5 } from "hono";
6533
6588
  import { GetObjectCommand } from "@aws-sdk/client-s3";
@@ -6993,11 +7048,11 @@ function injectShareHead(html, head) {
6993
7048
 
6994
7049
  // ../api/src/dashboard/app-icon.ts
6995
7050
  import { createHash as createHash2 } from "crypto";
6996
- import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
7051
+ import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
6997
7052
  import { join as join15 } from "path";
6998
7053
  import sharp2 from "sharp";
6999
7054
  var PLATE = "#0A0A0F";
7000
- var BRAND_DIR = existsSync15(join15(paths.WEB_ROOT, "public")) ? join15(paths.WEB_ROOT, "public") : join15(paths.PACKAGE_ROOT, "dist", "brand");
7055
+ var BRAND_DIR = existsSync16(join15(paths.WEB_ROOT, "public")) ? join15(paths.WEB_ROOT, "public") : join15(paths.PACKAGE_ROOT, "dist", "brand");
7001
7056
  var MARK_FILE = join15(BRAND_DIR, "icons", "icon-512.png");
7002
7057
  var MARK_SVG_FILE = join15(BRAND_DIR, "favicon.svg");
7003
7058
  var LAYOUT = {
@@ -7022,7 +7077,7 @@ function roundedPlate(size, fill = PLATE) {
7022
7077
  }
7023
7078
  async function renderBadge(box) {
7024
7079
  const transparent = { r: 0, g: 0, b: 0, alpha: 0 };
7025
- const raster = await sharp2(readFileSync14(MARK_SVG_FILE)).resize(512, 512, { fit: "contain", background: transparent }).png().toBuffer();
7080
+ const raster = await sharp2(readFileSync15(MARK_SVG_FILE)).resize(512, 512, { fit: "contain", background: transparent }).png().toBuffer();
7026
7081
  return sharp2(raster).trim().resize(box, box, { fit: "contain", background: transparent }).png().toBuffer();
7027
7082
  }
7028
7083
  async function sampleBackground(logo) {
@@ -7077,7 +7132,7 @@ async function renderAppIcon({
7077
7132
  if (hit) return hit;
7078
7133
  const out = logo && await composeIcon(logo, size, maskable, plain).catch(() => null) || // No workspace logo to lead with — the stock mark is still the best icon,
7079
7134
  // either on its usual dark plate or, for a favicon, trimmed and bare.
7080
- (transparent ? await renderBadge(size) : await sharp2(readFileSync14(MARK_FILE)).resize(size, size, { fit: "cover" }).png().toBuffer());
7135
+ (transparent ? await renderBadge(size) : await sharp2(readFileSync15(MARK_FILE)).resize(size, size, { fit: "cover" }).png().toBuffer());
7081
7136
  cache3.set(key, out);
7082
7137
  return out;
7083
7138
  }
@@ -8295,7 +8350,7 @@ var shellCache = null;
8295
8350
  function readShell(path) {
8296
8351
  const { mtimeMs } = statSync3(path);
8297
8352
  if (shellCache?.mtimeMs === mtimeMs) return shellCache.html;
8298
- const html = readFileSync15(path, "utf8").replaceAll('"/assets/', `"${BUILD_PREFIX}/`);
8353
+ const html = readFileSync16(path, "utf8").replaceAll('"/assets/', `"${BUILD_PREFIX}/`);
8299
8354
  shellCache = { mtimeMs, html };
8300
8355
  return html;
8301
8356
  }
@@ -8316,16 +8371,16 @@ function serveBuildFile(c, webDist, dir, name) {
8316
8371
  return c.text("not found", 404);
8317
8372
  }
8318
8373
  const path = join16(webDist, dir, name);
8319
- if (!existsSync16(path)) return c.text("not found", 404);
8374
+ if (!existsSync17(path)) return c.text("not found", 404);
8320
8375
  c.header("Content-Type", type);
8321
8376
  c.header("Cache-Control", "public, max-age=31536000, immutable");
8322
8377
  if (ext === ".css") {
8323
- return c.body(readFileSync15(path, "utf8").replaceAll("url(/fonts/", `url(${BUILD_PREFIX}/fonts/`));
8378
+ return c.body(readFileSync16(path, "utf8").replaceAll("url(/fonts/", `url(${BUILD_PREFIX}/fonts/`));
8324
8379
  }
8325
8380
  if (ext === ".js") {
8326
- return c.body(readFileSync15(path, "utf8").replaceAll('"/assets/', `"${BUILD_PREFIX}/`));
8381
+ return c.body(readFileSync16(path, "utf8").replaceAll('"/assets/', `"${BUILD_PREFIX}/`));
8327
8382
  }
8328
- return c.body(new Uint8Array(readFileSync15(path)));
8383
+ return c.body(new Uint8Array(readFileSync16(path)));
8329
8384
  }
8330
8385
  var HTML_SHARE_CSP = "sandbox allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox";
8331
8386
  function servePublishedHtml(c, share) {
@@ -8462,7 +8517,7 @@ ${analytics}` : head);
8462
8517
  const lang = canonicalLanguage(requested);
8463
8518
  if (!isTranslatableLanguage(lang)) return next();
8464
8519
  const shell = join16(webDist, "index.html");
8465
- if (!existsSync16(shell)) return next();
8520
+ if (!existsSync17(shell)) return next();
8466
8521
  const share = await getDocShare(c.req.param("id"));
8467
8522
  if (!share) return next();
8468
8523
  if (requested !== lang) return c.redirect(`/share/${share.id}/${lang}`, 302);
@@ -8498,7 +8553,7 @@ ${analytics}` : head);
8498
8553
  if (!share) return next();
8499
8554
  if (share.kind === "html") return servePublishedHtml(c, share);
8500
8555
  const shell = join16(webDist, "index.html");
8501
- if (!existsSync16(shell)) return next();
8556
+ if (!existsSync17(shell)) return next();
8502
8557
  return readerPage(
8503
8558
  c,
8504
8559
  share,
@@ -8564,7 +8619,7 @@ function createGuestRouter() {
8564
8619
  }
8565
8620
 
8566
8621
  // ../api/src/dashboard/auth-routes.ts
8567
- import { Hono as Hono8 } from "hono";
8622
+ import { Hono as Hono9 } from "hono";
8568
8623
 
8569
8624
  // ../api/src/dashboard/supabase-server-client.ts
8570
8625
  import { createServerClient, parseCookieHeader, serializeCookieHeader } from "@supabase/ssr";
@@ -8675,7 +8730,7 @@ function pruneExpired(now) {
8675
8730
  }
8676
8731
  }
8677
8732
  var SUPPORT_NAME = "Flyspace Support";
8678
- var SUPPORT_AVATAR_URL = "/logo/flyspace-mark.svg";
8733
+ var SUPPORT_AVATAR_URL = "/logo/flyspace-support.svg";
8679
8734
  async function resolveAuthContext(c, opts = {}) {
8680
8735
  const cookie = c.req?.header("Cookie") ?? "";
8681
8736
  if (!cookie) return resolveAuthContextUncached(c);
@@ -8806,7 +8861,7 @@ async function verifyTurnstileToken(token) {
8806
8861
  }
8807
8862
 
8808
8863
  // ../api/src/dashboard/onboarding-routes.ts
8809
- import { Hono as Hono7 } from "hono";
8864
+ import { Hono as Hono8 } from "hono";
8810
8865
 
8811
8866
  // ../api/src/store/device-store.ts
8812
8867
  import { createHash as createHash7, createPublicKey as createPublicKey2, randomBytes as randomBytes4, randomInt } from "crypto";
@@ -9035,23 +9090,83 @@ async function userEmail(userId) {
9035
9090
  }
9036
9091
 
9037
9092
  // ../api/src/dashboard/agent-bridge-data.ts
9038
- import { randomBytes as randomBytes5, randomUUID as randomUUID2, verify as verifyRaw } from "crypto";
9093
+ import { randomBytes as randomBytes5, randomUUID as randomUUID3, verify as verifyRaw } from "crypto";
9039
9094
  import { WebSocket, WebSocketServer } from "ws";
9040
9095
 
9041
9096
  // ../api/src/dashboard/host-identity.ts
9042
9097
  import { createPrivateKey as createPrivateKey2, createPublicKey as createPublicKey3, generateKeyPairSync as generateKeyPairSync2 } from "crypto";
9043
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
9044
- import { dirname as dirname5, join as join17 } from "path";
9098
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync17, writeFileSync as writeFileSync9 } from "fs";
9099
+ import { dirname as dirname6, join as join17 } from "path";
9045
9100
  function hostPrivateKeyPath() {
9046
9101
  return process.env["FLYSPACE_HOST_KEY_PATH"] || join17(paths.BASE_CONFIG_DIR, "host.key");
9047
9102
  }
9048
9103
  function loadHostPrivateKey(path = hostPrivateKeyPath()) {
9049
- if (!existsSync17(path)) {
9104
+ if (!existsSync18(path)) {
9050
9105
  throw new Error(
9051
9106
  `No Host identity at ${path}. Run \`pnpm run generate-host-key\` on this machine, then set FLYSPACE_HOST_PUBLIC_KEY on the Data process to the printed public key.`
9052
9107
  );
9053
9108
  }
9054
- return createPrivateKey2(readFileSync16(path, "utf8"));
9109
+ return createPrivateKey2(readFileSync17(path, "utf8"));
9110
+ }
9111
+
9112
+ // ../api/src/dashboard/uploads-routes.ts
9113
+ import { randomUUID as randomUUID2 } from "crypto";
9114
+ import { Hono as Hono7 } from "hono";
9115
+ import { PutObjectCommand as PutObjectCommand2, GetObjectCommand as GetObjectCommand3 } from "@aws-sdk/client-s3";
9116
+ import { getSignedUrl as getSignedUrl3 } from "@aws-sdk/s3-request-presigner";
9117
+ var MAX_FILE_BYTES = 10 * 1024 * 1024;
9118
+ var PRESIGN_TTL_SECONDS2 = 300;
9119
+ var KEY_PREFIX = "chat";
9120
+ var READABLE_PREFIXES = [KEY_PREFIX, SCRATCH_PREFIX];
9121
+ function sanitizeFilename(name) {
9122
+ const cleaned = name.replace(/[/\\]/g, "_").replace(/\s+/g, "-").replace(/[^\w.-]/g, "").trim();
9123
+ return (cleaned || "file").slice(-100);
9124
+ }
9125
+ function encodeKeyForUrl(key) {
9126
+ return key.split("/").map(encodeURIComponent).join("/");
9127
+ }
9128
+ async function presignAttachmentRead(key) {
9129
+ if (!READABLE_PREFIXES.some((prefix) => key.startsWith(`${prefix}/`))) return null;
9130
+ return getSignedUrl3(getR2Client(), new GetObjectCommand3({ Bucket: getR2Bucket(), Key: key }), {
9131
+ expiresIn: PRESIGN_TTL_SECONDS2
9132
+ });
9133
+ }
9134
+ function createUploadsRouter() {
9135
+ const app = new Hono7();
9136
+ app.post("/uploads", async (c) => {
9137
+ const body = await c.req.parseBody().catch(() => null);
9138
+ const file = body?.["file"];
9139
+ if (!(file instanceof File)) return c.json({ error: "missing file" }, 400);
9140
+ if (file.size > MAX_FILE_BYTES) {
9141
+ return c.json({ error: `file must be ${MAX_FILE_BYTES / (1024 * 1024)}MB or smaller` }, 400);
9142
+ }
9143
+ const key = `${KEY_PREFIX}/${getProfile()}/${randomUUID2()}-${sanitizeFilename(file.name)}`;
9144
+ const buffer = Buffer.from(await file.arrayBuffer());
9145
+ await getR2Client().send(
9146
+ new PutObjectCommand2({
9147
+ Bucket: getR2Bucket(),
9148
+ Key: key,
9149
+ Body: buffer,
9150
+ ContentType: file.type || "application/octet-stream"
9151
+ })
9152
+ );
9153
+ return c.json(
9154
+ {
9155
+ url: `/api/attachments/${encodeKeyForUrl(key)}`,
9156
+ name: file.name,
9157
+ type: file.type,
9158
+ size: file.size
9159
+ },
9160
+ 201
9161
+ );
9162
+ });
9163
+ app.get("/attachments/:key{.+}", async (c) => {
9164
+ const url = await presignAttachmentRead(c.req.param("key"));
9165
+ if (!url) return c.json({ error: "not found" }, 404);
9166
+ c.header("Cache-Control", `private, max-age=${PRESIGN_TTL_SECONDS2 - 60}`);
9167
+ return c.redirect(url, 302);
9168
+ });
9169
+ return app;
9055
9170
  }
9056
9171
 
9057
9172
  // ../core/src/bridge-protocol.ts
@@ -9216,7 +9331,7 @@ function hostUnavailable(c, agent, method) {
9216
9331
  return c.json({ error: "host offline", reason: agent.getHostCallFailure?.(method) ?? "offline" }, 503);
9217
9332
  }
9218
9333
  function createOnboardingRouter(agent) {
9219
- const app = new Hono7();
9334
+ const app = new Hono8();
9220
9335
  app.get("/scan", async (c) => {
9221
9336
  const ctx = await resolveAuthContext(c);
9222
9337
  if (ctx?.role !== "admin") return c.json({ error: "admin access required" }, 403);
@@ -9354,7 +9469,7 @@ async function ensureGreetingThread(workspaceId, channelId, ownerName) {
9354
9469
 
9355
9470
  // ../api/src/dashboard/auth-routes.ts
9356
9471
  function createAuthRouter() {
9357
- const auth = new Hono8();
9472
+ const auth = new Hono9();
9358
9473
  const emailedLink = (deliver) => async (c) => {
9359
9474
  const body = await c.req.json().catch(() => ({}));
9360
9475
  const email = body.email?.trim().toLowerCase();
@@ -9434,7 +9549,7 @@ var requireWorkspaceMembership = async (c, next) => {
9434
9549
  };
9435
9550
 
9436
9551
  // ../api/src/dashboard/billing-routes.ts
9437
- import { Hono as Hono9 } from "hono";
9552
+ import { Hono as Hono10 } from "hono";
9438
9553
  function isPlan(value) {
9439
9554
  return value === "local" || value === "cloud";
9440
9555
  }
@@ -9450,7 +9565,7 @@ async function requireActivePlan(c, next) {
9450
9565
  return next();
9451
9566
  }
9452
9567
  function createBillingRouter() {
9453
- const billing = new Hono9();
9568
+ const billing = new Hono10();
9454
9569
  billing.get("/", async (c) => {
9455
9570
  const auth = c.get("auth");
9456
9571
  const { planStatus, stripeSubscriptionId } = await getWorkspaceBilling(auth.workspaceId);
@@ -9506,7 +9621,7 @@ function createBillingRouter() {
9506
9621
  }
9507
9622
 
9508
9623
  // ../api/src/dashboard/admin-routes.ts
9509
- import { Hono as Hono10 } from "hono";
9624
+ import { Hono as Hono11 } from "hono";
9510
9625
 
9511
9626
  // ../api/src/dashboard/admin-middleware.ts
9512
9627
  function platformAdminEmails() {
@@ -9532,7 +9647,7 @@ var requirePlatformOperator = async (c, next) => {
9532
9647
  // ../api/src/dashboard/admin-routes.ts
9533
9648
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9534
9649
  function createAdminRouter() {
9535
- const admin = new Hono10();
9650
+ const admin = new Hono11();
9536
9651
  admin.use("*", requirePlatformOperator);
9537
9652
  admin.get("/workspaces", async (c) => {
9538
9653
  const summaries = await listWorkspaceSummaries();
@@ -9645,11 +9760,11 @@ var BOT_LABEL = "Workspace Agent";
9645
9760
  var BOT_DM_KEY = "claude";
9646
9761
 
9647
9762
  // ../api/src/dashboard/data-api.ts
9648
- import { existsSync as existsSync19 } from "fs";
9763
+ import { existsSync as existsSync20 } from "fs";
9649
9764
  import sharp5 from "sharp";
9650
9765
 
9651
9766
  // ../api/src/dashboard/automations-routes.ts
9652
- import { Hono as Hono11 } from "hono";
9767
+ import { Hono as Hono12 } from "hono";
9653
9768
  var TYPES2 = ["schedule", "trigger", "mention-trigger"];
9654
9769
  function isType(t) {
9655
9770
  return TYPES2.includes(t);
@@ -9666,7 +9781,7 @@ function statusFor(message) {
9666
9781
  return 400;
9667
9782
  }
9668
9783
  function createAutomationWriteRouter() {
9669
- const app = new Hono11();
9784
+ const app = new Hono12();
9670
9785
  const manager = () => new AutomationManager();
9671
9786
  app.get("/:type/:name", (c) => {
9672
9787
  const item = manager().get(c.req.param("name"));
@@ -9722,9 +9837,9 @@ function createAutomationWriteRouter() {
9722
9837
  }
9723
9838
 
9724
9839
  // ../api/src/dashboard/categories-routes.ts
9725
- import { Hono as Hono12 } from "hono";
9840
+ import { Hono as Hono13 } from "hono";
9726
9841
  function createCategoryWriteRouter() {
9727
- const app = new Hono12();
9842
+ const app = new Hono13();
9728
9843
  app.get("/", async (c) => {
9729
9844
  const { data, error: error2 } = await getSupabaseClient().from("categories").select("*").eq("profile", getProfile()).order("position", { ascending: true });
9730
9845
  if (error2) return c.json({ error: error2.message }, 500);
@@ -9778,7 +9893,7 @@ function createCategoryWriteRouter() {
9778
9893
  }
9779
9894
 
9780
9895
  // ../api/src/dashboard/chat-routes.ts
9781
- import { Hono as Hono13 } from "hono";
9896
+ import { Hono as Hono14 } from "hono";
9782
9897
 
9783
9898
  // ../api/src/dashboard/fork-summary.ts
9784
9899
  var GROQ_API_URL4 = "https://api.groq.com/openai/v1/chat/completions";
@@ -10265,7 +10380,7 @@ async function generateAutoTitle(threadId, channelId, body) {
10265
10380
  await supabase.from("threads").update({ auto_title: autoTitle }).eq("id", threadId).is("title", null);
10266
10381
  }
10267
10382
  function createChatRouter(agent = {}) {
10268
- const app = new Hono13();
10383
+ const app = new Hono14();
10269
10384
  const {
10270
10385
  onChatMessage: onMessage,
10271
10386
  isAgentThinking,
@@ -10779,7 +10894,7 @@ function createChatRouter(agent = {}) {
10779
10894
  }
10780
10895
 
10781
10896
  // ../api/src/dashboard/doc-shares-routes.ts
10782
- import { Hono as Hono14 } from "hono";
10897
+ import { Hono as Hono15 } from "hono";
10783
10898
 
10784
10899
  // ../api/src/dashboard/doc-share-features.ts
10785
10900
  import { DeleteObjectCommand as DeleteObjectCommand2 } from "@aws-sdk/client-s3";
@@ -10839,7 +10954,7 @@ async function updateShareFeatures(share, requested) {
10839
10954
 
10840
10955
  // ../api/src/dashboard/doc-share-publish.ts
10841
10956
  import { randomBytes as randomBytes6 } from "crypto";
10842
- import { CopyObjectCommand, PutObjectCommand as PutObjectCommand2 } from "@aws-sdk/client-s3";
10957
+ import { CopyObjectCommand, PutObjectCommand as PutObjectCommand3 } from "@aws-sdk/client-s3";
10843
10958
 
10844
10959
  // ../api/src/dashboard/bridge-types.ts
10845
10960
  var SHARE_ASSET_PLACEHOLDER = "/share/{{flyspace-share-id}}/assets/";
@@ -10935,7 +11050,7 @@ async function publishDocShare(input2, readBundle) {
10935
11050
  );
10936
11051
  } else {
10937
11052
  await client2.send(
10938
- new PutObjectCommand2({
11053
+ new PutObjectCommand3({
10939
11054
  Bucket: bucket,
10940
11055
  Key: key,
10941
11056
  Body: Buffer.from(asset.data, "base64"),
@@ -11114,7 +11229,7 @@ function resolveChannel2(c) {
11114
11229
  }
11115
11230
  var LIST_LIMIT = 500;
11116
11231
  function createDocShareListRouter() {
11117
- const app = new Hono14();
11232
+ const app = new Hono15();
11118
11233
  app.get("/shares", async (c) => {
11119
11234
  const viewer = c.get("auth");
11120
11235
  const readable = await filterReadableDocsChannels(
@@ -11158,7 +11273,7 @@ function createDocShareListRouter() {
11158
11273
  return app;
11159
11274
  }
11160
11275
  function createDocShareWriteRouter(agent = {}) {
11161
- const app = new Hono14();
11276
+ const app = new Hono15();
11162
11277
  app.use("/:id/docs/share", requireDocsAccess);
11163
11278
  app.use("/:id/docs/share/*", requireDocsAccess);
11164
11279
  app.use("/:id/docs/shares", requireDocsAccess);
@@ -11331,7 +11446,7 @@ function createDocShareWriteRouter(agent = {}) {
11331
11446
  }
11332
11447
 
11333
11448
  // ../api/src/dashboard/search-routes.ts
11334
- import { Hono as Hono15 } from "hono";
11449
+ import { Hono as Hono16 } from "hono";
11335
11450
  function matchScore(q, fields) {
11336
11451
  const terms = q.toLowerCase().split(/\s+/).filter(Boolean);
11337
11452
  if (terms.length === 0) return 0;
@@ -11479,7 +11594,7 @@ function searchAutomations(q) {
11479
11594
  }
11480
11595
  var RESULT_LIMIT = 30;
11481
11596
  function createSearchRouter() {
11482
- const app = new Hono15();
11597
+ const app = new Hono16();
11483
11598
  app.get("/", async (c) => {
11484
11599
  const q = c.req.query("q")?.trim();
11485
11600
  if (!q) return c.json({ results: [] });
@@ -11495,14 +11610,14 @@ function createSearchRouter() {
11495
11610
  }
11496
11611
 
11497
11612
  // ../api/src/dashboard/feature-flags-routes.ts
11498
- import { Hono as Hono16 } from "hono";
11613
+ import { Hono as Hono17 } from "hono";
11499
11614
  async function isFeatureEnabled(c, flagId) {
11500
11615
  const auth = c.get("auth");
11501
11616
  const enabled = await listEnabledFeatureFlags(auth.workspaceId);
11502
11617
  return resolveFeatureFlags(enabled, isDevHost(c))[flagId];
11503
11618
  }
11504
11619
  function createFeatureFlagsRouter() {
11505
- const app = new Hono16();
11620
+ const app = new Hono17();
11506
11621
  app.get("/", async (c) => {
11507
11622
  const auth = c.get("auth");
11508
11623
  const devHost = isDevHost(c);
@@ -11527,67 +11642,6 @@ function createFeatureFlagsRouter() {
11527
11642
  return app;
11528
11643
  }
11529
11644
 
11530
- // ../api/src/dashboard/uploads-routes.ts
11531
- import { randomUUID as randomUUID3 } from "crypto";
11532
- import { Hono as Hono17 } from "hono";
11533
- import { PutObjectCommand as PutObjectCommand3, GetObjectCommand as GetObjectCommand3 } from "@aws-sdk/client-s3";
11534
- import { getSignedUrl as getSignedUrl3 } from "@aws-sdk/s3-request-presigner";
11535
- var MAX_FILE_BYTES = 10 * 1024 * 1024;
11536
- var PRESIGN_TTL_SECONDS2 = 300;
11537
- var KEY_PREFIX = "chat";
11538
- var READABLE_PREFIXES = [KEY_PREFIX, SCRATCH_PREFIX];
11539
- function sanitizeFilename(name) {
11540
- const cleaned = name.replace(/[/\\]/g, "_").replace(/\s+/g, "-").replace(/[^\w.-]/g, "").trim();
11541
- return (cleaned || "file").slice(-100);
11542
- }
11543
- function encodeKeyForUrl(key) {
11544
- return key.split("/").map(encodeURIComponent).join("/");
11545
- }
11546
- function createUploadsRouter() {
11547
- const app = new Hono17();
11548
- app.post("/uploads", async (c) => {
11549
- const body = await c.req.parseBody().catch(() => null);
11550
- const file = body?.["file"];
11551
- if (!(file instanceof File)) return c.json({ error: "missing file" }, 400);
11552
- if (file.size > MAX_FILE_BYTES) {
11553
- return c.json({ error: `file must be ${MAX_FILE_BYTES / (1024 * 1024)}MB or smaller` }, 400);
11554
- }
11555
- const key = `${KEY_PREFIX}/${getProfile()}/${randomUUID3()}-${sanitizeFilename(file.name)}`;
11556
- const buffer = Buffer.from(await file.arrayBuffer());
11557
- await getR2Client().send(
11558
- new PutObjectCommand3({
11559
- Bucket: getR2Bucket(),
11560
- Key: key,
11561
- Body: buffer,
11562
- ContentType: file.type || "application/octet-stream"
11563
- })
11564
- );
11565
- return c.json(
11566
- {
11567
- url: `/api/attachments/${encodeKeyForUrl(key)}`,
11568
- name: file.name,
11569
- type: file.type,
11570
- size: file.size
11571
- },
11572
- 201
11573
- );
11574
- });
11575
- app.get("/attachments/:key{.+}", async (c) => {
11576
- const key = c.req.param("key");
11577
- if (!READABLE_PREFIXES.some((prefix) => key.startsWith(`${prefix}/`))) {
11578
- return c.json({ error: "not found" }, 404);
11579
- }
11580
- const url = await getSignedUrl3(
11581
- getR2Client(),
11582
- new GetObjectCommand3({ Bucket: getR2Bucket(), Key: key }),
11583
- { expiresIn: PRESIGN_TTL_SECONDS2 }
11584
- );
11585
- c.header("Cache-Control", `private, max-age=${PRESIGN_TTL_SECONDS2 - 60}`);
11586
- return c.redirect(url, 302);
11587
- });
11588
- return app;
11589
- }
11590
-
11591
11645
  // ../api/src/dashboard/transcribe-routes.ts
11592
11646
  import { Hono as Hono18 } from "hono";
11593
11647
  var GROQ_TRANSCRIBE_URL = "https://api.groq.com/openai/v1/audio/transcriptions";
@@ -13245,7 +13299,7 @@ async function sessionUsageForThreads(workspace, threadTsList) {
13245
13299
  }
13246
13300
 
13247
13301
  // ../api/src/dashboard/usage-report.ts
13248
- import { existsSync as existsSync18 } from "fs";
13302
+ import { existsSync as existsSync19 } from "fs";
13249
13303
  function mergeByLabel(buckets) {
13250
13304
  const merged = /* @__PURE__ */ new Map();
13251
13305
  for (const bucket of buckets) {
@@ -13291,7 +13345,7 @@ function toReport(days, rows, labelChannel) {
13291
13345
  };
13292
13346
  }
13293
13347
  function fromLocalStore(days, labelChannel) {
13294
- const store = existsSync18(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
13348
+ const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
13295
13349
  if (!store) return { ...EMPTY, days };
13296
13350
  const window = days === 0 ? void 0 : days;
13297
13351
  try {
@@ -13333,7 +13387,7 @@ async function buildChannelUsageReport(channelId, days, labelChannel) {
13333
13387
  const { totals, byDay } = await channelUsageForWorkspace(workspace, aliasIds, window);
13334
13388
  return { days, totals: totals.runs > 0 ? totals : null, byDay };
13335
13389
  } catch {
13336
- const store = existsSync18(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
13390
+ const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
13337
13391
  if (!store) return { days, totals: null, byDay: [] };
13338
13392
  try {
13339
13393
  const targetLabel = labelChannel(channelId);
@@ -13355,7 +13409,7 @@ async function buildThreadUsage(threadTs) {
13355
13409
  if (totals.runs === 0) return { threadTs, totals: null, byModel: [] };
13356
13410
  return { threadTs, totals, byModel };
13357
13411
  } catch {
13358
- const store = existsSync18(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
13412
+ const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
13359
13413
  if (!store) return { threadTs, totals: null, byModel: [] };
13360
13414
  try {
13361
13415
  const totals = store.threadTotals(threadTs);
@@ -13793,7 +13847,7 @@ function registerDataRoutes(api, agent = {}) {
13793
13847
  runs.set(name, { lastRun: run4.lastRunAt.toISOString(), lastStatus: run4.lastStatus });
13794
13848
  }
13795
13849
  } catch {
13796
- const runStore = existsSync19(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
13850
+ const runStore = existsSync20(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
13797
13851
  if (runStore) {
13798
13852
  try {
13799
13853
  for (const run4 of runStore.listLastRuns()) {
@@ -13855,7 +13909,7 @@ function registerDataRoutes(api, agent = {}) {
13855
13909
  const runs = (await recentRunsForWorkspace(getProfile(), 200)).map(toResponse5);
13856
13910
  return c.json({ runs });
13857
13911
  } catch {
13858
- const runStore = existsSync19(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
13912
+ const runStore = existsSync20(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
13859
13913
  if (!runStore) return c.json({ runs: [] });
13860
13914
  try {
13861
13915
  const runs = runStore.listRuns(200).map(toResponse5);
@@ -13977,7 +14031,7 @@ function registerDataRoutes(api, agent = {}) {
13977
14031
  } catch {
13978
14032
  latest2 = /* @__PURE__ */ new Map();
13979
14033
  totals = /* @__PURE__ */ new Map();
13980
- const usageStore = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
14034
+ const usageStore = existsSync20(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
13981
14035
  if (usageStore) {
13982
14036
  try {
13983
14037
  latest2 = usageStore.latestByThread(threadTsList);
@@ -14243,11 +14297,11 @@ function createDeviceAuthRouter() {
14243
14297
  }
14244
14298
 
14245
14299
  // ../api/src/dashboard/skills-routes.ts
14246
- import { dirname as dirname6 } from "path";
14300
+ import { dirname as dirname7 } from "path";
14247
14301
 
14248
14302
  // ../api/src/dashboard/skill-mirror.ts
14249
14303
  import { createHash as createHash11 } from "crypto";
14250
- import { existsSync as existsSync20, statSync as statSync4 } from "fs";
14304
+ import { existsSync as existsSync21, statSync as statSync4 } from "fs";
14251
14305
  var SYNC_INTERVAL_MS = 6e4;
14252
14306
  var UPSERT_BATCH = 50;
14253
14307
  function mirrorScopeOf(ref) {
@@ -14255,7 +14309,7 @@ function mirrorScopeOf(ref) {
14255
14309
  }
14256
14310
  function isDir(path) {
14257
14311
  try {
14258
- return existsSync20(path) && statSync4(path).isDirectory();
14312
+ return existsSync21(path) && statSync4(path).isDirectory();
14259
14313
  } catch {
14260
14314
  return false;
14261
14315
  }
@@ -14580,12 +14634,12 @@ function createGatedRouter(agent, register) {
14580
14634
  }
14581
14635
 
14582
14636
  // src/dashboard/host-api.ts
14583
- import { existsSync as existsSync29, readFileSync as readFileSync23, statSync as statSync8 } from "fs";
14637
+ import { existsSync as existsSync30, readFileSync as readFileSync24, statSync as statSync8 } from "fs";
14584
14638
  import { join as join26 } from "path";
14585
14639
  import sharp6 from "sharp";
14586
14640
 
14587
14641
  // src/dashboard/channels-routes.ts
14588
- import { existsSync as existsSync21, mkdirSync as mkdirSync10 } from "fs";
14642
+ import { existsSync as existsSync22, mkdirSync as mkdirSync10 } from "fs";
14589
14643
  import { Hono as Hono30 } from "hono";
14590
14644
  function coerceAccess(raw) {
14591
14645
  if (raw === "open" || raw === "*" || raw === "all") return raw;
@@ -14624,7 +14678,7 @@ function coerceMemberEmails(raw) {
14624
14678
  function ensureCwdDir(cwd) {
14625
14679
  const expanded = expandPath(cwd);
14626
14680
  if (isLogicalCwd(cwd) || isUnderLogicalRoot(expanded)) return;
14627
- if (!existsSync21(expanded)) mkdirSync10(expanded, { recursive: true });
14681
+ if (!existsSync22(expanded)) mkdirSync10(expanded, { recursive: true });
14628
14682
  }
14629
14683
  function toResponse3(channel) {
14630
14684
  return {
@@ -14774,13 +14828,13 @@ function createChannelWriteRouter() {
14774
14828
  import {
14775
14829
  closeSync as closeSync2,
14776
14830
  createReadStream,
14777
- existsSync as existsSync25,
14831
+ existsSync as existsSync26,
14778
14832
  lstatSync as lstatSync2,
14779
14833
  mkdirSync as mkdirSync11,
14780
14834
  openSync as openSync2,
14781
14835
  readSync as readSync2,
14782
14836
  readdirSync as readdirSync5,
14783
- readFileSync as readFileSync20,
14837
+ readFileSync as readFileSync21,
14784
14838
  realpathSync as realpathSync4,
14785
14839
  renameSync,
14786
14840
  rmSync as rmSync8,
@@ -14788,12 +14842,12 @@ import {
14788
14842
  writeFileSync as writeFileSync11
14789
14843
  } from "fs";
14790
14844
  import { Readable } from "stream";
14791
- import { basename as basename8, dirname as dirname7, extname, join as join23, relative, resolve as resolve5, sep } from "path";
14845
+ import { basename as basename8, dirname as dirname8, extname, join as join23, relative, resolve as resolve5, sep } from "path";
14792
14846
  import { Hono as Hono31 } from "hono";
14793
14847
  import { streamSSE as streamSSE2 } from "hono/streaming";
14794
14848
 
14795
14849
  // src/dashboard/docs-watcher.ts
14796
- import { existsSync as existsSync22, watch } from "fs";
14850
+ import { existsSync as existsSync23, watch } from "fs";
14797
14851
  import { basename as basename6, join as join18 } from "path";
14798
14852
  var DEBOUNCE_MS = 200;
14799
14853
  var MAX_PENDING = 200;
@@ -14835,7 +14889,7 @@ function watchDocs(cwd, listener) {
14835
14889
  const current = entries.get(cwd);
14836
14890
  if (!current || filename === null) return;
14837
14891
  const rel = filename.split("\\").join("/");
14838
- if (rel === root && !existsSync22(join18(cwd, rel))) return;
14892
+ if (rel === root && !existsSync23(join18(cwd, rel))) return;
14839
14893
  if (isIgnoredDocPath(rel)) return;
14840
14894
  if (current.pending.size < MAX_PENDING) current.pending.add(rel);
14841
14895
  if (current.timer === null) current.timer = setTimeout(() => flush(cwd), DEBOUNCE_MS);
@@ -15072,7 +15126,7 @@ async function readOrQueueDocAudio(input2) {
15072
15126
  }
15073
15127
 
15074
15128
  // src/dashboard/artifact-export.ts
15075
- import { readFileSync as readFileSync19 } from "fs";
15129
+ import { readFileSync as readFileSync20 } from "fs";
15076
15130
 
15077
15131
  // src/dashboard/chat-agent.ts
15078
15132
  import { homedir as homedir9 } from "os";
@@ -15290,7 +15344,7 @@ async function withMcpConfig(profile, connectionIds, fn) {
15290
15344
  }
15291
15345
 
15292
15346
  // src/claude/agents.ts
15293
- import { existsSync as existsSync23, readdirSync as readdirSync4, readFileSync as readFileSync17 } from "fs";
15347
+ import { existsSync as existsSync24, readdirSync as readdirSync4, readFileSync as readFileSync18 } from "fs";
15294
15348
  import { homedir as homedir8 } from "os";
15295
15349
  import { basename as basename7, join as join20 } from "path";
15296
15350
  import yaml2 from "js-yaml";
@@ -15305,7 +15359,7 @@ function expandHome(p) {
15305
15359
  function parseFrontmatter(file) {
15306
15360
  let content;
15307
15361
  try {
15308
- content = readFileSync17(file, "utf-8");
15362
+ content = readFileSync18(file, "utf-8");
15309
15363
  } catch {
15310
15364
  return {};
15311
15365
  }
@@ -15321,7 +15375,7 @@ function parseFrontmatter(file) {
15321
15375
  }
15322
15376
  }
15323
15377
  function collectFrom(root, source) {
15324
- if (!existsSync23(root)) return [];
15378
+ if (!existsSync24(root)) return [];
15325
15379
  let entries2;
15326
15380
  try {
15327
15381
  entries2 = readdirSync4(root).filter((f) => f.endsWith(".md"));
@@ -15351,7 +15405,7 @@ function readAgentPersona(configDir2, cwd, name) {
15351
15405
  const agent = listAgents(configDir2, cwd).find((a) => a.name === name);
15352
15406
  if (!agent) return void 0;
15353
15407
  try {
15354
- const content = readFileSync17(agent.path, "utf-8");
15408
+ const content = readFileSync18(agent.path, "utf-8");
15355
15409
  return content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
15356
15410
  } catch {
15357
15411
  return void 0;
@@ -15360,7 +15414,7 @@ function readAgentPersona(configDir2, cwd, name) {
15360
15414
 
15361
15415
  // src/dashboard/memory-extraction-runner.ts
15362
15416
  import { spawn as spawn4 } from "child_process";
15363
- import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync18, rmSync as rmSync7 } from "fs";
15417
+ import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync19, rmSync as rmSync7 } from "fs";
15364
15418
  import { tmpdir as tmpdir4 } from "os";
15365
15419
  import { join as join21 } from "path";
15366
15420
  var CLAUDE_MODEL = "sonnet";
@@ -15452,7 +15506,7 @@ function run3(inv, cwd) {
15452
15506
  let raw = stdout;
15453
15507
  if (inv.outputFile) {
15454
15508
  try {
15455
- raw = readFileSync18(inv.outputFile, "utf-8");
15509
+ raw = readFileSync19(inv.outputFile, "utf-8");
15456
15510
  } catch {
15457
15511
  raw = "";
15458
15512
  }
@@ -15769,23 +15823,43 @@ function extractAttachmentKeys(body) {
15769
15823
  }
15770
15824
  return keys;
15771
15825
  }
15826
+ async function fetchObject(key) {
15827
+ if (process.env["CLOUDFLARE_R2_ACCESS_KEY_ID"]) {
15828
+ const response2 = await getR2Client().send(
15829
+ new GetObjectCommand5({ Bucket: getR2Bucket(), Key: key })
15830
+ );
15831
+ return {
15832
+ mimetype: response2.ContentType ?? "application/octet-stream",
15833
+ contentLength: response2.ContentLength,
15834
+ read: async () => response2.Body?.transformToByteArray()
15835
+ };
15836
+ }
15837
+ const url = await requestAttachmentUrl(key);
15838
+ if (!url) throw new Error("Data API refused the attachment");
15839
+ const response = await fetch(url);
15840
+ if (!response.ok) throw new Error(`presigned GET answered ${response.status}`);
15841
+ const length = response.headers.get("content-length");
15842
+ return {
15843
+ mimetype: response.headers.get("content-type") ?? "application/octet-stream",
15844
+ contentLength: length === null ? void 0 : Number(length),
15845
+ read: async () => new Uint8Array(await response.arrayBuffer())
15846
+ };
15847
+ }
15772
15848
  async function downloadOne(key) {
15773
15849
  const lastSegment = key.slice(key.lastIndexOf("/") + 1);
15774
15850
  const name = displayName(lastSegment);
15775
15851
  try {
15776
- const response = await getR2Client().send(
15777
- new GetObjectCommand5({ Bucket: getR2Bucket(), Key: key })
15778
- );
15779
- const mimetype = response.ContentType ?? "application/octet-stream";
15852
+ const response = await fetchObject(key);
15853
+ const mimetype = response.mimetype;
15780
15854
  const sizeLimit = mimetypeSizeLimit(mimetype);
15781
15855
  if (sizeLimit === null) {
15782
15856
  if (debug) console.log(`[attachment-downloader] unsupported mimetype: ${mimetype} (${key})`);
15783
15857
  return { kind: "skip", skipped: { name, mimetype, reason: "unsupported" } };
15784
15858
  }
15785
- if (response.ContentLength !== void 0 && response.ContentLength > sizeLimit) {
15859
+ if (response.contentLength !== void 0 && response.contentLength > sizeLimit) {
15786
15860
  return { kind: "skip", skipped: { name, mimetype, reason: "too_large" } };
15787
15861
  }
15788
- const bytes = await response.Body?.transformToByteArray();
15862
+ const bytes = await response.read();
15789
15863
  if (!bytes) {
15790
15864
  return { kind: "skip", skipped: { name, mimetype, reason: "download_failed" } };
15791
15865
  }
@@ -15817,11 +15891,11 @@ async function downloadChatAttachments(body) {
15817
15891
  }
15818
15892
 
15819
15893
  // src/dashboard/doc-mention-guard.ts
15820
- import { existsSync as existsSync24, statSync as statSync5 } from "fs";
15894
+ import { existsSync as existsSync25, statSync as statSync5 } from "fs";
15821
15895
  var DOC_MENTION_RE = /(?<![\w@/`])@((?:[\w.-]+\/)+[\w.-]+\.[A-Za-z0-9]{1,6})(?::(\d+))?(?![\w-])/g;
15822
15896
  function isRealDoc(root, rel) {
15823
15897
  const abs = resolveDocPath(root, rel);
15824
- if (!abs || !existsSync24(abs)) return false;
15898
+ if (!abs || !existsSync25(abs)) return false;
15825
15899
  try {
15826
15900
  return statSync5(abs).isFile();
15827
15901
  } catch {
@@ -17091,7 +17165,7 @@ async function exportDocAsArtifact(channelId, docPath) {
17091
17165
  );
17092
17166
  const abs = resolveDocPath(channelConfig.cwd, docPath);
17093
17167
  if (!abs) throw new Error(`doc not found: ${docPath}`);
17094
- const content = readFileSync19(abs, "utf8");
17168
+ const content = readFileSync20(abs, "utf8");
17095
17169
  const title = extractTitle(abs, "md");
17096
17170
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
17097
17171
  const prompt = `Publish the document at \`${docPath}\` (already read below) as a Claude Artifact, then record the result as a stub file under this channel's \`docs/artifacts/\` folder (create the folder if it doesn't exist).
@@ -17372,12 +17446,12 @@ function readGitBranch(dir) {
17372
17446
  try {
17373
17447
  let gitDir = join23(dir, ".git");
17374
17448
  if (statSync6(gitDir).isFile()) {
17375
- const pointer = readFileSync20(gitDir, "utf8").trim();
17449
+ const pointer = readFileSync21(gitDir, "utf8").trim();
17376
17450
  const match = pointer.match(/^gitdir:\s*(.+)$/);
17377
17451
  if (!match) return void 0;
17378
17452
  gitDir = resolve5(dir, match[1]);
17379
17453
  }
17380
- const head = readFileSync20(join23(gitDir, "HEAD"), "utf8").trim();
17454
+ const head = readFileSync21(join23(gitDir, "HEAD"), "utf8").trim();
17381
17455
  const branchMatch = head.match(/^ref:\s*refs\/heads\/(.+)$/);
17382
17456
  if (branchMatch) return branchMatch[1];
17383
17457
  return /^[0-9a-f]{7,40}$/i.test(head) ? head.slice(0, 7) : void 0;
@@ -17406,7 +17480,7 @@ function listDocLevel(root, relDir = "", sort = "newest", showHidden = false) {
17406
17480
  if (SKIP_DIRS.has(entry.name)) continue;
17407
17481
  const probe3 = probeDir(abs, showHidden);
17408
17482
  if (!probe3.hasDocs && probe3.hasOther) continue;
17409
- const isRepo = existsSync25(join23(abs, ".git"));
17483
+ const isRepo = existsSync26(join23(abs, ".git"));
17410
17484
  dirs.push({
17411
17485
  path,
17412
17486
  name: entry.name,
@@ -17612,7 +17686,7 @@ function moveDocEntry(root, from, to) {
17612
17686
  return { status: "bad-target" };
17613
17687
  }
17614
17688
  }
17615
- const absToDir = dirname7(absTo);
17689
+ const absToDir = dirname8(absTo);
17616
17690
  if (resolveDirPath(root, relative(root, absToDir)) === null) return { status: "bad-target" };
17617
17691
  try {
17618
17692
  if (!statSync6(absToDir).isDirectory()) return { status: "bad-target" };
@@ -17703,7 +17777,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
17703
17777
  }
17704
17778
  if (!stat.isFile()) return c.json({ error: "not found" }, 404);
17705
17779
  if (stat.size > MAX_RAW_BYTES) return c.json({ error: "file too large" }, 413);
17706
- return c.body(withDocLinkBridge(readFileSync20(abs, "utf8")), 200, {
17780
+ return c.body(withDocLinkBridge(readFileSync21(abs, "utf8")), 200, {
17707
17781
  "Content-Type": "text/html; charset=utf-8",
17708
17782
  "Content-Security-Policy": HTML_SHARE_CSP,
17709
17783
  "X-Content-Type-Options": "nosniff",
@@ -17888,7 +17962,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
17888
17962
  title: kind ? extractTitle(abs, kind) : basename8(abs),
17889
17963
  mtime: stat.mtime.toISOString(),
17890
17964
  tooLarge: tooLarge || void 0,
17891
- content: tooLarge ? "" : readFileSync20(abs, "utf8")
17965
+ content: tooLarge ? "" : readFileSync21(abs, "utf8")
17892
17966
  });
17893
17967
  });
17894
17968
  app.put("/:id/docs/content", async (c) => {
@@ -17931,7 +18005,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
17931
18005
  channel,
17932
18006
  rel,
17933
18007
  title: extractTitle(abs, "md"),
17934
- content: readFileSync20(abs, "utf8")
18008
+ content: readFileSync21(abs, "utf8")
17935
18009
  };
17936
18010
  };
17937
18011
  app.get("/:id/docs/translation/:lang", async (c) => {
@@ -17986,8 +18060,8 @@ function withDocLinkBridge(html) {
17986
18060
  }
17987
18061
 
17988
18062
  // src/dashboard/skills-routes-host.ts
17989
- import { existsSync as existsSync26, statSync as statSync7 } from "fs";
17990
- import { dirname as dirname8 } from "path";
18063
+ import { existsSync as existsSync27, statSync as statSync7 } from "fs";
18064
+ import { dirname as dirname9 } from "path";
17991
18065
  import { Hono as Hono32 } from "hono";
17992
18066
  function workspaceScope() {
17993
18067
  const claudeConfigDir = profileClaudeConfigDir();
@@ -17995,7 +18069,7 @@ function workspaceScope() {
17995
18069
  return {
17996
18070
  dir: workspaceSkillsDir(claudeConfigDir),
17997
18071
  // `~/.claude` itself, which exists wherever the CLI has ever run.
17998
- root: dirname8(workspaceSkillsDir(claudeConfigDir)),
18072
+ root: dirname9(workspaceSkillsDir(claudeConfigDir)),
17999
18073
  cwd,
18000
18074
  claudeConfigDir,
18001
18075
  // Only plugin skills. Project skills belong to a specific channel's cwd, so
@@ -18021,7 +18095,7 @@ function inheritedSkills(scope, own) {
18021
18095
  }
18022
18096
  function rootExists(dir) {
18023
18097
  try {
18024
- return existsSync26(dir) && statSync7(dir).isDirectory();
18098
+ return existsSync27(dir) && statSync7(dir).isDirectory();
18025
18099
  } catch {
18026
18100
  return false;
18027
18101
  }
@@ -18131,7 +18205,7 @@ async function resolveAgentIdentities() {
18131
18205
 
18132
18206
  // ../api/src/dashboard/providers.ts
18133
18207
  import { execFileSync as execFileSync4 } from "child_process";
18134
- import { existsSync as existsSync27, readFileSync as readFileSync21 } from "fs";
18208
+ import { existsSync as existsSync28, readFileSync as readFileSync22 } from "fs";
18135
18209
  import { homedir as homedir10 } from "os";
18136
18210
  import { join as join24, resolve as resolve6 } from "path";
18137
18211
  var PROVIDER_LABELS = {
@@ -18154,7 +18228,7 @@ function binaryOnPath(bin) {
18154
18228
  function claudeAuthAvailable() {
18155
18229
  if (profileEnv("CLAUDE_CODE_OAUTH_TOKEN")?.trim()) return true;
18156
18230
  const configDir2 = profileClaudeConfigDir() || resolve6(homedir10(), ".claude");
18157
- if (existsSync27(join24(configDir2, ".credentials.json"))) return true;
18231
+ if (existsSync28(join24(configDir2, ".credentials.json"))) return true;
18158
18232
  if (process.platform !== "darwin") return false;
18159
18233
  try {
18160
18234
  execFileSync4("security", ["find-generic-password", "-s", "Claude Code-credentials"], {
@@ -18200,7 +18274,7 @@ function claudeAccount() {
18200
18274
  function codexAccount() {
18201
18275
  const home2 = process.env["CODEX_HOME"]?.trim() || join24(homedir10(), ".codex");
18202
18276
  try {
18203
- const auth = JSON.parse(readFileSync21(join24(home2, "auth.json"), "utf8"));
18277
+ const auth = JSON.parse(readFileSync22(join24(home2, "auth.json"), "utf8"));
18204
18278
  const payload = auth.tokens?.id_token?.split(".")[1];
18205
18279
  if (!payload) return void 0;
18206
18280
  const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
@@ -18231,9 +18305,9 @@ function providerStatuses() {
18231
18305
  // src/dashboard/provider-login-host.ts
18232
18306
  import { spawn as spawn6 } from "child_process";
18233
18307
  import { randomUUID as randomUUID5 } from "crypto";
18234
- import { chmodSync, existsSync as existsSync28, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync12 } from "fs";
18308
+ import { chmodSync, existsSync as existsSync29, mkdirSync as mkdirSync12, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "fs";
18235
18309
  import { createRequire } from "module";
18236
- import { dirname as dirname9, join as join25 } from "path";
18310
+ import { dirname as dirname10, join as join25 } from "path";
18237
18311
  import { Hono as Hono33 } from "hono";
18238
18312
  var SESSION_TTL_MS = 10 * 6e4;
18239
18313
  var URL_WAIT_MS = 3e4;
@@ -18296,10 +18370,10 @@ function waitFor(s, parse, ms) {
18296
18370
  }
18297
18371
  function loadPty() {
18298
18372
  const require2 = createRequire(import.meta.url);
18299
- const root = dirname9(require2.resolve("node-pty/package.json"));
18373
+ const root = dirname10(require2.resolve("node-pty/package.json"));
18300
18374
  const helper = join25(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper");
18301
18375
  try {
18302
- if (existsSync28(helper)) chmodSync(helper, 493);
18376
+ if (existsSync29(helper)) chmodSync(helper, 493);
18303
18377
  } catch {
18304
18378
  }
18305
18379
  return require2("node-pty");
@@ -18339,8 +18413,8 @@ async function startClaude() {
18339
18413
  return { id, url };
18340
18414
  }
18341
18415
  function saveClaudeToken(configFile, token) {
18342
- mkdirSync12(dirname9(configFile), { recursive: true });
18343
- const current = existsSync28(configFile) ? readFileSync22(configFile, "utf-8") : "";
18416
+ mkdirSync12(dirname10(configFile), { recursive: true });
18417
+ const current = existsSync29(configFile) ? readFileSync23(configFile, "utf-8") : "";
18344
18418
  writeFileSync12(configFile, upsertEnvLine(current, "CLAUDE_CODE_OAUTH_TOKEN", token), { mode: 384 });
18345
18419
  const cached = cachedProfileConfig();
18346
18420
  if (cached) cached.env["CLAUDE_CODE_OAUTH_TOKEN"] = token;
@@ -18559,7 +18633,7 @@ function registerHostRoutes(api) {
18559
18633
  const limit = Math.min(Number(c.req.query("limit")) || 200, 2e3);
18560
18634
  const filename = errors ? "custie-error.log" : "custie.log";
18561
18635
  const file = join26(paths.LOG_DIR, filename);
18562
- const exists = existsSync29(file);
18636
+ const exists = existsSync30(file);
18563
18637
  return c.json({
18564
18638
  file,
18565
18639
  exists,
@@ -18569,8 +18643,8 @@ function registerHostRoutes(api) {
18569
18643
  });
18570
18644
  }
18571
18645
  function tailFile(file, limit) {
18572
- if (!existsSync29(file)) return [];
18573
- const lines = readFileSync23(file, "utf-8").split("\n");
18646
+ if (!existsSync30(file)) return [];
18647
+ const lines = readFileSync24(file, "utf-8").split("\n");
18574
18648
  if (lines.at(-1) === "") lines.pop();
18575
18649
  return lines.slice(-limit);
18576
18650
  }
@@ -18588,7 +18662,7 @@ function createHostApiRouter(agent = {}) {
18588
18662
 
18589
18663
  // src/dashboard/agent-bridge-host.ts
18590
18664
  import { sign as signRaw, timingSafeEqual as timingSafeEqual2 } from "crypto";
18591
- import { existsSync as existsSync30, readFileSync as readFileSync25 } from "fs";
18665
+ import { existsSync as existsSync31, readFileSync as readFileSync26 } from "fs";
18592
18666
  import dotenv2 from "dotenv";
18593
18667
  import { WebSocket as WebSocket2 } from "ws";
18594
18668
 
@@ -18622,7 +18696,7 @@ async function handleHostRequest(req) {
18622
18696
  }
18623
18697
 
18624
18698
  // src/dashboard/doc-share-publisher.ts
18625
- import { readFileSync as readFileSync24, statSync as statSync9 } from "fs";
18699
+ import { readFileSync as readFileSync25, statSync as statSync9 } from "fs";
18626
18700
  import { extname as extname2, posix } from "path";
18627
18701
  var MAX_ASSET_BYTES = 20 * 1024 * 1024;
18628
18702
  var MAX_CONTENT_BYTES2 = 2 * 1024 * 1024;
@@ -18737,7 +18811,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
18737
18811
  }
18738
18812
  if (!stat.isFile()) return { status: "not-found" };
18739
18813
  if (stat.size > MAX_CONTENT_BYTES2) return { status: "too-large" };
18740
- const source = readFileSync24(abs, "utf8");
18814
+ const source = readFileSync25(abs, "utf8");
18741
18815
  const title = extractTitle(abs, kind === "markdown" ? "md" : kind);
18742
18816
  if (kind === "excalidraw") {
18743
18817
  try {
@@ -18808,7 +18882,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
18808
18882
  let text = null;
18809
18883
  try {
18810
18884
  const size = statSync9(docAbs).size;
18811
- if (size <= budget) text = readFileSync24(docAbs, "utf8");
18885
+ if (size <= budget) text = readFileSync25(docAbs, "utf8");
18812
18886
  } catch {
18813
18887
  continue;
18814
18888
  }
@@ -18825,7 +18899,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
18825
18899
  let body;
18826
18900
  try {
18827
18901
  if (statSync9(resolved2.abs).size > MAX_ASSET_BYTES) continue;
18828
- body = readFileSync24(resolved2.abs);
18902
+ body = readFileSync25(resolved2.abs);
18829
18903
  } catch {
18830
18904
  continue;
18831
18905
  }
@@ -18880,7 +18954,7 @@ function dataSocketUrl() {
18880
18954
  return `ws://127.0.0.1:${process.env["FLYSPACE_DATA_API_PORT"] ?? DEFAULT_DATA_API_PORT}${HOST_SOCKET_PATH}`;
18881
18955
  }
18882
18956
  function listLocalSessions() {
18883
- if (!existsSync30(paths.DB_FILE)) return [];
18957
+ if (!existsSync31(paths.DB_FILE)) return [];
18884
18958
  const store = new SessionStore(paths.DB_FILE);
18885
18959
  try {
18886
18960
  return store.listSessions();
@@ -18922,7 +18996,7 @@ function buildHostConfigSnapshot() {
18922
18996
  const snapshot2 = {};
18923
18997
  for (const profile of listProfiles()) {
18924
18998
  snapshot2[profile] = runWithProfile(profile, () => ({
18925
- env: existsSync30(paths.CONFIG_FILE) ? dotenv2.parse(readFileSync25(paths.CONFIG_FILE, "utf-8")) : {},
18999
+ env: existsSync31(paths.CONFIG_FILE) ? dotenv2.parse(readFileSync26(paths.CONFIG_FILE, "utf-8")) : {},
18926
19000
  agentModel: loadDashboardSettings().agent
18927
19001
  }));
18928
19002
  }
@@ -19002,6 +19076,10 @@ function connectHostSocket(url, callbacks, onAuthenticated, onWorkspaces) {
19002
19076
  handleHostDbResult(msg.id, msg.res);
19003
19077
  return;
19004
19078
  }
19079
+ if (msg.type === "attachment-url-result") {
19080
+ handleAttachmentUrlResult(msg.id, msg.url);
19081
+ return;
19082
+ }
19005
19083
  if (msg.type === "call") {
19006
19084
  void handleCall(msg, callbacks, ws);
19007
19085
  }
@@ -19168,7 +19246,7 @@ function startPushWatcher() {
19168
19246
  }
19169
19247
 
19170
19248
  // src/dashboard/machine-server.ts
19171
- import { existsSync as existsSync31 } from "fs";
19249
+ import { existsSync as existsSync32 } from "fs";
19172
19250
  import { join as join28 } from "path";
19173
19251
  import { serveStatic } from "@hono/node-server/serve-static";
19174
19252
 
@@ -19271,14 +19349,14 @@ async function bootMachine() {
19271
19349
  function mountSpa(app) {
19272
19350
  app.route("/share", createPublicShareAssetRouter(WEB_DIST));
19273
19351
  app.route("/", createAppIconRouter());
19274
- if (existsSync31(WEB_DIST) || existsSync31(join28(paths.WEB_ROOT, "package.json"))) {
19352
+ if (existsSync32(WEB_DIST) || existsSync32(join28(paths.WEB_ROOT, "package.json"))) {
19275
19353
  app.use("/*", serveStatic({ root: WEB_DIST }));
19276
19354
  }
19277
19355
  app.get("/assets/*", (c) => c.text("stale asset \u2014 reload for the current build", 404));
19278
19356
  const spaFallback = serveStatic({ path: join28(WEB_DIST, "index.html") });
19279
19357
  app.get("*", async (c, next) => {
19280
- if (!existsSync31(join28(WEB_DIST, "index.html"))) {
19281
- const isCheckout = existsSync31(join28(paths.WEB_ROOT, "package.json"));
19358
+ if (!existsSync32(join28(WEB_DIST, "index.html"))) {
19359
+ const isCheckout = existsSync32(join28(paths.WEB_ROOT, "package.json"));
19282
19360
  return c.text(
19283
19361
  isCheckout ? `Workspace UI build missing (${WEB_DIST}).
19284
19362
  Run \`pnpm build:web\` from the repo root.
@@ -19412,8 +19490,8 @@ function openBrowser2(url) {
19412
19490
  }
19413
19491
  }
19414
19492
  function ensureWebBuild() {
19415
- if (existsSync32(join29(paths.WEB_ROOT, "dist", "index.html"))) return;
19416
- if (!existsSync32(join29(paths.WEB_ROOT, "package.json"))) return;
19493
+ if (existsSync33(join29(paths.WEB_ROOT, "dist", "index.html"))) return;
19494
+ if (!existsSync33(join29(paths.WEB_ROOT, "package.json"))) return;
19417
19495
  console.log(" packages/ui/dist missing, building the dashboard UI...");
19418
19496
  const result = spawnSync("pnpm", ["build"], {
19419
19497
  cwd: paths.WEB_ROOT,
@@ -19463,7 +19541,7 @@ async function runDashboard(args) {
19463
19541
  }
19464
19542
 
19465
19543
  // src/commands/workspace.ts
19466
- import { existsSync as existsSync33, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
19544
+ import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
19467
19545
  var USAGE7 = `
19468
19546
  Usage: flyspace workspace <subcommand> [options]
19469
19547
 
@@ -19549,7 +19627,7 @@ function provisionLocal(botName) {
19549
19627
  ensureDirs();
19550
19628
  console.log(` config dir ready (${paths.CONFIG_DIR})`);
19551
19629
  console.log(` data dir ready (${paths.DATA_DIR})`);
19552
- if (existsSync33(paths.CONFIG_FILE)) {
19630
+ if (existsSync34(paths.CONFIG_FILE)) {
19553
19631
  console.log(" config.env exists");
19554
19632
  } else {
19555
19633
  writeFileSync13(paths.CONFIG_FILE, `BOT_NAME=${botName}
@@ -19566,7 +19644,7 @@ async function provisionChannels(specs) {
19566
19644
  continue;
19567
19645
  }
19568
19646
  const cwd = expandPath(spec.cwd);
19569
- if (!existsSync33(cwd)) {
19647
+ if (!existsSync34(cwd)) {
19570
19648
  mkdirSync14(cwd, { recursive: true });
19571
19649
  console.log(` created cwd ${cwd}`);
19572
19650
  }
@@ -19785,7 +19863,7 @@ async function main() {
19785
19863
  break;
19786
19864
  case "-v":
19787
19865
  case "--version":
19788
- printRaw("0.1.30");
19866
+ printRaw("0.1.31");
19789
19867
  break;
19790
19868
  case "-h":
19791
19869
  case "--help":