@sideboard-ai/core 0.1.194 → 0.1.195
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-VRPPFKAO.js → chunk-GFH3QVAV.js} +153 -48
- package/dist/{chunk-NY366OEZ.js → chunk-HAVI6AY3.js} +153 -48
- package/dist/index.cjs +1002 -888
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/mcp/run-stdio.cjs +171 -57
- package/dist/mcp/run-stdio.js +1 -1
- package/dist/{orchestrator-S3M2E7O6.js → orchestrator-6ESBGMDN.js} +1 -1
- package/dist/{orchestrator-A5KF2335.js → orchestrator-EHRFN3Q6.js} +1 -1
- 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);
|
|
4416
|
+
const out = [];
|
|
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);
|
|
4510
4442
|
const out = [];
|
|
4511
|
-
for (const
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
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");
|
|
@@ -14793,6 +14799,59 @@ var init_context_compact = __esm({
|
|
|
14793
14799
|
}
|
|
14794
14800
|
});
|
|
14795
14801
|
|
|
14802
|
+
// src/threads/worktree-runtime.ts
|
|
14803
|
+
function worktreeProcessPrefix(worktreePath) {
|
|
14804
|
+
return `wt:${normalizeWorktreePath(worktreePath)}`;
|
|
14805
|
+
}
|
|
14806
|
+
function worktreeSetupProcessKey(worktreePath) {
|
|
14807
|
+
return `${worktreeProcessPrefix(worktreePath)}:setup`;
|
|
14808
|
+
}
|
|
14809
|
+
function worktreeRunProcessKey(worktreePath, scriptName) {
|
|
14810
|
+
return `${worktreeProcessPrefix(worktreePath)}:run:${scriptName}`;
|
|
14811
|
+
}
|
|
14812
|
+
function worktreeDevProcessKey(worktreePath) {
|
|
14813
|
+
return `${worktreeProcessPrefix(worktreePath)}:dev`;
|
|
14814
|
+
}
|
|
14815
|
+
function isWorktreeRunProcessKey(key, worktreePath) {
|
|
14816
|
+
const prefix = worktreeProcessPrefix(worktreePath);
|
|
14817
|
+
return key.startsWith(`${prefix}:run:`) || key === `${prefix}:dev`;
|
|
14818
|
+
}
|
|
14819
|
+
function pickRichestSetupLog(snaps) {
|
|
14820
|
+
let best;
|
|
14821
|
+
for (const snap of snaps) {
|
|
14822
|
+
if (!snap.output && !snap.running && snap.exitCode == null) continue;
|
|
14823
|
+
if (!best) {
|
|
14824
|
+
best = snap;
|
|
14825
|
+
continue;
|
|
14826
|
+
}
|
|
14827
|
+
if (snap.running && !best.running) {
|
|
14828
|
+
best = snap;
|
|
14829
|
+
continue;
|
|
14830
|
+
}
|
|
14831
|
+
if (snap.running === best.running && snap.output.length > best.output.length) {
|
|
14832
|
+
best = snap;
|
|
14833
|
+
}
|
|
14834
|
+
}
|
|
14835
|
+
return best;
|
|
14836
|
+
}
|
|
14837
|
+
function mergeWorktreeActiveRuns(threads) {
|
|
14838
|
+
const byName = /* @__PURE__ */ new Map();
|
|
14839
|
+
let devPort = null;
|
|
14840
|
+
for (const thread of threads) {
|
|
14841
|
+
if (thread.devPort != null && devPort == null) devPort = thread.devPort;
|
|
14842
|
+
for (const run2 of thread.activeRuns ?? []) {
|
|
14843
|
+
if (!byName.has(run2.scriptName)) byName.set(run2.scriptName, run2);
|
|
14844
|
+
}
|
|
14845
|
+
}
|
|
14846
|
+
return { activeRuns: [...byName.values()], devPort };
|
|
14847
|
+
}
|
|
14848
|
+
var init_worktree_runtime = __esm({
|
|
14849
|
+
"src/threads/worktree-runtime.ts"() {
|
|
14850
|
+
"use strict";
|
|
14851
|
+
init_worktree_labels();
|
|
14852
|
+
}
|
|
14853
|
+
});
|
|
14854
|
+
|
|
14796
14855
|
// src/threads/chat-tabs.ts
|
|
14797
14856
|
function sameWorktreePath(a, b) {
|
|
14798
14857
|
return normalizeWorktreePath(a) === normalizeWorktreePath(b);
|
|
@@ -14855,7 +14914,7 @@ function forkMessageSlice(from, throughIndex) {
|
|
|
14855
14914
|
function buildForkTranscriptAttachment(baseTitle, messages) {
|
|
14856
14915
|
const title = baseTitle || "Chat";
|
|
14857
14916
|
return {
|
|
14858
|
-
id: (0,
|
|
14917
|
+
id: (0, import_node_crypto8.randomUUID)(),
|
|
14859
14918
|
name: `Transcript of ${title}.md`,
|
|
14860
14919
|
kind: "transcript",
|
|
14861
14920
|
content: formatTranscriptMarkdown(title, messages)
|
|
@@ -14889,7 +14948,8 @@ function createChatTab(input) {
|
|
|
14889
14948
|
planMode: from.planMode,
|
|
14890
14949
|
autonomy: input.autonomy ?? from.autonomy,
|
|
14891
14950
|
attachments: input.attachments ?? [],
|
|
14892
|
-
status: "idle"
|
|
14951
|
+
status: "idle",
|
|
14952
|
+
...mergeWorktreeActiveRuns(threadsSharingWorktree(binding.worktreePath))
|
|
14893
14953
|
});
|
|
14894
14954
|
writeThread(thread);
|
|
14895
14955
|
return thread;
|
|
@@ -14912,17 +14972,18 @@ function forkChatTab(input) {
|
|
|
14912
14972
|
}
|
|
14913
14973
|
return tab;
|
|
14914
14974
|
}
|
|
14915
|
-
var
|
|
14975
|
+
var import_node_crypto8;
|
|
14916
14976
|
var init_chat_tabs = __esm({
|
|
14917
14977
|
"src/threads/chat-tabs.ts"() {
|
|
14918
14978
|
"use strict";
|
|
14919
|
-
|
|
14979
|
+
import_node_crypto8 = require("crypto");
|
|
14920
14980
|
init_context_compact();
|
|
14921
14981
|
init_teams();
|
|
14922
14982
|
init_worktree_labels();
|
|
14923
14983
|
init_global_workspace();
|
|
14924
14984
|
init_orchestrator_capable();
|
|
14925
14985
|
init_thread_store();
|
|
14986
|
+
init_worktree_runtime();
|
|
14926
14987
|
init_worktree_labels();
|
|
14927
14988
|
}
|
|
14928
14989
|
});
|
|
@@ -15182,7 +15243,7 @@ function buildReviewRequestAttachment(content, opts) {
|
|
|
15182
15243
|
const path2 = opts?.path ?? REVIEW_SKILL_PATH;
|
|
15183
15244
|
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
15245
|
return {
|
|
15185
|
-
id: (0,
|
|
15246
|
+
id: (0, import_node_crypto9.randomUUID)(),
|
|
15186
15247
|
name,
|
|
15187
15248
|
kind: "file",
|
|
15188
15249
|
path: path2,
|
|
@@ -15217,11 +15278,11 @@ async function requestReview(threadRef, send2) {
|
|
|
15217
15278
|
const started = await send2(tab.id, REVIEW_REQUEST_PREFILL);
|
|
15218
15279
|
return { tab: started, from };
|
|
15219
15280
|
}
|
|
15220
|
-
var
|
|
15281
|
+
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
15282
|
var init_request_review = __esm({
|
|
15222
15283
|
"src/review/request-review.ts"() {
|
|
15223
15284
|
"use strict";
|
|
15224
|
-
|
|
15285
|
+
import_node_crypto9 = require("crypto");
|
|
15225
15286
|
import_node_fs38 = require("fs");
|
|
15226
15287
|
import_node_path38 = require("path");
|
|
15227
15288
|
init_global_workspace();
|
|
@@ -18092,7 +18153,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
|
18092
18153
|
`- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
|
|
18093
18154
|
].join("\n");
|
|
18094
18155
|
return {
|
|
18095
|
-
id: (0,
|
|
18156
|
+
id: (0, import_node_crypto10.randomUUID)(),
|
|
18096
18157
|
name: "Orchestration quota handoff.md",
|
|
18097
18158
|
kind: "transcript",
|
|
18098
18159
|
content: body
|
|
@@ -18113,11 +18174,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
18113
18174
|
sourceType: "orchestration"
|
|
18114
18175
|
});
|
|
18115
18176
|
}
|
|
18116
|
-
var
|
|
18177
|
+
var import_node_crypto10, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
|
|
18117
18178
|
var init_quota_failover = __esm({
|
|
18118
18179
|
"src/orchestrator/quota-failover.ts"() {
|
|
18119
18180
|
"use strict";
|
|
18120
|
-
|
|
18181
|
+
import_node_crypto10 = require("crypto");
|
|
18121
18182
|
init_session_quota();
|
|
18122
18183
|
init_app_settings();
|
|
18123
18184
|
init_global_workspace();
|
|
@@ -21039,6 +21100,7 @@ var init_orchestrator = __esm({
|
|
|
21039
21100
|
init_setup_last_error();
|
|
21040
21101
|
init_request_review();
|
|
21041
21102
|
init_fork_worktree();
|
|
21103
|
+
init_worktree_runtime();
|
|
21042
21104
|
init_quota_failover();
|
|
21043
21105
|
init_adopt();
|
|
21044
21106
|
init_stack_layers();
|
|
@@ -22204,22 +22266,25 @@ var init_orchestrator = __esm({
|
|
|
22204
22266
|
"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
22267
|
);
|
|
22206
22268
|
}
|
|
22207
|
-
const
|
|
22208
|
-
const
|
|
22209
|
-
const
|
|
22269
|
+
const siblings = threadsSharingWorktree(thread.worktreePath);
|
|
22270
|
+
const shared = mergeWorktreeActiveRuns(siblings);
|
|
22271
|
+
const runKey = worktreeRunProcessKey(thread.worktreePath, resolvedName);
|
|
22272
|
+
const existing = this.processes.get(runKey) ?? siblings.map((t) => this.processes.get(`${t.id}:run:${resolvedName}`)).find(Boolean);
|
|
22273
|
+
const active = shared.activeRuns.find((r) => r.scriptName === resolvedName);
|
|
22210
22274
|
if (existing && active) {
|
|
22211
22275
|
return { port: active.port, scriptName: resolvedName, ports: active.ports };
|
|
22212
22276
|
}
|
|
22213
22277
|
if (!scriptName) {
|
|
22214
|
-
const legacy = this.processes.get(`${
|
|
22215
|
-
if (legacy &&
|
|
22216
|
-
return { port:
|
|
22278
|
+
const legacy = this.processes.get(worktreeDevProcessKey(thread.worktreePath)) ?? siblings.map((t) => this.processes.get(`${t.id}:dev`)).find(Boolean);
|
|
22279
|
+
if (legacy && shared.devPort) {
|
|
22280
|
+
return { port: shared.devPort, scriptName: resolvedName, ports: [shared.devPort] };
|
|
22217
22281
|
}
|
|
22218
22282
|
}
|
|
22219
22283
|
const mode = getRunMode(thread.worktreePath, thread.repoPath);
|
|
22220
22284
|
if (mode === "nonconcurrent") {
|
|
22285
|
+
const here = normalizeWorktreePath(thread.worktreePath);
|
|
22221
22286
|
for (const t of listThreads()) {
|
|
22222
|
-
if (t.
|
|
22287
|
+
if (normalizeWorktreePath(t.worktreePath) === here) continue;
|
|
22223
22288
|
const runs = t.activeRuns ?? [];
|
|
22224
22289
|
if (runs.length > 0 || t.devPort) {
|
|
22225
22290
|
throw new Error(
|
|
@@ -22254,7 +22319,7 @@ var init_orchestrator = __esm({
|
|
|
22254
22319
|
});
|
|
22255
22320
|
const isDefault = scripts.find((s) => s.default)?.name === resolvedName || !scripts.some((s) => s.default) && (resolvedName === "dev" || resolvedName === scripts[0]?.name);
|
|
22256
22321
|
if (isDefault) {
|
|
22257
|
-
this.processes.set(
|
|
22322
|
+
this.processes.set(worktreeDevProcessKey(thread.worktreePath), {
|
|
22258
22323
|
kind: "dev",
|
|
22259
22324
|
pid: handle.pid,
|
|
22260
22325
|
startedAt,
|
|
@@ -22269,13 +22334,14 @@ var init_orchestrator = __esm({
|
|
|
22269
22334
|
startedAt
|
|
22270
22335
|
};
|
|
22271
22336
|
const nextRuns = [
|
|
22272
|
-
...
|
|
22337
|
+
...shared.activeRuns.filter((r) => r.scriptName !== resolvedName),
|
|
22273
22338
|
run2
|
|
22274
22339
|
];
|
|
22275
|
-
|
|
22276
|
-
|
|
22277
|
-
|
|
22278
|
-
|
|
22340
|
+
this.syncWorktreeRuns(
|
|
22341
|
+
thread.worktreePath,
|
|
22342
|
+
nextRuns,
|
|
22343
|
+
isDefault ? handle.port : shared.devPort
|
|
22344
|
+
);
|
|
22279
22345
|
this.emit({
|
|
22280
22346
|
type: "dev_server_started",
|
|
22281
22347
|
threadId: thread.id,
|
|
@@ -22284,15 +22350,14 @@ var init_orchestrator = __esm({
|
|
|
22284
22350
|
});
|
|
22285
22351
|
void handle.done.then(() => {
|
|
22286
22352
|
this.processes.delete(runKey);
|
|
22287
|
-
if (isDefault) this.processes.delete(
|
|
22288
|
-
const latest =
|
|
22289
|
-
const remaining =
|
|
22290
|
-
|
|
22353
|
+
if (isDefault) this.processes.delete(worktreeDevProcessKey(thread.worktreePath));
|
|
22354
|
+
const latest = mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath));
|
|
22355
|
+
const remaining = latest.activeRuns.filter((r) => r.scriptName !== resolvedName);
|
|
22356
|
+
this.syncWorktreeRuns(
|
|
22357
|
+
thread.worktreePath,
|
|
22358
|
+
remaining,
|
|
22359
|
+
isDefault ? null : latest.devPort
|
|
22291
22360
|
);
|
|
22292
|
-
updateThread(thread.id, {
|
|
22293
|
-
activeRuns: remaining,
|
|
22294
|
-
devPort: isDefault ? null : latest?.devPort ?? null
|
|
22295
|
-
});
|
|
22296
22361
|
this.emit({
|
|
22297
22362
|
type: "dev_server_stopped",
|
|
22298
22363
|
threadId: thread.id,
|
|
@@ -22303,51 +22368,82 @@ var init_orchestrator = __esm({
|
|
|
22303
22368
|
}
|
|
22304
22369
|
stopDev(threadRef, scriptName) {
|
|
22305
22370
|
const thread = this.requireThread(threadRef);
|
|
22371
|
+
const siblings = threadsSharingWorktree(thread.worktreePath);
|
|
22372
|
+
const shared = mergeWorktreeActiveRuns(siblings);
|
|
22306
22373
|
if (scriptName) {
|
|
22307
|
-
const
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
|
|
22311
|
-
const
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
|
|
22374
|
+
const keys = [
|
|
22375
|
+
worktreeRunProcessKey(thread.worktreePath, scriptName),
|
|
22376
|
+
...siblings.map((t) => `${t.id}:run:${scriptName}`)
|
|
22377
|
+
];
|
|
22378
|
+
for (const key of keys) {
|
|
22379
|
+
const proc = this.processes.get(key);
|
|
22380
|
+
if (proc) proc.kill();
|
|
22381
|
+
this.processes.delete(key);
|
|
22382
|
+
}
|
|
22383
|
+
const remaining = shared.activeRuns.filter((r) => r.scriptName !== scriptName);
|
|
22384
|
+
const isPrimary = shared.devPort != null && shared.activeRuns.find((r) => r.scriptName === scriptName)?.port === shared.devPort;
|
|
22385
|
+
this.syncWorktreeRuns(
|
|
22386
|
+
thread.worktreePath,
|
|
22387
|
+
remaining,
|
|
22388
|
+
isPrimary ? null : shared.devPort
|
|
22389
|
+
);
|
|
22317
22390
|
this.emit({ type: "dev_server_stopped", threadId: thread.id, scriptName });
|
|
22318
22391
|
return;
|
|
22319
22392
|
}
|
|
22320
22393
|
for (const [key, proc] of [...this.processes.entries()]) {
|
|
22321
|
-
if (key.startsWith(`${
|
|
22394
|
+
if (isWorktreeRunProcessKey(key, thread.worktreePath) || siblings.some((t) => key.startsWith(`${t.id}:run:`) || key === `${t.id}:dev`)) {
|
|
22322
22395
|
proc.kill();
|
|
22323
22396
|
this.processes.delete(key);
|
|
22324
22397
|
}
|
|
22325
22398
|
}
|
|
22326
|
-
|
|
22399
|
+
this.syncWorktreeRuns(thread.worktreePath, [], null);
|
|
22327
22400
|
this.emit({ type: "dev_server_stopped", threadId: thread.id });
|
|
22328
22401
|
}
|
|
22402
|
+
syncWorktreeRuns(worktreePath, activeRuns, devPort) {
|
|
22403
|
+
for (const sibling of threadsSharingWorktree(worktreePath)) {
|
|
22404
|
+
updateThread(sibling.id, { activeRuns, devPort });
|
|
22405
|
+
}
|
|
22406
|
+
}
|
|
22329
22407
|
listThreadRunScripts(threadRef) {
|
|
22330
22408
|
const thread = this.requireThread(threadRef);
|
|
22331
22409
|
return listRunScripts(thread.worktreePath, thread.repoPath);
|
|
22332
22410
|
}
|
|
22333
22411
|
getActiveRuns(threadRef) {
|
|
22334
|
-
|
|
22412
|
+
const thread = this.requireThread(threadRef);
|
|
22413
|
+
return mergeWorktreeActiveRuns(threadsSharingWorktree(thread.worktreePath)).activeRuns;
|
|
22335
22414
|
}
|
|
22336
22415
|
getSetupLog(threadRef) {
|
|
22337
22416
|
const thread = this.getThread(threadRef);
|
|
22338
22417
|
if (!thread) return readSetupLog(threadRef);
|
|
22339
|
-
const
|
|
22340
|
-
|
|
22418
|
+
const siblingIds = [
|
|
22419
|
+
thread.id,
|
|
22420
|
+
...threadsSharingWorktree(thread.worktreePath).map((t) => t.id)
|
|
22421
|
+
];
|
|
22422
|
+
const snap = pickRichestSetupLog([
|
|
22423
|
+
readSetupLog(setupLogKeyForWorktree(thread.worktreePath)),
|
|
22424
|
+
...[...new Set(siblingIds)].map((id) => readSetupLog(id))
|
|
22425
|
+
]) ?? readSetupLog(thread.id);
|
|
22426
|
+
if (snap.running && !this.hasWorktreeSetupProcess(thread.worktreePath)) {
|
|
22341
22427
|
return { ...snap, running: false };
|
|
22342
22428
|
}
|
|
22343
22429
|
return snap;
|
|
22344
22430
|
}
|
|
22431
|
+
hasWorktreeSetupProcess(worktreePath) {
|
|
22432
|
+
if (this.processes.has(worktreeSetupProcessKey(worktreePath))) return true;
|
|
22433
|
+
return threadsSharingWorktree(worktreePath).some(
|
|
22434
|
+
(t) => this.processes.has(`${t.id}:setup`)
|
|
22435
|
+
);
|
|
22436
|
+
}
|
|
22437
|
+
writeSetupLog(threadId, worktreePath, fn) {
|
|
22438
|
+
fn(setupLogKeyForWorktree(worktreePath));
|
|
22439
|
+
fn(threadId);
|
|
22440
|
+
}
|
|
22345
22441
|
async runSetup(threadRef, opts) {
|
|
22346
22442
|
const thread = this.requireThread(threadRef);
|
|
22347
22443
|
this.assertNotGlobal(thread, "Setup");
|
|
22348
|
-
const key =
|
|
22349
|
-
if (this.
|
|
22350
|
-
throw new Error("Setup already running for this
|
|
22444
|
+
const key = worktreeSetupProcessKey(thread.worktreePath);
|
|
22445
|
+
if (this.hasWorktreeSetupProcess(thread.worktreePath)) {
|
|
22446
|
+
throw new Error("Setup already running for this worktree");
|
|
22351
22447
|
}
|
|
22352
22448
|
const abort = new AbortController();
|
|
22353
22449
|
this.processes.set(key, {
|
|
@@ -22355,13 +22451,17 @@ var init_orchestrator = __esm({
|
|
|
22355
22451
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22356
22452
|
kill: () => abort.abort()
|
|
22357
22453
|
});
|
|
22358
|
-
|
|
22454
|
+
this.writeSetupLog(thread.id, thread.worktreePath, beginSetupLog);
|
|
22359
22455
|
this.emit({ type: "setup_started", threadId: thread.id });
|
|
22360
22456
|
let finished = false;
|
|
22361
22457
|
const finish = (exitCode, source) => {
|
|
22362
22458
|
if (finished) return;
|
|
22363
22459
|
finished = true;
|
|
22364
|
-
|
|
22460
|
+
this.writeSetupLog(
|
|
22461
|
+
thread.id,
|
|
22462
|
+
thread.worktreePath,
|
|
22463
|
+
(logKey) => finishSetupLog(logKey, exitCode, source)
|
|
22464
|
+
);
|
|
22365
22465
|
this.emit({ type: "setup_finished", threadId: thread.id, exitCode });
|
|
22366
22466
|
};
|
|
22367
22467
|
try {
|
|
@@ -22369,14 +22469,22 @@ var init_orchestrator = __esm({
|
|
|
22369
22469
|
thread.repoPath,
|
|
22370
22470
|
thread.worktreePath,
|
|
22371
22471
|
(line) => {
|
|
22372
|
-
|
|
22472
|
+
this.writeSetupLog(
|
|
22473
|
+
thread.id,
|
|
22474
|
+
thread.worktreePath,
|
|
22475
|
+
(logKey) => appendSetupLog(logKey, line)
|
|
22476
|
+
);
|
|
22373
22477
|
this.emit({ type: "setup_output", threadId: thread.id, line });
|
|
22374
22478
|
},
|
|
22375
22479
|
{ signal: abort.signal }
|
|
22376
22480
|
);
|
|
22377
22481
|
if (!setup.ran) {
|
|
22378
22482
|
const line = "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)";
|
|
22379
|
-
|
|
22483
|
+
this.writeSetupLog(
|
|
22484
|
+
thread.id,
|
|
22485
|
+
thread.worktreePath,
|
|
22486
|
+
(logKey) => appendSetupLog(logKey, line)
|
|
22487
|
+
);
|
|
22380
22488
|
this.emit({ type: "setup_output", threadId: thread.id, line });
|
|
22381
22489
|
finish(null, null);
|
|
22382
22490
|
throw new Error(line);
|
|
@@ -22404,8 +22512,14 @@ var init_orchestrator = __esm({
|
|
|
22404
22512
|
}
|
|
22405
22513
|
cancelSetup(threadRef) {
|
|
22406
22514
|
const thread = this.requireThread(threadRef);
|
|
22407
|
-
const
|
|
22408
|
-
|
|
22515
|
+
const keys = [
|
|
22516
|
+
worktreeSetupProcessKey(thread.worktreePath),
|
|
22517
|
+
...threadsSharingWorktree(thread.worktreePath).map((t) => `${t.id}:setup`)
|
|
22518
|
+
];
|
|
22519
|
+
for (const key of [...new Set(keys)]) {
|
|
22520
|
+
const proc = this.processes.get(key);
|
|
22521
|
+
if (proc) proc.kill();
|
|
22522
|
+
}
|
|
22409
22523
|
}
|
|
22410
22524
|
async applyIntoMain(threadRef, opts) {
|
|
22411
22525
|
const thread = this.requireThread(threadRef);
|
|
@@ -24205,7 +24319,7 @@ function issueActivityKind(issue, sinceIso) {
|
|
|
24205
24319
|
}
|
|
24206
24320
|
|
|
24207
24321
|
// src/integrations/linear-oauth.ts
|
|
24208
|
-
var
|
|
24322
|
+
var import_node_crypto11 = require("crypto");
|
|
24209
24323
|
var import_node_http = require("http");
|
|
24210
24324
|
init_app_settings();
|
|
24211
24325
|
init_fetch();
|
|
@@ -24237,8 +24351,8 @@ function linearOAuthCredentials() {
|
|
|
24237
24351
|
return { clientId, clientSecret };
|
|
24238
24352
|
}
|
|
24239
24353
|
function createLinearPkce() {
|
|
24240
|
-
const verifier = (0,
|
|
24241
|
-
const challenge = (0,
|
|
24354
|
+
const verifier = (0, import_node_crypto11.randomBytes)(32).toString("base64url");
|
|
24355
|
+
const challenge = (0, import_node_crypto11.createHash)("sha256").update(verifier).digest("base64url");
|
|
24242
24356
|
return { verifier, challenge };
|
|
24243
24357
|
}
|
|
24244
24358
|
function linearOAuthAuthorizeUrl(clientId, state, codeChallenge) {
|
|
@@ -24369,7 +24483,7 @@ async function startLinearOAuth(opts) {
|
|
|
24369
24483
|
throw new LinearOAuthCancelledError();
|
|
24370
24484
|
}
|
|
24371
24485
|
const { clientId, clientSecret } = linearOAuthCredentials();
|
|
24372
|
-
const state = (0,
|
|
24486
|
+
const state = (0, import_node_crypto11.randomBytes)(16).toString("hex");
|
|
24373
24487
|
const pkce = createLinearPkce();
|
|
24374
24488
|
const authorizeUrl = linearOAuthAuthorizeUrl(clientId, state, pkce.challenge);
|
|
24375
24489
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
@@ -26222,7 +26336,7 @@ function buildPathRefAttachment(input) {
|
|
|
26222
26336
|
init_stage_files();
|
|
26223
26337
|
|
|
26224
26338
|
// src/composer/pasted-text.ts
|
|
26225
|
-
var
|
|
26339
|
+
var import_node_crypto12 = require("crypto");
|
|
26226
26340
|
var PASTE_ATTACH_MIN_CHARS = 1200;
|
|
26227
26341
|
var PASTE_ATTACH_MIN_LINES = 15;
|
|
26228
26342
|
var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
|
|
@@ -26249,7 +26363,7 @@ function nextPastedTextName(existing) {
|
|
|
26249
26363
|
}
|
|
26250
26364
|
function buildPastedTextAttachment(text7, opts) {
|
|
26251
26365
|
return {
|
|
26252
|
-
id: opts?.id ?? (0,
|
|
26366
|
+
id: opts?.id ?? (0, import_node_crypto12.randomUUID)(),
|
|
26253
26367
|
name: opts?.name ?? "Pasted text #1.txt",
|
|
26254
26368
|
kind: "file",
|
|
26255
26369
|
path: opts?.path,
|
|
@@ -26403,7 +26517,7 @@ init_orphan_cleanup();
|
|
|
26403
26517
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
26404
26518
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
26405
26519
|
var import_zod9 = require("zod");
|
|
26406
|
-
var
|
|
26520
|
+
var import_node_crypto14 = require("crypto");
|
|
26407
26521
|
var import_node_path52 = require("path");
|
|
26408
26522
|
init_orchestrator();
|
|
26409
26523
|
init_worktree();
|
|
@@ -28130,7 +28244,7 @@ init_worktree();
|
|
|
28130
28244
|
init_paths();
|
|
28131
28245
|
|
|
28132
28246
|
// src/board/board-pins.ts
|
|
28133
|
-
var
|
|
28247
|
+
var import_node_crypto13 = require("crypto");
|
|
28134
28248
|
var import_node_fs54 = require("fs");
|
|
28135
28249
|
var import_node_path50 = require("path");
|
|
28136
28250
|
init_paths();
|
|
@@ -28162,7 +28276,7 @@ function addBoardPin(input) {
|
|
|
28162
28276
|
const ref = input.ref.trim();
|
|
28163
28277
|
if (!ref) throw new Error("Board item needs a ref");
|
|
28164
28278
|
const pin = {
|
|
28165
|
-
id: (0,
|
|
28279
|
+
id: (0, import_node_crypto13.randomUUID)(),
|
|
28166
28280
|
kind: input.kind,
|
|
28167
28281
|
ref,
|
|
28168
28282
|
repoPath: input.repoPath.trim(),
|
|
@@ -28996,7 +29110,7 @@ async function startMcpServer() {
|
|
|
28996
29110
|
const ident = issue?.identifier ?? ref.trim();
|
|
28997
29111
|
const attachments = issue ? [
|
|
28998
29112
|
{
|
|
28999
|
-
id: (0,
|
|
29113
|
+
id: (0, import_node_crypto14.randomUUID)(),
|
|
29000
29114
|
name: issue.identifier,
|
|
29001
29115
|
kind: "issue",
|
|
29002
29116
|
content: [
|
|
@@ -29951,7 +30065,7 @@ init_orch_mcp_isolation();
|
|
|
29951
30065
|
init_workspaces2();
|
|
29952
30066
|
|
|
29953
30067
|
// src/slack/oauth.ts
|
|
29954
|
-
var
|
|
30068
|
+
var import_node_crypto15 = require("crypto");
|
|
29955
30069
|
init_app_settings();
|
|
29956
30070
|
init_workspaces2();
|
|
29957
30071
|
|
|
@@ -30175,7 +30289,7 @@ async function startSlackOAuth(opts) {
|
|
|
30175
30289
|
throw new SlackOAuthCancelledError();
|
|
30176
30290
|
}
|
|
30177
30291
|
const { clientId } = slackOAuthCredentials();
|
|
30178
|
-
const state = (0,
|
|
30292
|
+
const state = (0, import_node_crypto15.randomBytes)(16).toString("hex");
|
|
30179
30293
|
const authorizeUrl = slackOAuthAuthorizeUrl(clientId, state);
|
|
30180
30294
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
30181
30295
|
const pollIntervalMs = opts?.pollIntervalMs ?? 400;
|
|
@@ -30457,7 +30571,7 @@ init_workspaces2();
|
|
|
30457
30571
|
init_reply_target();
|
|
30458
30572
|
|
|
30459
30573
|
// src/slack/relay-protocol.ts
|
|
30460
|
-
var
|
|
30574
|
+
var import_node_crypto16 = require("crypto");
|
|
30461
30575
|
var SLACK_RELAY_PING_INTERVAL_MS = 2e4;
|
|
30462
30576
|
var SLACK_RELAY_PONG_TIMEOUT_MS = 15e3;
|
|
30463
30577
|
function slackRelaySessionKey(teamId, userId, deviceId) {
|
|
@@ -30467,7 +30581,7 @@ function slackRelayUserKey(teamId, userId) {
|
|
|
30467
30581
|
return `${teamId.trim()}:${userId.trim()}`;
|
|
30468
30582
|
}
|
|
30469
30583
|
function newSlackRelayEventId() {
|
|
30470
|
-
return (0,
|
|
30584
|
+
return (0, import_node_crypto16.randomBytes)(12).toString("hex");
|
|
30471
30585
|
}
|
|
30472
30586
|
function parseSlackRelayClientMessage(raw) {
|
|
30473
30587
|
try {
|