flyspace-cli 0.1.32 → 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-6YFDWUNF.js → chunk-KTMAS4TP.js} +126 -18
- package/dist/cli.js +939 -127
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -102,6 +102,9 @@ function loadConfig() {
|
|
|
102
102
|
const autoRespondChannelIds = new Set(
|
|
103
103
|
(process.env["AUTO_RESPOND_CHANNEL_IDS"] ?? "").split(",").map((s) => s.trim()).filter(Boolean)
|
|
104
104
|
);
|
|
105
|
+
const slackEnglishOnlyExemptChannelIds = new Set(
|
|
106
|
+
(process.env["SLACK_ENGLISH_ONLY_EXEMPT_CHANNEL_IDS"] ?? "").split(",").map((s) => s.trim()).filter(Boolean)
|
|
107
|
+
);
|
|
105
108
|
const slack = slackConfigured();
|
|
106
109
|
return {
|
|
107
110
|
slackBotToken: slack ? requireEnv("SLACK_BOT_TOKEN") : void 0,
|
|
@@ -118,7 +121,9 @@ function loadConfig() {
|
|
|
118
121
|
maxBudgetUsd: parseBudget(process.env["MAX_BUDGET_USD"]),
|
|
119
122
|
ownerUserId,
|
|
120
123
|
autoRespondChannelIds,
|
|
121
|
-
codexModel: process.env["CUSTIE_CODEX_MODEL"]?.trim() || DEFAULT_CODEX_MODEL
|
|
124
|
+
codexModel: process.env["CUSTIE_CODEX_MODEL"]?.trim() || DEFAULT_CODEX_MODEL,
|
|
125
|
+
slackEnglishOnly: process.env["SLACK_ENGLISH_ONLY"] === "true",
|
|
126
|
+
slackEnglishOnlyExemptChannelIds
|
|
122
127
|
};
|
|
123
128
|
}
|
|
124
129
|
|
|
@@ -290,6 +295,15 @@ function resolveLogicalCwd(p) {
|
|
|
290
295
|
function isUnderLogicalRoot(cwd) {
|
|
291
296
|
return [logicalRoot(), ...workspaceRoots.values()].some((r) => cwd.startsWith(r + sep));
|
|
292
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
|
+
}
|
|
293
307
|
function ensureLogicalCwdDir(cwd) {
|
|
294
308
|
if (!isUnderLogicalRoot(cwd) || existsSync2(cwd)) return;
|
|
295
309
|
mkdirSync(cwd, { recursive: true });
|
|
@@ -356,6 +370,7 @@ function rowToChannel(row) {
|
|
|
356
370
|
isDefault: row.is_default === true,
|
|
357
371
|
profile: row.profile ?? "default",
|
|
358
372
|
archivedAt: row.archived_at,
|
|
373
|
+
greetedAt: row.greeted_at,
|
|
359
374
|
createdAt: row.created_at,
|
|
360
375
|
updatedAt: row.updated_at
|
|
361
376
|
};
|
|
@@ -516,7 +531,7 @@ function resolveEffort(explicitEffort, channelId, fallback) {
|
|
|
516
531
|
function toRow(input) {
|
|
517
532
|
const row = {};
|
|
518
533
|
if (input.name !== void 0) row["name"] = input.name;
|
|
519
|
-
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));
|
|
520
535
|
if (input.categoryId !== void 0) row["category_id"] = input.categoryId;
|
|
521
536
|
if (input.externalChannelId !== void 0) row["external_channel_id"] = input.externalChannelId;
|
|
522
537
|
if (input.model !== void 0) row["model"] = input.model;
|
|
@@ -616,6 +631,12 @@ async function updateChannelById(id, patch) {
|
|
|
616
631
|
await refreshChannelRegistry();
|
|
617
632
|
return rowToChannel(data);
|
|
618
633
|
}
|
|
634
|
+
async function markChannelGreeted(id) {
|
|
635
|
+
const { data, error } = await getSupabaseClient().from("channels").update({ greeted_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id).is("greeted_at", null).select("id");
|
|
636
|
+
if (error) throw new Error(error.message);
|
|
637
|
+
await refreshChannelRegistry();
|
|
638
|
+
return (data ?? []).length > 0;
|
|
639
|
+
}
|
|
619
640
|
async function updateChannelsMcpConnections(updates) {
|
|
620
641
|
if (!updates.length) return;
|
|
621
642
|
const supabase = getSupabaseClient();
|
|
@@ -2575,14 +2596,15 @@ var workspaceCache = /* @__PURE__ */ new Map();
|
|
|
2575
2596
|
async function getWorkspaceBySlug(slug) {
|
|
2576
2597
|
const cached = workspaceCache.get(slug);
|
|
2577
2598
|
if (cached && cached.expiresAt > Date.now()) return cached.workspace;
|
|
2578
|
-
const { data, error } = await getSupabaseClient().from("workspaces").select("id, slug, agent_connected_at, projects_setup_at, plan").eq("slug", slug).maybeSingle();
|
|
2599
|
+
const { data, error } = await getSupabaseClient().from("workspaces").select("id, slug, agent_connected_at, projects_setup_at, plan, archived_at").eq("slug", slug).maybeSingle();
|
|
2579
2600
|
if (error) throw new Error(`getWorkspaceBySlug(${slug}): ${error.message}`);
|
|
2580
2601
|
const workspace = data ? {
|
|
2581
2602
|
id: data.id,
|
|
2582
2603
|
slug: data.slug,
|
|
2583
2604
|
agentConnectedAt: data.agent_connected_at,
|
|
2584
2605
|
projectsSetupAt: data.projects_setup_at,
|
|
2585
|
-
plan: data.plan ?? null
|
|
2606
|
+
plan: data.plan ?? null,
|
|
2607
|
+
archivedAt: data.archived_at ?? null
|
|
2586
2608
|
} : null;
|
|
2587
2609
|
workspaceCache.set(slug, { workspace, expiresAt: Date.now() + WORKSPACE_CACHE_TTL_MS });
|
|
2588
2610
|
return workspace;
|
|
@@ -2597,29 +2619,70 @@ async function listWorkspaces() {
|
|
|
2597
2619
|
if (error) throw new Error(`listWorkspaces: ${error.message}`);
|
|
2598
2620
|
return data ?? [];
|
|
2599
2621
|
}
|
|
2622
|
+
var TRIAL_DAYS = 14;
|
|
2600
2623
|
async function listWorkspaceSummaries() {
|
|
2601
2624
|
const db = getSupabaseClient();
|
|
2602
|
-
const [workspaces, settings, channels, memberships] = await Promise.all([
|
|
2603
|
-
db.from("workspaces").select("id, slug"),
|
|
2604
|
-
db.from("workspace_settings").select("profile, display_name"),
|
|
2625
|
+
const [workspaces, settings, channels, memberships, activity] = await Promise.all([
|
|
2626
|
+
db.from("workspaces").select("id, slug, created_at, agent_connected_at, plan, plan_status, archived_at"),
|
|
2627
|
+
db.from("workspace_settings").select("profile, display_name, logo"),
|
|
2605
2628
|
db.from("channels").select("profile").is("archived_at", null),
|
|
2606
|
-
db.from("memberships").select("workspace_id").eq("status", "active")
|
|
2629
|
+
db.from("memberships").select("workspace_id, user_id, email, handle, role, accepted_at").eq("status", "active").eq("is_support", false).order("invited_at", { ascending: true }),
|
|
2630
|
+
db.rpc("workspace_last_activity")
|
|
2607
2631
|
]);
|
|
2608
|
-
for (const r of [workspaces, settings, channels, memberships]) {
|
|
2632
|
+
for (const r of [workspaces, settings, channels, memberships, activity]) {
|
|
2609
2633
|
if (r.error) throw new Error(`listWorkspaceSummaries: ${r.error.message}`);
|
|
2610
2634
|
}
|
|
2611
2635
|
const displayNames = new Map(
|
|
2612
2636
|
(settings.data ?? []).map((s) => [s.profile, s.display_name])
|
|
2613
2637
|
);
|
|
2638
|
+
const logos = new Map(
|
|
2639
|
+
(settings.data ?? []).map((s) => [s.profile, s.logo])
|
|
2640
|
+
);
|
|
2641
|
+
const lastActivity = new Map(
|
|
2642
|
+
(activity.data ?? []).map((a) => [
|
|
2643
|
+
a.profile,
|
|
2644
|
+
a.last_activity_at
|
|
2645
|
+
])
|
|
2646
|
+
);
|
|
2614
2647
|
const tally = (keys) => keys.reduce((m, k) => m.set(k, (m.get(k) ?? 0) + 1), /* @__PURE__ */ new Map());
|
|
2615
2648
|
const channelCounts = tally((channels.data ?? []).map((c) => c.profile));
|
|
2616
2649
|
const memberCounts = tally((memberships.data ?? []).map((m) => m.workspace_id));
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2650
|
+
const owners = /* @__PURE__ */ new Map();
|
|
2651
|
+
for (const m of memberships.data ?? []) {
|
|
2652
|
+
if (m.role !== "admin" || owners.has(m.workspace_id)) continue;
|
|
2653
|
+
owners.set(m.workspace_id, {
|
|
2654
|
+
userId: m.user_id,
|
|
2655
|
+
email: m.email,
|
|
2656
|
+
handle: m.handle,
|
|
2657
|
+
joinedAt: m.accepted_at ?? null
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
2660
|
+
return (workspaces.data ?? []).map((w) => {
|
|
2661
|
+
const createdAt = w.created_at;
|
|
2662
|
+
const planStatus = w.plan_status ?? null;
|
|
2663
|
+
return {
|
|
2664
|
+
id: w.id,
|
|
2665
|
+
slug: w.slug,
|
|
2666
|
+
createdAt,
|
|
2667
|
+
agentConnectedAt: w.agent_connected_at,
|
|
2668
|
+
plan: w.plan ?? null,
|
|
2669
|
+
planStatus,
|
|
2670
|
+
trialEndsAt: planStatus === "trialing" ? new Date(new Date(createdAt).getTime() + TRIAL_DAYS * 864e5).toISOString() : null,
|
|
2671
|
+
archivedAt: w.archived_at ?? null,
|
|
2672
|
+
displayName: displayNames.get(w.slug) || null,
|
|
2673
|
+
logo: logos.get(w.slug) || null,
|
|
2674
|
+
channelCount: channelCounts.get(w.slug) ?? 0,
|
|
2675
|
+
memberCount: memberCounts.get(w.id) ?? 0,
|
|
2676
|
+
lastActivityAt: lastActivity.get(w.slug) ?? null,
|
|
2677
|
+
owner: owners.get(w.id) ?? null
|
|
2678
|
+
};
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2681
|
+
async function setWorkspaceArchived(workspaceId, archived) {
|
|
2682
|
+
const { data, error } = await getSupabaseClient().from("workspaces").update({ archived_at: archived ? (/* @__PURE__ */ new Date()).toISOString() : null }).eq("id", workspaceId).select("slug");
|
|
2683
|
+
if (error) throw new Error(`setWorkspaceArchived(${workspaceId}): ${error.message}`);
|
|
2684
|
+
if (!data?.length) throw new Error("workspace not found");
|
|
2685
|
+
for (const row of data) workspaceCache.delete(row.slug);
|
|
2623
2686
|
}
|
|
2624
2687
|
async function createWorkspace(slug) {
|
|
2625
2688
|
const { data, error } = await getSupabaseClient().from("workspaces").insert({ slug }).select("id, slug").single();
|
|
@@ -5383,6 +5446,29 @@ async function forkThreadWithSummary(ctx, deps) {
|
|
|
5383
5446
|
return { forked: true, newThreadTs };
|
|
5384
5447
|
}
|
|
5385
5448
|
|
|
5449
|
+
// src/slack/reply-language.ts
|
|
5450
|
+
var CJK = /[-ヿ㐀-䶿一-鿿가-豈-]/g;
|
|
5451
|
+
var CJK_THRESHOLD = 10;
|
|
5452
|
+
var REWRITE_PROMPT = "[system: this Slack channel is English-only. Your previous reply was NOT posted because it was not in English. Rewrite that same reply in English (en-AU spelling): same content, same structure, and keep any [BUTTONS: ...] marker with its labels translated. Do not run any tools and do not mention this rewrite. Output only the rewritten reply.]";
|
|
5453
|
+
function isEnglishOnlyChannel(config, channelId) {
|
|
5454
|
+
return config.slackEnglishOnly && !config.slackEnglishOnlyExemptChannelIds.has(channelId);
|
|
5455
|
+
}
|
|
5456
|
+
function isNonEnglishReply(text) {
|
|
5457
|
+
const prose = text.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "");
|
|
5458
|
+
return (prose.match(CJK)?.length ?? 0) >= CJK_THRESHOLD;
|
|
5459
|
+
}
|
|
5460
|
+
async function enforceEnglishReply(text, rewrite) {
|
|
5461
|
+
if (!isNonEnglishReply(text)) return text;
|
|
5462
|
+
try {
|
|
5463
|
+
const rewritten = await rewrite(REWRITE_PROMPT);
|
|
5464
|
+
if (rewritten?.trim()) return rewritten;
|
|
5465
|
+
console.error("[reply-language] rewrite returned nothing \u2014 posting the original");
|
|
5466
|
+
} catch (err) {
|
|
5467
|
+
console.error("[reply-language] rewrite failed \u2014 posting the original:", err);
|
|
5468
|
+
}
|
|
5469
|
+
return text;
|
|
5470
|
+
}
|
|
5471
|
+
|
|
5386
5472
|
// src/store/identity-map.ts
|
|
5387
5473
|
function identityHandleFor(slackUserId) {
|
|
5388
5474
|
const raw = process.env.SLACK_IDENTITY_MAP;
|
|
@@ -6549,7 +6635,12 @@ ${trimmedDetail.slice(0, 1500)}
|
|
|
6549
6635
|
if (codexResponse.sessionId) {
|
|
6550
6636
|
store.saveSession(channelId, sessionKey, codexResponse.sessionId, "codex");
|
|
6551
6637
|
}
|
|
6552
|
-
const
|
|
6638
|
+
const codexSessionId = codexResponse.sessionId;
|
|
6639
|
+
const codexText = codexSessionId && isEnglishOnlyChannel(config, channelId) ? await enforceEnglishReply(codexResponse.text, async (p) => {
|
|
6640
|
+
const r = await askCodex(p, cwd, codexOptions, codexSessionId);
|
|
6641
|
+
return r.isError ? null : r.text;
|
|
6642
|
+
}) : codexResponse.text;
|
|
6643
|
+
const { cleanedText: cleanedText2, buttons: buttons2 } = extractButtons(codexText);
|
|
6553
6644
|
const actionsBlock2 = buttons2 ? buildActionsBlock(buttons2) : null;
|
|
6554
6645
|
const rtBlocks2 = markdownToBlocks(cleanedText2);
|
|
6555
6646
|
const messages2 = rtBlocks2.length > 0 ? rtBlocks2.map((b) => ({ blocks: [b], text: blockToFallbackText(b) })) : splitMessage(toSlackMarkdown(cleanedText2)).map((text) => ({ text }));
|
|
@@ -6740,7 +6831,21 @@ retry: ${retryDetail}`);
|
|
|
6740
6831
|
} else {
|
|
6741
6832
|
store.saveSession(channelId, sessionKey, resolved.sessionId);
|
|
6742
6833
|
}
|
|
6743
|
-
const
|
|
6834
|
+
const replyText = !resolved.isError && resolved.sessionId && isEnglishOnlyChannel(config, channelId) ? await enforceEnglishReply(resolved.text, async (p) => {
|
|
6835
|
+
const r = await askClaude(
|
|
6836
|
+
p,
|
|
6837
|
+
cwd,
|
|
6838
|
+
botName,
|
|
6839
|
+
askOptions,
|
|
6840
|
+
claudeConfigDir,
|
|
6841
|
+
resolved.sessionId,
|
|
6842
|
+
claudeCodeOauthToken
|
|
6843
|
+
);
|
|
6844
|
+
if (r.isError) return null;
|
|
6845
|
+
store.saveSession(channelId, sessionKey, r.sessionId);
|
|
6846
|
+
return r.text;
|
|
6847
|
+
}) : resolved.text;
|
|
6848
|
+
const { cleanedText, buttons } = extractButtons(replyText);
|
|
6744
6849
|
const actionsBlock = buttons ? buildActionsBlock(buttons) : null;
|
|
6745
6850
|
const rtBlocks = markdownToBlocks(cleanedText);
|
|
6746
6851
|
const messages = rtBlocks.length > 0 ? rtBlocks.map((b) => ({ blocks: [b], text: blockToFallbackText(b) })) : splitMessage(toSlackMarkdown(cleanedText)).map((text) => ({ text }));
|
|
@@ -6757,7 +6862,7 @@ retry: ${retryDetail}`);
|
|
|
6757
6862
|
}
|
|
6758
6863
|
if (debug4) {
|
|
6759
6864
|
console.log(
|
|
6760
|
-
`[response] length=${
|
|
6865
|
+
`[response] length=${replyText.length} messages=${messages.length} buttons=${buttons?.length ?? 0}`
|
|
6761
6866
|
);
|
|
6762
6867
|
}
|
|
6763
6868
|
await clearReaction();
|
|
@@ -8975,6 +9080,7 @@ export {
|
|
|
8975
9080
|
createChannel,
|
|
8976
9081
|
DefaultChannelError,
|
|
8977
9082
|
updateChannelById,
|
|
9083
|
+
markChannelGreeted,
|
|
8978
9084
|
updateChannelsMcpConnections,
|
|
8979
9085
|
ensureAgentDmChannel,
|
|
8980
9086
|
ensureGeneralChannel,
|
|
@@ -9035,7 +9141,9 @@ export {
|
|
|
9035
9141
|
getWorkspaceBySlug,
|
|
9036
9142
|
getWorkspaceById,
|
|
9037
9143
|
listWorkspaces,
|
|
9144
|
+
TRIAL_DAYS,
|
|
9038
9145
|
listWorkspaceSummaries,
|
|
9146
|
+
setWorkspaceArchived,
|
|
9039
9147
|
createWorkspace,
|
|
9040
9148
|
deleteWorkspace,
|
|
9041
9149
|
setWorkspaceStripeSubscription,
|