flyspace-cli 0.1.29 → 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.
- package/dist/cli.js +370 -252
- package/package.json +7 -2
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
|
|
892
|
+
return void 0;
|
|
869
893
|
}
|
|
870
|
-
|
|
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.
|
|
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)];
|
|
@@ -2108,11 +2131,11 @@ async function serverReachable() {
|
|
|
2108
2131
|
return err.cause ? String(err.cause) : err.message;
|
|
2109
2132
|
}
|
|
2110
2133
|
}
|
|
2111
|
-
function checkService(name, installed, file,
|
|
2134
|
+
function checkService(name, installed, file, running2, fix) {
|
|
2112
2135
|
if (!installed) bad2(`${name} not installed`, `run: ${fix}`);
|
|
2113
2136
|
else if (serviceBinaryMissing(file))
|
|
2114
2137
|
bad2(`${name} points at a CLI that no longer exists`, `run: ${fix}`);
|
|
2115
|
-
else if (!
|
|
2138
|
+
else if (!running2) bad2(`${name} not running`, `logs: ${tilde3(paths.LOG_DIR)}`);
|
|
2116
2139
|
else ok2(`${name} running`);
|
|
2117
2140
|
}
|
|
2118
2141
|
async function runDoctor(args) {
|
|
@@ -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
|
|
2406
|
-
|
|
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
|
+
}
|
|
2407
2433
|
try {
|
|
2408
|
-
const
|
|
2409
|
-
const at =
|
|
2410
|
-
if (at > 0) prefix
|
|
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") };
|
|
2411
2437
|
} catch {
|
|
2412
2438
|
}
|
|
2413
|
-
|
|
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) {
|
|
2454
|
+
try {
|
|
2455
|
+
const pkg = join11(target.prefix, "lib", "node_modules", "flyspace-cli", "package.json");
|
|
2456
|
+
return JSON.parse(readFileSync10(pkg, "utf-8")).version;
|
|
2457
|
+
} catch {
|
|
2458
|
+
return void 0;
|
|
2459
|
+
}
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
2432
|
-
|
|
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
|
|
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
|
|
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 (!
|
|
2456
|
-
const raw =
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 ?
|
|
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 (
|
|
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 =
|
|
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 (!
|
|
5806
|
+
if (!existsSync13(paths.PROMPT_FILE)) {
|
|
5752
5807
|
const defaultPrompt = resolve4(paths.PACKAGE_ROOT, "system.default.md");
|
|
5753
|
-
if (
|
|
5754
|
-
const content =
|
|
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 (
|
|
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
|
|
6133
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
6079
6134
|
|
|
6080
6135
|
// src/store/claude-memory-import.ts
|
|
6081
|
-
import { existsSync as
|
|
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 =
|
|
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 (!
|
|
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,
|
|
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 === "-" ?
|
|
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,
|
|
@@ -6348,13 +6403,13 @@ async function runMcpCmd(args) {
|
|
|
6348
6403
|
}
|
|
6349
6404
|
|
|
6350
6405
|
// src/commands/dashboard.ts
|
|
6351
|
-
import { spawn as
|
|
6352
|
-
import { existsSync as
|
|
6406
|
+
import { spawn as spawn8, spawnSync } from "child_process";
|
|
6407
|
+
import { existsSync as existsSync33 } from "fs";
|
|
6353
6408
|
import { platform as platform7 } from "os";
|
|
6354
|
-
import { join as
|
|
6409
|
+
import { join as join29 } from "path";
|
|
6355
6410
|
|
|
6356
6411
|
// src/reconcile.ts
|
|
6357
|
-
import { existsSync as
|
|
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 =
|
|
6369
|
-
const hadDataDir =
|
|
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 (!
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
}
|
|
@@ -7398,8 +7453,8 @@ var inFlight = /* @__PURE__ */ new Map();
|
|
|
7398
7453
|
async function readOrCreateTranslation(input2) {
|
|
7399
7454
|
const contentHash = translationHash(input2.title, input2.content);
|
|
7400
7455
|
const key = `${input2.shareId}:${contentHash}:${input2.lang}`;
|
|
7401
|
-
const
|
|
7402
|
-
if (
|
|
7456
|
+
const running2 = inFlight.get(key);
|
|
7457
|
+
if (running2) return running2;
|
|
7403
7458
|
const job = (async () => {
|
|
7404
7459
|
const cached = await getDocShareTranslation(input2.shareId, input2.lang, contentHash);
|
|
7405
7460
|
if (cached) return cached;
|
|
@@ -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 =
|
|
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 (!
|
|
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(
|
|
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(
|
|
8381
|
+
return c.body(readFileSync16(path, "utf8").replaceAll('"/assets/', `"${BUILD_PREFIX}/`));
|
|
8327
8382
|
}
|
|
8328
|
-
return c.body(new Uint8Array(
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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-
|
|
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);
|
|
@@ -8805,6 +8860,9 @@ async function verifyTurnstileToken(token) {
|
|
|
8805
8860
|
return data.action === void 0 || data.action === "login";
|
|
8806
8861
|
}
|
|
8807
8862
|
|
|
8863
|
+
// ../api/src/dashboard/onboarding-routes.ts
|
|
8864
|
+
import { Hono as Hono8 } from "hono";
|
|
8865
|
+
|
|
8808
8866
|
// ../api/src/store/device-store.ts
|
|
8809
8867
|
import { createHash as createHash7, createPublicKey as createPublicKey2, randomBytes as randomBytes4, randomInt } from "crypto";
|
|
8810
8868
|
var DEVICE_CODE_TTL_MS = 10 * 6e4;
|
|
@@ -9031,27 +9089,84 @@ async function userEmail(userId) {
|
|
|
9031
9089
|
return data.user?.email ?? null;
|
|
9032
9090
|
}
|
|
9033
9091
|
|
|
9034
|
-
// ../api/src/dashboard/onboarding-routes.ts
|
|
9035
|
-
import { Hono as Hono7 } from "hono";
|
|
9036
|
-
|
|
9037
9092
|
// ../api/src/dashboard/agent-bridge-data.ts
|
|
9038
|
-
import { randomBytes as randomBytes5, randomUUID as
|
|
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
|
|
9044
|
-
import { dirname as
|
|
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 (!
|
|
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(
|
|
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
|
|
@@ -9207,11 +9322,16 @@ var CHANNEL_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
|
9207
9322
|
function isWorkspaceOwner(ctx) {
|
|
9208
9323
|
return ctx?.role === "admin" && !ctx.isSupport;
|
|
9209
9324
|
}
|
|
9325
|
+
async function isHostOwner(ctx) {
|
|
9326
|
+
if (ctx.role !== "admin") return false;
|
|
9327
|
+
if (ctx.isSupport) return true;
|
|
9328
|
+
return (await listWorkspaceDevices(ctx.workspaceId)).some((d) => d.userId === ctx.userId);
|
|
9329
|
+
}
|
|
9210
9330
|
function hostUnavailable(c, agent, method) {
|
|
9211
9331
|
return c.json({ error: "host offline", reason: agent.getHostCallFailure?.(method) ?? "offline" }, 503);
|
|
9212
9332
|
}
|
|
9213
9333
|
function createOnboardingRouter(agent) {
|
|
9214
|
-
const app = new
|
|
9334
|
+
const app = new Hono8();
|
|
9215
9335
|
app.get("/scan", async (c) => {
|
|
9216
9336
|
const ctx = await resolveAuthContext(c);
|
|
9217
9337
|
if (ctx?.role !== "admin") return c.json({ error: "admin access required" }, 403);
|
|
@@ -9349,7 +9469,7 @@ async function ensureGreetingThread(workspaceId, channelId, ownerName) {
|
|
|
9349
9469
|
|
|
9350
9470
|
// ../api/src/dashboard/auth-routes.ts
|
|
9351
9471
|
function createAuthRouter() {
|
|
9352
|
-
const auth = new
|
|
9472
|
+
const auth = new Hono9();
|
|
9353
9473
|
const emailedLink = (deliver) => async (c) => {
|
|
9354
9474
|
const body = await c.req.json().catch(() => ({}));
|
|
9355
9475
|
const email = body.email?.trim().toLowerCase();
|
|
@@ -9389,7 +9509,7 @@ function createAuthRouter() {
|
|
|
9389
9509
|
owner: isWorkspaceOwner(ctx),
|
|
9390
9510
|
// Only the Host's owner can run `flyspace upgrade` on it, so other
|
|
9391
9511
|
// admins never see the upgrade banner. Support staff can tell them to.
|
|
9392
|
-
hostOwner:
|
|
9512
|
+
hostOwner: await isHostOwner(ctx)
|
|
9393
9513
|
});
|
|
9394
9514
|
});
|
|
9395
9515
|
auth.patch("/handle", async (c) => {
|
|
@@ -9429,7 +9549,7 @@ var requireWorkspaceMembership = async (c, next) => {
|
|
|
9429
9549
|
};
|
|
9430
9550
|
|
|
9431
9551
|
// ../api/src/dashboard/billing-routes.ts
|
|
9432
|
-
import { Hono as
|
|
9552
|
+
import { Hono as Hono10 } from "hono";
|
|
9433
9553
|
function isPlan(value) {
|
|
9434
9554
|
return value === "local" || value === "cloud";
|
|
9435
9555
|
}
|
|
@@ -9445,7 +9565,7 @@ async function requireActivePlan(c, next) {
|
|
|
9445
9565
|
return next();
|
|
9446
9566
|
}
|
|
9447
9567
|
function createBillingRouter() {
|
|
9448
|
-
const billing = new
|
|
9568
|
+
const billing = new Hono10();
|
|
9449
9569
|
billing.get("/", async (c) => {
|
|
9450
9570
|
const auth = c.get("auth");
|
|
9451
9571
|
const { planStatus, stripeSubscriptionId } = await getWorkspaceBilling(auth.workspaceId);
|
|
@@ -9501,7 +9621,7 @@ function createBillingRouter() {
|
|
|
9501
9621
|
}
|
|
9502
9622
|
|
|
9503
9623
|
// ../api/src/dashboard/admin-routes.ts
|
|
9504
|
-
import { Hono as
|
|
9624
|
+
import { Hono as Hono11 } from "hono";
|
|
9505
9625
|
|
|
9506
9626
|
// ../api/src/dashboard/admin-middleware.ts
|
|
9507
9627
|
function platformAdminEmails() {
|
|
@@ -9527,7 +9647,7 @@ var requirePlatformOperator = async (c, next) => {
|
|
|
9527
9647
|
// ../api/src/dashboard/admin-routes.ts
|
|
9528
9648
|
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
9529
9649
|
function createAdminRouter() {
|
|
9530
|
-
const admin = new
|
|
9650
|
+
const admin = new Hono11();
|
|
9531
9651
|
admin.use("*", requirePlatformOperator);
|
|
9532
9652
|
admin.get("/workspaces", async (c) => {
|
|
9533
9653
|
const summaries = await listWorkspaceSummaries();
|
|
@@ -9640,11 +9760,11 @@ var BOT_LABEL = "Workspace Agent";
|
|
|
9640
9760
|
var BOT_DM_KEY = "claude";
|
|
9641
9761
|
|
|
9642
9762
|
// ../api/src/dashboard/data-api.ts
|
|
9643
|
-
import { existsSync as
|
|
9763
|
+
import { existsSync as existsSync20 } from "fs";
|
|
9644
9764
|
import sharp5 from "sharp";
|
|
9645
9765
|
|
|
9646
9766
|
// ../api/src/dashboard/automations-routes.ts
|
|
9647
|
-
import { Hono as
|
|
9767
|
+
import { Hono as Hono12 } from "hono";
|
|
9648
9768
|
var TYPES2 = ["schedule", "trigger", "mention-trigger"];
|
|
9649
9769
|
function isType(t) {
|
|
9650
9770
|
return TYPES2.includes(t);
|
|
@@ -9661,7 +9781,7 @@ function statusFor(message) {
|
|
|
9661
9781
|
return 400;
|
|
9662
9782
|
}
|
|
9663
9783
|
function createAutomationWriteRouter() {
|
|
9664
|
-
const app = new
|
|
9784
|
+
const app = new Hono12();
|
|
9665
9785
|
const manager = () => new AutomationManager();
|
|
9666
9786
|
app.get("/:type/:name", (c) => {
|
|
9667
9787
|
const item = manager().get(c.req.param("name"));
|
|
@@ -9717,9 +9837,9 @@ function createAutomationWriteRouter() {
|
|
|
9717
9837
|
}
|
|
9718
9838
|
|
|
9719
9839
|
// ../api/src/dashboard/categories-routes.ts
|
|
9720
|
-
import { Hono as
|
|
9840
|
+
import { Hono as Hono13 } from "hono";
|
|
9721
9841
|
function createCategoryWriteRouter() {
|
|
9722
|
-
const app = new
|
|
9842
|
+
const app = new Hono13();
|
|
9723
9843
|
app.get("/", async (c) => {
|
|
9724
9844
|
const { data, error: error2 } = await getSupabaseClient().from("categories").select("*").eq("profile", getProfile()).order("position", { ascending: true });
|
|
9725
9845
|
if (error2) return c.json({ error: error2.message }, 500);
|
|
@@ -9773,7 +9893,7 @@ function createCategoryWriteRouter() {
|
|
|
9773
9893
|
}
|
|
9774
9894
|
|
|
9775
9895
|
// ../api/src/dashboard/chat-routes.ts
|
|
9776
|
-
import { Hono as
|
|
9896
|
+
import { Hono as Hono14 } from "hono";
|
|
9777
9897
|
|
|
9778
9898
|
// ../api/src/dashboard/fork-summary.ts
|
|
9779
9899
|
var GROQ_API_URL4 = "https://api.groq.com/openai/v1/chat/completions";
|
|
@@ -10057,8 +10177,8 @@ async function translateThread(threadId, lang, messages) {
|
|
|
10057
10177
|
if (messages.length === 0) return [];
|
|
10058
10178
|
const shape = createHash8("sha256").update(messages.map((m) => translationHash2(m.author, m.body)).join("\n")).digest("hex");
|
|
10059
10179
|
const key = `${threadId}:${lang}:${shape}`;
|
|
10060
|
-
const
|
|
10061
|
-
if (
|
|
10180
|
+
const running2 = inFlight4.get(key);
|
|
10181
|
+
if (running2) return running2;
|
|
10062
10182
|
const job = (async () => {
|
|
10063
10183
|
const hashes = messages.map((m) => ({
|
|
10064
10184
|
replyId: m.id,
|
|
@@ -10260,7 +10380,7 @@ async function generateAutoTitle(threadId, channelId, body) {
|
|
|
10260
10380
|
await supabase.from("threads").update({ auto_title: autoTitle }).eq("id", threadId).is("title", null);
|
|
10261
10381
|
}
|
|
10262
10382
|
function createChatRouter(agent = {}) {
|
|
10263
|
-
const app = new
|
|
10383
|
+
const app = new Hono14();
|
|
10264
10384
|
const {
|
|
10265
10385
|
onChatMessage: onMessage,
|
|
10266
10386
|
isAgentThinking,
|
|
@@ -10774,7 +10894,7 @@ function createChatRouter(agent = {}) {
|
|
|
10774
10894
|
}
|
|
10775
10895
|
|
|
10776
10896
|
// ../api/src/dashboard/doc-shares-routes.ts
|
|
10777
|
-
import { Hono as
|
|
10897
|
+
import { Hono as Hono15 } from "hono";
|
|
10778
10898
|
|
|
10779
10899
|
// ../api/src/dashboard/doc-share-features.ts
|
|
10780
10900
|
import { DeleteObjectCommand as DeleteObjectCommand2 } from "@aws-sdk/client-s3";
|
|
@@ -10834,7 +10954,7 @@ async function updateShareFeatures(share, requested) {
|
|
|
10834
10954
|
|
|
10835
10955
|
// ../api/src/dashboard/doc-share-publish.ts
|
|
10836
10956
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
10837
|
-
import { CopyObjectCommand, PutObjectCommand as
|
|
10957
|
+
import { CopyObjectCommand, PutObjectCommand as PutObjectCommand3 } from "@aws-sdk/client-s3";
|
|
10838
10958
|
|
|
10839
10959
|
// ../api/src/dashboard/bridge-types.ts
|
|
10840
10960
|
var SHARE_ASSET_PLACEHOLDER = "/share/{{flyspace-share-id}}/assets/";
|
|
@@ -10930,7 +11050,7 @@ async function publishDocShare(input2, readBundle) {
|
|
|
10930
11050
|
);
|
|
10931
11051
|
} else {
|
|
10932
11052
|
await client2.send(
|
|
10933
|
-
new
|
|
11053
|
+
new PutObjectCommand3({
|
|
10934
11054
|
Bucket: bucket,
|
|
10935
11055
|
Key: key,
|
|
10936
11056
|
Body: Buffer.from(asset.data, "base64"),
|
|
@@ -11109,7 +11229,7 @@ function resolveChannel2(c) {
|
|
|
11109
11229
|
}
|
|
11110
11230
|
var LIST_LIMIT = 500;
|
|
11111
11231
|
function createDocShareListRouter() {
|
|
11112
|
-
const app = new
|
|
11232
|
+
const app = new Hono15();
|
|
11113
11233
|
app.get("/shares", async (c) => {
|
|
11114
11234
|
const viewer = c.get("auth");
|
|
11115
11235
|
const readable = await filterReadableDocsChannels(
|
|
@@ -11153,7 +11273,7 @@ function createDocShareListRouter() {
|
|
|
11153
11273
|
return app;
|
|
11154
11274
|
}
|
|
11155
11275
|
function createDocShareWriteRouter(agent = {}) {
|
|
11156
|
-
const app = new
|
|
11276
|
+
const app = new Hono15();
|
|
11157
11277
|
app.use("/:id/docs/share", requireDocsAccess);
|
|
11158
11278
|
app.use("/:id/docs/share/*", requireDocsAccess);
|
|
11159
11279
|
app.use("/:id/docs/shares", requireDocsAccess);
|
|
@@ -11326,7 +11446,7 @@ function createDocShareWriteRouter(agent = {}) {
|
|
|
11326
11446
|
}
|
|
11327
11447
|
|
|
11328
11448
|
// ../api/src/dashboard/search-routes.ts
|
|
11329
|
-
import { Hono as
|
|
11449
|
+
import { Hono as Hono16 } from "hono";
|
|
11330
11450
|
function matchScore(q, fields) {
|
|
11331
11451
|
const terms = q.toLowerCase().split(/\s+/).filter(Boolean);
|
|
11332
11452
|
if (terms.length === 0) return 0;
|
|
@@ -11474,7 +11594,7 @@ function searchAutomations(q) {
|
|
|
11474
11594
|
}
|
|
11475
11595
|
var RESULT_LIMIT = 30;
|
|
11476
11596
|
function createSearchRouter() {
|
|
11477
|
-
const app = new
|
|
11597
|
+
const app = new Hono16();
|
|
11478
11598
|
app.get("/", async (c) => {
|
|
11479
11599
|
const q = c.req.query("q")?.trim();
|
|
11480
11600
|
if (!q) return c.json({ results: [] });
|
|
@@ -11490,14 +11610,14 @@ function createSearchRouter() {
|
|
|
11490
11610
|
}
|
|
11491
11611
|
|
|
11492
11612
|
// ../api/src/dashboard/feature-flags-routes.ts
|
|
11493
|
-
import { Hono as
|
|
11613
|
+
import { Hono as Hono17 } from "hono";
|
|
11494
11614
|
async function isFeatureEnabled(c, flagId) {
|
|
11495
11615
|
const auth = c.get("auth");
|
|
11496
11616
|
const enabled = await listEnabledFeatureFlags(auth.workspaceId);
|
|
11497
11617
|
return resolveFeatureFlags(enabled, isDevHost(c))[flagId];
|
|
11498
11618
|
}
|
|
11499
11619
|
function createFeatureFlagsRouter() {
|
|
11500
|
-
const app = new
|
|
11620
|
+
const app = new Hono17();
|
|
11501
11621
|
app.get("/", async (c) => {
|
|
11502
11622
|
const auth = c.get("auth");
|
|
11503
11623
|
const devHost = isDevHost(c);
|
|
@@ -11522,67 +11642,6 @@ function createFeatureFlagsRouter() {
|
|
|
11522
11642
|
return app;
|
|
11523
11643
|
}
|
|
11524
11644
|
|
|
11525
|
-
// ../api/src/dashboard/uploads-routes.ts
|
|
11526
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
11527
|
-
import { Hono as Hono17 } from "hono";
|
|
11528
|
-
import { PutObjectCommand as PutObjectCommand3, GetObjectCommand as GetObjectCommand3 } from "@aws-sdk/client-s3";
|
|
11529
|
-
import { getSignedUrl as getSignedUrl3 } from "@aws-sdk/s3-request-presigner";
|
|
11530
|
-
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
11531
|
-
var PRESIGN_TTL_SECONDS2 = 300;
|
|
11532
|
-
var KEY_PREFIX = "chat";
|
|
11533
|
-
var READABLE_PREFIXES = [KEY_PREFIX, SCRATCH_PREFIX];
|
|
11534
|
-
function sanitizeFilename(name) {
|
|
11535
|
-
const cleaned = name.replace(/[/\\]/g, "_").replace(/\s+/g, "-").replace(/[^\w.-]/g, "").trim();
|
|
11536
|
-
return (cleaned || "file").slice(-100);
|
|
11537
|
-
}
|
|
11538
|
-
function encodeKeyForUrl(key) {
|
|
11539
|
-
return key.split("/").map(encodeURIComponent).join("/");
|
|
11540
|
-
}
|
|
11541
|
-
function createUploadsRouter() {
|
|
11542
|
-
const app = new Hono17();
|
|
11543
|
-
app.post("/uploads", async (c) => {
|
|
11544
|
-
const body = await c.req.parseBody().catch(() => null);
|
|
11545
|
-
const file = body?.["file"];
|
|
11546
|
-
if (!(file instanceof File)) return c.json({ error: "missing file" }, 400);
|
|
11547
|
-
if (file.size > MAX_FILE_BYTES) {
|
|
11548
|
-
return c.json({ error: `file must be ${MAX_FILE_BYTES / (1024 * 1024)}MB or smaller` }, 400);
|
|
11549
|
-
}
|
|
11550
|
-
const key = `${KEY_PREFIX}/${getProfile()}/${randomUUID3()}-${sanitizeFilename(file.name)}`;
|
|
11551
|
-
const buffer = Buffer.from(await file.arrayBuffer());
|
|
11552
|
-
await getR2Client().send(
|
|
11553
|
-
new PutObjectCommand3({
|
|
11554
|
-
Bucket: getR2Bucket(),
|
|
11555
|
-
Key: key,
|
|
11556
|
-
Body: buffer,
|
|
11557
|
-
ContentType: file.type || "application/octet-stream"
|
|
11558
|
-
})
|
|
11559
|
-
);
|
|
11560
|
-
return c.json(
|
|
11561
|
-
{
|
|
11562
|
-
url: `/api/attachments/${encodeKeyForUrl(key)}`,
|
|
11563
|
-
name: file.name,
|
|
11564
|
-
type: file.type,
|
|
11565
|
-
size: file.size
|
|
11566
|
-
},
|
|
11567
|
-
201
|
|
11568
|
-
);
|
|
11569
|
-
});
|
|
11570
|
-
app.get("/attachments/:key{.+}", async (c) => {
|
|
11571
|
-
const key = c.req.param("key");
|
|
11572
|
-
if (!READABLE_PREFIXES.some((prefix) => key.startsWith(`${prefix}/`))) {
|
|
11573
|
-
return c.json({ error: "not found" }, 404);
|
|
11574
|
-
}
|
|
11575
|
-
const url = await getSignedUrl3(
|
|
11576
|
-
getR2Client(),
|
|
11577
|
-
new GetObjectCommand3({ Bucket: getR2Bucket(), Key: key }),
|
|
11578
|
-
{ expiresIn: PRESIGN_TTL_SECONDS2 }
|
|
11579
|
-
);
|
|
11580
|
-
c.header("Cache-Control", `private, max-age=${PRESIGN_TTL_SECONDS2 - 60}`);
|
|
11581
|
-
return c.redirect(url, 302);
|
|
11582
|
-
});
|
|
11583
|
-
return app;
|
|
11584
|
-
}
|
|
11585
|
-
|
|
11586
11645
|
// ../api/src/dashboard/transcribe-routes.ts
|
|
11587
11646
|
import { Hono as Hono18 } from "hono";
|
|
11588
11647
|
var GROQ_TRANSCRIBE_URL = "https://api.groq.com/openai/v1/audio/transcriptions";
|
|
@@ -13240,7 +13299,7 @@ async function sessionUsageForThreads(workspace, threadTsList) {
|
|
|
13240
13299
|
}
|
|
13241
13300
|
|
|
13242
13301
|
// ../api/src/dashboard/usage-report.ts
|
|
13243
|
-
import { existsSync as
|
|
13302
|
+
import { existsSync as existsSync19 } from "fs";
|
|
13244
13303
|
function mergeByLabel(buckets) {
|
|
13245
13304
|
const merged = /* @__PURE__ */ new Map();
|
|
13246
13305
|
for (const bucket of buckets) {
|
|
@@ -13286,7 +13345,7 @@ function toReport(days, rows, labelChannel) {
|
|
|
13286
13345
|
};
|
|
13287
13346
|
}
|
|
13288
13347
|
function fromLocalStore(days, labelChannel) {
|
|
13289
|
-
const store =
|
|
13348
|
+
const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
|
|
13290
13349
|
if (!store) return { ...EMPTY, days };
|
|
13291
13350
|
const window = days === 0 ? void 0 : days;
|
|
13292
13351
|
try {
|
|
@@ -13328,7 +13387,7 @@ async function buildChannelUsageReport(channelId, days, labelChannel) {
|
|
|
13328
13387
|
const { totals, byDay } = await channelUsageForWorkspace(workspace, aliasIds, window);
|
|
13329
13388
|
return { days, totals: totals.runs > 0 ? totals : null, byDay };
|
|
13330
13389
|
} catch {
|
|
13331
|
-
const store =
|
|
13390
|
+
const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
|
|
13332
13391
|
if (!store) return { days, totals: null, byDay: [] };
|
|
13333
13392
|
try {
|
|
13334
13393
|
const targetLabel = labelChannel(channelId);
|
|
@@ -13350,7 +13409,7 @@ async function buildThreadUsage(threadTs) {
|
|
|
13350
13409
|
if (totals.runs === 0) return { threadTs, totals: null, byModel: [] };
|
|
13351
13410
|
return { threadTs, totals, byModel };
|
|
13352
13411
|
} catch {
|
|
13353
|
-
const store =
|
|
13412
|
+
const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
|
|
13354
13413
|
if (!store) return { threadTs, totals: null, byModel: [] };
|
|
13355
13414
|
try {
|
|
13356
13415
|
const totals = store.threadTotals(threadTs);
|
|
@@ -13701,6 +13760,7 @@ async function resolveThreadLinks(sessions2) {
|
|
|
13701
13760
|
}
|
|
13702
13761
|
|
|
13703
13762
|
// ../api/src/dashboard/data-api.ts
|
|
13763
|
+
var SELF_UPGRADE_MIN_VERSION = "0.1.30";
|
|
13704
13764
|
function registerDataRoutes(api, agent = {}) {
|
|
13705
13765
|
api.get("/profile", (c) => c.json({ profile: getProfile() }));
|
|
13706
13766
|
api.get("/host/status", async (c) => {
|
|
@@ -13714,7 +13774,20 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13714
13774
|
const cliVersion = agent.getHostCliVersion?.(getProfile()) ?? null;
|
|
13715
13775
|
const latest2 = cliVersion ? await latestCliVersion() : null;
|
|
13716
13776
|
const outdated = !!(cliVersion && latest2 && isOlderVersion(cliVersion, latest2));
|
|
13717
|
-
|
|
13777
|
+
const selfUpgrade = !!cliVersion && !isOlderVersion(cliVersion, SELF_UPGRADE_MIN_VERSION);
|
|
13778
|
+
return c.json({
|
|
13779
|
+
online: !paused,
|
|
13780
|
+
cliVersion,
|
|
13781
|
+
selfUpgrade,
|
|
13782
|
+
...outdated ? { latestCliVersion: latest2 } : {}
|
|
13783
|
+
});
|
|
13784
|
+
});
|
|
13785
|
+
api.post("/host/upgrade", async (c) => {
|
|
13786
|
+
const auth = c.get("auth");
|
|
13787
|
+
if (!await isHostOwner(auth)) return c.json({ error: "only the Host owner can upgrade it" }, 403);
|
|
13788
|
+
const res = await agent.upgradeHost?.();
|
|
13789
|
+
if (!res) return c.json({ error: "Host is offline" }, 503);
|
|
13790
|
+
return c.json(res, 202);
|
|
13718
13791
|
});
|
|
13719
13792
|
api.get("/channels", async (c) => {
|
|
13720
13793
|
const auth = c.get("auth");
|
|
@@ -13774,7 +13847,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13774
13847
|
runs.set(name, { lastRun: run4.lastRunAt.toISOString(), lastStatus: run4.lastStatus });
|
|
13775
13848
|
}
|
|
13776
13849
|
} catch {
|
|
13777
|
-
const runStore =
|
|
13850
|
+
const runStore = existsSync20(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
|
|
13778
13851
|
if (runStore) {
|
|
13779
13852
|
try {
|
|
13780
13853
|
for (const run4 of runStore.listLastRuns()) {
|
|
@@ -13836,7 +13909,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13836
13909
|
const runs = (await recentRunsForWorkspace(getProfile(), 200)).map(toResponse5);
|
|
13837
13910
|
return c.json({ runs });
|
|
13838
13911
|
} catch {
|
|
13839
|
-
const runStore =
|
|
13912
|
+
const runStore = existsSync20(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
|
|
13840
13913
|
if (!runStore) return c.json({ runs: [] });
|
|
13841
13914
|
try {
|
|
13842
13915
|
const runs = runStore.listRuns(200).map(toResponse5);
|
|
@@ -13902,9 +13975,9 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13902
13975
|
void broadcastHostStatusChange();
|
|
13903
13976
|
let cancelled = 0;
|
|
13904
13977
|
if (body.paused) {
|
|
13905
|
-
const
|
|
13906
|
-
if (
|
|
13907
|
-
const { data } = await getSupabaseClient().from("threads").select("id, channel_id").in("id",
|
|
13978
|
+
const running2 = await agent.listAgentRuns?.() ?? [];
|
|
13979
|
+
if (running2.length > 0) {
|
|
13980
|
+
const { data } = await getSupabaseClient().from("threads").select("id, channel_id").in("id", running2);
|
|
13908
13981
|
const profile = getProfile();
|
|
13909
13982
|
for (const t of data ?? []) {
|
|
13910
13983
|
if (findOwningProfile(t.channel_id) !== profile) continue;
|
|
@@ -13922,7 +13995,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13922
13995
|
const host = trustedHost(c);
|
|
13923
13996
|
const known = await listKnownWorkspaces();
|
|
13924
13997
|
const hostProfile = profileFromHost(host, known);
|
|
13925
|
-
const
|
|
13998
|
+
const running2 = new Set(await agent.listRunningProfiles?.() ?? []);
|
|
13926
13999
|
const auth = c.get("auth");
|
|
13927
14000
|
const [{ data: memberships }, workspaces] = await Promise.all([
|
|
13928
14001
|
getSupabaseClient().from("memberships").select("workspace_id").eq("user_id", auth.userId),
|
|
@@ -13939,7 +14012,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13939
14012
|
return {
|
|
13940
14013
|
name,
|
|
13941
14014
|
active: name === active,
|
|
13942
|
-
running:
|
|
14015
|
+
running: running2.has(name),
|
|
13943
14016
|
displayName: branding.displayName,
|
|
13944
14017
|
logo: branding.logo,
|
|
13945
14018
|
hostname: (hostProfile ? workspaceHostname(host, name) : null) ?? void 0
|
|
@@ -13958,7 +14031,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13958
14031
|
} catch {
|
|
13959
14032
|
latest2 = /* @__PURE__ */ new Map();
|
|
13960
14033
|
totals = /* @__PURE__ */ new Map();
|
|
13961
|
-
const usageStore =
|
|
14034
|
+
const usageStore = existsSync20(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
|
|
13962
14035
|
if (usageStore) {
|
|
13963
14036
|
try {
|
|
13964
14037
|
latest2 = usageStore.latestByThread(threadTsList);
|
|
@@ -14224,11 +14297,11 @@ function createDeviceAuthRouter() {
|
|
|
14224
14297
|
}
|
|
14225
14298
|
|
|
14226
14299
|
// ../api/src/dashboard/skills-routes.ts
|
|
14227
|
-
import { dirname as
|
|
14300
|
+
import { dirname as dirname7 } from "path";
|
|
14228
14301
|
|
|
14229
14302
|
// ../api/src/dashboard/skill-mirror.ts
|
|
14230
14303
|
import { createHash as createHash11 } from "crypto";
|
|
14231
|
-
import { existsSync as
|
|
14304
|
+
import { existsSync as existsSync21, statSync as statSync4 } from "fs";
|
|
14232
14305
|
var SYNC_INTERVAL_MS = 6e4;
|
|
14233
14306
|
var UPSERT_BATCH = 50;
|
|
14234
14307
|
function mirrorScopeOf(ref) {
|
|
@@ -14236,7 +14309,7 @@ function mirrorScopeOf(ref) {
|
|
|
14236
14309
|
}
|
|
14237
14310
|
function isDir(path) {
|
|
14238
14311
|
try {
|
|
14239
|
-
return
|
|
14312
|
+
return existsSync21(path) && statSync4(path).isDirectory();
|
|
14240
14313
|
} catch {
|
|
14241
14314
|
return false;
|
|
14242
14315
|
}
|
|
@@ -14313,16 +14386,16 @@ function installSkillSync(profiles, options = {}) {
|
|
|
14313
14386
|
if (!databaseReachable()) return () => {
|
|
14314
14387
|
};
|
|
14315
14388
|
const { intervalMs = SYNC_INTERVAL_MS, onError = (e) => console.error("[skill-sync]", e) } = options;
|
|
14316
|
-
let
|
|
14389
|
+
let running2 = false;
|
|
14317
14390
|
const tick = async () => {
|
|
14318
|
-
if (
|
|
14319
|
-
|
|
14391
|
+
if (running2) return;
|
|
14392
|
+
running2 = true;
|
|
14320
14393
|
try {
|
|
14321
14394
|
for (const profile of profiles) {
|
|
14322
14395
|
await runWithProfile(profile, () => pushSkillScopes()).catch(onError);
|
|
14323
14396
|
}
|
|
14324
14397
|
} finally {
|
|
14325
|
-
|
|
14398
|
+
running2 = false;
|
|
14326
14399
|
}
|
|
14327
14400
|
};
|
|
14328
14401
|
void tick();
|
|
@@ -14561,12 +14634,12 @@ function createGatedRouter(agent, register) {
|
|
|
14561
14634
|
}
|
|
14562
14635
|
|
|
14563
14636
|
// src/dashboard/host-api.ts
|
|
14564
|
-
import { existsSync as
|
|
14637
|
+
import { existsSync as existsSync30, readFileSync as readFileSync24, statSync as statSync8 } from "fs";
|
|
14565
14638
|
import { join as join26 } from "path";
|
|
14566
14639
|
import sharp6 from "sharp";
|
|
14567
14640
|
|
|
14568
14641
|
// src/dashboard/channels-routes.ts
|
|
14569
|
-
import { existsSync as
|
|
14642
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync10 } from "fs";
|
|
14570
14643
|
import { Hono as Hono30 } from "hono";
|
|
14571
14644
|
function coerceAccess(raw) {
|
|
14572
14645
|
if (raw === "open" || raw === "*" || raw === "all") return raw;
|
|
@@ -14605,7 +14678,7 @@ function coerceMemberEmails(raw) {
|
|
|
14605
14678
|
function ensureCwdDir(cwd) {
|
|
14606
14679
|
const expanded = expandPath(cwd);
|
|
14607
14680
|
if (isLogicalCwd(cwd) || isUnderLogicalRoot(expanded)) return;
|
|
14608
|
-
if (!
|
|
14681
|
+
if (!existsSync22(expanded)) mkdirSync10(expanded, { recursive: true });
|
|
14609
14682
|
}
|
|
14610
14683
|
function toResponse3(channel) {
|
|
14611
14684
|
return {
|
|
@@ -14755,13 +14828,13 @@ function createChannelWriteRouter() {
|
|
|
14755
14828
|
import {
|
|
14756
14829
|
closeSync as closeSync2,
|
|
14757
14830
|
createReadStream,
|
|
14758
|
-
existsSync as
|
|
14831
|
+
existsSync as existsSync26,
|
|
14759
14832
|
lstatSync as lstatSync2,
|
|
14760
14833
|
mkdirSync as mkdirSync11,
|
|
14761
14834
|
openSync as openSync2,
|
|
14762
14835
|
readSync as readSync2,
|
|
14763
14836
|
readdirSync as readdirSync5,
|
|
14764
|
-
readFileSync as
|
|
14837
|
+
readFileSync as readFileSync21,
|
|
14765
14838
|
realpathSync as realpathSync4,
|
|
14766
14839
|
renameSync,
|
|
14767
14840
|
rmSync as rmSync8,
|
|
@@ -14769,12 +14842,12 @@ import {
|
|
|
14769
14842
|
writeFileSync as writeFileSync11
|
|
14770
14843
|
} from "fs";
|
|
14771
14844
|
import { Readable } from "stream";
|
|
14772
|
-
import { basename as basename8, dirname as
|
|
14845
|
+
import { basename as basename8, dirname as dirname8, extname, join as join23, relative, resolve as resolve5, sep } from "path";
|
|
14773
14846
|
import { Hono as Hono31 } from "hono";
|
|
14774
14847
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
14775
14848
|
|
|
14776
14849
|
// src/dashboard/docs-watcher.ts
|
|
14777
|
-
import { existsSync as
|
|
14850
|
+
import { existsSync as existsSync23, watch } from "fs";
|
|
14778
14851
|
import { basename as basename6, join as join18 } from "path";
|
|
14779
14852
|
var DEBOUNCE_MS = 200;
|
|
14780
14853
|
var MAX_PENDING = 200;
|
|
@@ -14816,7 +14889,7 @@ function watchDocs(cwd, listener) {
|
|
|
14816
14889
|
const current = entries.get(cwd);
|
|
14817
14890
|
if (!current || filename === null) return;
|
|
14818
14891
|
const rel = filename.split("\\").join("/");
|
|
14819
|
-
if (rel === root && !
|
|
14892
|
+
if (rel === root && !existsSync23(join18(cwd, rel))) return;
|
|
14820
14893
|
if (isIgnoredDocPath(rel)) return;
|
|
14821
14894
|
if (current.pending.size < MAX_PENDING) current.pending.add(rel);
|
|
14822
14895
|
if (current.timer === null) current.timer = setTimeout(() => flush(cwd), DEBOUNCE_MS);
|
|
@@ -14874,8 +14947,8 @@ var inFlight5 = /* @__PURE__ */ new Map();
|
|
|
14874
14947
|
async function readOrCreateDocTranslation(input2) {
|
|
14875
14948
|
const contentHash = translationHash(input2.title, input2.content);
|
|
14876
14949
|
const key = `${input2.channelId}:${input2.sourcePath}:${contentHash}:${input2.lang}`;
|
|
14877
|
-
const
|
|
14878
|
-
if (
|
|
14950
|
+
const running2 = inFlight5.get(key);
|
|
14951
|
+
if (running2) return running2;
|
|
14879
14952
|
const job = (async () => {
|
|
14880
14953
|
const cached = await getDocTranslation(input2.channelId, input2.sourcePath, input2.lang, contentHash);
|
|
14881
14954
|
if (cached) return cached;
|
|
@@ -15053,7 +15126,7 @@ async function readOrQueueDocAudio(input2) {
|
|
|
15053
15126
|
}
|
|
15054
15127
|
|
|
15055
15128
|
// src/dashboard/artifact-export.ts
|
|
15056
|
-
import { readFileSync as
|
|
15129
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
15057
15130
|
|
|
15058
15131
|
// src/dashboard/chat-agent.ts
|
|
15059
15132
|
import { homedir as homedir9 } from "os";
|
|
@@ -15271,7 +15344,7 @@ async function withMcpConfig(profile, connectionIds, fn) {
|
|
|
15271
15344
|
}
|
|
15272
15345
|
|
|
15273
15346
|
// src/claude/agents.ts
|
|
15274
|
-
import { existsSync as
|
|
15347
|
+
import { existsSync as existsSync24, readdirSync as readdirSync4, readFileSync as readFileSync18 } from "fs";
|
|
15275
15348
|
import { homedir as homedir8 } from "os";
|
|
15276
15349
|
import { basename as basename7, join as join20 } from "path";
|
|
15277
15350
|
import yaml2 from "js-yaml";
|
|
@@ -15286,7 +15359,7 @@ function expandHome(p) {
|
|
|
15286
15359
|
function parseFrontmatter(file) {
|
|
15287
15360
|
let content;
|
|
15288
15361
|
try {
|
|
15289
|
-
content =
|
|
15362
|
+
content = readFileSync18(file, "utf-8");
|
|
15290
15363
|
} catch {
|
|
15291
15364
|
return {};
|
|
15292
15365
|
}
|
|
@@ -15302,7 +15375,7 @@ function parseFrontmatter(file) {
|
|
|
15302
15375
|
}
|
|
15303
15376
|
}
|
|
15304
15377
|
function collectFrom(root, source) {
|
|
15305
|
-
if (!
|
|
15378
|
+
if (!existsSync24(root)) return [];
|
|
15306
15379
|
let entries2;
|
|
15307
15380
|
try {
|
|
15308
15381
|
entries2 = readdirSync4(root).filter((f) => f.endsWith(".md"));
|
|
@@ -15332,7 +15405,7 @@ function readAgentPersona(configDir2, cwd, name) {
|
|
|
15332
15405
|
const agent = listAgents(configDir2, cwd).find((a) => a.name === name);
|
|
15333
15406
|
if (!agent) return void 0;
|
|
15334
15407
|
try {
|
|
15335
|
-
const content =
|
|
15408
|
+
const content = readFileSync18(agent.path, "utf-8");
|
|
15336
15409
|
return content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
15337
15410
|
} catch {
|
|
15338
15411
|
return void 0;
|
|
@@ -15341,7 +15414,7 @@ function readAgentPersona(configDir2, cwd, name) {
|
|
|
15341
15414
|
|
|
15342
15415
|
// src/dashboard/memory-extraction-runner.ts
|
|
15343
15416
|
import { spawn as spawn4 } from "child_process";
|
|
15344
|
-
import { mkdtempSync as mkdtempSync2, readFileSync as
|
|
15417
|
+
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync19, rmSync as rmSync7 } from "fs";
|
|
15345
15418
|
import { tmpdir as tmpdir4 } from "os";
|
|
15346
15419
|
import { join as join21 } from "path";
|
|
15347
15420
|
var CLAUDE_MODEL = "sonnet";
|
|
@@ -15433,7 +15506,7 @@ function run3(inv, cwd) {
|
|
|
15433
15506
|
let raw = stdout;
|
|
15434
15507
|
if (inv.outputFile) {
|
|
15435
15508
|
try {
|
|
15436
|
-
raw =
|
|
15509
|
+
raw = readFileSync19(inv.outputFile, "utf-8");
|
|
15437
15510
|
} catch {
|
|
15438
15511
|
raw = "";
|
|
15439
15512
|
}
|
|
@@ -15750,23 +15823,43 @@ function extractAttachmentKeys(body) {
|
|
|
15750
15823
|
}
|
|
15751
15824
|
return keys;
|
|
15752
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
|
+
}
|
|
15753
15848
|
async function downloadOne(key) {
|
|
15754
15849
|
const lastSegment = key.slice(key.lastIndexOf("/") + 1);
|
|
15755
15850
|
const name = displayName(lastSegment);
|
|
15756
15851
|
try {
|
|
15757
|
-
const response = await
|
|
15758
|
-
|
|
15759
|
-
);
|
|
15760
|
-
const mimetype = response.ContentType ?? "application/octet-stream";
|
|
15852
|
+
const response = await fetchObject(key);
|
|
15853
|
+
const mimetype = response.mimetype;
|
|
15761
15854
|
const sizeLimit = mimetypeSizeLimit(mimetype);
|
|
15762
15855
|
if (sizeLimit === null) {
|
|
15763
15856
|
if (debug) console.log(`[attachment-downloader] unsupported mimetype: ${mimetype} (${key})`);
|
|
15764
15857
|
return { kind: "skip", skipped: { name, mimetype, reason: "unsupported" } };
|
|
15765
15858
|
}
|
|
15766
|
-
if (response.
|
|
15859
|
+
if (response.contentLength !== void 0 && response.contentLength > sizeLimit) {
|
|
15767
15860
|
return { kind: "skip", skipped: { name, mimetype, reason: "too_large" } };
|
|
15768
15861
|
}
|
|
15769
|
-
const bytes = await response.
|
|
15862
|
+
const bytes = await response.read();
|
|
15770
15863
|
if (!bytes) {
|
|
15771
15864
|
return { kind: "skip", skipped: { name, mimetype, reason: "download_failed" } };
|
|
15772
15865
|
}
|
|
@@ -15798,11 +15891,11 @@ async function downloadChatAttachments(body) {
|
|
|
15798
15891
|
}
|
|
15799
15892
|
|
|
15800
15893
|
// src/dashboard/doc-mention-guard.ts
|
|
15801
|
-
import { existsSync as
|
|
15894
|
+
import { existsSync as existsSync25, statSync as statSync5 } from "fs";
|
|
15802
15895
|
var DOC_MENTION_RE = /(?<![\w@/`])@((?:[\w.-]+\/)+[\w.-]+\.[A-Za-z0-9]{1,6})(?::(\d+))?(?![\w-])/g;
|
|
15803
15896
|
function isRealDoc(root, rel) {
|
|
15804
15897
|
const abs = resolveDocPath(root, rel);
|
|
15805
|
-
if (!abs || !
|
|
15898
|
+
if (!abs || !existsSync25(abs)) return false;
|
|
15806
15899
|
try {
|
|
15807
15900
|
return statSync5(abs).isFile();
|
|
15808
15901
|
} catch {
|
|
@@ -17072,7 +17165,7 @@ async function exportDocAsArtifact(channelId, docPath) {
|
|
|
17072
17165
|
);
|
|
17073
17166
|
const abs = resolveDocPath(channelConfig.cwd, docPath);
|
|
17074
17167
|
if (!abs) throw new Error(`doc not found: ${docPath}`);
|
|
17075
|
-
const content =
|
|
17168
|
+
const content = readFileSync20(abs, "utf8");
|
|
17076
17169
|
const title = extractTitle(abs, "md");
|
|
17077
17170
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
17078
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).
|
|
@@ -17353,12 +17446,12 @@ function readGitBranch(dir) {
|
|
|
17353
17446
|
try {
|
|
17354
17447
|
let gitDir = join23(dir, ".git");
|
|
17355
17448
|
if (statSync6(gitDir).isFile()) {
|
|
17356
|
-
const pointer =
|
|
17449
|
+
const pointer = readFileSync21(gitDir, "utf8").trim();
|
|
17357
17450
|
const match = pointer.match(/^gitdir:\s*(.+)$/);
|
|
17358
17451
|
if (!match) return void 0;
|
|
17359
17452
|
gitDir = resolve5(dir, match[1]);
|
|
17360
17453
|
}
|
|
17361
|
-
const head =
|
|
17454
|
+
const head = readFileSync21(join23(gitDir, "HEAD"), "utf8").trim();
|
|
17362
17455
|
const branchMatch = head.match(/^ref:\s*refs\/heads\/(.+)$/);
|
|
17363
17456
|
if (branchMatch) return branchMatch[1];
|
|
17364
17457
|
return /^[0-9a-f]{7,40}$/i.test(head) ? head.slice(0, 7) : void 0;
|
|
@@ -17387,7 +17480,7 @@ function listDocLevel(root, relDir = "", sort = "newest", showHidden = false) {
|
|
|
17387
17480
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
17388
17481
|
const probe3 = probeDir(abs, showHidden);
|
|
17389
17482
|
if (!probe3.hasDocs && probe3.hasOther) continue;
|
|
17390
|
-
const isRepo =
|
|
17483
|
+
const isRepo = existsSync26(join23(abs, ".git"));
|
|
17391
17484
|
dirs.push({
|
|
17392
17485
|
path,
|
|
17393
17486
|
name: entry.name,
|
|
@@ -17593,7 +17686,7 @@ function moveDocEntry(root, from, to) {
|
|
|
17593
17686
|
return { status: "bad-target" };
|
|
17594
17687
|
}
|
|
17595
17688
|
}
|
|
17596
|
-
const absToDir =
|
|
17689
|
+
const absToDir = dirname8(absTo);
|
|
17597
17690
|
if (resolveDirPath(root, relative(root, absToDir)) === null) return { status: "bad-target" };
|
|
17598
17691
|
try {
|
|
17599
17692
|
if (!statSync6(absToDir).isDirectory()) return { status: "bad-target" };
|
|
@@ -17684,7 +17777,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
|
|
|
17684
17777
|
}
|
|
17685
17778
|
if (!stat.isFile()) return c.json({ error: "not found" }, 404);
|
|
17686
17779
|
if (stat.size > MAX_RAW_BYTES) return c.json({ error: "file too large" }, 413);
|
|
17687
|
-
return c.body(withDocLinkBridge(
|
|
17780
|
+
return c.body(withDocLinkBridge(readFileSync21(abs, "utf8")), 200, {
|
|
17688
17781
|
"Content-Type": "text/html; charset=utf-8",
|
|
17689
17782
|
"Content-Security-Policy": HTML_SHARE_CSP,
|
|
17690
17783
|
"X-Content-Type-Options": "nosniff",
|
|
@@ -17869,7 +17962,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
|
|
|
17869
17962
|
title: kind ? extractTitle(abs, kind) : basename8(abs),
|
|
17870
17963
|
mtime: stat.mtime.toISOString(),
|
|
17871
17964
|
tooLarge: tooLarge || void 0,
|
|
17872
|
-
content: tooLarge ? "" :
|
|
17965
|
+
content: tooLarge ? "" : readFileSync21(abs, "utf8")
|
|
17873
17966
|
});
|
|
17874
17967
|
});
|
|
17875
17968
|
app.put("/:id/docs/content", async (c) => {
|
|
@@ -17912,7 +18005,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
|
|
|
17912
18005
|
channel,
|
|
17913
18006
|
rel,
|
|
17914
18007
|
title: extractTitle(abs, "md"),
|
|
17915
|
-
content:
|
|
18008
|
+
content: readFileSync21(abs, "utf8")
|
|
17916
18009
|
};
|
|
17917
18010
|
};
|
|
17918
18011
|
app.get("/:id/docs/translation/:lang", async (c) => {
|
|
@@ -17967,8 +18060,8 @@ function withDocLinkBridge(html) {
|
|
|
17967
18060
|
}
|
|
17968
18061
|
|
|
17969
18062
|
// src/dashboard/skills-routes-host.ts
|
|
17970
|
-
import { existsSync as
|
|
17971
|
-
import { dirname as
|
|
18063
|
+
import { existsSync as existsSync27, statSync as statSync7 } from "fs";
|
|
18064
|
+
import { dirname as dirname9 } from "path";
|
|
17972
18065
|
import { Hono as Hono32 } from "hono";
|
|
17973
18066
|
function workspaceScope() {
|
|
17974
18067
|
const claudeConfigDir = profileClaudeConfigDir();
|
|
@@ -17976,7 +18069,7 @@ function workspaceScope() {
|
|
|
17976
18069
|
return {
|
|
17977
18070
|
dir: workspaceSkillsDir(claudeConfigDir),
|
|
17978
18071
|
// `~/.claude` itself, which exists wherever the CLI has ever run.
|
|
17979
|
-
root:
|
|
18072
|
+
root: dirname9(workspaceSkillsDir(claudeConfigDir)),
|
|
17980
18073
|
cwd,
|
|
17981
18074
|
claudeConfigDir,
|
|
17982
18075
|
// Only plugin skills. Project skills belong to a specific channel's cwd, so
|
|
@@ -18002,7 +18095,7 @@ function inheritedSkills(scope, own) {
|
|
|
18002
18095
|
}
|
|
18003
18096
|
function rootExists(dir) {
|
|
18004
18097
|
try {
|
|
18005
|
-
return
|
|
18098
|
+
return existsSync27(dir) && statSync7(dir).isDirectory();
|
|
18006
18099
|
} catch {
|
|
18007
18100
|
return false;
|
|
18008
18101
|
}
|
|
@@ -18112,7 +18205,7 @@ async function resolveAgentIdentities() {
|
|
|
18112
18205
|
|
|
18113
18206
|
// ../api/src/dashboard/providers.ts
|
|
18114
18207
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
18115
|
-
import { existsSync as
|
|
18208
|
+
import { existsSync as existsSync28, readFileSync as readFileSync22 } from "fs";
|
|
18116
18209
|
import { homedir as homedir10 } from "os";
|
|
18117
18210
|
import { join as join24, resolve as resolve6 } from "path";
|
|
18118
18211
|
var PROVIDER_LABELS = {
|
|
@@ -18135,7 +18228,7 @@ function binaryOnPath(bin) {
|
|
|
18135
18228
|
function claudeAuthAvailable() {
|
|
18136
18229
|
if (profileEnv("CLAUDE_CODE_OAUTH_TOKEN")?.trim()) return true;
|
|
18137
18230
|
const configDir2 = profileClaudeConfigDir() || resolve6(homedir10(), ".claude");
|
|
18138
|
-
if (
|
|
18231
|
+
if (existsSync28(join24(configDir2, ".credentials.json"))) return true;
|
|
18139
18232
|
if (process.platform !== "darwin") return false;
|
|
18140
18233
|
try {
|
|
18141
18234
|
execFileSync4("security", ["find-generic-password", "-s", "Claude Code-credentials"], {
|
|
@@ -18181,7 +18274,7 @@ function claudeAccount() {
|
|
|
18181
18274
|
function codexAccount() {
|
|
18182
18275
|
const home2 = process.env["CODEX_HOME"]?.trim() || join24(homedir10(), ".codex");
|
|
18183
18276
|
try {
|
|
18184
|
-
const auth = JSON.parse(
|
|
18277
|
+
const auth = JSON.parse(readFileSync22(join24(home2, "auth.json"), "utf8"));
|
|
18185
18278
|
const payload = auth.tokens?.id_token?.split(".")[1];
|
|
18186
18279
|
if (!payload) return void 0;
|
|
18187
18280
|
const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
@@ -18212,9 +18305,9 @@ function providerStatuses() {
|
|
|
18212
18305
|
// src/dashboard/provider-login-host.ts
|
|
18213
18306
|
import { spawn as spawn6 } from "child_process";
|
|
18214
18307
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
18215
|
-
import { chmodSync, existsSync as
|
|
18308
|
+
import { chmodSync, existsSync as existsSync29, mkdirSync as mkdirSync12, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "fs";
|
|
18216
18309
|
import { createRequire } from "module";
|
|
18217
|
-
import { dirname as
|
|
18310
|
+
import { dirname as dirname10, join as join25 } from "path";
|
|
18218
18311
|
import { Hono as Hono33 } from "hono";
|
|
18219
18312
|
var SESSION_TTL_MS = 10 * 6e4;
|
|
18220
18313
|
var URL_WAIT_MS = 3e4;
|
|
@@ -18277,10 +18370,10 @@ function waitFor(s, parse, ms) {
|
|
|
18277
18370
|
}
|
|
18278
18371
|
function loadPty() {
|
|
18279
18372
|
const require2 = createRequire(import.meta.url);
|
|
18280
|
-
const root =
|
|
18373
|
+
const root = dirname10(require2.resolve("node-pty/package.json"));
|
|
18281
18374
|
const helper = join25(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper");
|
|
18282
18375
|
try {
|
|
18283
|
-
if (
|
|
18376
|
+
if (existsSync29(helper)) chmodSync(helper, 493);
|
|
18284
18377
|
} catch {
|
|
18285
18378
|
}
|
|
18286
18379
|
return require2("node-pty");
|
|
@@ -18320,8 +18413,8 @@ async function startClaude() {
|
|
|
18320
18413
|
return { id, url };
|
|
18321
18414
|
}
|
|
18322
18415
|
function saveClaudeToken(configFile, token) {
|
|
18323
|
-
mkdirSync12(
|
|
18324
|
-
const current =
|
|
18416
|
+
mkdirSync12(dirname10(configFile), { recursive: true });
|
|
18417
|
+
const current = existsSync29(configFile) ? readFileSync23(configFile, "utf-8") : "";
|
|
18325
18418
|
writeFileSync12(configFile, upsertEnvLine(current, "CLAUDE_CODE_OAUTH_TOKEN", token), { mode: 384 });
|
|
18326
18419
|
const cached = cachedProfileConfig();
|
|
18327
18420
|
if (cached) cached.env["CLAUDE_CODE_OAUTH_TOKEN"] = token;
|
|
@@ -18540,7 +18633,7 @@ function registerHostRoutes(api) {
|
|
|
18540
18633
|
const limit = Math.min(Number(c.req.query("limit")) || 200, 2e3);
|
|
18541
18634
|
const filename = errors ? "custie-error.log" : "custie.log";
|
|
18542
18635
|
const file = join26(paths.LOG_DIR, filename);
|
|
18543
|
-
const exists =
|
|
18636
|
+
const exists = existsSync30(file);
|
|
18544
18637
|
return c.json({
|
|
18545
18638
|
file,
|
|
18546
18639
|
exists,
|
|
@@ -18550,8 +18643,8 @@ function registerHostRoutes(api) {
|
|
|
18550
18643
|
});
|
|
18551
18644
|
}
|
|
18552
18645
|
function tailFile(file, limit) {
|
|
18553
|
-
if (!
|
|
18554
|
-
const lines =
|
|
18646
|
+
if (!existsSync30(file)) return [];
|
|
18647
|
+
const lines = readFileSync24(file, "utf-8").split("\n");
|
|
18555
18648
|
if (lines.at(-1) === "") lines.pop();
|
|
18556
18649
|
return lines.slice(-limit);
|
|
18557
18650
|
}
|
|
@@ -18569,7 +18662,7 @@ function createHostApiRouter(agent = {}) {
|
|
|
18569
18662
|
|
|
18570
18663
|
// src/dashboard/agent-bridge-host.ts
|
|
18571
18664
|
import { sign as signRaw, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
18572
|
-
import { existsSync as
|
|
18665
|
+
import { existsSync as existsSync31, readFileSync as readFileSync26 } from "fs";
|
|
18573
18666
|
import dotenv2 from "dotenv";
|
|
18574
18667
|
import { WebSocket as WebSocket2 } from "ws";
|
|
18575
18668
|
|
|
@@ -18603,7 +18696,7 @@ async function handleHostRequest(req) {
|
|
|
18603
18696
|
}
|
|
18604
18697
|
|
|
18605
18698
|
// src/dashboard/doc-share-publisher.ts
|
|
18606
|
-
import { readFileSync as
|
|
18699
|
+
import { readFileSync as readFileSync25, statSync as statSync9 } from "fs";
|
|
18607
18700
|
import { extname as extname2, posix } from "path";
|
|
18608
18701
|
var MAX_ASSET_BYTES = 20 * 1024 * 1024;
|
|
18609
18702
|
var MAX_CONTENT_BYTES2 = 2 * 1024 * 1024;
|
|
@@ -18718,7 +18811,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
18718
18811
|
}
|
|
18719
18812
|
if (!stat.isFile()) return { status: "not-found" };
|
|
18720
18813
|
if (stat.size > MAX_CONTENT_BYTES2) return { status: "too-large" };
|
|
18721
|
-
const source =
|
|
18814
|
+
const source = readFileSync25(abs, "utf8");
|
|
18722
18815
|
const title = extractTitle(abs, kind === "markdown" ? "md" : kind);
|
|
18723
18816
|
if (kind === "excalidraw") {
|
|
18724
18817
|
try {
|
|
@@ -18789,7 +18882,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
18789
18882
|
let text = null;
|
|
18790
18883
|
try {
|
|
18791
18884
|
const size = statSync9(docAbs).size;
|
|
18792
|
-
if (size <= budget) text =
|
|
18885
|
+
if (size <= budget) text = readFileSync25(docAbs, "utf8");
|
|
18793
18886
|
} catch {
|
|
18794
18887
|
continue;
|
|
18795
18888
|
}
|
|
@@ -18806,7 +18899,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
18806
18899
|
let body;
|
|
18807
18900
|
try {
|
|
18808
18901
|
if (statSync9(resolved2.abs).size > MAX_ASSET_BYTES) continue;
|
|
18809
|
-
body =
|
|
18902
|
+
body = readFileSync25(resolved2.abs);
|
|
18810
18903
|
} catch {
|
|
18811
18904
|
continue;
|
|
18812
18905
|
}
|
|
@@ -18832,6 +18925,26 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
18832
18925
|
return { status: "ok", kind, title, content: content + renderAttachments(attachments), assets };
|
|
18833
18926
|
}
|
|
18834
18927
|
|
|
18928
|
+
// src/dashboard/host-upgrade.ts
|
|
18929
|
+
import { spawn as spawn7 } from "child_process";
|
|
18930
|
+
import { mkdirSync as mkdirSync13, openSync as openSync3 } from "fs";
|
|
18931
|
+
import { join as join27 } from "path";
|
|
18932
|
+
var running = false;
|
|
18933
|
+
function startHostUpgrade() {
|
|
18934
|
+
if (running) return { started: false };
|
|
18935
|
+
mkdirSync13(paths.LOG_DIR, { recursive: true });
|
|
18936
|
+
const log5 = openSync3(join27(paths.LOG_DIR, "host-upgrade.log"), "a");
|
|
18937
|
+
const child = spawn7(process.execPath, [process.argv[1] ?? "", "upgrade"], {
|
|
18938
|
+
detached: true,
|
|
18939
|
+
stdio: ["ignore", log5, log5]
|
|
18940
|
+
});
|
|
18941
|
+
running = true;
|
|
18942
|
+
child.on("exit", () => running = false);
|
|
18943
|
+
child.on("error", () => running = false);
|
|
18944
|
+
child.unref();
|
|
18945
|
+
return { started: true };
|
|
18946
|
+
}
|
|
18947
|
+
|
|
18835
18948
|
// src/dashboard/agent-bridge-host.ts
|
|
18836
18949
|
var HOSTED_DATA_SOCKET_ORIGIN = "wss://flyspace-api.fly.dev";
|
|
18837
18950
|
function dataSocketUrl() {
|
|
@@ -18841,7 +18954,7 @@ function dataSocketUrl() {
|
|
|
18841
18954
|
return `ws://127.0.0.1:${process.env["FLYSPACE_DATA_API_PORT"] ?? DEFAULT_DATA_API_PORT}${HOST_SOCKET_PATH}`;
|
|
18842
18955
|
}
|
|
18843
18956
|
function listLocalSessions() {
|
|
18844
|
-
if (!
|
|
18957
|
+
if (!existsSync31(paths.DB_FILE)) return [];
|
|
18845
18958
|
const store = new SessionStore(paths.DB_FILE);
|
|
18846
18959
|
try {
|
|
18847
18960
|
return store.listSessions();
|
|
@@ -18872,6 +18985,7 @@ function localAgentCallbacks(chatAgent) {
|
|
|
18872
18985
|
docsRequest: (req) => handleDocsRequest(req),
|
|
18873
18986
|
hostRequest: (req) => handleHostRequest(req),
|
|
18874
18987
|
listSessions: () => listLocalSessions(),
|
|
18988
|
+
upgradeHost: () => startHostUpgrade(),
|
|
18875
18989
|
scanOnboarding: (slug) => scanOnboarding(slug),
|
|
18876
18990
|
linkProjectFolders: (slug, items) => linkProjectFolders(slug, items),
|
|
18877
18991
|
isHostOnline: () => true
|
|
@@ -18882,7 +18996,7 @@ function buildHostConfigSnapshot() {
|
|
|
18882
18996
|
const snapshot2 = {};
|
|
18883
18997
|
for (const profile of listProfiles()) {
|
|
18884
18998
|
snapshot2[profile] = runWithProfile(profile, () => ({
|
|
18885
|
-
env:
|
|
18999
|
+
env: existsSync31(paths.CONFIG_FILE) ? dotenv2.parse(readFileSync26(paths.CONFIG_FILE, "utf-8")) : {},
|
|
18886
19000
|
agentModel: loadDashboardSettings().agent
|
|
18887
19001
|
}));
|
|
18888
19002
|
}
|
|
@@ -18962,6 +19076,10 @@ function connectHostSocket(url, callbacks, onAuthenticated, onWorkspaces) {
|
|
|
18962
19076
|
handleHostDbResult(msg.id, msg.res);
|
|
18963
19077
|
return;
|
|
18964
19078
|
}
|
|
19079
|
+
if (msg.type === "attachment-url-result") {
|
|
19080
|
+
handleAttachmentUrlResult(msg.id, msg.url);
|
|
19081
|
+
return;
|
|
19082
|
+
}
|
|
18965
19083
|
if (msg.type === "call") {
|
|
18966
19084
|
void handleCall(msg, callbacks, ws);
|
|
18967
19085
|
}
|
|
@@ -19128,8 +19246,8 @@ function startPushWatcher() {
|
|
|
19128
19246
|
}
|
|
19129
19247
|
|
|
19130
19248
|
// src/dashboard/machine-server.ts
|
|
19131
|
-
import { existsSync as
|
|
19132
|
-
import { join as
|
|
19249
|
+
import { existsSync as existsSync32 } from "fs";
|
|
19250
|
+
import { join as join28 } from "path";
|
|
19133
19251
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
19134
19252
|
|
|
19135
19253
|
// src/dashboard/app-icon-routes.ts
|
|
@@ -19211,7 +19329,7 @@ function pngResponse(c, png) {
|
|
|
19211
19329
|
}
|
|
19212
19330
|
|
|
19213
19331
|
// src/dashboard/machine-server.ts
|
|
19214
|
-
var WEB_DIST =
|
|
19332
|
+
var WEB_DIST = join28(paths.WEB_ROOT, "dist");
|
|
19215
19333
|
async function bootMachine() {
|
|
19216
19334
|
await reconcileAll().catch(
|
|
19217
19335
|
(err) => console.error("[dashboard] reconcileAll failed:", err.message)
|
|
@@ -19231,14 +19349,14 @@ async function bootMachine() {
|
|
|
19231
19349
|
function mountSpa(app) {
|
|
19232
19350
|
app.route("/share", createPublicShareAssetRouter(WEB_DIST));
|
|
19233
19351
|
app.route("/", createAppIconRouter());
|
|
19234
|
-
if (
|
|
19352
|
+
if (existsSync32(WEB_DIST) || existsSync32(join28(paths.WEB_ROOT, "package.json"))) {
|
|
19235
19353
|
app.use("/*", serveStatic({ root: WEB_DIST }));
|
|
19236
19354
|
}
|
|
19237
19355
|
app.get("/assets/*", (c) => c.text("stale asset \u2014 reload for the current build", 404));
|
|
19238
|
-
const spaFallback = serveStatic({ path:
|
|
19356
|
+
const spaFallback = serveStatic({ path: join28(WEB_DIST, "index.html") });
|
|
19239
19357
|
app.get("*", async (c, next) => {
|
|
19240
|
-
if (!
|
|
19241
|
-
const isCheckout =
|
|
19358
|
+
if (!existsSync32(join28(WEB_DIST, "index.html"))) {
|
|
19359
|
+
const isCheckout = existsSync32(join28(paths.WEB_ROOT, "package.json"));
|
|
19242
19360
|
return c.text(
|
|
19243
19361
|
isCheckout ? `Workspace UI build missing (${WEB_DIST}).
|
|
19244
19362
|
Run \`pnpm build:web\` from the repo root.
|
|
@@ -19367,13 +19485,13 @@ function flagValue(args, name) {
|
|
|
19367
19485
|
function openBrowser2(url) {
|
|
19368
19486
|
const cmd = platform7() === "darwin" ? "open" : platform7() === "win32" ? "start" : "xdg-open";
|
|
19369
19487
|
try {
|
|
19370
|
-
|
|
19488
|
+
spawn8(cmd, [url], { stdio: "ignore", detached: true }).unref();
|
|
19371
19489
|
} catch {
|
|
19372
19490
|
}
|
|
19373
19491
|
}
|
|
19374
19492
|
function ensureWebBuild() {
|
|
19375
|
-
if (
|
|
19376
|
-
if (!
|
|
19493
|
+
if (existsSync33(join29(paths.WEB_ROOT, "dist", "index.html"))) return;
|
|
19494
|
+
if (!existsSync33(join29(paths.WEB_ROOT, "package.json"))) return;
|
|
19377
19495
|
console.log(" packages/ui/dist missing, building the dashboard UI...");
|
|
19378
19496
|
const result = spawnSync("pnpm", ["build"], {
|
|
19379
19497
|
cwd: paths.WEB_ROOT,
|
|
@@ -19423,7 +19541,7 @@ async function runDashboard(args) {
|
|
|
19423
19541
|
}
|
|
19424
19542
|
|
|
19425
19543
|
// src/commands/workspace.ts
|
|
19426
|
-
import { existsSync as
|
|
19544
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
|
|
19427
19545
|
var USAGE7 = `
|
|
19428
19546
|
Usage: flyspace workspace <subcommand> [options]
|
|
19429
19547
|
|
|
@@ -19509,7 +19627,7 @@ function provisionLocal(botName) {
|
|
|
19509
19627
|
ensureDirs();
|
|
19510
19628
|
console.log(` config dir ready (${paths.CONFIG_DIR})`);
|
|
19511
19629
|
console.log(` data dir ready (${paths.DATA_DIR})`);
|
|
19512
|
-
if (
|
|
19630
|
+
if (existsSync34(paths.CONFIG_FILE)) {
|
|
19513
19631
|
console.log(" config.env exists");
|
|
19514
19632
|
} else {
|
|
19515
19633
|
writeFileSync13(paths.CONFIG_FILE, `BOT_NAME=${botName}
|
|
@@ -19526,8 +19644,8 @@ async function provisionChannels(specs) {
|
|
|
19526
19644
|
continue;
|
|
19527
19645
|
}
|
|
19528
19646
|
const cwd = expandPath(spec.cwd);
|
|
19529
|
-
if (!
|
|
19530
|
-
|
|
19647
|
+
if (!existsSync34(cwd)) {
|
|
19648
|
+
mkdirSync14(cwd, { recursive: true });
|
|
19531
19649
|
console.log(` created cwd ${cwd}`);
|
|
19532
19650
|
}
|
|
19533
19651
|
await createChannel({ name: spec.name, cwd, position: index });
|
|
@@ -19604,10 +19722,10 @@ async function runReconcileCmd(args) {
|
|
|
19604
19722
|
}
|
|
19605
19723
|
|
|
19606
19724
|
// src/cli.ts
|
|
19607
|
-
import { join as
|
|
19725
|
+
import { join as join30 } from "path";
|
|
19608
19726
|
var home = flyspaceHome();
|
|
19609
19727
|
if (home) {
|
|
19610
|
-
const bin =
|
|
19728
|
+
const bin = join30(home, "bin");
|
|
19611
19729
|
const path = process.env.PATH ?? "";
|
|
19612
19730
|
if (!path.split(":").includes(bin)) process.env.PATH = path ? `${bin}:${path}` : bin;
|
|
19613
19731
|
}
|
|
@@ -19745,7 +19863,7 @@ async function main() {
|
|
|
19745
19863
|
break;
|
|
19746
19864
|
case "-v":
|
|
19747
19865
|
case "--version":
|
|
19748
|
-
printRaw("0.1.
|
|
19866
|
+
printRaw("0.1.31");
|
|
19749
19867
|
break;
|
|
19750
19868
|
case "-h":
|
|
19751
19869
|
case "--help":
|