@sideboard-ai/core 0.1.288 → 0.1.290
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-GZLTMGSH.js → agents-4BBCKITL.js} +4 -4
- package/dist/{agents-UQMHFZSM.js → agents-XAKSP653.js} +4 -4
- package/dist/{chunk-HNB2HTCV.js → chunk-2IXKTAIT.js} +1 -1
- package/dist/{chunk-442O6OXM.js → chunk-4VXWLY3L.js} +13 -12
- package/dist/{chunk-3W5SL5EW.js → chunk-5HPMKLJ7.js} +1 -1
- package/dist/{chunk-4NM72HJZ.js → chunk-G74TSBJ3.js} +4 -4
- package/dist/{chunk-WQSIUZEB.js → chunk-GBAACE3C.js} +4 -4
- package/dist/{chunk-2N77CLRQ.js → chunk-LUQ7MIVD.js} +3 -3
- package/dist/{chunk-IA4MPG7F.js → chunk-O4324MXE.js} +2 -2
- package/dist/{chunk-WW5XGEMY.js → chunk-QOUTSISS.js} +14 -13
- package/dist/{chunk-DBOFDEQ7.js → chunk-V3BSO2O6.js} +2 -2
- package/dist/{chunk-6NVQNTJO.js → chunk-VNCPRMIK.js} +3 -3
- package/dist/{conductor-MCU63Z5U.js → conductor-5MNGC25D.js} +4 -4
- package/dist/{conductor-ZLYZVIAV.js → conductor-UFJXJPTF.js} +4 -4
- package/dist/{coordinator-prompt-KHCXXDRO.js → coordinator-prompt-5Q66Y5KW.js} +3 -3
- package/dist/{coordinator-prompt-UV6THLMD.js → coordinator-prompt-BYPEW6DE.js} +3 -3
- package/dist/{global-workspace-GDHZSFP6.js → global-workspace-BTH2YGDZ.js} +3 -3
- package/dist/{global-workspace-AXTSYVAE.js → global-workspace-YKOYXSB2.js} +3 -3
- package/dist/index.cjs +695 -145
- package/dist/index.d.cts +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +641 -102
- package/dist/mcp/run-stdio.cjs +2 -1
- package/dist/mcp/run-stdio.js +6 -6
- package/dist/{orchestrator-5ZBHR6BJ.js → orchestrator-EQSK2RMI.js} +6 -6
- package/dist/{orchestrator-SDH6W6ZL.js → orchestrator-GSOW3RPA.js} +6 -6
- package/dist/{thread-store-7XBOUQ5K.js → thread-store-DPKESDFR.js} +1 -1
- package/dist/{thread-store-ZQZDREVH.js → thread-store-X6S2GIR5.js} +1 -1
- package/dist/{workspaces-Q2R2XGO3.js → workspaces-ELO2YVTU.js} +3 -3
- package/dist/{workspaces-L2UR4KC2.js → workspaces-RFD6I4FM.js} +3 -3
- package/dist/{worktree-2GOK4VYT.js → worktree-ADVQ6X6C.js} +2 -2
- package/dist/{worktree-YEUTJ7MZ.js → worktree-TN7DZDJZ.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-N5UGX3JL.js → chunk-QB5MDOHL.js} +0 -0
- /package/dist/{chunk-JPH7PYSK.js → chunk-YRXV3E7J.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -186,7 +186,7 @@ import {
|
|
|
186
186
|
worktreeBoardStatus,
|
|
187
187
|
worktreeCleanupSettings,
|
|
188
188
|
writeWorktreeFile
|
|
189
|
-
} from "./chunk-
|
|
189
|
+
} from "./chunk-QOUTSISS.js";
|
|
190
190
|
import {
|
|
191
191
|
LEGACY_PLAN_FILE_REL,
|
|
192
192
|
PLAN_FILE_NAME,
|
|
@@ -281,7 +281,7 @@ import {
|
|
|
281
281
|
userMcpNamesToDisable,
|
|
282
282
|
worktreeMcpToolNames,
|
|
283
283
|
writeInjectedMcpConfig
|
|
284
|
-
} from "./chunk-
|
|
284
|
+
} from "./chunk-VNCPRMIK.js";
|
|
285
285
|
import {
|
|
286
286
|
brightsyMcpServerName,
|
|
287
287
|
connectBrightsyTeam,
|
|
@@ -408,7 +408,7 @@ import {
|
|
|
408
408
|
workspaceAgentChatSummaries,
|
|
409
409
|
worktreeRunPortsPath,
|
|
410
410
|
wrapReviewSkillMarkdown
|
|
411
|
-
} from "./chunk-
|
|
411
|
+
} from "./chunk-GBAACE3C.js";
|
|
412
412
|
import {
|
|
413
413
|
STDERR_EVENT_MAX_CHARS,
|
|
414
414
|
STDOUT_EVENT_MAX_CHARS,
|
|
@@ -608,7 +608,7 @@ import {
|
|
|
608
608
|
stageBuffersAsAttachments,
|
|
609
609
|
syncWorkspacesFromThreads,
|
|
610
610
|
takenTeamSlugsForOrchestration
|
|
611
|
-
} from "./chunk-
|
|
611
|
+
} from "./chunk-V3BSO2O6.js";
|
|
612
612
|
import {
|
|
613
613
|
GITHUB_PR_BODY_MAX_CHARS,
|
|
614
614
|
GITHUB_PR_BODY_SAFE_CHARS,
|
|
@@ -696,7 +696,7 @@ import {
|
|
|
696
696
|
suggestSlug,
|
|
697
697
|
switchWorktreeBranch,
|
|
698
698
|
warmGithubAgentAuth
|
|
699
|
-
} from "./chunk-
|
|
699
|
+
} from "./chunk-2IXKTAIT.js";
|
|
700
700
|
import {
|
|
701
701
|
FAMOUS_SOCCER_TEAMS,
|
|
702
702
|
allocateTeamName,
|
|
@@ -728,7 +728,7 @@ import {
|
|
|
728
728
|
worktreeNameFromPath,
|
|
729
729
|
worktreeSlugForTicket,
|
|
730
730
|
writeThread
|
|
731
|
-
} from "./chunk-
|
|
731
|
+
} from "./chunk-QB5MDOHL.js";
|
|
732
732
|
import "./chunk-2P4XKWV2.js";
|
|
733
733
|
import {
|
|
734
734
|
BRIGHTSY_OAUTH_CLIENT_ID,
|
|
@@ -5126,7 +5126,7 @@ async function handleJobStop(params, _ctx) {
|
|
|
5126
5126
|
return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
|
|
5127
5127
|
}
|
|
5128
5128
|
async function defaultTurnWait(ref, timeoutMs) {
|
|
5129
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
5129
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-GSOW3RPA.js");
|
|
5130
5130
|
const orch = getOrchestrator2();
|
|
5131
5131
|
const thread = await orch.waitForTurn(ref, timeoutMs, {
|
|
5132
5132
|
resolveIfStillRunning: true
|
|
@@ -5439,15 +5439,15 @@ async function getHomeBoardInputs(workspaces, opts) {
|
|
|
5439
5439
|
}
|
|
5440
5440
|
const promise = (async () => {
|
|
5441
5441
|
const inputs = await loadHomeBoardInputs(workspaces);
|
|
5442
|
-
const
|
|
5442
|
+
const fetchedAt2 = opts?.now ?? Date.now();
|
|
5443
5443
|
const pins = syncBoardPins(listBoardPins(), inputs.issues, inputs.prs);
|
|
5444
5444
|
replaceBoardPins(pins);
|
|
5445
|
-
const loaded = { ...inputs, fetchedAt, fromCache: false, pins };
|
|
5445
|
+
const loaded = { ...inputs, fetchedAt: fetchedAt2, fromCache: false, pins };
|
|
5446
5446
|
if (shouldCacheHomeBoardInputs(inputs)) {
|
|
5447
5447
|
const entry = {
|
|
5448
5448
|
version: CACHE_VERSION,
|
|
5449
5449
|
workspaceKey: key,
|
|
5450
|
-
fetchedAt,
|
|
5450
|
+
fetchedAt: fetchedAt2,
|
|
5451
5451
|
inputs
|
|
5452
5452
|
};
|
|
5453
5453
|
memory = entry;
|
|
@@ -9165,14 +9165,46 @@ var RemoteHub = class {
|
|
|
9165
9165
|
this.codes.delete(code);
|
|
9166
9166
|
this.attachPhone(socket, host);
|
|
9167
9167
|
}
|
|
9168
|
+
/** In-memory session, with the live Mac's label when that socket is up. */
|
|
9169
|
+
memorySession(sessionToken) {
|
|
9170
|
+
const cached2 = this.phonesByToken.get(sessionToken);
|
|
9171
|
+
if (!cached2) return null;
|
|
9172
|
+
const live = this.hosts.get(cached2.deviceId);
|
|
9173
|
+
const remembered = this.remembered.get(cached2.deviceId);
|
|
9174
|
+
const current = live?.sessionToken ?? remembered?.sessionToken ?? null;
|
|
9175
|
+
if (current && current !== sessionToken) {
|
|
9176
|
+
this.phonesByToken.delete(sessionToken);
|
|
9177
|
+
return null;
|
|
9178
|
+
}
|
|
9179
|
+
return {
|
|
9180
|
+
deviceId: cached2.deviceId,
|
|
9181
|
+
deviceLabel: live?.deviceLabel || remembered?.deviceLabel || cached2.deviceLabel
|
|
9182
|
+
};
|
|
9183
|
+
}
|
|
9184
|
+
knownSession(sessionToken) {
|
|
9185
|
+
const cached2 = this.memorySession(sessionToken);
|
|
9186
|
+
if (cached2) return cached2;
|
|
9187
|
+
const stored = this.opts.phoneSessions?.find(sessionToken) ?? null;
|
|
9188
|
+
if (!stored) return null;
|
|
9189
|
+
const live = this.hosts.get(stored.deviceId);
|
|
9190
|
+
const remembered = this.remembered.get(stored.deviceId);
|
|
9191
|
+
const current = live?.sessionToken ?? remembered?.sessionToken ?? null;
|
|
9192
|
+
if (current && current !== sessionToken) return null;
|
|
9193
|
+
this.phonesByToken.set(sessionToken, stored);
|
|
9194
|
+
if (remembered && !remembered.sessionToken) remembered.sessionToken = sessionToken;
|
|
9195
|
+
if (live && !live.sessionToken) live.sessionToken = sessionToken;
|
|
9196
|
+
return {
|
|
9197
|
+
deviceId: stored.deviceId,
|
|
9198
|
+
deviceLabel: live?.deviceLabel || remembered?.deviceLabel || stored.deviceLabel
|
|
9199
|
+
};
|
|
9200
|
+
}
|
|
9168
9201
|
resumePhone(socket, sessionToken) {
|
|
9169
|
-
const
|
|
9170
|
-
|
|
9171
|
-
if (!deviceId || !remembered || remembered.sessionToken !== sessionToken) {
|
|
9202
|
+
const known = this.knownSession(sessionToken);
|
|
9203
|
+
if (!known) {
|
|
9172
9204
|
send2(socket, { type: "error", message: "phone session expired \u2014 pair again" });
|
|
9173
9205
|
return;
|
|
9174
9206
|
}
|
|
9175
|
-
const host = this.hosts.get(deviceId);
|
|
9207
|
+
const host = this.hosts.get(known.deviceId);
|
|
9176
9208
|
if (!host) {
|
|
9177
9209
|
send2(socket, { type: "host_offline" });
|
|
9178
9210
|
return;
|
|
@@ -9181,17 +9213,13 @@ var RemoteHub = class {
|
|
|
9181
9213
|
}
|
|
9182
9214
|
listHosts(socket, sessionTokens) {
|
|
9183
9215
|
const hosts = sessionTokens.map((sessionToken) => {
|
|
9184
|
-
const
|
|
9185
|
-
|
|
9186
|
-
const
|
|
9187
|
-
if (!valid || !deviceId || !remembered) {
|
|
9188
|
-
return { sessionToken, deviceId: "", deviceLabel: "", online: false };
|
|
9189
|
-
}
|
|
9190
|
-
const host = this.hosts.get(deviceId);
|
|
9216
|
+
const known = this.knownSession(sessionToken);
|
|
9217
|
+
if (!known) return { sessionToken, deviceId: "", deviceLabel: "", online: false };
|
|
9218
|
+
const host = this.hosts.get(known.deviceId);
|
|
9191
9219
|
return {
|
|
9192
9220
|
sessionToken,
|
|
9193
|
-
deviceId,
|
|
9194
|
-
deviceLabel: host?.deviceLabel ||
|
|
9221
|
+
deviceId: known.deviceId,
|
|
9222
|
+
deviceLabel: host?.deviceLabel || known.deviceLabel,
|
|
9195
9223
|
online: Boolean(host)
|
|
9196
9224
|
};
|
|
9197
9225
|
});
|
|
@@ -9222,18 +9250,20 @@ var RemoteHub = class {
|
|
|
9222
9250
|
}
|
|
9223
9251
|
host.phone = null;
|
|
9224
9252
|
}
|
|
9253
|
+
const created = sessionToken == null;
|
|
9225
9254
|
const token = sessionToken ?? randomBytes5(24).toString("hex");
|
|
9226
9255
|
if (host.sessionToken && host.sessionToken !== token) {
|
|
9227
9256
|
this.phonesByToken.delete(host.sessionToken);
|
|
9228
9257
|
}
|
|
9229
9258
|
host.sessionToken = token;
|
|
9230
9259
|
host.phone = socket;
|
|
9231
|
-
this.phonesByToken.set(token, host.deviceId);
|
|
9260
|
+
this.phonesByToken.set(token, { deviceId: host.deviceId, deviceLabel: host.deviceLabel });
|
|
9232
9261
|
this.remembered.set(host.deviceId, {
|
|
9233
9262
|
hostSecret: host.hostSecret,
|
|
9234
9263
|
sessionToken: token,
|
|
9235
9264
|
deviceLabel: host.deviceLabel
|
|
9236
9265
|
});
|
|
9266
|
+
if (created) this.opts.phoneSessions?.save(host.deviceId, token);
|
|
9237
9267
|
this.bySocket.set(socket, { role: "phone", deviceId: host.deviceId });
|
|
9238
9268
|
this.opts.onAuthorized?.(socket);
|
|
9239
9269
|
send2(socket, {
|
|
@@ -9273,7 +9303,11 @@ var RemoteHub = class {
|
|
|
9273
9303
|
import { WebSocket as WsWebSocket3 } from "ws";
|
|
9274
9304
|
|
|
9275
9305
|
// src/remote/phone-media.ts
|
|
9306
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
9307
|
+
import { join as join6 } from "path";
|
|
9276
9308
|
var PHONE_IMAGE_MAX_BYTES = 15e5;
|
|
9309
|
+
var PHONE_TEXT_MAX_BYTES = 18e4;
|
|
9310
|
+
var PHONE_DIR_MAX = 200;
|
|
9277
9311
|
var IMAGE_MIME = {
|
|
9278
9312
|
png: "image/png",
|
|
9279
9313
|
jpg: "image/jpeg",
|
|
@@ -9320,9 +9354,74 @@ function phoneImageDataUrl(worktreePath, raw) {
|
|
|
9320
9354
|
return null;
|
|
9321
9355
|
}
|
|
9322
9356
|
}
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9357
|
+
function phoneDirectoryListing(worktreePath, relative) {
|
|
9358
|
+
try {
|
|
9359
|
+
const entries = readdirSync2(join6(worktreePath, relative), { withFileTypes: true });
|
|
9360
|
+
const dirs = new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
9361
|
+
const names = entries.map((entry) => entry.name).filter((name) => name && !name.includes("/") && !name.includes("\0")).sort((a, b) => a.localeCompare(b));
|
|
9362
|
+
const shown = names.slice(0, PHONE_DIR_MAX);
|
|
9363
|
+
const lines = shown.map((name) => dirs.has(name) ? `${name}/` : name);
|
|
9364
|
+
const truncated = names.length > shown.length;
|
|
9365
|
+
if (truncated) lines.push(`\u2026 ${names.length - shown.length} more`);
|
|
9366
|
+
return { content: lines.join("\n"), truncated };
|
|
9367
|
+
} catch {
|
|
9368
|
+
return null;
|
|
9369
|
+
}
|
|
9370
|
+
}
|
|
9371
|
+
function phoneFilePreview(worktreePath, relative, kind) {
|
|
9372
|
+
if (kind === "dir") {
|
|
9373
|
+
const listing = phoneDirectoryListing(worktreePath, relative);
|
|
9374
|
+
if (!listing) return null;
|
|
9375
|
+
return {
|
|
9376
|
+
content: listing.content,
|
|
9377
|
+
encoding: "utf8",
|
|
9378
|
+
binary: false,
|
|
9379
|
+
directory: true,
|
|
9380
|
+
truncated: listing.truncated
|
|
9381
|
+
};
|
|
9382
|
+
}
|
|
9383
|
+
if (relative.toLowerCase().endsWith(".svg")) {
|
|
9384
|
+
try {
|
|
9385
|
+
const buf = readFileSync5(join6(worktreePath, relative));
|
|
9386
|
+
const truncated = buf.length > PHONE_TEXT_MAX_BYTES;
|
|
9387
|
+
return {
|
|
9388
|
+
content: buf.subarray(0, PHONE_TEXT_MAX_BYTES).toString("utf8"),
|
|
9389
|
+
encoding: "utf8",
|
|
9390
|
+
binary: false,
|
|
9391
|
+
directory: false,
|
|
9392
|
+
truncated,
|
|
9393
|
+
mime: "image/svg+xml"
|
|
9394
|
+
};
|
|
9395
|
+
} catch {
|
|
9396
|
+
return null;
|
|
9397
|
+
}
|
|
9398
|
+
}
|
|
9399
|
+
const mime = phoneImageMime(relative);
|
|
9400
|
+
if (mime) {
|
|
9401
|
+
const dataUrl = phoneImageDataUrl(worktreePath, relative);
|
|
9402
|
+
if (!dataUrl) return null;
|
|
9403
|
+
return {
|
|
9404
|
+
content: dataUrl,
|
|
9405
|
+
encoding: "base64",
|
|
9406
|
+
binary: true,
|
|
9407
|
+
directory: false,
|
|
9408
|
+
truncated: false,
|
|
9409
|
+
mime
|
|
9410
|
+
};
|
|
9411
|
+
}
|
|
9412
|
+
try {
|
|
9413
|
+
const file = readWorktreeFile(worktreePath, relative, { maxBytes: PHONE_TEXT_MAX_BYTES });
|
|
9414
|
+
return {
|
|
9415
|
+
content: file.content,
|
|
9416
|
+
encoding: file.encoding,
|
|
9417
|
+
binary: file.binary,
|
|
9418
|
+
directory: false,
|
|
9419
|
+
truncated: file.truncated
|
|
9420
|
+
};
|
|
9421
|
+
} catch {
|
|
9422
|
+
return null;
|
|
9423
|
+
}
|
|
9424
|
+
}
|
|
9326
9425
|
|
|
9327
9426
|
// src/remote/phone-artifact.ts
|
|
9328
9427
|
var FENCE_RE = /```([a-zA-Z0-9_+-]*)[^\n]*\n([\s\S]*?)(?:```|$)/g;
|
|
@@ -9459,6 +9558,263 @@ function parseOpenArtifact(value, chatId) {
|
|
|
9459
9558
|
const hint = oneLine(str3(record.hint), 200);
|
|
9460
9559
|
return { op: "open-artifact", chatId, title, ...hint ? { hint } : {} };
|
|
9461
9560
|
}
|
|
9561
|
+
var PHONE_ARTIFACT_MAX_CHARS = 18e4;
|
|
9562
|
+
function phoneArtifactDocuments(sources) {
|
|
9563
|
+
const docs = [];
|
|
9564
|
+
for (const source of sources) {
|
|
9565
|
+
const hints = /* @__PURE__ */ new Set();
|
|
9566
|
+
for (const match of source.text.matchAll(new RegExp(FENCE_RE.source, "g"))) {
|
|
9567
|
+
const language = match[1] ?? "";
|
|
9568
|
+
const content = match[2] ?? "";
|
|
9569
|
+
const kind = kindFor(language, content);
|
|
9570
|
+
if (!kind) continue;
|
|
9571
|
+
const hint = artifactHint(content);
|
|
9572
|
+
if (hint) hints.add(hint);
|
|
9573
|
+
docs.push({ title: titleFor(kind, content), content, kind });
|
|
9574
|
+
}
|
|
9575
|
+
for (const part of source.parts ?? []) {
|
|
9576
|
+
if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
|
|
9577
|
+
const input = asRecord2(part.input);
|
|
9578
|
+
const content = toolContent(input);
|
|
9579
|
+
const hint = artifactHint(content);
|
|
9580
|
+
if (hint && hints.has(hint)) continue;
|
|
9581
|
+
const typed = kindFor(str3(input?.type), content) ?? kindFor("", content);
|
|
9582
|
+
const title = oneLine(str3(input?.title), 80) || (typed ? titleFor(typed, content) : "Artifact");
|
|
9583
|
+
if (!str3(input?.title) && content.trim().length < MIN_FENCE_CHARS) continue;
|
|
9584
|
+
if (hint) hints.add(hint);
|
|
9585
|
+
docs.push({ title, content, kind: typed ?? "text" });
|
|
9586
|
+
}
|
|
9587
|
+
}
|
|
9588
|
+
return docs;
|
|
9589
|
+
}
|
|
9590
|
+
function phoneArtifactReply(docs, request) {
|
|
9591
|
+
const match = pickArtifactMatch(docs, request);
|
|
9592
|
+
if (!match) return null;
|
|
9593
|
+
const truncated = match.content.length > PHONE_ARTIFACT_MAX_CHARS;
|
|
9594
|
+
return {
|
|
9595
|
+
title: match.title,
|
|
9596
|
+
kind: match.kind,
|
|
9597
|
+
content: truncated ? match.content.slice(0, PHONE_ARTIFACT_MAX_CHARS) : match.content,
|
|
9598
|
+
truncated
|
|
9599
|
+
};
|
|
9600
|
+
}
|
|
9601
|
+
function newest(items) {
|
|
9602
|
+
return items.length ? items[items.length - 1] ?? null : null;
|
|
9603
|
+
}
|
|
9604
|
+
function pickArtifactMatch(items, request) {
|
|
9605
|
+
const hint = request.hint?.trim();
|
|
9606
|
+
const title = request.title.trim();
|
|
9607
|
+
if (hint) {
|
|
9608
|
+
const exact = items.filter((item) => item.content && artifactHint(item.content) === hint);
|
|
9609
|
+
const exactTitled = title ? exact.filter((item) => item.title === title) : [];
|
|
9610
|
+
const exactMatch = newest(exactTitled.length ? exactTitled : exact);
|
|
9611
|
+
if (exactMatch) return exactMatch;
|
|
9612
|
+
if (title) {
|
|
9613
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
9614
|
+
const item = items[i];
|
|
9615
|
+
if (!item?.content || item.title !== title) continue;
|
|
9616
|
+
if (normalizedBody(item.content).startsWith(hint)) return item;
|
|
9617
|
+
}
|
|
9618
|
+
}
|
|
9619
|
+
}
|
|
9620
|
+
if (!title) return null;
|
|
9621
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
9622
|
+
if (items[i]?.title === title) return items[i] ?? null;
|
|
9623
|
+
}
|
|
9624
|
+
return null;
|
|
9625
|
+
}
|
|
9626
|
+
|
|
9627
|
+
// src/remote/phone-chats.ts
|
|
9628
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
9629
|
+
|
|
9630
|
+
// src/remote/phone-header.ts
|
|
9631
|
+
var PHONE_HEADER_GIT_ACTIONS = [
|
|
9632
|
+
"commit-push",
|
|
9633
|
+
"create-draft",
|
|
9634
|
+
"resolve-conflicts",
|
|
9635
|
+
"ready-for-review",
|
|
9636
|
+
"merge"
|
|
9637
|
+
];
|
|
9638
|
+
var HIDDEN = {
|
|
9639
|
+
visible: false,
|
|
9640
|
+
branch: "",
|
|
9641
|
+
additions: 0,
|
|
9642
|
+
deletions: 0,
|
|
9643
|
+
unpushed: 0,
|
|
9644
|
+
dirty: false,
|
|
9645
|
+
ready: false,
|
|
9646
|
+
title: ""
|
|
9647
|
+
};
|
|
9648
|
+
function phoneHeaderFrom(thread, diff) {
|
|
9649
|
+
if (isGlobalThread(thread)) return HIDDEN;
|
|
9650
|
+
const branch = thread.branchName.trim().replace(/^thread\//, "");
|
|
9651
|
+
if (!branch) return HIDDEN;
|
|
9652
|
+
const ready = diff != null;
|
|
9653
|
+
const uncommitted = diff?.scopeStats?.uncommitted;
|
|
9654
|
+
const additions = ready ? Math.max(0, Math.trunc(uncommitted?.additions ?? 0)) : 0;
|
|
9655
|
+
const deletions = ready ? Math.max(0, Math.trunc(uncommitted?.deletions ?? 0)) : 0;
|
|
9656
|
+
const unpushed = ready ? Math.max(0, Math.trunc(diff?.unpushed ?? 0)) : 0;
|
|
9657
|
+
const dirty = ready && (Boolean(diff?.dirty) || additions > 0 || deletions > 0);
|
|
9658
|
+
const pr = prSnapshot(thread);
|
|
9659
|
+
const action = ready ? headerAction(pr, { dirty: Boolean(diff?.dirty), unpushed }) : null;
|
|
9660
|
+
const lines = [branch];
|
|
9661
|
+
if (!ready) lines.push("Loading git status\u2026");
|
|
9662
|
+
else if (dirty) lines.push(`Uncommitted +${additions} \u2212${deletions}`);
|
|
9663
|
+
else lines.push("Working tree clean");
|
|
9664
|
+
if (unpushed > 0) lines.push(`${unpushed} unpushed commit${unpushed === 1 ? "" : "s"}`);
|
|
9665
|
+
return {
|
|
9666
|
+
visible: true,
|
|
9667
|
+
branch,
|
|
9668
|
+
additions,
|
|
9669
|
+
deletions,
|
|
9670
|
+
unpushed,
|
|
9671
|
+
dirty,
|
|
9672
|
+
ready,
|
|
9673
|
+
title: lines.join("\n"),
|
|
9674
|
+
...pr.url ? { prUrl: pr.url } : {},
|
|
9675
|
+
...pr.number ? { prNumber: pr.number } : {},
|
|
9676
|
+
...pr.status ? { prStatus: pr.status } : {},
|
|
9677
|
+
...action ? { action: action.id, actionLabel: action.label } : {}
|
|
9678
|
+
};
|
|
9679
|
+
}
|
|
9680
|
+
function phonePrPill(thread) {
|
|
9681
|
+
const pr = prSnapshot(thread);
|
|
9682
|
+
if (!pr.url) return null;
|
|
9683
|
+
return { url: pr.url, number: pr.number, status: pr.status };
|
|
9684
|
+
}
|
|
9685
|
+
async function readPhoneHeader(thread) {
|
|
9686
|
+
const pending = phoneHeaderFrom(thread, null);
|
|
9687
|
+
if (!pending.visible) return pending;
|
|
9688
|
+
try {
|
|
9689
|
+
const diff = await getOrchestrator().diff(thread.id, { includePatches: false });
|
|
9690
|
+
return phoneHeaderFrom(thread, diff);
|
|
9691
|
+
} catch {
|
|
9692
|
+
return pending;
|
|
9693
|
+
}
|
|
9694
|
+
}
|
|
9695
|
+
function prSnapshot(thread) {
|
|
9696
|
+
const url = thread.prUrl?.trim() || "";
|
|
9697
|
+
const number = url.match(/\/pull\/(\d+)/)?.[1] ?? "";
|
|
9698
|
+
const state = (thread.prState ?? "").toUpperCase();
|
|
9699
|
+
const merged = state === "MERGED";
|
|
9700
|
+
const closed = state === "CLOSED";
|
|
9701
|
+
const open = Boolean(url) && !merged && !closed;
|
|
9702
|
+
const draft = open && Boolean(thread.prIsDraft);
|
|
9703
|
+
const inMergeQueue = open && Boolean(thread.prIsInMergeQueue);
|
|
9704
|
+
const mergeable = (thread.prMergeable ?? "").toUpperCase();
|
|
9705
|
+
const mergeState = (thread.prMergeStateStatus ?? "").toUpperCase();
|
|
9706
|
+
const mergeConflicts = open && !inMergeQueue && (mergeable === "CONFLICTING" || mergeState === "DIRTY");
|
|
9707
|
+
const branchBehind = open && !inMergeQueue && !mergeConflicts && mergeState === "BEHIND";
|
|
9708
|
+
const checksFailed = Boolean(thread.prChecksFailed);
|
|
9709
|
+
const review = (open && !draft ? thread.prReviewDecision : "") ?? "";
|
|
9710
|
+
return {
|
|
9711
|
+
url,
|
|
9712
|
+
number,
|
|
9713
|
+
status: pillStatus({ url, merged, closed, draft, inMergeQueue, mergeConflicts, branchBehind, checksFailed, review }),
|
|
9714
|
+
merged,
|
|
9715
|
+
closed,
|
|
9716
|
+
open,
|
|
9717
|
+
draft,
|
|
9718
|
+
cowboy: Boolean(thread.cowboy),
|
|
9719
|
+
inMergeQueue,
|
|
9720
|
+
mergeConflicts,
|
|
9721
|
+
branchBehind,
|
|
9722
|
+
checksFailed,
|
|
9723
|
+
review: review.toUpperCase()
|
|
9724
|
+
};
|
|
9725
|
+
}
|
|
9726
|
+
function pillStatus(pr) {
|
|
9727
|
+
if (!pr.url) return "";
|
|
9728
|
+
if (pr.merged) return "Merged";
|
|
9729
|
+
if (pr.closed) return "Closed";
|
|
9730
|
+
if (pr.inMergeQueue) return "Queued";
|
|
9731
|
+
if (pr.mergeConflicts) return "Merge conflicts";
|
|
9732
|
+
if (pr.branchBehind) return "Behind base";
|
|
9733
|
+
if (pr.draft) return "Draft";
|
|
9734
|
+
if (pr.checksFailed) return "Checks failing";
|
|
9735
|
+
if (pr.review === "CHANGES_REQUESTED") return "Changes requested";
|
|
9736
|
+
if (pr.review !== "APPROVED") return "Needs approval";
|
|
9737
|
+
return "Approved";
|
|
9738
|
+
}
|
|
9739
|
+
function headerAction(pr, diff) {
|
|
9740
|
+
const hasLocalChanges = diff.dirty || diff.unpushed > 0;
|
|
9741
|
+
const originInSync = !hasLocalChanges;
|
|
9742
|
+
if (pr.merged) return null;
|
|
9743
|
+
if (pr.closed) return { id: "open-pr", label: "Closed" };
|
|
9744
|
+
if (pr.cowboy) {
|
|
9745
|
+
return hasLocalChanges ? { id: "commit-push", label: "Commit & push" } : { id: "commit-push", label: "Push" };
|
|
9746
|
+
}
|
|
9747
|
+
if (!pr.url) return { id: "create-draft", label: "Create PR" };
|
|
9748
|
+
if (pr.inMergeQueue) return { id: "open-pr", label: "Queued" };
|
|
9749
|
+
if (pr.mergeConflicts) return { id: "resolve-conflicts", label: "Resolve" };
|
|
9750
|
+
if (pr.branchBehind) return { id: "resolve-conflicts", label: "Update" };
|
|
9751
|
+
if (hasLocalChanges) return { id: "commit-push", label: "Commit & push" };
|
|
9752
|
+
if (pr.draft) {
|
|
9753
|
+
return originInSync ? { id: "ready-for-review", label: "Ready for review" } : { id: "commit-push", label: "Commit & push" };
|
|
9754
|
+
}
|
|
9755
|
+
if (pr.checksFailed) return { id: "open-checks", label: "Checks failing" };
|
|
9756
|
+
if (pr.review === "CHANGES_REQUESTED") return { id: "open-pr", label: "Changes requested" };
|
|
9757
|
+
if (pr.review !== "APPROVED") return { id: "open-pr", label: "Needs approval" };
|
|
9758
|
+
return { id: "merge", label: "Merge" };
|
|
9759
|
+
}
|
|
9760
|
+
|
|
9761
|
+
// src/remote/phone-panes.ts
|
|
9762
|
+
var MAX_ROWS = 200;
|
|
9763
|
+
function parsePhonePane(value, chatId) {
|
|
9764
|
+
const pane = value && typeof value === "object" ? value.pane : void 0;
|
|
9765
|
+
if (pane !== "files" && pane !== "changes" && pane !== "checks") return "invalid";
|
|
9766
|
+
const scopeRaw = value.scope;
|
|
9767
|
+
const scope = scopeRaw === "uncommitted" || scopeRaw === "commits" ? scopeRaw : void 0;
|
|
9768
|
+
return { op: "pane", chatId, pane, ...scope && pane === "changes" ? { scope } : {} };
|
|
9769
|
+
}
|
|
9770
|
+
async function readPhonePane(thread, pane, scope) {
|
|
9771
|
+
try {
|
|
9772
|
+
if (pane === "files") return await readFiles(thread.id);
|
|
9773
|
+
if (pane === "changes") return await readChanges(thread.id, scope ?? "commits");
|
|
9774
|
+
return await readChecks(thread.id);
|
|
9775
|
+
} catch (err) {
|
|
9776
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9777
|
+
return { rows: [], note: message };
|
|
9778
|
+
}
|
|
9779
|
+
}
|
|
9780
|
+
async function readFiles(threadId) {
|
|
9781
|
+
const paths = await getOrchestrator().listFiles(threadId);
|
|
9782
|
+
const shown = paths.slice(0, MAX_ROWS);
|
|
9783
|
+
return {
|
|
9784
|
+
rows: shown.map((path2) => ({ label: path2, path: path2 })),
|
|
9785
|
+
...paths.length > shown.length ? { note: `${paths.length - shown.length} more files on the Mac` } : {}
|
|
9786
|
+
};
|
|
9787
|
+
}
|
|
9788
|
+
async function readChanges(threadId, scope) {
|
|
9789
|
+
const diff = await getOrchestrator().diff(threadId, { includePatches: false, scope });
|
|
9790
|
+
const shown = diff.files.slice(0, MAX_ROWS);
|
|
9791
|
+
return {
|
|
9792
|
+
rows: shown.map((file) => ({
|
|
9793
|
+
label: file.path,
|
|
9794
|
+
path: file.path,
|
|
9795
|
+
detail: changeDetail(file.additions, file.deletions)
|
|
9796
|
+
})),
|
|
9797
|
+
...diff.files.length > shown.length ? { note: `${diff.files.length - shown.length} more changed files` } : {}
|
|
9798
|
+
};
|
|
9799
|
+
}
|
|
9800
|
+
function changeDetail(additions, deletions) {
|
|
9801
|
+
const add = additions ?? 0;
|
|
9802
|
+
const del = deletions ?? 0;
|
|
9803
|
+
if (add === 0 && del === 0) return void 0;
|
|
9804
|
+
return `+${add} \u2212${del}`;
|
|
9805
|
+
}
|
|
9806
|
+
async function readChecks(threadId) {
|
|
9807
|
+
const checks = await getOrchestrator().getPrChecks(threadId);
|
|
9808
|
+
if (!checks) return { rows: [], note: "No pull request on this branch." };
|
|
9809
|
+
const shown = checks.slice(0, MAX_ROWS);
|
|
9810
|
+
return {
|
|
9811
|
+
rows: shown.map((check) => ({
|
|
9812
|
+
label: check.name,
|
|
9813
|
+
detail: check.bucket || check.state,
|
|
9814
|
+
...check.link ? { href: check.link } : {}
|
|
9815
|
+
}))
|
|
9816
|
+
};
|
|
9817
|
+
}
|
|
9462
9818
|
|
|
9463
9819
|
// src/remote/phone-defaults.ts
|
|
9464
9820
|
function row(resolved) {
|
|
@@ -9476,6 +9832,29 @@ function phoneAccountDefaults() {
|
|
|
9476
9832
|
};
|
|
9477
9833
|
}
|
|
9478
9834
|
|
|
9835
|
+
// src/remote/phone-viewer-login.ts
|
|
9836
|
+
var TTL_MS = 5 * 6e4;
|
|
9837
|
+
var cached = "";
|
|
9838
|
+
var fetchedAt = 0;
|
|
9839
|
+
var inflight2 = null;
|
|
9840
|
+
function phoneViewerLogin() {
|
|
9841
|
+
return cached;
|
|
9842
|
+
}
|
|
9843
|
+
function refreshPhoneViewerLogin(force = false) {
|
|
9844
|
+
const now = Date.now();
|
|
9845
|
+
if (!force && fetchedAt && now - fetchedAt < TTL_MS) return Promise.resolve(cached);
|
|
9846
|
+
if (inflight2) return inflight2;
|
|
9847
|
+
const run2 = getGitHubStatus().then((status) => {
|
|
9848
|
+
cached = status.login?.trim() ?? "";
|
|
9849
|
+
fetchedAt = Date.now();
|
|
9850
|
+
return cached;
|
|
9851
|
+
}).catch(() => cached).finally(() => {
|
|
9852
|
+
if (inflight2 === run2) inflight2 = null;
|
|
9853
|
+
});
|
|
9854
|
+
inflight2 = run2;
|
|
9855
|
+
return run2;
|
|
9856
|
+
}
|
|
9857
|
+
|
|
9479
9858
|
// src/remote/phone-stream.ts
|
|
9480
9859
|
var PHONE_STREAM_FLUSH_MS = 80;
|
|
9481
9860
|
function phoneStreamFrame(parts) {
|
|
@@ -9536,10 +9915,61 @@ function listPhoneMessages(thread) {
|
|
|
9536
9915
|
return live.slice(-TRANSCRIPT_LIMIT);
|
|
9537
9916
|
}
|
|
9538
9917
|
|
|
9539
|
-
// src/remote/phone-
|
|
9540
|
-
var PHONE_CONTROL_PREFIX = "\0sb.phone\n";
|
|
9918
|
+
// src/remote/phone-history.ts
|
|
9541
9919
|
var PREVIEW_LIMIT = 90;
|
|
9542
9920
|
var HISTORY_PAGE_SIZE = 40;
|
|
9921
|
+
function previewOf(thread) {
|
|
9922
|
+
const messages = listPhoneMessages(thread);
|
|
9923
|
+
const last = messages[messages.length - 1];
|
|
9924
|
+
if (!last) return "";
|
|
9925
|
+
return clip(last.text.replace(/\s+/g, " ").trim(), PREVIEW_LIMIT);
|
|
9926
|
+
}
|
|
9927
|
+
function repoName(repoPath) {
|
|
9928
|
+
const parts = repoPath.replace(/\/$/, "").split("/");
|
|
9929
|
+
return parts[parts.length - 1] || repoPath;
|
|
9930
|
+
}
|
|
9931
|
+
function isProjectPath(path2) {
|
|
9932
|
+
if (!path2 || path2 === "/" || path2 === ".") return false;
|
|
9933
|
+
return !isGlobalThread({ repoPath: path2 });
|
|
9934
|
+
}
|
|
9935
|
+
function historyStamp(thread) {
|
|
9936
|
+
return thread.archivedAt || thread.updatedAt;
|
|
9937
|
+
}
|
|
9938
|
+
function historyCursor(chat) {
|
|
9939
|
+
return `${chat.updatedAt} ${chat.id}`;
|
|
9940
|
+
}
|
|
9941
|
+
function listPhoneHistory(opts) {
|
|
9942
|
+
const query = opts?.query?.trim().toLowerCase() ?? "";
|
|
9943
|
+
const after = opts?.after?.trim() ?? "";
|
|
9944
|
+
const all = listThreads({ includeArchived: true }).filter((thread) => thread.status === "archived").filter((thread) => isGlobalThread(thread) || isProjectPath(thread.repoPath)).sort((a, b) => historyStamp(a) < historyStamp(b) ? 1 : historyStamp(a) > historyStamp(b) ? -1 : 0).map((thread) => ({
|
|
9945
|
+
id: thread.id,
|
|
9946
|
+
title: thread.title?.trim() || "Untitled",
|
|
9947
|
+
preview: previewOf(thread),
|
|
9948
|
+
updatedAt: historyStamp(thread),
|
|
9949
|
+
where: isGlobalThread(thread) ? "Orchestration" : repoName(thread.repoPath),
|
|
9950
|
+
agent: thread.agent
|
|
9951
|
+
})).filter((chat) => {
|
|
9952
|
+
if (!query) return true;
|
|
9953
|
+
return `${chat.title} ${chat.where} ${chat.agent} ${chat.preview}`.toLowerCase().includes(query);
|
|
9954
|
+
});
|
|
9955
|
+
let start = 0;
|
|
9956
|
+
if (after) {
|
|
9957
|
+
const index = all.findIndex((chat) => historyCursor(chat) === after);
|
|
9958
|
+
start = index >= 0 ? index + 1 : all.length;
|
|
9959
|
+
}
|
|
9960
|
+
const chats = all.slice(start, start + HISTORY_PAGE_SIZE);
|
|
9961
|
+
const last = chats.at(-1);
|
|
9962
|
+
const more = start + chats.length < all.length;
|
|
9963
|
+
return {
|
|
9964
|
+
chats,
|
|
9965
|
+
total: all.length,
|
|
9966
|
+
...more && last ? { next: historyCursor(last) } : {}
|
|
9967
|
+
};
|
|
9968
|
+
}
|
|
9969
|
+
|
|
9970
|
+
// src/remote/phone-chats.ts
|
|
9971
|
+
var PHONE_CONTROL_PREFIX = "\0sb.phone\n";
|
|
9972
|
+
var PREVIEW_LIMIT2 = 90;
|
|
9543
9973
|
var AGENT_KINDS = /* @__PURE__ */ new Set(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
9544
9974
|
var MAX_PHONE_FILES = 8;
|
|
9545
9975
|
var MAX_PHONE_FILE_CHARS = Math.ceil(8 * 1024 * 1024 * 4 / 3);
|
|
@@ -9569,7 +9999,7 @@ function parsePhoneControl(value) {
|
|
|
9569
9999
|
}
|
|
9570
10000
|
if (op === "sources") {
|
|
9571
10001
|
const repoPath = textField(value, "repoPath");
|
|
9572
|
-
if (!repoPath || !
|
|
10002
|
+
if (!repoPath || !isProjectPath2(repoPath)) return "invalid";
|
|
9573
10003
|
const query = textField(value, "query");
|
|
9574
10004
|
return { op: "sources", repoPath, ...query ? { query } : {} };
|
|
9575
10005
|
}
|
|
@@ -9587,7 +10017,15 @@ function parsePhoneControl(value) {
|
|
|
9587
10017
|
if (op === "create") return parsePhoneCreate(value);
|
|
9588
10018
|
const chatId = typeof value.chatId === "string" ? value.chatId.trim() : "";
|
|
9589
10019
|
if (!chatId) return "invalid";
|
|
9590
|
-
if (op === "open" || op === "archive" || op === "restore" || op === "stop"
|
|
10020
|
+
if (op === "open" || op === "archive" || op === "restore" || op === "stop" || op === "review" || op === "header") {
|
|
10021
|
+
return { op, chatId };
|
|
10022
|
+
}
|
|
10023
|
+
if (op === "git") {
|
|
10024
|
+
const action = textField(value, "action");
|
|
10025
|
+
if (!PHONE_HEADER_GIT_ACTIONS.includes(action)) return "invalid";
|
|
10026
|
+
return { op: "git", chatId, action };
|
|
10027
|
+
}
|
|
10028
|
+
if (op === "pane") return parsePhonePane(value, chatId);
|
|
9591
10029
|
if (op === "open-artifact") return parseOpenArtifact(value, chatId);
|
|
9592
10030
|
if (op === "open-file" || op === "media") {
|
|
9593
10031
|
const path2 = textField(value, "path");
|
|
@@ -9727,7 +10165,7 @@ function parsePhoneCreate(value) {
|
|
|
9727
10165
|
}
|
|
9728
10166
|
if (where === "project") {
|
|
9729
10167
|
const repoPath = textField(value, "repoPath");
|
|
9730
|
-
if (!repoPath || !
|
|
10168
|
+
if (!repoPath || !isProjectPath2(repoPath)) return "invalid";
|
|
9731
10169
|
const sourceRaw = textField(value, "sourceType");
|
|
9732
10170
|
let sourceType;
|
|
9733
10171
|
if (sourceRaw === "branch" || sourceRaw === "pr" || sourceRaw === "ticket") sourceType = sourceRaw;
|
|
@@ -9750,94 +10188,70 @@ function parsePhoneCreate(value) {
|
|
|
9750
10188
|
return "invalid";
|
|
9751
10189
|
}
|
|
9752
10190
|
function summarize(thread) {
|
|
10191
|
+
const pr = phonePrPill(thread);
|
|
9753
10192
|
return {
|
|
9754
10193
|
id: thread.id,
|
|
9755
10194
|
title: thread.title?.trim() || "Untitled",
|
|
9756
10195
|
status: thread.status,
|
|
9757
|
-
preview:
|
|
10196
|
+
preview: previewOf2(thread),
|
|
9758
10197
|
updatedAt: thread.updatedAt,
|
|
9759
|
-
agent: thread.agent
|
|
10198
|
+
agent: thread.agent,
|
|
10199
|
+
...pr?.url ? { prUrl: pr.url } : {},
|
|
10200
|
+
...pr?.number ? { prNumber: pr.number } : {},
|
|
10201
|
+
...pr?.status ? { prStatus: pr.status } : {}
|
|
9760
10202
|
};
|
|
9761
10203
|
}
|
|
9762
|
-
function
|
|
10204
|
+
function previewOf2(thread) {
|
|
9763
10205
|
const messages = listPhoneMessages(thread);
|
|
9764
10206
|
const last = messages[messages.length - 1];
|
|
9765
10207
|
if (!last) return "";
|
|
9766
|
-
return clip(last.text.replace(/\s+/g, " ").trim(),
|
|
10208
|
+
return clip(last.text.replace(/\s+/g, " ").trim(), PREVIEW_LIMIT2);
|
|
9767
10209
|
}
|
|
9768
|
-
function
|
|
10210
|
+
function repoName2(repoPath) {
|
|
9769
10211
|
const parts = repoPath.replace(/\/$/, "").split("/");
|
|
9770
10212
|
return parts[parts.length - 1] || repoPath;
|
|
9771
10213
|
}
|
|
9772
|
-
function
|
|
10214
|
+
function isProjectPath2(path2) {
|
|
9773
10215
|
if (!path2 || path2 === "/" || path2 === ".") return false;
|
|
9774
10216
|
return !isGlobalThread({ repoPath: path2 });
|
|
9775
10217
|
}
|
|
9776
10218
|
function listPhoneChats() {
|
|
9777
10219
|
return listGlobalThreads().slice().sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).map(summarize);
|
|
9778
10220
|
}
|
|
9779
|
-
function listPhoneSidebar() {
|
|
10221
|
+
function listPhoneSidebar(viewerLogin = phoneViewerLogin()) {
|
|
10222
|
+
const settings = loadAppSettings();
|
|
9780
10223
|
const threads = listThreads();
|
|
9781
10224
|
const byRepo = /* @__PURE__ */ new Map();
|
|
9782
10225
|
for (const thread of threads) {
|
|
9783
|
-
if (!
|
|
10226
|
+
if (!isProjectPath2(thread.repoPath) || isRemovedWorkspace(thread.repoPath)) continue;
|
|
9784
10227
|
const list = byRepo.get(thread.repoPath) ?? [];
|
|
9785
10228
|
list.push(thread);
|
|
9786
10229
|
byRepo.set(thread.repoPath, list);
|
|
9787
10230
|
}
|
|
9788
10231
|
for (const workspace of listWorkspaces()) {
|
|
9789
|
-
if (!
|
|
10232
|
+
if (!isProjectPath2(workspace.path) || byRepo.has(workspace.path)) continue;
|
|
9790
10233
|
byRepo.set(workspace.path, []);
|
|
9791
10234
|
}
|
|
9792
|
-
const projects = [...byRepo.entries()].sort(([a], [b]) =>
|
|
9793
|
-
name:
|
|
10235
|
+
const projects = [...byRepo.entries()].sort(([a], [b]) => repoName2(a).localeCompare(repoName2(b))).map(([path2, repoThreads]) => ({
|
|
10236
|
+
name: repoName2(path2),
|
|
9794
10237
|
path: path2,
|
|
9795
10238
|
worktrees: groupHomeBoardWorktrees(repoThreads).map((group) => {
|
|
9796
10239
|
const tags = workspaceTagsFromGroup(group);
|
|
10240
|
+
const badge = classifyWorktreeListBadges(
|
|
10241
|
+
group,
|
|
10242
|
+
viewerLogin,
|
|
10243
|
+
resolveProjectReviewLabel(settings, path2)
|
|
10244
|
+
)[0];
|
|
9797
10245
|
return {
|
|
9798
10246
|
label: worktreeDisplayLabelForGroup(group),
|
|
9799
10247
|
chats: group.map(summarize),
|
|
9800
|
-
...tags.length > 0 ? { tags } : {}
|
|
10248
|
+
...tags.length > 0 ? { tags } : {},
|
|
10249
|
+
...badge ? { badge: { id: badge.id, label: badge.label, title: badge.title } } : {}
|
|
9801
10250
|
};
|
|
9802
10251
|
})
|
|
9803
10252
|
}));
|
|
9804
10253
|
return { orchestration: listPhoneChats(), projects, defaults: phoneAccountDefaults() };
|
|
9805
10254
|
}
|
|
9806
|
-
function historyStamp(thread) {
|
|
9807
|
-
return thread.archivedAt || thread.updatedAt;
|
|
9808
|
-
}
|
|
9809
|
-
function historyCursor(chat) {
|
|
9810
|
-
return `${chat.updatedAt} ${chat.id}`;
|
|
9811
|
-
}
|
|
9812
|
-
function historyQueryMatch(chat, query) {
|
|
9813
|
-
if (!query) return true;
|
|
9814
|
-
return `${chat.title} ${chat.where} ${chat.agent} ${chat.preview}`.toLowerCase().includes(query);
|
|
9815
|
-
}
|
|
9816
|
-
function listPhoneHistory(opts) {
|
|
9817
|
-
const query = opts?.query?.trim().toLowerCase() ?? "";
|
|
9818
|
-
const after = opts?.after?.trim() ?? "";
|
|
9819
|
-
const all = listThreads({ includeArchived: true }).filter((thread) => thread.status === "archived").filter((thread) => isGlobalThread(thread) || isProjectPath(thread.repoPath)).sort((a, b) => historyStamp(a) < historyStamp(b) ? 1 : historyStamp(a) > historyStamp(b) ? -1 : 0).map((thread) => ({
|
|
9820
|
-
id: thread.id,
|
|
9821
|
-
title: thread.title?.trim() || "Untitled",
|
|
9822
|
-
preview: previewOf(thread),
|
|
9823
|
-
updatedAt: historyStamp(thread),
|
|
9824
|
-
where: isGlobalThread(thread) ? "Orchestration" : repoName(thread.repoPath),
|
|
9825
|
-
agent: thread.agent
|
|
9826
|
-
})).filter((chat) => historyQueryMatch(chat, query));
|
|
9827
|
-
let start = 0;
|
|
9828
|
-
if (after) {
|
|
9829
|
-
const index = all.findIndex((chat) => historyCursor(chat) === after);
|
|
9830
|
-
start = index >= 0 ? index + 1 : all.length;
|
|
9831
|
-
}
|
|
9832
|
-
const chats = all.slice(start, start + HISTORY_PAGE_SIZE);
|
|
9833
|
-
const last = chats.at(-1);
|
|
9834
|
-
const more = start + chats.length < all.length;
|
|
9835
|
-
return {
|
|
9836
|
-
chats,
|
|
9837
|
-
total: all.length,
|
|
9838
|
-
...more && last ? { next: historyCursor(last) } : {}
|
|
9839
|
-
};
|
|
9840
|
-
}
|
|
9841
10255
|
function matchPhoneThread(chatId) {
|
|
9842
10256
|
const id = chatId.trim();
|
|
9843
10257
|
if (!id) return null;
|
|
@@ -9850,7 +10264,7 @@ function matchPhoneThread(chatId) {
|
|
|
9850
10264
|
}
|
|
9851
10265
|
function visiblePhoneThread(thread) {
|
|
9852
10266
|
if (isGlobalThread(thread)) return true;
|
|
9853
|
-
return
|
|
10267
|
+
return isProjectPath2(thread.repoPath);
|
|
9854
10268
|
}
|
|
9855
10269
|
function findPhoneChat(chatId) {
|
|
9856
10270
|
const thread = matchPhoneThread(chatId);
|
|
@@ -9916,7 +10330,7 @@ function projectPlace(thread) {
|
|
|
9916
10330
|
}
|
|
9917
10331
|
function assertKnownProject(repoPath) {
|
|
9918
10332
|
const path2 = repoPath.trim();
|
|
9919
|
-
if (!
|
|
10333
|
+
if (!isProjectPath2(path2)) throw new Error("Pick a project on this Mac.");
|
|
9920
10334
|
const known = listWorkspaces().some((workspace) => sameRepoPath(workspace.path, path2)) || listThreads().some(
|
|
9921
10335
|
(thread) => thread.status !== "archived" && sameRepoPath(thread.repoPath, path2)
|
|
9922
10336
|
);
|
|
@@ -10019,6 +10433,55 @@ function archivePhoneChat(chatId) {
|
|
|
10019
10433
|
return true;
|
|
10020
10434
|
}
|
|
10021
10435
|
|
|
10436
|
+
// src/remote/phone-workspace-ops.ts
|
|
10437
|
+
function dispatchPhoneWorkspace(cmd, emit, publishOpened) {
|
|
10438
|
+
if (cmd.op === "pane") {
|
|
10439
|
+
const thread = readPhoneThread(cmd.chatId);
|
|
10440
|
+
if (!thread) {
|
|
10441
|
+
emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
10442
|
+
return;
|
|
10443
|
+
}
|
|
10444
|
+
void readPhonePane(thread, cmd.pane, cmd.scope).then((body) => {
|
|
10445
|
+
emit({
|
|
10446
|
+
op: "pane",
|
|
10447
|
+
chatId: cmd.chatId,
|
|
10448
|
+
pane: cmd.pane,
|
|
10449
|
+
rows: body.rows,
|
|
10450
|
+
...body.note ? { note: body.note } : {}
|
|
10451
|
+
});
|
|
10452
|
+
});
|
|
10453
|
+
return;
|
|
10454
|
+
}
|
|
10455
|
+
if (cmd.op === "header") {
|
|
10456
|
+
const thread = readPhoneThread(cmd.chatId);
|
|
10457
|
+
if (!thread) {
|
|
10458
|
+
emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
10459
|
+
return;
|
|
10460
|
+
}
|
|
10461
|
+
void readPhoneHeader(thread).then((header) => {
|
|
10462
|
+
emit({ op: "header", chatId: cmd.chatId, ...header });
|
|
10463
|
+
});
|
|
10464
|
+
return;
|
|
10465
|
+
}
|
|
10466
|
+
if (cmd.op === "git") {
|
|
10467
|
+
void getOrchestrator().askGit(cmd.chatId, cmd.action).then((tab) => publishOpened(tab.id)).catch((err) => {
|
|
10468
|
+
emit({
|
|
10469
|
+
op: "error",
|
|
10470
|
+
chatId: cmd.chatId,
|
|
10471
|
+
message: err instanceof Error ? err.message : String(err)
|
|
10472
|
+
});
|
|
10473
|
+
});
|
|
10474
|
+
return;
|
|
10475
|
+
}
|
|
10476
|
+
void getOrchestrator().requestReview(cmd.chatId).then((tab) => publishOpened(tab.id)).catch((err) => {
|
|
10477
|
+
emit({
|
|
10478
|
+
op: "error",
|
|
10479
|
+
chatId: cmd.chatId,
|
|
10480
|
+
message: err instanceof Error ? err.message : String(err)
|
|
10481
|
+
});
|
|
10482
|
+
});
|
|
10483
|
+
}
|
|
10484
|
+
|
|
10022
10485
|
// src/remote/phone-sources.ts
|
|
10023
10486
|
function sourceWarning(label, err) {
|
|
10024
10487
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -10584,10 +11047,25 @@ function handlePhoneControl(cmd, opts) {
|
|
|
10584
11047
|
emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That file is not in this worktree." });
|
|
10585
11048
|
return;
|
|
10586
11049
|
}
|
|
10587
|
-
|
|
10588
|
-
|
|
11050
|
+
const preview = phoneFilePreview(thread.worktreePath, relative, kind === "dir" ? "dir" : "file");
|
|
11051
|
+
if (!preview) {
|
|
11052
|
+
emitControl(opts, {
|
|
11053
|
+
op: "error",
|
|
11054
|
+
chatId: cmd.chatId,
|
|
11055
|
+
message: "That file could not be opened on the phone."
|
|
11056
|
+
});
|
|
11057
|
+
return;
|
|
11058
|
+
}
|
|
11059
|
+
emitControl(opts, {
|
|
11060
|
+
op: "file",
|
|
11061
|
+
chatId: cmd.chatId,
|
|
10589
11062
|
path: relative,
|
|
10590
|
-
|
|
11063
|
+
content: preview.content,
|
|
11064
|
+
encoding: preview.encoding,
|
|
11065
|
+
...preview.binary ? { binary: true } : {},
|
|
11066
|
+
...preview.directory ? { directory: true } : {},
|
|
11067
|
+
...preview.truncated ? { truncated: true } : {},
|
|
11068
|
+
...preview.mime ? { mime: preview.mime } : {},
|
|
10591
11069
|
...cmd.startLine != null ? { startLine: cmd.startLine } : {},
|
|
10592
11070
|
...cmd.endLine != null ? { endLine: cmd.endLine } : {}
|
|
10593
11071
|
});
|
|
@@ -10602,13 +11080,34 @@ function handlePhoneControl(cmd, opts) {
|
|
|
10602
11080
|
emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
10603
11081
|
return;
|
|
10604
11082
|
}
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
|
|
11083
|
+
const liveParts = readTurnLiveParts(thread.id);
|
|
11084
|
+
const docs = phoneArtifactDocuments([
|
|
11085
|
+
...(thread.messages ?? []).filter((message) => message.role === "agent").map((message) => ({ text: message.text.trim(), parts: message.parts })),
|
|
11086
|
+
{ text: visibleAssistantText(void 0, liveParts), parts: liveParts }
|
|
11087
|
+
]);
|
|
11088
|
+
const body = phoneArtifactReply(docs, { title: cmd.title, ...cmd.hint ? { hint: cmd.hint } : {} });
|
|
11089
|
+
if (!body) {
|
|
11090
|
+
emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That artifact is not in this chat." });
|
|
11091
|
+
return;
|
|
11092
|
+
}
|
|
11093
|
+
emitControl(opts, {
|
|
11094
|
+
op: "artifact",
|
|
11095
|
+
chatId: cmd.chatId,
|
|
11096
|
+
title: body.title,
|
|
11097
|
+
kind: body.kind,
|
|
11098
|
+
content: body.content,
|
|
11099
|
+
...body.truncated ? { truncated: true } : {}
|
|
10609
11100
|
});
|
|
10610
11101
|
return;
|
|
10611
11102
|
}
|
|
11103
|
+
if (cmd.op === "pane" || cmd.op === "header" || cmd.op === "git" || cmd.op === "review") {
|
|
11104
|
+
dispatchPhoneWorkspace(
|
|
11105
|
+
cmd,
|
|
11106
|
+
(reply) => emitControl(opts, reply),
|
|
11107
|
+
(chatId2) => publishOpenedChat(opts, chatId2)
|
|
11108
|
+
);
|
|
11109
|
+
return;
|
|
11110
|
+
}
|
|
10612
11111
|
if (cmd.op === "stop") {
|
|
10613
11112
|
if (!readPhoneThread(cmd.chatId)) {
|
|
10614
11113
|
emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
@@ -10696,7 +11195,9 @@ function chatKey(row2) {
|
|
|
10696
11195
|
function phoneBoardKey(sidebar) {
|
|
10697
11196
|
const orchestration = sidebar.orchestration.map(chatKey).join("\n");
|
|
10698
11197
|
const projects = sidebar.projects.map((project) => {
|
|
10699
|
-
const trees = project.worktrees.map(
|
|
11198
|
+
const trees = project.worktrees.map(
|
|
11199
|
+
(tree) => `${tree.label} ${tree.badge?.id ?? ""} ${(tree.tags ?? []).join(",")} ${tree.chats.map(chatKey).join("\n")}`
|
|
11200
|
+
).join("\n");
|
|
10700
11201
|
return `${project.path} ${project.name}
|
|
10701
11202
|
${trees}`;
|
|
10702
11203
|
}).join("\n---\n");
|
|
@@ -10774,10 +11275,23 @@ function startPhoneBoardSync(opts) {
|
|
|
10774
11275
|
const unsubscribe = (opts.subscribe ?? subscribePhoneBoard)(schedule);
|
|
10775
11276
|
const offEvents = (opts.events ?? ((listener) => getOrchestrator().on(listener)))(handle);
|
|
10776
11277
|
push();
|
|
11278
|
+
let loginTimer = null;
|
|
11279
|
+
const refreshViewerLogin = opts.refreshViewerLogin;
|
|
11280
|
+
if (refreshViewerLogin) {
|
|
11281
|
+
const afterLogin = () => {
|
|
11282
|
+
void refreshViewerLogin().then(() => {
|
|
11283
|
+
if (!stopped) push();
|
|
11284
|
+
});
|
|
11285
|
+
};
|
|
11286
|
+
afterLogin();
|
|
11287
|
+
loginTimer = setInterval(afterLogin, opts.viewerLoginIntervalMs ?? 5 * 6e4);
|
|
11288
|
+
}
|
|
10777
11289
|
return () => {
|
|
10778
11290
|
stopped = true;
|
|
10779
11291
|
if (timer) clearTimeout(timer);
|
|
10780
11292
|
timer = null;
|
|
11293
|
+
if (loginTimer) clearInterval(loginTimer);
|
|
11294
|
+
loginTimer = null;
|
|
10781
11295
|
unsubscribe();
|
|
10782
11296
|
offEvents();
|
|
10783
11297
|
};
|
|
@@ -10844,7 +11358,8 @@ function runRemoteHost(opts) {
|
|
|
10844
11358
|
},
|
|
10845
11359
|
onRunningChat: (chatId) => {
|
|
10846
11360
|
deliverPhoneTurn({ onOutbound: emit }, chatId);
|
|
10847
|
-
}
|
|
11361
|
+
},
|
|
11362
|
+
refreshViewerLogin: () => refreshPhoneViewerLogin(true)
|
|
10848
11363
|
});
|
|
10849
11364
|
};
|
|
10850
11365
|
const send3 = (msg) => {
|
|
@@ -11023,14 +11538,14 @@ import { randomBytes as randomBytes7 } from "crypto";
|
|
|
11023
11538
|
|
|
11024
11539
|
// src/relay/accounts.ts
|
|
11025
11540
|
import { createHash as createHash2, randomBytes as randomBytes6, randomUUID as randomUUID8, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
11026
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
11541
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, realpathSync } from "fs";
|
|
11027
11542
|
import { createRequire } from "module";
|
|
11028
|
-
import { dirname, join as
|
|
11543
|
+
import { dirname, join as join7 } from "path";
|
|
11029
11544
|
var GITHUB_ACCOUNT_ISSUER = "https://github.com";
|
|
11030
11545
|
var RELAY_DEVICE_CAP = 3;
|
|
11031
11546
|
function thisModuleFile() {
|
|
11032
11547
|
const cjsFile = typeof __filename !== "undefined" ? __filename : "";
|
|
11033
|
-
return cjsFile || process.argv[1] ||
|
|
11548
|
+
return cjsFile || process.argv[1] || join7(process.cwd(), "package.json");
|
|
11034
11549
|
}
|
|
11035
11550
|
function openDatabase(filePath) {
|
|
11036
11551
|
const req = createRequire(thisModuleFile());
|
|
@@ -11068,7 +11583,7 @@ function assertRelayAccountsDurable(filePath) {
|
|
|
11068
11583
|
if (process.env.NODE_ENV !== "production") return;
|
|
11069
11584
|
if (process.platform !== "linux") return;
|
|
11070
11585
|
const dir = realpathSync(dirname(filePath));
|
|
11071
|
-
const mounts =
|
|
11586
|
+
const mounts = readFileSync6("/proc/mounts", "utf8");
|
|
11072
11587
|
const point = longestMountPoint(dir, mounts);
|
|
11073
11588
|
if (point === "/") {
|
|
11074
11589
|
throw new Error(
|
|
@@ -11106,9 +11621,15 @@ var RelayAccountStore = class {
|
|
|
11106
11621
|
account_id TEXT NOT NULL,
|
|
11107
11622
|
host_secret_hash TEXT NOT NULL,
|
|
11108
11623
|
device_label TEXT NOT NULL,
|
|
11109
|
-
created_at INTEGER NOT NULL
|
|
11624
|
+
created_at INTEGER NOT NULL,
|
|
11625
|
+
session_token_hash TEXT
|
|
11110
11626
|
);
|
|
11111
11627
|
`);
|
|
11628
|
+
const cols = this.db.prepare("PRAGMA table_info(devices)").all();
|
|
11629
|
+
if (!cols.some((col) => col.name === "session_token_hash")) {
|
|
11630
|
+
this.db.exec("ALTER TABLE devices ADD COLUMN session_token_hash TEXT");
|
|
11631
|
+
}
|
|
11632
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS devices_session ON devices(session_token_hash)");
|
|
11112
11633
|
}
|
|
11113
11634
|
close() {
|
|
11114
11635
|
this.db.close();
|
|
@@ -11237,6 +11758,20 @@ var RelayAccountStore = class {
|
|
|
11237
11758
|
throw err;
|
|
11238
11759
|
}
|
|
11239
11760
|
}
|
|
11761
|
+
/** Store a phone pairing when it is created. A later process looks it up on a memory miss. */
|
|
11762
|
+
savePhoneSession(deviceId, sessionToken) {
|
|
11763
|
+
const id = deviceId.trim();
|
|
11764
|
+
const token = sessionToken.trim();
|
|
11765
|
+
if (!id || !token) return;
|
|
11766
|
+
this.db.prepare("UPDATE devices SET session_token_hash = ? WHERE device_id = ?").run(sha256(token), id);
|
|
11767
|
+
}
|
|
11768
|
+
findPhoneSession(sessionToken) {
|
|
11769
|
+
const token = sessionToken.trim();
|
|
11770
|
+
if (!token) return null;
|
|
11771
|
+
const row2 = this.db.prepare("SELECT device_id, device_label FROM devices WHERE session_token_hash = ?").get(sha256(token));
|
|
11772
|
+
if (!row2) return null;
|
|
11773
|
+
return { deviceId: row2.device_id, deviceLabel: row2.device_label };
|
|
11774
|
+
}
|
|
11240
11775
|
requireAccount(accountId) {
|
|
11241
11776
|
const row2 = this.db.prepare("SELECT id FROM accounts WHERE id = ?").get(accountId);
|
|
11242
11777
|
if (!row2) throw new Error("That Sideboard account is no longer on the relay.");
|
|
@@ -12226,6 +12761,10 @@ async function startRelayServer(opts) {
|
|
|
12226
12761
|
const decision = accountStore.authorizeHost(input);
|
|
12227
12762
|
return decision.ok ? { ok: true } : { ok: false, message: decision.message };
|
|
12228
12763
|
} : void 0,
|
|
12764
|
+
phoneSessions: accountStore ? {
|
|
12765
|
+
find: (sessionToken) => accountStore.findPhoneSession(sessionToken),
|
|
12766
|
+
save: (deviceId, sessionToken) => accountStore.savePhoneSession(deviceId, sessionToken)
|
|
12767
|
+
} : void 0,
|
|
12229
12768
|
onAuthorized: (socket) => authMarks.get(socket)?.()
|
|
12230
12769
|
});
|
|
12231
12770
|
const handleCallback = async (reqUrl, res) => {
|
|
@@ -12521,8 +13060,8 @@ async function startRelayServer(opts) {
|
|
|
12521
13060
|
}
|
|
12522
13061
|
|
|
12523
13062
|
// src/slack/outbound-watch.ts
|
|
12524
|
-
import { existsSync as existsSync5, readFileSync as
|
|
12525
|
-
import { join as
|
|
13063
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
|
|
13064
|
+
import { join as join8 } from "path";
|
|
12526
13065
|
var MAX_WATCHES = 40;
|
|
12527
13066
|
var MAX_REPLIES_PER_WATCH = 30;
|
|
12528
13067
|
var WATCH_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -12530,7 +13069,7 @@ var POLL_INTERVAL_MS = 12e3;
|
|
|
12530
13069
|
var lastPollMs = 0;
|
|
12531
13070
|
var nameCache = /* @__PURE__ */ new Map();
|
|
12532
13071
|
function storePath3() {
|
|
12533
|
-
return
|
|
13072
|
+
return join8(appDataDir(), "slack-outbound-watch.json");
|
|
12534
13073
|
}
|
|
12535
13074
|
function watchId(teamId, channelId, ts) {
|
|
12536
13075
|
return `${teamId}:${channelId}:${ts}`;
|
|
@@ -12545,7 +13084,7 @@ function readStore3() {
|
|
|
12545
13084
|
const path2 = storePath3();
|
|
12546
13085
|
if (!existsSync5(path2)) return [];
|
|
12547
13086
|
try {
|
|
12548
|
-
const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(
|
|
13087
|
+
const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync7(path2, "utf8"));
|
|
12549
13088
|
return Array.isArray(parsed?.watches) ? parsed.watches : [];
|
|
12550
13089
|
} catch {
|
|
12551
13090
|
return [];
|
|
@@ -12597,7 +13136,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
12597
13136
|
await continueOnReply(threadId, prompt);
|
|
12598
13137
|
return;
|
|
12599
13138
|
}
|
|
12600
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
13139
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-GSOW3RPA.js");
|
|
12601
13140
|
await getOrchestrator2().send(threadId, prompt);
|
|
12602
13141
|
} catch {
|
|
12603
13142
|
}
|
|
@@ -12706,8 +13245,8 @@ function displayName(user) {
|
|
|
12706
13245
|
return fromProfile || user.real_name?.trim() || user.name?.trim() || "";
|
|
12707
13246
|
}
|
|
12708
13247
|
async function resolveUserName(token, userId, fallback, fetchImpl) {
|
|
12709
|
-
const
|
|
12710
|
-
if (
|
|
13248
|
+
const cached2 = nameCache.get(userId);
|
|
13249
|
+
if (cached2) return cached2;
|
|
12711
13250
|
try {
|
|
12712
13251
|
const data = await slackApi(token, "users.info", { user: userId }, fetchImpl);
|
|
12713
13252
|
const name = displayName(data.user ?? {}) || fallback;
|