flyspace-cli 0.1.0 → 0.1.1
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-DCCECKHH.js → chunk-6D2PIFEA.js} +326 -230
- package/dist/cli.js +744 -374
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -266,6 +266,12 @@ function stopAutomationRegistryPolling() {
|
|
|
266
266
|
import { existsSync as existsSync2, mkdirSync, statSync } from "fs";
|
|
267
267
|
import { homedir } from "os";
|
|
268
268
|
import { isAbsolute, resolve as resolve2 } from "path";
|
|
269
|
+
function isLogicalCwd(p) {
|
|
270
|
+
return /^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/.test(p);
|
|
271
|
+
}
|
|
272
|
+
function resolveLogicalCwd(p) {
|
|
273
|
+
return resolve2(process.env["FLYSPACE_ROOT"] || resolve2(homedir(), "Flyspace"), p);
|
|
274
|
+
}
|
|
269
275
|
function expandPath(p) {
|
|
270
276
|
let expanded = p;
|
|
271
277
|
if (expanded === "~") {
|
|
@@ -296,7 +302,7 @@ function rowToChannel(row) {
|
|
|
296
302
|
id: row.id,
|
|
297
303
|
name: row.name,
|
|
298
304
|
categoryId: row.category_id,
|
|
299
|
-
cwd: expandPath(row.cwd),
|
|
305
|
+
cwd: isLogicalCwd(row.cwd) ? resolveLogicalCwd(row.cwd) : expandPath(row.cwd),
|
|
300
306
|
externalChannelId: row.external_channel_id,
|
|
301
307
|
model: row.model,
|
|
302
308
|
effort: row.effort && isEffort(row.effort) ? row.effort : null,
|
|
@@ -487,7 +493,7 @@ function resolveEffort(explicitEffort, channelId, fallback) {
|
|
|
487
493
|
function toRow(input) {
|
|
488
494
|
const row = {};
|
|
489
495
|
if (input.name !== void 0) row["name"] = input.name;
|
|
490
|
-
if (input.cwd !== void 0) row["cwd"] = expandPath(input.cwd);
|
|
496
|
+
if (input.cwd !== void 0) row["cwd"] = isLogicalCwd(input.cwd) ? input.cwd : expandPath(input.cwd);
|
|
491
497
|
if (input.categoryId !== void 0) row["category_id"] = input.categoryId;
|
|
492
498
|
if (input.externalChannelId !== void 0) row["external_channel_id"] = input.externalChannelId;
|
|
493
499
|
if (input.model !== void 0) row["model"] = input.model;
|
|
@@ -513,7 +519,7 @@ async function createChannel(input) {
|
|
|
513
519
|
const { data, error } = await getSupabaseClient().from("channels").insert({ ...toRow(input), profile: getProfile() }).select().single();
|
|
514
520
|
if (error) throw new Error(error.message);
|
|
515
521
|
const channel = rowToChannel(data);
|
|
516
|
-
if (channel.cwd && !isCwdUsable(channel.cwd)) {
|
|
522
|
+
if (channel.cwd && !isLogicalCwd(input.cwd) && !isCwdUsable(channel.cwd)) {
|
|
517
523
|
try {
|
|
518
524
|
mkdirSync(channel.cwd, { recursive: true });
|
|
519
525
|
} catch (err) {
|
|
@@ -696,8 +702,8 @@ function loadEffectiveAutomations() {
|
|
|
696
702
|
|
|
697
703
|
// src/claude/agent.ts
|
|
698
704
|
import { spawn } from "child_process";
|
|
699
|
-
import { existsSync as
|
|
700
|
-
import { resolve as
|
|
705
|
+
import { existsSync as existsSync8, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
706
|
+
import { resolve as resolve4 } from "path";
|
|
701
707
|
import { randomUUID } from "crypto";
|
|
702
708
|
|
|
703
709
|
// src/claude/container.ts
|
|
@@ -1012,6 +1018,181 @@ function resolveMiseEnv(cwd) {
|
|
|
1012
1018
|
}
|
|
1013
1019
|
}
|
|
1014
1020
|
|
|
1021
|
+
// src/store/output-styles-store.ts
|
|
1022
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync2, renameSync, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1023
|
+
import { homedir as homedir2 } from "os";
|
|
1024
|
+
import { join as join4, resolve as resolve3, sep } from "path";
|
|
1025
|
+
var BUILT_IN_OUTPUT_STYLES = [
|
|
1026
|
+
{ slug: "default", name: "Default", description: "Claude Code\u2019s standard software engineering prompt." },
|
|
1027
|
+
{ slug: "proactive", name: "Proactive", description: "Works autonomously and makes reasonable calls instead of asking." },
|
|
1028
|
+
{ slug: "concise", name: "Concise", description: "The shortest answer that fully addresses the question." },
|
|
1029
|
+
{ slug: "explanatory", name: "Explanatory", description: "Walks through the reasoning and trade-offs, not just the result." },
|
|
1030
|
+
{ slug: "learning", name: "Learning", description: "Collaborative mode that asks you to write small pieces of code." }
|
|
1031
|
+
];
|
|
1032
|
+
var OutputStyleConflictError = class extends Error {
|
|
1033
|
+
};
|
|
1034
|
+
var ID_RE = /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;
|
|
1035
|
+
function resolveDir(claudeConfigDir) {
|
|
1036
|
+
return claudeConfigDir || join4(homedir2(), ".claude");
|
|
1037
|
+
}
|
|
1038
|
+
function stylesDir(claudeConfigDir) {
|
|
1039
|
+
return join4(resolveDir(claudeConfigDir), "output-styles");
|
|
1040
|
+
}
|
|
1041
|
+
function styleFile(claudeConfigDir, id) {
|
|
1042
|
+
if (!ID_RE.test(id)) return null;
|
|
1043
|
+
const dir = resolve3(stylesDir(claudeConfigDir));
|
|
1044
|
+
const file = resolve3(dir, `${id}.md`);
|
|
1045
|
+
return file.startsWith(dir + sep) ? file : null;
|
|
1046
|
+
}
|
|
1047
|
+
function slugify(name) {
|
|
1048
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1049
|
+
}
|
|
1050
|
+
function unquote(raw) {
|
|
1051
|
+
const v = raw.trim();
|
|
1052
|
+
if (v.startsWith('"')) {
|
|
1053
|
+
try {
|
|
1054
|
+
return JSON.parse(v);
|
|
1055
|
+
} catch {
|
|
1056
|
+
return v.slice(1, v.endsWith('"') ? -1 : void 0);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
if (v.startsWith("'") && v.endsWith("'") && v.length > 1) return v.slice(1, -1).replaceAll("''", "'");
|
|
1060
|
+
return v;
|
|
1061
|
+
}
|
|
1062
|
+
function parseStyle(text, fallbackName) {
|
|
1063
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)([\s\S]*)$/.exec(text);
|
|
1064
|
+
if (!m) return { name: fallbackName, description: "", body: text.trim(), extra: [] };
|
|
1065
|
+
let name = "";
|
|
1066
|
+
let description = "";
|
|
1067
|
+
const extra = [];
|
|
1068
|
+
for (const line of m[1].split(/\r?\n/)) {
|
|
1069
|
+
const kv = /^(name|description):(.*)$/.exec(line);
|
|
1070
|
+
if (kv) {
|
|
1071
|
+
if (kv[1] === "name") name = unquote(kv[2]);
|
|
1072
|
+
else description = unquote(kv[2]);
|
|
1073
|
+
} else if (line.trim()) extra.push(line);
|
|
1074
|
+
}
|
|
1075
|
+
return { name: name || fallbackName, description, body: m[2].trim(), extra };
|
|
1076
|
+
}
|
|
1077
|
+
function serialize(p) {
|
|
1078
|
+
const head = [`name: ${JSON.stringify(p.name)}`, `description: ${JSON.stringify(p.description)}`, ...p.extra];
|
|
1079
|
+
return `---
|
|
1080
|
+
${head.join("\n")}
|
|
1081
|
+
---
|
|
1082
|
+
|
|
1083
|
+
${p.body.trim()}
|
|
1084
|
+
`;
|
|
1085
|
+
}
|
|
1086
|
+
function atomicWrite(file, content) {
|
|
1087
|
+
mkdirSync5(resolve3(file, ".."), { recursive: true });
|
|
1088
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
1089
|
+
writeFileSync2(tmp, content);
|
|
1090
|
+
renameSync(tmp, file);
|
|
1091
|
+
}
|
|
1092
|
+
function readStyle(claudeConfigDir, id) {
|
|
1093
|
+
const file = styleFile(claudeConfigDir, id);
|
|
1094
|
+
if (!file || !existsSync7(file)) return null;
|
|
1095
|
+
const stat = statSync2(file);
|
|
1096
|
+
const p = parseStyle(readFileSync2(file, "utf8"), id);
|
|
1097
|
+
return {
|
|
1098
|
+
id,
|
|
1099
|
+
name: p.name,
|
|
1100
|
+
description: p.description,
|
|
1101
|
+
body: p.body,
|
|
1102
|
+
createdAt: stat.birthtime.toISOString(),
|
|
1103
|
+
updatedAt: stat.mtime.toISOString()
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
function listOutputStyles(claudeConfigDir) {
|
|
1107
|
+
const dir = stylesDir(claudeConfigDir);
|
|
1108
|
+
if (!existsSync7(dir)) return [];
|
|
1109
|
+
return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => readStyle(claudeConfigDir, f.slice(0, -3))).filter((s) => !!s).sort((a, b) => a.name.localeCompare(b.name));
|
|
1110
|
+
}
|
|
1111
|
+
function assertNameFree(claudeConfigDir, name, exceptId) {
|
|
1112
|
+
const lower = name.toLowerCase();
|
|
1113
|
+
const taken = BUILT_IN_OUTPUT_STYLES.some((s) => s.name.toLowerCase() === lower) || listOutputStyles(claudeConfigDir).some((s) => s.id !== exceptId && s.name.toLowerCase() === lower);
|
|
1114
|
+
if (taken) throw new OutputStyleConflictError(`an output style named "${name}" already exists`);
|
|
1115
|
+
}
|
|
1116
|
+
function createOutputStyle(claudeConfigDir, params) {
|
|
1117
|
+
const id = slugify(params.name);
|
|
1118
|
+
if (!id) throw new Error("name must contain letters or digits");
|
|
1119
|
+
assertNameFree(claudeConfigDir, params.name);
|
|
1120
|
+
const file = styleFile(claudeConfigDir, id);
|
|
1121
|
+
if (existsSync7(file) || BUILT_IN_OUTPUT_STYLES.some((s) => s.slug === id)) {
|
|
1122
|
+
throw new OutputStyleConflictError(`an output style named "${params.name}" already exists`);
|
|
1123
|
+
}
|
|
1124
|
+
atomicWrite(file, serialize({ ...params, extra: [] }));
|
|
1125
|
+
return readStyle(claudeConfigDir, id);
|
|
1126
|
+
}
|
|
1127
|
+
function updateOutputStyle(claudeConfigDir, id, patch) {
|
|
1128
|
+
const file = styleFile(claudeConfigDir, id);
|
|
1129
|
+
if (!file || !existsSync7(file)) return null;
|
|
1130
|
+
const current = parseStyle(readFileSync2(file, "utf8"), id);
|
|
1131
|
+
if (patch.name !== void 0 && patch.name !== current.name) assertNameFree(claudeConfigDir, patch.name, id);
|
|
1132
|
+
const next = { ...current, ...patch };
|
|
1133
|
+
atomicWrite(file, serialize(next));
|
|
1134
|
+
if (next.name !== current.name && readSettings(claudeConfigDir)["outputStyle"] === current.name) {
|
|
1135
|
+
writeDefaultName(claudeConfigDir, next.name);
|
|
1136
|
+
}
|
|
1137
|
+
return readStyle(claudeConfigDir, id);
|
|
1138
|
+
}
|
|
1139
|
+
function deleteOutputStyle(claudeConfigDir, id) {
|
|
1140
|
+
const file = styleFile(claudeConfigDir, id);
|
|
1141
|
+
if (!file || !existsSync7(file)) return false;
|
|
1142
|
+
const name = parseStyle(readFileSync2(file, "utf8"), id).name;
|
|
1143
|
+
rmSync(file);
|
|
1144
|
+
if (readSettings(claudeConfigDir)["outputStyle"] === name) writeDefaultName(claudeConfigDir, void 0);
|
|
1145
|
+
return true;
|
|
1146
|
+
}
|
|
1147
|
+
function settingsFile(claudeConfigDir) {
|
|
1148
|
+
return join4(resolveDir(claudeConfigDir), "settings.json");
|
|
1149
|
+
}
|
|
1150
|
+
function readSettings(claudeConfigDir) {
|
|
1151
|
+
const file = settingsFile(claudeConfigDir);
|
|
1152
|
+
if (!existsSync7(file)) return {};
|
|
1153
|
+
const parsed = JSON.parse(readFileSync2(file, "utf8"));
|
|
1154
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1155
|
+
throw new Error(`${file} is not a JSON object`);
|
|
1156
|
+
}
|
|
1157
|
+
return parsed;
|
|
1158
|
+
}
|
|
1159
|
+
function writeDefaultName(claudeConfigDir, name) {
|
|
1160
|
+
const settings = readSettings(claudeConfigDir);
|
|
1161
|
+
if (name === void 0) delete settings["outputStyle"];
|
|
1162
|
+
else settings["outputStyle"] = name;
|
|
1163
|
+
atomicWrite(settingsFile(claudeConfigDir), `${JSON.stringify(settings, null, 2)}
|
|
1164
|
+
`);
|
|
1165
|
+
}
|
|
1166
|
+
function resolveSelectedOutputStyle(claudeConfigDir) {
|
|
1167
|
+
let value;
|
|
1168
|
+
try {
|
|
1169
|
+
value = readSettings(claudeConfigDir)["outputStyle"];
|
|
1170
|
+
} catch {
|
|
1171
|
+
return void 0;
|
|
1172
|
+
}
|
|
1173
|
+
if (typeof value !== "string") return void 0;
|
|
1174
|
+
const builtIn = BUILT_IN_OUTPUT_STYLES.find((s) => s.name.toLowerCase() === value.toLowerCase());
|
|
1175
|
+
if (builtIn) {
|
|
1176
|
+
return builtIn.slug === "default" ? void 0 : { id: builtIn.slug, name: builtIn.name, builtIn: true };
|
|
1177
|
+
}
|
|
1178
|
+
const custom = listOutputStyles(claudeConfigDir).find((s) => s.name === value);
|
|
1179
|
+
return custom ? { id: custom.id, name: custom.name, builtIn: false, body: custom.body } : void 0;
|
|
1180
|
+
}
|
|
1181
|
+
function getOutputStyleDefault(claudeConfigDir) {
|
|
1182
|
+
return resolveSelectedOutputStyle(claudeConfigDir)?.id ?? "default";
|
|
1183
|
+
}
|
|
1184
|
+
function setOutputStyleDefault(claudeConfigDir, id) {
|
|
1185
|
+
const builtIn = BUILT_IN_OUTPUT_STYLES.find((s) => s.slug === id);
|
|
1186
|
+
if (builtIn) {
|
|
1187
|
+
writeDefaultName(claudeConfigDir, builtIn.slug === "default" ? void 0 : builtIn.name);
|
|
1188
|
+
return true;
|
|
1189
|
+
}
|
|
1190
|
+
const style = readStyle(claudeConfigDir, id);
|
|
1191
|
+
if (!style) return false;
|
|
1192
|
+
writeDefaultName(claudeConfigDir, style.name);
|
|
1193
|
+
return true;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1015
1196
|
// src/claude/models.ts
|
|
1016
1197
|
var MODEL_OPTIONS = [
|
|
1017
1198
|
{ value: "opus", label: "Opus", provider: "claude" },
|
|
@@ -1408,14 +1589,14 @@ function getMaxDurationMs(streaming) {
|
|
|
1408
1589
|
}
|
|
1409
1590
|
function loadSystemPrompt() {
|
|
1410
1591
|
const customPath = paths.PROMPT_FILE;
|
|
1411
|
-
const defaultPath =
|
|
1412
|
-
const filePath =
|
|
1413
|
-
return
|
|
1592
|
+
const defaultPath = resolve4(paths.PACKAGE_ROOT, "system.default.md");
|
|
1593
|
+
const filePath = existsSync8(customPath) ? customPath : defaultPath;
|
|
1594
|
+
return readFileSync3(filePath, "utf-8").trim();
|
|
1414
1595
|
}
|
|
1415
1596
|
function loadCapabilities() {
|
|
1416
|
-
const capabilitiesPath =
|
|
1417
|
-
if (!
|
|
1418
|
-
return
|
|
1597
|
+
const capabilitiesPath = resolve4(paths.PACKAGE_ROOT, "system.capabilities.md");
|
|
1598
|
+
if (!existsSync8(capabilitiesPath)) return "";
|
|
1599
|
+
return readFileSync3(capabilitiesPath, "utf-8").trim();
|
|
1419
1600
|
}
|
|
1420
1601
|
function buildSystemPrompt(botName, agentPersona, channelPrompt, outputStyle) {
|
|
1421
1602
|
const prompt = loadSystemPrompt().replaceAll("{{botName}}", botName);
|
|
@@ -1451,12 +1632,12 @@ function owningServiceLabel(env = process.env) {
|
|
|
1451
1632
|
if (!label || label === "0") return void 0;
|
|
1452
1633
|
return /^[A-Za-z0-9._-]+$/.test(label) ? label : void 0;
|
|
1453
1634
|
}
|
|
1454
|
-
function buildHookSettings(runtime,
|
|
1455
|
-
const guardScript = runtime === "container" ? `${CONTAINER_PATHS.hooks}/bash-guard.mjs` :
|
|
1635
|
+
function buildHookSettings(runtime, builtInStyleName) {
|
|
1636
|
+
const guardScript = runtime === "container" ? `${CONTAINER_PATHS.hooks}/bash-guard.mjs` : resolve4(paths.PACKAGE_ROOT, "hooks/bash-guard.mjs");
|
|
1456
1637
|
const selfLabel = runtime === "container" ? void 0 : owningServiceLabel();
|
|
1457
1638
|
const guardEnv = selfLabel ? `FLYSPACE_SELF_SERVICE_LABEL='${selfLabel}' ` : "";
|
|
1458
1639
|
return JSON.stringify({
|
|
1459
|
-
...
|
|
1640
|
+
...builtInStyleName ? { outputStyle: builtInStyleName } : {},
|
|
1460
1641
|
hooks: {
|
|
1461
1642
|
PreToolUse: [
|
|
1462
1643
|
{
|
|
@@ -1467,7 +1648,7 @@ function buildHookSettings(runtime, nativeStyleName) {
|
|
|
1467
1648
|
}
|
|
1468
1649
|
});
|
|
1469
1650
|
}
|
|
1470
|
-
function buildArgs(prompt, botName, options, resumeSessionId) {
|
|
1651
|
+
function buildArgs(prompt, botName, options, resumeSessionId, isolatedStyle) {
|
|
1471
1652
|
const args = [
|
|
1472
1653
|
"--print",
|
|
1473
1654
|
// stream-json emits NDJSON events as they happen (needed for live
|
|
@@ -1481,10 +1662,9 @@ function buildArgs(prompt, botName, options, resumeSessionId) {
|
|
|
1481
1662
|
botName,
|
|
1482
1663
|
options.agentPersona,
|
|
1483
1664
|
options.channelPrompt,
|
|
1484
|
-
//
|
|
1485
|
-
//
|
|
1486
|
-
|
|
1487
|
-
options.outputStyle?.nativeStyleName ? void 0 : options.outputStyle?.body
|
|
1665
|
+
// Host runs: the CLI applies settings.json's style itself. An isolated run
|
|
1666
|
+
// cannot see that file, so a custom style's body is injected here instead.
|
|
1667
|
+
isolatedStyle?.builtIn ? void 0 : isolatedStyle?.body
|
|
1488
1668
|
),
|
|
1489
1669
|
// A container or sandbox run inherits no user settings: CLAUDE_CONFIG_DIR
|
|
1490
1670
|
// for both points at an isolated per-channel directory, not the machine's
|
|
@@ -1494,7 +1674,7 @@ function buildArgs(prompt, botName, options, resumeSessionId) {
|
|
|
1494
1674
|
"--setting-sources",
|
|
1495
1675
|
options.runtime === "container" || options.runtime === "sandbox" ? "project,local" : "user,project,local",
|
|
1496
1676
|
"--settings",
|
|
1497
|
-
buildHookSettings(options.runtime ?? "host",
|
|
1677
|
+
buildHookSettings(options.runtime ?? "host", isolatedStyle?.builtIn ? isolatedStyle.name : void 0)
|
|
1498
1678
|
];
|
|
1499
1679
|
if (mcpStrictConfigEnabled()) {
|
|
1500
1680
|
args.push("--strict-mcp-config");
|
|
@@ -1528,8 +1708,15 @@ function recordUsage2(response, options) {
|
|
|
1528
1708
|
});
|
|
1529
1709
|
}
|
|
1530
1710
|
function runCli(prompt, cwd, botName, options, claudeConfigDir, resumeSessionId, claudeCodeOauthToken) {
|
|
1531
|
-
return new Promise((
|
|
1532
|
-
const
|
|
1711
|
+
return new Promise((resolve7, reject) => {
|
|
1712
|
+
const isolated = options.runtime === "container" || options.runtime === "sandbox";
|
|
1713
|
+
const args = buildArgs(
|
|
1714
|
+
prompt,
|
|
1715
|
+
botName,
|
|
1716
|
+
options,
|
|
1717
|
+
resumeSessionId,
|
|
1718
|
+
isolated ? resolveSelectedOutputStyle(claudeConfigDir) : void 0
|
|
1719
|
+
);
|
|
1533
1720
|
const env = resolveMiseEnv(cwd);
|
|
1534
1721
|
env["FLYSPACE_SCRATCH_DIR"] = paths.SCRATCH_DIR;
|
|
1535
1722
|
env["CLAUDE_CODE_ARTIFACT"] = "1";
|
|
@@ -1668,7 +1855,7 @@ function runCli(prompt, cwd, botName, options, claudeConfigDir, resumeSessionId,
|
|
|
1668
1855
|
console.log(`[agent] stderr: ${stderr.trim()}`);
|
|
1669
1856
|
}
|
|
1670
1857
|
if (cancelled) {
|
|
1671
|
-
|
|
1858
|
+
resolve7({
|
|
1672
1859
|
sessionId: liveSessionId ?? resumeSessionId ?? "",
|
|
1673
1860
|
text: "",
|
|
1674
1861
|
isCancelled: true
|
|
@@ -1677,7 +1864,7 @@ function runCli(prompt, cwd, botName, options, claudeConfigDir, resumeSessionId,
|
|
|
1677
1864
|
}
|
|
1678
1865
|
if (timedOut) {
|
|
1679
1866
|
const text = timeoutKind === "wall-clock" && streaming ? `This task ran for over ${Math.round(maxDurationMs / 6e4)} minutes in total, so I was terminated as a runaway backstop. I was still making progress \u2014 the task is just too big for one turn. Consider splitting it into smaller steps, or continue and I'll pick up where I left off.` : `I got stuck for over ${Math.round((timeoutKind === "idle" ? idleTimeoutMs : maxDurationMs) / 6e4)} minutes with no activity and had to be terminated. Most likely cause: a Bash tool call to a long-running process (dev server, watcher, \`tail -f\`, etc.) that never exits. If you wanted to start a server, ask me to run it in the background (\`nohup ... > /tmp/log 2>&1 & disown\`) and I'll come back with the PID.`;
|
|
1680
|
-
|
|
1867
|
+
resolve7({
|
|
1681
1868
|
sessionId: liveSessionId ?? resumeSessionId ?? "",
|
|
1682
1869
|
text,
|
|
1683
1870
|
isError: true,
|
|
@@ -1705,16 +1892,16 @@ function runCli(prompt, cwd, botName, options, claudeConfigDir, resumeSessionId,
|
|
|
1705
1892
|
options.model
|
|
1706
1893
|
);
|
|
1707
1894
|
recordUsage2(response, options);
|
|
1708
|
-
|
|
1895
|
+
resolve7(response);
|
|
1709
1896
|
} catch {
|
|
1710
1897
|
const fallback = onLine ? "" : stdout.trim();
|
|
1711
|
-
|
|
1898
|
+
resolve7({ sessionId: resumeSessionId ?? "", text: fallback || "No response" });
|
|
1712
1899
|
}
|
|
1713
1900
|
});
|
|
1714
1901
|
});
|
|
1715
1902
|
}
|
|
1716
1903
|
async function askClaude(prompt, cwd, botName, options, claudeConfigDir, resumeSessionId, claudeCodeOauthToken) {
|
|
1717
|
-
if (!
|
|
1904
|
+
if (!existsSync8(cwd) || !statSync3(cwd).isDirectory()) {
|
|
1718
1905
|
console.error(`[agent] working directory does not exist: ${cwd}`);
|
|
1719
1906
|
return {
|
|
1720
1907
|
sessionId: resumeSessionId ?? "",
|
|
@@ -2035,97 +2222,9 @@ async function setAgentPaused(workspaceId, paused) {
|
|
|
2035
2222
|
if (error) throw new Error(`setAgentPaused(${workspaceId}): ${error.message}`);
|
|
2036
2223
|
}
|
|
2037
2224
|
|
|
2038
|
-
// src/store/output-styles-store.ts
|
|
2039
|
-
var NATIVE_OUTPUT_STYLE_NAMES = {
|
|
2040
|
-
concise: "Concise",
|
|
2041
|
-
explanatory: "Explanatory"
|
|
2042
|
-
};
|
|
2043
|
-
var BUILT_IN_STYLE_BODIES = {
|
|
2044
|
-
concise: "Lead with the result. Skip preamble and narration, and keep responses short by default. When asked for an explanation or more detail, answer in full.",
|
|
2045
|
-
explanatory: 'Alongside completing the task, share brief "Insights" on the reasoning behind implementation choices and the codebase patterns involved.',
|
|
2046
|
-
formal: "Respond in structured, businesslike prose. No casual asides, contractions, or emoji."
|
|
2047
|
-
};
|
|
2048
|
-
function rowToOutputStyle(row) {
|
|
2049
|
-
return {
|
|
2050
|
-
id: row["id"],
|
|
2051
|
-
workspaceId: row["workspace_id"],
|
|
2052
|
-
name: row["name"],
|
|
2053
|
-
description: row["description"] ?? "",
|
|
2054
|
-
body: row["body"] ?? "",
|
|
2055
|
-
createdAt: row["created_at"],
|
|
2056
|
-
updatedAt: row["updated_at"]
|
|
2057
|
-
};
|
|
2058
|
-
}
|
|
2059
|
-
async function listOutputStyles(workspaceId) {
|
|
2060
|
-
const { data, error } = await getSupabaseClient().from("output_styles").select("*").eq("workspace_id", workspaceId).order("created_at", { ascending: true });
|
|
2061
|
-
if (error) throw new Error(`Failed to list output styles: ${error.message}`);
|
|
2062
|
-
return (data ?? []).map(rowToOutputStyle);
|
|
2063
|
-
}
|
|
2064
|
-
async function getOutputStyle(workspaceId, id) {
|
|
2065
|
-
const { data, error } = await getSupabaseClient().from("output_styles").select("*").eq("workspace_id", workspaceId).eq("id", id).maybeSingle();
|
|
2066
|
-
if (error) throw new Error(`Failed to load output style ${id}: ${error.message}`);
|
|
2067
|
-
return data ? rowToOutputStyle(data) : null;
|
|
2068
|
-
}
|
|
2069
|
-
async function insertOutputStyle(params) {
|
|
2070
|
-
const { data, error } = await getSupabaseClient().from("output_styles").insert({
|
|
2071
|
-
workspace_id: params.workspaceId,
|
|
2072
|
-
name: params.name,
|
|
2073
|
-
description: params.description,
|
|
2074
|
-
body: params.body
|
|
2075
|
-
}).select("*").single();
|
|
2076
|
-
if (error) throw new Error(`Failed to create output style: ${error.message}`);
|
|
2077
|
-
return rowToOutputStyle(data);
|
|
2078
|
-
}
|
|
2079
|
-
async function updateOutputStyle(workspaceId, id, patch) {
|
|
2080
|
-
const update = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2081
|
-
if (patch.name !== void 0) update["name"] = patch.name;
|
|
2082
|
-
if (patch.description !== void 0) update["description"] = patch.description;
|
|
2083
|
-
if (patch.body !== void 0) update["body"] = patch.body;
|
|
2084
|
-
const { data, error } = await getSupabaseClient().from("output_styles").update(update).eq("id", id).eq("workspace_id", workspaceId).select("*").maybeSingle();
|
|
2085
|
-
if (error) throw new Error(`Failed to update output style: ${error.message}`);
|
|
2086
|
-
return data ? rowToOutputStyle(data) : null;
|
|
2087
|
-
}
|
|
2088
|
-
async function deleteOutputStyle(workspaceId, id) {
|
|
2089
|
-
const { data, error } = await getSupabaseClient().from("output_styles").delete().eq("id", id).eq("workspace_id", workspaceId).select("id").maybeSingle();
|
|
2090
|
-
if (error) throw new Error(`Failed to delete output style: ${error.message}`);
|
|
2091
|
-
return !!data;
|
|
2092
|
-
}
|
|
2093
|
-
async function getWorkspaceOutputStyleDefault(workspaceId) {
|
|
2094
|
-
const { data, error } = await getSupabaseClient().from("workspaces").select("output_style_default").eq("id", workspaceId).maybeSingle();
|
|
2095
|
-
if (error) {
|
|
2096
|
-
throw new Error(`Failed to load the workspace's default output style: ${error.message}`);
|
|
2097
|
-
}
|
|
2098
|
-
return data?.["output_style_default"] ?? "default";
|
|
2099
|
-
}
|
|
2100
|
-
async function setWorkspaceOutputStyleDefault(workspaceId, id) {
|
|
2101
|
-
const { error } = await getSupabaseClient().from("workspaces").update({ output_style_default: id }).eq("id", workspaceId);
|
|
2102
|
-
if (error) {
|
|
2103
|
-
throw new Error(`Failed to set the workspace's default output style: ${error.message}`);
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
async function getEffectiveOutputStyle(workspaceId) {
|
|
2107
|
-
const defaultId = await getWorkspaceOutputStyleDefault(workspaceId);
|
|
2108
|
-
if (defaultId === "default") return void 0;
|
|
2109
|
-
const nativeStyleName = NATIVE_OUTPUT_STYLE_NAMES[defaultId];
|
|
2110
|
-
if (nativeStyleName) return { nativeStyleName, body: BUILT_IN_STYLE_BODIES[defaultId] };
|
|
2111
|
-
if (defaultId in BUILT_IN_STYLE_BODIES) return { body: BUILT_IN_STYLE_BODIES[defaultId] };
|
|
2112
|
-
const style = await getOutputStyle(workspaceId, defaultId);
|
|
2113
|
-
return style?.body ? { body: style.body } : void 0;
|
|
2114
|
-
}
|
|
2115
|
-
async function resolveOutputStyleForProfile(profile) {
|
|
2116
|
-
try {
|
|
2117
|
-
const workspace = await getWorkspaceBySlug(profile);
|
|
2118
|
-
if (!workspace) return void 0;
|
|
2119
|
-
return await getEffectiveOutputStyle(workspace.id);
|
|
2120
|
-
} catch (err) {
|
|
2121
|
-
console.error("[output-styles] resolveOutputStyleForProfile failed:", err.message);
|
|
2122
|
-
return void 0;
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2126
2225
|
// src/dashboard/settings.ts
|
|
2127
|
-
import { readFileSync as
|
|
2128
|
-
import { dirname, resolve as
|
|
2226
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync9, mkdirSync as mkdirSync6, rmSync as rmSync2 } from "fs";
|
|
2227
|
+
import { dirname, resolve as resolve5 } from "path";
|
|
2129
2228
|
|
|
2130
2229
|
// src/store/workspace-settings-store.ts
|
|
2131
2230
|
var BRANDING_DEFAULTS = {
|
|
@@ -2277,9 +2376,9 @@ function parseAgentProfiles(raw) {
|
|
|
2277
2376
|
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
2278
2377
|
}
|
|
2279
2378
|
function loadDashboardSettings() {
|
|
2280
|
-
if (!
|
|
2379
|
+
if (!existsSync9(paths.DASHBOARD_SETTINGS_FILE)) return localDefaults();
|
|
2281
2380
|
try {
|
|
2282
|
-
const raw = JSON.parse(
|
|
2381
|
+
const raw = JSON.parse(readFileSync4(paths.DASHBOARD_SETTINGS_FILE, "utf-8"));
|
|
2283
2382
|
const providers = parseProviders(raw.providers, {
|
|
2284
2383
|
agent: normalizeDefaultModel(raw.agent, LOCAL_DEFAULTS.agent),
|
|
2285
2384
|
fallbackModel: typeof raw.fallbackModel === "string" && isAllowedModel(raw.fallbackModel) ? raw.fallbackModel : void 0
|
|
@@ -2306,18 +2405,18 @@ function withFallbackModel(providers, model) {
|
|
|
2306
2405
|
return [cleared[0], { runner, model }, ...cleared.slice(1).filter((p) => p.runner !== runner)];
|
|
2307
2406
|
}
|
|
2308
2407
|
function readSettingsFile() {
|
|
2309
|
-
if (!
|
|
2408
|
+
if (!existsSync9(paths.DASHBOARD_SETTINGS_FILE)) return null;
|
|
2310
2409
|
try {
|
|
2311
|
-
const raw = JSON.parse(
|
|
2410
|
+
const raw = JSON.parse(readFileSync4(paths.DASHBOARD_SETTINGS_FILE, "utf-8"));
|
|
2312
2411
|
return raw && typeof raw === "object" ? raw : null;
|
|
2313
2412
|
} catch {
|
|
2314
2413
|
return null;
|
|
2315
2414
|
}
|
|
2316
2415
|
}
|
|
2317
2416
|
function writeSettings(next) {
|
|
2318
|
-
|
|
2417
|
+
mkdirSync6(dirname(paths.DASHBOARD_SETTINGS_FILE), { recursive: true });
|
|
2319
2418
|
const merged = { ...readSettingsFile() ?? {}, ...next };
|
|
2320
|
-
|
|
2419
|
+
writeFileSync3(paths.DASHBOARD_SETTINGS_FILE, JSON.stringify(merged, null, 2), "utf-8");
|
|
2321
2420
|
return next;
|
|
2322
2421
|
}
|
|
2323
2422
|
function saveDashboardSettings(patch) {
|
|
@@ -2381,17 +2480,17 @@ async function loadFullDashboardSettings(profile = getProfile()) {
|
|
|
2381
2480
|
return { ...await loadWorkspaceBranding(profile), ...loadDashboardSettings() };
|
|
2382
2481
|
}
|
|
2383
2482
|
function defaultPromptPath() {
|
|
2384
|
-
return
|
|
2483
|
+
return resolve5(paths.PACKAGE_ROOT, "system.default.md");
|
|
2385
2484
|
}
|
|
2386
2485
|
function readDefaultPrompt() {
|
|
2387
2486
|
const path = defaultPromptPath();
|
|
2388
|
-
return
|
|
2487
|
+
return existsSync9(path) ? readFileSync4(path, "utf-8").trim() : "";
|
|
2389
2488
|
}
|
|
2390
2489
|
function loadWorkspacePrompt() {
|
|
2391
2490
|
const defaultPrompt = readDefaultPrompt();
|
|
2392
|
-
const custom =
|
|
2491
|
+
const custom = existsSync9(paths.PROMPT_FILE);
|
|
2393
2492
|
return {
|
|
2394
|
-
prompt: custom ?
|
|
2493
|
+
prompt: custom ? readFileSync4(paths.PROMPT_FILE, "utf-8").trim() : defaultPrompt,
|
|
2395
2494
|
defaultPrompt,
|
|
2396
2495
|
custom
|
|
2397
2496
|
};
|
|
@@ -2399,13 +2498,13 @@ function loadWorkspacePrompt() {
|
|
|
2399
2498
|
function saveWorkspacePrompt(prompt) {
|
|
2400
2499
|
const trimmed = prompt.trim();
|
|
2401
2500
|
if (!trimmed || trimmed === readDefaultPrompt()) return resetWorkspacePrompt();
|
|
2402
|
-
|
|
2403
|
-
|
|
2501
|
+
mkdirSync6(dirname(paths.PROMPT_FILE), { recursive: true });
|
|
2502
|
+
writeFileSync3(paths.PROMPT_FILE, `${trimmed}
|
|
2404
2503
|
`, "utf-8");
|
|
2405
2504
|
return loadWorkspacePrompt();
|
|
2406
2505
|
}
|
|
2407
2506
|
function resetWorkspacePrompt() {
|
|
2408
|
-
|
|
2507
|
+
rmSync2(paths.PROMPT_FILE, { force: true });
|
|
2409
2508
|
return loadWorkspacePrompt();
|
|
2410
2509
|
}
|
|
2411
2510
|
function saveAgentProfile(name, patch) {
|
|
@@ -2445,9 +2544,9 @@ async function saveBotIdentity(patch, profile = getProfile()) {
|
|
|
2445
2544
|
|
|
2446
2545
|
// src/codex/agent.ts
|
|
2447
2546
|
import { spawn as spawn2 } from "child_process";
|
|
2448
|
-
import { existsSync as
|
|
2547
|
+
import { existsSync as existsSync10, mkdtempSync, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync4 } from "fs";
|
|
2449
2548
|
import { tmpdir } from "os";
|
|
2450
|
-
import { join as
|
|
2549
|
+
import { join as join5 } from "path";
|
|
2451
2550
|
var AUTH_ERROR_PATTERNS = [
|
|
2452
2551
|
/401 unauthorized/i,
|
|
2453
2552
|
/missing bearer or basic authentication/i,
|
|
@@ -2578,8 +2677,8 @@ function getMaxDurationMs2() {
|
|
|
2578
2677
|
}
|
|
2579
2678
|
function codexAuthAvailable(env = process.env) {
|
|
2580
2679
|
if (env["OPENAI_API_KEY"]?.trim()) return true;
|
|
2581
|
-
const home = env["CODEX_HOME"]?.trim() ||
|
|
2582
|
-
return
|
|
2680
|
+
const home = env["CODEX_HOME"]?.trim() || join5(env["HOME"] ?? "", ".codex");
|
|
2681
|
+
return existsSync10(join5(home, "auth.json"));
|
|
2583
2682
|
}
|
|
2584
2683
|
function applySystemPrompt(prompt, options, resumeSessionId) {
|
|
2585
2684
|
if (resumeSessionId || !options.systemPrompt?.trim()) return prompt;
|
|
@@ -2628,13 +2727,13 @@ function recordCodexUsage(response, options) {
|
|
|
2628
2727
|
});
|
|
2629
2728
|
}
|
|
2630
2729
|
function runCodexCli(prompt, cwd, options, resumeSessionId) {
|
|
2631
|
-
return new Promise((
|
|
2730
|
+
return new Promise((resolve7, reject) => {
|
|
2632
2731
|
const finish = (response) => {
|
|
2633
2732
|
recordCodexUsage(response, options);
|
|
2634
|
-
|
|
2733
|
+
resolve7(response);
|
|
2635
2734
|
};
|
|
2636
|
-
const scratch = mkdtempSync(
|
|
2637
|
-
const lastMessageFile =
|
|
2735
|
+
const scratch = mkdtempSync(join5(tmpdir(), "flyspace-codex-"));
|
|
2736
|
+
const lastMessageFile = join5(scratch, "last-message.txt");
|
|
2638
2737
|
const args = buildArgs2(prompt, options, lastMessageFile, resumeSessionId);
|
|
2639
2738
|
console.log(`[codex] spawning codex CLI in ${cwd}`);
|
|
2640
2739
|
console.log(`[codex] args: ${args.slice(0, -1).join(" ")}`);
|
|
@@ -2704,7 +2803,7 @@ function runCodexCli(prompt, cwd, options, resumeSessionId) {
|
|
|
2704
2803
|
const cleanup = () => {
|
|
2705
2804
|
clearTimeout(timeoutHandle);
|
|
2706
2805
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
2707
|
-
|
|
2806
|
+
rmSync3(scratch, { recursive: true, force: true });
|
|
2708
2807
|
};
|
|
2709
2808
|
child.stdout.on("data", (data) => {
|
|
2710
2809
|
const chunk = data.toString();
|
|
@@ -2725,7 +2824,7 @@ function runCodexCli(prompt, cwd, options, resumeSessionId) {
|
|
|
2725
2824
|
child.on("close", (code) => {
|
|
2726
2825
|
let lastMessage = "";
|
|
2727
2826
|
try {
|
|
2728
|
-
lastMessage =
|
|
2827
|
+
lastMessage = readFileSync5(lastMessageFile, "utf8").trim();
|
|
2729
2828
|
} catch {
|
|
2730
2829
|
lastMessage = "";
|
|
2731
2830
|
}
|
|
@@ -2761,7 +2860,7 @@ function runCodexCli(prompt, cwd, options, resumeSessionId) {
|
|
|
2761
2860
|
});
|
|
2762
2861
|
}
|
|
2763
2862
|
async function askCodex(prompt, cwd, options = {}, resumeSessionId) {
|
|
2764
|
-
if (!
|
|
2863
|
+
if (!existsSync10(cwd) || !statSync4(cwd).isDirectory()) {
|
|
2765
2864
|
console.error(`[codex] working directory does not exist: ${cwd}`);
|
|
2766
2865
|
return {
|
|
2767
2866
|
sessionId: resumeSessionId ?? "",
|
|
@@ -3209,12 +3308,12 @@ function renderAsciiTable(t) {
|
|
|
3209
3308
|
(h, i) => Math.max(h.length, ...rows2.map((r) => (r[i] || "").length))
|
|
3210
3309
|
);
|
|
3211
3310
|
const fmt = (cells) => "| " + cells.map((c, i) => c.padEnd(widths[i])).join(" | ") + " |";
|
|
3212
|
-
const
|
|
3213
|
-
return [fmt(headers),
|
|
3311
|
+
const sep3 = "|" + widths.map((w) => "-".repeat(w + 2)).join("|") + "|";
|
|
3312
|
+
return [fmt(headers), sep3, ...rows2.map(fmt)].join("\n");
|
|
3214
3313
|
}
|
|
3215
3314
|
|
|
3216
3315
|
// src/store/channel-cache.ts
|
|
3217
|
-
import { mkdirSync as
|
|
3316
|
+
import { mkdirSync as mkdirSync7 } from "fs";
|
|
3218
3317
|
|
|
3219
3318
|
// src/store/sqlite.ts
|
|
3220
3319
|
import Database from "better-sqlite3";
|
|
@@ -3238,7 +3337,7 @@ function getDb() {
|
|
|
3238
3337
|
const file = paths.DB_FILE;
|
|
3239
3338
|
const existing = dbs.get(file);
|
|
3240
3339
|
if (existing) return existing;
|
|
3241
|
-
|
|
3340
|
+
mkdirSync7(paths.DATA_DIR, { recursive: true });
|
|
3242
3341
|
const db = openLocalDatabase(file);
|
|
3243
3342
|
if (!db) return null;
|
|
3244
3343
|
db.exec(`
|
|
@@ -3374,7 +3473,6 @@ async function runAutomation(opts) {
|
|
|
3374
3473
|
|
|
3375
3474
|
`;
|
|
3376
3475
|
try {
|
|
3377
|
-
const outputStyle = await resolveOutputStyleForProfile(getProfile());
|
|
3378
3476
|
const useCodex = (explicitModel ? runnerForModel(explicitModel) === "codex" : runnerForModel(loadDashboardSettings().agent) === "codex") && !requiresClaudeAutomationRunner(prompt);
|
|
3379
3477
|
const response = useCodex ? await askCodex(automationPrefix + prompt, cwd, {
|
|
3380
3478
|
...explicitModel && runnerForModel(explicitModel) === "codex" ? { model: explicitModel } : codexModel?.trim() ? { model: codexModel.trim() } : {},
|
|
@@ -3384,7 +3482,7 @@ async function runAutomation(opts) {
|
|
|
3384
3482
|
botName,
|
|
3385
3483
|
void 0,
|
|
3386
3484
|
resolveChannelPrompt(channel),
|
|
3387
|
-
|
|
3485
|
+
resolveSelectedOutputStyle(claudeConfigDir)?.body
|
|
3388
3486
|
),
|
|
3389
3487
|
// Same attribution as the Claude branch below — no userId, the
|
|
3390
3488
|
// schedule's name is the label.
|
|
@@ -3399,7 +3497,6 @@ async function runAutomation(opts) {
|
|
|
3399
3497
|
model,
|
|
3400
3498
|
maxBudgetUsd,
|
|
3401
3499
|
channelPrompt: resolveChannelPrompt(channel),
|
|
3402
|
-
outputStyle,
|
|
3403
3500
|
// No userId: nobody triggered this. The schedule's name is the
|
|
3404
3501
|
// label, which is what makes "which automation is burning tokens"
|
|
3405
3502
|
// answerable without joining anything.
|
|
@@ -3446,7 +3543,7 @@ var DEFAULT_TIMEOUT_MS = 12e4;
|
|
|
3446
3543
|
var KILL_GRACE_MS3 = 5e3;
|
|
3447
3544
|
function runScriptAutomation(opts) {
|
|
3448
3545
|
const { name, script, cwd, timeoutMs = DEFAULT_TIMEOUT_MS } = opts;
|
|
3449
|
-
return new Promise((
|
|
3546
|
+
return new Promise((resolve7, reject) => {
|
|
3450
3547
|
const child = spawn3("/bin/bash", [script], { cwd, env: resolveMiseEnv(cwd), stdio: ["ignore", "pipe", "pipe"] });
|
|
3451
3548
|
let stdout = "";
|
|
3452
3549
|
let stderr = "";
|
|
@@ -3479,7 +3576,7 @@ function runScriptAutomation(opts) {
|
|
|
3479
3576
|
} else if (code !== 0) {
|
|
3480
3577
|
reject(new Error(`script exited with code ${code}: ${stderr.trim()}`));
|
|
3481
3578
|
} else {
|
|
3482
|
-
|
|
3579
|
+
resolve7();
|
|
3483
3580
|
}
|
|
3484
3581
|
});
|
|
3485
3582
|
});
|
|
@@ -3835,17 +3932,17 @@ var TriggerEngine = class {
|
|
|
3835
3932
|
};
|
|
3836
3933
|
|
|
3837
3934
|
// src/store/user-cache.ts
|
|
3838
|
-
import { existsSync as
|
|
3839
|
-
import { join as
|
|
3935
|
+
import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
|
|
3936
|
+
import { join as join6 } from "path";
|
|
3840
3937
|
function jsonUserCachePath() {
|
|
3841
|
-
const file =
|
|
3842
|
-
return
|
|
3938
|
+
const file = join6(paths.CONFIG_DIR, "slack_users.json");
|
|
3939
|
+
return existsSync11(file) ? file : void 0;
|
|
3843
3940
|
}
|
|
3844
3941
|
function readJsonUserCache() {
|
|
3845
3942
|
const file = jsonUserCachePath();
|
|
3846
3943
|
if (!file) return void 0;
|
|
3847
3944
|
try {
|
|
3848
|
-
const parsed = JSON.parse(
|
|
3945
|
+
const parsed = JSON.parse(readFileSync6(file, "utf8"));
|
|
3849
3946
|
return parsed.users;
|
|
3850
3947
|
} catch {
|
|
3851
3948
|
return void 0;
|
|
@@ -4237,8 +4334,8 @@ var MessageQueue = class {
|
|
|
4237
4334
|
const chains = Array.from(this.chains.values());
|
|
4238
4335
|
if (chains.length === 0) return true;
|
|
4239
4336
|
let timer;
|
|
4240
|
-
const timeout = new Promise((
|
|
4241
|
-
timer = setTimeout(() =>
|
|
4337
|
+
const timeout = new Promise((resolve7) => {
|
|
4338
|
+
timer = setTimeout(() => resolve7(false), timeoutMs);
|
|
4242
4339
|
});
|
|
4243
4340
|
const done = Promise.all(chains).then(() => true);
|
|
4244
4341
|
const result = await Promise.race([done, timeout]);
|
|
@@ -4252,7 +4349,7 @@ var MessageQueue = class {
|
|
|
4252
4349
|
|
|
4253
4350
|
// src/slack/file-downloader.ts
|
|
4254
4351
|
import { writeFile } from "fs/promises";
|
|
4255
|
-
import { join as
|
|
4352
|
+
import { join as join7, extname } from "path";
|
|
4256
4353
|
var debug3 = process.env["DEBUG"] === "true";
|
|
4257
4354
|
var SUPPORTED_IMAGE_MIMETYPES = /* @__PURE__ */ new Set([
|
|
4258
4355
|
"image/png",
|
|
@@ -4352,7 +4449,7 @@ async function downloadOne(file, botToken) {
|
|
|
4352
4449
|
const ext = extname(originalName) || `.${file.filetype ?? "bin"}`;
|
|
4353
4450
|
const safeName = sanitizeFilename(originalName.replace(ext, ""));
|
|
4354
4451
|
const filename = `${Date.now()}-${file.id ?? "unnamed"}-${safeName}${ext}`;
|
|
4355
|
-
const localPath =
|
|
4452
|
+
const localPath = join7(paths.UPLOADS_DIR, filename);
|
|
4356
4453
|
await writeFile(localPath, buffer);
|
|
4357
4454
|
if (debug3)
|
|
4358
4455
|
console.log(`[file-downloader] saved ${file.id} -> ${localPath} (${buffer.length} bytes)`);
|
|
@@ -4767,44 +4864,44 @@ async function summariseThreadTranscript(transcript, opts, deps = {}) {
|
|
|
4767
4864
|
}
|
|
4768
4865
|
|
|
4769
4866
|
// src/claude/skills.ts
|
|
4770
|
-
import { existsSync as
|
|
4771
|
-
import { homedir as
|
|
4772
|
-
import { basename as basename2, dirname as dirname2, join as
|
|
4867
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
4868
|
+
import { homedir as homedir4 } from "os";
|
|
4869
|
+
import { basename as basename2, dirname as dirname2, join as join9 } from "path";
|
|
4773
4870
|
import yaml3 from "js-yaml";
|
|
4774
4871
|
|
|
4775
4872
|
// src/claude/skill-files.ts
|
|
4776
4873
|
import {
|
|
4777
|
-
existsSync as
|
|
4778
|
-
mkdirSync as
|
|
4779
|
-
readFileSync as
|
|
4780
|
-
readdirSync,
|
|
4781
|
-
rmSync as
|
|
4782
|
-
statSync as
|
|
4783
|
-
writeFileSync as
|
|
4874
|
+
existsSync as existsSync12,
|
|
4875
|
+
mkdirSync as mkdirSync8,
|
|
4876
|
+
readFileSync as readFileSync7,
|
|
4877
|
+
readdirSync as readdirSync2,
|
|
4878
|
+
rmSync as rmSync4,
|
|
4879
|
+
statSync as statSync5,
|
|
4880
|
+
writeFileSync as writeFileSync4
|
|
4784
4881
|
} from "fs";
|
|
4785
|
-
import { homedir as
|
|
4786
|
-
import { join as
|
|
4882
|
+
import { homedir as homedir3 } from "os";
|
|
4883
|
+
import { join as join8, resolve as resolve6, sep as sep2 } from "path";
|
|
4787
4884
|
import yaml2 from "js-yaml";
|
|
4788
4885
|
var MAX_SKILL_BYTES = 2 * 1024 * 1024;
|
|
4789
4886
|
var SKILL_NAME = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
4790
4887
|
function expandHome(p) {
|
|
4791
|
-
if (p === "~") return
|
|
4792
|
-
if (p.startsWith("~/")) return
|
|
4888
|
+
if (p === "~") return homedir3();
|
|
4889
|
+
if (p.startsWith("~/")) return join8(homedir3(), p.slice(2));
|
|
4793
4890
|
return p;
|
|
4794
4891
|
}
|
|
4795
4892
|
function workspaceSkillsDir(configDir) {
|
|
4796
|
-
return
|
|
4893
|
+
return join8(configDir ? expandHome(configDir) : join8(homedir3(), ".claude"), "skills");
|
|
4797
4894
|
}
|
|
4798
4895
|
function channelSkillsDir(cwd) {
|
|
4799
|
-
return
|
|
4896
|
+
return join8(cwd, ".claude", "skills");
|
|
4800
4897
|
}
|
|
4801
4898
|
function isValidSkillName(name) {
|
|
4802
4899
|
return SKILL_NAME.test(name);
|
|
4803
4900
|
}
|
|
4804
4901
|
function resolveSkillPath(dir, name) {
|
|
4805
4902
|
if (!isValidSkillName(name)) return null;
|
|
4806
|
-
const abs =
|
|
4807
|
-
if (!abs.startsWith(
|
|
4903
|
+
const abs = resolve6(dir, name, "SKILL.md");
|
|
4904
|
+
if (!abs.startsWith(resolve6(dir) + sep2)) return null;
|
|
4808
4905
|
return abs;
|
|
4809
4906
|
}
|
|
4810
4907
|
function splitFrontmatter(raw) {
|
|
@@ -4831,7 +4928,7 @@ ${body.replace(/^\n+/, "").trimEnd()}
|
|
|
4831
4928
|
}
|
|
4832
4929
|
function readMeta(abs) {
|
|
4833
4930
|
try {
|
|
4834
|
-
const { fm } = splitFrontmatter(
|
|
4931
|
+
const { fm } = splitFrontmatter(readFileSync7(abs, "utf-8"));
|
|
4835
4932
|
return {
|
|
4836
4933
|
name: typeof fm["name"] === "string" ? fm["name"] : void 0,
|
|
4837
4934
|
description: typeof fm["description"] === "string" ? fm["description"] : void 0
|
|
@@ -4841,20 +4938,20 @@ function readMeta(abs) {
|
|
|
4841
4938
|
}
|
|
4842
4939
|
}
|
|
4843
4940
|
function listSkillFiles(dir) {
|
|
4844
|
-
if (!
|
|
4941
|
+
if (!existsSync12(dir)) return [];
|
|
4845
4942
|
let entries;
|
|
4846
4943
|
try {
|
|
4847
|
-
entries =
|
|
4944
|
+
entries = readdirSync2(dir);
|
|
4848
4945
|
} catch {
|
|
4849
4946
|
return [];
|
|
4850
4947
|
}
|
|
4851
4948
|
const rows2 = [];
|
|
4852
4949
|
for (const entry of entries) {
|
|
4853
4950
|
if (!isValidSkillName(entry)) continue;
|
|
4854
|
-
const abs =
|
|
4951
|
+
const abs = join8(dir, entry, "SKILL.md");
|
|
4855
4952
|
let stat;
|
|
4856
4953
|
try {
|
|
4857
|
-
stat =
|
|
4954
|
+
stat = statSync5(abs);
|
|
4858
4955
|
} catch {
|
|
4859
4956
|
continue;
|
|
4860
4957
|
}
|
|
@@ -4875,12 +4972,12 @@ function readSkillFile(dir, name) {
|
|
|
4875
4972
|
if (!abs) return null;
|
|
4876
4973
|
let stat;
|
|
4877
4974
|
try {
|
|
4878
|
-
stat =
|
|
4975
|
+
stat = statSync5(abs);
|
|
4879
4976
|
} catch {
|
|
4880
4977
|
return null;
|
|
4881
4978
|
}
|
|
4882
4979
|
if (!stat.isFile() || stat.size > MAX_SKILL_BYTES) return null;
|
|
4883
|
-
const { fm, body } = splitFrontmatter(
|
|
4980
|
+
const { fm, body } = splitFrontmatter(readFileSync7(abs, "utf-8"));
|
|
4884
4981
|
return {
|
|
4885
4982
|
name,
|
|
4886
4983
|
description: typeof fm["description"] === "string" ? fm["description"].trim() : "",
|
|
@@ -4894,7 +4991,7 @@ function writeSkillFile(dir, name, patch, baseMtime) {
|
|
|
4894
4991
|
if (!abs) return { status: "not-found" };
|
|
4895
4992
|
let stat;
|
|
4896
4993
|
try {
|
|
4897
|
-
stat =
|
|
4994
|
+
stat = statSync5(abs);
|
|
4898
4995
|
} catch {
|
|
4899
4996
|
return { status: "not-found" };
|
|
4900
4997
|
}
|
|
@@ -4902,18 +4999,18 @@ function writeSkillFile(dir, name, patch, baseMtime) {
|
|
|
4902
4999
|
if (baseMtime && stat.mtime.toISOString() !== baseMtime) {
|
|
4903
5000
|
return { status: "conflict", mtime: stat.mtime.toISOString() };
|
|
4904
5001
|
}
|
|
4905
|
-
const { fm } = splitFrontmatter(
|
|
5002
|
+
const { fm } = splitFrontmatter(readFileSync7(abs, "utf-8"));
|
|
4906
5003
|
const content = buildSkillFile({ ...fm, name, description: patch.description }, patch.body);
|
|
4907
5004
|
if (Buffer.byteLength(content, "utf8") > MAX_SKILL_BYTES) return { status: "too-large" };
|
|
4908
|
-
|
|
4909
|
-
return { status: "ok", mtime:
|
|
5005
|
+
writeFileSync4(abs, content, "utf8");
|
|
5006
|
+
return { status: "ok", mtime: statSync5(abs).mtime.toISOString() };
|
|
4910
5007
|
}
|
|
4911
5008
|
function createSkillFile(dir, name, description) {
|
|
4912
5009
|
const abs = resolveSkillPath(dir, name);
|
|
4913
5010
|
if (!abs) return { status: "bad-name" };
|
|
4914
|
-
if (
|
|
4915
|
-
|
|
4916
|
-
|
|
5011
|
+
if (existsSync12(abs)) return { status: "exists" };
|
|
5012
|
+
mkdirSync8(join8(dir, name), { recursive: true });
|
|
5013
|
+
writeFileSync4(
|
|
4917
5014
|
abs,
|
|
4918
5015
|
buildSkillFile(
|
|
4919
5016
|
{ name, description },
|
|
@@ -4929,24 +5026,24 @@ Describe what this skill does and when to use it.
|
|
|
4929
5026
|
}
|
|
4930
5027
|
function deleteSkillFile(dir, name) {
|
|
4931
5028
|
const abs = resolveSkillPath(dir, name);
|
|
4932
|
-
if (!abs || !
|
|
4933
|
-
|
|
5029
|
+
if (!abs || !existsSync12(abs)) return { status: "not-found" };
|
|
5030
|
+
rmSync4(join8(dir, name), { recursive: true, force: true });
|
|
4934
5031
|
return { status: "ok" };
|
|
4935
5032
|
}
|
|
4936
5033
|
|
|
4937
5034
|
// src/claude/skills.ts
|
|
4938
5035
|
function defaultConfigDir() {
|
|
4939
|
-
return
|
|
5036
|
+
return join9(homedir4(), ".claude");
|
|
4940
5037
|
}
|
|
4941
5038
|
function expandHome2(p) {
|
|
4942
|
-
if (p === "~") return
|
|
4943
|
-
if (p.startsWith("~/")) return
|
|
5039
|
+
if (p === "~") return homedir4();
|
|
5040
|
+
if (p.startsWith("~/")) return join9(homedir4(), p.slice(2));
|
|
4944
5041
|
return p;
|
|
4945
5042
|
}
|
|
4946
5043
|
function parseFrontmatter(file) {
|
|
4947
5044
|
let content;
|
|
4948
5045
|
try {
|
|
4949
|
-
content =
|
|
5046
|
+
content = readFileSync8(file, "utf-8");
|
|
4950
5047
|
} catch {
|
|
4951
5048
|
return {};
|
|
4952
5049
|
}
|
|
@@ -4964,25 +5061,25 @@ function parseFrontmatter(file) {
|
|
|
4964
5061
|
function isSymlinkedDir(root, entry) {
|
|
4965
5062
|
if (!entry.isSymbolicLink()) return false;
|
|
4966
5063
|
try {
|
|
4967
|
-
return
|
|
5064
|
+
return statSync6(join9(root, entry.name)).isDirectory();
|
|
4968
5065
|
} catch {
|
|
4969
5066
|
return false;
|
|
4970
5067
|
}
|
|
4971
5068
|
}
|
|
4972
5069
|
function findSkillFiles(root, maxDepth, out) {
|
|
4973
|
-
if (maxDepth < 0 || !
|
|
5070
|
+
if (maxDepth < 0 || !existsSync13(root)) return;
|
|
4974
5071
|
let entries;
|
|
4975
5072
|
try {
|
|
4976
|
-
entries =
|
|
5073
|
+
entries = readdirSync3(root, { withFileTypes: true });
|
|
4977
5074
|
} catch {
|
|
4978
5075
|
return;
|
|
4979
5076
|
}
|
|
4980
5077
|
for (const entry of entries) {
|
|
4981
5078
|
if (entry.isFile()) {
|
|
4982
|
-
if (entry.name === "SKILL.md") out.push(
|
|
5079
|
+
if (entry.name === "SKILL.md") out.push(join9(root, entry.name));
|
|
4983
5080
|
} else if (entry.isDirectory() || isSymlinkedDir(root, entry)) {
|
|
4984
5081
|
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
4985
|
-
findSkillFiles(
|
|
5082
|
+
findSkillFiles(join9(root, entry.name), maxDepth - 1, out);
|
|
4986
5083
|
}
|
|
4987
5084
|
}
|
|
4988
5085
|
}
|
|
@@ -4991,7 +5088,7 @@ function pluginNameFor(file, root) {
|
|
|
4991
5088
|
while (dir.startsWith(root) && dir !== dirname2(dir)) {
|
|
4992
5089
|
try {
|
|
4993
5090
|
const manifest = JSON.parse(
|
|
4994
|
-
|
|
5091
|
+
readFileSync8(join9(dir, ".claude-plugin", "plugin.json"), "utf-8")
|
|
4995
5092
|
);
|
|
4996
5093
|
if (typeof manifest.name === "string" && manifest.name.trim()) return manifest.name.trim();
|
|
4997
5094
|
} catch {
|
|
@@ -5020,10 +5117,10 @@ function collectFrom(root, source, maxDepth, namePrefix) {
|
|
|
5020
5117
|
function collectAll(configDir, cwd) {
|
|
5021
5118
|
const base = configDir ? expandHome2(configDir) : defaultConfigDir();
|
|
5022
5119
|
const collected = [
|
|
5023
|
-
...collectFrom(
|
|
5120
|
+
...collectFrom(join9(cwd, ".claude", "skills"), "project", 2),
|
|
5024
5121
|
...nestedProjectRoots(cwd).flatMap(({ dir, prefix }) => collectFrom(dir, "project", 2, prefix)),
|
|
5025
|
-
...collectFrom(
|
|
5026
|
-
...collectFrom(
|
|
5122
|
+
...collectFrom(join9(base, "skills"), "user", 2),
|
|
5123
|
+
...collectFrom(join9(base, "plugins"), "plugin", 8)
|
|
5027
5124
|
];
|
|
5028
5125
|
const byName = /* @__PURE__ */ new Map();
|
|
5029
5126
|
for (const skill of collected) {
|
|
@@ -5034,7 +5131,7 @@ function collectAll(configDir, cwd) {
|
|
|
5034
5131
|
function nestedProjectRoots(cwd) {
|
|
5035
5132
|
let entries;
|
|
5036
5133
|
try {
|
|
5037
|
-
entries =
|
|
5134
|
+
entries = readdirSync3(cwd, { withFileTypes: true });
|
|
5038
5135
|
} catch {
|
|
5039
5136
|
return [];
|
|
5040
5137
|
}
|
|
@@ -5042,8 +5139,8 @@ function nestedProjectRoots(cwd) {
|
|
|
5042
5139
|
for (const entry of entries) {
|
|
5043
5140
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
5044
5141
|
if (!entry.isDirectory() && !isSymlinkedDir(cwd, entry)) continue;
|
|
5045
|
-
const skillsDir =
|
|
5046
|
-
if (
|
|
5142
|
+
const skillsDir = join9(cwd, entry.name, ".claude", "skills");
|
|
5143
|
+
if (existsSync13(skillsDir)) roots.push({ dir: skillsDir, prefix: entry.name });
|
|
5047
5144
|
}
|
|
5048
5145
|
return roots;
|
|
5049
5146
|
}
|
|
@@ -5055,8 +5152,8 @@ function listPluginSkillDetails(configDir, cwd) {
|
|
|
5055
5152
|
for (const { file, ...skill } of collectAll(configDir, cwd)) {
|
|
5056
5153
|
if (skill.source !== "plugin") continue;
|
|
5057
5154
|
try {
|
|
5058
|
-
const { body } = splitFrontmatter(
|
|
5059
|
-
details.push({ ...skill, body, path: file, mtime:
|
|
5155
|
+
const { body } = splitFrontmatter(readFileSync8(file, "utf-8"));
|
|
5156
|
+
details.push({ ...skill, body, path: file, mtime: statSync6(file).mtime.toISOString() });
|
|
5060
5157
|
} catch {
|
|
5061
5158
|
}
|
|
5062
5159
|
}
|
|
@@ -5065,15 +5162,15 @@ function listPluginSkillDetails(configDir, cwd) {
|
|
|
5065
5162
|
function findSkillDetail(configDir, cwd, name, sources) {
|
|
5066
5163
|
const base = configDir ? expandHome2(configDir) : defaultConfigDir();
|
|
5067
5164
|
const roots = [
|
|
5068
|
-
[
|
|
5165
|
+
[join9(cwd, ".claude", "skills"), "project", 2],
|
|
5069
5166
|
...nestedProjectRoots(cwd).map(({ dir, prefix }) => [
|
|
5070
5167
|
dir,
|
|
5071
5168
|
"project",
|
|
5072
5169
|
2,
|
|
5073
5170
|
prefix
|
|
5074
5171
|
]),
|
|
5075
|
-
[
|
|
5076
|
-
[
|
|
5172
|
+
[join9(base, "skills"), "user", 2],
|
|
5173
|
+
[join9(base, "plugins"), "plugin", 8]
|
|
5077
5174
|
];
|
|
5078
5175
|
for (const [root, source, maxDepth, namePrefix] of roots) {
|
|
5079
5176
|
if (!sources.includes(source)) continue;
|
|
@@ -5087,8 +5184,8 @@ function findSkillDetail(configDir, cwd, name, sources) {
|
|
|
5087
5184
|
let raw;
|
|
5088
5185
|
let mtime;
|
|
5089
5186
|
try {
|
|
5090
|
-
raw =
|
|
5091
|
-
mtime =
|
|
5187
|
+
raw = readFileSync8(file, "utf-8");
|
|
5188
|
+
mtime = statSync6(file).mtime.toISOString();
|
|
5092
5189
|
} catch {
|
|
5093
5190
|
continue;
|
|
5094
5191
|
}
|
|
@@ -5579,7 +5676,6 @@ ${trimmedDetail.slice(0, 1500)}
|
|
|
5579
5676
|
const threadModelOverride = store.getModelOverride(channelId, sessionKey);
|
|
5580
5677
|
const effectiveModel = resolveModel(threadModelOverride, channelId, model);
|
|
5581
5678
|
const runner = threadModelOverride ? runnerForModel(threadModelOverride) : runnerForModel(loadDashboardSettings().agent);
|
|
5582
|
-
const outputStyle = await resolveOutputStyleForProfile(getProfile());
|
|
5583
5679
|
if (runner === "codex") {
|
|
5584
5680
|
const stored = store.getSession(channelId, sessionKey);
|
|
5585
5681
|
const resumeId = stored?.runner === "codex" ? stored.sessionId : void 0;
|
|
@@ -5593,7 +5689,7 @@ ${trimmedDetail.slice(0, 1500)}
|
|
|
5593
5689
|
botName,
|
|
5594
5690
|
void 0,
|
|
5595
5691
|
resolveChannelPrompt(channelId),
|
|
5596
|
-
|
|
5692
|
+
resolveSelectedOutputStyle(claudeConfigDir)?.body
|
|
5597
5693
|
),
|
|
5598
5694
|
signal: abortController.signal,
|
|
5599
5695
|
// Same attribution as the Claude path's `askOptions.usageContext`
|
|
@@ -5686,7 +5782,6 @@ ${trimmedDetail.slice(0, 1500)}
|
|
|
5686
5782
|
// workspace prompt for this channel only — same source the dashboard
|
|
5687
5783
|
// chat path reads through resolveChannelConfig().
|
|
5688
5784
|
channelPrompt: resolveChannelPrompt(channelId),
|
|
5689
|
-
outputStyle,
|
|
5690
5785
|
signal: abortController.signal,
|
|
5691
5786
|
// Attribution for the usage row each spawn writes. Sharing one options
|
|
5692
5787
|
// object across every attempt is deliberate: the retries below are
|
|
@@ -7848,13 +7943,13 @@ async function recoverStaleReactions(client, reactionStore) {
|
|
|
7848
7943
|
var ts2 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
7849
7944
|
var POWER_POLL_MS = 3e4;
|
|
7850
7945
|
function isOnACPower() {
|
|
7851
|
-
return new Promise((
|
|
7946
|
+
return new Promise((resolve7) => {
|
|
7852
7947
|
execFile("pmset", ["-g", "ps"], { timeout: 2e3 }, (err, stdout) => {
|
|
7853
7948
|
if (err) {
|
|
7854
|
-
|
|
7949
|
+
resolve7(true);
|
|
7855
7950
|
return;
|
|
7856
7951
|
}
|
|
7857
|
-
|
|
7952
|
+
resolve7(stdout.includes("'AC Power'"));
|
|
7858
7953
|
});
|
|
7859
7954
|
});
|
|
7860
7955
|
}
|
|
@@ -8019,6 +8114,15 @@ export {
|
|
|
8019
8114
|
loadAutomations,
|
|
8020
8115
|
loadAutomationsFile,
|
|
8021
8116
|
loadEffectiveAutomations,
|
|
8117
|
+
BUILT_IN_OUTPUT_STYLES,
|
|
8118
|
+
OutputStyleConflictError,
|
|
8119
|
+
listOutputStyles,
|
|
8120
|
+
createOutputStyle,
|
|
8121
|
+
updateOutputStyle,
|
|
8122
|
+
deleteOutputStyle,
|
|
8123
|
+
resolveSelectedOutputStyle,
|
|
8124
|
+
getOutputStyleDefault,
|
|
8125
|
+
setOutputStyleDefault,
|
|
8022
8126
|
runnerForModel,
|
|
8023
8127
|
isCodexModel,
|
|
8024
8128
|
agentLabel,
|
|
@@ -8057,14 +8161,6 @@ export {
|
|
|
8057
8161
|
setAllowGuestAccess,
|
|
8058
8162
|
getAgentPaused,
|
|
8059
8163
|
setAgentPaused,
|
|
8060
|
-
listOutputStyles,
|
|
8061
|
-
getOutputStyle,
|
|
8062
|
-
insertOutputStyle,
|
|
8063
|
-
updateOutputStyle,
|
|
8064
|
-
deleteOutputStyle,
|
|
8065
|
-
getWorkspaceOutputStyleDefault,
|
|
8066
|
-
setWorkspaceOutputStyleDefault,
|
|
8067
|
-
resolveOutputStyleForProfile,
|
|
8068
8164
|
BRANDING_DEFAULTS,
|
|
8069
8165
|
getWorkspaceBranding,
|
|
8070
8166
|
localDefaults,
|