flyspace-cli 0.1.30 → 0.1.32
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 +315 -235
- 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
|
|
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.32" : 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
|
|
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.32";
|
|
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,17 @@ 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";
|
|
3111
|
+
function serviceLogFile(errors) {
|
|
3112
|
+
const suffix = errors ? "-error.log" : ".log";
|
|
3113
|
+
const candidates = ["host", "custie"].map((name) => join12(paths.LOG_DIR, `${name}${suffix}`));
|
|
3114
|
+
return candidates.find((file) => existsSync12(file)) ?? candidates[0];
|
|
3115
|
+
}
|
|
3056
3116
|
async function runLogs(args) {
|
|
3057
3117
|
const showErrors = args.includes("--error") || args.includes("-e");
|
|
3058
|
-
const
|
|
3059
|
-
|
|
3060
|
-
if (!existsSync11(logFile)) {
|
|
3118
|
+
const logFile = serviceLogFile(showErrors);
|
|
3119
|
+
if (!existsSync12(logFile)) {
|
|
3061
3120
|
console.error(`Log file not found: ${logFile}`);
|
|
3062
3121
|
console.error("Is the service installed? Run `flyspace install` first.");
|
|
3063
3122
|
process.exit(1);
|
|
@@ -3078,7 +3137,7 @@ async function runLogs(args) {
|
|
|
3078
3137
|
|
|
3079
3138
|
// src/commands/slack.ts
|
|
3080
3139
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3081
|
-
import { existsSync as
|
|
3140
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
|
|
3082
3141
|
import { resolve as resolve4 } from "path";
|
|
3083
3142
|
import { tmpdir as tmpdir2, homedir as homedir6 } from "os";
|
|
3084
3143
|
import { input, confirm as confirm4, select, checkbox } from "@inquirer/prompts";
|
|
@@ -5164,7 +5223,7 @@ async function threadReplies(client2, nameOrId, ts2) {
|
|
|
5164
5223
|
async function postMessage(client2, args) {
|
|
5165
5224
|
const channelArg = getArg2(args, "--channel");
|
|
5166
5225
|
const textFilePath = getArg2(args, "--text-file");
|
|
5167
|
-
const text = textFilePath ?
|
|
5226
|
+
const text = textFilePath ? readFileSync12(textFilePath, "utf8") : getArg2(args, "--text");
|
|
5168
5227
|
const threadTs = getArg2(args, "--thread");
|
|
5169
5228
|
const markdown = args.includes("--markdown");
|
|
5170
5229
|
const broadcast = args.includes("--broadcast") && Boolean(threadTs);
|
|
@@ -5262,7 +5321,7 @@ function defaultClaudeConfigDir() {
|
|
|
5262
5321
|
return process.env.CLAUDE_CONFIG_DIR || resolve4(homedir6(), ".claude");
|
|
5263
5322
|
}
|
|
5264
5323
|
function hasClaudeCredentials(configDir2) {
|
|
5265
|
-
if (
|
|
5324
|
+
if (existsSync13(resolve4(configDir2, ".credentials.json"))) {
|
|
5266
5325
|
return true;
|
|
5267
5326
|
}
|
|
5268
5327
|
if (process.platform === "darwin") {
|
|
@@ -5319,7 +5378,7 @@ async function ensureClaudeCodeReady() {
|
|
|
5319
5378
|
}
|
|
5320
5379
|
function getManifestYaml(botName) {
|
|
5321
5380
|
const manifestPath = resolve4(paths.PACKAGE_ROOT, "slack-app-manifest.yml");
|
|
5322
|
-
const raw =
|
|
5381
|
+
const raw = readFileSync12(manifestPath, "utf-8");
|
|
5323
5382
|
return raw.replace(/name: Custie/g, `name: ${botName}`);
|
|
5324
5383
|
}
|
|
5325
5384
|
async function stepPersonalise() {
|
|
@@ -5748,10 +5807,10 @@ function printNextSteps() {
|
|
|
5748
5807
|
console.log(" Run \x1B[1mflyspace service install\x1B[0m\n");
|
|
5749
5808
|
}
|
|
5750
5809
|
function copyDefaultPrompt(botName) {
|
|
5751
|
-
if (!
|
|
5810
|
+
if (!existsSync13(paths.PROMPT_FILE)) {
|
|
5752
5811
|
const defaultPrompt = resolve4(paths.PACKAGE_ROOT, "system.default.md");
|
|
5753
|
-
if (
|
|
5754
|
-
const content =
|
|
5812
|
+
if (existsSync13(defaultPrompt)) {
|
|
5813
|
+
const content = readFileSync12(defaultPrompt, "utf-8").replace(/\{\{botName\}\}/g, botName);
|
|
5755
5814
|
writeFileSync7(paths.PROMPT_FILE, content);
|
|
5756
5815
|
success4(`Default prompt copied to ${paths.PROMPT_FILE}`);
|
|
5757
5816
|
}
|
|
@@ -5764,7 +5823,7 @@ async function runSlackSetup(args) {
|
|
|
5764
5823
|
const prereq = await ensureClaudeCodeReady();
|
|
5765
5824
|
if (!prereq) return;
|
|
5766
5825
|
const { claudeConfigDir } = prereq;
|
|
5767
|
-
if (
|
|
5826
|
+
if (existsSync13(paths.CONFIG_FILE)) {
|
|
5768
5827
|
const reconfigure = await confirm4({
|
|
5769
5828
|
message: `Config already exists at ${paths.CONFIG_FILE}. Reconfigure?`,
|
|
5770
5829
|
default: false
|
|
@@ -6075,10 +6134,10 @@ async function runChatCmd(args) {
|
|
|
6075
6134
|
}
|
|
6076
6135
|
|
|
6077
6136
|
// src/commands/memory.ts
|
|
6078
|
-
import { readFileSync as
|
|
6137
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
6079
6138
|
|
|
6080
6139
|
// src/store/claude-memory-import.ts
|
|
6081
|
-
import { existsSync as
|
|
6140
|
+
import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync13 } from "fs";
|
|
6082
6141
|
import { homedir as homedir7 } from "os";
|
|
6083
6142
|
import { basename as basename5, join as join14 } from "path";
|
|
6084
6143
|
import dotenv from "dotenv";
|
|
@@ -6088,7 +6147,7 @@ function claudeMemoryDir(cwd, configDir2) {
|
|
|
6088
6147
|
function profileClaudeConfigDirFor(profile) {
|
|
6089
6148
|
const dir = profile === "default" ? paths.BASE_CONFIG_DIR : join14(paths.PROFILES_DIR, profile);
|
|
6090
6149
|
const file = join14(dir, "config.env");
|
|
6091
|
-
const env =
|
|
6150
|
+
const env = existsSync14(file) ? dotenv.parse(readFileSync13(file)) : {};
|
|
6092
6151
|
return env["CLAUDE_CONFIG_DIR"]?.trim() || join14(homedir7(), ".claude");
|
|
6093
6152
|
}
|
|
6094
6153
|
var TYPE_MAP = {
|
|
@@ -6134,11 +6193,11 @@ function parseClaudeMemory(fileName, text, channelId) {
|
|
|
6134
6193
|
return input2;
|
|
6135
6194
|
}
|
|
6136
6195
|
async function importClaudeMemories(dir, channelId, opts = {}) {
|
|
6137
|
-
if (!
|
|
6196
|
+
if (!existsSync14(dir)) throw new Error(`No Claude memory directory at ${dir}`);
|
|
6138
6197
|
const files = readdirSync3(dir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md").sort();
|
|
6139
6198
|
const results = [];
|
|
6140
6199
|
for (const file of files) {
|
|
6141
|
-
const parsed = parseClaudeMemory(file,
|
|
6200
|
+
const parsed = parseClaudeMemory(file, readFileSync13(join14(dir, file), "utf-8"), channelId);
|
|
6142
6201
|
if ("skip" in parsed) {
|
|
6143
6202
|
results.push({ file, outcome: "skipped", reason: parsed.skip });
|
|
6144
6203
|
continue;
|
|
@@ -6250,7 +6309,7 @@ ${memory.body}`);
|
|
|
6250
6309
|
);
|
|
6251
6310
|
}
|
|
6252
6311
|
if (!isMemoryType(type)) throw new Error(`--type must be one of ${MEMORY_TYPES.join(", ")}`);
|
|
6253
|
-
const body = bodyArg === "-" ?
|
|
6312
|
+
const body = bodyArg === "-" ? readFileSync14(0, "utf-8") : bodyArg;
|
|
6254
6313
|
const thread = getArg4(rest, "--thread");
|
|
6255
6314
|
const { created, changed } = await saveMemory(
|
|
6256
6315
|
target,
|
|
@@ -6349,12 +6408,12 @@ async function runMcpCmd(args) {
|
|
|
6349
6408
|
|
|
6350
6409
|
// src/commands/dashboard.ts
|
|
6351
6410
|
import { spawn as spawn8, spawnSync } from "child_process";
|
|
6352
|
-
import { existsSync as
|
|
6411
|
+
import { existsSync as existsSync33 } from "fs";
|
|
6353
6412
|
import { platform as platform7 } from "os";
|
|
6354
|
-
import { join as
|
|
6413
|
+
import { join as join28 } from "path";
|
|
6355
6414
|
|
|
6356
6415
|
// src/reconcile.ts
|
|
6357
|
-
import { existsSync as
|
|
6416
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
6358
6417
|
async function boundWorkspaces2() {
|
|
6359
6418
|
return (await boundWorkspaces())?.workspaces ?? null;
|
|
6360
6419
|
}
|
|
@@ -6365,12 +6424,12 @@ async function servedWorkspaces2() {
|
|
|
6365
6424
|
async function reconcileProfile(profile) {
|
|
6366
6425
|
return runWithProfile(profile, async () => {
|
|
6367
6426
|
const log5 = [];
|
|
6368
|
-
const hadConfigDir =
|
|
6369
|
-
const hadDataDir =
|
|
6427
|
+
const hadConfigDir = existsSync15(paths.CONFIG_DIR);
|
|
6428
|
+
const hadDataDir = existsSync15(paths.DATA_DIR);
|
|
6370
6429
|
ensureDirs();
|
|
6371
6430
|
if (!hadConfigDir) log5.push(`config dir created (${paths.CONFIG_DIR})`);
|
|
6372
6431
|
if (!hadDataDir) log5.push(`data dir created (${paths.DATA_DIR})`);
|
|
6373
|
-
if (!
|
|
6432
|
+
if (!existsSync15(paths.CONFIG_FILE)) {
|
|
6374
6433
|
writeFileSync8(paths.CONFIG_FILE, `BOT_NAME=${profile}
|
|
6375
6434
|
`);
|
|
6376
6435
|
log5.push(`config.env written (${paths.CONFIG_FILE})`);
|
|
@@ -6527,7 +6586,7 @@ var requireDocsAccess = async (c, next) => {
|
|
|
6527
6586
|
import { Hono as Hono29 } from "hono";
|
|
6528
6587
|
|
|
6529
6588
|
// ../api/src/dashboard/public-share-routes.ts
|
|
6530
|
-
import { existsSync as
|
|
6589
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16, statSync as statSync3 } from "fs";
|
|
6531
6590
|
import { join as join16 } from "path";
|
|
6532
6591
|
import { Hono as Hono5 } from "hono";
|
|
6533
6592
|
import { GetObjectCommand } from "@aws-sdk/client-s3";
|
|
@@ -6993,11 +7052,11 @@ function injectShareHead(html, head) {
|
|
|
6993
7052
|
|
|
6994
7053
|
// ../api/src/dashboard/app-icon.ts
|
|
6995
7054
|
import { createHash as createHash2 } from "crypto";
|
|
6996
|
-
import { existsSync as
|
|
7055
|
+
import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
|
|
6997
7056
|
import { join as join15 } from "path";
|
|
6998
7057
|
import sharp2 from "sharp";
|
|
6999
7058
|
var PLATE = "#0A0A0F";
|
|
7000
|
-
var BRAND_DIR =
|
|
7059
|
+
var BRAND_DIR = existsSync16(join15(paths.WEB_ROOT, "public")) ? join15(paths.WEB_ROOT, "public") : join15(paths.PACKAGE_ROOT, "dist", "brand");
|
|
7001
7060
|
var MARK_FILE = join15(BRAND_DIR, "icons", "icon-512.png");
|
|
7002
7061
|
var MARK_SVG_FILE = join15(BRAND_DIR, "favicon.svg");
|
|
7003
7062
|
var LAYOUT = {
|
|
@@ -7022,7 +7081,7 @@ function roundedPlate(size, fill = PLATE) {
|
|
|
7022
7081
|
}
|
|
7023
7082
|
async function renderBadge(box) {
|
|
7024
7083
|
const transparent = { r: 0, g: 0, b: 0, alpha: 0 };
|
|
7025
|
-
const raster = await sharp2(
|
|
7084
|
+
const raster = await sharp2(readFileSync15(MARK_SVG_FILE)).resize(512, 512, { fit: "contain", background: transparent }).png().toBuffer();
|
|
7026
7085
|
return sharp2(raster).trim().resize(box, box, { fit: "contain", background: transparent }).png().toBuffer();
|
|
7027
7086
|
}
|
|
7028
7087
|
async function sampleBackground(logo) {
|
|
@@ -7077,7 +7136,7 @@ async function renderAppIcon({
|
|
|
7077
7136
|
if (hit) return hit;
|
|
7078
7137
|
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
7138
|
// either on its usual dark plate or, for a favicon, trimmed and bare.
|
|
7080
|
-
(transparent ? await renderBadge(size) : await sharp2(
|
|
7139
|
+
(transparent ? await renderBadge(size) : await sharp2(readFileSync15(MARK_FILE)).resize(size, size, { fit: "cover" }).png().toBuffer());
|
|
7081
7140
|
cache3.set(key, out);
|
|
7082
7141
|
return out;
|
|
7083
7142
|
}
|
|
@@ -8295,7 +8354,7 @@ var shellCache = null;
|
|
|
8295
8354
|
function readShell(path) {
|
|
8296
8355
|
const { mtimeMs } = statSync3(path);
|
|
8297
8356
|
if (shellCache?.mtimeMs === mtimeMs) return shellCache.html;
|
|
8298
|
-
const html =
|
|
8357
|
+
const html = readFileSync16(path, "utf8").replaceAll('"/assets/', `"${BUILD_PREFIX}/`);
|
|
8299
8358
|
shellCache = { mtimeMs, html };
|
|
8300
8359
|
return html;
|
|
8301
8360
|
}
|
|
@@ -8316,16 +8375,16 @@ function serveBuildFile(c, webDist, dir, name) {
|
|
|
8316
8375
|
return c.text("not found", 404);
|
|
8317
8376
|
}
|
|
8318
8377
|
const path = join16(webDist, dir, name);
|
|
8319
|
-
if (!
|
|
8378
|
+
if (!existsSync17(path)) return c.text("not found", 404);
|
|
8320
8379
|
c.header("Content-Type", type);
|
|
8321
8380
|
c.header("Cache-Control", "public, max-age=31536000, immutable");
|
|
8322
8381
|
if (ext === ".css") {
|
|
8323
|
-
return c.body(
|
|
8382
|
+
return c.body(readFileSync16(path, "utf8").replaceAll("url(/fonts/", `url(${BUILD_PREFIX}/fonts/`));
|
|
8324
8383
|
}
|
|
8325
8384
|
if (ext === ".js") {
|
|
8326
|
-
return c.body(
|
|
8385
|
+
return c.body(readFileSync16(path, "utf8").replaceAll('"/assets/', `"${BUILD_PREFIX}/`));
|
|
8327
8386
|
}
|
|
8328
|
-
return c.body(new Uint8Array(
|
|
8387
|
+
return c.body(new Uint8Array(readFileSync16(path)));
|
|
8329
8388
|
}
|
|
8330
8389
|
var HTML_SHARE_CSP = "sandbox allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox";
|
|
8331
8390
|
function servePublishedHtml(c, share) {
|
|
@@ -8462,7 +8521,7 @@ ${analytics}` : head);
|
|
|
8462
8521
|
const lang = canonicalLanguage(requested);
|
|
8463
8522
|
if (!isTranslatableLanguage(lang)) return next();
|
|
8464
8523
|
const shell = join16(webDist, "index.html");
|
|
8465
|
-
if (!
|
|
8524
|
+
if (!existsSync17(shell)) return next();
|
|
8466
8525
|
const share = await getDocShare(c.req.param("id"));
|
|
8467
8526
|
if (!share) return next();
|
|
8468
8527
|
if (requested !== lang) return c.redirect(`/share/${share.id}/${lang}`, 302);
|
|
@@ -8498,7 +8557,7 @@ ${analytics}` : head);
|
|
|
8498
8557
|
if (!share) return next();
|
|
8499
8558
|
if (share.kind === "html") return servePublishedHtml(c, share);
|
|
8500
8559
|
const shell = join16(webDist, "index.html");
|
|
8501
|
-
if (!
|
|
8560
|
+
if (!existsSync17(shell)) return next();
|
|
8502
8561
|
return readerPage(
|
|
8503
8562
|
c,
|
|
8504
8563
|
share,
|
|
@@ -8564,7 +8623,7 @@ function createGuestRouter() {
|
|
|
8564
8623
|
}
|
|
8565
8624
|
|
|
8566
8625
|
// ../api/src/dashboard/auth-routes.ts
|
|
8567
|
-
import { Hono as
|
|
8626
|
+
import { Hono as Hono9 } from "hono";
|
|
8568
8627
|
|
|
8569
8628
|
// ../api/src/dashboard/supabase-server-client.ts
|
|
8570
8629
|
import { createServerClient, parseCookieHeader, serializeCookieHeader } from "@supabase/ssr";
|
|
@@ -8675,7 +8734,7 @@ function pruneExpired(now) {
|
|
|
8675
8734
|
}
|
|
8676
8735
|
}
|
|
8677
8736
|
var SUPPORT_NAME = "Flyspace Support";
|
|
8678
|
-
var SUPPORT_AVATAR_URL = "/logo/flyspace-
|
|
8737
|
+
var SUPPORT_AVATAR_URL = "/logo/flyspace-support.svg";
|
|
8679
8738
|
async function resolveAuthContext(c, opts = {}) {
|
|
8680
8739
|
const cookie = c.req?.header("Cookie") ?? "";
|
|
8681
8740
|
if (!cookie) return resolveAuthContextUncached(c);
|
|
@@ -8806,7 +8865,7 @@ async function verifyTurnstileToken(token) {
|
|
|
8806
8865
|
}
|
|
8807
8866
|
|
|
8808
8867
|
// ../api/src/dashboard/onboarding-routes.ts
|
|
8809
|
-
import { Hono as
|
|
8868
|
+
import { Hono as Hono8 } from "hono";
|
|
8810
8869
|
|
|
8811
8870
|
// ../api/src/store/device-store.ts
|
|
8812
8871
|
import { createHash as createHash7, createPublicKey as createPublicKey2, randomBytes as randomBytes4, randomInt } from "crypto";
|
|
@@ -9035,23 +9094,83 @@ async function userEmail(userId) {
|
|
|
9035
9094
|
}
|
|
9036
9095
|
|
|
9037
9096
|
// ../api/src/dashboard/agent-bridge-data.ts
|
|
9038
|
-
import { randomBytes as randomBytes5, randomUUID as
|
|
9097
|
+
import { randomBytes as randomBytes5, randomUUID as randomUUID3, verify as verifyRaw } from "crypto";
|
|
9039
9098
|
import { WebSocket, WebSocketServer } from "ws";
|
|
9040
9099
|
|
|
9041
9100
|
// ../api/src/dashboard/host-identity.ts
|
|
9042
9101
|
import { createPrivateKey as createPrivateKey2, createPublicKey as createPublicKey3, generateKeyPairSync as generateKeyPairSync2 } from "crypto";
|
|
9043
|
-
import { existsSync as
|
|
9044
|
-
import { dirname as
|
|
9102
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync17, writeFileSync as writeFileSync9 } from "fs";
|
|
9103
|
+
import { dirname as dirname6, join as join17 } from "path";
|
|
9045
9104
|
function hostPrivateKeyPath() {
|
|
9046
9105
|
return process.env["FLYSPACE_HOST_KEY_PATH"] || join17(paths.BASE_CONFIG_DIR, "host.key");
|
|
9047
9106
|
}
|
|
9048
9107
|
function loadHostPrivateKey(path = hostPrivateKeyPath()) {
|
|
9049
|
-
if (!
|
|
9108
|
+
if (!existsSync18(path)) {
|
|
9050
9109
|
throw new Error(
|
|
9051
9110
|
`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
9111
|
);
|
|
9053
9112
|
}
|
|
9054
|
-
return createPrivateKey2(
|
|
9113
|
+
return createPrivateKey2(readFileSync17(path, "utf8"));
|
|
9114
|
+
}
|
|
9115
|
+
|
|
9116
|
+
// ../api/src/dashboard/uploads-routes.ts
|
|
9117
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
9118
|
+
import { Hono as Hono7 } from "hono";
|
|
9119
|
+
import { PutObjectCommand as PutObjectCommand2, GetObjectCommand as GetObjectCommand3 } from "@aws-sdk/client-s3";
|
|
9120
|
+
import { getSignedUrl as getSignedUrl3 } from "@aws-sdk/s3-request-presigner";
|
|
9121
|
+
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
9122
|
+
var PRESIGN_TTL_SECONDS2 = 300;
|
|
9123
|
+
var KEY_PREFIX = "chat";
|
|
9124
|
+
var READABLE_PREFIXES = [KEY_PREFIX, SCRATCH_PREFIX];
|
|
9125
|
+
function sanitizeFilename(name) {
|
|
9126
|
+
const cleaned = name.replace(/[/\\]/g, "_").replace(/\s+/g, "-").replace(/[^\w.-]/g, "").trim();
|
|
9127
|
+
return (cleaned || "file").slice(-100);
|
|
9128
|
+
}
|
|
9129
|
+
function encodeKeyForUrl(key) {
|
|
9130
|
+
return key.split("/").map(encodeURIComponent).join("/");
|
|
9131
|
+
}
|
|
9132
|
+
async function presignAttachmentRead(key) {
|
|
9133
|
+
if (!READABLE_PREFIXES.some((prefix) => key.startsWith(`${prefix}/`))) return null;
|
|
9134
|
+
return getSignedUrl3(getR2Client(), new GetObjectCommand3({ Bucket: getR2Bucket(), Key: key }), {
|
|
9135
|
+
expiresIn: PRESIGN_TTL_SECONDS2
|
|
9136
|
+
});
|
|
9137
|
+
}
|
|
9138
|
+
function createUploadsRouter() {
|
|
9139
|
+
const app = new Hono7();
|
|
9140
|
+
app.post("/uploads", async (c) => {
|
|
9141
|
+
const body = await c.req.parseBody().catch(() => null);
|
|
9142
|
+
const file = body?.["file"];
|
|
9143
|
+
if (!(file instanceof File)) return c.json({ error: "missing file" }, 400);
|
|
9144
|
+
if (file.size > MAX_FILE_BYTES) {
|
|
9145
|
+
return c.json({ error: `file must be ${MAX_FILE_BYTES / (1024 * 1024)}MB or smaller` }, 400);
|
|
9146
|
+
}
|
|
9147
|
+
const key = `${KEY_PREFIX}/${getProfile()}/${randomUUID2()}-${sanitizeFilename(file.name)}`;
|
|
9148
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
9149
|
+
await getR2Client().send(
|
|
9150
|
+
new PutObjectCommand2({
|
|
9151
|
+
Bucket: getR2Bucket(),
|
|
9152
|
+
Key: key,
|
|
9153
|
+
Body: buffer,
|
|
9154
|
+
ContentType: file.type || "application/octet-stream"
|
|
9155
|
+
})
|
|
9156
|
+
);
|
|
9157
|
+
return c.json(
|
|
9158
|
+
{
|
|
9159
|
+
url: `/api/attachments/${encodeKeyForUrl(key)}`,
|
|
9160
|
+
name: file.name,
|
|
9161
|
+
type: file.type,
|
|
9162
|
+
size: file.size
|
|
9163
|
+
},
|
|
9164
|
+
201
|
|
9165
|
+
);
|
|
9166
|
+
});
|
|
9167
|
+
app.get("/attachments/:key{.+}", async (c) => {
|
|
9168
|
+
const url = await presignAttachmentRead(c.req.param("key"));
|
|
9169
|
+
if (!url) return c.json({ error: "not found" }, 404);
|
|
9170
|
+
c.header("Cache-Control", `private, max-age=${PRESIGN_TTL_SECONDS2 - 60}`);
|
|
9171
|
+
return c.redirect(url, 302);
|
|
9172
|
+
});
|
|
9173
|
+
return app;
|
|
9055
9174
|
}
|
|
9056
9175
|
|
|
9057
9176
|
// ../core/src/bridge-protocol.ts
|
|
@@ -9216,7 +9335,7 @@ function hostUnavailable(c, agent, method) {
|
|
|
9216
9335
|
return c.json({ error: "host offline", reason: agent.getHostCallFailure?.(method) ?? "offline" }, 503);
|
|
9217
9336
|
}
|
|
9218
9337
|
function createOnboardingRouter(agent) {
|
|
9219
|
-
const app = new
|
|
9338
|
+
const app = new Hono8();
|
|
9220
9339
|
app.get("/scan", async (c) => {
|
|
9221
9340
|
const ctx = await resolveAuthContext(c);
|
|
9222
9341
|
if (ctx?.role !== "admin") return c.json({ error: "admin access required" }, 403);
|
|
@@ -9354,7 +9473,7 @@ async function ensureGreetingThread(workspaceId, channelId, ownerName) {
|
|
|
9354
9473
|
|
|
9355
9474
|
// ../api/src/dashboard/auth-routes.ts
|
|
9356
9475
|
function createAuthRouter() {
|
|
9357
|
-
const auth = new
|
|
9476
|
+
const auth = new Hono9();
|
|
9358
9477
|
const emailedLink = (deliver) => async (c) => {
|
|
9359
9478
|
const body = await c.req.json().catch(() => ({}));
|
|
9360
9479
|
const email = body.email?.trim().toLowerCase();
|
|
@@ -9434,7 +9553,7 @@ var requireWorkspaceMembership = async (c, next) => {
|
|
|
9434
9553
|
};
|
|
9435
9554
|
|
|
9436
9555
|
// ../api/src/dashboard/billing-routes.ts
|
|
9437
|
-
import { Hono as
|
|
9556
|
+
import { Hono as Hono10 } from "hono";
|
|
9438
9557
|
function isPlan(value) {
|
|
9439
9558
|
return value === "local" || value === "cloud";
|
|
9440
9559
|
}
|
|
@@ -9450,7 +9569,7 @@ async function requireActivePlan(c, next) {
|
|
|
9450
9569
|
return next();
|
|
9451
9570
|
}
|
|
9452
9571
|
function createBillingRouter() {
|
|
9453
|
-
const billing = new
|
|
9572
|
+
const billing = new Hono10();
|
|
9454
9573
|
billing.get("/", async (c) => {
|
|
9455
9574
|
const auth = c.get("auth");
|
|
9456
9575
|
const { planStatus, stripeSubscriptionId } = await getWorkspaceBilling(auth.workspaceId);
|
|
@@ -9506,7 +9625,7 @@ function createBillingRouter() {
|
|
|
9506
9625
|
}
|
|
9507
9626
|
|
|
9508
9627
|
// ../api/src/dashboard/admin-routes.ts
|
|
9509
|
-
import { Hono as
|
|
9628
|
+
import { Hono as Hono11 } from "hono";
|
|
9510
9629
|
|
|
9511
9630
|
// ../api/src/dashboard/admin-middleware.ts
|
|
9512
9631
|
function platformAdminEmails() {
|
|
@@ -9532,7 +9651,7 @@ var requirePlatformOperator = async (c, next) => {
|
|
|
9532
9651
|
// ../api/src/dashboard/admin-routes.ts
|
|
9533
9652
|
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
9534
9653
|
function createAdminRouter() {
|
|
9535
|
-
const admin = new
|
|
9654
|
+
const admin = new Hono11();
|
|
9536
9655
|
admin.use("*", requirePlatformOperator);
|
|
9537
9656
|
admin.get("/workspaces", async (c) => {
|
|
9538
9657
|
const summaries = await listWorkspaceSummaries();
|
|
@@ -9645,11 +9764,11 @@ var BOT_LABEL = "Workspace Agent";
|
|
|
9645
9764
|
var BOT_DM_KEY = "claude";
|
|
9646
9765
|
|
|
9647
9766
|
// ../api/src/dashboard/data-api.ts
|
|
9648
|
-
import { existsSync as
|
|
9767
|
+
import { existsSync as existsSync20 } from "fs";
|
|
9649
9768
|
import sharp5 from "sharp";
|
|
9650
9769
|
|
|
9651
9770
|
// ../api/src/dashboard/automations-routes.ts
|
|
9652
|
-
import { Hono as
|
|
9771
|
+
import { Hono as Hono12 } from "hono";
|
|
9653
9772
|
var TYPES2 = ["schedule", "trigger", "mention-trigger"];
|
|
9654
9773
|
function isType(t) {
|
|
9655
9774
|
return TYPES2.includes(t);
|
|
@@ -9666,7 +9785,7 @@ function statusFor(message) {
|
|
|
9666
9785
|
return 400;
|
|
9667
9786
|
}
|
|
9668
9787
|
function createAutomationWriteRouter() {
|
|
9669
|
-
const app = new
|
|
9788
|
+
const app = new Hono12();
|
|
9670
9789
|
const manager = () => new AutomationManager();
|
|
9671
9790
|
app.get("/:type/:name", (c) => {
|
|
9672
9791
|
const item = manager().get(c.req.param("name"));
|
|
@@ -9722,9 +9841,9 @@ function createAutomationWriteRouter() {
|
|
|
9722
9841
|
}
|
|
9723
9842
|
|
|
9724
9843
|
// ../api/src/dashboard/categories-routes.ts
|
|
9725
|
-
import { Hono as
|
|
9844
|
+
import { Hono as Hono13 } from "hono";
|
|
9726
9845
|
function createCategoryWriteRouter() {
|
|
9727
|
-
const app = new
|
|
9846
|
+
const app = new Hono13();
|
|
9728
9847
|
app.get("/", async (c) => {
|
|
9729
9848
|
const { data, error: error2 } = await getSupabaseClient().from("categories").select("*").eq("profile", getProfile()).order("position", { ascending: true });
|
|
9730
9849
|
if (error2) return c.json({ error: error2.message }, 500);
|
|
@@ -9778,7 +9897,7 @@ function createCategoryWriteRouter() {
|
|
|
9778
9897
|
}
|
|
9779
9898
|
|
|
9780
9899
|
// ../api/src/dashboard/chat-routes.ts
|
|
9781
|
-
import { Hono as
|
|
9900
|
+
import { Hono as Hono14 } from "hono";
|
|
9782
9901
|
|
|
9783
9902
|
// ../api/src/dashboard/fork-summary.ts
|
|
9784
9903
|
var GROQ_API_URL4 = "https://api.groq.com/openai/v1/chat/completions";
|
|
@@ -10265,7 +10384,7 @@ async function generateAutoTitle(threadId, channelId, body) {
|
|
|
10265
10384
|
await supabase.from("threads").update({ auto_title: autoTitle }).eq("id", threadId).is("title", null);
|
|
10266
10385
|
}
|
|
10267
10386
|
function createChatRouter(agent = {}) {
|
|
10268
|
-
const app = new
|
|
10387
|
+
const app = new Hono14();
|
|
10269
10388
|
const {
|
|
10270
10389
|
onChatMessage: onMessage,
|
|
10271
10390
|
isAgentThinking,
|
|
@@ -10779,7 +10898,7 @@ function createChatRouter(agent = {}) {
|
|
|
10779
10898
|
}
|
|
10780
10899
|
|
|
10781
10900
|
// ../api/src/dashboard/doc-shares-routes.ts
|
|
10782
|
-
import { Hono as
|
|
10901
|
+
import { Hono as Hono15 } from "hono";
|
|
10783
10902
|
|
|
10784
10903
|
// ../api/src/dashboard/doc-share-features.ts
|
|
10785
10904
|
import { DeleteObjectCommand as DeleteObjectCommand2 } from "@aws-sdk/client-s3";
|
|
@@ -10839,7 +10958,7 @@ async function updateShareFeatures(share, requested) {
|
|
|
10839
10958
|
|
|
10840
10959
|
// ../api/src/dashboard/doc-share-publish.ts
|
|
10841
10960
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
10842
|
-
import { CopyObjectCommand, PutObjectCommand as
|
|
10961
|
+
import { CopyObjectCommand, PutObjectCommand as PutObjectCommand3 } from "@aws-sdk/client-s3";
|
|
10843
10962
|
|
|
10844
10963
|
// ../api/src/dashboard/bridge-types.ts
|
|
10845
10964
|
var SHARE_ASSET_PLACEHOLDER = "/share/{{flyspace-share-id}}/assets/";
|
|
@@ -10935,7 +11054,7 @@ async function publishDocShare(input2, readBundle) {
|
|
|
10935
11054
|
);
|
|
10936
11055
|
} else {
|
|
10937
11056
|
await client2.send(
|
|
10938
|
-
new
|
|
11057
|
+
new PutObjectCommand3({
|
|
10939
11058
|
Bucket: bucket,
|
|
10940
11059
|
Key: key,
|
|
10941
11060
|
Body: Buffer.from(asset.data, "base64"),
|
|
@@ -11114,7 +11233,7 @@ function resolveChannel2(c) {
|
|
|
11114
11233
|
}
|
|
11115
11234
|
var LIST_LIMIT = 500;
|
|
11116
11235
|
function createDocShareListRouter() {
|
|
11117
|
-
const app = new
|
|
11236
|
+
const app = new Hono15();
|
|
11118
11237
|
app.get("/shares", async (c) => {
|
|
11119
11238
|
const viewer = c.get("auth");
|
|
11120
11239
|
const readable = await filterReadableDocsChannels(
|
|
@@ -11158,7 +11277,7 @@ function createDocShareListRouter() {
|
|
|
11158
11277
|
return app;
|
|
11159
11278
|
}
|
|
11160
11279
|
function createDocShareWriteRouter(agent = {}) {
|
|
11161
|
-
const app = new
|
|
11280
|
+
const app = new Hono15();
|
|
11162
11281
|
app.use("/:id/docs/share", requireDocsAccess);
|
|
11163
11282
|
app.use("/:id/docs/share/*", requireDocsAccess);
|
|
11164
11283
|
app.use("/:id/docs/shares", requireDocsAccess);
|
|
@@ -11331,7 +11450,7 @@ function createDocShareWriteRouter(agent = {}) {
|
|
|
11331
11450
|
}
|
|
11332
11451
|
|
|
11333
11452
|
// ../api/src/dashboard/search-routes.ts
|
|
11334
|
-
import { Hono as
|
|
11453
|
+
import { Hono as Hono16 } from "hono";
|
|
11335
11454
|
function matchScore(q, fields) {
|
|
11336
11455
|
const terms = q.toLowerCase().split(/\s+/).filter(Boolean);
|
|
11337
11456
|
if (terms.length === 0) return 0;
|
|
@@ -11479,7 +11598,7 @@ function searchAutomations(q) {
|
|
|
11479
11598
|
}
|
|
11480
11599
|
var RESULT_LIMIT = 30;
|
|
11481
11600
|
function createSearchRouter() {
|
|
11482
|
-
const app = new
|
|
11601
|
+
const app = new Hono16();
|
|
11483
11602
|
app.get("/", async (c) => {
|
|
11484
11603
|
const q = c.req.query("q")?.trim();
|
|
11485
11604
|
if (!q) return c.json({ results: [] });
|
|
@@ -11495,14 +11614,14 @@ function createSearchRouter() {
|
|
|
11495
11614
|
}
|
|
11496
11615
|
|
|
11497
11616
|
// ../api/src/dashboard/feature-flags-routes.ts
|
|
11498
|
-
import { Hono as
|
|
11617
|
+
import { Hono as Hono17 } from "hono";
|
|
11499
11618
|
async function isFeatureEnabled(c, flagId) {
|
|
11500
11619
|
const auth = c.get("auth");
|
|
11501
11620
|
const enabled = await listEnabledFeatureFlags(auth.workspaceId);
|
|
11502
11621
|
return resolveFeatureFlags(enabled, isDevHost(c))[flagId];
|
|
11503
11622
|
}
|
|
11504
11623
|
function createFeatureFlagsRouter() {
|
|
11505
|
-
const app = new
|
|
11624
|
+
const app = new Hono17();
|
|
11506
11625
|
app.get("/", async (c) => {
|
|
11507
11626
|
const auth = c.get("auth");
|
|
11508
11627
|
const devHost = isDevHost(c);
|
|
@@ -11527,67 +11646,6 @@ function createFeatureFlagsRouter() {
|
|
|
11527
11646
|
return app;
|
|
11528
11647
|
}
|
|
11529
11648
|
|
|
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
11649
|
// ../api/src/dashboard/transcribe-routes.ts
|
|
11592
11650
|
import { Hono as Hono18 } from "hono";
|
|
11593
11651
|
var GROQ_TRANSCRIBE_URL = "https://api.groq.com/openai/v1/audio/transcriptions";
|
|
@@ -13245,7 +13303,7 @@ async function sessionUsageForThreads(workspace, threadTsList) {
|
|
|
13245
13303
|
}
|
|
13246
13304
|
|
|
13247
13305
|
// ../api/src/dashboard/usage-report.ts
|
|
13248
|
-
import { existsSync as
|
|
13306
|
+
import { existsSync as existsSync19 } from "fs";
|
|
13249
13307
|
function mergeByLabel(buckets) {
|
|
13250
13308
|
const merged = /* @__PURE__ */ new Map();
|
|
13251
13309
|
for (const bucket of buckets) {
|
|
@@ -13291,7 +13349,7 @@ function toReport(days, rows, labelChannel) {
|
|
|
13291
13349
|
};
|
|
13292
13350
|
}
|
|
13293
13351
|
function fromLocalStore(days, labelChannel) {
|
|
13294
|
-
const store =
|
|
13352
|
+
const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
|
|
13295
13353
|
if (!store) return { ...EMPTY, days };
|
|
13296
13354
|
const window = days === 0 ? void 0 : days;
|
|
13297
13355
|
try {
|
|
@@ -13333,7 +13391,7 @@ async function buildChannelUsageReport(channelId, days, labelChannel) {
|
|
|
13333
13391
|
const { totals, byDay } = await channelUsageForWorkspace(workspace, aliasIds, window);
|
|
13334
13392
|
return { days, totals: totals.runs > 0 ? totals : null, byDay };
|
|
13335
13393
|
} catch {
|
|
13336
|
-
const store =
|
|
13394
|
+
const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
|
|
13337
13395
|
if (!store) return { days, totals: null, byDay: [] };
|
|
13338
13396
|
try {
|
|
13339
13397
|
const targetLabel = labelChannel(channelId);
|
|
@@ -13355,7 +13413,7 @@ async function buildThreadUsage(threadTs) {
|
|
|
13355
13413
|
if (totals.runs === 0) return { threadTs, totals: null, byModel: [] };
|
|
13356
13414
|
return { threadTs, totals, byModel };
|
|
13357
13415
|
} catch {
|
|
13358
|
-
const store =
|
|
13416
|
+
const store = existsSync19(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
|
|
13359
13417
|
if (!store) return { threadTs, totals: null, byModel: [] };
|
|
13360
13418
|
try {
|
|
13361
13419
|
const totals = store.threadTotals(threadTs);
|
|
@@ -13793,7 +13851,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13793
13851
|
runs.set(name, { lastRun: run4.lastRunAt.toISOString(), lastStatus: run4.lastStatus });
|
|
13794
13852
|
}
|
|
13795
13853
|
} catch {
|
|
13796
|
-
const runStore =
|
|
13854
|
+
const runStore = existsSync20(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
|
|
13797
13855
|
if (runStore) {
|
|
13798
13856
|
try {
|
|
13799
13857
|
for (const run4 of runStore.listLastRuns()) {
|
|
@@ -13855,7 +13913,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13855
13913
|
const runs = (await recentRunsForWorkspace(getProfile(), 200)).map(toResponse5);
|
|
13856
13914
|
return c.json({ runs });
|
|
13857
13915
|
} catch {
|
|
13858
|
-
const runStore =
|
|
13916
|
+
const runStore = existsSync20(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
|
|
13859
13917
|
if (!runStore) return c.json({ runs: [] });
|
|
13860
13918
|
try {
|
|
13861
13919
|
const runs = runStore.listRuns(200).map(toResponse5);
|
|
@@ -13977,7 +14035,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
13977
14035
|
} catch {
|
|
13978
14036
|
latest2 = /* @__PURE__ */ new Map();
|
|
13979
14037
|
totals = /* @__PURE__ */ new Map();
|
|
13980
|
-
const usageStore =
|
|
14038
|
+
const usageStore = existsSync20(paths.DB_FILE) ? getLocalCache().openUsageStore(paths.DB_FILE) : null;
|
|
13981
14039
|
if (usageStore) {
|
|
13982
14040
|
try {
|
|
13983
14041
|
latest2 = usageStore.latestByThread(threadTsList);
|
|
@@ -14243,11 +14301,11 @@ function createDeviceAuthRouter() {
|
|
|
14243
14301
|
}
|
|
14244
14302
|
|
|
14245
14303
|
// ../api/src/dashboard/skills-routes.ts
|
|
14246
|
-
import { dirname as
|
|
14304
|
+
import { dirname as dirname7 } from "path";
|
|
14247
14305
|
|
|
14248
14306
|
// ../api/src/dashboard/skill-mirror.ts
|
|
14249
14307
|
import { createHash as createHash11 } from "crypto";
|
|
14250
|
-
import { existsSync as
|
|
14308
|
+
import { existsSync as existsSync21, statSync as statSync4 } from "fs";
|
|
14251
14309
|
var SYNC_INTERVAL_MS = 6e4;
|
|
14252
14310
|
var UPSERT_BATCH = 50;
|
|
14253
14311
|
function mirrorScopeOf(ref) {
|
|
@@ -14255,7 +14313,7 @@ function mirrorScopeOf(ref) {
|
|
|
14255
14313
|
}
|
|
14256
14314
|
function isDir(path) {
|
|
14257
14315
|
try {
|
|
14258
|
-
return
|
|
14316
|
+
return existsSync21(path) && statSync4(path).isDirectory();
|
|
14259
14317
|
} catch {
|
|
14260
14318
|
return false;
|
|
14261
14319
|
}
|
|
@@ -14580,12 +14638,11 @@ function createGatedRouter(agent, register) {
|
|
|
14580
14638
|
}
|
|
14581
14639
|
|
|
14582
14640
|
// src/dashboard/host-api.ts
|
|
14583
|
-
import { existsSync as
|
|
14584
|
-
import { join as join26 } from "path";
|
|
14641
|
+
import { existsSync as existsSync30, readFileSync as readFileSync24, statSync as statSync8 } from "fs";
|
|
14585
14642
|
import sharp6 from "sharp";
|
|
14586
14643
|
|
|
14587
14644
|
// src/dashboard/channels-routes.ts
|
|
14588
|
-
import { existsSync as
|
|
14645
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync10 } from "fs";
|
|
14589
14646
|
import { Hono as Hono30 } from "hono";
|
|
14590
14647
|
function coerceAccess(raw) {
|
|
14591
14648
|
if (raw === "open" || raw === "*" || raw === "all") return raw;
|
|
@@ -14624,7 +14681,7 @@ function coerceMemberEmails(raw) {
|
|
|
14624
14681
|
function ensureCwdDir(cwd) {
|
|
14625
14682
|
const expanded = expandPath(cwd);
|
|
14626
14683
|
if (isLogicalCwd(cwd) || isUnderLogicalRoot(expanded)) return;
|
|
14627
|
-
if (!
|
|
14684
|
+
if (!existsSync22(expanded)) mkdirSync10(expanded, { recursive: true });
|
|
14628
14685
|
}
|
|
14629
14686
|
function toResponse3(channel) {
|
|
14630
14687
|
return {
|
|
@@ -14774,13 +14831,13 @@ function createChannelWriteRouter() {
|
|
|
14774
14831
|
import {
|
|
14775
14832
|
closeSync as closeSync2,
|
|
14776
14833
|
createReadStream,
|
|
14777
|
-
existsSync as
|
|
14834
|
+
existsSync as existsSync26,
|
|
14778
14835
|
lstatSync as lstatSync2,
|
|
14779
14836
|
mkdirSync as mkdirSync11,
|
|
14780
14837
|
openSync as openSync2,
|
|
14781
14838
|
readSync as readSync2,
|
|
14782
14839
|
readdirSync as readdirSync5,
|
|
14783
|
-
readFileSync as
|
|
14840
|
+
readFileSync as readFileSync21,
|
|
14784
14841
|
realpathSync as realpathSync4,
|
|
14785
14842
|
renameSync,
|
|
14786
14843
|
rmSync as rmSync8,
|
|
@@ -14788,12 +14845,12 @@ import {
|
|
|
14788
14845
|
writeFileSync as writeFileSync11
|
|
14789
14846
|
} from "fs";
|
|
14790
14847
|
import { Readable } from "stream";
|
|
14791
|
-
import { basename as basename8, dirname as
|
|
14848
|
+
import { basename as basename8, dirname as dirname8, extname, join as join23, relative, resolve as resolve5, sep } from "path";
|
|
14792
14849
|
import { Hono as Hono31 } from "hono";
|
|
14793
14850
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
14794
14851
|
|
|
14795
14852
|
// src/dashboard/docs-watcher.ts
|
|
14796
|
-
import { existsSync as
|
|
14853
|
+
import { existsSync as existsSync23, watch } from "fs";
|
|
14797
14854
|
import { basename as basename6, join as join18 } from "path";
|
|
14798
14855
|
var DEBOUNCE_MS = 200;
|
|
14799
14856
|
var MAX_PENDING = 200;
|
|
@@ -14835,7 +14892,7 @@ function watchDocs(cwd, listener) {
|
|
|
14835
14892
|
const current = entries.get(cwd);
|
|
14836
14893
|
if (!current || filename === null) return;
|
|
14837
14894
|
const rel = filename.split("\\").join("/");
|
|
14838
|
-
if (rel === root && !
|
|
14895
|
+
if (rel === root && !existsSync23(join18(cwd, rel))) return;
|
|
14839
14896
|
if (isIgnoredDocPath(rel)) return;
|
|
14840
14897
|
if (current.pending.size < MAX_PENDING) current.pending.add(rel);
|
|
14841
14898
|
if (current.timer === null) current.timer = setTimeout(() => flush(cwd), DEBOUNCE_MS);
|
|
@@ -15072,7 +15129,7 @@ async function readOrQueueDocAudio(input2) {
|
|
|
15072
15129
|
}
|
|
15073
15130
|
|
|
15074
15131
|
// src/dashboard/artifact-export.ts
|
|
15075
|
-
import { readFileSync as
|
|
15132
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
15076
15133
|
|
|
15077
15134
|
// src/dashboard/chat-agent.ts
|
|
15078
15135
|
import { homedir as homedir9 } from "os";
|
|
@@ -15290,7 +15347,7 @@ async function withMcpConfig(profile, connectionIds, fn) {
|
|
|
15290
15347
|
}
|
|
15291
15348
|
|
|
15292
15349
|
// src/claude/agents.ts
|
|
15293
|
-
import { existsSync as
|
|
15350
|
+
import { existsSync as existsSync24, readdirSync as readdirSync4, readFileSync as readFileSync18 } from "fs";
|
|
15294
15351
|
import { homedir as homedir8 } from "os";
|
|
15295
15352
|
import { basename as basename7, join as join20 } from "path";
|
|
15296
15353
|
import yaml2 from "js-yaml";
|
|
@@ -15305,7 +15362,7 @@ function expandHome(p) {
|
|
|
15305
15362
|
function parseFrontmatter(file) {
|
|
15306
15363
|
let content;
|
|
15307
15364
|
try {
|
|
15308
|
-
content =
|
|
15365
|
+
content = readFileSync18(file, "utf-8");
|
|
15309
15366
|
} catch {
|
|
15310
15367
|
return {};
|
|
15311
15368
|
}
|
|
@@ -15321,7 +15378,7 @@ function parseFrontmatter(file) {
|
|
|
15321
15378
|
}
|
|
15322
15379
|
}
|
|
15323
15380
|
function collectFrom(root, source) {
|
|
15324
|
-
if (!
|
|
15381
|
+
if (!existsSync24(root)) return [];
|
|
15325
15382
|
let entries2;
|
|
15326
15383
|
try {
|
|
15327
15384
|
entries2 = readdirSync4(root).filter((f) => f.endsWith(".md"));
|
|
@@ -15351,7 +15408,7 @@ function readAgentPersona(configDir2, cwd, name) {
|
|
|
15351
15408
|
const agent = listAgents(configDir2, cwd).find((a) => a.name === name);
|
|
15352
15409
|
if (!agent) return void 0;
|
|
15353
15410
|
try {
|
|
15354
|
-
const content =
|
|
15411
|
+
const content = readFileSync18(agent.path, "utf-8");
|
|
15355
15412
|
return content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
15356
15413
|
} catch {
|
|
15357
15414
|
return void 0;
|
|
@@ -15360,7 +15417,7 @@ function readAgentPersona(configDir2, cwd, name) {
|
|
|
15360
15417
|
|
|
15361
15418
|
// src/dashboard/memory-extraction-runner.ts
|
|
15362
15419
|
import { spawn as spawn4 } from "child_process";
|
|
15363
|
-
import { mkdtempSync as mkdtempSync2, readFileSync as
|
|
15420
|
+
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync19, rmSync as rmSync7 } from "fs";
|
|
15364
15421
|
import { tmpdir as tmpdir4 } from "os";
|
|
15365
15422
|
import { join as join21 } from "path";
|
|
15366
15423
|
var CLAUDE_MODEL = "sonnet";
|
|
@@ -15452,7 +15509,7 @@ function run3(inv, cwd) {
|
|
|
15452
15509
|
let raw = stdout;
|
|
15453
15510
|
if (inv.outputFile) {
|
|
15454
15511
|
try {
|
|
15455
|
-
raw =
|
|
15512
|
+
raw = readFileSync19(inv.outputFile, "utf-8");
|
|
15456
15513
|
} catch {
|
|
15457
15514
|
raw = "";
|
|
15458
15515
|
}
|
|
@@ -15769,23 +15826,43 @@ function extractAttachmentKeys(body) {
|
|
|
15769
15826
|
}
|
|
15770
15827
|
return keys;
|
|
15771
15828
|
}
|
|
15829
|
+
async function fetchObject(key) {
|
|
15830
|
+
if (process.env["CLOUDFLARE_R2_ACCESS_KEY_ID"]) {
|
|
15831
|
+
const response2 = await getR2Client().send(
|
|
15832
|
+
new GetObjectCommand5({ Bucket: getR2Bucket(), Key: key })
|
|
15833
|
+
);
|
|
15834
|
+
return {
|
|
15835
|
+
mimetype: response2.ContentType ?? "application/octet-stream",
|
|
15836
|
+
contentLength: response2.ContentLength,
|
|
15837
|
+
read: async () => response2.Body?.transformToByteArray()
|
|
15838
|
+
};
|
|
15839
|
+
}
|
|
15840
|
+
const url = await requestAttachmentUrl(key);
|
|
15841
|
+
if (!url) throw new Error("Data API refused the attachment");
|
|
15842
|
+
const response = await fetch(url);
|
|
15843
|
+
if (!response.ok) throw new Error(`presigned GET answered ${response.status}`);
|
|
15844
|
+
const length = response.headers.get("content-length");
|
|
15845
|
+
return {
|
|
15846
|
+
mimetype: response.headers.get("content-type") ?? "application/octet-stream",
|
|
15847
|
+
contentLength: length === null ? void 0 : Number(length),
|
|
15848
|
+
read: async () => new Uint8Array(await response.arrayBuffer())
|
|
15849
|
+
};
|
|
15850
|
+
}
|
|
15772
15851
|
async function downloadOne(key) {
|
|
15773
15852
|
const lastSegment = key.slice(key.lastIndexOf("/") + 1);
|
|
15774
15853
|
const name = displayName(lastSegment);
|
|
15775
15854
|
try {
|
|
15776
|
-
const response = await
|
|
15777
|
-
|
|
15778
|
-
);
|
|
15779
|
-
const mimetype = response.ContentType ?? "application/octet-stream";
|
|
15855
|
+
const response = await fetchObject(key);
|
|
15856
|
+
const mimetype = response.mimetype;
|
|
15780
15857
|
const sizeLimit = mimetypeSizeLimit(mimetype);
|
|
15781
15858
|
if (sizeLimit === null) {
|
|
15782
15859
|
if (debug) console.log(`[attachment-downloader] unsupported mimetype: ${mimetype} (${key})`);
|
|
15783
15860
|
return { kind: "skip", skipped: { name, mimetype, reason: "unsupported" } };
|
|
15784
15861
|
}
|
|
15785
|
-
if (response.
|
|
15862
|
+
if (response.contentLength !== void 0 && response.contentLength > sizeLimit) {
|
|
15786
15863
|
return { kind: "skip", skipped: { name, mimetype, reason: "too_large" } };
|
|
15787
15864
|
}
|
|
15788
|
-
const bytes = await response.
|
|
15865
|
+
const bytes = await response.read();
|
|
15789
15866
|
if (!bytes) {
|
|
15790
15867
|
return { kind: "skip", skipped: { name, mimetype, reason: "download_failed" } };
|
|
15791
15868
|
}
|
|
@@ -15817,11 +15894,11 @@ async function downloadChatAttachments(body) {
|
|
|
15817
15894
|
}
|
|
15818
15895
|
|
|
15819
15896
|
// src/dashboard/doc-mention-guard.ts
|
|
15820
|
-
import { existsSync as
|
|
15897
|
+
import { existsSync as existsSync25, statSync as statSync5 } from "fs";
|
|
15821
15898
|
var DOC_MENTION_RE = /(?<![\w@/`])@((?:[\w.-]+\/)+[\w.-]+\.[A-Za-z0-9]{1,6})(?::(\d+))?(?![\w-])/g;
|
|
15822
15899
|
function isRealDoc(root, rel) {
|
|
15823
15900
|
const abs = resolveDocPath(root, rel);
|
|
15824
|
-
if (!abs || !
|
|
15901
|
+
if (!abs || !existsSync25(abs)) return false;
|
|
15825
15902
|
try {
|
|
15826
15903
|
return statSync5(abs).isFile();
|
|
15827
15904
|
} catch {
|
|
@@ -17091,7 +17168,7 @@ async function exportDocAsArtifact(channelId, docPath) {
|
|
|
17091
17168
|
);
|
|
17092
17169
|
const abs = resolveDocPath(channelConfig.cwd, docPath);
|
|
17093
17170
|
if (!abs) throw new Error(`doc not found: ${docPath}`);
|
|
17094
|
-
const content =
|
|
17171
|
+
const content = readFileSync20(abs, "utf8");
|
|
17095
17172
|
const title = extractTitle(abs, "md");
|
|
17096
17173
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
17097
17174
|
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 +17449,12 @@ function readGitBranch(dir) {
|
|
|
17372
17449
|
try {
|
|
17373
17450
|
let gitDir = join23(dir, ".git");
|
|
17374
17451
|
if (statSync6(gitDir).isFile()) {
|
|
17375
|
-
const pointer =
|
|
17452
|
+
const pointer = readFileSync21(gitDir, "utf8").trim();
|
|
17376
17453
|
const match = pointer.match(/^gitdir:\s*(.+)$/);
|
|
17377
17454
|
if (!match) return void 0;
|
|
17378
17455
|
gitDir = resolve5(dir, match[1]);
|
|
17379
17456
|
}
|
|
17380
|
-
const head =
|
|
17457
|
+
const head = readFileSync21(join23(gitDir, "HEAD"), "utf8").trim();
|
|
17381
17458
|
const branchMatch = head.match(/^ref:\s*refs\/heads\/(.+)$/);
|
|
17382
17459
|
if (branchMatch) return branchMatch[1];
|
|
17383
17460
|
return /^[0-9a-f]{7,40}$/i.test(head) ? head.slice(0, 7) : void 0;
|
|
@@ -17406,7 +17483,7 @@ function listDocLevel(root, relDir = "", sort = "newest", showHidden = false) {
|
|
|
17406
17483
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
17407
17484
|
const probe3 = probeDir(abs, showHidden);
|
|
17408
17485
|
if (!probe3.hasDocs && probe3.hasOther) continue;
|
|
17409
|
-
const isRepo =
|
|
17486
|
+
const isRepo = existsSync26(join23(abs, ".git"));
|
|
17410
17487
|
dirs.push({
|
|
17411
17488
|
path,
|
|
17412
17489
|
name: entry.name,
|
|
@@ -17612,7 +17689,7 @@ function moveDocEntry(root, from, to) {
|
|
|
17612
17689
|
return { status: "bad-target" };
|
|
17613
17690
|
}
|
|
17614
17691
|
}
|
|
17615
|
-
const absToDir =
|
|
17692
|
+
const absToDir = dirname8(absTo);
|
|
17616
17693
|
if (resolveDirPath(root, relative(root, absToDir)) === null) return { status: "bad-target" };
|
|
17617
17694
|
try {
|
|
17618
17695
|
if (!statSync6(absToDir).isDirectory()) return { status: "bad-target" };
|
|
@@ -17703,7 +17780,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
|
|
|
17703
17780
|
}
|
|
17704
17781
|
if (!stat.isFile()) return c.json({ error: "not found" }, 404);
|
|
17705
17782
|
if (stat.size > MAX_RAW_BYTES) return c.json({ error: "file too large" }, 413);
|
|
17706
|
-
return c.body(withDocLinkBridge(
|
|
17783
|
+
return c.body(withDocLinkBridge(readFileSync21(abs, "utf8")), 200, {
|
|
17707
17784
|
"Content-Type": "text/html; charset=utf-8",
|
|
17708
17785
|
"Content-Security-Policy": HTML_SHARE_CSP,
|
|
17709
17786
|
"X-Content-Type-Options": "nosniff",
|
|
@@ -17888,7 +17965,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
|
|
|
17888
17965
|
title: kind ? extractTitle(abs, kind) : basename8(abs),
|
|
17889
17966
|
mtime: stat.mtime.toISOString(),
|
|
17890
17967
|
tooLarge: tooLarge || void 0,
|
|
17891
|
-
content: tooLarge ? "" :
|
|
17968
|
+
content: tooLarge ? "" : readFileSync21(abs, "utf8")
|
|
17892
17969
|
});
|
|
17893
17970
|
});
|
|
17894
17971
|
app.put("/:id/docs/content", async (c) => {
|
|
@@ -17931,7 +18008,7 @@ function createDocsRouter({ gate = requireDocsAccess } = {}) {
|
|
|
17931
18008
|
channel,
|
|
17932
18009
|
rel,
|
|
17933
18010
|
title: extractTitle(abs, "md"),
|
|
17934
|
-
content:
|
|
18011
|
+
content: readFileSync21(abs, "utf8")
|
|
17935
18012
|
};
|
|
17936
18013
|
};
|
|
17937
18014
|
app.get("/:id/docs/translation/:lang", async (c) => {
|
|
@@ -17986,8 +18063,8 @@ function withDocLinkBridge(html) {
|
|
|
17986
18063
|
}
|
|
17987
18064
|
|
|
17988
18065
|
// src/dashboard/skills-routes-host.ts
|
|
17989
|
-
import { existsSync as
|
|
17990
|
-
import { dirname as
|
|
18066
|
+
import { existsSync as existsSync27, statSync as statSync7 } from "fs";
|
|
18067
|
+
import { dirname as dirname9 } from "path";
|
|
17991
18068
|
import { Hono as Hono32 } from "hono";
|
|
17992
18069
|
function workspaceScope() {
|
|
17993
18070
|
const claudeConfigDir = profileClaudeConfigDir();
|
|
@@ -17995,7 +18072,7 @@ function workspaceScope() {
|
|
|
17995
18072
|
return {
|
|
17996
18073
|
dir: workspaceSkillsDir(claudeConfigDir),
|
|
17997
18074
|
// `~/.claude` itself, which exists wherever the CLI has ever run.
|
|
17998
|
-
root:
|
|
18075
|
+
root: dirname9(workspaceSkillsDir(claudeConfigDir)),
|
|
17999
18076
|
cwd,
|
|
18000
18077
|
claudeConfigDir,
|
|
18001
18078
|
// Only plugin skills. Project skills belong to a specific channel's cwd, so
|
|
@@ -18021,7 +18098,7 @@ function inheritedSkills(scope, own) {
|
|
|
18021
18098
|
}
|
|
18022
18099
|
function rootExists(dir) {
|
|
18023
18100
|
try {
|
|
18024
|
-
return
|
|
18101
|
+
return existsSync27(dir) && statSync7(dir).isDirectory();
|
|
18025
18102
|
} catch {
|
|
18026
18103
|
return false;
|
|
18027
18104
|
}
|
|
@@ -18131,7 +18208,7 @@ async function resolveAgentIdentities() {
|
|
|
18131
18208
|
|
|
18132
18209
|
// ../api/src/dashboard/providers.ts
|
|
18133
18210
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
18134
|
-
import { existsSync as
|
|
18211
|
+
import { existsSync as existsSync28, readFileSync as readFileSync22 } from "fs";
|
|
18135
18212
|
import { homedir as homedir10 } from "os";
|
|
18136
18213
|
import { join as join24, resolve as resolve6 } from "path";
|
|
18137
18214
|
var PROVIDER_LABELS = {
|
|
@@ -18154,7 +18231,7 @@ function binaryOnPath(bin) {
|
|
|
18154
18231
|
function claudeAuthAvailable() {
|
|
18155
18232
|
if (profileEnv("CLAUDE_CODE_OAUTH_TOKEN")?.trim()) return true;
|
|
18156
18233
|
const configDir2 = profileClaudeConfigDir() || resolve6(homedir10(), ".claude");
|
|
18157
|
-
if (
|
|
18234
|
+
if (existsSync28(join24(configDir2, ".credentials.json"))) return true;
|
|
18158
18235
|
if (process.platform !== "darwin") return false;
|
|
18159
18236
|
try {
|
|
18160
18237
|
execFileSync4("security", ["find-generic-password", "-s", "Claude Code-credentials"], {
|
|
@@ -18200,7 +18277,7 @@ function claudeAccount() {
|
|
|
18200
18277
|
function codexAccount() {
|
|
18201
18278
|
const home2 = process.env["CODEX_HOME"]?.trim() || join24(homedir10(), ".codex");
|
|
18202
18279
|
try {
|
|
18203
|
-
const auth = JSON.parse(
|
|
18280
|
+
const auth = JSON.parse(readFileSync22(join24(home2, "auth.json"), "utf8"));
|
|
18204
18281
|
const payload = auth.tokens?.id_token?.split(".")[1];
|
|
18205
18282
|
if (!payload) return void 0;
|
|
18206
18283
|
const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
@@ -18231,9 +18308,9 @@ function providerStatuses() {
|
|
|
18231
18308
|
// src/dashboard/provider-login-host.ts
|
|
18232
18309
|
import { spawn as spawn6 } from "child_process";
|
|
18233
18310
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
18234
|
-
import { chmodSync, existsSync as
|
|
18311
|
+
import { chmodSync, existsSync as existsSync29, mkdirSync as mkdirSync12, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "fs";
|
|
18235
18312
|
import { createRequire } from "module";
|
|
18236
|
-
import { dirname as
|
|
18313
|
+
import { dirname as dirname10, join as join25 } from "path";
|
|
18237
18314
|
import { Hono as Hono33 } from "hono";
|
|
18238
18315
|
var SESSION_TTL_MS = 10 * 6e4;
|
|
18239
18316
|
var URL_WAIT_MS = 3e4;
|
|
@@ -18296,10 +18373,10 @@ function waitFor(s, parse, ms) {
|
|
|
18296
18373
|
}
|
|
18297
18374
|
function loadPty() {
|
|
18298
18375
|
const require2 = createRequire(import.meta.url);
|
|
18299
|
-
const root =
|
|
18376
|
+
const root = dirname10(require2.resolve("node-pty/package.json"));
|
|
18300
18377
|
const helper = join25(root, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper");
|
|
18301
18378
|
try {
|
|
18302
|
-
if (
|
|
18379
|
+
if (existsSync29(helper)) chmodSync(helper, 493);
|
|
18303
18380
|
} catch {
|
|
18304
18381
|
}
|
|
18305
18382
|
return require2("node-pty");
|
|
@@ -18339,8 +18416,8 @@ async function startClaude() {
|
|
|
18339
18416
|
return { id, url };
|
|
18340
18417
|
}
|
|
18341
18418
|
function saveClaudeToken(configFile, token) {
|
|
18342
|
-
mkdirSync12(
|
|
18343
|
-
const current =
|
|
18419
|
+
mkdirSync12(dirname10(configFile), { recursive: true });
|
|
18420
|
+
const current = existsSync29(configFile) ? readFileSync23(configFile, "utf-8") : "";
|
|
18344
18421
|
writeFileSync12(configFile, upsertEnvLine(current, "CLAUDE_CODE_OAUTH_TOKEN", token), { mode: 384 });
|
|
18345
18422
|
const cached = cachedProfileConfig();
|
|
18346
18423
|
if (cached) cached.env["CLAUDE_CODE_OAUTH_TOKEN"] = token;
|
|
@@ -18557,9 +18634,8 @@ function registerHostRoutes(api) {
|
|
|
18557
18634
|
api.get("/logs", (c) => {
|
|
18558
18635
|
const errors = c.req.query("error") === "true" || c.req.query("error") === "1";
|
|
18559
18636
|
const limit = Math.min(Number(c.req.query("limit")) || 200, 2e3);
|
|
18560
|
-
const
|
|
18561
|
-
const
|
|
18562
|
-
const exists = existsSync29(file);
|
|
18637
|
+
const file = serviceLogFile(errors);
|
|
18638
|
+
const exists = existsSync30(file);
|
|
18563
18639
|
return c.json({
|
|
18564
18640
|
file,
|
|
18565
18641
|
exists,
|
|
@@ -18569,8 +18645,8 @@ function registerHostRoutes(api) {
|
|
|
18569
18645
|
});
|
|
18570
18646
|
}
|
|
18571
18647
|
function tailFile(file, limit) {
|
|
18572
|
-
if (!
|
|
18573
|
-
const lines =
|
|
18648
|
+
if (!existsSync30(file)) return [];
|
|
18649
|
+
const lines = readFileSync24(file, "utf-8").split("\n");
|
|
18574
18650
|
if (lines.at(-1) === "") lines.pop();
|
|
18575
18651
|
return lines.slice(-limit);
|
|
18576
18652
|
}
|
|
@@ -18588,7 +18664,7 @@ function createHostApiRouter(agent = {}) {
|
|
|
18588
18664
|
|
|
18589
18665
|
// src/dashboard/agent-bridge-host.ts
|
|
18590
18666
|
import { sign as signRaw, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
18591
|
-
import { existsSync as
|
|
18667
|
+
import { existsSync as existsSync31, readFileSync as readFileSync26 } from "fs";
|
|
18592
18668
|
import dotenv2 from "dotenv";
|
|
18593
18669
|
import { WebSocket as WebSocket2 } from "ws";
|
|
18594
18670
|
|
|
@@ -18622,7 +18698,7 @@ async function handleHostRequest(req) {
|
|
|
18622
18698
|
}
|
|
18623
18699
|
|
|
18624
18700
|
// src/dashboard/doc-share-publisher.ts
|
|
18625
|
-
import { readFileSync as
|
|
18701
|
+
import { readFileSync as readFileSync25, statSync as statSync9 } from "fs";
|
|
18626
18702
|
import { extname as extname2, posix } from "path";
|
|
18627
18703
|
var MAX_ASSET_BYTES = 20 * 1024 * 1024;
|
|
18628
18704
|
var MAX_CONTENT_BYTES2 = 2 * 1024 * 1024;
|
|
@@ -18737,7 +18813,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
18737
18813
|
}
|
|
18738
18814
|
if (!stat.isFile()) return { status: "not-found" };
|
|
18739
18815
|
if (stat.size > MAX_CONTENT_BYTES2) return { status: "too-large" };
|
|
18740
|
-
const source =
|
|
18816
|
+
const source = readFileSync25(abs, "utf8");
|
|
18741
18817
|
const title = extractTitle(abs, kind === "markdown" ? "md" : kind);
|
|
18742
18818
|
if (kind === "excalidraw") {
|
|
18743
18819
|
try {
|
|
@@ -18808,7 +18884,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
18808
18884
|
let text = null;
|
|
18809
18885
|
try {
|
|
18810
18886
|
const size = statSync9(docAbs).size;
|
|
18811
|
-
if (size <= budget) text =
|
|
18887
|
+
if (size <= budget) text = readFileSync25(docAbs, "utf8");
|
|
18812
18888
|
} catch {
|
|
18813
18889
|
continue;
|
|
18814
18890
|
}
|
|
@@ -18825,7 +18901,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
18825
18901
|
let body;
|
|
18826
18902
|
try {
|
|
18827
18903
|
if (statSync9(resolved2.abs).size > MAX_ASSET_BYTES) continue;
|
|
18828
|
-
body =
|
|
18904
|
+
body = readFileSync25(resolved2.abs);
|
|
18829
18905
|
} catch {
|
|
18830
18906
|
continue;
|
|
18831
18907
|
}
|
|
@@ -18854,12 +18930,12 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
18854
18930
|
// src/dashboard/host-upgrade.ts
|
|
18855
18931
|
import { spawn as spawn7 } from "child_process";
|
|
18856
18932
|
import { mkdirSync as mkdirSync13, openSync as openSync3 } from "fs";
|
|
18857
|
-
import { join as
|
|
18933
|
+
import { join as join26 } from "path";
|
|
18858
18934
|
var running = false;
|
|
18859
18935
|
function startHostUpgrade() {
|
|
18860
18936
|
if (running) return { started: false };
|
|
18861
18937
|
mkdirSync13(paths.LOG_DIR, { recursive: true });
|
|
18862
|
-
const log5 = openSync3(
|
|
18938
|
+
const log5 = openSync3(join26(paths.LOG_DIR, "host-upgrade.log"), "a");
|
|
18863
18939
|
const child = spawn7(process.execPath, [process.argv[1] ?? "", "upgrade"], {
|
|
18864
18940
|
detached: true,
|
|
18865
18941
|
stdio: ["ignore", log5, log5]
|
|
@@ -18880,7 +18956,7 @@ function dataSocketUrl() {
|
|
|
18880
18956
|
return `ws://127.0.0.1:${process.env["FLYSPACE_DATA_API_PORT"] ?? DEFAULT_DATA_API_PORT}${HOST_SOCKET_PATH}`;
|
|
18881
18957
|
}
|
|
18882
18958
|
function listLocalSessions() {
|
|
18883
|
-
if (!
|
|
18959
|
+
if (!existsSync31(paths.DB_FILE)) return [];
|
|
18884
18960
|
const store = new SessionStore(paths.DB_FILE);
|
|
18885
18961
|
try {
|
|
18886
18962
|
return store.listSessions();
|
|
@@ -18922,7 +18998,7 @@ function buildHostConfigSnapshot() {
|
|
|
18922
18998
|
const snapshot2 = {};
|
|
18923
18999
|
for (const profile of listProfiles()) {
|
|
18924
19000
|
snapshot2[profile] = runWithProfile(profile, () => ({
|
|
18925
|
-
env:
|
|
19001
|
+
env: existsSync31(paths.CONFIG_FILE) ? dotenv2.parse(readFileSync26(paths.CONFIG_FILE, "utf-8")) : {},
|
|
18926
19002
|
agentModel: loadDashboardSettings().agent
|
|
18927
19003
|
}));
|
|
18928
19004
|
}
|
|
@@ -19002,6 +19078,10 @@ function connectHostSocket(url, callbacks, onAuthenticated, onWorkspaces) {
|
|
|
19002
19078
|
handleHostDbResult(msg.id, msg.res);
|
|
19003
19079
|
return;
|
|
19004
19080
|
}
|
|
19081
|
+
if (msg.type === "attachment-url-result") {
|
|
19082
|
+
handleAttachmentUrlResult(msg.id, msg.url);
|
|
19083
|
+
return;
|
|
19084
|
+
}
|
|
19005
19085
|
if (msg.type === "call") {
|
|
19006
19086
|
void handleCall(msg, callbacks, ws);
|
|
19007
19087
|
}
|
|
@@ -19168,8 +19248,8 @@ function startPushWatcher() {
|
|
|
19168
19248
|
}
|
|
19169
19249
|
|
|
19170
19250
|
// src/dashboard/machine-server.ts
|
|
19171
|
-
import { existsSync as
|
|
19172
|
-
import { join as
|
|
19251
|
+
import { existsSync as existsSync32 } from "fs";
|
|
19252
|
+
import { join as join27 } from "path";
|
|
19173
19253
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
19174
19254
|
|
|
19175
19255
|
// src/dashboard/app-icon-routes.ts
|
|
@@ -19251,7 +19331,7 @@ function pngResponse(c, png) {
|
|
|
19251
19331
|
}
|
|
19252
19332
|
|
|
19253
19333
|
// src/dashboard/machine-server.ts
|
|
19254
|
-
var WEB_DIST =
|
|
19334
|
+
var WEB_DIST = join27(paths.WEB_ROOT, "dist");
|
|
19255
19335
|
async function bootMachine() {
|
|
19256
19336
|
await reconcileAll().catch(
|
|
19257
19337
|
(err) => console.error("[dashboard] reconcileAll failed:", err.message)
|
|
@@ -19271,14 +19351,14 @@ async function bootMachine() {
|
|
|
19271
19351
|
function mountSpa(app) {
|
|
19272
19352
|
app.route("/share", createPublicShareAssetRouter(WEB_DIST));
|
|
19273
19353
|
app.route("/", createAppIconRouter());
|
|
19274
|
-
if (
|
|
19354
|
+
if (existsSync32(WEB_DIST) || existsSync32(join27(paths.WEB_ROOT, "package.json"))) {
|
|
19275
19355
|
app.use("/*", serveStatic({ root: WEB_DIST }));
|
|
19276
19356
|
}
|
|
19277
19357
|
app.get("/assets/*", (c) => c.text("stale asset \u2014 reload for the current build", 404));
|
|
19278
|
-
const spaFallback = serveStatic({ path:
|
|
19358
|
+
const spaFallback = serveStatic({ path: join27(WEB_DIST, "index.html") });
|
|
19279
19359
|
app.get("*", async (c, next) => {
|
|
19280
|
-
if (!
|
|
19281
|
-
const isCheckout =
|
|
19360
|
+
if (!existsSync32(join27(WEB_DIST, "index.html"))) {
|
|
19361
|
+
const isCheckout = existsSync32(join27(paths.WEB_ROOT, "package.json"));
|
|
19282
19362
|
return c.text(
|
|
19283
19363
|
isCheckout ? `Workspace UI build missing (${WEB_DIST}).
|
|
19284
19364
|
Run \`pnpm build:web\` from the repo root.
|
|
@@ -19412,8 +19492,8 @@ function openBrowser2(url) {
|
|
|
19412
19492
|
}
|
|
19413
19493
|
}
|
|
19414
19494
|
function ensureWebBuild() {
|
|
19415
|
-
if (
|
|
19416
|
-
if (!
|
|
19495
|
+
if (existsSync33(join28(paths.WEB_ROOT, "dist", "index.html"))) return;
|
|
19496
|
+
if (!existsSync33(join28(paths.WEB_ROOT, "package.json"))) return;
|
|
19417
19497
|
console.log(" packages/ui/dist missing, building the dashboard UI...");
|
|
19418
19498
|
const result = spawnSync("pnpm", ["build"], {
|
|
19419
19499
|
cwd: paths.WEB_ROOT,
|
|
@@ -19463,7 +19543,7 @@ async function runDashboard(args) {
|
|
|
19463
19543
|
}
|
|
19464
19544
|
|
|
19465
19545
|
// src/commands/workspace.ts
|
|
19466
|
-
import { existsSync as
|
|
19546
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
|
|
19467
19547
|
var USAGE7 = `
|
|
19468
19548
|
Usage: flyspace workspace <subcommand> [options]
|
|
19469
19549
|
|
|
@@ -19549,7 +19629,7 @@ function provisionLocal(botName) {
|
|
|
19549
19629
|
ensureDirs();
|
|
19550
19630
|
console.log(` config dir ready (${paths.CONFIG_DIR})`);
|
|
19551
19631
|
console.log(` data dir ready (${paths.DATA_DIR})`);
|
|
19552
|
-
if (
|
|
19632
|
+
if (existsSync34(paths.CONFIG_FILE)) {
|
|
19553
19633
|
console.log(" config.env exists");
|
|
19554
19634
|
} else {
|
|
19555
19635
|
writeFileSync13(paths.CONFIG_FILE, `BOT_NAME=${botName}
|
|
@@ -19566,7 +19646,7 @@ async function provisionChannels(specs) {
|
|
|
19566
19646
|
continue;
|
|
19567
19647
|
}
|
|
19568
19648
|
const cwd = expandPath(spec.cwd);
|
|
19569
|
-
if (!
|
|
19649
|
+
if (!existsSync34(cwd)) {
|
|
19570
19650
|
mkdirSync14(cwd, { recursive: true });
|
|
19571
19651
|
console.log(` created cwd ${cwd}`);
|
|
19572
19652
|
}
|
|
@@ -19644,10 +19724,10 @@ async function runReconcileCmd(args) {
|
|
|
19644
19724
|
}
|
|
19645
19725
|
|
|
19646
19726
|
// src/cli.ts
|
|
19647
|
-
import { join as
|
|
19727
|
+
import { join as join29 } from "path";
|
|
19648
19728
|
var home = flyspaceHome();
|
|
19649
19729
|
if (home) {
|
|
19650
|
-
const bin =
|
|
19730
|
+
const bin = join29(home, "bin");
|
|
19651
19731
|
const path = process.env.PATH ?? "";
|
|
19652
19732
|
if (!path.split(":").includes(bin)) process.env.PATH = path ? `${bin}:${path}` : bin;
|
|
19653
19733
|
}
|
|
@@ -19785,7 +19865,7 @@ async function main() {
|
|
|
19785
19865
|
break;
|
|
19786
19866
|
case "-v":
|
|
19787
19867
|
case "--version":
|
|
19788
|
-
printRaw("0.1.
|
|
19868
|
+
printRaw("0.1.32");
|
|
19789
19869
|
break;
|
|
19790
19870
|
case "-h":
|
|
19791
19871
|
case "--help":
|