flyspace-cli 0.1.41 → 0.1.42
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 +129 -69
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19487,7 +19487,7 @@ async function runAuthCmd(args) {
|
|
|
19487
19487
|
|
|
19488
19488
|
// ../api/src/version.ts
|
|
19489
19489
|
import { readFileSync as readFileSync8 } from "fs";
|
|
19490
|
-
var CLI_VERSION = true ? "0.1.
|
|
19490
|
+
var CLI_VERSION = true ? "0.1.42" : devVersion();
|
|
19491
19491
|
function isOlderVersion(a, b) {
|
|
19492
19492
|
const parse = (v) => v.replace(/^v/, "").split("-")[0].split(".").map(Number);
|
|
19493
19493
|
const [pa, pb] = [parse(a), parse(b)];
|
|
@@ -19672,9 +19672,9 @@ import { tmpdir } from "os";
|
|
|
19672
19672
|
import { promisify } from "util";
|
|
19673
19673
|
var execAsync = promisify(execFile);
|
|
19674
19674
|
var exec = (cmd, args, opts) => {
|
|
19675
|
-
const
|
|
19676
|
-
|
|
19677
|
-
return
|
|
19675
|
+
const run5 = execAsync(cmd, args, opts);
|
|
19676
|
+
run5.child.stdin?.end();
|
|
19677
|
+
return run5;
|
|
19678
19678
|
};
|
|
19679
19679
|
var BOLD3 = "\x1B[1m";
|
|
19680
19680
|
var DIM3 = "\x1B[2m";
|
|
@@ -20111,7 +20111,7 @@ function npmEnv(target) {
|
|
|
20111
20111
|
return nodeBin ? { ...process.env, PATH: `${nodeBin}:${process.env.PATH ?? ""}` } : process.env;
|
|
20112
20112
|
}
|
|
20113
20113
|
async function runUpgrade() {
|
|
20114
|
-
const current = "0.1.
|
|
20114
|
+
const current = "0.1.42";
|
|
20115
20115
|
console.log(`Current version: ${current}`);
|
|
20116
20116
|
console.log("Checking for updates...\n");
|
|
20117
20117
|
try {
|
|
@@ -25331,9 +25331,9 @@ async function synthesizeWithRetry(script, voice) {
|
|
|
25331
25331
|
}
|
|
25332
25332
|
var renderQueue = Promise.resolve();
|
|
25333
25333
|
function queued(job) {
|
|
25334
|
-
const
|
|
25335
|
-
renderQueue =
|
|
25336
|
-
return
|
|
25334
|
+
const run5 = renderQueue.then(job, job);
|
|
25335
|
+
renderQueue = run5.catch(() => void 0);
|
|
25336
|
+
return run5;
|
|
25337
25337
|
}
|
|
25338
25338
|
function renderArticleAudio(title, content, lang) {
|
|
25339
25339
|
return queued(() => renderArticleAudioNow(title, content, lang));
|
|
@@ -25407,12 +25407,12 @@ function queueShareAudio(share) {
|
|
|
25407
25407
|
}
|
|
25408
25408
|
const current = inFlight2.get(share.id);
|
|
25409
25409
|
if (current?.hash === hash) return current.done;
|
|
25410
|
-
const
|
|
25410
|
+
const run5 = () => runShareAudioJob(share, hash).catch((err) => {
|
|
25411
25411
|
console.error(`share audio job crashed (${share.id}):`, err);
|
|
25412
25412
|
}).finally(() => {
|
|
25413
25413
|
if (inFlight2.get(share.id)?.hash === hash) inFlight2.delete(share.id);
|
|
25414
25414
|
});
|
|
25415
|
-
const done = current ? current.done.then(
|
|
25415
|
+
const done = current ? current.done.then(run5) : run5();
|
|
25416
25416
|
inFlight2.set(share.id, { hash, done });
|
|
25417
25417
|
return done;
|
|
25418
25418
|
}
|
|
@@ -29973,22 +29973,13 @@ function createTranscribeRouter() {
|
|
|
29973
29973
|
import { Hono as Hono19 } from "hono";
|
|
29974
29974
|
var GROQ_API_URL8 = "https://api.groq.com/openai/v1/chat/completions";
|
|
29975
29975
|
var MODEL2 = "openai/gpt-oss-120b";
|
|
29976
|
-
var
|
|
29977
|
-
var
|
|
29976
|
+
var MAX_REPLY_CHARS = 4e3;
|
|
29977
|
+
var MAX_EARLIER_CHARS = 1500;
|
|
29978
29978
|
var THREAD_TAIL_REPLIES = 5;
|
|
29979
|
-
var
|
|
29980
|
-
|
|
29981
|
-
|
|
29982
|
-
|
|
29983
|
-
Never add greetings, thanks, pleasantries, filler, or generic chat phrases. Never repeat the draft.
|
|
29984
|
-
Output ONLY the continuation text (one sentence at most), no quotes or explanation, in the draft's language. Start with a space if the continuation needs one.
|
|
29985
|
-
If you cannot predict a specific, useful continuation, output nothing.`;
|
|
29986
|
-
function hasIntent(before) {
|
|
29987
|
-
const paragraph = before.split("\n").pop() ?? "";
|
|
29988
|
-
const cjk = paragraph.match(/[\u3400-\u9fff]/g)?.length ?? 0;
|
|
29989
|
-
const words = paragraph.match(/[A-Za-z0-9]+/g)?.length ?? 0;
|
|
29990
|
-
return cjk >= MIN_CJK_CHARS || words >= MIN_WORDS || cjk + words * 2 >= MIN_CJK_CHARS;
|
|
29991
|
-
}
|
|
29979
|
+
var RULES2 = `You predict the next message a user will send in a chat thread, right after the AI agent's last reply, like Claude Code's prompt suggestions. The composer is empty; you suggest the whole message.
|
|
29980
|
+
Base it on the agent's last reply: answer a question it asked, approve or pick an option it offered, or ask for the obvious next step (commit, deploy, run the tests, check a specific file or feature it named). Use specific nouns from the reply.
|
|
29981
|
+
Keep it short (under 15 words), in the language the user writes in. No greetings, thanks or filler.
|
|
29982
|
+
Output ONLY the message text, no quotes or explanation. If there is no clear next step, output nothing.`;
|
|
29992
29983
|
function createAutocompleteRouter() {
|
|
29993
29984
|
const app = new Hono19();
|
|
29994
29985
|
app.post("/autocomplete", async (c) => {
|
|
@@ -29997,15 +29988,14 @@ function createAutocompleteRouter() {
|
|
|
29997
29988
|
const apiKey = process.env["GROQ_API_KEY"];
|
|
29998
29989
|
if (!apiKey) return c.json({ error: "autocomplete is not configured" }, 503);
|
|
29999
29990
|
const body = await c.req.json().catch(() => null);
|
|
30000
|
-
const before = typeof body?.before === "string" ? body.before.slice(-MAX_BEFORE_CHARS) : "";
|
|
30001
|
-
if (!hasIntent(before)) return c.json({ text: "" });
|
|
30002
29991
|
const channel = typeof body?.channelId === "string" ? getChannelRegistry().find((ch) => ch.id === body.channelId) : void 0;
|
|
30003
|
-
|
|
30004
|
-
|
|
30005
|
-
const
|
|
30006
|
-
|
|
30007
|
-
${
|
|
30008
|
-
` : ""
|
|
29992
|
+
if (!channel || !canSeeChannel(channel, c.get("auth")) || typeof body?.threadId !== "string")
|
|
29993
|
+
return c.json({ text: "" });
|
|
29994
|
+
const thread = await threadContext(channel, body.threadId);
|
|
29995
|
+
if (!thread) return c.json({ text: "" });
|
|
29996
|
+
const context = `Channel: #${channel.name}${channel.prompt ? `
|
|
29997
|
+
Channel instructions: ${channel.prompt.slice(0, 500)}` : ""}
|
|
29998
|
+
${thread}`;
|
|
30009
29999
|
const res = await fetch(GROQ_API_URL8, {
|
|
30010
30000
|
method: "POST",
|
|
30011
30001
|
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
@@ -30019,8 +30009,7 @@ ${thread}
|
|
|
30019
30009
|
temperature: 0.2,
|
|
30020
30010
|
messages: [
|
|
30021
30011
|
{ role: "system", content: RULES2 },
|
|
30022
|
-
{ role: "user", content:
|
|
30023
|
-
${before}` }
|
|
30012
|
+
{ role: "user", content: context }
|
|
30024
30013
|
]
|
|
30025
30014
|
})
|
|
30026
30015
|
}).catch(() => null);
|
|
@@ -30041,15 +30030,18 @@ async function threadContext(channel, threadId) {
|
|
|
30041
30030
|
]);
|
|
30042
30031
|
const channelId = thread.data?.channel_id;
|
|
30043
30032
|
if (!channelId || channelId !== channel.id && channelId !== channel.externalChannelId) return "";
|
|
30033
|
+
const [last, ...older] = tail.data ?? [];
|
|
30034
|
+
if (!last || last.author !== AGENT_AUTHOR) return "";
|
|
30044
30035
|
const rows = [...opener.data ?? []];
|
|
30045
|
-
for (const r of (
|
|
30046
|
-
|
|
30047
|
-
const text = rows.map((r) => `${r.author}: ${r.body}`).join("\n\n").slice(-MAX_THREAD_CHARS);
|
|
30036
|
+
for (const r of older.reverse()) if (!rows.some((x) => x.id === r.id)) rows.push(r);
|
|
30037
|
+
const earlier = rows.filter((r) => r.id !== last.id).map((r) => `${r.author}: ${r.body}`).join("\n\n").slice(-MAX_EARLIER_CHARS);
|
|
30048
30038
|
const title = thread.data?.title ? `Thread title: ${thread.data.title}
|
|
30049
30039
|
` : "";
|
|
30050
|
-
return
|
|
30051
|
-
${
|
|
30052
|
-
|
|
30040
|
+
return `${title}Earlier messages:
|
|
30041
|
+
${earlier}
|
|
30042
|
+
|
|
30043
|
+
Agent's last reply:
|
|
30044
|
+
${last.body.slice(-MAX_REPLY_CHARS)}`;
|
|
30053
30045
|
}
|
|
30054
30046
|
|
|
30055
30047
|
// ../api/src/dashboard/avatars-routes.ts
|
|
@@ -31782,10 +31774,10 @@ async function buildChannelUsageReport(channelId, days, labelChannel) {
|
|
|
31782
31774
|
}
|
|
31783
31775
|
}
|
|
31784
31776
|
var COMPACT_THRESHOLD_TOKENS = Number(process.env["SESSION_COMPACT_THRESHOLD_TOKENS"]) || 14e4;
|
|
31785
|
-
function contextOf(
|
|
31786
|
-
if (!
|
|
31787
|
-
const total =
|
|
31788
|
-
const turns =
|
|
31777
|
+
function contextOf(run5) {
|
|
31778
|
+
if (!run5) return void 0;
|
|
31779
|
+
const total = run5.inputTokens + run5.cacheReadTokens + run5.cacheCreationTokens;
|
|
31780
|
+
const turns = run5.numTurns ?? 1;
|
|
31789
31781
|
return Math.round(turns > 0 ? total / turns : total);
|
|
31790
31782
|
}
|
|
31791
31783
|
async function buildThreadUsage(threadTs) {
|
|
@@ -32587,6 +32579,12 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
32587
32579
|
if (!res) return c.json({ error: "Host is offline" }, 503);
|
|
32588
32580
|
return c.json(res, 202);
|
|
32589
32581
|
});
|
|
32582
|
+
api.get("/mcp-servers", async (c) => {
|
|
32583
|
+
const auth = c.get("auth");
|
|
32584
|
+
if (!await isHostOwner(auth)) return c.json({ error: "only the Host owner can view MCP servers" }, 403);
|
|
32585
|
+
const res = await agent.listMcpServers?.();
|
|
32586
|
+
return c.json(res ?? { servers: null });
|
|
32587
|
+
});
|
|
32590
32588
|
api.get("/channels", async (c) => {
|
|
32591
32589
|
const auth = c.get("auth");
|
|
32592
32590
|
const channels = getProfileChannelRegistry().filter((ch) => canSeeChannel(ch, auth));
|
|
@@ -32645,17 +32643,17 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
32645
32643
|
const auto = loadEffectiveAutomations();
|
|
32646
32644
|
const runs2 = /* @__PURE__ */ new Map();
|
|
32647
32645
|
try {
|
|
32648
|
-
for (const [name,
|
|
32649
|
-
runs2.set(name, { lastRun:
|
|
32646
|
+
for (const [name, run5] of await lastRunsForWorkspace(getProfile())) {
|
|
32647
|
+
runs2.set(name, { lastRun: run5.lastRunAt.toISOString(), lastStatus: run5.lastStatus });
|
|
32650
32648
|
}
|
|
32651
32649
|
} catch {
|
|
32652
32650
|
const runStore = existsSync20(paths.DB_FILE) ? getLocalCache().openAutomationRunStore(paths.DB_FILE) : null;
|
|
32653
32651
|
if (runStore) {
|
|
32654
32652
|
try {
|
|
32655
|
-
for (const
|
|
32656
|
-
runs2.set(
|
|
32657
|
-
lastRun:
|
|
32658
|
-
lastStatus:
|
|
32653
|
+
for (const run5 of runStore.listLastRuns()) {
|
|
32654
|
+
runs2.set(run5.name, {
|
|
32655
|
+
lastRun: run5.lastRunAt.toISOString(),
|
|
32656
|
+
lastStatus: run5.lastStatus
|
|
32659
32657
|
});
|
|
32660
32658
|
}
|
|
32661
32659
|
} finally {
|
|
@@ -32846,17 +32844,17 @@ function registerDataRoutes(api, agent = {}) {
|
|
|
32846
32844
|
}
|
|
32847
32845
|
const threadLinks = await resolveThreadLinks(raw);
|
|
32848
32846
|
const sessions2 = raw.map((s) => {
|
|
32849
|
-
const
|
|
32850
|
-
const turns =
|
|
32851
|
-
const total =
|
|
32847
|
+
const run5 = latest2.get(s.threadTs);
|
|
32848
|
+
const turns = run5?.numTurns ?? 1;
|
|
32849
|
+
const total = run5 ? run5.inputTokens + run5.cacheReadTokens + run5.cacheCreationTokens : void 0;
|
|
32852
32850
|
const link = s.channelId.startsWith("chat:") ? threadLinks.chat.get(s.threadTs) : threadLinks.external.get(`${s.channelId}:${s.threadTs}`);
|
|
32853
32851
|
return {
|
|
32854
32852
|
...s,
|
|
32855
32853
|
channelLabel: channelLabel(s.channelId),
|
|
32856
|
-
model:
|
|
32857
|
-
numTurns:
|
|
32854
|
+
model: run5?.model,
|
|
32855
|
+
numTurns: run5?.numTurns,
|
|
32858
32856
|
contextTokens: total !== void 0 ? Math.round(total / (turns > 0 ? turns : 1)) : void 0,
|
|
32859
|
-
rolledOver:
|
|
32857
|
+
rolledOver: run5?.label === "rollover",
|
|
32860
32858
|
threadTitle: link?.title ?? void 0,
|
|
32861
32859
|
chatChannelId: link?.chatChannelId,
|
|
32862
32860
|
chatThreadId: link?.chatThreadId,
|
|
@@ -34880,21 +34878,21 @@ var ChatAgent = class {
|
|
|
34880
34878
|
* kill.
|
|
34881
34879
|
*/
|
|
34882
34880
|
async maybeSupersede(msg) {
|
|
34883
|
-
const
|
|
34884
|
-
if (!
|
|
34881
|
+
const run5 = this.activeRuns.get(msg.threadId);
|
|
34882
|
+
if (!run5 || run5.msg.author !== msg.author || run5.msg.replyId === msg.replyId) return;
|
|
34885
34883
|
try {
|
|
34886
|
-
if (!await this.shouldInterrupt(
|
|
34884
|
+
if (!await this.shouldInterrupt(run5.msg.body, msg.body)) return;
|
|
34887
34885
|
} catch (err) {
|
|
34888
34886
|
console.error("[chat-agent] interrupt classifier failed:", err.message);
|
|
34889
34887
|
return;
|
|
34890
34888
|
}
|
|
34891
|
-
if (this.activeRuns.get(msg.threadId) !==
|
|
34889
|
+
if (this.activeRuns.get(msg.threadId) !== run5) return;
|
|
34892
34890
|
console.log(
|
|
34893
34891
|
`[chat-agent] follow-up supersedes in-flight run \u2014 aborting. thread=${msg.threadId}`
|
|
34894
34892
|
);
|
|
34895
|
-
|
|
34893
|
+
run5.superseded = true;
|
|
34896
34894
|
this.supersedingReplies.add(msg.replyId);
|
|
34897
|
-
|
|
34895
|
+
run5.abort.abort();
|
|
34898
34896
|
}
|
|
34899
34897
|
/**
|
|
34900
34898
|
* Picks up messages nobody answered. `handleMessage` is fire-and-forget and
|
|
@@ -35102,11 +35100,11 @@ var ChatAgent = class {
|
|
|
35102
35100
|
* continue with `--resume`. Returns whether a run was actually in flight —
|
|
35103
35101
|
* a queued-but-not-started message is not affected. */
|
|
35104
35102
|
cancel(threadId) {
|
|
35105
|
-
const
|
|
35106
|
-
if (!
|
|
35107
|
-
|
|
35108
|
-
this.stoppedReplies.set(threadId,
|
|
35109
|
-
|
|
35103
|
+
const run5 = this.activeRuns.get(threadId);
|
|
35104
|
+
if (!run5) return false;
|
|
35105
|
+
run5.superseded = true;
|
|
35106
|
+
this.stoppedReplies.set(threadId, run5.msg.replyId);
|
|
35107
|
+
run5.abort.abort();
|
|
35110
35108
|
return true;
|
|
35111
35109
|
}
|
|
35112
35110
|
/**
|
|
@@ -38023,6 +38021,67 @@ function startHostUpgrade() {
|
|
|
38023
38021
|
return { started: true };
|
|
38024
38022
|
}
|
|
38025
38023
|
|
|
38024
|
+
// src/dashboard/mcp-servers-host.ts
|
|
38025
|
+
import { execFile as execFile2 } from "child_process";
|
|
38026
|
+
import { homedir as homedir11 } from "os";
|
|
38027
|
+
import { promisify as promisify2 } from "util";
|
|
38028
|
+
|
|
38029
|
+
// ../api/src/dashboard/mcp-servers.ts
|
|
38030
|
+
var LINE = /^(.+?): (.+) - [✔✘!✓✗] ?(.*)$/u;
|
|
38031
|
+
function redactTarget(raw) {
|
|
38032
|
+
const typed = raw.match(/^(.*) \((HTTP|SSE)\)$/);
|
|
38033
|
+
const value = typed ? typed[1] : raw;
|
|
38034
|
+
if (/^https?:\/\//.test(value)) {
|
|
38035
|
+
try {
|
|
38036
|
+
const url = new URL(value);
|
|
38037
|
+
return {
|
|
38038
|
+
transport: typed?.[2] === "SSE" ? "sse" : "http",
|
|
38039
|
+
target: `${url.origin}${url.pathname}`
|
|
38040
|
+
};
|
|
38041
|
+
} catch {
|
|
38042
|
+
return { transport: "http", target: "" };
|
|
38043
|
+
}
|
|
38044
|
+
}
|
|
38045
|
+
const command = value.split(/\s+/)[0] ?? "";
|
|
38046
|
+
return { transport: "stdio", target: command.split("/").pop() ?? "" };
|
|
38047
|
+
}
|
|
38048
|
+
function statusOf(text) {
|
|
38049
|
+
if (/^connected/i.test(text)) return "connected";
|
|
38050
|
+
if (/auth/i.test(text)) return "needs-auth";
|
|
38051
|
+
if (/fail|error|closed/i.test(text)) return "failed";
|
|
38052
|
+
return "unknown";
|
|
38053
|
+
}
|
|
38054
|
+
function parseMcpList(output) {
|
|
38055
|
+
const servers = [];
|
|
38056
|
+
for (const line of output.split("\n")) {
|
|
38057
|
+
const m = line.trim().match(LINE);
|
|
38058
|
+
if (!m) continue;
|
|
38059
|
+
const name = m[1].trim();
|
|
38060
|
+
const detail = m[3].trim();
|
|
38061
|
+
const source = name.startsWith("claude.ai ") ? "claude.ai" : name.startsWith("plugin:") ? "plugin" : "local";
|
|
38062
|
+
servers.push({
|
|
38063
|
+
name: source === "claude.ai" ? name.slice("claude.ai ".length) : name,
|
|
38064
|
+
...redactTarget(m[2].trim()),
|
|
38065
|
+
status: statusOf(detail),
|
|
38066
|
+
detail,
|
|
38067
|
+
source
|
|
38068
|
+
});
|
|
38069
|
+
}
|
|
38070
|
+
return servers;
|
|
38071
|
+
}
|
|
38072
|
+
|
|
38073
|
+
// src/dashboard/mcp-servers-host.ts
|
|
38074
|
+
var run4 = promisify2(execFile2);
|
|
38075
|
+
async function listMcpServers() {
|
|
38076
|
+
try {
|
|
38077
|
+
const { stdout } = await run4("claude", ["mcp", "list"], { cwd: homedir11(), timeout: 45e3 });
|
|
38078
|
+
return { servers: parseMcpList(stdout) };
|
|
38079
|
+
} catch (err) {
|
|
38080
|
+
console.error(`[mcp-servers] claude mcp list failed: ${err.message}`);
|
|
38081
|
+
return { servers: [], error: "Could not run `claude mcp list` on the Host." };
|
|
38082
|
+
}
|
|
38083
|
+
}
|
|
38084
|
+
|
|
38026
38085
|
// src/dashboard/agent-bridge-host.ts
|
|
38027
38086
|
var HOSTED_DATA_SOCKET_ORIGIN = "wss://flyspace-api.fly.dev";
|
|
38028
38087
|
function dataSocketUrl() {
|
|
@@ -38067,6 +38126,7 @@ function localAgentCallbacks(chatAgent) {
|
|
|
38067
38126
|
docsRequest: (req) => handleDocsRequest(req),
|
|
38068
38127
|
hostRequest: (req) => handleHostRequest(req),
|
|
38069
38128
|
listSessions: () => listLocalSessions(),
|
|
38129
|
+
listMcpServers: () => listMcpServers(),
|
|
38070
38130
|
upgradeHost: () => startHostUpgrade(),
|
|
38071
38131
|
scanOnboarding: (slug) => scanOnboarding(slug),
|
|
38072
38132
|
linkProjectFolders: (slug, items) => linkProjectFolders(slug, items),
|
|
@@ -38961,7 +39021,7 @@ async function main() {
|
|
|
38961
39021
|
break;
|
|
38962
39022
|
case "-v":
|
|
38963
39023
|
case "--version":
|
|
38964
|
-
printRaw("0.1.
|
|
39024
|
+
printRaw("0.1.42");
|
|
38965
39025
|
break;
|
|
38966
39026
|
case "-h":
|
|
38967
39027
|
case "--help":
|