@sideboard-ai/core 0.1.194 → 0.1.196
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/{agents-OXDDPH2O.js → agents-2JBVL66D.js} +1 -1
- package/dist/{agents-WQL7SZY6.js → agents-CT4T6HA3.js} +1 -1
- package/dist/{chunk-VRPPFKAO.js → chunk-3IGCU4AT.js} +154 -49
- package/dist/{chunk-6H2XPPPR.js → chunk-5DY2QWVN.js} +4 -0
- package/dist/{chunk-2X6RGKLI.js → chunk-NWIBA5CP.js} +4 -0
- package/dist/{chunk-NY366OEZ.js → chunk-NXXJBRWQ.js} +155 -50
- package/dist/index.cjs +1006 -888
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -2
- package/dist/mcp/run-stdio.cjs +175 -57
- package/dist/mcp/run-stdio.js +2 -2
- package/dist/{orchestrator-A5KF2335.js → orchestrator-2FTS55SG.js} +2 -2
- package/dist/{orchestrator-S3M2E7O6.js → orchestrator-CT7VSL7B.js} +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2956,762 +2956,10 @@ var init_thread_store = __esm({
|
|
|
2956
2956
|
}
|
|
2957
2957
|
});
|
|
2958
2958
|
|
|
2959
|
-
// src/
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
function emptySetupLog() {
|
|
2964
|
-
return { ...EMPTY };
|
|
2965
|
-
}
|
|
2966
|
-
function mergeSetupOutput(prev, incoming) {
|
|
2967
|
-
if (!prev) return incoming;
|
|
2968
|
-
if (!incoming) return prev;
|
|
2969
|
-
if (prev === incoming) return prev;
|
|
2970
|
-
if (prev.startsWith(incoming) || prev.endsWith(incoming)) return prev;
|
|
2971
|
-
if (incoming.startsWith(prev) || incoming.endsWith(prev)) return incoming;
|
|
2972
|
-
return incoming.length >= prev.length ? incoming : prev;
|
|
2973
|
-
}
|
|
2974
|
-
function clone(snap) {
|
|
2975
|
-
return { ...snap };
|
|
2976
|
-
}
|
|
2977
|
-
function persistNow(threadId) {
|
|
2978
|
-
const timer = persistTimers.get(threadId);
|
|
2979
|
-
if (timer) {
|
|
2980
|
-
clearTimeout(timer);
|
|
2981
|
-
persistTimers.delete(threadId);
|
|
2982
|
-
}
|
|
2983
|
-
const snap = memory.get(threadId);
|
|
2984
|
-
if (!snap) return;
|
|
2985
|
-
try {
|
|
2986
|
-
(0, import_node_fs10.writeFileSync)(setupLogPath(threadId), `${JSON.stringify(snap)}
|
|
2987
|
-
`, "utf8");
|
|
2988
|
-
} catch {
|
|
2989
|
-
}
|
|
2990
|
-
}
|
|
2991
|
-
function schedulePersist(threadId) {
|
|
2992
|
-
if (persistTimers.has(threadId)) return;
|
|
2993
|
-
persistTimers.set(
|
|
2994
|
-
threadId,
|
|
2995
|
-
setTimeout(() => {
|
|
2996
|
-
persistTimers.delete(threadId);
|
|
2997
|
-
persistNow(threadId);
|
|
2998
|
-
}, 100)
|
|
2999
|
-
);
|
|
3000
|
-
}
|
|
3001
|
-
function loadFromDisk(threadId) {
|
|
3002
|
-
const path2 = setupLogPath(threadId);
|
|
3003
|
-
if (!(0, import_node_fs10.existsSync)(path2)) return null;
|
|
3004
|
-
try {
|
|
3005
|
-
const raw = JSON.parse((0, import_node_fs10.readFileSync)(path2, "utf8"));
|
|
3006
|
-
return {
|
|
3007
|
-
output: typeof raw.output === "string" ? raw.output : "",
|
|
3008
|
-
running: Boolean(raw.running),
|
|
3009
|
-
exitCode: typeof raw.exitCode === "number" ? raw.exitCode : null,
|
|
3010
|
-
source: typeof raw.source === "string" ? raw.source : null
|
|
3011
|
-
};
|
|
3012
|
-
} catch {
|
|
3013
|
-
return null;
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3016
|
-
function readSetupLog(threadId) {
|
|
3017
|
-
const cached = memory.get(threadId);
|
|
3018
|
-
if (cached) return clone(cached);
|
|
3019
|
-
const disk = loadFromDisk(threadId);
|
|
3020
|
-
if (disk) {
|
|
3021
|
-
memory.set(threadId, disk);
|
|
3022
|
-
return clone(disk);
|
|
3023
|
-
}
|
|
3024
|
-
return emptySetupLog();
|
|
3025
|
-
}
|
|
3026
|
-
function beginSetupLog(threadId) {
|
|
3027
|
-
const snap = {
|
|
3028
|
-
output: "",
|
|
3029
|
-
running: true,
|
|
3030
|
-
exitCode: null,
|
|
3031
|
-
source: null
|
|
3032
|
-
};
|
|
3033
|
-
memory.set(threadId, snap);
|
|
3034
|
-
persistNow(threadId);
|
|
3035
|
-
return clone(snap);
|
|
3036
|
-
}
|
|
3037
|
-
function appendSetupLog(threadId, line) {
|
|
3038
|
-
const current = memory.get(threadId) ?? readSetupLog(threadId);
|
|
3039
|
-
const snap = {
|
|
3040
|
-
...current,
|
|
3041
|
-
output: current.output ? `${current.output}
|
|
3042
|
-
${line}` : line,
|
|
3043
|
-
running: true
|
|
3044
|
-
};
|
|
3045
|
-
memory.set(threadId, snap);
|
|
3046
|
-
schedulePersist(threadId);
|
|
3047
|
-
return clone(snap);
|
|
3048
|
-
}
|
|
3049
|
-
function finishSetupLog(threadId, exitCode, source) {
|
|
3050
|
-
const current = memory.get(threadId) ?? readSetupLog(threadId);
|
|
3051
|
-
const snap = {
|
|
3052
|
-
...current,
|
|
3053
|
-
running: false,
|
|
3054
|
-
exitCode,
|
|
3055
|
-
source: source ?? current.source
|
|
3056
|
-
};
|
|
3057
|
-
memory.set(threadId, snap);
|
|
3058
|
-
persistNow(threadId);
|
|
3059
|
-
return clone(snap);
|
|
3060
|
-
}
|
|
3061
|
-
var import_node_fs10, import_node_path11, EMPTY, memory, persistTimers;
|
|
3062
|
-
var init_setup_log = __esm({
|
|
3063
|
-
"src/store/setup-log.ts"() {
|
|
3064
|
-
"use strict";
|
|
3065
|
-
import_node_fs10 = require("fs");
|
|
3066
|
-
import_node_path11 = require("path");
|
|
3067
|
-
init_paths();
|
|
3068
|
-
EMPTY = {
|
|
3069
|
-
output: "",
|
|
3070
|
-
running: false,
|
|
3071
|
-
exitCode: null,
|
|
3072
|
-
source: null
|
|
3073
|
-
};
|
|
3074
|
-
memory = /* @__PURE__ */ new Map();
|
|
3075
|
-
persistTimers = /* @__PURE__ */ new Map();
|
|
3076
|
-
}
|
|
3077
|
-
});
|
|
3078
|
-
|
|
3079
|
-
// src/store/schedules.ts
|
|
3080
|
-
function schedulesPath() {
|
|
3081
|
-
return (0, import_node_path12.join)(appDataDir(), "schedules.json");
|
|
3082
|
-
}
|
|
3083
|
-
function parseDurationMs(every) {
|
|
3084
|
-
const m = every.trim().match(DURATION_RE);
|
|
3085
|
-
if (!m) return null;
|
|
3086
|
-
const n = Number.parseInt(m[1], 10);
|
|
3087
|
-
if (!Number.isFinite(n) || n <= 0) return null;
|
|
3088
|
-
const unit = m[2].toLowerCase();
|
|
3089
|
-
return n * UNIT_MS[unit];
|
|
3090
|
-
}
|
|
3091
|
-
function defaultScheduleName(prompt) {
|
|
3092
|
-
const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
3093
|
-
if (!line) return "Untitled schedule";
|
|
3094
|
-
return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
|
|
3095
|
-
}
|
|
3096
|
-
function resolveScheduleThreadId(raw, env = process.env) {
|
|
3097
|
-
const value = raw?.trim() || "";
|
|
3098
|
-
if (!value) return null;
|
|
3099
|
-
if (value.toLowerCase() === "self") {
|
|
3100
|
-
return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
3101
|
-
}
|
|
3102
|
-
return value;
|
|
3103
|
-
}
|
|
3104
|
-
function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
|
|
3105
|
-
if (when.kind === "once") {
|
|
3106
|
-
const at = new Date(when.at);
|
|
3107
|
-
if (Number.isNaN(at.getTime())) {
|
|
3108
|
-
throw new Error(`Invalid once-at datetime: ${when.at}`);
|
|
3109
|
-
}
|
|
3110
|
-
return at;
|
|
3111
|
-
}
|
|
3112
|
-
if (when.kind === "every") {
|
|
3113
|
-
const ms = parseDurationMs(when.every);
|
|
3114
|
-
if (!ms) {
|
|
3115
|
-
throw new Error(`Invalid interval: ${when.every} (use 15m, 1h, 6h, 1d)`);
|
|
3116
|
-
}
|
|
3117
|
-
return new Date(from.getTime() + ms);
|
|
3118
|
-
}
|
|
3119
|
-
try {
|
|
3120
|
-
const job = new import_croner.Cron(when.expr, {
|
|
3121
|
-
timezone: when.tz?.trim() || void 0,
|
|
3122
|
-
paused: true
|
|
3123
|
-
});
|
|
3124
|
-
const next = job.nextRun(from);
|
|
3125
|
-
if (!next) {
|
|
3126
|
-
throw new Error(`Cron expression has no next run: ${when.expr}`);
|
|
3127
|
-
}
|
|
3128
|
-
return next;
|
|
3129
|
-
} catch (err) {
|
|
3130
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3131
|
-
throw new Error(`Invalid cron: ${message}`);
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
function formatScheduleWhen(when) {
|
|
3135
|
-
if (when.kind === "once") return `once at ${when.at}`;
|
|
3136
|
-
if (when.kind === "every") return `every ${when.every}`;
|
|
3137
|
-
const tz = when.tz?.trim();
|
|
3138
|
-
return tz ? `cron ${when.expr} (${tz})` : `cron ${when.expr}`;
|
|
3139
|
-
}
|
|
3140
|
-
function nowIso2(d = /* @__PURE__ */ new Date()) {
|
|
3141
|
-
return d.toISOString();
|
|
3142
|
-
}
|
|
3143
|
-
function normalizeWhen(raw) {
|
|
3144
|
-
if (!raw || typeof raw !== "object") {
|
|
3145
|
-
throw new Error("Schedule is missing `when`");
|
|
3146
|
-
}
|
|
3147
|
-
const rec = raw;
|
|
3148
|
-
const kind = rec.kind;
|
|
3149
|
-
if (kind === "once" && typeof rec.at === "string") {
|
|
3150
|
-
return { kind: "once", at: rec.at };
|
|
3151
|
-
}
|
|
3152
|
-
if (kind === "every" && typeof rec.every === "string") {
|
|
3153
|
-
return { kind: "every", every: rec.every };
|
|
3154
|
-
}
|
|
3155
|
-
if (kind === "cron" && typeof rec.expr === "string") {
|
|
3156
|
-
return {
|
|
3157
|
-
kind: "cron",
|
|
3158
|
-
expr: rec.expr,
|
|
3159
|
-
tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
|
|
3160
|
-
};
|
|
3161
|
-
}
|
|
3162
|
-
throw new Error("Schedule `when` must be once, every, or cron");
|
|
3163
|
-
}
|
|
3164
|
-
function normalizeTask(raw) {
|
|
3165
|
-
const when = normalizeWhen(raw.when);
|
|
3166
|
-
const agent = raw.agent && isOrchestratorCapableAgent(raw.agent) ? raw.agent : null;
|
|
3167
|
-
return {
|
|
3168
|
-
id: raw.id,
|
|
3169
|
-
name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : defaultScheduleName(raw.prompt),
|
|
3170
|
-
prompt: String(raw.prompt ?? ""),
|
|
3171
|
-
enabled: Boolean(raw.enabled),
|
|
3172
|
-
when,
|
|
3173
|
-
threadId: raw.threadId?.trim() || null,
|
|
3174
|
-
agent,
|
|
3175
|
-
model: raw.model?.trim() || null,
|
|
3176
|
-
nextRunAt: raw.nextRunAt,
|
|
3177
|
-
lastRunAt: raw.lastRunAt ?? null,
|
|
3178
|
-
lastThreadId: raw.lastThreadId ?? null,
|
|
3179
|
-
lastError: raw.lastError ?? null,
|
|
3180
|
-
createdBy: raw.createdBy === "mcp" || raw.createdBy === "cli" || raw.createdBy === "ui" ? raw.createdBy : "ui",
|
|
3181
|
-
createdAt: raw.createdAt,
|
|
3182
|
-
updatedAt: raw.updatedAt
|
|
3183
|
-
};
|
|
3184
|
-
}
|
|
3185
|
-
function readAll() {
|
|
3186
|
-
const path2 = schedulesPath();
|
|
3187
|
-
if (!(0, import_node_fs11.existsSync)(path2)) return [];
|
|
3188
|
-
try {
|
|
3189
|
-
const parsed = JSON.parse((0, import_node_fs11.readFileSync)(path2, "utf8"));
|
|
3190
|
-
if (!Array.isArray(parsed)) return [];
|
|
3191
|
-
return parsed.map((row) => {
|
|
3192
|
-
try {
|
|
3193
|
-
return normalizeTask(row);
|
|
3194
|
-
} catch {
|
|
3195
|
-
return null;
|
|
3196
|
-
}
|
|
3197
|
-
}).filter((row) => Boolean(row?.id && row.prompt));
|
|
3198
|
-
} catch {
|
|
3199
|
-
return [];
|
|
3200
|
-
}
|
|
3201
|
-
}
|
|
3202
|
-
function writeAll(rows) {
|
|
3203
|
-
writePrivateFile(schedulesPath(), `${JSON.stringify(rows, null, 2)}
|
|
3204
|
-
`);
|
|
3205
|
-
}
|
|
3206
|
-
function buildTask(input, now) {
|
|
3207
|
-
const prompt = input.prompt.trim();
|
|
3208
|
-
if (!prompt) throw new Error("Schedule prompt is required");
|
|
3209
|
-
const when = normalizeWhen(input.when);
|
|
3210
|
-
const nextRunAt = computeNextRunAt(when, now).toISOString();
|
|
3211
|
-
const ts = nowIso2(now);
|
|
3212
|
-
const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
|
|
3213
|
-
return {
|
|
3214
|
-
id: (0, import_node_crypto4.randomUUID)(),
|
|
3215
|
-
name: input.name?.trim() || defaultScheduleName(prompt),
|
|
3216
|
-
prompt,
|
|
3217
|
-
enabled: input.enabled !== false,
|
|
3218
|
-
when,
|
|
3219
|
-
threadId: input.threadId?.trim() || null,
|
|
3220
|
-
agent,
|
|
3221
|
-
model: input.model?.trim() || null,
|
|
3222
|
-
nextRunAt,
|
|
3223
|
-
lastRunAt: null,
|
|
3224
|
-
lastThreadId: null,
|
|
3225
|
-
lastError: null,
|
|
3226
|
-
createdBy: input.createdBy,
|
|
3227
|
-
createdAt: ts,
|
|
3228
|
-
updatedAt: ts
|
|
3229
|
-
};
|
|
3230
|
-
}
|
|
3231
|
-
function listSchedules() {
|
|
3232
|
-
return readAll().sort((a, b) => a.nextRunAt.localeCompare(b.nextRunAt));
|
|
3233
|
-
}
|
|
3234
|
-
function hasEnabledSchedules() {
|
|
3235
|
-
return listSchedules().some((row) => row.enabled);
|
|
3236
|
-
}
|
|
3237
|
-
function getSchedule(id) {
|
|
3238
|
-
const key = id.trim();
|
|
3239
|
-
if (!key) return null;
|
|
3240
|
-
return readAll().find(
|
|
3241
|
-
(row) => row.id === key || row.id.startsWith(key) && key.length >= 8
|
|
3242
|
-
) ?? null;
|
|
3243
|
-
}
|
|
3244
|
-
function createSchedule(input) {
|
|
3245
|
-
const task = buildTask(input, /* @__PURE__ */ new Date());
|
|
3246
|
-
const rows = readAll();
|
|
3247
|
-
rows.push(task);
|
|
3248
|
-
writeAll(rows);
|
|
3249
|
-
return task;
|
|
3250
|
-
}
|
|
3251
|
-
function updateSchedule(id, patch) {
|
|
3252
|
-
const rows = readAll();
|
|
3253
|
-
const idx = rows.findIndex((row) => row.id === id || id.length >= 8 && row.id.startsWith(id));
|
|
3254
|
-
if (idx < 0) throw new Error(`Schedule not found: ${id}`);
|
|
3255
|
-
const prev = rows[idx];
|
|
3256
|
-
const when = patch.when ? normalizeWhen(patch.when) : prev.when;
|
|
3257
|
-
const prompt = patch.prompt !== void 0 ? patch.prompt.trim() : prev.prompt;
|
|
3258
|
-
if (!prompt) throw new Error("Schedule prompt is required");
|
|
3259
|
-
const whenChanged = patch.when !== void 0;
|
|
3260
|
-
const next = {
|
|
3261
|
-
...prev,
|
|
3262
|
-
name: patch.name !== void 0 ? patch.name.trim() || defaultScheduleName(prompt) : prev.name,
|
|
3263
|
-
prompt,
|
|
3264
|
-
enabled: patch.enabled ?? prev.enabled,
|
|
3265
|
-
when,
|
|
3266
|
-
threadId: patch.threadId !== void 0 ? patch.threadId?.trim() || null : prev.threadId,
|
|
3267
|
-
agent: patch.agent !== void 0 ? patch.agent ? coerceOrchestratorAgent(patch.agent) : null : prev.agent,
|
|
3268
|
-
model: patch.model !== void 0 ? patch.model?.trim() || null : prev.model,
|
|
3269
|
-
nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
|
|
3270
|
-
lastError: whenChanged ? null : prev.lastError,
|
|
3271
|
-
updatedAt: nowIso2()
|
|
3272
|
-
};
|
|
3273
|
-
rows[idx] = next;
|
|
3274
|
-
writeAll(rows);
|
|
3275
|
-
return next;
|
|
3276
|
-
}
|
|
3277
|
-
function deleteSchedule(id) {
|
|
3278
|
-
const rows = readAll();
|
|
3279
|
-
const next = rows.filter(
|
|
3280
|
-
(row) => row.id !== id && !(id.length >= 8 && row.id.startsWith(id))
|
|
3281
|
-
);
|
|
3282
|
-
if (next.length === rows.length) throw new Error(`Schedule not found: ${id}`);
|
|
3283
|
-
writeAll(next);
|
|
3284
|
-
}
|
|
3285
|
-
function recordScheduleRun(id, result) {
|
|
3286
|
-
const rows = readAll();
|
|
3287
|
-
const idx = rows.findIndex((row) => row.id === id);
|
|
3288
|
-
if (idx < 0) throw new Error(`Schedule not found: ${id}`);
|
|
3289
|
-
const prev = rows[idx];
|
|
3290
|
-
const firedAt = result.firedAt ?? /* @__PURE__ */ new Date();
|
|
3291
|
-
const next = {
|
|
3292
|
-
...prev,
|
|
3293
|
-
enabled: prev.when.kind === "once" ? false : prev.enabled,
|
|
3294
|
-
nextRunAt: prev.when.kind === "once" ? prev.nextRunAt : computeNextRunAt(prev.when, firedAt).toISOString(),
|
|
3295
|
-
lastRunAt: nowIso2(firedAt),
|
|
3296
|
-
lastThreadId: result.lastThreadId !== void 0 ? result.lastThreadId : prev.lastThreadId,
|
|
3297
|
-
lastError: result.lastError ?? null,
|
|
3298
|
-
updatedAt: nowIso2(firedAt)
|
|
3299
|
-
};
|
|
3300
|
-
rows[idx] = next;
|
|
3301
|
-
writeAll(rows);
|
|
3302
|
-
return next;
|
|
3303
|
-
}
|
|
3304
|
-
var import_node_crypto4, import_node_fs11, import_node_path12, import_croner, DURATION_RE, UNIT_MS;
|
|
3305
|
-
var init_schedules = __esm({
|
|
3306
|
-
"src/store/schedules.ts"() {
|
|
3307
|
-
"use strict";
|
|
3308
|
-
import_node_crypto4 = require("crypto");
|
|
3309
|
-
import_node_fs11 = require("fs");
|
|
3310
|
-
import_node_path12 = require("path");
|
|
3311
|
-
import_croner = require("croner");
|
|
3312
|
-
init_orchestrator_capable();
|
|
3313
|
-
init_paths();
|
|
3314
|
-
init_private_file();
|
|
3315
|
-
DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
3316
|
-
UNIT_MS = {
|
|
3317
|
-
s: 1e3,
|
|
3318
|
-
m: 6e4,
|
|
3319
|
-
h: 36e5,
|
|
3320
|
-
d: 864e5
|
|
3321
|
-
};
|
|
3322
|
-
}
|
|
3323
|
-
});
|
|
3324
|
-
|
|
3325
|
-
// src/store/desktop-host.ts
|
|
3326
|
-
function desktopHostPidPath() {
|
|
3327
|
-
return (0, import_node_path13.join)(appDataDir(), "desktop-host.pid");
|
|
3328
|
-
}
|
|
3329
|
-
function claimDesktopHost(pid = process.pid) {
|
|
3330
|
-
(0, import_node_fs12.writeFileSync)(desktopHostPidPath(), `${pid}
|
|
3331
|
-
`, "utf8");
|
|
3332
|
-
}
|
|
3333
|
-
function releaseDesktopHost(pid = process.pid) {
|
|
3334
|
-
if (readDesktopHostPid() !== pid) return;
|
|
3335
|
-
try {
|
|
3336
|
-
(0, import_node_fs12.unlinkSync)(desktopHostPidPath());
|
|
3337
|
-
} catch {
|
|
3338
|
-
}
|
|
3339
|
-
}
|
|
3340
|
-
function readDesktopHostPid() {
|
|
3341
|
-
try {
|
|
3342
|
-
const pid = Number.parseInt((0, import_node_fs12.readFileSync)(desktopHostPidPath(), "utf8").trim(), 10);
|
|
3343
|
-
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
3344
|
-
return pid;
|
|
3345
|
-
} catch {
|
|
3346
|
-
return null;
|
|
3347
|
-
}
|
|
3348
|
-
}
|
|
3349
|
-
function pidAlive(pid) {
|
|
3350
|
-
try {
|
|
3351
|
-
process.kill(pid, 0);
|
|
3352
|
-
return true;
|
|
3353
|
-
} catch {
|
|
3354
|
-
return false;
|
|
3355
|
-
}
|
|
3356
|
-
}
|
|
3357
|
-
function isDesktopHostAlive() {
|
|
3358
|
-
const pid = readDesktopHostPid();
|
|
3359
|
-
return pid != null && pidAlive(pid);
|
|
3360
|
-
}
|
|
3361
|
-
function isThisProcessDesktopHost() {
|
|
3362
|
-
return readDesktopHostPid() === process.pid && pidAlive(process.pid);
|
|
3363
|
-
}
|
|
3364
|
-
function thisProcessShouldDrainAgentQueues() {
|
|
3365
|
-
if (isThisProcessDesktopHost()) return true;
|
|
3366
|
-
return !isDesktopHostAlive();
|
|
3367
|
-
}
|
|
3368
|
-
var import_node_fs12, import_node_path13;
|
|
3369
|
-
var init_desktop_host = __esm({
|
|
3370
|
-
"src/store/desktop-host.ts"() {
|
|
3371
|
-
"use strict";
|
|
3372
|
-
import_node_fs12 = require("fs");
|
|
3373
|
-
import_node_path13 = require("path");
|
|
3374
|
-
init_paths();
|
|
3375
|
-
}
|
|
3376
|
-
});
|
|
3377
|
-
|
|
3378
|
-
// src/brightsy/cloud-connect-constants.ts
|
|
3379
|
-
function parseForceStopMessage(message) {
|
|
3380
|
-
const normalized = message.replace(/\r\n/g, "\n");
|
|
3381
|
-
const nl = normalized.indexOf("\n");
|
|
3382
|
-
const firstLine = (nl === -1 ? normalized : normalized.slice(0, nl)).trim();
|
|
3383
|
-
const remainder = (nl === -1 ? "" : normalized.slice(nl + 1)).trim();
|
|
3384
|
-
const forceStop = firstLine.toLowerCase() === SIDEBOARD_FORCE_STOP.toLowerCase();
|
|
3385
|
-
return { forceStop, remainder: forceStop ? remainder : message };
|
|
3386
|
-
}
|
|
3387
|
-
var CLOUD_ORCHESTRATOR_GOAL, SIDEBOARD_FORCE_STOP, CLOUD_COORDINATOR_BUSY_REPLY, CLOUD_COORDINATOR_STOPPED_REPLY, CLOUD_COORDINATOR_TIMEOUT_REPLY;
|
|
3388
|
-
var init_cloud_connect_constants = __esm({
|
|
3389
|
-
"src/brightsy/cloud-connect-constants.ts"() {
|
|
3390
|
-
"use strict";
|
|
3391
|
-
CLOUD_ORCHESTRATOR_GOAL = "Cloud-connected Sideboard orchestrator";
|
|
3392
|
-
SIDEBOARD_FORCE_STOP = "SIDEBOARD_FORCE_STOP";
|
|
3393
|
-
CLOUD_COORDINATOR_BUSY_REPLY = [
|
|
3394
|
-
"Sideboard is busy with an in-progress client/desktop tool turn on the global coordinator.",
|
|
3395
|
-
"I did not start a new orchestration turn.",
|
|
3396
|
-
`To force-stop the in-progress turn, send another desktop request whose first line is exactly ${SIDEBOARD_FORCE_STOP}`,
|
|
3397
|
-
"(optional follow-up request on later lines).",
|
|
3398
|
-
"What do you want me to do? (retry later, wait until idle, force-stop, rephrase, or cancel)"
|
|
3399
|
-
].join(" ");
|
|
3400
|
-
CLOUD_COORDINATOR_STOPPED_REPLY = [
|
|
3401
|
-
"Sideboard force-stopped the in-progress global coordinator turn.",
|
|
3402
|
-
"No new orchestration request was started."
|
|
3403
|
-
].join(" ");
|
|
3404
|
-
CLOUD_COORDINATOR_TIMEOUT_REPLY = [
|
|
3405
|
-
"Sideboard global coordinator timed out waiting for the local agent turn to finish.",
|
|
3406
|
-
"I did not produce a full orchestration result.",
|
|
3407
|
-
`To force-stop the in-progress turn, send another desktop request whose first line is exactly ${SIDEBOARD_FORCE_STOP}`,
|
|
3408
|
-
"(optional follow-up request on later lines).",
|
|
3409
|
-
"What do you want me to do? (retry later, wait, force-stop, rephrase, or cancel)"
|
|
3410
|
-
].join(" ");
|
|
3411
|
-
}
|
|
3412
|
-
});
|
|
3413
|
-
|
|
3414
|
-
// src/paths/workspace-scratch.ts
|
|
3415
|
-
function attachmentsGitignoreBody() {
|
|
3416
|
-
return ATTACHMENTS_GITIGNORE;
|
|
3417
|
-
}
|
|
3418
|
-
function isWorkspaceScratchPath(relativePath) {
|
|
3419
|
-
const p = relativePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
3420
|
-
return p === ATTACHMENTS_DIR || p.startsWith(`${ATTACHMENTS_DIR}/`) || p === CLI_DIR || p.startsWith(`${CLI_DIR}/`) || p === LEGACY_ATTACHMENTS_DIR || p.startsWith(`${LEGACY_ATTACHMENTS_DIR}/`) || p === ".sideboard" || p.startsWith(".sideboard/") || p === ".context" || p.startsWith(".context/");
|
|
3421
|
-
}
|
|
3422
|
-
var ATTACHMENTS_DIR, CLI_DIR, DETACHED_JOBS_DIR, LEGACY_ATTACHMENTS_DIR, LEGACY_DETACHED_JOBS_DIR, ATTACHMENTS_GITIGNORE;
|
|
3423
|
-
var init_workspace_scratch = __esm({
|
|
3424
|
-
"src/paths/workspace-scratch.ts"() {
|
|
3425
|
-
"use strict";
|
|
3426
|
-
ATTACHMENTS_DIR = ".context/attachments";
|
|
3427
|
-
CLI_DIR = ".context/cli";
|
|
3428
|
-
DETACHED_JOBS_DIR = ".context/.sideboard/detached-jobs";
|
|
3429
|
-
LEGACY_ATTACHMENTS_DIR = ".sideboard/attachments";
|
|
3430
|
-
LEGACY_DETACHED_JOBS_DIR = ".sideboard/detached-jobs";
|
|
3431
|
-
ATTACHMENTS_GITIGNORE = `# Sideboard / workspace attachments (local only)
|
|
3432
|
-
*
|
|
3433
|
-
!.gitignore
|
|
3434
|
-
`;
|
|
3435
|
-
}
|
|
3436
|
-
});
|
|
3437
|
-
|
|
3438
|
-
// src/composer/stage-files.ts
|
|
3439
|
-
function fileExtension(filePath) {
|
|
3440
|
-
const base = (0, import_node_path14.basename)(filePath).toLowerCase();
|
|
3441
|
-
return base.includes(".") ? base.split(".").pop() || "" : "";
|
|
3442
|
-
}
|
|
3443
|
-
function isImageFilePath(filePath) {
|
|
3444
|
-
return IMAGE_EXTENSIONS.has(fileExtension(filePath));
|
|
3445
|
-
}
|
|
3446
|
-
function imageMimeType(filePath) {
|
|
3447
|
-
return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
|
|
3448
|
-
}
|
|
3449
|
-
function ensureAttachmentsDir(worktreePath) {
|
|
3450
|
-
const dir = (0, import_node_path14.join)(worktreePath, ATTACHMENTS_DIR);
|
|
3451
|
-
(0, import_node_fs13.mkdirSync)(dir, { recursive: true });
|
|
3452
|
-
const gi = (0, import_node_path14.join)(dir, ".gitignore");
|
|
3453
|
-
if (!(0, import_node_fs13.existsSync)(gi)) {
|
|
3454
|
-
(0, import_node_fs13.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
3455
|
-
}
|
|
3456
|
-
return dir;
|
|
3457
|
-
}
|
|
3458
|
-
function uniqueAttachmentName(dir, originalName) {
|
|
3459
|
-
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
3460
|
-
if (!(0, import_node_fs13.existsSync)((0, import_node_path14.join)(dir, safe))) return safe;
|
|
3461
|
-
const ext = (0, import_node_path14.extname)(safe);
|
|
3462
|
-
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
3463
|
-
for (let i = 1; i < 1e4; i++) {
|
|
3464
|
-
const candidate = `${stem}-${i}${ext}`;
|
|
3465
|
-
if (!(0, import_node_fs13.existsSync)((0, import_node_path14.join)(dir, candidate))) return candidate;
|
|
3466
|
-
}
|
|
3467
|
-
return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
|
|
3468
|
-
}
|
|
3469
|
-
function previewDataUrlFromBuf(filePath, buf) {
|
|
3470
|
-
if (!isImageFilePath(filePath)) return void 0;
|
|
3471
|
-
if (buf.length > MAX_PREVIEW_BYTES) return void 0;
|
|
3472
|
-
return `data:${imageMimeType(filePath)};base64,${buf.toString("base64")}`;
|
|
3473
|
-
}
|
|
3474
|
-
function attachmentFromBuffer(name, buf, opts) {
|
|
3475
|
-
const previewDataUrl = previewDataUrlFromBuf(name, buf);
|
|
3476
|
-
if (isImageFilePath(name)) {
|
|
3477
|
-
const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
|
|
3478
|
-
return {
|
|
3479
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3480
|
-
name,
|
|
3481
|
-
kind: "file",
|
|
3482
|
-
path: opts.path,
|
|
3483
|
-
previewDataUrl,
|
|
3484
|
-
content: [
|
|
3485
|
-
`Image attached: ${pathHint}`,
|
|
3486
|
-
opts.path ? `Use the Read tool on \`${opts.path}\` to view this image.` : "The image is shown in the composer; copy it into the worktree if you need to inspect pixels."
|
|
3487
|
-
].join("\n")
|
|
3488
|
-
};
|
|
3489
|
-
}
|
|
3490
|
-
if (buf.length > MAX_INLINE_BYTES) {
|
|
3491
|
-
return {
|
|
3492
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3493
|
-
name,
|
|
3494
|
-
kind: "file",
|
|
3495
|
-
path: opts.path,
|
|
3496
|
-
content: opts.path ? `(file too large to attach inline: \`${opts.path}\`, ${buf.length} bytes \u2014 use the Read tool)` : `(file too large to attach inline: ${opts.sourceLabel || name}, ${buf.length} bytes)`
|
|
3497
|
-
};
|
|
3498
|
-
}
|
|
3499
|
-
if (buf.includes(0)) {
|
|
3500
|
-
return {
|
|
3501
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3502
|
-
name,
|
|
3503
|
-
kind: "file",
|
|
3504
|
-
path: opts.path,
|
|
3505
|
-
content: opts.path ? `(binary file at \`${opts.path}\` \u2014 use tools to inspect)` : `(binary file attached by path only: ${opts.sourceLabel || name})`
|
|
3506
|
-
};
|
|
3507
|
-
}
|
|
3508
|
-
return {
|
|
3509
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3510
|
-
name,
|
|
3511
|
-
kind: "file",
|
|
3512
|
-
path: opts.path,
|
|
3513
|
-
content: buf.toString("utf8")
|
|
3514
|
-
};
|
|
3515
|
-
}
|
|
3516
|
-
function attachmentFromAbsolutePath(absolutePath) {
|
|
3517
|
-
const name = (0, import_node_path14.basename)(absolutePath);
|
|
3518
|
-
try {
|
|
3519
|
-
const st = (0, import_node_fs13.statSync)(absolutePath);
|
|
3520
|
-
if (!st.isFile()) {
|
|
3521
|
-
return {
|
|
3522
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3523
|
-
name,
|
|
3524
|
-
kind: "file",
|
|
3525
|
-
content: `(not a file: ${absolutePath})`
|
|
3526
|
-
};
|
|
3527
|
-
}
|
|
3528
|
-
const buf = (0, import_node_fs13.readFileSync)(absolutePath);
|
|
3529
|
-
return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
|
|
3530
|
-
} catch (err) {
|
|
3531
|
-
return {
|
|
3532
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3533
|
-
name,
|
|
3534
|
-
kind: "file",
|
|
3535
|
-
content: `(could not read ${absolutePath}: ${err instanceof Error ? err.message : String(err)})`
|
|
3536
|
-
};
|
|
3537
|
-
}
|
|
3538
|
-
}
|
|
3539
|
-
function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
3540
|
-
if (absolutePaths.length === 0) return [];
|
|
3541
|
-
const dir = ensureAttachmentsDir(worktreePath);
|
|
3542
|
-
const out = [];
|
|
3543
|
-
for (const abs of absolutePaths) {
|
|
3544
|
-
const originalName = (0, import_node_path14.basename)(abs);
|
|
3545
|
-
try {
|
|
3546
|
-
const st = (0, import_node_fs13.statSync)(abs);
|
|
3547
|
-
if (!st.isFile()) continue;
|
|
3548
|
-
const name = uniqueAttachmentName(dir, originalName);
|
|
3549
|
-
const destAbs = (0, import_node_path14.join)(dir, name);
|
|
3550
|
-
(0, import_node_fs13.copyFileSync)(abs, destAbs);
|
|
3551
|
-
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
3552
|
-
const buf = (0, import_node_fs13.readFileSync)(destAbs);
|
|
3553
|
-
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
3554
|
-
} catch (err) {
|
|
3555
|
-
out.push({
|
|
3556
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3557
|
-
name: originalName,
|
|
3558
|
-
kind: "file",
|
|
3559
|
-
content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
|
|
3560
|
-
});
|
|
3561
|
-
}
|
|
3562
|
-
}
|
|
3563
|
-
return out;
|
|
3564
|
-
}
|
|
3565
|
-
function stageBuffersAsAttachments(worktreePath, buffers2) {
|
|
3566
|
-
if (buffers2.length === 0) return [];
|
|
3567
|
-
const dir = ensureAttachmentsDir(worktreePath);
|
|
3568
|
-
const out = [];
|
|
3569
|
-
for (const item of buffers2) {
|
|
3570
|
-
const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
3571
|
-
try {
|
|
3572
|
-
const buf = Buffer.from(item.dataBase64, "base64");
|
|
3573
|
-
const name = uniqueAttachmentName(dir, originalName);
|
|
3574
|
-
const destAbs = (0, import_node_path14.join)(dir, name);
|
|
3575
|
-
(0, import_node_fs13.writeFileSync)(destAbs, buf);
|
|
3576
|
-
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
3577
|
-
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
3578
|
-
} catch (err) {
|
|
3579
|
-
out.push({
|
|
3580
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3581
|
-
name: originalName,
|
|
3582
|
-
kind: "file",
|
|
3583
|
-
content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
|
|
3584
|
-
});
|
|
3585
|
-
}
|
|
3586
|
-
}
|
|
3587
|
-
return out;
|
|
3588
|
-
}
|
|
3589
|
-
function attachmentsFromBuffers(buffers2) {
|
|
3590
|
-
return buffers2.map((item) => {
|
|
3591
|
-
const name = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
3592
|
-
try {
|
|
3593
|
-
const buf = Buffer.from(item.dataBase64, "base64");
|
|
3594
|
-
return attachmentFromBuffer(name, buf, { sourceLabel: name });
|
|
3595
|
-
} catch (err) {
|
|
3596
|
-
return {
|
|
3597
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3598
|
-
name,
|
|
3599
|
-
kind: "file",
|
|
3600
|
-
content: `(could not attach ${name}: ${err instanceof Error ? err.message : String(err)})`
|
|
3601
|
-
};
|
|
3602
|
-
}
|
|
3603
|
-
});
|
|
3604
|
-
}
|
|
3605
|
-
function isWorktreeRelativePath(p) {
|
|
3606
|
-
if (!p || p.includes("..")) return false;
|
|
3607
|
-
if (p.startsWith("/")) return false;
|
|
3608
|
-
if (/^[A-Za-z]:[\\/]/.test(p)) return false;
|
|
3609
|
-
return true;
|
|
3610
|
-
}
|
|
3611
|
-
function dataUrlToBase64(url) {
|
|
3612
|
-
if (!url) return null;
|
|
3613
|
-
const m = /^data:[^;]+;base64,(.+)$/s.exec(url);
|
|
3614
|
-
return m?.[1] ?? null;
|
|
3615
|
-
}
|
|
3616
|
-
function persistPendingFileAttachments(worktreePath, attachments) {
|
|
3617
|
-
if (attachments.length === 0) return attachments;
|
|
3618
|
-
const keep = [];
|
|
3619
|
-
const buffers2 = [];
|
|
3620
|
-
for (const att of attachments) {
|
|
3621
|
-
if (att.kind !== "file") {
|
|
3622
|
-
keep.push(att);
|
|
3623
|
-
continue;
|
|
3624
|
-
}
|
|
3625
|
-
if (att.path && isWorktreeRelativePath(att.path)) {
|
|
3626
|
-
keep.push(att);
|
|
3627
|
-
continue;
|
|
3628
|
-
}
|
|
3629
|
-
const fromPreview = dataUrlToBase64(att.previewDataUrl);
|
|
3630
|
-
if (fromPreview) {
|
|
3631
|
-
buffers2.push({ name: att.name, dataBase64: fromPreview });
|
|
3632
|
-
continue;
|
|
3633
|
-
}
|
|
3634
|
-
if (att.content && !IMAGE_HINT_RE.test(att.content) && !PLACEHOLDER_CONTENT_RE.test(att.content)) {
|
|
3635
|
-
buffers2.push({
|
|
3636
|
-
name: att.name,
|
|
3637
|
-
dataBase64: Buffer.from(att.content, "utf8").toString("base64")
|
|
3638
|
-
});
|
|
3639
|
-
continue;
|
|
3640
|
-
}
|
|
3641
|
-
keep.push(att);
|
|
3642
|
-
}
|
|
3643
|
-
if (buffers2.length === 0) return attachments;
|
|
3644
|
-
return [...keep, ...stageBuffersAsAttachments(worktreePath, buffers2)];
|
|
3645
|
-
}
|
|
3646
|
-
function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
3647
|
-
const out = [];
|
|
3648
|
-
for (const rel of relativePaths) {
|
|
3649
|
-
if (!rel || rel.includes("..") || rel.startsWith("/")) {
|
|
3650
|
-
out.push({
|
|
3651
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3652
|
-
name: (0, import_node_path14.basename)(rel) || "file",
|
|
3653
|
-
kind: "file",
|
|
3654
|
-
content: `(invalid path: ${rel})`
|
|
3655
|
-
});
|
|
3656
|
-
continue;
|
|
3657
|
-
}
|
|
3658
|
-
const name = (0, import_node_path14.basename)(rel);
|
|
3659
|
-
try {
|
|
3660
|
-
const abs = (0, import_node_path14.join)(worktreePath, rel);
|
|
3661
|
-
const st = (0, import_node_fs13.statSync)(abs);
|
|
3662
|
-
if (!st.isFile()) continue;
|
|
3663
|
-
const buf = (0, import_node_fs13.readFileSync)(abs);
|
|
3664
|
-
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
3665
|
-
} catch (err) {
|
|
3666
|
-
out.push({
|
|
3667
|
-
id: (0, import_node_crypto5.randomUUID)(),
|
|
3668
|
-
name,
|
|
3669
|
-
kind: "file",
|
|
3670
|
-
content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
|
|
3671
|
-
});
|
|
3672
|
-
}
|
|
3673
|
-
}
|
|
3674
|
-
return out;
|
|
3675
|
-
}
|
|
3676
|
-
var import_node_fs13, import_node_path14, import_node_crypto5, IMAGE_EXTENSIONS, IMAGE_MIME_BY_EXT, MAX_INLINE_BYTES, MAX_PREVIEW_BYTES, IMAGE_HINT_RE, PLACEHOLDER_CONTENT_RE;
|
|
3677
|
-
var init_stage_files = __esm({
|
|
3678
|
-
"src/composer/stage-files.ts"() {
|
|
3679
|
-
"use strict";
|
|
3680
|
-
import_node_fs13 = require("fs");
|
|
3681
|
-
import_node_path14 = require("path");
|
|
3682
|
-
import_node_crypto5 = require("crypto");
|
|
3683
|
-
init_workspace_scratch();
|
|
3684
|
-
IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
3685
|
-
"png",
|
|
3686
|
-
"jpg",
|
|
3687
|
-
"jpeg",
|
|
3688
|
-
"gif",
|
|
3689
|
-
"webp",
|
|
3690
|
-
"svg",
|
|
3691
|
-
"bmp",
|
|
3692
|
-
"ico"
|
|
3693
|
-
]);
|
|
3694
|
-
IMAGE_MIME_BY_EXT = {
|
|
3695
|
-
png: "image/png",
|
|
3696
|
-
jpg: "image/jpeg",
|
|
3697
|
-
jpeg: "image/jpeg",
|
|
3698
|
-
gif: "image/gif",
|
|
3699
|
-
webp: "image/webp",
|
|
3700
|
-
svg: "image/svg+xml",
|
|
3701
|
-
bmp: "image/bmp",
|
|
3702
|
-
ico: "image/x-icon"
|
|
3703
|
-
};
|
|
3704
|
-
MAX_INLINE_BYTES = 4e5;
|
|
3705
|
-
MAX_PREVIEW_BYTES = 5e6;
|
|
3706
|
-
IMAGE_HINT_RE = /^Image attached:/;
|
|
3707
|
-
PLACEHOLDER_CONTENT_RE = /^\((could not |file too large|binary file|not a file|invalid path)/;
|
|
3708
|
-
}
|
|
3709
|
-
});
|
|
3710
|
-
|
|
3711
|
-
// src/git/team-meta.ts
|
|
3712
|
-
var SOCCER_TEAM_META;
|
|
3713
|
-
var init_team_meta = __esm({
|
|
3714
|
-
"src/git/team-meta.ts"() {
|
|
2959
|
+
// src/git/team-meta.ts
|
|
2960
|
+
var SOCCER_TEAM_META;
|
|
2961
|
+
var init_team_meta = __esm({
|
|
2962
|
+
"src/git/team-meta.ts"() {
|
|
3715
2963
|
"use strict";
|
|
3716
2964
|
SOCCER_TEAM_META = {
|
|
3717
2965
|
"aalborg": { location: "Aalborg, Denmark", league: "Danish Superliga" },
|
|
@@ -4502,77 +3750,835 @@ var init_teams = __esm({
|
|
|
4502
3750
|
}
|
|
4503
3751
|
});
|
|
4504
3752
|
|
|
4505
|
-
// src/git/worktree-labels.ts
|
|
4506
|
-
function normalizeWorktreePath(worktreePath) {
|
|
4507
|
-
const trimmed = (worktreePath ?? "").replace(/\/+$/, "") || "/";
|
|
4508
|
-
const absolute = trimmed.startsWith("/");
|
|
4509
|
-
const parts = trimmed.split("/").filter((p) => p && p !== ".");
|
|
3753
|
+
// src/git/worktree-labels.ts
|
|
3754
|
+
function normalizeWorktreePath(worktreePath) {
|
|
3755
|
+
const trimmed = (worktreePath ?? "").replace(/\/+$/, "") || "/";
|
|
3756
|
+
const absolute = trimmed.startsWith("/");
|
|
3757
|
+
const parts = trimmed.split("/").filter((p) => p && p !== ".");
|
|
3758
|
+
const out = [];
|
|
3759
|
+
for (const p of parts) {
|
|
3760
|
+
if (p === "..") {
|
|
3761
|
+
out.pop();
|
|
3762
|
+
continue;
|
|
3763
|
+
}
|
|
3764
|
+
out.push(p);
|
|
3765
|
+
}
|
|
3766
|
+
return (absolute ? "/" : "") + out.join("/");
|
|
3767
|
+
}
|
|
3768
|
+
function worktreeNameFromPath(worktreePath) {
|
|
3769
|
+
const normalized = worktreePath.replace(/\/+$/, "");
|
|
3770
|
+
const parts = normalized.split("/");
|
|
3771
|
+
return parts[parts.length - 1] || worktreePath;
|
|
3772
|
+
}
|
|
3773
|
+
function ticketSlugForBranch(sourceRef) {
|
|
3774
|
+
const raw = sourceRef.trim();
|
|
3775
|
+
if (!raw) return null;
|
|
3776
|
+
const linear = raw.match(/\b([A-Za-z]{2,8}-\d{1,6})\b/);
|
|
3777
|
+
if (linear) return linear[1].toLowerCase();
|
|
3778
|
+
const hashed = raw.match(/^#(\d{1,8})$/);
|
|
3779
|
+
if (hashed) return hashed[1];
|
|
3780
|
+
const digits = raw.match(/^(\d{1,8})$/);
|
|
3781
|
+
if (digits) return digits[1];
|
|
3782
|
+
const slug = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
3783
|
+
return slug || null;
|
|
3784
|
+
}
|
|
3785
|
+
function worktreeSlugForTicket(ticketRef, teamSlug) {
|
|
3786
|
+
const ticket = ticketSlugForBranch(ticketRef);
|
|
3787
|
+
const team = teamSlug.trim().replace(/^thread\//, "");
|
|
3788
|
+
if (!ticket) return team;
|
|
3789
|
+
if (!team) return ticket;
|
|
3790
|
+
return `${ticket}-${team}`;
|
|
3791
|
+
}
|
|
3792
|
+
function isPlaceholderBranch(branchName, worktreePath) {
|
|
3793
|
+
const branch = branchName.trim();
|
|
3794
|
+
if (!branch || branch === "HEAD") return true;
|
|
3795
|
+
if (branch.startsWith("thread/")) return true;
|
|
3796
|
+
const dir = worktreeNameFromPath(worktreePath).toLowerCase();
|
|
3797
|
+
return branch.toLowerCase() === dir;
|
|
3798
|
+
}
|
|
3799
|
+
function branchDisplayLabel(branchName, worktreePath) {
|
|
3800
|
+
const dir = worktreeNameFromPath(worktreePath);
|
|
3801
|
+
if (isPlaceholderBranch(branchName, worktreePath)) {
|
|
3802
|
+
const slug = branchName.replace(/^thread\//, "") || dir;
|
|
3803
|
+
return teamNameFromSlug(slug);
|
|
3804
|
+
}
|
|
3805
|
+
return branchName.trim();
|
|
3806
|
+
}
|
|
3807
|
+
function threadDisplayLabel(thread) {
|
|
3808
|
+
if (thread.userSetTitle && thread.title?.trim()) return thread.title.trim();
|
|
3809
|
+
if (thread.prTitle?.trim()) return thread.prTitle.trim();
|
|
3810
|
+
return branchDisplayLabel(thread.branchName, thread.worktreePath);
|
|
3811
|
+
}
|
|
3812
|
+
function worktreeDisplayLabel(thread) {
|
|
3813
|
+
return threadDisplayLabel(thread);
|
|
3814
|
+
}
|
|
3815
|
+
function worktreeDisplayLabelForGroup(threads) {
|
|
3816
|
+
if (threads.length === 0) return "Worktree";
|
|
3817
|
+
const canonical = [...threads].sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
|
|
3818
|
+
return threadDisplayLabel(canonical);
|
|
3819
|
+
}
|
|
3820
|
+
var init_worktree_labels = __esm({
|
|
3821
|
+
"src/git/worktree-labels.ts"() {
|
|
3822
|
+
"use strict";
|
|
3823
|
+
init_teams();
|
|
3824
|
+
}
|
|
3825
|
+
});
|
|
3826
|
+
|
|
3827
|
+
// src/store/setup-log.ts
|
|
3828
|
+
function setupLogPath(threadId) {
|
|
3829
|
+
return (0, import_node_path11.join)(threadsDir(), `${threadId}.setup.log.json`);
|
|
3830
|
+
}
|
|
3831
|
+
function setupLogKeyForWorktree(worktreePath) {
|
|
3832
|
+
const norm = normalizeWorktreePath(worktreePath);
|
|
3833
|
+
return `wt-${(0, import_node_crypto4.createHash)("sha1").update(norm).digest("hex").slice(0, 16)}`;
|
|
3834
|
+
}
|
|
3835
|
+
function emptySetupLog() {
|
|
3836
|
+
return { ...EMPTY };
|
|
3837
|
+
}
|
|
3838
|
+
function mergeSetupOutput(prev, incoming) {
|
|
3839
|
+
if (!prev) return incoming;
|
|
3840
|
+
if (!incoming) return prev;
|
|
3841
|
+
if (prev === incoming) return prev;
|
|
3842
|
+
if (prev.startsWith(incoming) || prev.endsWith(incoming)) return prev;
|
|
3843
|
+
if (incoming.startsWith(prev) || incoming.endsWith(prev)) return incoming;
|
|
3844
|
+
return incoming.length >= prev.length ? incoming : prev;
|
|
3845
|
+
}
|
|
3846
|
+
function clone(snap) {
|
|
3847
|
+
return { ...snap };
|
|
3848
|
+
}
|
|
3849
|
+
function persistNow(threadId) {
|
|
3850
|
+
const timer = persistTimers.get(threadId);
|
|
3851
|
+
if (timer) {
|
|
3852
|
+
clearTimeout(timer);
|
|
3853
|
+
persistTimers.delete(threadId);
|
|
3854
|
+
}
|
|
3855
|
+
const snap = memory.get(threadId);
|
|
3856
|
+
if (!snap) return;
|
|
3857
|
+
try {
|
|
3858
|
+
(0, import_node_fs10.writeFileSync)(setupLogPath(threadId), `${JSON.stringify(snap)}
|
|
3859
|
+
`, "utf8");
|
|
3860
|
+
} catch {
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
function schedulePersist(threadId) {
|
|
3864
|
+
if (persistTimers.has(threadId)) return;
|
|
3865
|
+
persistTimers.set(
|
|
3866
|
+
threadId,
|
|
3867
|
+
setTimeout(() => {
|
|
3868
|
+
persistTimers.delete(threadId);
|
|
3869
|
+
persistNow(threadId);
|
|
3870
|
+
}, 100)
|
|
3871
|
+
);
|
|
3872
|
+
}
|
|
3873
|
+
function loadFromDisk(threadId) {
|
|
3874
|
+
const path2 = setupLogPath(threadId);
|
|
3875
|
+
if (!(0, import_node_fs10.existsSync)(path2)) return null;
|
|
3876
|
+
try {
|
|
3877
|
+
const raw = JSON.parse((0, import_node_fs10.readFileSync)(path2, "utf8"));
|
|
3878
|
+
return {
|
|
3879
|
+
output: typeof raw.output === "string" ? raw.output : "",
|
|
3880
|
+
running: Boolean(raw.running),
|
|
3881
|
+
exitCode: typeof raw.exitCode === "number" ? raw.exitCode : null,
|
|
3882
|
+
source: typeof raw.source === "string" ? raw.source : null
|
|
3883
|
+
};
|
|
3884
|
+
} catch {
|
|
3885
|
+
return null;
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
function readSetupLog(threadId) {
|
|
3889
|
+
const cached = memory.get(threadId);
|
|
3890
|
+
if (cached) return clone(cached);
|
|
3891
|
+
const disk = loadFromDisk(threadId);
|
|
3892
|
+
if (disk) {
|
|
3893
|
+
memory.set(threadId, disk);
|
|
3894
|
+
return clone(disk);
|
|
3895
|
+
}
|
|
3896
|
+
return emptySetupLog();
|
|
3897
|
+
}
|
|
3898
|
+
function beginSetupLog(threadId) {
|
|
3899
|
+
const snap = {
|
|
3900
|
+
output: "",
|
|
3901
|
+
running: true,
|
|
3902
|
+
exitCode: null,
|
|
3903
|
+
source: null
|
|
3904
|
+
};
|
|
3905
|
+
memory.set(threadId, snap);
|
|
3906
|
+
persistNow(threadId);
|
|
3907
|
+
return clone(snap);
|
|
3908
|
+
}
|
|
3909
|
+
function appendSetupLog(threadId, line) {
|
|
3910
|
+
const current = memory.get(threadId) ?? readSetupLog(threadId);
|
|
3911
|
+
const snap = {
|
|
3912
|
+
...current,
|
|
3913
|
+
output: current.output ? `${current.output}
|
|
3914
|
+
${line}` : line,
|
|
3915
|
+
running: true
|
|
3916
|
+
};
|
|
3917
|
+
memory.set(threadId, snap);
|
|
3918
|
+
schedulePersist(threadId);
|
|
3919
|
+
return clone(snap);
|
|
3920
|
+
}
|
|
3921
|
+
function finishSetupLog(threadId, exitCode, source) {
|
|
3922
|
+
const current = memory.get(threadId) ?? readSetupLog(threadId);
|
|
3923
|
+
const snap = {
|
|
3924
|
+
...current,
|
|
3925
|
+
running: false,
|
|
3926
|
+
exitCode,
|
|
3927
|
+
source: source ?? current.source
|
|
3928
|
+
};
|
|
3929
|
+
memory.set(threadId, snap);
|
|
3930
|
+
persistNow(threadId);
|
|
3931
|
+
return clone(snap);
|
|
3932
|
+
}
|
|
3933
|
+
var import_node_crypto4, import_node_fs10, import_node_path11, EMPTY, memory, persistTimers;
|
|
3934
|
+
var init_setup_log = __esm({
|
|
3935
|
+
"src/store/setup-log.ts"() {
|
|
3936
|
+
"use strict";
|
|
3937
|
+
import_node_crypto4 = require("crypto");
|
|
3938
|
+
import_node_fs10 = require("fs");
|
|
3939
|
+
import_node_path11 = require("path");
|
|
3940
|
+
init_worktree_labels();
|
|
3941
|
+
init_paths();
|
|
3942
|
+
EMPTY = {
|
|
3943
|
+
output: "",
|
|
3944
|
+
running: false,
|
|
3945
|
+
exitCode: null,
|
|
3946
|
+
source: null
|
|
3947
|
+
};
|
|
3948
|
+
memory = /* @__PURE__ */ new Map();
|
|
3949
|
+
persistTimers = /* @__PURE__ */ new Map();
|
|
3950
|
+
}
|
|
3951
|
+
});
|
|
3952
|
+
|
|
3953
|
+
// src/store/schedules.ts
|
|
3954
|
+
function schedulesPath() {
|
|
3955
|
+
return (0, import_node_path12.join)(appDataDir(), "schedules.json");
|
|
3956
|
+
}
|
|
3957
|
+
function parseDurationMs(every) {
|
|
3958
|
+
const m = every.trim().match(DURATION_RE);
|
|
3959
|
+
if (!m) return null;
|
|
3960
|
+
const n = Number.parseInt(m[1], 10);
|
|
3961
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
3962
|
+
const unit = m[2].toLowerCase();
|
|
3963
|
+
return n * UNIT_MS[unit];
|
|
3964
|
+
}
|
|
3965
|
+
function defaultScheduleName(prompt) {
|
|
3966
|
+
const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
3967
|
+
if (!line) return "Untitled schedule";
|
|
3968
|
+
return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
|
|
3969
|
+
}
|
|
3970
|
+
function resolveScheduleThreadId(raw, env = process.env) {
|
|
3971
|
+
const value = raw?.trim() || "";
|
|
3972
|
+
if (!value) return null;
|
|
3973
|
+
if (value.toLowerCase() === "self") {
|
|
3974
|
+
return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
|
|
3975
|
+
}
|
|
3976
|
+
return value;
|
|
3977
|
+
}
|
|
3978
|
+
function computeNextRunAt(when, from = /* @__PURE__ */ new Date()) {
|
|
3979
|
+
if (when.kind === "once") {
|
|
3980
|
+
const at = new Date(when.at);
|
|
3981
|
+
if (Number.isNaN(at.getTime())) {
|
|
3982
|
+
throw new Error(`Invalid once-at datetime: ${when.at}`);
|
|
3983
|
+
}
|
|
3984
|
+
return at;
|
|
3985
|
+
}
|
|
3986
|
+
if (when.kind === "every") {
|
|
3987
|
+
const ms = parseDurationMs(when.every);
|
|
3988
|
+
if (!ms) {
|
|
3989
|
+
throw new Error(`Invalid interval: ${when.every} (use 15m, 1h, 6h, 1d)`);
|
|
3990
|
+
}
|
|
3991
|
+
return new Date(from.getTime() + ms);
|
|
3992
|
+
}
|
|
3993
|
+
try {
|
|
3994
|
+
const job = new import_croner.Cron(when.expr, {
|
|
3995
|
+
timezone: when.tz?.trim() || void 0,
|
|
3996
|
+
paused: true
|
|
3997
|
+
});
|
|
3998
|
+
const next = job.nextRun(from);
|
|
3999
|
+
if (!next) {
|
|
4000
|
+
throw new Error(`Cron expression has no next run: ${when.expr}`);
|
|
4001
|
+
}
|
|
4002
|
+
return next;
|
|
4003
|
+
} catch (err) {
|
|
4004
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4005
|
+
throw new Error(`Invalid cron: ${message}`);
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
function formatScheduleWhen(when) {
|
|
4009
|
+
if (when.kind === "once") return `once at ${when.at}`;
|
|
4010
|
+
if (when.kind === "every") return `every ${when.every}`;
|
|
4011
|
+
const tz = when.tz?.trim();
|
|
4012
|
+
return tz ? `cron ${when.expr} (${tz})` : `cron ${when.expr}`;
|
|
4013
|
+
}
|
|
4014
|
+
function nowIso2(d = /* @__PURE__ */ new Date()) {
|
|
4015
|
+
return d.toISOString();
|
|
4016
|
+
}
|
|
4017
|
+
function normalizeWhen(raw) {
|
|
4018
|
+
if (!raw || typeof raw !== "object") {
|
|
4019
|
+
throw new Error("Schedule is missing `when`");
|
|
4020
|
+
}
|
|
4021
|
+
const rec = raw;
|
|
4022
|
+
const kind = rec.kind;
|
|
4023
|
+
if (kind === "once" && typeof rec.at === "string") {
|
|
4024
|
+
return { kind: "once", at: rec.at };
|
|
4025
|
+
}
|
|
4026
|
+
if (kind === "every" && typeof rec.every === "string") {
|
|
4027
|
+
return { kind: "every", every: rec.every };
|
|
4028
|
+
}
|
|
4029
|
+
if (kind === "cron" && typeof rec.expr === "string") {
|
|
4030
|
+
return {
|
|
4031
|
+
kind: "cron",
|
|
4032
|
+
expr: rec.expr,
|
|
4033
|
+
tz: typeof rec.tz === "string" && rec.tz.trim() ? rec.tz.trim() : void 0
|
|
4034
|
+
};
|
|
4035
|
+
}
|
|
4036
|
+
throw new Error("Schedule `when` must be once, every, or cron");
|
|
4037
|
+
}
|
|
4038
|
+
function normalizeTask(raw) {
|
|
4039
|
+
const when = normalizeWhen(raw.when);
|
|
4040
|
+
const agent = raw.agent && isOrchestratorCapableAgent(raw.agent) ? raw.agent : null;
|
|
4041
|
+
return {
|
|
4042
|
+
id: raw.id,
|
|
4043
|
+
name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : defaultScheduleName(raw.prompt),
|
|
4044
|
+
prompt: String(raw.prompt ?? ""),
|
|
4045
|
+
enabled: Boolean(raw.enabled),
|
|
4046
|
+
when,
|
|
4047
|
+
threadId: raw.threadId?.trim() || null,
|
|
4048
|
+
agent,
|
|
4049
|
+
model: raw.model?.trim() || null,
|
|
4050
|
+
nextRunAt: raw.nextRunAt,
|
|
4051
|
+
lastRunAt: raw.lastRunAt ?? null,
|
|
4052
|
+
lastThreadId: raw.lastThreadId ?? null,
|
|
4053
|
+
lastError: raw.lastError ?? null,
|
|
4054
|
+
createdBy: raw.createdBy === "mcp" || raw.createdBy === "cli" || raw.createdBy === "ui" ? raw.createdBy : "ui",
|
|
4055
|
+
createdAt: raw.createdAt,
|
|
4056
|
+
updatedAt: raw.updatedAt
|
|
4057
|
+
};
|
|
4058
|
+
}
|
|
4059
|
+
function readAll() {
|
|
4060
|
+
const path2 = schedulesPath();
|
|
4061
|
+
if (!(0, import_node_fs11.existsSync)(path2)) return [];
|
|
4062
|
+
try {
|
|
4063
|
+
const parsed = JSON.parse((0, import_node_fs11.readFileSync)(path2, "utf8"));
|
|
4064
|
+
if (!Array.isArray(parsed)) return [];
|
|
4065
|
+
return parsed.map((row) => {
|
|
4066
|
+
try {
|
|
4067
|
+
return normalizeTask(row);
|
|
4068
|
+
} catch {
|
|
4069
|
+
return null;
|
|
4070
|
+
}
|
|
4071
|
+
}).filter((row) => Boolean(row?.id && row.prompt));
|
|
4072
|
+
} catch {
|
|
4073
|
+
return [];
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
function writeAll(rows) {
|
|
4077
|
+
writePrivateFile(schedulesPath(), `${JSON.stringify(rows, null, 2)}
|
|
4078
|
+
`);
|
|
4079
|
+
}
|
|
4080
|
+
function buildTask(input, now) {
|
|
4081
|
+
const prompt = input.prompt.trim();
|
|
4082
|
+
if (!prompt) throw new Error("Schedule prompt is required");
|
|
4083
|
+
const when = normalizeWhen(input.when);
|
|
4084
|
+
const nextRunAt = computeNextRunAt(when, now).toISOString();
|
|
4085
|
+
const ts = nowIso2(now);
|
|
4086
|
+
const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
|
|
4087
|
+
return {
|
|
4088
|
+
id: (0, import_node_crypto5.randomUUID)(),
|
|
4089
|
+
name: input.name?.trim() || defaultScheduleName(prompt),
|
|
4090
|
+
prompt,
|
|
4091
|
+
enabled: input.enabled !== false,
|
|
4092
|
+
when,
|
|
4093
|
+
threadId: input.threadId?.trim() || null,
|
|
4094
|
+
agent,
|
|
4095
|
+
model: input.model?.trim() || null,
|
|
4096
|
+
nextRunAt,
|
|
4097
|
+
lastRunAt: null,
|
|
4098
|
+
lastThreadId: null,
|
|
4099
|
+
lastError: null,
|
|
4100
|
+
createdBy: input.createdBy,
|
|
4101
|
+
createdAt: ts,
|
|
4102
|
+
updatedAt: ts
|
|
4103
|
+
};
|
|
4104
|
+
}
|
|
4105
|
+
function listSchedules() {
|
|
4106
|
+
return readAll().sort((a, b) => a.nextRunAt.localeCompare(b.nextRunAt));
|
|
4107
|
+
}
|
|
4108
|
+
function hasEnabledSchedules() {
|
|
4109
|
+
return listSchedules().some((row) => row.enabled);
|
|
4110
|
+
}
|
|
4111
|
+
function getSchedule(id) {
|
|
4112
|
+
const key = id.trim();
|
|
4113
|
+
if (!key) return null;
|
|
4114
|
+
return readAll().find(
|
|
4115
|
+
(row) => row.id === key || row.id.startsWith(key) && key.length >= 8
|
|
4116
|
+
) ?? null;
|
|
4117
|
+
}
|
|
4118
|
+
function createSchedule(input) {
|
|
4119
|
+
const task = buildTask(input, /* @__PURE__ */ new Date());
|
|
4120
|
+
const rows = readAll();
|
|
4121
|
+
rows.push(task);
|
|
4122
|
+
writeAll(rows);
|
|
4123
|
+
return task;
|
|
4124
|
+
}
|
|
4125
|
+
function updateSchedule(id, patch) {
|
|
4126
|
+
const rows = readAll();
|
|
4127
|
+
const idx = rows.findIndex((row) => row.id === id || id.length >= 8 && row.id.startsWith(id));
|
|
4128
|
+
if (idx < 0) throw new Error(`Schedule not found: ${id}`);
|
|
4129
|
+
const prev = rows[idx];
|
|
4130
|
+
const when = patch.when ? normalizeWhen(patch.when) : prev.when;
|
|
4131
|
+
const prompt = patch.prompt !== void 0 ? patch.prompt.trim() : prev.prompt;
|
|
4132
|
+
if (!prompt) throw new Error("Schedule prompt is required");
|
|
4133
|
+
const whenChanged = patch.when !== void 0;
|
|
4134
|
+
const next = {
|
|
4135
|
+
...prev,
|
|
4136
|
+
name: patch.name !== void 0 ? patch.name.trim() || defaultScheduleName(prompt) : prev.name,
|
|
4137
|
+
prompt,
|
|
4138
|
+
enabled: patch.enabled ?? prev.enabled,
|
|
4139
|
+
when,
|
|
4140
|
+
threadId: patch.threadId !== void 0 ? patch.threadId?.trim() || null : prev.threadId,
|
|
4141
|
+
agent: patch.agent !== void 0 ? patch.agent ? coerceOrchestratorAgent(patch.agent) : null : prev.agent,
|
|
4142
|
+
model: patch.model !== void 0 ? patch.model?.trim() || null : prev.model,
|
|
4143
|
+
nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
|
|
4144
|
+
lastError: whenChanged ? null : prev.lastError,
|
|
4145
|
+
updatedAt: nowIso2()
|
|
4146
|
+
};
|
|
4147
|
+
rows[idx] = next;
|
|
4148
|
+
writeAll(rows);
|
|
4149
|
+
return next;
|
|
4150
|
+
}
|
|
4151
|
+
function deleteSchedule(id) {
|
|
4152
|
+
const rows = readAll();
|
|
4153
|
+
const next = rows.filter(
|
|
4154
|
+
(row) => row.id !== id && !(id.length >= 8 && row.id.startsWith(id))
|
|
4155
|
+
);
|
|
4156
|
+
if (next.length === rows.length) throw new Error(`Schedule not found: ${id}`);
|
|
4157
|
+
writeAll(next);
|
|
4158
|
+
}
|
|
4159
|
+
function recordScheduleRun(id, result) {
|
|
4160
|
+
const rows = readAll();
|
|
4161
|
+
const idx = rows.findIndex((row) => row.id === id);
|
|
4162
|
+
if (idx < 0) throw new Error(`Schedule not found: ${id}`);
|
|
4163
|
+
const prev = rows[idx];
|
|
4164
|
+
const firedAt = result.firedAt ?? /* @__PURE__ */ new Date();
|
|
4165
|
+
const next = {
|
|
4166
|
+
...prev,
|
|
4167
|
+
enabled: prev.when.kind === "once" ? false : prev.enabled,
|
|
4168
|
+
nextRunAt: prev.when.kind === "once" ? prev.nextRunAt : computeNextRunAt(prev.when, firedAt).toISOString(),
|
|
4169
|
+
lastRunAt: nowIso2(firedAt),
|
|
4170
|
+
lastThreadId: result.lastThreadId !== void 0 ? result.lastThreadId : prev.lastThreadId,
|
|
4171
|
+
lastError: result.lastError ?? null,
|
|
4172
|
+
updatedAt: nowIso2(firedAt)
|
|
4173
|
+
};
|
|
4174
|
+
rows[idx] = next;
|
|
4175
|
+
writeAll(rows);
|
|
4176
|
+
return next;
|
|
4177
|
+
}
|
|
4178
|
+
var import_node_crypto5, import_node_fs11, import_node_path12, import_croner, DURATION_RE, UNIT_MS;
|
|
4179
|
+
var init_schedules = __esm({
|
|
4180
|
+
"src/store/schedules.ts"() {
|
|
4181
|
+
"use strict";
|
|
4182
|
+
import_node_crypto5 = require("crypto");
|
|
4183
|
+
import_node_fs11 = require("fs");
|
|
4184
|
+
import_node_path12 = require("path");
|
|
4185
|
+
import_croner = require("croner");
|
|
4186
|
+
init_orchestrator_capable();
|
|
4187
|
+
init_paths();
|
|
4188
|
+
init_private_file();
|
|
4189
|
+
DURATION_RE = /^(\d+)(s|m|h|d)$/i;
|
|
4190
|
+
UNIT_MS = {
|
|
4191
|
+
s: 1e3,
|
|
4192
|
+
m: 6e4,
|
|
4193
|
+
h: 36e5,
|
|
4194
|
+
d: 864e5
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
});
|
|
4198
|
+
|
|
4199
|
+
// src/store/desktop-host.ts
|
|
4200
|
+
function desktopHostPidPath() {
|
|
4201
|
+
return (0, import_node_path13.join)(appDataDir(), "desktop-host.pid");
|
|
4202
|
+
}
|
|
4203
|
+
function claimDesktopHost(pid = process.pid) {
|
|
4204
|
+
(0, import_node_fs12.writeFileSync)(desktopHostPidPath(), `${pid}
|
|
4205
|
+
`, "utf8");
|
|
4206
|
+
}
|
|
4207
|
+
function releaseDesktopHost(pid = process.pid) {
|
|
4208
|
+
if (readDesktopHostPid() !== pid) return;
|
|
4209
|
+
try {
|
|
4210
|
+
(0, import_node_fs12.unlinkSync)(desktopHostPidPath());
|
|
4211
|
+
} catch {
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
function readDesktopHostPid() {
|
|
4215
|
+
try {
|
|
4216
|
+
const pid = Number.parseInt((0, import_node_fs12.readFileSync)(desktopHostPidPath(), "utf8").trim(), 10);
|
|
4217
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
4218
|
+
return pid;
|
|
4219
|
+
} catch {
|
|
4220
|
+
return null;
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
function pidAlive(pid) {
|
|
4224
|
+
try {
|
|
4225
|
+
process.kill(pid, 0);
|
|
4226
|
+
return true;
|
|
4227
|
+
} catch {
|
|
4228
|
+
return false;
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
function isDesktopHostAlive() {
|
|
4232
|
+
const pid = readDesktopHostPid();
|
|
4233
|
+
return pid != null && pidAlive(pid);
|
|
4234
|
+
}
|
|
4235
|
+
function isThisProcessDesktopHost() {
|
|
4236
|
+
return readDesktopHostPid() === process.pid && pidAlive(process.pid);
|
|
4237
|
+
}
|
|
4238
|
+
function thisProcessShouldDrainAgentQueues() {
|
|
4239
|
+
if (isThisProcessDesktopHost()) return true;
|
|
4240
|
+
return !isDesktopHostAlive();
|
|
4241
|
+
}
|
|
4242
|
+
var import_node_fs12, import_node_path13;
|
|
4243
|
+
var init_desktop_host = __esm({
|
|
4244
|
+
"src/store/desktop-host.ts"() {
|
|
4245
|
+
"use strict";
|
|
4246
|
+
import_node_fs12 = require("fs");
|
|
4247
|
+
import_node_path13 = require("path");
|
|
4248
|
+
init_paths();
|
|
4249
|
+
}
|
|
4250
|
+
});
|
|
4251
|
+
|
|
4252
|
+
// src/brightsy/cloud-connect-constants.ts
|
|
4253
|
+
function parseForceStopMessage(message) {
|
|
4254
|
+
const normalized = message.replace(/\r\n/g, "\n");
|
|
4255
|
+
const nl = normalized.indexOf("\n");
|
|
4256
|
+
const firstLine = (nl === -1 ? normalized : normalized.slice(0, nl)).trim();
|
|
4257
|
+
const remainder = (nl === -1 ? "" : normalized.slice(nl + 1)).trim();
|
|
4258
|
+
const forceStop = firstLine.toLowerCase() === SIDEBOARD_FORCE_STOP.toLowerCase();
|
|
4259
|
+
return { forceStop, remainder: forceStop ? remainder : message };
|
|
4260
|
+
}
|
|
4261
|
+
var CLOUD_ORCHESTRATOR_GOAL, SIDEBOARD_FORCE_STOP, CLOUD_COORDINATOR_BUSY_REPLY, CLOUD_COORDINATOR_STOPPED_REPLY, CLOUD_COORDINATOR_TIMEOUT_REPLY;
|
|
4262
|
+
var init_cloud_connect_constants = __esm({
|
|
4263
|
+
"src/brightsy/cloud-connect-constants.ts"() {
|
|
4264
|
+
"use strict";
|
|
4265
|
+
CLOUD_ORCHESTRATOR_GOAL = "Cloud-connected Sideboard orchestrator";
|
|
4266
|
+
SIDEBOARD_FORCE_STOP = "SIDEBOARD_FORCE_STOP";
|
|
4267
|
+
CLOUD_COORDINATOR_BUSY_REPLY = [
|
|
4268
|
+
"Sideboard is busy with an in-progress client/desktop tool turn on the global coordinator.",
|
|
4269
|
+
"I did not start a new orchestration turn.",
|
|
4270
|
+
`To force-stop the in-progress turn, send another desktop request whose first line is exactly ${SIDEBOARD_FORCE_STOP}`,
|
|
4271
|
+
"(optional follow-up request on later lines).",
|
|
4272
|
+
"What do you want me to do? (retry later, wait until idle, force-stop, rephrase, or cancel)"
|
|
4273
|
+
].join(" ");
|
|
4274
|
+
CLOUD_COORDINATOR_STOPPED_REPLY = [
|
|
4275
|
+
"Sideboard force-stopped the in-progress global coordinator turn.",
|
|
4276
|
+
"No new orchestration request was started."
|
|
4277
|
+
].join(" ");
|
|
4278
|
+
CLOUD_COORDINATOR_TIMEOUT_REPLY = [
|
|
4279
|
+
"Sideboard global coordinator timed out waiting for the local agent turn to finish.",
|
|
4280
|
+
"I did not produce a full orchestration result.",
|
|
4281
|
+
`To force-stop the in-progress turn, send another desktop request whose first line is exactly ${SIDEBOARD_FORCE_STOP}`,
|
|
4282
|
+
"(optional follow-up request on later lines).",
|
|
4283
|
+
"What do you want me to do? (retry later, wait, force-stop, rephrase, or cancel)"
|
|
4284
|
+
].join(" ");
|
|
4285
|
+
}
|
|
4286
|
+
});
|
|
4287
|
+
|
|
4288
|
+
// src/paths/workspace-scratch.ts
|
|
4289
|
+
function attachmentsGitignoreBody() {
|
|
4290
|
+
return ATTACHMENTS_GITIGNORE;
|
|
4291
|
+
}
|
|
4292
|
+
function isWorkspaceScratchPath(relativePath) {
|
|
4293
|
+
const p = relativePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
4294
|
+
return p === ATTACHMENTS_DIR || p.startsWith(`${ATTACHMENTS_DIR}/`) || p === CLI_DIR || p.startsWith(`${CLI_DIR}/`) || p === LEGACY_ATTACHMENTS_DIR || p.startsWith(`${LEGACY_ATTACHMENTS_DIR}/`) || p === ".sideboard" || p.startsWith(".sideboard/") || p === ".context" || p.startsWith(".context/");
|
|
4295
|
+
}
|
|
4296
|
+
var ATTACHMENTS_DIR, CLI_DIR, DETACHED_JOBS_DIR, LEGACY_ATTACHMENTS_DIR, LEGACY_DETACHED_JOBS_DIR, ATTACHMENTS_GITIGNORE;
|
|
4297
|
+
var init_workspace_scratch = __esm({
|
|
4298
|
+
"src/paths/workspace-scratch.ts"() {
|
|
4299
|
+
"use strict";
|
|
4300
|
+
ATTACHMENTS_DIR = ".context/attachments";
|
|
4301
|
+
CLI_DIR = ".context/cli";
|
|
4302
|
+
DETACHED_JOBS_DIR = ".context/.sideboard/detached-jobs";
|
|
4303
|
+
LEGACY_ATTACHMENTS_DIR = ".sideboard/attachments";
|
|
4304
|
+
LEGACY_DETACHED_JOBS_DIR = ".sideboard/detached-jobs";
|
|
4305
|
+
ATTACHMENTS_GITIGNORE = `# Sideboard / workspace attachments (local only)
|
|
4306
|
+
*
|
|
4307
|
+
!.gitignore
|
|
4308
|
+
`;
|
|
4309
|
+
}
|
|
4310
|
+
});
|
|
4311
|
+
|
|
4312
|
+
// src/composer/stage-files.ts
|
|
4313
|
+
function fileExtension(filePath) {
|
|
4314
|
+
const base = (0, import_node_path14.basename)(filePath).toLowerCase();
|
|
4315
|
+
return base.includes(".") ? base.split(".").pop() || "" : "";
|
|
4316
|
+
}
|
|
4317
|
+
function isImageFilePath(filePath) {
|
|
4318
|
+
return IMAGE_EXTENSIONS.has(fileExtension(filePath));
|
|
4319
|
+
}
|
|
4320
|
+
function imageMimeType(filePath) {
|
|
4321
|
+
return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
|
|
4322
|
+
}
|
|
4323
|
+
function ensureAttachmentsDir(worktreePath) {
|
|
4324
|
+
const dir = (0, import_node_path14.join)(worktreePath, ATTACHMENTS_DIR);
|
|
4325
|
+
(0, import_node_fs13.mkdirSync)(dir, { recursive: true });
|
|
4326
|
+
const gi = (0, import_node_path14.join)(dir, ".gitignore");
|
|
4327
|
+
if (!(0, import_node_fs13.existsSync)(gi)) {
|
|
4328
|
+
(0, import_node_fs13.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
|
|
4329
|
+
}
|
|
4330
|
+
return dir;
|
|
4331
|
+
}
|
|
4332
|
+
function uniqueAttachmentName(dir, originalName) {
|
|
4333
|
+
const safe = originalName.replace(/[/\\]/g, "_") || "file";
|
|
4334
|
+
if (!(0, import_node_fs13.existsSync)((0, import_node_path14.join)(dir, safe))) return safe;
|
|
4335
|
+
const ext = (0, import_node_path14.extname)(safe);
|
|
4336
|
+
const stem = ext ? safe.slice(0, -ext.length) : safe;
|
|
4337
|
+
for (let i = 1; i < 1e4; i++) {
|
|
4338
|
+
const candidate = `${stem}-${i}${ext}`;
|
|
4339
|
+
if (!(0, import_node_fs13.existsSync)((0, import_node_path14.join)(dir, candidate))) return candidate;
|
|
4340
|
+
}
|
|
4341
|
+
return `${stem}-${(0, import_node_crypto6.randomUUID)()}${ext}`;
|
|
4342
|
+
}
|
|
4343
|
+
function previewDataUrlFromBuf(filePath, buf) {
|
|
4344
|
+
if (!isImageFilePath(filePath)) return void 0;
|
|
4345
|
+
if (buf.length > MAX_PREVIEW_BYTES) return void 0;
|
|
4346
|
+
return `data:${imageMimeType(filePath)};base64,${buf.toString("base64")}`;
|
|
4347
|
+
}
|
|
4348
|
+
function attachmentFromBuffer(name, buf, opts) {
|
|
4349
|
+
const previewDataUrl = previewDataUrlFromBuf(name, buf);
|
|
4350
|
+
if (isImageFilePath(name)) {
|
|
4351
|
+
const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
|
|
4352
|
+
return {
|
|
4353
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4354
|
+
name,
|
|
4355
|
+
kind: "file",
|
|
4356
|
+
path: opts.path,
|
|
4357
|
+
previewDataUrl,
|
|
4358
|
+
content: [
|
|
4359
|
+
`Image attached: ${pathHint}`,
|
|
4360
|
+
opts.path ? `Use the Read tool on \`${opts.path}\` to view this image.` : "The image is shown in the composer; copy it into the worktree if you need to inspect pixels."
|
|
4361
|
+
].join("\n")
|
|
4362
|
+
};
|
|
4363
|
+
}
|
|
4364
|
+
if (buf.length > MAX_INLINE_BYTES) {
|
|
4365
|
+
return {
|
|
4366
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4367
|
+
name,
|
|
4368
|
+
kind: "file",
|
|
4369
|
+
path: opts.path,
|
|
4370
|
+
content: opts.path ? `(file too large to attach inline: \`${opts.path}\`, ${buf.length} bytes \u2014 use the Read tool)` : `(file too large to attach inline: ${opts.sourceLabel || name}, ${buf.length} bytes)`
|
|
4371
|
+
};
|
|
4372
|
+
}
|
|
4373
|
+
if (buf.includes(0)) {
|
|
4374
|
+
return {
|
|
4375
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4376
|
+
name,
|
|
4377
|
+
kind: "file",
|
|
4378
|
+
path: opts.path,
|
|
4379
|
+
content: opts.path ? `(binary file at \`${opts.path}\` \u2014 use tools to inspect)` : `(binary file attached by path only: ${opts.sourceLabel || name})`
|
|
4380
|
+
};
|
|
4381
|
+
}
|
|
4382
|
+
return {
|
|
4383
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4384
|
+
name,
|
|
4385
|
+
kind: "file",
|
|
4386
|
+
path: opts.path,
|
|
4387
|
+
content: buf.toString("utf8")
|
|
4388
|
+
};
|
|
4389
|
+
}
|
|
4390
|
+
function attachmentFromAbsolutePath(absolutePath) {
|
|
4391
|
+
const name = (0, import_node_path14.basename)(absolutePath);
|
|
4392
|
+
try {
|
|
4393
|
+
const st = (0, import_node_fs13.statSync)(absolutePath);
|
|
4394
|
+
if (!st.isFile()) {
|
|
4395
|
+
return {
|
|
4396
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4397
|
+
name,
|
|
4398
|
+
kind: "file",
|
|
4399
|
+
content: `(not a file: ${absolutePath})`
|
|
4400
|
+
};
|
|
4401
|
+
}
|
|
4402
|
+
const buf = (0, import_node_fs13.readFileSync)(absolutePath);
|
|
4403
|
+
return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
|
|
4404
|
+
} catch (err) {
|
|
4405
|
+
return {
|
|
4406
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4407
|
+
name,
|
|
4408
|
+
kind: "file",
|
|
4409
|
+
content: `(could not read ${absolutePath}: ${err instanceof Error ? err.message : String(err)})`
|
|
4410
|
+
};
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
4414
|
+
if (absolutePaths.length === 0) return [];
|
|
4415
|
+
const dir = ensureAttachmentsDir(worktreePath);
|
|
4510
4416
|
const out = [];
|
|
4511
|
-
for (const
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4417
|
+
for (const abs of absolutePaths) {
|
|
4418
|
+
const originalName = (0, import_node_path14.basename)(abs);
|
|
4419
|
+
try {
|
|
4420
|
+
const st = (0, import_node_fs13.statSync)(abs);
|
|
4421
|
+
if (!st.isFile()) continue;
|
|
4422
|
+
const name = uniqueAttachmentName(dir, originalName);
|
|
4423
|
+
const destAbs = (0, import_node_path14.join)(dir, name);
|
|
4424
|
+
(0, import_node_fs13.copyFileSync)(abs, destAbs);
|
|
4425
|
+
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
4426
|
+
const buf = (0, import_node_fs13.readFileSync)(destAbs);
|
|
4427
|
+
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
4428
|
+
} catch (err) {
|
|
4429
|
+
out.push({
|
|
4430
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4431
|
+
name: originalName,
|
|
4432
|
+
kind: "file",
|
|
4433
|
+
content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
|
|
4434
|
+
});
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
return out;
|
|
4438
|
+
}
|
|
4439
|
+
function stageBuffersAsAttachments(worktreePath, buffers2) {
|
|
4440
|
+
if (buffers2.length === 0) return [];
|
|
4441
|
+
const dir = ensureAttachmentsDir(worktreePath);
|
|
4442
|
+
const out = [];
|
|
4443
|
+
for (const item of buffers2) {
|
|
4444
|
+
const originalName = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
4445
|
+
try {
|
|
4446
|
+
const buf = Buffer.from(item.dataBase64, "base64");
|
|
4447
|
+
const name = uniqueAttachmentName(dir, originalName);
|
|
4448
|
+
const destAbs = (0, import_node_path14.join)(dir, name);
|
|
4449
|
+
(0, import_node_fs13.writeFileSync)(destAbs, buf);
|
|
4450
|
+
const rel = `${ATTACHMENTS_DIR}/${name}`;
|
|
4451
|
+
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
4452
|
+
} catch (err) {
|
|
4453
|
+
out.push({
|
|
4454
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4455
|
+
name: originalName,
|
|
4456
|
+
kind: "file",
|
|
4457
|
+
content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
|
|
4458
|
+
});
|
|
4515
4459
|
}
|
|
4516
|
-
out.push(p);
|
|
4517
4460
|
}
|
|
4518
|
-
return
|
|
4519
|
-
}
|
|
4520
|
-
function worktreeNameFromPath(worktreePath) {
|
|
4521
|
-
const normalized = worktreePath.replace(/\/+$/, "");
|
|
4522
|
-
const parts = normalized.split("/");
|
|
4523
|
-
return parts[parts.length - 1] || worktreePath;
|
|
4461
|
+
return out;
|
|
4524
4462
|
}
|
|
4525
|
-
function
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4463
|
+
function attachmentsFromBuffers(buffers2) {
|
|
4464
|
+
return buffers2.map((item) => {
|
|
4465
|
+
const name = (item.name || "file").replace(/[/\\]/g, "_") || "file";
|
|
4466
|
+
try {
|
|
4467
|
+
const buf = Buffer.from(item.dataBase64, "base64");
|
|
4468
|
+
return attachmentFromBuffer(name, buf, { sourceLabel: name });
|
|
4469
|
+
} catch (err) {
|
|
4470
|
+
return {
|
|
4471
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4472
|
+
name,
|
|
4473
|
+
kind: "file",
|
|
4474
|
+
content: `(could not attach ${name}: ${err instanceof Error ? err.message : String(err)})`
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4477
|
+
});
|
|
4536
4478
|
}
|
|
4537
|
-
function
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
if (
|
|
4541
|
-
|
|
4542
|
-
return `${ticket}-${team}`;
|
|
4479
|
+
function isWorktreeRelativePath(p) {
|
|
4480
|
+
if (!p || p.includes("..")) return false;
|
|
4481
|
+
if (p.startsWith("/")) return false;
|
|
4482
|
+
if (/^[A-Za-z]:[\\/]/.test(p)) return false;
|
|
4483
|
+
return true;
|
|
4543
4484
|
}
|
|
4544
|
-
function
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
const dir = worktreeNameFromPath(worktreePath).toLowerCase();
|
|
4549
|
-
return branch.toLowerCase() === dir;
|
|
4485
|
+
function dataUrlToBase64(url) {
|
|
4486
|
+
if (!url) return null;
|
|
4487
|
+
const m = /^data:[^;]+;base64,(.+)$/s.exec(url);
|
|
4488
|
+
return m?.[1] ?? null;
|
|
4550
4489
|
}
|
|
4551
|
-
function
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4490
|
+
function persistPendingFileAttachments(worktreePath, attachments) {
|
|
4491
|
+
if (attachments.length === 0) return attachments;
|
|
4492
|
+
const keep = [];
|
|
4493
|
+
const buffers2 = [];
|
|
4494
|
+
for (const att of attachments) {
|
|
4495
|
+
if (att.kind !== "file") {
|
|
4496
|
+
keep.push(att);
|
|
4497
|
+
continue;
|
|
4498
|
+
}
|
|
4499
|
+
if (att.path && isWorktreeRelativePath(att.path)) {
|
|
4500
|
+
keep.push(att);
|
|
4501
|
+
continue;
|
|
4502
|
+
}
|
|
4503
|
+
const fromPreview = dataUrlToBase64(att.previewDataUrl);
|
|
4504
|
+
if (fromPreview) {
|
|
4505
|
+
buffers2.push({ name: att.name, dataBase64: fromPreview });
|
|
4506
|
+
continue;
|
|
4507
|
+
}
|
|
4508
|
+
if (att.content && !IMAGE_HINT_RE.test(att.content) && !PLACEHOLDER_CONTENT_RE.test(att.content)) {
|
|
4509
|
+
buffers2.push({
|
|
4510
|
+
name: att.name,
|
|
4511
|
+
dataBase64: Buffer.from(att.content, "utf8").toString("base64")
|
|
4512
|
+
});
|
|
4513
|
+
continue;
|
|
4514
|
+
}
|
|
4515
|
+
keep.push(att);
|
|
4556
4516
|
}
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
function threadDisplayLabel(thread) {
|
|
4560
|
-
if (thread.userSetTitle && thread.title?.trim()) return thread.title.trim();
|
|
4561
|
-
if (thread.prTitle?.trim()) return thread.prTitle.trim();
|
|
4562
|
-
return branchDisplayLabel(thread.branchName, thread.worktreePath);
|
|
4563
|
-
}
|
|
4564
|
-
function worktreeDisplayLabel(thread) {
|
|
4565
|
-
return threadDisplayLabel(thread);
|
|
4517
|
+
if (buffers2.length === 0) return attachments;
|
|
4518
|
+
return [...keep, ...stageBuffersAsAttachments(worktreePath, buffers2)];
|
|
4566
4519
|
}
|
|
4567
|
-
function
|
|
4568
|
-
|
|
4569
|
-
const
|
|
4570
|
-
|
|
4520
|
+
function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
4521
|
+
const out = [];
|
|
4522
|
+
for (const rel of relativePaths) {
|
|
4523
|
+
if (!rel || rel.includes("..") || rel.startsWith("/")) {
|
|
4524
|
+
out.push({
|
|
4525
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4526
|
+
name: (0, import_node_path14.basename)(rel) || "file",
|
|
4527
|
+
kind: "file",
|
|
4528
|
+
content: `(invalid path: ${rel})`
|
|
4529
|
+
});
|
|
4530
|
+
continue;
|
|
4531
|
+
}
|
|
4532
|
+
const name = (0, import_node_path14.basename)(rel);
|
|
4533
|
+
try {
|
|
4534
|
+
const abs = (0, import_node_path14.join)(worktreePath, rel);
|
|
4535
|
+
const st = (0, import_node_fs13.statSync)(abs);
|
|
4536
|
+
if (!st.isFile()) continue;
|
|
4537
|
+
const buf = (0, import_node_fs13.readFileSync)(abs);
|
|
4538
|
+
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
4539
|
+
} catch (err) {
|
|
4540
|
+
out.push({
|
|
4541
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
4542
|
+
name,
|
|
4543
|
+
kind: "file",
|
|
4544
|
+
content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
|
|
4545
|
+
});
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
return out;
|
|
4571
4549
|
}
|
|
4572
|
-
var
|
|
4573
|
-
|
|
4550
|
+
var import_node_fs13, import_node_path14, import_node_crypto6, IMAGE_EXTENSIONS, IMAGE_MIME_BY_EXT, MAX_INLINE_BYTES, MAX_PREVIEW_BYTES, IMAGE_HINT_RE, PLACEHOLDER_CONTENT_RE;
|
|
4551
|
+
var init_stage_files = __esm({
|
|
4552
|
+
"src/composer/stage-files.ts"() {
|
|
4574
4553
|
"use strict";
|
|
4575
|
-
|
|
4554
|
+
import_node_fs13 = require("fs");
|
|
4555
|
+
import_node_path14 = require("path");
|
|
4556
|
+
import_node_crypto6 = require("crypto");
|
|
4557
|
+
init_workspace_scratch();
|
|
4558
|
+
IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4559
|
+
"png",
|
|
4560
|
+
"jpg",
|
|
4561
|
+
"jpeg",
|
|
4562
|
+
"gif",
|
|
4563
|
+
"webp",
|
|
4564
|
+
"svg",
|
|
4565
|
+
"bmp",
|
|
4566
|
+
"ico"
|
|
4567
|
+
]);
|
|
4568
|
+
IMAGE_MIME_BY_EXT = {
|
|
4569
|
+
png: "image/png",
|
|
4570
|
+
jpg: "image/jpeg",
|
|
4571
|
+
jpeg: "image/jpeg",
|
|
4572
|
+
gif: "image/gif",
|
|
4573
|
+
webp: "image/webp",
|
|
4574
|
+
svg: "image/svg+xml",
|
|
4575
|
+
bmp: "image/bmp",
|
|
4576
|
+
ico: "image/x-icon"
|
|
4577
|
+
};
|
|
4578
|
+
MAX_INLINE_BYTES = 4e5;
|
|
4579
|
+
MAX_PREVIEW_BYTES = 5e6;
|
|
4580
|
+
IMAGE_HINT_RE = /^Image attached:/;
|
|
4581
|
+
PLACEHOLDER_CONTENT_RE = /^\((could not |file too large|binary file|not a file|invalid path)/;
|
|
4576
4582
|
}
|
|
4577
4583
|
});
|
|
4578
4584
|
|
|
@@ -7282,7 +7288,7 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
7282
7288
|
const bodyText = clampGithubPrBody(opts.body ?? opts.title);
|
|
7283
7289
|
const bodyFile = (0, import_node_path20.join)(
|
|
7284
7290
|
(0, import_node_os6.tmpdir)(),
|
|
7285
|
-
`sideboard-pr-body-${process.pid}-${(0,
|
|
7291
|
+
`sideboard-pr-body-${process.pid}-${(0, import_node_crypto7.randomBytes)(6).toString("hex")}.md`
|
|
7286
7292
|
);
|
|
7287
7293
|
(0, import_node_fs18.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
|
|
7288
7294
|
`, "utf8");
|
|
@@ -7423,11 +7429,11 @@ function allocateTeamSlug(repoPath) {
|
|
|
7423
7429
|
}
|
|
7424
7430
|
throw new Error("No available soccer team worktree directories left");
|
|
7425
7431
|
}
|
|
7426
|
-
var
|
|
7432
|
+
var import_node_crypto7, import_node_fs18, import_node_os6, import_node_path20, PR_DETAILS_JSON_FIELDS;
|
|
7427
7433
|
var init_worktree = __esm({
|
|
7428
7434
|
"src/git/worktree.ts"() {
|
|
7429
7435
|
"use strict";
|
|
7430
|
-
|
|
7436
|
+
import_node_crypto7 = require("crypto");
|
|
7431
7437
|
import_node_fs18 = require("fs");
|
|
7432
7438
|
import_node_os6 = require("os");
|
|
7433
7439
|
import_node_path20 = require("path");
|
|
@@ -12161,6 +12167,9 @@ function toCodexDisableUserMcpArgs(names) {
|
|
|
12161
12167
|
}
|
|
12162
12168
|
return args;
|
|
12163
12169
|
}
|
|
12170
|
+
function toCodexUnattendedAppsArgs() {
|
|
12171
|
+
return ["-c", "features.apps=false", "-c", "apps._default.enabled=false"];
|
|
12172
|
+
}
|
|
12164
12173
|
var import_node_fs32, import_node_os11, import_node_path33, import_smol_toml2;
|
|
12165
12174
|
var init_orch_mcp_isolation = __esm({
|
|
12166
12175
|
"src/agents/orch-mcp-isolation.ts"() {
|
|
@@ -12356,6 +12365,7 @@ var init_codex = __esm({
|
|
|
12356
12365
|
orchestratorThreadId: isOrchestrator ? thread.id : null
|
|
12357
12366
|
});
|
|
12358
12367
|
const mcpOverrides = [
|
|
12368
|
+
...toCodexUnattendedAppsArgs(),
|
|
12359
12369
|
...toCodexMcpConfigArgs(injected2),
|
|
12360
12370
|
...isOrchestrator ? toCodexDisableUserMcpArgs(
|
|
12361
12371
|
userMcpNamesToDisable({
|
|
@@ -14793,6 +14803,59 @@ var init_context_compact = __esm({
|
|
|
14793
14803
|
}
|
|
14794
14804
|
});
|
|
14795
14805
|
|
|
14806
|
+
// src/threads/worktree-runtime.ts
|
|
14807
|
+
function worktreeProcessPrefix(worktreePath) {
|
|
14808
|
+
return `wt:${normalizeWorktreePath(worktreePath)}`;
|
|
14809
|
+
}
|
|
14810
|
+
function worktreeSetupProcessKey(worktreePath) {
|
|
14811
|
+
return `${worktreeProcessPrefix(worktreePath)}:setup`;
|
|
14812
|
+
}
|
|
14813
|
+
function worktreeRunProcessKey(worktreePath, scriptName) {
|
|
14814
|
+
return `${worktreeProcessPrefix(worktreePath)}:run:${scriptName}`;
|
|
14815
|
+
}
|
|
14816
|
+
function worktreeDevProcessKey(worktreePath) {
|
|
14817
|
+
return `${worktreeProcessPrefix(worktreePath)}:dev`;
|
|
14818
|
+
}
|
|
14819
|
+
function isWorktreeRunProcessKey(key, worktreePath) {
|
|
14820
|
+
const prefix = worktreeProcessPrefix(worktreePath);
|
|
14821
|
+
return key.startsWith(`${prefix}:run:`) || key === `${prefix}:dev`;
|
|
14822
|
+
}
|
|
14823
|
+
function pickRichestSetupLog(snaps) {
|
|
14824
|
+
let best;
|
|
14825
|
+
for (const snap of snaps) {
|
|
14826
|
+
if (!snap.output && !snap.running && snap.exitCode == null) continue;
|
|
14827
|
+
if (!best) {
|
|
14828
|
+
best = snap;
|
|
14829
|
+
continue;
|
|
14830
|
+
}
|
|
14831
|
+
if (snap.running && !best.running) {
|
|
14832
|
+
best = snap;
|
|
14833
|
+
continue;
|
|
14834
|
+
}
|
|
14835
|
+
if (snap.running === best.running && snap.output.length > best.output.length) {
|
|
14836
|
+
best = snap;
|
|
14837
|
+
}
|
|
14838
|
+
}
|
|
14839
|
+
return best;
|
|
14840
|
+
}
|
|
14841
|
+
function mergeWorktreeActiveRuns(threads) {
|
|
14842
|
+
const byName = /* @__PURE__ */ new Map();
|
|
14843
|
+
let devPort = null;
|
|
14844
|
+
for (const thread of threads) {
|
|
14845
|
+
if (thread.devPort != null && devPort == null) devPort = thread.devPort;
|
|
14846
|
+
for (const run2 of thread.activeRuns ?? []) {
|
|
14847
|
+
if (!byName.has(run2.scriptName)) byName.set(run2.scriptName, run2);
|
|
14848
|
+
}
|
|
14849
|
+
}
|
|
14850
|
+
return { activeRuns: [...byName.values()], devPort };
|
|
14851
|
+
}
|
|
14852
|
+
var init_worktree_runtime = __esm({
|
|
14853
|
+
"src/threads/worktree-runtime.ts"() {
|
|
14854
|
+
"use strict";
|
|
14855
|
+
init_worktree_labels();
|
|
14856
|
+
}
|
|
14857
|
+
});
|
|
14858
|
+
|
|
14796
14859
|
// src/threads/chat-tabs.ts
|
|
14797
14860
|
function sameWorktreePath(a, b) {
|
|
14798
14861
|
return normalizeWorktreePath(a) === normalizeWorktreePath(b);
|
|
@@ -14855,7 +14918,7 @@ function forkMessageSlice(from, throughIndex) {
|
|
|
14855
14918
|
function buildForkTranscriptAttachment(baseTitle, messages) {
|
|
14856
14919
|
const title = baseTitle || "Chat";
|
|
14857
14920
|
return {
|
|
14858
|
-
id: (0,
|
|
14921
|
+
id: (0, import_node_crypto8.randomUUID)(),
|
|
14859
14922
|
name: `Transcript of ${title}.md`,
|
|
14860
14923
|
kind: "transcript",
|
|
14861
14924
|
content: formatTranscriptMarkdown(title, messages)
|
|
@@ -14889,7 +14952,8 @@ function createChatTab(input) {
|
|
|
14889
14952
|
planMode: from.planMode,
|
|
14890
14953
|
autonomy: input.autonomy ?? from.autonomy,
|
|
14891
14954
|
attachments: input.attachments ?? [],
|
|
14892
|
-
status: "idle"
|
|
14955
|
+
status: "idle",
|
|
14956
|
+
...mergeWorktreeActiveRuns(threadsSharingWorktree(binding.worktreePath))
|
|
14893
14957
|
});
|
|
14894
14958
|
writeThread(thread);
|
|
14895
14959
|
return thread;
|
|
@@ -14912,17 +14976,18 @@ function forkChatTab(input) {
|
|
|
14912
14976
|
}
|
|
14913
14977
|
return tab;
|
|
14914
14978
|
}
|
|
14915
|
-
var
|
|
14979
|
+
var import_node_crypto8;
|
|
14916
14980
|
var init_chat_tabs = __esm({
|
|
14917
14981
|
"src/threads/chat-tabs.ts"() {
|
|
14918
14982
|
"use strict";
|
|
14919
|
-
|
|
14983
|
+
import_node_crypto8 = require("crypto");
|
|
14920
14984
|
init_context_compact();
|
|
14921
14985
|
init_teams();
|
|
14922
14986
|
init_worktree_labels();
|
|
14923
14987
|
init_global_workspace();
|
|
14924
14988
|
init_orchestrator_capable();
|
|
14925
14989
|
init_thread_store();
|
|
14990
|
+
init_worktree_runtime();
|
|
14926
14991
|
init_worktree_labels();
|
|
14927
14992
|
}
|
|
14928
14993
|
});
|
|
@@ -15182,7 +15247,7 @@ function buildReviewRequestAttachment(content, opts) {
|
|
|
15182
15247
|
const path2 = opts?.path ?? REVIEW_SKILL_PATH;
|
|
15183
15248
|
const name = opts?.name ?? (path2 === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path2 === REPO_REVIEW_PATH || path2 === CONTEXT_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
|
|
15184
15249
|
return {
|
|
15185
|
-
id: (0,
|
|
15250
|
+
id: (0, import_node_crypto9.randomUUID)(),
|
|
15186
15251
|
name,
|
|
15187
15252
|
kind: "file",
|
|
15188
15253
|
path: path2,
|
|
@@ -15217,11 +15282,11 @@ async function requestReview(threadRef, send2) {
|
|
|
15217
15282
|
const started = await send2(tab.id, REVIEW_REQUEST_PREFILL);
|
|
15218
15283
|
return { tab: started, from };
|
|
15219
15284
|
}
|
|
15220
|
-
var
|
|
15285
|
+
var import_node_crypto9, import_node_fs38, import_node_path38, REPO_REVIEW_PATH, REPO_REVIEW_NAME, CONTEXT_REVIEW_PATH, REVIEW_REQUEST_PATH, LEGACY_REVIEW_REQUEST_PATH, REVIEW_REQUEST_NAME, REVIEW_REQUEST_PREFILL, LEGACY_REVIEW_TEMPLATE_MARKERS;
|
|
15221
15286
|
var init_request_review = __esm({
|
|
15222
15287
|
"src/review/request-review.ts"() {
|
|
15223
15288
|
"use strict";
|
|
15224
|
-
|
|
15289
|
+
import_node_crypto9 = require("crypto");
|
|
15225
15290
|
import_node_fs38 = require("fs");
|
|
15226
15291
|
import_node_path38 = require("path");
|
|
15227
15292
|
init_global_workspace();
|
|
@@ -18092,7 +18157,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
|
18092
18157
|
`- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
|
|
18093
18158
|
].join("\n");
|
|
18094
18159
|
return {
|
|
18095
|
-
id: (0,
|
|
18160
|
+
id: (0, import_node_crypto10.randomUUID)(),
|
|
18096
18161
|
name: "Orchestration quota handoff.md",
|
|
18097
18162
|
kind: "transcript",
|
|
18098
18163
|
content: body
|
|
@@ -18113,11 +18178,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
18113
18178
|
sourceType: "orchestration"
|
|
18114
18179
|
});
|
|
18115
18180
|
}
|
|
18116
|
-
var
|
|
18181
|
+
var import_node_crypto10, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
|
|
18117
18182
|
var init_quota_failover = __esm({
|
|
18118
18183
|
"src/orchestrator/quota-failover.ts"() {
|
|
18119
18184
|
"use strict";
|
|
18120
|
-
|
|
18185
|
+
import_node_crypto10 = require("crypto");
|
|
18121
18186
|
init_session_quota();
|
|
18122
18187
|
init_app_settings();
|
|
18123
18188
|
init_global_workspace();
|
|
@@ -21039,6 +21104,7 @@ var init_orchestrator = __esm({
|
|
|
21039
21104
|
init_setup_last_error();
|
|
21040
21105
|
init_request_review();
|
|
21041
21106
|
init_fork_worktree();
|
|
21107
|
+
init_worktree_runtime();
|
|
21042
21108
|
init_quota_failover();
|
|
21043
21109
|
init_adopt();
|
|
21044
21110
|
init_stack_layers();
|
|
@@ -22204,22 +22270,25 @@ var init_orchestrator = __esm({
|
|
|
22204
22270
|
"No run script found for this worktree. Add [scripts.run.*] in the worktree\u2019s .sideboard/settings.toml or .conductor/settings.toml (or settings.local.toml on the main repo)."
|
|
22205
22271
|
);
|
|
22206
22272
|
}
|
|
22207
|
-
const
|
|
22208
|
-
const
|
|
22209
|
-
const
|
|
22273
|
+
const siblings = threadsSharingWorktree(thread.worktreePath);
|
|
22274
|
+
const shared = mergeWorktreeActiveRuns(siblings);
|
|
22275
|
+
const runKey = worktreeRunProcessKey(thread.worktreePath, resolvedName);
|
|
22276
|
+
const existing = this.processes.get(runKey) ?? siblings.map((t) => this.processes.get(`${t.id}:run:${resolvedName}`)).find(Boolean);
|
|
22277
|
+
const active = shared.activeRuns.find((r) => r.scriptName === resolvedName);
|
|
22210
22278
|
if (existing && active) {
|
|
22211
22279
|
return { port: active.port, scriptName: resolvedName, ports: active.ports };
|
|
22212
22280
|
}
|
|
22213
22281
|
if (!scriptName) {
|
|
22214
|
-
const legacy = this.processes.get(`${
|
|
22215
|
-
if (legacy &&
|
|
22216
|
-
return { port:
|
|
22282
|
+
const legacy = this.processes.get(worktreeDevProcessKey(thread.worktreePath)) ?? siblings.map((t) => this.processes.get(`${t.id}:dev`)).find(Boolean);
|
|
22283
|
+
if (legacy && shared.devPort) {
|
|
22284
|
+
return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
|
|
22217
22285
|
}
|
|
22218
22286
|
}
|
|
22219
22287
|
const mode = getRunMode(thread.worktreePath, thread.repoPath);
|
|
22220
22288
|
if (mode === "nonconcurrent") {
|
|
22289
|
+
const here = normalizeWorktreePath(thread.worktreePath);
|
|
22221
22290
|
for (const t of listThreads()) {
|
|
22222
|
-
if (t.
|
|
22291
|
+
if (normalizeWorktreePath(t.worktreePath) === here) continue;
|
|
22223
22292
|
const runs = t.activeRuns ?? [];
|
|
22224
22293
|
if (runs.length > 0 || t.devPort) {
|
|
22225
22294
|
throw new Error(
|
|
@@ -22254,7 +22323,7 @@ var init_orchestrator = __esm({
|
|
|
22254
22323
|
});
|
|
22255
22324
|
const isDefault = scripts.find((s) => s.default)?.name === resolvedName || !scripts.some((s) => s.default) && (resolvedName === "dev" || resolvedName === scripts[0]?.name);
|
|
22256
22325
|
if (isDefault) {
|
|
22257
|
-
this.processes.set(
|
|
22326
|
+
this.processes.set(worktreeDevProcessKey(thread.worktreePath), {
|
|
22258
22327
|
kind: "dev",
|
|
22259
22328
|
pid: handle.pid,
|
|
22260
22329
|
startedAt,
|
|
@@ -22269,13 +22338,14 @@ var init_orchestrator = __esm({
|
|
|
22269
22338
|
startedAt
|
|
22270
22339
|
};
|
|
22271
22340
|
const nextRuns = [
|
|
22272
|
-
...
|
|
22341
|
+
...shared.activeRuns.filter((r) => r.scriptName !== resolvedName),
|
|
22273
22342
|
run2
|
|
22274
22343
|
];
|
|
22275
|
-
|
|
22276
|
-
|
|
22277
|
-
|
|
22278
|
-
|
|
22344
|
+
this.syncWorktreeRuns(
|
|
22345
|
+
thread.worktreePath,
|
|
22346
|
+
nextRuns,
|
|
22347
|
+
isDefault ? handle.port : shared.devPort
|
|
22348
|
+
);
|
|
22279
22349
|
this.emit({
|
|
22280
22350
|
type: "dev_server_started",
|
|
22281
22351
|
threadId: thread.id,
|
|
@@ -22284,15 +22354,14 @@ var init_orchestrator = __esm({
|
|
|
22284
22354
|
});
|
|
22285
22355
|
void handle.done.then(() => {
|
|
22286
22356
|
this.processes.delete(runKey);
|
|
22287
|
-
if (isDefault) this.processes.delete(
|
|
22288
|
-
const latest =
|
|
22289
|
-
const remaining =
|
|
22290
|
-
|
|
22357
|
+
if (isDefault) this.processes.delete(worktreeDevProcessKey(thread.worktreePath));
|
|
22358
|
+
const latest = mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath));
|
|
22359
|
+
const remaining = latest.activeRuns.filter((r) => r.scriptName !== resolvedName);
|
|
22360
|
+
this.syncWorktreeRuns(
|
|
22361
|
+
thread.worktreePath,
|
|
22362
|
+
remaining,
|
|
22363
|
+
isDefault ? null : latest.devPort
|
|
22291
22364
|
);
|
|
22292
|
-
updateThread(thread.id, {
|
|
22293
|
-
activeRuns: remaining,
|
|
22294
|
-
devPort: isDefault ? null : latest?.devPort ?? null
|
|
22295
|
-
});
|
|
22296
22365
|
this.emit({
|
|
22297
22366
|
type: "dev_server_stopped",
|
|
22298
22367
|
threadId: thread.id,
|
|
@@ -22303,51 +22372,82 @@ var init_orchestrator = __esm({
|
|
|
22303
22372
|
}
|
|
22304
22373
|
stopDev(threadRef, scriptName) {
|
|
22305
22374
|
const thread = this.requireThread(threadRef);
|
|
22375
|
+
const siblings = threadsSharingWorktree(thread.worktreePath);
|
|
22376
|
+
const shared = mergeWorktreeActiveRuns(siblings);
|
|
22306
22377
|
if (scriptName) {
|
|
22307
|
-
const
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
|
|
22311
|
-
const
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
|
|
22378
|
+
const keys = [
|
|
22379
|
+
worktreeRunProcessKey(thread.worktreePath, scriptName),
|
|
22380
|
+
...siblings.map((t) => `${t.id}:run:${scriptName}`)
|
|
22381
|
+
];
|
|
22382
|
+
for (const key of keys) {
|
|
22383
|
+
const proc = this.processes.get(key);
|
|
22384
|
+
if (proc) proc.kill();
|
|
22385
|
+
this.processes.delete(key);
|
|
22386
|
+
}
|
|
22387
|
+
const remaining = shared.activeRuns.filter((r) => r.scriptName !== scriptName);
|
|
22388
|
+
const isPrimary = shared.devPort != null && shared.activeRuns.find((r) => r.scriptName === scriptName)?.port === shared.devPort;
|
|
22389
|
+
this.syncWorktreeRuns(
|
|
22390
|
+
thread.worktreePath,
|
|
22391
|
+
remaining,
|
|
22392
|
+
isPrimary ? null : shared.devPort
|
|
22393
|
+
);
|
|
22317
22394
|
this.emit({ type: "dev_server_stopped", threadId: thread.id, scriptName });
|
|
22318
22395
|
return;
|
|
22319
22396
|
}
|
|
22320
22397
|
for (const [key, proc] of [...this.processes.entries()]) {
|
|
22321
|
-
if (key.startsWith(`${
|
|
22398
|
+
if (isWorktreeRunProcessKey(key, thread.worktreePath) || siblings.some((t) => key.startsWith(`${t.id}:run:`) || key === `${t.id}:dev`)) {
|
|
22322
22399
|
proc.kill();
|
|
22323
22400
|
this.processes.delete(key);
|
|
22324
22401
|
}
|
|
22325
22402
|
}
|
|
22326
|
-
|
|
22403
|
+
this.syncWorktreeRuns(thread.worktreePath, [], null);
|
|
22327
22404
|
this.emit({ type: "dev_server_stopped", threadId: thread.id });
|
|
22328
22405
|
}
|
|
22406
|
+
syncWorktreeRuns(worktreePath, activeRuns, devPort) {
|
|
22407
|
+
for (const sibling of threadsSharingWorktree(worktreePath)) {
|
|
22408
|
+
updateThread(sibling.id, { activeRuns, devPort });
|
|
22409
|
+
}
|
|
22410
|
+
}
|
|
22329
22411
|
listThreadRunScripts(threadRef) {
|
|
22330
22412
|
const thread = this.requireThread(threadRef);
|
|
22331
22413
|
return listRunScripts(thread.worktreePath, thread.repoPath);
|
|
22332
22414
|
}
|
|
22333
22415
|
getActiveRuns(threadRef) {
|
|
22334
|
-
|
|
22416
|
+
const thread = this.requireThread(threadRef);
|
|
22417
|
+
return mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath)).activeRuns;
|
|
22335
22418
|
}
|
|
22336
22419
|
getSetupLog(threadRef) {
|
|
22337
22420
|
const thread = this.getThread(threadRef);
|
|
22338
22421
|
if (!thread) return readSetupLog(threadRef);
|
|
22339
|
-
const
|
|
22340
|
-
|
|
22422
|
+
const siblingIds = [
|
|
22423
|
+
thread.id,
|
|
22424
|
+
...threadsSharingWorktree(thread.worktreePath).map((t) => t.id)
|
|
22425
|
+
];
|
|
22426
|
+
const snap = pickRichestSetupLog([
|
|
22427
|
+
readSetupLog(setupLogKeyForWorktree(thread.worktreePath)),
|
|
22428
|
+
...[...new Set(siblingIds)].map((id) => readSetupLog(id))
|
|
22429
|
+
]) ?? readSetupLog(thread.id);
|
|
22430
|
+
if (snap.running && !this.hasWorktreeSetupProcess(thread.worktreePath)) {
|
|
22341
22431
|
return { ...snap, running: false };
|
|
22342
22432
|
}
|
|
22343
22433
|
return snap;
|
|
22344
22434
|
}
|
|
22435
|
+
hasWorktreeSetupProcess(worktreePath) {
|
|
22436
|
+
if (this.processes.has(worktreeSetupProcessKey(worktreePath))) return true;
|
|
22437
|
+
return threadsSharingWorktree(worktreePath).some(
|
|
22438
|
+
(t) => this.processes.has(`${t.id}:setup`)
|
|
22439
|
+
);
|
|
22440
|
+
}
|
|
22441
|
+
writeSetupLog(threadId, worktreePath, fn) {
|
|
22442
|
+
fn(setupLogKeyForWorktree(worktreePath));
|
|
22443
|
+
fn(threadId);
|
|
22444
|
+
}
|
|
22345
22445
|
async runSetup(threadRef, opts) {
|
|
22346
22446
|
const thread = this.requireThread(threadRef);
|
|
22347
22447
|
this.assertNotGlobal(thread, "Setup");
|
|
22348
|
-
const key =
|
|
22349
|
-
if (this.
|
|
22350
|
-
throw new Error("Setup already running for this
|
|
22448
|
+
const key = worktreeSetupProcessKey(thread.worktreePath);
|
|
22449
|
+
if (this.hasWorktreeSetupProcess(thread.worktreePath)) {
|
|
22450
|
+
throw new Error("Setup already running for this worktree");
|
|
22351
22451
|
}
|
|
22352
22452
|
const abort = new AbortController();
|
|
22353
22453
|
this.processes.set(key, {
|
|
@@ -22355,13 +22455,17 @@ var init_orchestrator = __esm({
|
|
|
22355
22455
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22356
22456
|
kill: () => abort.abort()
|
|
22357
22457
|
});
|
|
22358
|
-
|
|
22458
|
+
this.writeSetupLog(thread.id, thread.worktreePath, beginSetupLog);
|
|
22359
22459
|
this.emit({ type: "setup_started", threadId: thread.id });
|
|
22360
22460
|
let finished = false;
|
|
22361
22461
|
const finish = (exitCode, source) => {
|
|
22362
22462
|
if (finished) return;
|
|
22363
22463
|
finished = true;
|
|
22364
|
-
|
|
22464
|
+
this.writeSetupLog(
|
|
22465
|
+
thread.id,
|
|
22466
|
+
thread.worktreePath,
|
|
22467
|
+
(logKey) => finishSetupLog(logKey, exitCode, source)
|
|
22468
|
+
);
|
|
22365
22469
|
this.emit({ type: "setup_finished", threadId: thread.id, exitCode });
|
|
22366
22470
|
};
|
|
22367
22471
|
try {
|
|
@@ -22369,14 +22473,22 @@ var init_orchestrator = __esm({
|
|
|
22369
22473
|
thread.repoPath,
|
|
22370
22474
|
thread.worktreePath,
|
|
22371
22475
|
(line) => {
|
|
22372
|
-
|
|
22476
|
+
this.writeSetupLog(
|
|
22477
|
+
thread.id,
|
|
22478
|
+
thread.worktreePath,
|
|
22479
|
+
(logKey) => appendSetupLog(logKey, line)
|
|
22480
|
+
);
|
|
22373
22481
|
this.emit({ type: "setup_output", threadId: thread.id, line });
|
|
22374
22482
|
},
|
|
22375
22483
|
{ signal: abort.signal }
|
|
22376
22484
|
);
|
|
22377
22485
|
if (!setup.ran) {
|
|
22378
22486
|
const line = "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)";
|
|
22379
|
-
|
|
22487
|
+
this.writeSetupLog(
|
|
22488
|
+
thread.id,
|
|
22489
|
+
thread.worktreePath,
|
|
22490
|
+
(logKey) => appendSetupLog(logKey, line)
|
|
22491
|
+
);
|
|
22380
22492
|
this.emit({ type: "setup_output", threadId: thread.id, line });
|
|
22381
22493
|
finish(null, null);
|
|
22382
22494
|
throw new Error(line);
|
|
@@ -22404,8 +22516,14 @@ var init_orchestrator = __esm({
|
|
|
22404
22516
|
}
|
|
22405
22517
|
cancelSetup(threadRef) {
|
|
22406
22518
|
const thread = this.requireThread(threadRef);
|
|
22407
|
-
const
|
|
22408
|
-
|
|
22519
|
+
const keys = [
|
|
22520
|
+
worktreeSetupProcessKey(thread.worktreePath),
|
|
22521
|
+
...threadsSharingWorktree(thread.worktreePath).map((t) => `${t.id}:setup`)
|
|
22522
|
+
];
|
|
22523
|
+
for (const key of [...new Set(keys)]) {
|
|
22524
|
+
const proc = this.processes.get(key);
|
|
22525
|
+
if (proc) proc.kill();
|
|
22526
|
+
}
|
|
22409
22527
|
}
|
|
22410
22528
|
async applyIntoMain(threadRef, opts) {
|
|
22411
22529
|
const thread = this.requireThread(threadRef);
|
|
@@ -24205,7 +24323,7 @@ function issueActivityKind(issue, sinceIso) {
|
|
|
24205
24323
|
}
|
|
24206
24324
|
|
|
24207
24325
|
// src/integrations/linear-oauth.ts
|
|
24208
|
-
var
|
|
24326
|
+
var import_node_crypto11 = require("crypto");
|
|
24209
24327
|
var import_node_http = require("http");
|
|
24210
24328
|
init_app_settings();
|
|
24211
24329
|
init_fetch();
|
|
@@ -24237,8 +24355,8 @@ function linearOAuthCredentials() {
|
|
|
24237
24355
|
return { clientId, clientSecret };
|
|
24238
24356
|
}
|
|
24239
24357
|
function createLinearPkce() {
|
|
24240
|
-
const verifier = (0,
|
|
24241
|
-
const challenge = (0,
|
|
24358
|
+
const verifier = (0, import_node_crypto11.randomBytes)(32).toString("base64url");
|
|
24359
|
+
const challenge = (0, import_node_crypto11.createHash)("sha256").update(verifier).digest("base64url");
|
|
24242
24360
|
return { verifier, challenge };
|
|
24243
24361
|
}
|
|
24244
24362
|
function linearOAuthAuthorizeUrl(clientId, state, codeChallenge) {
|
|
@@ -24369,7 +24487,7 @@ async function startLinearOAuth(opts) {
|
|
|
24369
24487
|
throw new LinearOAuthCancelledError();
|
|
24370
24488
|
}
|
|
24371
24489
|
const { clientId, clientSecret } = linearOAuthCredentials();
|
|
24372
|
-
const state = (0,
|
|
24490
|
+
const state = (0, import_node_crypto11.randomBytes)(16).toString("hex");
|
|
24373
24491
|
const pkce = createLinearPkce();
|
|
24374
24492
|
const authorizeUrl = linearOAuthAuthorizeUrl(clientId, state, pkce.challenge);
|
|
24375
24493
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
@@ -26222,7 +26340,7 @@ function buildPathRefAttachment(input) {
|
|
|
26222
26340
|
init_stage_files();
|
|
26223
26341
|
|
|
26224
26342
|
// src/composer/pasted-text.ts
|
|
26225
|
-
var
|
|
26343
|
+
var import_node_crypto12 = require("crypto");
|
|
26226
26344
|
var PASTE_ATTACH_MIN_CHARS = 1200;
|
|
26227
26345
|
var PASTE_ATTACH_MIN_LINES = 15;
|
|
26228
26346
|
var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
|
|
@@ -26249,7 +26367,7 @@ function nextPastedTextName(existing) {
|
|
|
26249
26367
|
}
|
|
26250
26368
|
function buildPastedTextAttachment(text7, opts) {
|
|
26251
26369
|
return {
|
|
26252
|
-
id: opts?.id ?? (0,
|
|
26370
|
+
id: opts?.id ?? (0, import_node_crypto12.randomUUID)(),
|
|
26253
26371
|
name: opts?.name ?? "Pasted text #1.txt",
|
|
26254
26372
|
kind: "file",
|
|
26255
26373
|
path: opts?.path,
|
|
@@ -26403,7 +26521,7 @@ init_orphan_cleanup();
|
|
|
26403
26521
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
26404
26522
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
26405
26523
|
var import_zod9 = require("zod");
|
|
26406
|
-
var
|
|
26524
|
+
var import_node_crypto14 = require("crypto");
|
|
26407
26525
|
var import_node_path52 = require("path");
|
|
26408
26526
|
init_orchestrator();
|
|
26409
26527
|
init_worktree();
|
|
@@ -28130,7 +28248,7 @@ init_worktree();
|
|
|
28130
28248
|
init_paths();
|
|
28131
28249
|
|
|
28132
28250
|
// src/board/board-pins.ts
|
|
28133
|
-
var
|
|
28251
|
+
var import_node_crypto13 = require("crypto");
|
|
28134
28252
|
var import_node_fs54 = require("fs");
|
|
28135
28253
|
var import_node_path50 = require("path");
|
|
28136
28254
|
init_paths();
|
|
@@ -28162,7 +28280,7 @@ function addBoardPin(input) {
|
|
|
28162
28280
|
const ref = input.ref.trim();
|
|
28163
28281
|
if (!ref) throw new Error("Board item needs a ref");
|
|
28164
28282
|
const pin = {
|
|
28165
|
-
id: (0,
|
|
28283
|
+
id: (0, import_node_crypto13.randomUUID)(),
|
|
28166
28284
|
kind: input.kind,
|
|
28167
28285
|
ref,
|
|
28168
28286
|
repoPath: input.repoPath.trim(),
|
|
@@ -28996,7 +29114,7 @@ async function startMcpServer() {
|
|
|
28996
29114
|
const ident = issue?.identifier ?? ref.trim();
|
|
28997
29115
|
const attachments = issue ? [
|
|
28998
29116
|
{
|
|
28999
|
-
id: (0,
|
|
29117
|
+
id: (0, import_node_crypto14.randomUUID)(),
|
|
29000
29118
|
name: issue.identifier,
|
|
29001
29119
|
kind: "issue",
|
|
29002
29120
|
content: [
|
|
@@ -29951,7 +30069,7 @@ init_orch_mcp_isolation();
|
|
|
29951
30069
|
init_workspaces2();
|
|
29952
30070
|
|
|
29953
30071
|
// src/slack/oauth.ts
|
|
29954
|
-
var
|
|
30072
|
+
var import_node_crypto15 = require("crypto");
|
|
29955
30073
|
init_app_settings();
|
|
29956
30074
|
init_workspaces2();
|
|
29957
30075
|
|
|
@@ -30175,7 +30293,7 @@ async function startSlackOAuth(opts) {
|
|
|
30175
30293
|
throw new SlackOAuthCancelledError();
|
|
30176
30294
|
}
|
|
30177
30295
|
const { clientId } = slackOAuthCredentials();
|
|
30178
|
-
const state = (0,
|
|
30296
|
+
const state = (0, import_node_crypto15.randomBytes)(16).toString("hex");
|
|
30179
30297
|
const authorizeUrl = slackOAuthAuthorizeUrl(clientId, state);
|
|
30180
30298
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
30181
30299
|
const pollIntervalMs = opts?.pollIntervalMs ?? 400;
|
|
@@ -30457,7 +30575,7 @@ init_workspaces2();
|
|
|
30457
30575
|
init_reply_target();
|
|
30458
30576
|
|
|
30459
30577
|
// src/slack/relay-protocol.ts
|
|
30460
|
-
var
|
|
30578
|
+
var import_node_crypto16 = require("crypto");
|
|
30461
30579
|
var SLACK_RELAY_PING_INTERVAL_MS = 2e4;
|
|
30462
30580
|
var SLACK_RELAY_PONG_TIMEOUT_MS = 15e3;
|
|
30463
30581
|
function slackRelaySessionKey(teamId, userId, deviceId) {
|
|
@@ -30467,7 +30585,7 @@ function slackRelayUserKey(teamId, userId) {
|
|
|
30467
30585
|
return `${teamId.trim()}:${userId.trim()}`;
|
|
30468
30586
|
}
|
|
30469
30587
|
function newSlackRelayEventId() {
|
|
30470
|
-
return (0,
|
|
30588
|
+
return (0, import_node_crypto16.randomBytes)(12).toString("hex");
|
|
30471
30589
|
}
|
|
30472
30590
|
function parseSlackRelayClientMessage(raw) {
|
|
30473
30591
|
try {
|