flyspace-cli 0.1.33 → 0.1.34
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/{chunk-KGX44L3V.js → chunk-KTMAS4TP.js} +10 -1
- package/dist/cli.js +569 -88
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -295,6 +295,15 @@ function resolveLogicalCwd(p) {
|
|
|
295
295
|
function isUnderLogicalRoot(cwd) {
|
|
296
296
|
return [logicalRoot(), ...workspaceRoots.values()].some((r) => cwd.startsWith(r + sep));
|
|
297
297
|
}
|
|
298
|
+
function toLogicalCwd(cwd) {
|
|
299
|
+
const fromRoot = relative(logicalRoot(), cwd).split(sep).join("/");
|
|
300
|
+
if (isLogicalCwd(fromRoot)) return fromRoot;
|
|
301
|
+
for (const [slug, root] of workspaceRoots) {
|
|
302
|
+
const name = relative(root, cwd);
|
|
303
|
+
if (isLogicalCwd(`${slug}/${name}`)) return `${slug}/${name}`;
|
|
304
|
+
}
|
|
305
|
+
return cwd;
|
|
306
|
+
}
|
|
298
307
|
function ensureLogicalCwdDir(cwd) {
|
|
299
308
|
if (!isUnderLogicalRoot(cwd) || existsSync2(cwd)) return;
|
|
300
309
|
mkdirSync(cwd, { recursive: true });
|
|
@@ -522,7 +531,7 @@ function resolveEffort(explicitEffort, channelId, fallback) {
|
|
|
522
531
|
function toRow(input) {
|
|
523
532
|
const row = {};
|
|
524
533
|
if (input.name !== void 0) row["name"] = input.name;
|
|
525
|
-
if (input.cwd !== void 0) row["cwd"] = isLogicalCwd(input.cwd) ? input.cwd : expandPath(input.cwd);
|
|
534
|
+
if (input.cwd !== void 0) row["cwd"] = isLogicalCwd(input.cwd) ? input.cwd : toLogicalCwd(expandPath(input.cwd));
|
|
526
535
|
if (input.categoryId !== void 0) row["category_id"] = input.categoryId;
|
|
527
536
|
if (input.externalChannelId !== void 0) row["external_channel_id"] = input.externalChannelId;
|
|
528
537
|
if (input.model !== void 0) row["model"] = input.model;
|
package/dist/cli.js
CHANGED
|
@@ -187,7 +187,7 @@ import {
|
|
|
187
187
|
warnOnBrokenChannelCwds,
|
|
188
188
|
workspaceSkillsDir,
|
|
189
189
|
writeSkillFile
|
|
190
|
-
} from "./chunk-
|
|
190
|
+
} from "./chunk-KTMAS4TP.js";
|
|
191
191
|
import {
|
|
192
192
|
firstReplyBody,
|
|
193
193
|
getThread,
|
|
@@ -621,14 +621,14 @@ function handleHostDbResult(id, res) {
|
|
|
621
621
|
}
|
|
622
622
|
function whenConnected() {
|
|
623
623
|
if (sender) return Promise.resolve(sender);
|
|
624
|
-
return new Promise((
|
|
624
|
+
return new Promise((resolve8, reject) => {
|
|
625
625
|
const timer2 = setTimeout(() => {
|
|
626
626
|
onSender = onSender.filter((w) => w !== wake);
|
|
627
627
|
reject(new Error("Host is not connected to the Data API"));
|
|
628
628
|
}, REQUEST_TIMEOUT_MS);
|
|
629
629
|
const wake = () => {
|
|
630
630
|
clearTimeout(timer2);
|
|
631
|
-
|
|
631
|
+
resolve8(sender);
|
|
632
632
|
};
|
|
633
633
|
onSender.push(wake);
|
|
634
634
|
});
|
|
@@ -636,12 +636,12 @@ function whenConnected() {
|
|
|
636
636
|
async function requestHostDb(req) {
|
|
637
637
|
const send2 = await whenConnected();
|
|
638
638
|
const id = randomUUID();
|
|
639
|
-
return new Promise((
|
|
639
|
+
return new Promise((resolve8, reject) => {
|
|
640
640
|
const timer2 = setTimeout(() => {
|
|
641
641
|
pending.delete(id);
|
|
642
642
|
reject(new Error("Data API did not answer in time"));
|
|
643
643
|
}, REQUEST_TIMEOUT_MS);
|
|
644
|
-
pending.set(id, { resolve:
|
|
644
|
+
pending.set(id, { resolve: resolve8, reject, timer: timer2 });
|
|
645
645
|
send2({ type: "db", id, req });
|
|
646
646
|
});
|
|
647
647
|
}
|
|
@@ -653,14 +653,14 @@ function handleAttachmentUrlResult(id, url) {
|
|
|
653
653
|
async function requestAttachmentUrl(key) {
|
|
654
654
|
const send2 = await whenConnected();
|
|
655
655
|
const id = randomUUID();
|
|
656
|
-
return new Promise((
|
|
656
|
+
return new Promise((resolve8, reject) => {
|
|
657
657
|
const timer2 = setTimeout(() => {
|
|
658
658
|
pendingUrls.delete(id);
|
|
659
659
|
reject(new Error("Data API did not answer in time"));
|
|
660
660
|
}, REQUEST_TIMEOUT_MS);
|
|
661
661
|
pendingUrls.set(id, (url) => {
|
|
662
662
|
clearTimeout(timer2);
|
|
663
|
-
|
|
663
|
+
resolve8(url);
|
|
664
664
|
});
|
|
665
665
|
send2({ type: "attachment-url", id, key });
|
|
666
666
|
});
|
|
@@ -807,10 +807,10 @@ function editConfig() {
|
|
|
807
807
|
const editor = process.env["EDITOR"] || "vi";
|
|
808
808
|
console.log(`[flyspace] Opening ${paths.CONFIG_FILE} in ${editor}...`);
|
|
809
809
|
const child = spawn(editor, [paths.CONFIG_FILE], { stdio: "inherit" });
|
|
810
|
-
return new Promise((
|
|
810
|
+
return new Promise((resolve8, reject) => {
|
|
811
811
|
child.on("close", (code) => {
|
|
812
812
|
if (code === 0) {
|
|
813
|
-
|
|
813
|
+
resolve8();
|
|
814
814
|
} else {
|
|
815
815
|
reject(new Error(`Editor exited with code ${code}`));
|
|
816
816
|
}
|
|
@@ -1861,7 +1861,7 @@ async function runAuthCmd(args) {
|
|
|
1861
1861
|
}
|
|
1862
1862
|
|
|
1863
1863
|
// ../api/src/version.ts
|
|
1864
|
-
var CLI_VERSION = true ? "0.1.
|
|
1864
|
+
var CLI_VERSION = true ? "0.1.34" : null;
|
|
1865
1865
|
function isOlderVersion(a, b) {
|
|
1866
1866
|
const parse = (v) => v.replace(/^v/, "").split("-")[0].split(".").map(Number);
|
|
1867
1867
|
const [pa, pb] = [parse(a), parse(b)];
|
|
@@ -2471,14 +2471,19 @@ function npmEnv(target) {
|
|
|
2471
2471
|
return nodeBin ? { ...process.env, PATH: `${nodeBin}:${process.env.PATH ?? ""}` } : process.env;
|
|
2472
2472
|
}
|
|
2473
2473
|
async function runUpgrade() {
|
|
2474
|
-
const current = "0.1.
|
|
2474
|
+
const current = "0.1.34";
|
|
2475
2475
|
console.log(`Current version: ${current}`);
|
|
2476
2476
|
console.log("Checking for updates...\n");
|
|
2477
2477
|
try {
|
|
2478
2478
|
const latest2 = execSync6(`npm view ${"flyspace-cli"} version`, { encoding: "utf-8", env: npmEnv() }).trim();
|
|
2479
2479
|
const stale = installTargets().filter((t) => installedVersion(t) !== latest2);
|
|
2480
2480
|
if (stale.length === 0) {
|
|
2481
|
-
|
|
2481
|
+
if (current === latest2) {
|
|
2482
|
+
console.log(`Already on the latest version (${current}).`);
|
|
2483
|
+
return;
|
|
2484
|
+
}
|
|
2485
|
+
console.log(`${latest2} is installed but this process runs ${current}. Restarting...`);
|
|
2486
|
+
restartInstalledProfiles();
|
|
2482
2487
|
return;
|
|
2483
2488
|
}
|
|
2484
2489
|
console.log(`New version available: ${latest2}`);
|
|
@@ -4795,10 +4800,10 @@ var DEFAULT_DASHBOARD_PORT = 4747;
|
|
|
4795
4800
|
var DEFAULT_DASHBOARD_HOST = "127.0.0.1";
|
|
4796
4801
|
async function findFreePort(start, host = DEFAULT_DASHBOARD_HOST, attempts = 50) {
|
|
4797
4802
|
for (let port = start; port < start + attempts; port++) {
|
|
4798
|
-
const free = await new Promise((
|
|
4803
|
+
const free = await new Promise((resolve8) => {
|
|
4799
4804
|
const probe3 = createServer();
|
|
4800
|
-
probe3.once("error", () =>
|
|
4801
|
-
probe3.once("listening", () => probe3.close(() =>
|
|
4805
|
+
probe3.once("error", () => resolve8(false));
|
|
4806
|
+
probe3.once("listening", () => probe3.close(() => resolve8(true)));
|
|
4802
4807
|
probe3.listen(port, host);
|
|
4803
4808
|
});
|
|
4804
4809
|
if (free) return port;
|
|
@@ -5626,14 +5631,14 @@ async function stepBrowserTokens(botName) {
|
|
|
5626
5631
|
const appLink = page2.locator('a[href*="/apps/A"]').first();
|
|
5627
5632
|
const hasExistingApp = await appLink.isVisible({ timeout: 3e3 }).catch(() => false);
|
|
5628
5633
|
if (hasExistingApp) {
|
|
5629
|
-
const tokenPromise = new Promise((
|
|
5634
|
+
const tokenPromise = new Promise((resolve8) => {
|
|
5630
5635
|
page2.on("request", (req) => {
|
|
5631
5636
|
if (token) return;
|
|
5632
5637
|
const postData = req.postData() || "";
|
|
5633
5638
|
const match = postData.match(/xoxc-[a-f0-9-]+/);
|
|
5634
5639
|
if (match) {
|
|
5635
5640
|
token = match[0];
|
|
5636
|
-
|
|
5641
|
+
resolve8(token);
|
|
5637
5642
|
}
|
|
5638
5643
|
});
|
|
5639
5644
|
});
|
|
@@ -5684,7 +5689,7 @@ async function stepBrowserTokens(botName) {
|
|
|
5684
5689
|
log4("Almost done! The browser will now open an install page.\n");
|
|
5685
5690
|
console.log(' \x1B[1mPlease click "Install to Workspace", then click "Allow".\x1B[0m\n');
|
|
5686
5691
|
await page2.goto(installUrl);
|
|
5687
|
-
const installPage = await new Promise((
|
|
5692
|
+
const installPage = await new Promise((resolve8, reject) => {
|
|
5688
5693
|
let settled = false;
|
|
5689
5694
|
let interval;
|
|
5690
5695
|
let timeout;
|
|
@@ -5697,7 +5702,7 @@ async function stepBrowserTokens(botName) {
|
|
|
5697
5702
|
};
|
|
5698
5703
|
const checkUrl = (p) => {
|
|
5699
5704
|
if (p.url().includes("install-on-team?success=1")) {
|
|
5700
|
-
finish2(() =>
|
|
5705
|
+
finish2(() => resolve8(p));
|
|
5701
5706
|
}
|
|
5702
5707
|
};
|
|
5703
5708
|
context.on("page", (newPage) => {
|
|
@@ -6411,9 +6416,9 @@ async function runMcpCmd(args) {
|
|
|
6411
6416
|
|
|
6412
6417
|
// src/commands/dashboard.ts
|
|
6413
6418
|
import { spawn as spawn8, spawnSync } from "child_process";
|
|
6414
|
-
import { existsSync as
|
|
6419
|
+
import { existsSync as existsSync34 } from "fs";
|
|
6415
6420
|
import { platform as platform7 } from "os";
|
|
6416
|
-
import { join as
|
|
6421
|
+
import { join as join29 } from "path";
|
|
6417
6422
|
|
|
6418
6423
|
// src/reconcile.ts
|
|
6419
6424
|
import { existsSync as existsSync15, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
@@ -6586,7 +6591,7 @@ var requireDocsAccess = async (c, next) => {
|
|
|
6586
6591
|
};
|
|
6587
6592
|
|
|
6588
6593
|
// ../api/src/dashboard/api.ts
|
|
6589
|
-
import { Hono as
|
|
6594
|
+
import { Hono as Hono31 } from "hono";
|
|
6590
6595
|
|
|
6591
6596
|
// ../api/src/dashboard/public-share-routes.ts
|
|
6592
6597
|
import { existsSync as existsSync17, readFileSync as readFileSync16, statSync as statSync3 } from "fs";
|
|
@@ -7651,7 +7656,7 @@ function truncationNotice(lang, voice) {
|
|
|
7651
7656
|
if (lang === "en" || lang.startsWith("en-")) return TRUNCATION_NOTICE.en;
|
|
7652
7657
|
return null;
|
|
7653
7658
|
}
|
|
7654
|
-
var sleep2 = (ms) => new Promise((
|
|
7659
|
+
var sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
7655
7660
|
async function synthesizeWithRetry(script, voice) {
|
|
7656
7661
|
let lastError;
|
|
7657
7662
|
for (let attempt = 1; attempt <= SYNTH_ATTEMPTS; attempt += 1) {
|
|
@@ -13992,14 +13997,244 @@ function createScopedMemoriesRouter() {
|
|
|
13992
13997
|
return router;
|
|
13993
13998
|
}
|
|
13994
13999
|
|
|
14000
|
+
// ../api/src/dashboard/session-import-routes.ts
|
|
14001
|
+
import { Hono as Hono28 } from "hono";
|
|
14002
|
+
var MAX_FOLDERS2 = 20;
|
|
14003
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
14004
|
+
var IMPORT_RANGES = ["all", "7d", "latest5"];
|
|
14005
|
+
async function ask(agent, req) {
|
|
14006
|
+
const res = await agent.sessionImportRequest?.(req);
|
|
14007
|
+
return res && res.op === req.op ? res : null;
|
|
14008
|
+
}
|
|
14009
|
+
function hostUnavailable2(c, agent) {
|
|
14010
|
+
return c.json(
|
|
14011
|
+
{
|
|
14012
|
+
error: "host offline",
|
|
14013
|
+
reason: agent.getHostCallFailure?.("sessionImportRequest") ?? "offline"
|
|
14014
|
+
},
|
|
14015
|
+
503
|
|
14016
|
+
);
|
|
14017
|
+
}
|
|
14018
|
+
function pickSessions(sessions2, range, now = Date.now()) {
|
|
14019
|
+
if (range === "latest5") return sessions2.slice(0, 5);
|
|
14020
|
+
if (range === "7d") return sessions2.filter((s) => s.lastUsed >= now - 7 * DAY_MS);
|
|
14021
|
+
return sessions2;
|
|
14022
|
+
}
|
|
14023
|
+
function channelForFolder(folder, channels) {
|
|
14024
|
+
return folder.linkedAs ? channels.find((ch) => ch.name === folder.linkedAs && !ch.archivedAt) : void 0;
|
|
14025
|
+
}
|
|
14026
|
+
async function importRows(channelIds) {
|
|
14027
|
+
if (!channelIds.length) return [];
|
|
14028
|
+
const { data, error: error2 } = await getSupabaseClient().from("session_imports").select("channel_id, agent, session_id, thread_id, last_message_uuid, message_count").in("channel_id", channelIds);
|
|
14029
|
+
if (error2) throw new Error(error2.message);
|
|
14030
|
+
return data ?? [];
|
|
14031
|
+
}
|
|
14032
|
+
async function importableFolders(folders, channels) {
|
|
14033
|
+
const mapped = folders.map((f) => ({ folder: f, channel: channelForFolder(f, channels) }));
|
|
14034
|
+
const rows = await importRows(mapped.flatMap((m) => m.channel ? [m.channel.id] : []));
|
|
14035
|
+
const byKey = new Map(rows.map((r) => [rowKey(r.channel_id, r.agent, r.session_id), r]));
|
|
14036
|
+
return mapped.map(({ folder, channel }) => {
|
|
14037
|
+
const sessions2 = folder.sessions.flatMap((s) => {
|
|
14038
|
+
const row = channel ? byKey.get(rowKey(channel.id, s.agent, s.sessionId)) : void 0;
|
|
14039
|
+
return s.bound && !row ? [] : [{ ...s, row }];
|
|
14040
|
+
});
|
|
14041
|
+
return { folder: { ...folder, sessions: sessions2 }, channel };
|
|
14042
|
+
}).filter((m) => m.folder.sessions.length);
|
|
14043
|
+
}
|
|
14044
|
+
var rowKey = (channelId, agent, sessionId) => `${channelId}:${agent}:${sessionId}`;
|
|
14045
|
+
function replyRows(threadId, messages, author, fallbackAt) {
|
|
14046
|
+
return messages.map((m, i) => ({
|
|
14047
|
+
thread_id: threadId,
|
|
14048
|
+
author: m.role === "user" ? author : AGENT_AUTHOR,
|
|
14049
|
+
body: m.text,
|
|
14050
|
+
created_at: m.at ?? new Date(fallbackAt + i).toISOString()
|
|
14051
|
+
}));
|
|
14052
|
+
}
|
|
14053
|
+
async function syncSession(agent, channelId, session, author, existing) {
|
|
14054
|
+
const supabase = getSupabaseClient();
|
|
14055
|
+
const read = await ask(agent, {
|
|
14056
|
+
op: "read",
|
|
14057
|
+
agent: session.agent,
|
|
14058
|
+
sessionId: session.sessionId,
|
|
14059
|
+
...existing?.last_message_uuid ? { afterUuid: existing.last_message_uuid } : {}
|
|
14060
|
+
});
|
|
14061
|
+
if (!read) return null;
|
|
14062
|
+
const messages = read.messages ?? [];
|
|
14063
|
+
if (!messages.length) return 0;
|
|
14064
|
+
const last = messages[messages.length - 1];
|
|
14065
|
+
if (existing) {
|
|
14066
|
+
const { error: error3 } = await supabase.from("thread_replies").insert(replyRows(existing.thread_id, messages, author, Date.now()));
|
|
14067
|
+
if (error3) throw new Error(error3.message);
|
|
14068
|
+
await supabase.from("session_imports").update({
|
|
14069
|
+
last_message_uuid: last.uuid,
|
|
14070
|
+
message_count: existing.message_count + messages.length,
|
|
14071
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
14072
|
+
}).eq("channel_id", channelId).eq("agent", session.agent).eq("session_id", session.sessionId);
|
|
14073
|
+
return messages.length;
|
|
14074
|
+
}
|
|
14075
|
+
const startedAt = messages[0].at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
14076
|
+
const { data: thread, error: threadError } = await supabase.from("threads").insert({
|
|
14077
|
+
channel_id: channelId,
|
|
14078
|
+
title: null,
|
|
14079
|
+
author,
|
|
14080
|
+
imported_agent: session.agent,
|
|
14081
|
+
created_at: startedAt
|
|
14082
|
+
}).select("id").single();
|
|
14083
|
+
if (threadError || !thread) throw new Error(threadError?.message ?? "could not create thread");
|
|
14084
|
+
const threadId = thread.id;
|
|
14085
|
+
const { error: claimError } = await supabase.from("session_imports").insert({
|
|
14086
|
+
channel_id: channelId,
|
|
14087
|
+
agent: session.agent,
|
|
14088
|
+
session_id: session.sessionId,
|
|
14089
|
+
thread_id: threadId,
|
|
14090
|
+
last_message_uuid: last.uuid,
|
|
14091
|
+
message_count: messages.length
|
|
14092
|
+
});
|
|
14093
|
+
if (claimError) {
|
|
14094
|
+
await supabase.from("threads").delete().eq("id", threadId);
|
|
14095
|
+
if (claimError.code === "23505") return 0;
|
|
14096
|
+
throw new Error(claimError.message);
|
|
14097
|
+
}
|
|
14098
|
+
const { error: error2 } = await supabase.from("thread_replies").insert(replyRows(threadId, messages, author, Date.parse(startedAt)));
|
|
14099
|
+
if (error2) throw new Error(error2.message);
|
|
14100
|
+
await ask(agent, {
|
|
14101
|
+
op: "bind",
|
|
14102
|
+
items: [{ channelId, threadId, agent: session.agent, sessionId: session.sessionId }]
|
|
14103
|
+
});
|
|
14104
|
+
return messages.length;
|
|
14105
|
+
}
|
|
14106
|
+
function createSessionImportRouter(agent) {
|
|
14107
|
+
const app = new Hono28();
|
|
14108
|
+
const scanFolders = async () => {
|
|
14109
|
+
const scan = await ask(agent, { op: "scan", slug: getProfile() });
|
|
14110
|
+
return scan && importableFolders(scan.folders, getProfileChannelRegistry());
|
|
14111
|
+
};
|
|
14112
|
+
const channelParam = (c) => getProfileChannelRegistry().find((ch) => ch.id === c.req.param("channelId"));
|
|
14113
|
+
app.get("/scan", async (c) => {
|
|
14114
|
+
const ctx = await resolveAuthContext(c);
|
|
14115
|
+
if (ctx?.role !== "admin") return c.json({ error: "admin access required" }, 403);
|
|
14116
|
+
const folders = await scanFolders();
|
|
14117
|
+
if (!folders) return hostUnavailable2(c, agent);
|
|
14118
|
+
return c.json({
|
|
14119
|
+
folders: folders.map(({ folder, channel }) => ({
|
|
14120
|
+
...folder,
|
|
14121
|
+
channel: channel ? { id: channel.id, name: channel.name } : null,
|
|
14122
|
+
sessions: folder.sessions.map(({ row, bound: _bound, ...s }) => ({
|
|
14123
|
+
...s,
|
|
14124
|
+
imported: !!row
|
|
14125
|
+
}))
|
|
14126
|
+
}))
|
|
14127
|
+
});
|
|
14128
|
+
});
|
|
14129
|
+
app.post("/import", async (c) => {
|
|
14130
|
+
const ctx = await resolveAuthContext(c);
|
|
14131
|
+
if (ctx?.role !== "admin") return c.json({ error: "admin access required" }, 403);
|
|
14132
|
+
const body = await c.req.json().catch(() => ({}));
|
|
14133
|
+
const picks = (Array.isArray(body.folders) ? body.folders : []).filter((f) => typeof f?.path === "string").map((f) => ({
|
|
14134
|
+
path: f.path,
|
|
14135
|
+
range: IMPORT_RANGES.includes(f.range ?? "") ? f.range : "all"
|
|
14136
|
+
}));
|
|
14137
|
+
if (!picks.length) return c.json({ error: "no folders chosen" }, 400);
|
|
14138
|
+
if (picks.length > MAX_FOLDERS2) return c.json({ error: `at most ${MAX_FOLDERS2} folders` }, 400);
|
|
14139
|
+
const folders = await scanFolders();
|
|
14140
|
+
if (!folders) return hostUnavailable2(c, agent);
|
|
14141
|
+
const offered = new Map(folders.map((m) => [m.folder.path, m]));
|
|
14142
|
+
if (picks.some((p) => !offered.has(p.path)))
|
|
14143
|
+
return c.json({ error: "folder was not offered by the scan" }, 400);
|
|
14144
|
+
const needLink = picks.filter((p) => !offered.get(p.path).channel);
|
|
14145
|
+
if (needLink.length && !isWorkspaceOwner(ctx)) {
|
|
14146
|
+
return c.json({ error: "workspace owner access required to add new folders" }, 403);
|
|
14147
|
+
}
|
|
14148
|
+
const taken = new Set(getProfileChannelRegistry().map((ch) => ch.name));
|
|
14149
|
+
const items = needLink.map((p) => ({
|
|
14150
|
+
name: uniqueChannelName(offered.get(p.path).folder.name, taken),
|
|
14151
|
+
path: p.path
|
|
14152
|
+
}));
|
|
14153
|
+
const linked = items.length ? await agent.linkProjectFolders?.(getProfile(), items) : [];
|
|
14154
|
+
if (!linked) return hostUnavailable2(c, agent);
|
|
14155
|
+
const created = /* @__PURE__ */ new Map();
|
|
14156
|
+
for (const [i, l] of linked.entries())
|
|
14157
|
+
created.set(items[i].path, await createChannel({ name: l.name, cwd: l.cwd }));
|
|
14158
|
+
const results = [];
|
|
14159
|
+
for (const p of picks) {
|
|
14160
|
+
const { folder, channel: existing } = offered.get(p.path);
|
|
14161
|
+
const channel = existing ?? created.get(p.path);
|
|
14162
|
+
let threads = 0;
|
|
14163
|
+
let messages = 0;
|
|
14164
|
+
for (const s of pickSessions(folder.sessions, p.range)) {
|
|
14165
|
+
if (s.row) continue;
|
|
14166
|
+
const n = await syncSession(agent, channel.id, s, ctx.handle, void 0);
|
|
14167
|
+
if (n === null) return hostUnavailable2(c, agent);
|
|
14168
|
+
if (n > 0) threads += 1;
|
|
14169
|
+
messages += n;
|
|
14170
|
+
}
|
|
14171
|
+
results.push({
|
|
14172
|
+
path: p.path,
|
|
14173
|
+
channel: { id: channel.id, name: channel.name },
|
|
14174
|
+
created: !existing,
|
|
14175
|
+
threads,
|
|
14176
|
+
messages
|
|
14177
|
+
});
|
|
14178
|
+
}
|
|
14179
|
+
return c.json({ results }, 201);
|
|
14180
|
+
});
|
|
14181
|
+
app.get("/channels/:channelId", async (c) => {
|
|
14182
|
+
const ctx = await resolveAuthContext(c);
|
|
14183
|
+
if (ctx?.role !== "admin") return c.json({ error: "admin access required" }, 403);
|
|
14184
|
+
const channel = channelParam(c);
|
|
14185
|
+
if (!channel) return c.json({ error: "channel not found" }, 404);
|
|
14186
|
+
const folders = await scanFolders();
|
|
14187
|
+
if (!folders) return hostUnavailable2(c, agent);
|
|
14188
|
+
const folder = folders.find((m) => m.channel?.id === channel.id)?.folder;
|
|
14189
|
+
const sessions2 = [];
|
|
14190
|
+
for (const { row, bound: _bound, ...s } of folder?.sessions ?? []) {
|
|
14191
|
+
let newMessages = 0;
|
|
14192
|
+
if (row) {
|
|
14193
|
+
const read = await ask(agent, {
|
|
14194
|
+
op: "read",
|
|
14195
|
+
agent: s.agent,
|
|
14196
|
+
sessionId: s.sessionId,
|
|
14197
|
+
...row.last_message_uuid ? { afterUuid: row.last_message_uuid } : {}
|
|
14198
|
+
});
|
|
14199
|
+
newMessages = read?.messages?.length ?? 0;
|
|
14200
|
+
}
|
|
14201
|
+
const status = !row ? "not-imported" : newMessages ? "new" : "synced";
|
|
14202
|
+
sessions2.push({ ...s, threadId: row?.thread_id ?? null, status, newMessages });
|
|
14203
|
+
}
|
|
14204
|
+
return c.json({ folder: folder?.path ?? null, sessions: sessions2 });
|
|
14205
|
+
});
|
|
14206
|
+
app.post("/channels/:channelId/sync", async (c) => {
|
|
14207
|
+
const ctx = await resolveAuthContext(c);
|
|
14208
|
+
if (ctx?.role !== "admin") return c.json({ error: "admin access required" }, 403);
|
|
14209
|
+
const channel = channelParam(c);
|
|
14210
|
+
if (!channel) return c.json({ error: "channel not found" }, 404);
|
|
14211
|
+
const body = await c.req.json().catch(() => ({}));
|
|
14212
|
+
const only = Array.isArray(body.sessionIds) ? new Set(body.sessionIds.filter((s) => typeof s === "string")) : null;
|
|
14213
|
+
const folders = await scanFolders();
|
|
14214
|
+
if (!folders) return hostUnavailable2(c, agent);
|
|
14215
|
+
const folder = folders.find((m) => m.channel?.id === channel.id)?.folder;
|
|
14216
|
+
let threads = 0;
|
|
14217
|
+
let messages = 0;
|
|
14218
|
+
for (const s of folder?.sessions ?? []) {
|
|
14219
|
+
if (only && !only.has(s.sessionId)) continue;
|
|
14220
|
+
const n = await syncSession(agent, channel.id, s, ctx.handle, s.row);
|
|
14221
|
+
if (n === null) return hostUnavailable2(c, agent);
|
|
14222
|
+
if (!s.row && n > 0) threads += 1;
|
|
14223
|
+
messages += n;
|
|
14224
|
+
}
|
|
14225
|
+
return c.json({ threads, messages });
|
|
14226
|
+
});
|
|
14227
|
+
return app;
|
|
14228
|
+
}
|
|
14229
|
+
|
|
13995
14230
|
// ../api/src/dashboard/host-status-signal.ts
|
|
13996
14231
|
var HOST_STATUS_CHANNEL = "host-status";
|
|
13997
14232
|
var channelReady = null;
|
|
13998
14233
|
function getHostStatusChannel() {
|
|
13999
|
-
channelReady ??= new Promise((
|
|
14234
|
+
channelReady ??= new Promise((resolve8) => {
|
|
14000
14235
|
const channel = getSupabaseClient().channel(HOST_STATUS_CHANNEL);
|
|
14001
14236
|
channel.subscribe((state) => {
|
|
14002
|
-
if (state === "SUBSCRIBED")
|
|
14237
|
+
if (state === "SUBSCRIBED") resolve8(channel);
|
|
14003
14238
|
});
|
|
14004
14239
|
});
|
|
14005
14240
|
return channelReady;
|
|
@@ -14164,6 +14399,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
14164
14399
|
});
|
|
14165
14400
|
api.route("/channels", createDocShareWriteRouter(agent));
|
|
14166
14401
|
api.route("/onboarding", createOnboardingRouter(agent));
|
|
14402
|
+
api.route("/session-import", createSessionImportRouter(agent));
|
|
14167
14403
|
api.route("/channels", createChannelGreetingRouter(agent));
|
|
14168
14404
|
api.route("/channels", createChannelMembersRouter());
|
|
14169
14405
|
api.route("/channels", createMemoriesRouter());
|
|
@@ -14418,7 +14654,7 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
14418
14654
|
// ../api/src/dashboard/device-auth-routes.ts
|
|
14419
14655
|
import { verify } from "crypto";
|
|
14420
14656
|
import { getConnInfo } from "@hono/node-server/conninfo";
|
|
14421
|
-
import { Hono as
|
|
14657
|
+
import { Hono as Hono29 } from "hono";
|
|
14422
14658
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14423
14659
|
function cleanText(value, max) {
|
|
14424
14660
|
if (typeof value !== "string") return null;
|
|
@@ -14466,7 +14702,7 @@ async function boundWorkspaceIds(publicKey, userId) {
|
|
|
14466
14702
|
return new Set(existing ? (await listDeviceWorkspaces(existing)).map((w) => w.id) : []);
|
|
14467
14703
|
}
|
|
14468
14704
|
function createDeviceAuthRouter() {
|
|
14469
|
-
const app = new
|
|
14705
|
+
const app = new Hono29();
|
|
14470
14706
|
app.post("/code", async (c) => {
|
|
14471
14707
|
const body = await readBody(c);
|
|
14472
14708
|
const publicKey = typeof body["public_key"] === "string" ? body["public_key"] : "";
|
|
@@ -14785,7 +15021,7 @@ var workspaceRef = () => ({ kind: "workspace" });
|
|
|
14785
15021
|
var channelRef = (c) => ({ kind: "channel", channelId: c.req.param("id") });
|
|
14786
15022
|
|
|
14787
15023
|
// ../api/src/dashboard/output-styles-routes.ts
|
|
14788
|
-
import { Hono as
|
|
15024
|
+
import { Hono as Hono30 } from "hono";
|
|
14789
15025
|
function requireAdmin3(auth) {
|
|
14790
15026
|
return auth?.role === "admin" ? null : "admin access required";
|
|
14791
15027
|
}
|
|
@@ -14805,7 +15041,7 @@ function failure(c, err) {
|
|
|
14805
15041
|
return c.json({ error: message }, err instanceof OutputStyleConflictError ? 409 : 500);
|
|
14806
15042
|
}
|
|
14807
15043
|
function createOutputStylesRouter() {
|
|
14808
|
-
const app = new
|
|
15044
|
+
const app = new Hono30();
|
|
14809
15045
|
app.get("/", (c) => {
|
|
14810
15046
|
const auth = c.get("auth");
|
|
14811
15047
|
const denied = requireAdmin3(auth);
|
|
@@ -14897,7 +15133,7 @@ function createOutputStylesRouter() {
|
|
|
14897
15133
|
return app;
|
|
14898
15134
|
}
|
|
14899
15135
|
async function handleOutputStylesRequest(req) {
|
|
14900
|
-
const app = new
|
|
15136
|
+
const app = new Hono30();
|
|
14901
15137
|
app.use("*", async (c, next) => {
|
|
14902
15138
|
c.set("auth", { role: "admin" });
|
|
14903
15139
|
await next();
|
|
@@ -14918,7 +15154,7 @@ import { streamSSE } from "hono/streaming";
|
|
|
14918
15154
|
|
|
14919
15155
|
// ../api/src/dashboard/api.ts
|
|
14920
15156
|
function createGatedRouter(agent, register) {
|
|
14921
|
-
const api = new
|
|
15157
|
+
const api = new Hono31();
|
|
14922
15158
|
api.use("*", async (c, next) => {
|
|
14923
15159
|
const known = await listKnownWorkspaces();
|
|
14924
15160
|
const fromHost = profileFromHost(trustedHost(c), known);
|
|
@@ -14971,7 +15207,7 @@ import sharp7 from "sharp";
|
|
|
14971
15207
|
|
|
14972
15208
|
// src/dashboard/channels-routes.ts
|
|
14973
15209
|
import { existsSync as existsSync22, mkdirSync as mkdirSync10 } from "fs";
|
|
14974
|
-
import { Hono as
|
|
15210
|
+
import { Hono as Hono32 } from "hono";
|
|
14975
15211
|
function coerceAccess(raw) {
|
|
14976
15212
|
if (raw === "open" || raw === "*" || raw === "all") return raw;
|
|
14977
15213
|
if (Array.isArray(raw) && raw.every((v) => typeof v === "string")) return raw;
|
|
@@ -15034,7 +15270,7 @@ function toResponse3(channel) {
|
|
|
15034
15270
|
};
|
|
15035
15271
|
}
|
|
15036
15272
|
function createChannelWriteRouter() {
|
|
15037
|
-
const app = new
|
|
15273
|
+
const app = new Hono32();
|
|
15038
15274
|
app.post("/", async (c) => {
|
|
15039
15275
|
const body = await c.req.json().catch(() => ({}));
|
|
15040
15276
|
const name = typeof body["name"] === "string" ? body["name"].trim() : "";
|
|
@@ -15174,7 +15410,7 @@ import {
|
|
|
15174
15410
|
} from "fs";
|
|
15175
15411
|
import { Readable } from "stream";
|
|
15176
15412
|
import { basename as basename8, dirname as dirname8, extname, join as join23, relative, resolve as resolve5, sep } from "path";
|
|
15177
|
-
import { Hono as
|
|
15413
|
+
import { Hono as Hono33 } from "hono";
|
|
15178
15414
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
15179
15415
|
|
|
15180
15416
|
// src/dashboard/docs-watcher.ts
|
|
@@ -15476,7 +15712,7 @@ function needsRefresh(expiresAt, now = Date.now()) {
|
|
|
15476
15712
|
if (!expiresAt) return false;
|
|
15477
15713
|
return new Date(expiresAt).getTime() - now < REFRESH_SKEW_MS;
|
|
15478
15714
|
}
|
|
15479
|
-
var sleep3 = (ms) => new Promise((
|
|
15715
|
+
var sleep3 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
15480
15716
|
async function refreshConnection(params) {
|
|
15481
15717
|
const { workspaceId, connection, provider } = params;
|
|
15482
15718
|
const deps = params.deps ?? {};
|
|
@@ -15810,7 +16046,7 @@ ${req.user}`,
|
|
|
15810
16046
|
};
|
|
15811
16047
|
}
|
|
15812
16048
|
function run3(inv, cwd) {
|
|
15813
|
-
return new Promise((
|
|
16049
|
+
return new Promise((resolve8) => {
|
|
15814
16050
|
const child = spawn4(inv.command, inv.args, {
|
|
15815
16051
|
cwd,
|
|
15816
16052
|
env: inv.env,
|
|
@@ -15824,14 +16060,14 @@ function run3(inv, cwd) {
|
|
|
15824
16060
|
child.on("error", (err) => {
|
|
15825
16061
|
clearTimeout(timer2);
|
|
15826
16062
|
console.error(`[memory-extraction] ${inv.command} failed to start:`, err.message);
|
|
15827
|
-
|
|
16063
|
+
resolve8({ ok: false });
|
|
15828
16064
|
});
|
|
15829
16065
|
child.on("close", (code) => {
|
|
15830
16066
|
clearTimeout(timer2);
|
|
15831
16067
|
if (code !== 0) {
|
|
15832
16068
|
const detail = (stderr || stdout).trim().split("\n").at(-1) ?? "";
|
|
15833
16069
|
console.error(`[memory-extraction] ${inv.command} exited ${code}: ${detail.slice(0, 200)}`);
|
|
15834
|
-
|
|
16070
|
+
resolve8({ ok: false });
|
|
15835
16071
|
return;
|
|
15836
16072
|
}
|
|
15837
16073
|
let raw = stdout;
|
|
@@ -15842,7 +16078,7 @@ function run3(inv, cwd) {
|
|
|
15842
16078
|
raw = "";
|
|
15843
16079
|
}
|
|
15844
16080
|
}
|
|
15845
|
-
|
|
16081
|
+
resolve8({ ok: true, raw });
|
|
15846
16082
|
});
|
|
15847
16083
|
child.stdin.end(inv.stdin);
|
|
15848
16084
|
});
|
|
@@ -15904,7 +16140,7 @@ Rules:
|
|
|
15904
16140
|
- No preamble like "Summary:" \u2014 start directly with the summary text.`;
|
|
15905
16141
|
var HAIKU_TIMEOUT_MS = 6e4;
|
|
15906
16142
|
function runHaikuPrint(input2, claudeConfigDir, claudeCodeOauthToken) {
|
|
15907
|
-
return new Promise((
|
|
16143
|
+
return new Promise((resolve8, reject) => {
|
|
15908
16144
|
const args = [
|
|
15909
16145
|
"--print",
|
|
15910
16146
|
"--model",
|
|
@@ -15955,7 +16191,7 @@ function runHaikuPrint(input2, claudeConfigDir, claudeCodeOauthToken) {
|
|
|
15955
16191
|
reject(new Error("claude -p --model haiku returned empty output"));
|
|
15956
16192
|
return;
|
|
15957
16193
|
}
|
|
15958
|
-
|
|
16194
|
+
resolve8(text);
|
|
15959
16195
|
});
|
|
15960
16196
|
});
|
|
15961
16197
|
}
|
|
@@ -16351,6 +16587,15 @@ var ChatAgent = class {
|
|
|
16351
16587
|
* restart picks up edits. */
|
|
16352
16588
|
workspaceRoots = null;
|
|
16353
16589
|
/** Fire-and-forget entry point, called by chat-routes after every write. */
|
|
16590
|
+
/** The CLI of the session already bound to this thread, if any. */
|
|
16591
|
+
boundRunner(channelId, threadId) {
|
|
16592
|
+
const store = this.openSessionStore();
|
|
16593
|
+
try {
|
|
16594
|
+
return store.getSession(`chat:${channelId}`, threadId)?.runner;
|
|
16595
|
+
} finally {
|
|
16596
|
+
store.close();
|
|
16597
|
+
}
|
|
16598
|
+
}
|
|
16354
16599
|
handleMessage(msg) {
|
|
16355
16600
|
if (msg.author === AGENT_AUTHOR) return;
|
|
16356
16601
|
this.seenReplies.add(msg.replyId);
|
|
@@ -16731,7 +16976,7 @@ var ChatAgent = class {
|
|
|
16731
16976
|
);
|
|
16732
16977
|
ensureLogicalCwdDir(baseConfig.cwd);
|
|
16733
16978
|
const pickedModel = msg.model?.trim();
|
|
16734
|
-
let activeRunner = pickedModel ? runnerForModel(pickedModel) : defaultRunner;
|
|
16979
|
+
let activeRunner = pickedModel ? runnerForModel(pickedModel) : this.boundRunner(thread.channel_id, msg.threadId) ?? defaultRunner;
|
|
16735
16980
|
let channelConfig = pickedModel ? {
|
|
16736
16981
|
...baseConfig,
|
|
16737
16982
|
...activeRunner === "codex" ? { model: pickedModel, codexModel: pickedModel } : { model: pickedModel }
|
|
@@ -17037,22 +17282,22 @@ ${msg.body}` : msg.body;
|
|
|
17037
17282
|
let ready;
|
|
17038
17283
|
try {
|
|
17039
17284
|
channel = this.supabase().channel(`agent-thinking:${threadId}`);
|
|
17040
|
-
ready = new Promise((
|
|
17285
|
+
ready = new Promise((resolve8) => {
|
|
17041
17286
|
const timeout = setTimeout(() => {
|
|
17042
17287
|
console.error(`[chat-agent] thinking-channel ${threadId}: subscribe timed out after 10s`);
|
|
17043
|
-
|
|
17288
|
+
resolve8(false);
|
|
17044
17289
|
}, 1e4);
|
|
17045
17290
|
channel.subscribe((status, err) => {
|
|
17046
17291
|
if (status === "SUBSCRIBED") {
|
|
17047
17292
|
clearTimeout(timeout);
|
|
17048
|
-
|
|
17293
|
+
resolve8(true);
|
|
17049
17294
|
} else if (status === "CHANNEL_ERROR" || status === "TIMED_OUT" || status === "CLOSED") {
|
|
17050
17295
|
console.error(
|
|
17051
17296
|
`[chat-agent] thinking-channel ${threadId}: ${status}`,
|
|
17052
17297
|
err?.message ?? ""
|
|
17053
17298
|
);
|
|
17054
17299
|
clearTimeout(timeout);
|
|
17055
|
-
|
|
17300
|
+
resolve8(false);
|
|
17056
17301
|
}
|
|
17057
17302
|
});
|
|
17058
17303
|
});
|
|
@@ -17099,19 +17344,19 @@ ${msg.body}` : msg.body;
|
|
|
17099
17344
|
let ready;
|
|
17100
17345
|
try {
|
|
17101
17346
|
channel = this.supabase().channel("channel-activity");
|
|
17102
|
-
ready = new Promise((
|
|
17347
|
+
ready = new Promise((resolve8) => {
|
|
17103
17348
|
const timeout = setTimeout(() => {
|
|
17104
17349
|
console.error("[chat-agent] channel-activity: subscribe timed out after 10s");
|
|
17105
|
-
|
|
17350
|
+
resolve8(false);
|
|
17106
17351
|
}, 1e4);
|
|
17107
17352
|
channel.subscribe((status, err) => {
|
|
17108
17353
|
if (status === "SUBSCRIBED") {
|
|
17109
17354
|
clearTimeout(timeout);
|
|
17110
|
-
|
|
17355
|
+
resolve8(true);
|
|
17111
17356
|
} else if (status === "CHANNEL_ERROR" || status === "TIMED_OUT" || status === "CLOSED") {
|
|
17112
17357
|
console.error(`[chat-agent] channel-activity: ${status}`, err?.message ?? "");
|
|
17113
17358
|
clearTimeout(timeout);
|
|
17114
|
-
|
|
17359
|
+
resolve8(false);
|
|
17115
17360
|
}
|
|
17116
17361
|
});
|
|
17117
17362
|
});
|
|
@@ -18055,7 +18300,7 @@ function deleteDocEntry(root, path) {
|
|
|
18055
18300
|
};
|
|
18056
18301
|
}
|
|
18057
18302
|
function createDocsRouter({ gate = requireDocsAccess } = {}) {
|
|
18058
|
-
const app = new
|
|
18303
|
+
const app = new Hono33();
|
|
18059
18304
|
app.use("/:id/docs", gate);
|
|
18060
18305
|
app.use("/:id/docs/*", gate);
|
|
18061
18306
|
app.get("/:id/docs", (c) => {
|
|
@@ -18393,7 +18638,7 @@ function withDocLinkBridge(html) {
|
|
|
18393
18638
|
// src/dashboard/skills-routes-host.ts
|
|
18394
18639
|
import { existsSync as existsSync27, statSync as statSync7 } from "fs";
|
|
18395
18640
|
import { dirname as dirname9 } from "path";
|
|
18396
|
-
import { Hono as
|
|
18641
|
+
import { Hono as Hono34 } from "hono";
|
|
18397
18642
|
function workspaceScope() {
|
|
18398
18643
|
const claudeConfigDir = profileClaudeConfigDir();
|
|
18399
18644
|
const cwd = defaultClaudeCwd();
|
|
@@ -18499,12 +18744,12 @@ var diskBackend = {
|
|
|
18499
18744
|
write: writeSkillAndSync
|
|
18500
18745
|
};
|
|
18501
18746
|
function createWorkspaceSkillsRouter() {
|
|
18502
|
-
const app = new
|
|
18747
|
+
const app = new Hono34();
|
|
18503
18748
|
mountSkillRoutes(app, "/skills", workspaceRef, diskBackend);
|
|
18504
18749
|
return app;
|
|
18505
18750
|
}
|
|
18506
18751
|
function createChannelSkillsRouter() {
|
|
18507
|
-
const app = new
|
|
18752
|
+
const app = new Hono34();
|
|
18508
18753
|
mountSkillRoutes(app, "/:id/skill-files", channelRef, diskBackend);
|
|
18509
18754
|
return app;
|
|
18510
18755
|
}
|
|
@@ -18639,7 +18884,7 @@ import { randomUUID as randomUUID5 } from "crypto";
|
|
|
18639
18884
|
import { chmodSync, existsSync as existsSync29, mkdirSync as mkdirSync12, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "fs";
|
|
18640
18885
|
import { createRequire } from "module";
|
|
18641
18886
|
import { dirname as dirname10, join as join25 } from "path";
|
|
18642
|
-
import { Hono as
|
|
18887
|
+
import { Hono as Hono35 } from "hono";
|
|
18643
18888
|
var SESSION_TTL_MS = 10 * 6e4;
|
|
18644
18889
|
var URL_WAIT_MS = 3e4;
|
|
18645
18890
|
var TOKEN_WAIT_MS = 6e4;
|
|
@@ -18688,13 +18933,13 @@ function track(session) {
|
|
|
18688
18933
|
return [id, s];
|
|
18689
18934
|
}
|
|
18690
18935
|
function waitFor(s, parse, ms) {
|
|
18691
|
-
return new Promise((
|
|
18936
|
+
return new Promise((resolve8) => {
|
|
18692
18937
|
const started = Date.now();
|
|
18693
18938
|
const tick = setInterval(() => {
|
|
18694
18939
|
const v = parse();
|
|
18695
18940
|
if (v !== void 0 || s.status !== "pending" || Date.now() - started > ms) {
|
|
18696
18941
|
clearInterval(tick);
|
|
18697
|
-
|
|
18942
|
+
resolve8(v);
|
|
18698
18943
|
}
|
|
18699
18944
|
}, 200);
|
|
18700
18945
|
});
|
|
@@ -18796,7 +19041,7 @@ function failure2(c, err) {
|
|
|
18796
19041
|
return c.json({ error: err.message }, 500);
|
|
18797
19042
|
}
|
|
18798
19043
|
function createProviderLoginRouter() {
|
|
18799
|
-
const app = new
|
|
19044
|
+
const app = new Hono35();
|
|
18800
19045
|
app.use("*", async (c, next) => {
|
|
18801
19046
|
const denied = requireAdmin4(c.get("auth"));
|
|
18802
19047
|
if (denied) return c.json({ error: denied }, 403);
|
|
@@ -18840,7 +19085,7 @@ function createProviderLoginRouter() {
|
|
|
18840
19085
|
return app;
|
|
18841
19086
|
}
|
|
18842
19087
|
async function handleProviderLoginRequest(req) {
|
|
18843
|
-
const app = new
|
|
19088
|
+
const app = new Hono35();
|
|
18844
19089
|
app.use("*", async (c, next) => {
|
|
18845
19090
|
c.set("auth", { role: "admin" });
|
|
18846
19091
|
await next();
|
|
@@ -18992,18 +19237,253 @@ function createHostApiRouter(agent = {}) {
|
|
|
18992
19237
|
|
|
18993
19238
|
// src/dashboard/agent-bridge-host.ts
|
|
18994
19239
|
import { sign as signRaw, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
18995
|
-
import { existsSync as
|
|
19240
|
+
import { existsSync as existsSync32, readFileSync as readFileSync27 } from "fs";
|
|
18996
19241
|
import dotenv2 from "dotenv";
|
|
18997
19242
|
import { WebSocket as WebSocket2 } from "ws";
|
|
18998
19243
|
|
|
19244
|
+
// src/dashboard/session-import-host.ts
|
|
19245
|
+
import {
|
|
19246
|
+
closeSync as closeSync3,
|
|
19247
|
+
existsSync as existsSync31,
|
|
19248
|
+
openSync as openSync3,
|
|
19249
|
+
readdirSync as readdirSync6,
|
|
19250
|
+
readFileSync as readFileSync25,
|
|
19251
|
+
readSync as readSync3,
|
|
19252
|
+
realpathSync as realpathSync5
|
|
19253
|
+
} from "fs";
|
|
19254
|
+
import { basename as basename9, join as join26, resolve as resolve7 } from "path";
|
|
19255
|
+
var MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
19256
|
+
var HEAD_BYTES = 64 * 1024;
|
|
19257
|
+
var TITLE_MAX = 80;
|
|
19258
|
+
function readHead2(file) {
|
|
19259
|
+
let fd;
|
|
19260
|
+
try {
|
|
19261
|
+
fd = openSync3(file, "r");
|
|
19262
|
+
const buf = Buffer.alloc(HEAD_BYTES);
|
|
19263
|
+
return buf.toString("utf-8", 0, readSync3(fd, buf, 0, buf.length, 0));
|
|
19264
|
+
} catch {
|
|
19265
|
+
return "";
|
|
19266
|
+
} finally {
|
|
19267
|
+
if (fd !== void 0) closeSync3(fd);
|
|
19268
|
+
}
|
|
19269
|
+
}
|
|
19270
|
+
function parseLines(text) {
|
|
19271
|
+
const out = [];
|
|
19272
|
+
for (const line of text.split("\n")) {
|
|
19273
|
+
if (!line) continue;
|
|
19274
|
+
try {
|
|
19275
|
+
out.push(JSON.parse(line));
|
|
19276
|
+
} catch {
|
|
19277
|
+
}
|
|
19278
|
+
}
|
|
19279
|
+
return out;
|
|
19280
|
+
}
|
|
19281
|
+
function cleanText2(text) {
|
|
19282
|
+
const t = text.trim();
|
|
19283
|
+
if (!t || t.startsWith("<")) return null;
|
|
19284
|
+
return t;
|
|
19285
|
+
}
|
|
19286
|
+
function blocksText(content, kinds) {
|
|
19287
|
+
if (typeof content === "string") return cleanText2(content);
|
|
19288
|
+
if (!Array.isArray(content)) return null;
|
|
19289
|
+
const parts = content.filter(
|
|
19290
|
+
(b) => kinds.includes(b?.type) && typeof b.text === "string"
|
|
19291
|
+
).map((b) => b.text);
|
|
19292
|
+
return parts.length ? cleanText2(parts.join("\n\n")) : null;
|
|
19293
|
+
}
|
|
19294
|
+
function boundSessionIds() {
|
|
19295
|
+
if (!existsSync31(paths.DB_FILE)) return /* @__PURE__ */ new Set();
|
|
19296
|
+
const store = new SessionStore(paths.DB_FILE);
|
|
19297
|
+
try {
|
|
19298
|
+
return new Set(store.listSessions().map((s) => s.sessionId));
|
|
19299
|
+
} finally {
|
|
19300
|
+
store.close();
|
|
19301
|
+
}
|
|
19302
|
+
}
|
|
19303
|
+
function claudeMessages(lines) {
|
|
19304
|
+
const out = [];
|
|
19305
|
+
for (const o of lines) {
|
|
19306
|
+
if (o["type"] !== "user" && o["type"] !== "assistant" || o["isMeta"] || o["isSidechain"])
|
|
19307
|
+
continue;
|
|
19308
|
+
if (o["entrypoint"] === "sdk-cli") continue;
|
|
19309
|
+
const role = o["type"];
|
|
19310
|
+
const text = blocksText(o["message"]?.content, ["text"]);
|
|
19311
|
+
if (!text || typeof o["uuid"] !== "string") continue;
|
|
19312
|
+
const at = typeof o["timestamp"] === "string" ? o["timestamp"] : null;
|
|
19313
|
+
const last = out[out.length - 1];
|
|
19314
|
+
if (role === "assistant" && last?.role === "assistant") {
|
|
19315
|
+
last.text += `
|
|
19316
|
+
|
|
19317
|
+
${text}`;
|
|
19318
|
+
last.uuid = o["uuid"];
|
|
19319
|
+
} else {
|
|
19320
|
+
out.push({ uuid: o["uuid"], role, text, at });
|
|
19321
|
+
}
|
|
19322
|
+
}
|
|
19323
|
+
return out;
|
|
19324
|
+
}
|
|
19325
|
+
function codexMessages(lines) {
|
|
19326
|
+
const out = [];
|
|
19327
|
+
lines.forEach((o, i) => {
|
|
19328
|
+
const p = o["payload"];
|
|
19329
|
+
if (o["type"] !== "response_item" || p?.type !== "message") return;
|
|
19330
|
+
if (p.role !== "user" && p.role !== "assistant") return;
|
|
19331
|
+
const text = blocksText(p.content, ["input_text", "output_text"]);
|
|
19332
|
+
if (!text) return;
|
|
19333
|
+
out.push({
|
|
19334
|
+
uuid: `line:${i}`,
|
|
19335
|
+
role: p.role,
|
|
19336
|
+
text,
|
|
19337
|
+
at: typeof o["timestamp"] === "string" ? o["timestamp"] : null
|
|
19338
|
+
});
|
|
19339
|
+
});
|
|
19340
|
+
return out;
|
|
19341
|
+
}
|
|
19342
|
+
function claudeSummary(file, mtime) {
|
|
19343
|
+
const head = parseLines(readHead2(file));
|
|
19344
|
+
if (head.some((o) => o["entrypoint"] === "sdk-cli")) return null;
|
|
19345
|
+
const first = claudeMessages(head).find((m) => m.role === "user");
|
|
19346
|
+
if (!first) return null;
|
|
19347
|
+
return {
|
|
19348
|
+
agent: "claude",
|
|
19349
|
+
sessionId: basename9(file, ".jsonl"),
|
|
19350
|
+
title: first.text.slice(0, TITLE_MAX),
|
|
19351
|
+
lastUsed: mtime
|
|
19352
|
+
};
|
|
19353
|
+
}
|
|
19354
|
+
function codexSummary(file, mtime) {
|
|
19355
|
+
const head = parseLines(readHead2(file));
|
|
19356
|
+
const meta = head.find((o) => o["type"] === "session_meta")?.["payload"];
|
|
19357
|
+
if (!meta?.id || !meta.cwd || meta.originator === "codex_exec") return null;
|
|
19358
|
+
const first = codexMessages(head).find((m) => m.role === "user");
|
|
19359
|
+
if (!first) return null;
|
|
19360
|
+
return {
|
|
19361
|
+
cwd: meta.cwd,
|
|
19362
|
+
session: {
|
|
19363
|
+
agent: "codex",
|
|
19364
|
+
sessionId: meta.id,
|
|
19365
|
+
title: first.text.slice(0, TITLE_MAX),
|
|
19366
|
+
lastUsed: mtime
|
|
19367
|
+
}
|
|
19368
|
+
};
|
|
19369
|
+
}
|
|
19370
|
+
function enabledAgents() {
|
|
19371
|
+
return new Set(
|
|
19372
|
+
detectRunners().filter((r) => r.installed && r.loggedIn).map((r) => r.id)
|
|
19373
|
+
);
|
|
19374
|
+
}
|
|
19375
|
+
function scanSessionFolders(slug) {
|
|
19376
|
+
const agents = enabledAgents();
|
|
19377
|
+
const bound = boundSessionIds();
|
|
19378
|
+
const since2 = Date.now() - MAX_AGE_MS;
|
|
19379
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
19380
|
+
const add = (cwd, s) => {
|
|
19381
|
+
const path = resolve7(cwd);
|
|
19382
|
+
if (isExcluded(path)) return;
|
|
19383
|
+
byPath.set(path, [
|
|
19384
|
+
...byPath.get(path) ?? [],
|
|
19385
|
+
bound.has(s.sessionId) ? { ...s, bound: true } : s
|
|
19386
|
+
]);
|
|
19387
|
+
};
|
|
19388
|
+
if (agents.has("claude")) {
|
|
19389
|
+
const root = join26(claudeDir(), "projects");
|
|
19390
|
+
let dirs = [];
|
|
19391
|
+
try {
|
|
19392
|
+
dirs = readdirSync6(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
19393
|
+
} catch {
|
|
19394
|
+
}
|
|
19395
|
+
for (const dir of dirs) {
|
|
19396
|
+
const files = jsonlFiles(join26(root, dir), 0).filter((f) => f.mtime >= since2);
|
|
19397
|
+
let cwd = null;
|
|
19398
|
+
for (const f of files) {
|
|
19399
|
+
cwd ??= cwdFromTranscript(f.file);
|
|
19400
|
+
if (cwd) break;
|
|
19401
|
+
}
|
|
19402
|
+
if (!cwd) continue;
|
|
19403
|
+
for (const f of files) {
|
|
19404
|
+
const s = claudeSummary(f.file, f.mtime);
|
|
19405
|
+
if (s) add(cwd, s);
|
|
19406
|
+
}
|
|
19407
|
+
}
|
|
19408
|
+
}
|
|
19409
|
+
if (agents.has("codex")) {
|
|
19410
|
+
for (const f of jsonlFiles(join26(codexDir(), "sessions"), 4)) {
|
|
19411
|
+
if (f.mtime < since2) continue;
|
|
19412
|
+
const s = codexSummary(f.file, f.mtime);
|
|
19413
|
+
if (s) add(s.cwd, s.session);
|
|
19414
|
+
}
|
|
19415
|
+
}
|
|
19416
|
+
const linked = linkedTargets(slug);
|
|
19417
|
+
const folders = [...byPath].map(([path, sessions2]) => {
|
|
19418
|
+
let real = path;
|
|
19419
|
+
try {
|
|
19420
|
+
real = realpathSync5(path);
|
|
19421
|
+
} catch {
|
|
19422
|
+
}
|
|
19423
|
+
sessions2.sort((a, b) => b.lastUsed - a.lastUsed);
|
|
19424
|
+
return {
|
|
19425
|
+
path,
|
|
19426
|
+
name: basename9(path),
|
|
19427
|
+
lastUsed: sessions2[0].lastUsed,
|
|
19428
|
+
sessions: sessions2,
|
|
19429
|
+
...linked.has(real) ? { linkedAs: linked.get(real) } : {}
|
|
19430
|
+
};
|
|
19431
|
+
});
|
|
19432
|
+
return folders.sort((a, b) => b.lastUsed - a.lastUsed);
|
|
19433
|
+
}
|
|
19434
|
+
function findTranscript(agent, sessionId) {
|
|
19435
|
+
if (!/^[A-Za-z0-9-]+$/.test(sessionId)) return null;
|
|
19436
|
+
if (agent === "claude") {
|
|
19437
|
+
const root = join26(claudeDir(), "projects");
|
|
19438
|
+
try {
|
|
19439
|
+
for (const e of readdirSync6(root, { withFileTypes: true })) {
|
|
19440
|
+
const file = join26(root, e.name, `${sessionId}.jsonl`);
|
|
19441
|
+
if (e.isDirectory() && existsSync31(file)) return file;
|
|
19442
|
+
}
|
|
19443
|
+
} catch {
|
|
19444
|
+
}
|
|
19445
|
+
return null;
|
|
19446
|
+
}
|
|
19447
|
+
return jsonlFiles(join26(codexDir(), "sessions"), 4).find((f) => f.file.endsWith(`${sessionId}.jsonl`))?.file ?? null;
|
|
19448
|
+
}
|
|
19449
|
+
function readSessionMessages(agent, sessionId, afterUuid) {
|
|
19450
|
+
const file = findTranscript(agent, sessionId);
|
|
19451
|
+
if (!file) return null;
|
|
19452
|
+
const lines = parseLines(readFileSync25(file, "utf-8"));
|
|
19453
|
+
const messages = agent === "claude" ? claudeMessages(lines) : codexMessages(lines);
|
|
19454
|
+
if (!afterUuid) return messages;
|
|
19455
|
+
const idx = messages.findIndex((m) => m.uuid === afterUuid);
|
|
19456
|
+
return idx === -1 ? [] : messages.slice(idx + 1);
|
|
19457
|
+
}
|
|
19458
|
+
function bindSessions(items) {
|
|
19459
|
+
const store = new SessionStore(paths.DB_FILE);
|
|
19460
|
+
try {
|
|
19461
|
+
for (const i of items)
|
|
19462
|
+
store.saveSession(`chat:${i.channelId}`, i.threadId, i.sessionId, i.agent);
|
|
19463
|
+
} finally {
|
|
19464
|
+
store.close();
|
|
19465
|
+
}
|
|
19466
|
+
return items.length;
|
|
19467
|
+
}
|
|
19468
|
+
function handleSessionImportRequest(req) {
|
|
19469
|
+
switch (req.op) {
|
|
19470
|
+
case "scan":
|
|
19471
|
+
return { op: "scan", folders: scanSessionFolders(req.slug) };
|
|
19472
|
+
case "read":
|
|
19473
|
+
return { op: "read", messages: readSessionMessages(req.agent, req.sessionId, req.afterUuid) };
|
|
19474
|
+
case "bind":
|
|
19475
|
+
return { op: "bind", bound: bindSessions(req.items) };
|
|
19476
|
+
}
|
|
19477
|
+
}
|
|
19478
|
+
|
|
18999
19479
|
// src/dashboard/docs-relay-host.ts
|
|
19000
|
-
import { Hono as
|
|
19480
|
+
import { Hono as Hono36 } from "hono";
|
|
19001
19481
|
var RETURNED_HEADERS = ["content-type", "content-disposition", "content-security-policy", "cache-control"];
|
|
19002
19482
|
async function handleDocsRequest(req) {
|
|
19003
19483
|
return runRelayed(req, (app) => app.route("/channels", createDocsRouter({ gate: async (_c, next) => next() })));
|
|
19004
19484
|
}
|
|
19005
19485
|
async function runRelayed(req, register) {
|
|
19006
|
-
const app = new
|
|
19486
|
+
const app = new Hono36();
|
|
19007
19487
|
app.use("*", async (c, next) => {
|
|
19008
19488
|
c.set("auth", req.auth);
|
|
19009
19489
|
await next();
|
|
@@ -19026,7 +19506,7 @@ async function handleHostRequest(req) {
|
|
|
19026
19506
|
}
|
|
19027
19507
|
|
|
19028
19508
|
// src/dashboard/doc-share-publisher.ts
|
|
19029
|
-
import { readFileSync as
|
|
19509
|
+
import { readFileSync as readFileSync26, statSync as statSync9 } from "fs";
|
|
19030
19510
|
import { extname as extname2, posix } from "path";
|
|
19031
19511
|
var MAX_ASSET_BYTES = 20 * 1024 * 1024;
|
|
19032
19512
|
var MAX_CONTENT_BYTES2 = 2 * 1024 * 1024;
|
|
@@ -19141,7 +19621,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
19141
19621
|
}
|
|
19142
19622
|
if (!stat.isFile()) return { status: "not-found" };
|
|
19143
19623
|
if (stat.size > MAX_CONTENT_BYTES2) return { status: "too-large" };
|
|
19144
|
-
const source =
|
|
19624
|
+
const source = readFileSync26(abs, "utf8");
|
|
19145
19625
|
const title = extractTitle(abs, kind === "markdown" ? "md" : kind);
|
|
19146
19626
|
if (kind === "excalidraw") {
|
|
19147
19627
|
try {
|
|
@@ -19212,7 +19692,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
19212
19692
|
let text = null;
|
|
19213
19693
|
try {
|
|
19214
19694
|
const size = statSync9(docAbs).size;
|
|
19215
|
-
if (size <= budget) text =
|
|
19695
|
+
if (size <= budget) text = readFileSync26(docAbs, "utf8");
|
|
19216
19696
|
} catch {
|
|
19217
19697
|
continue;
|
|
19218
19698
|
}
|
|
@@ -19229,7 +19709,7 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
19229
19709
|
let body;
|
|
19230
19710
|
try {
|
|
19231
19711
|
if (statSync9(resolved2.abs).size > MAX_ASSET_BYTES) continue;
|
|
19232
|
-
body =
|
|
19712
|
+
body = readFileSync26(resolved2.abs);
|
|
19233
19713
|
} catch {
|
|
19234
19714
|
continue;
|
|
19235
19715
|
}
|
|
@@ -19257,13 +19737,13 @@ function readDocShareBundle({ cwd, sourcePath }) {
|
|
|
19257
19737
|
|
|
19258
19738
|
// src/dashboard/host-upgrade.ts
|
|
19259
19739
|
import { spawn as spawn7 } from "child_process";
|
|
19260
|
-
import { mkdirSync as mkdirSync13, openSync as
|
|
19261
|
-
import { join as
|
|
19740
|
+
import { mkdirSync as mkdirSync13, openSync as openSync4 } from "fs";
|
|
19741
|
+
import { join as join27 } from "path";
|
|
19262
19742
|
var running = false;
|
|
19263
19743
|
function startHostUpgrade() {
|
|
19264
19744
|
if (running) return { started: false };
|
|
19265
19745
|
mkdirSync13(paths.LOG_DIR, { recursive: true });
|
|
19266
|
-
const log5 =
|
|
19746
|
+
const log5 = openSync4(join27(paths.LOG_DIR, "host-upgrade.log"), "a");
|
|
19267
19747
|
const child = spawn7(process.execPath, [process.argv[1] ?? "", "upgrade"], {
|
|
19268
19748
|
detached: true,
|
|
19269
19749
|
stdio: ["ignore", log5, log5]
|
|
@@ -19284,7 +19764,7 @@ function dataSocketUrl() {
|
|
|
19284
19764
|
return `ws://127.0.0.1:${process.env["FLYSPACE_DATA_API_PORT"] ?? DEFAULT_DATA_API_PORT}${HOST_SOCKET_PATH}`;
|
|
19285
19765
|
}
|
|
19286
19766
|
function listLocalSessions() {
|
|
19287
|
-
if (!
|
|
19767
|
+
if (!existsSync32(paths.DB_FILE)) return [];
|
|
19288
19768
|
const store = new SessionStore(paths.DB_FILE);
|
|
19289
19769
|
try {
|
|
19290
19770
|
return store.listSessions();
|
|
@@ -19321,6 +19801,7 @@ function localAgentCallbacks(chatAgent) {
|
|
|
19321
19801
|
upgradeHost: () => startHostUpgrade(),
|
|
19322
19802
|
scanOnboarding: (slug) => scanOnboarding(slug),
|
|
19323
19803
|
linkProjectFolders: (slug, items) => linkProjectFolders(slug, items),
|
|
19804
|
+
sessionImportRequest: (req) => handleSessionImportRequest(req),
|
|
19324
19805
|
isHostOnline: () => true
|
|
19325
19806
|
};
|
|
19326
19807
|
}
|
|
@@ -19329,7 +19810,7 @@ function buildHostConfigSnapshot() {
|
|
|
19329
19810
|
const snapshot2 = {};
|
|
19330
19811
|
for (const profile of listProfiles()) {
|
|
19331
19812
|
snapshot2[profile] = runWithProfile(profile, () => ({
|
|
19332
|
-
env:
|
|
19813
|
+
env: existsSync32(paths.CONFIG_FILE) ? dotenv2.parse(readFileSync27(paths.CONFIG_FILE, "utf-8")) : {},
|
|
19333
19814
|
agentModel: loadDashboardSettings().agent
|
|
19334
19815
|
}));
|
|
19335
19816
|
}
|
|
@@ -19579,12 +20060,12 @@ function startPushWatcher() {
|
|
|
19579
20060
|
}
|
|
19580
20061
|
|
|
19581
20062
|
// src/dashboard/machine-server.ts
|
|
19582
|
-
import { existsSync as
|
|
19583
|
-
import { join as
|
|
20063
|
+
import { existsSync as existsSync33 } from "fs";
|
|
20064
|
+
import { join as join28 } from "path";
|
|
19584
20065
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
19585
20066
|
|
|
19586
20067
|
// src/dashboard/app-icon-routes.ts
|
|
19587
|
-
import { Hono as
|
|
20068
|
+
import { Hono as Hono37 } from "hono";
|
|
19588
20069
|
var NEUTRAL_MANIFEST = {
|
|
19589
20070
|
id: "/",
|
|
19590
20071
|
name: "Flyspace",
|
|
@@ -19622,7 +20103,7 @@ async function withResolvedProfile2(c, next) {
|
|
|
19622
20103
|
return profile ? runWithProfile(profile, () => next()) : next();
|
|
19623
20104
|
}
|
|
19624
20105
|
function createAppIconRouter() {
|
|
19625
|
-
const app = new
|
|
20106
|
+
const app = new Hono37();
|
|
19626
20107
|
app.get("/manifest.json", withResolvedProfile2, async (c) => {
|
|
19627
20108
|
if (!await resolvedProfileHost(c)) return c.json(NEUTRAL_MANIFEST);
|
|
19628
20109
|
const profile = getProfile();
|
|
@@ -19662,7 +20143,7 @@ function pngResponse(c, png) {
|
|
|
19662
20143
|
}
|
|
19663
20144
|
|
|
19664
20145
|
// src/dashboard/machine-server.ts
|
|
19665
|
-
var WEB_DIST =
|
|
20146
|
+
var WEB_DIST = join28(paths.WEB_ROOT, "dist");
|
|
19666
20147
|
async function bootMachine() {
|
|
19667
20148
|
await reconcileAll().catch(
|
|
19668
20149
|
(err) => console.error("[dashboard] reconcileAll failed:", err.message)
|
|
@@ -19682,14 +20163,14 @@ async function bootMachine() {
|
|
|
19682
20163
|
function mountSpa(app) {
|
|
19683
20164
|
app.route("/share", createPublicShareAssetRouter(WEB_DIST));
|
|
19684
20165
|
app.route("/", createAppIconRouter());
|
|
19685
|
-
if (
|
|
20166
|
+
if (existsSync33(WEB_DIST) || existsSync33(join28(paths.WEB_ROOT, "package.json"))) {
|
|
19686
20167
|
app.use("/*", serveStatic({ root: WEB_DIST }));
|
|
19687
20168
|
}
|
|
19688
20169
|
app.get("/assets/*", (c) => c.text("stale asset \u2014 reload for the current build", 404));
|
|
19689
|
-
const spaFallback = serveStatic({ path:
|
|
20170
|
+
const spaFallback = serveStatic({ path: join28(WEB_DIST, "index.html") });
|
|
19690
20171
|
app.get("*", async (c, next) => {
|
|
19691
|
-
if (!
|
|
19692
|
-
const isCheckout =
|
|
20172
|
+
if (!existsSync33(join28(WEB_DIST, "index.html"))) {
|
|
20173
|
+
const isCheckout = existsSync33(join28(paths.WEB_ROOT, "package.json"));
|
|
19693
20174
|
return c.text(
|
|
19694
20175
|
isCheckout ? `Workspace UI build missing (${WEB_DIST}).
|
|
19695
20176
|
Run \`pnpm build:web\` from the repo root.
|
|
@@ -19823,8 +20304,8 @@ function openBrowser2(url) {
|
|
|
19823
20304
|
}
|
|
19824
20305
|
}
|
|
19825
20306
|
function ensureWebBuild() {
|
|
19826
|
-
if (
|
|
19827
|
-
if (!
|
|
20307
|
+
if (existsSync34(join29(paths.WEB_ROOT, "dist", "index.html"))) return;
|
|
20308
|
+
if (!existsSync34(join29(paths.WEB_ROOT, "package.json"))) return;
|
|
19828
20309
|
console.log(" packages/ui/dist missing, building the dashboard UI...");
|
|
19829
20310
|
const result = spawnSync("pnpm", ["build"], {
|
|
19830
20311
|
cwd: paths.WEB_ROOT,
|
|
@@ -19874,7 +20355,7 @@ async function runDashboard(args) {
|
|
|
19874
20355
|
}
|
|
19875
20356
|
|
|
19876
20357
|
// src/commands/workspace.ts
|
|
19877
|
-
import { existsSync as
|
|
20358
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13 } from "fs";
|
|
19878
20359
|
var USAGE7 = `
|
|
19879
20360
|
Usage: flyspace workspace <subcommand> [options]
|
|
19880
20361
|
|
|
@@ -19960,7 +20441,7 @@ function provisionLocal(botName) {
|
|
|
19960
20441
|
ensureDirs();
|
|
19961
20442
|
console.log(` config dir ready (${paths.CONFIG_DIR})`);
|
|
19962
20443
|
console.log(` data dir ready (${paths.DATA_DIR})`);
|
|
19963
|
-
if (
|
|
20444
|
+
if (existsSync35(paths.CONFIG_FILE)) {
|
|
19964
20445
|
console.log(" config.env exists");
|
|
19965
20446
|
} else {
|
|
19966
20447
|
writeFileSync13(paths.CONFIG_FILE, `BOT_NAME=${botName}
|
|
@@ -19977,7 +20458,7 @@ async function provisionChannels(specs) {
|
|
|
19977
20458
|
continue;
|
|
19978
20459
|
}
|
|
19979
20460
|
const cwd = expandPath(spec.cwd);
|
|
19980
|
-
if (!
|
|
20461
|
+
if (!existsSync35(cwd)) {
|
|
19981
20462
|
mkdirSync14(cwd, { recursive: true });
|
|
19982
20463
|
console.log(` created cwd ${cwd}`);
|
|
19983
20464
|
}
|
|
@@ -20055,10 +20536,10 @@ async function runReconcileCmd(args) {
|
|
|
20055
20536
|
}
|
|
20056
20537
|
|
|
20057
20538
|
// src/cli.ts
|
|
20058
|
-
import { join as
|
|
20539
|
+
import { join as join30 } from "path";
|
|
20059
20540
|
var home = flyspaceHome();
|
|
20060
20541
|
if (home) {
|
|
20061
|
-
const bin =
|
|
20542
|
+
const bin = join30(home, "bin");
|
|
20062
20543
|
const path = process.env.PATH ?? "";
|
|
20063
20544
|
if (!path.split(":").includes(bin)) process.env.PATH = path ? `${bin}:${path}` : bin;
|
|
20064
20545
|
}
|
|
@@ -20196,7 +20677,7 @@ async function main() {
|
|
|
20196
20677
|
break;
|
|
20197
20678
|
case "-v":
|
|
20198
20679
|
case "--version":
|
|
20199
|
-
printRaw("0.1.
|
|
20680
|
+
printRaw("0.1.34");
|
|
20200
20681
|
break;
|
|
20201
20682
|
case "-h":
|
|
20202
20683
|
case "--help":
|
package/dist/index.js
CHANGED