@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.cjs
CHANGED
|
@@ -1160,9 +1160,9 @@ function sumOptional(a, b) {
|
|
|
1160
1160
|
function fromInclusiveInputUsage(opts) {
|
|
1161
1161
|
const totalInput = Number(opts.inputTokens) || 0;
|
|
1162
1162
|
const outputTokens = Number(opts.outputTokens) || 0;
|
|
1163
|
-
const
|
|
1163
|
+
const cached2 = Number(opts.cachedInputTokens) || 0;
|
|
1164
1164
|
if (!totalInput && !outputTokens) return null;
|
|
1165
|
-
const cacheReadTokens =
|
|
1165
|
+
const cacheReadTokens = cached2 > 0 ? Math.min(cached2, totalInput) : 0;
|
|
1166
1166
|
return {
|
|
1167
1167
|
inputTokens: Math.max(0, totalInput - cacheReadTokens),
|
|
1168
1168
|
outputTokens,
|
|
@@ -4702,8 +4702,8 @@ function readThread(id) {
|
|
|
4702
4702
|
return null;
|
|
4703
4703
|
}
|
|
4704
4704
|
const cache2 = cacheForDir();
|
|
4705
|
-
const
|
|
4706
|
-
if (
|
|
4705
|
+
const cached2 = cache2.byId.get(id);
|
|
4706
|
+
if (cached2 && cache2.mtimeMs.get(id) === mtimeMs) return cached2;
|
|
4707
4707
|
const raw = (0, import_node_fs12.readFileSync)(path2, "utf8");
|
|
4708
4708
|
const thread = normalizeThread(JSON.parse(raw));
|
|
4709
4709
|
rememberThread(thread, mtimeMs);
|
|
@@ -6922,15 +6922,15 @@ function loadFromDisk(threadId) {
|
|
|
6922
6922
|
}
|
|
6923
6923
|
}
|
|
6924
6924
|
function memFor(threadId) {
|
|
6925
|
-
const
|
|
6926
|
-
if (
|
|
6925
|
+
const cached2 = memory.get(threadId);
|
|
6926
|
+
if (cached2) return cached2;
|
|
6927
6927
|
const mem = fromSnapshot(loadFromDisk(threadId) ?? emptySetupLog());
|
|
6928
6928
|
memory.set(threadId, mem);
|
|
6929
6929
|
return mem;
|
|
6930
6930
|
}
|
|
6931
6931
|
function readSetupLog(threadId) {
|
|
6932
|
-
const
|
|
6933
|
-
if (
|
|
6932
|
+
const cached2 = memory.get(threadId);
|
|
6933
|
+
if (cached2) return materialize(cached2);
|
|
6934
6934
|
const disk = loadFromDisk(threadId);
|
|
6935
6935
|
if (disk) {
|
|
6936
6936
|
memory.set(threadId, fromSnapshot(disk));
|
|
@@ -7076,15 +7076,15 @@ function loadFromDisk2(key) {
|
|
|
7076
7076
|
}
|
|
7077
7077
|
}
|
|
7078
7078
|
function memFor2(key) {
|
|
7079
|
-
const
|
|
7080
|
-
if (
|
|
7079
|
+
const cached2 = memory2.get(key);
|
|
7080
|
+
if (cached2) return cached2;
|
|
7081
7081
|
const mem = fromSnapshot2(loadFromDisk2(key) ?? emptyRunLog());
|
|
7082
7082
|
memory2.set(key, mem);
|
|
7083
7083
|
return mem;
|
|
7084
7084
|
}
|
|
7085
7085
|
function readRunLog(key) {
|
|
7086
|
-
const
|
|
7087
|
-
if (
|
|
7086
|
+
const cached2 = memory2.get(key);
|
|
7087
|
+
if (cached2) return materialize2(cached2);
|
|
7088
7088
|
const disk = loadFromDisk2(key);
|
|
7089
7089
|
if (disk) {
|
|
7090
7090
|
memory2.set(key, fromSnapshot2(disk));
|
|
@@ -15128,8 +15128,8 @@ var init_claude = __esm({
|
|
|
15128
15128
|
return { type: "stdout", data: line };
|
|
15129
15129
|
}
|
|
15130
15130
|
},
|
|
15131
|
-
async resolveSessionId(_worktreePath,
|
|
15132
|
-
return
|
|
15131
|
+
async resolveSessionId(_worktreePath, cached2) {
|
|
15132
|
+
return cached2;
|
|
15133
15133
|
},
|
|
15134
15134
|
async buildAttach(thread) {
|
|
15135
15135
|
const claude = resolveClaudeExecutable();
|
|
@@ -15513,8 +15513,8 @@ var init_codex = __esm({
|
|
|
15513
15513
|
return { type: "stdout", data: line };
|
|
15514
15514
|
}
|
|
15515
15515
|
},
|
|
15516
|
-
async resolveSessionId(_worktreePath,
|
|
15517
|
-
return
|
|
15516
|
+
async resolveSessionId(_worktreePath, cached2) {
|
|
15517
|
+
return cached2;
|
|
15518
15518
|
},
|
|
15519
15519
|
async buildAttach(thread) {
|
|
15520
15520
|
const codex = resolveAgentExecutable("codex");
|
|
@@ -16150,8 +16150,8 @@ var init_cursor = __esm({
|
|
|
16150
16150
|
parseEvent(line) {
|
|
16151
16151
|
return parseCursorRunnerLine(line);
|
|
16152
16152
|
},
|
|
16153
|
-
async resolveSessionId(_worktreePath,
|
|
16154
|
-
return
|
|
16153
|
+
async resolveSessionId(_worktreePath, cached2) {
|
|
16154
|
+
return cached2;
|
|
16155
16155
|
},
|
|
16156
16156
|
async buildAttach(thread) {
|
|
16157
16157
|
const which = await run("which", ["cursor"], { reject: false });
|
|
@@ -16633,8 +16633,8 @@ var init_opencode = __esm({
|
|
|
16633
16633
|
return { type: "stdout", data: line };
|
|
16634
16634
|
}
|
|
16635
16635
|
},
|
|
16636
|
-
async resolveSessionId(worktreePath,
|
|
16637
|
-
const cachedId =
|
|
16636
|
+
async resolveSessionId(worktreePath, cached2) {
|
|
16637
|
+
const cachedId = cached2?.trim() || null;
|
|
16638
16638
|
const listed = await run(
|
|
16639
16639
|
resolveAgentExecutable("opencode"),
|
|
16640
16640
|
["session", "list", "--format", "json"],
|
|
@@ -16961,7 +16961,7 @@ function numberOrUndef(value) {
|
|
|
16961
16961
|
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
16962
16962
|
return value;
|
|
16963
16963
|
}
|
|
16964
|
-
function parseClaudeUsagePayload(raw,
|
|
16964
|
+
function parseClaudeUsagePayload(raw, fetchedAt2 = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
16965
16965
|
if (!raw || typeof raw !== "object") return null;
|
|
16966
16966
|
const obj = raw;
|
|
16967
16967
|
const source = obj.rate_limits && typeof obj.rate_limits === "object" ? obj.rate_limits : obj;
|
|
@@ -16975,7 +16975,7 @@ function parseClaudeUsagePayload(raw, fetchedAt = (/* @__PURE__ */ new Date()).t
|
|
|
16975
16975
|
return {
|
|
16976
16976
|
windows,
|
|
16977
16977
|
...extra ? { extraUsage: extra } : {},
|
|
16978
|
-
fetchedAt
|
|
16978
|
+
fetchedAt: fetchedAt2
|
|
16979
16979
|
};
|
|
16980
16980
|
}
|
|
16981
16981
|
function hottestClaudeWindow(usage) {
|
|
@@ -17018,15 +17018,15 @@ function formatClaudeUsageResetClock(resetsAt) {
|
|
|
17018
17018
|
return at.toISOString();
|
|
17019
17019
|
}
|
|
17020
17020
|
}
|
|
17021
|
-
function formatClaudeUsageFetchedAt(
|
|
17022
|
-
const at = new Date(
|
|
17021
|
+
function formatClaudeUsageFetchedAt(fetchedAt2, now = /* @__PURE__ */ new Date()) {
|
|
17022
|
+
const at = new Date(fetchedAt2);
|
|
17023
17023
|
if (Number.isNaN(at.getTime())) return "";
|
|
17024
17024
|
const minutes = Math.max(0, Math.round((now.getTime() - at.getTime()) / 6e4));
|
|
17025
17025
|
if (minutes < 1) return "Updated just now";
|
|
17026
17026
|
if (minutes < 60) return `Updated ${minutes}m ago`;
|
|
17027
17027
|
const hours = Math.round(minutes / 60);
|
|
17028
17028
|
if (hours < 36) return `Updated ${hours}h ago`;
|
|
17029
|
-
return `Updated ${formatClaudeUsageResetClock(
|
|
17029
|
+
return `Updated ${formatClaudeUsageResetClock(fetchedAt2)}`;
|
|
17030
17030
|
}
|
|
17031
17031
|
function formatClaudeExtraUsageDetail(extra) {
|
|
17032
17032
|
if (!extra.enabled) return "Extra usage off";
|
|
@@ -26376,12 +26376,13 @@ function formatArtifactDirective() {
|
|
|
26376
26376
|
"Sideboard side column (desktop UI) \u2014 Sideboard MCP tools present_artifact, present_schema, present_files, ask_user (see their descriptions). claude.ai\u2019s \u201CArtifact\u201D tool does not exist here; never say artifacts, the CMS UI, or the Files column are unavailable.",
|
|
26377
26377
|
"- Standalone documents: a fenced block tagged `html` (preferred), `svg`, or `markdown` containing the FULL document opens the side column by itself. Use that or present_artifact \u2014 never both for the same body.",
|
|
26378
26378
|
"- present_artifact type=log appends: same artifact_id, content = new lines only (plus status/phase). Do not resend the full log or wrap it in HTML.",
|
|
26379
|
+
"- Chat links open the side column and files: `[label](sideboard://artifact/<artifact_id or title>)` opens that pane; `[label](sideboard://file/<path>#L12-L40)` opens the file (line range optional). A repo-relative markdown link opens the file too.",
|
|
26379
26380
|
"- Data: a markdown table is enough to read. Call present_schema only when the user needs to filter/edit/publish/persist rows \u2014 including when they ask for an editable table after you already showed markdown. Never re-present rows you already wrote just to display them.",
|
|
26380
26381
|
"- ask_user only when work is blocked on a few concrete options (approach fork, which API, auth vs cookies): first a short chat message explaining the decision and each option, then the call (description on every option), then stop and wait. Not for greetings, check-ins, an invented menu of next tasks, or after a review \u2014 reply in chat. If one option is the obvious default, proceed. ask_user already notifies the parent orchestrator. Call notify_orchestrator only when blocked without a picker."
|
|
26381
26382
|
].join("\n");
|
|
26382
26383
|
}
|
|
26383
26384
|
function formatUiReminder() {
|
|
26384
|
-
return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos, \u201Cwhat next?\u201D, or after a review) \u2014 reply in chat. ask_user wakes the parent orchestrator; notify_orchestrator if blocked without a picker. Do not say artifacts/CMS UI are unavailable. Dev / preview: run_dev_script (same as the Dev button; pass name \u2014 several scripts can run at once) returns {url, port, preview} (SIDEBOARD_PORT \u2014 never guess :3000). preview=url \u2192 open url; preview=window \u2192 Electron window is the app, url is renderer/HMR only. list_run_scripts active[].url if already running; get_run_log for that script\u2019s Run-tab terminal; stop_dev_script to stop one.";
|
|
26385
|
+
return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). Chat links: `[name](sideboard://artifact/<id-or-title>)` opens that pane; `[name](sideboard://file/path#L12-L40)` opens a file. ask_user only for a real multiple-choice (not hellos, \u201Cwhat next?\u201D, or after a review) \u2014 reply in chat. ask_user wakes the parent orchestrator; notify_orchestrator if blocked without a picker. Do not say artifacts/CMS UI are unavailable. Dev / preview: run_dev_script (same as the Dev button; pass name \u2014 several scripts can run at once) returns {url, port, preview} (SIDEBOARD_PORT \u2014 never guess :3000). preview=url \u2192 open url; preview=window \u2192 Electron window is the app, url is renderer/HMR only. list_run_scripts active[].url if already running; get_run_log for that script\u2019s Run-tab terminal; stop_dev_script to stop one.";
|
|
26385
26386
|
}
|
|
26386
26387
|
var import_node_fs63, import_node_path60, PR_GOAL_RE, GITHUB_TICKET_REF, KEYED_TICKET_REF;
|
|
26387
26388
|
var init_instructions = __esm({
|
|
@@ -36106,15 +36107,15 @@ async function getHomeBoardInputs(workspaces, opts) {
|
|
|
36106
36107
|
}
|
|
36107
36108
|
const promise = (async () => {
|
|
36108
36109
|
const inputs = await loadHomeBoardInputs(workspaces);
|
|
36109
|
-
const
|
|
36110
|
+
const fetchedAt2 = opts?.now ?? Date.now();
|
|
36110
36111
|
const pins = syncBoardPins(listBoardPins(), inputs.issues, inputs.prs);
|
|
36111
36112
|
replaceBoardPins(pins);
|
|
36112
|
-
const loaded = { ...inputs, fetchedAt, fromCache: false, pins };
|
|
36113
|
+
const loaded = { ...inputs, fetchedAt: fetchedAt2, fromCache: false, pins };
|
|
36113
36114
|
if (shouldCacheHomeBoardInputs(inputs)) {
|
|
36114
36115
|
const entry = {
|
|
36115
36116
|
version: CACHE_VERSION,
|
|
36116
36117
|
workspaceKey: key,
|
|
36117
|
-
fetchedAt,
|
|
36118
|
+
fetchedAt: fetchedAt2,
|
|
36118
36119
|
inputs
|
|
36119
36120
|
};
|
|
36120
36121
|
memory3 = entry;
|
|
@@ -39883,14 +39884,46 @@ var RemoteHub = class {
|
|
|
39883
39884
|
this.codes.delete(code);
|
|
39884
39885
|
this.attachPhone(socket, host);
|
|
39885
39886
|
}
|
|
39887
|
+
/** In-memory session, with the live Mac's label when that socket is up. */
|
|
39888
|
+
memorySession(sessionToken) {
|
|
39889
|
+
const cached2 = this.phonesByToken.get(sessionToken);
|
|
39890
|
+
if (!cached2) return null;
|
|
39891
|
+
const live = this.hosts.get(cached2.deviceId);
|
|
39892
|
+
const remembered = this.remembered.get(cached2.deviceId);
|
|
39893
|
+
const current = live?.sessionToken ?? remembered?.sessionToken ?? null;
|
|
39894
|
+
if (current && current !== sessionToken) {
|
|
39895
|
+
this.phonesByToken.delete(sessionToken);
|
|
39896
|
+
return null;
|
|
39897
|
+
}
|
|
39898
|
+
return {
|
|
39899
|
+
deviceId: cached2.deviceId,
|
|
39900
|
+
deviceLabel: live?.deviceLabel || remembered?.deviceLabel || cached2.deviceLabel
|
|
39901
|
+
};
|
|
39902
|
+
}
|
|
39903
|
+
knownSession(sessionToken) {
|
|
39904
|
+
const cached2 = this.memorySession(sessionToken);
|
|
39905
|
+
if (cached2) return cached2;
|
|
39906
|
+
const stored = this.opts.phoneSessions?.find(sessionToken) ?? null;
|
|
39907
|
+
if (!stored) return null;
|
|
39908
|
+
const live = this.hosts.get(stored.deviceId);
|
|
39909
|
+
const remembered = this.remembered.get(stored.deviceId);
|
|
39910
|
+
const current = live?.sessionToken ?? remembered?.sessionToken ?? null;
|
|
39911
|
+
if (current && current !== sessionToken) return null;
|
|
39912
|
+
this.phonesByToken.set(sessionToken, stored);
|
|
39913
|
+
if (remembered && !remembered.sessionToken) remembered.sessionToken = sessionToken;
|
|
39914
|
+
if (live && !live.sessionToken) live.sessionToken = sessionToken;
|
|
39915
|
+
return {
|
|
39916
|
+
deviceId: stored.deviceId,
|
|
39917
|
+
deviceLabel: live?.deviceLabel || remembered?.deviceLabel || stored.deviceLabel
|
|
39918
|
+
};
|
|
39919
|
+
}
|
|
39886
39920
|
resumePhone(socket, sessionToken) {
|
|
39887
|
-
const
|
|
39888
|
-
|
|
39889
|
-
if (!deviceId || !remembered || remembered.sessionToken !== sessionToken) {
|
|
39921
|
+
const known = this.knownSession(sessionToken);
|
|
39922
|
+
if (!known) {
|
|
39890
39923
|
send2(socket, { type: "error", message: "phone session expired \u2014 pair again" });
|
|
39891
39924
|
return;
|
|
39892
39925
|
}
|
|
39893
|
-
const host = this.hosts.get(deviceId);
|
|
39926
|
+
const host = this.hosts.get(known.deviceId);
|
|
39894
39927
|
if (!host) {
|
|
39895
39928
|
send2(socket, { type: "host_offline" });
|
|
39896
39929
|
return;
|
|
@@ -39899,17 +39932,13 @@ var RemoteHub = class {
|
|
|
39899
39932
|
}
|
|
39900
39933
|
listHosts(socket, sessionTokens) {
|
|
39901
39934
|
const hosts = sessionTokens.map((sessionToken) => {
|
|
39902
|
-
const
|
|
39903
|
-
|
|
39904
|
-
const
|
|
39905
|
-
if (!valid || !deviceId || !remembered) {
|
|
39906
|
-
return { sessionToken, deviceId: "", deviceLabel: "", online: false };
|
|
39907
|
-
}
|
|
39908
|
-
const host = this.hosts.get(deviceId);
|
|
39935
|
+
const known = this.knownSession(sessionToken);
|
|
39936
|
+
if (!known) return { sessionToken, deviceId: "", deviceLabel: "", online: false };
|
|
39937
|
+
const host = this.hosts.get(known.deviceId);
|
|
39909
39938
|
return {
|
|
39910
39939
|
sessionToken,
|
|
39911
|
-
deviceId,
|
|
39912
|
-
deviceLabel: host?.deviceLabel ||
|
|
39940
|
+
deviceId: known.deviceId,
|
|
39941
|
+
deviceLabel: host?.deviceLabel || known.deviceLabel,
|
|
39913
39942
|
online: Boolean(host)
|
|
39914
39943
|
};
|
|
39915
39944
|
});
|
|
@@ -39940,18 +39969,20 @@ var RemoteHub = class {
|
|
|
39940
39969
|
}
|
|
39941
39970
|
host.phone = null;
|
|
39942
39971
|
}
|
|
39972
|
+
const created = sessionToken == null;
|
|
39943
39973
|
const token = sessionToken ?? (0, import_node_crypto25.randomBytes)(24).toString("hex");
|
|
39944
39974
|
if (host.sessionToken && host.sessionToken !== token) {
|
|
39945
39975
|
this.phonesByToken.delete(host.sessionToken);
|
|
39946
39976
|
}
|
|
39947
39977
|
host.sessionToken = token;
|
|
39948
39978
|
host.phone = socket;
|
|
39949
|
-
this.phonesByToken.set(token, host.deviceId);
|
|
39979
|
+
this.phonesByToken.set(token, { deviceId: host.deviceId, deviceLabel: host.deviceLabel });
|
|
39950
39980
|
this.remembered.set(host.deviceId, {
|
|
39951
39981
|
hostSecret: host.hostSecret,
|
|
39952
39982
|
sessionToken: token,
|
|
39953
39983
|
deviceLabel: host.deviceLabel
|
|
39954
39984
|
});
|
|
39985
|
+
if (created) this.opts.phoneSessions?.save(host.deviceId, token);
|
|
39955
39986
|
this.bySocket.set(socket, { role: "phone", deviceId: host.deviceId });
|
|
39956
39987
|
this.opts.onAuthorized?.(socket);
|
|
39957
39988
|
send2(socket, {
|
|
@@ -39996,10 +40027,16 @@ init_stage_files();
|
|
|
39996
40027
|
init_orchestrator();
|
|
39997
40028
|
init_app_settings();
|
|
39998
40029
|
init_implied_first_prompt();
|
|
40030
|
+
init_message_parts();
|
|
40031
|
+
init_turn_live();
|
|
39999
40032
|
|
|
40000
40033
|
// src/remote/phone-media.ts
|
|
40034
|
+
var import_node_fs71 = require("fs");
|
|
40035
|
+
var import_node_path69 = require("path");
|
|
40001
40036
|
init_diff();
|
|
40002
40037
|
var PHONE_IMAGE_MAX_BYTES = 15e5;
|
|
40038
|
+
var PHONE_TEXT_MAX_BYTES = 18e4;
|
|
40039
|
+
var PHONE_DIR_MAX = 200;
|
|
40003
40040
|
var IMAGE_MIME = {
|
|
40004
40041
|
png: "image/png",
|
|
40005
40042
|
jpg: "image/jpeg",
|
|
@@ -40046,21 +40083,74 @@ function phoneImageDataUrl(worktreePath, raw) {
|
|
|
40046
40083
|
return null;
|
|
40047
40084
|
}
|
|
40048
40085
|
}
|
|
40049
|
-
|
|
40050
|
-
|
|
40051
|
-
|
|
40052
|
-
|
|
40053
|
-
|
|
40054
|
-
|
|
40055
|
-
|
|
40056
|
-
|
|
40057
|
-
|
|
40058
|
-
|
|
40059
|
-
|
|
40060
|
-
|
|
40061
|
-
|
|
40062
|
-
|
|
40063
|
-
|
|
40086
|
+
function phoneDirectoryListing(worktreePath, relative) {
|
|
40087
|
+
try {
|
|
40088
|
+
const entries = (0, import_node_fs71.readdirSync)((0, import_node_path69.join)(worktreePath, relative), { withFileTypes: true });
|
|
40089
|
+
const dirs = new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
40090
|
+
const names = entries.map((entry) => entry.name).filter((name) => name && !name.includes("/") && !name.includes("\0")).sort((a, b) => a.localeCompare(b));
|
|
40091
|
+
const shown = names.slice(0, PHONE_DIR_MAX);
|
|
40092
|
+
const lines = shown.map((name) => dirs.has(name) ? `${name}/` : name);
|
|
40093
|
+
const truncated = names.length > shown.length;
|
|
40094
|
+
if (truncated) lines.push(`\u2026 ${names.length - shown.length} more`);
|
|
40095
|
+
return { content: lines.join("\n"), truncated };
|
|
40096
|
+
} catch {
|
|
40097
|
+
return null;
|
|
40098
|
+
}
|
|
40099
|
+
}
|
|
40100
|
+
function phoneFilePreview(worktreePath, relative, kind) {
|
|
40101
|
+
if (kind === "dir") {
|
|
40102
|
+
const listing = phoneDirectoryListing(worktreePath, relative);
|
|
40103
|
+
if (!listing) return null;
|
|
40104
|
+
return {
|
|
40105
|
+
content: listing.content,
|
|
40106
|
+
encoding: "utf8",
|
|
40107
|
+
binary: false,
|
|
40108
|
+
directory: true,
|
|
40109
|
+
truncated: listing.truncated
|
|
40110
|
+
};
|
|
40111
|
+
}
|
|
40112
|
+
if (relative.toLowerCase().endsWith(".svg")) {
|
|
40113
|
+
try {
|
|
40114
|
+
const buf = (0, import_node_fs71.readFileSync)((0, import_node_path69.join)(worktreePath, relative));
|
|
40115
|
+
const truncated = buf.length > PHONE_TEXT_MAX_BYTES;
|
|
40116
|
+
return {
|
|
40117
|
+
content: buf.subarray(0, PHONE_TEXT_MAX_BYTES).toString("utf8"),
|
|
40118
|
+
encoding: "utf8",
|
|
40119
|
+
binary: false,
|
|
40120
|
+
directory: false,
|
|
40121
|
+
truncated,
|
|
40122
|
+
mime: "image/svg+xml"
|
|
40123
|
+
};
|
|
40124
|
+
} catch {
|
|
40125
|
+
return null;
|
|
40126
|
+
}
|
|
40127
|
+
}
|
|
40128
|
+
const mime = phoneImageMime(relative);
|
|
40129
|
+
if (mime) {
|
|
40130
|
+
const dataUrl = phoneImageDataUrl(worktreePath, relative);
|
|
40131
|
+
if (!dataUrl) return null;
|
|
40132
|
+
return {
|
|
40133
|
+
content: dataUrl,
|
|
40134
|
+
encoding: "base64",
|
|
40135
|
+
binary: true,
|
|
40136
|
+
directory: false,
|
|
40137
|
+
truncated: false,
|
|
40138
|
+
mime
|
|
40139
|
+
};
|
|
40140
|
+
}
|
|
40141
|
+
try {
|
|
40142
|
+
const file = readWorktreeFile(worktreePath, relative, { maxBytes: PHONE_TEXT_MAX_BYTES });
|
|
40143
|
+
return {
|
|
40144
|
+
content: file.content,
|
|
40145
|
+
encoding: file.encoding,
|
|
40146
|
+
binary: file.binary,
|
|
40147
|
+
directory: false,
|
|
40148
|
+
truncated: file.truncated
|
|
40149
|
+
};
|
|
40150
|
+
} catch {
|
|
40151
|
+
return null;
|
|
40152
|
+
}
|
|
40153
|
+
}
|
|
40064
40154
|
|
|
40065
40155
|
// src/remote/phone-artifact.ts
|
|
40066
40156
|
var FENCE_RE = /```([a-zA-Z0-9_+-]*)[^\n]*\n([\s\S]*?)(?:```|$)/g;
|
|
@@ -40197,6 +40287,281 @@ function parseOpenArtifact(value, chatId) {
|
|
|
40197
40287
|
const hint = oneLine(str6(record.hint), 200);
|
|
40198
40288
|
return { op: "open-artifact", chatId, title, ...hint ? { hint } : {} };
|
|
40199
40289
|
}
|
|
40290
|
+
var PHONE_ARTIFACT_MAX_CHARS = 18e4;
|
|
40291
|
+
function phoneArtifactDocuments(sources) {
|
|
40292
|
+
const docs = [];
|
|
40293
|
+
for (const source of sources) {
|
|
40294
|
+
const hints = /* @__PURE__ */ new Set();
|
|
40295
|
+
for (const match of source.text.matchAll(new RegExp(FENCE_RE.source, "g"))) {
|
|
40296
|
+
const language = match[1] ?? "";
|
|
40297
|
+
const content = match[2] ?? "";
|
|
40298
|
+
const kind = kindFor(language, content);
|
|
40299
|
+
if (!kind) continue;
|
|
40300
|
+
const hint = artifactHint(content);
|
|
40301
|
+
if (hint) hints.add(hint);
|
|
40302
|
+
docs.push({ title: titleFor(kind, content), content, kind });
|
|
40303
|
+
}
|
|
40304
|
+
for (const part of source.parts ?? []) {
|
|
40305
|
+
if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
|
|
40306
|
+
const input = asRecord14(part.input);
|
|
40307
|
+
const content = toolContent(input);
|
|
40308
|
+
const hint = artifactHint(content);
|
|
40309
|
+
if (hint && hints.has(hint)) continue;
|
|
40310
|
+
const typed = kindFor(str6(input?.type), content) ?? kindFor("", content);
|
|
40311
|
+
const title = oneLine(str6(input?.title), 80) || (typed ? titleFor(typed, content) : "Artifact");
|
|
40312
|
+
if (!str6(input?.title) && content.trim().length < MIN_FENCE_CHARS) continue;
|
|
40313
|
+
if (hint) hints.add(hint);
|
|
40314
|
+
docs.push({ title, content, kind: typed ?? "text" });
|
|
40315
|
+
}
|
|
40316
|
+
}
|
|
40317
|
+
return docs;
|
|
40318
|
+
}
|
|
40319
|
+
function phoneArtifactReply(docs, request) {
|
|
40320
|
+
const match = pickArtifactMatch(docs, request);
|
|
40321
|
+
if (!match) return null;
|
|
40322
|
+
const truncated = match.content.length > PHONE_ARTIFACT_MAX_CHARS;
|
|
40323
|
+
return {
|
|
40324
|
+
title: match.title,
|
|
40325
|
+
kind: match.kind,
|
|
40326
|
+
content: truncated ? match.content.slice(0, PHONE_ARTIFACT_MAX_CHARS) : match.content,
|
|
40327
|
+
truncated
|
|
40328
|
+
};
|
|
40329
|
+
}
|
|
40330
|
+
function newest(items) {
|
|
40331
|
+
return items.length ? items[items.length - 1] ?? null : null;
|
|
40332
|
+
}
|
|
40333
|
+
function pickArtifactMatch(items, request) {
|
|
40334
|
+
const hint = request.hint?.trim();
|
|
40335
|
+
const title = request.title.trim();
|
|
40336
|
+
if (hint) {
|
|
40337
|
+
const exact = items.filter((item) => item.content && artifactHint(item.content) === hint);
|
|
40338
|
+
const exactTitled = title ? exact.filter((item) => item.title === title) : [];
|
|
40339
|
+
const exactMatch = newest(exactTitled.length ? exactTitled : exact);
|
|
40340
|
+
if (exactMatch) return exactMatch;
|
|
40341
|
+
if (title) {
|
|
40342
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
40343
|
+
const item = items[i];
|
|
40344
|
+
if (!item?.content || item.title !== title) continue;
|
|
40345
|
+
if (normalizedBody(item.content).startsWith(hint)) return item;
|
|
40346
|
+
}
|
|
40347
|
+
}
|
|
40348
|
+
}
|
|
40349
|
+
if (!title) return null;
|
|
40350
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
40351
|
+
if (items[i]?.title === title) return items[i] ?? null;
|
|
40352
|
+
}
|
|
40353
|
+
return null;
|
|
40354
|
+
}
|
|
40355
|
+
|
|
40356
|
+
// src/remote/phone-workspace-ops.ts
|
|
40357
|
+
init_orchestrator();
|
|
40358
|
+
|
|
40359
|
+
// src/remote/phone-chats.ts
|
|
40360
|
+
var import_node_crypto26 = require("crypto");
|
|
40361
|
+
init_list_models();
|
|
40362
|
+
init_home_board();
|
|
40363
|
+
init_stage_files();
|
|
40364
|
+
init_worktree_labels();
|
|
40365
|
+
init_thinking_effort();
|
|
40366
|
+
init_app_settings();
|
|
40367
|
+
init_global_workspace();
|
|
40368
|
+
init_thread_store();
|
|
40369
|
+
init_workspaces();
|
|
40370
|
+
init_chat_tabs();
|
|
40371
|
+
init_create();
|
|
40372
|
+
init_orchestrator();
|
|
40373
|
+
|
|
40374
|
+
// src/remote/phone-header.ts
|
|
40375
|
+
init_orchestrator();
|
|
40376
|
+
init_global_workspace();
|
|
40377
|
+
var PHONE_HEADER_GIT_ACTIONS = [
|
|
40378
|
+
"commit-push",
|
|
40379
|
+
"create-draft",
|
|
40380
|
+
"resolve-conflicts",
|
|
40381
|
+
"ready-for-review",
|
|
40382
|
+
"merge"
|
|
40383
|
+
];
|
|
40384
|
+
var HIDDEN = {
|
|
40385
|
+
visible: false,
|
|
40386
|
+
branch: "",
|
|
40387
|
+
additions: 0,
|
|
40388
|
+
deletions: 0,
|
|
40389
|
+
unpushed: 0,
|
|
40390
|
+
dirty: false,
|
|
40391
|
+
ready: false,
|
|
40392
|
+
title: ""
|
|
40393
|
+
};
|
|
40394
|
+
function phoneHeaderFrom(thread, diff) {
|
|
40395
|
+
if (isGlobalThread(thread)) return HIDDEN;
|
|
40396
|
+
const branch = thread.branchName.trim().replace(/^thread\//, "");
|
|
40397
|
+
if (!branch) return HIDDEN;
|
|
40398
|
+
const ready = diff != null;
|
|
40399
|
+
const uncommitted = diff?.scopeStats?.uncommitted;
|
|
40400
|
+
const additions = ready ? Math.max(0, Math.trunc(uncommitted?.additions ?? 0)) : 0;
|
|
40401
|
+
const deletions = ready ? Math.max(0, Math.trunc(uncommitted?.deletions ?? 0)) : 0;
|
|
40402
|
+
const unpushed = ready ? Math.max(0, Math.trunc(diff?.unpushed ?? 0)) : 0;
|
|
40403
|
+
const dirty = ready && (Boolean(diff?.dirty) || additions > 0 || deletions > 0);
|
|
40404
|
+
const pr = prSnapshot(thread);
|
|
40405
|
+
const action = ready ? headerAction(pr, { dirty: Boolean(diff?.dirty), unpushed }) : null;
|
|
40406
|
+
const lines = [branch];
|
|
40407
|
+
if (!ready) lines.push("Loading git status\u2026");
|
|
40408
|
+
else if (dirty) lines.push(`Uncommitted +${additions} \u2212${deletions}`);
|
|
40409
|
+
else lines.push("Working tree clean");
|
|
40410
|
+
if (unpushed > 0) lines.push(`${unpushed} unpushed commit${unpushed === 1 ? "" : "s"}`);
|
|
40411
|
+
return {
|
|
40412
|
+
visible: true,
|
|
40413
|
+
branch,
|
|
40414
|
+
additions,
|
|
40415
|
+
deletions,
|
|
40416
|
+
unpushed,
|
|
40417
|
+
dirty,
|
|
40418
|
+
ready,
|
|
40419
|
+
title: lines.join("\n"),
|
|
40420
|
+
...pr.url ? { prUrl: pr.url } : {},
|
|
40421
|
+
...pr.number ? { prNumber: pr.number } : {},
|
|
40422
|
+
...pr.status ? { prStatus: pr.status } : {},
|
|
40423
|
+
...action ? { action: action.id, actionLabel: action.label } : {}
|
|
40424
|
+
};
|
|
40425
|
+
}
|
|
40426
|
+
function phonePrPill(thread) {
|
|
40427
|
+
const pr = prSnapshot(thread);
|
|
40428
|
+
if (!pr.url) return null;
|
|
40429
|
+
return { url: pr.url, number: pr.number, status: pr.status };
|
|
40430
|
+
}
|
|
40431
|
+
async function readPhoneHeader(thread) {
|
|
40432
|
+
const pending = phoneHeaderFrom(thread, null);
|
|
40433
|
+
if (!pending.visible) return pending;
|
|
40434
|
+
try {
|
|
40435
|
+
const diff = await getOrchestrator().diff(thread.id, { includePatches: false });
|
|
40436
|
+
return phoneHeaderFrom(thread, diff);
|
|
40437
|
+
} catch {
|
|
40438
|
+
return pending;
|
|
40439
|
+
}
|
|
40440
|
+
}
|
|
40441
|
+
function prSnapshot(thread) {
|
|
40442
|
+
const url = thread.prUrl?.trim() || "";
|
|
40443
|
+
const number = url.match(/\/pull\/(\d+)/)?.[1] ?? "";
|
|
40444
|
+
const state = (thread.prState ?? "").toUpperCase();
|
|
40445
|
+
const merged = state === "MERGED";
|
|
40446
|
+
const closed = state === "CLOSED";
|
|
40447
|
+
const open = Boolean(url) && !merged && !closed;
|
|
40448
|
+
const draft = open && Boolean(thread.prIsDraft);
|
|
40449
|
+
const inMergeQueue = open && Boolean(thread.prIsInMergeQueue);
|
|
40450
|
+
const mergeable = (thread.prMergeable ?? "").toUpperCase();
|
|
40451
|
+
const mergeState = (thread.prMergeStateStatus ?? "").toUpperCase();
|
|
40452
|
+
const mergeConflicts = open && !inMergeQueue && (mergeable === "CONFLICTING" || mergeState === "DIRTY");
|
|
40453
|
+
const branchBehind = open && !inMergeQueue && !mergeConflicts && mergeState === "BEHIND";
|
|
40454
|
+
const checksFailed = Boolean(thread.prChecksFailed);
|
|
40455
|
+
const review = (open && !draft ? thread.prReviewDecision : "") ?? "";
|
|
40456
|
+
return {
|
|
40457
|
+
url,
|
|
40458
|
+
number,
|
|
40459
|
+
status: pillStatus({ url, merged, closed, draft, inMergeQueue, mergeConflicts, branchBehind, checksFailed, review }),
|
|
40460
|
+
merged,
|
|
40461
|
+
closed,
|
|
40462
|
+
open,
|
|
40463
|
+
draft,
|
|
40464
|
+
cowboy: Boolean(thread.cowboy),
|
|
40465
|
+
inMergeQueue,
|
|
40466
|
+
mergeConflicts,
|
|
40467
|
+
branchBehind,
|
|
40468
|
+
checksFailed,
|
|
40469
|
+
review: review.toUpperCase()
|
|
40470
|
+
};
|
|
40471
|
+
}
|
|
40472
|
+
function pillStatus(pr) {
|
|
40473
|
+
if (!pr.url) return "";
|
|
40474
|
+
if (pr.merged) return "Merged";
|
|
40475
|
+
if (pr.closed) return "Closed";
|
|
40476
|
+
if (pr.inMergeQueue) return "Queued";
|
|
40477
|
+
if (pr.mergeConflicts) return "Merge conflicts";
|
|
40478
|
+
if (pr.branchBehind) return "Behind base";
|
|
40479
|
+
if (pr.draft) return "Draft";
|
|
40480
|
+
if (pr.checksFailed) return "Checks failing";
|
|
40481
|
+
if (pr.review === "CHANGES_REQUESTED") return "Changes requested";
|
|
40482
|
+
if (pr.review !== "APPROVED") return "Needs approval";
|
|
40483
|
+
return "Approved";
|
|
40484
|
+
}
|
|
40485
|
+
function headerAction(pr, diff) {
|
|
40486
|
+
const hasLocalChanges = diff.dirty || diff.unpushed > 0;
|
|
40487
|
+
const originInSync = !hasLocalChanges;
|
|
40488
|
+
if (pr.merged) return null;
|
|
40489
|
+
if (pr.closed) return { id: "open-pr", label: "Closed" };
|
|
40490
|
+
if (pr.cowboy) {
|
|
40491
|
+
return hasLocalChanges ? { id: "commit-push", label: "Commit & push" } : { id: "commit-push", label: "Push" };
|
|
40492
|
+
}
|
|
40493
|
+
if (!pr.url) return { id: "create-draft", label: "Create PR" };
|
|
40494
|
+
if (pr.inMergeQueue) return { id: "open-pr", label: "Queued" };
|
|
40495
|
+
if (pr.mergeConflicts) return { id: "resolve-conflicts", label: "Resolve" };
|
|
40496
|
+
if (pr.branchBehind) return { id: "resolve-conflicts", label: "Update" };
|
|
40497
|
+
if (hasLocalChanges) return { id: "commit-push", label: "Commit & push" };
|
|
40498
|
+
if (pr.draft) {
|
|
40499
|
+
return originInSync ? { id: "ready-for-review", label: "Ready for review" } : { id: "commit-push", label: "Commit & push" };
|
|
40500
|
+
}
|
|
40501
|
+
if (pr.checksFailed) return { id: "open-checks", label: "Checks failing" };
|
|
40502
|
+
if (pr.review === "CHANGES_REQUESTED") return { id: "open-pr", label: "Changes requested" };
|
|
40503
|
+
if (pr.review !== "APPROVED") return { id: "open-pr", label: "Needs approval" };
|
|
40504
|
+
return { id: "merge", label: "Merge" };
|
|
40505
|
+
}
|
|
40506
|
+
|
|
40507
|
+
// src/remote/phone-panes.ts
|
|
40508
|
+
init_orchestrator();
|
|
40509
|
+
var MAX_ROWS = 200;
|
|
40510
|
+
function parsePhonePane(value, chatId) {
|
|
40511
|
+
const pane = value && typeof value === "object" ? value.pane : void 0;
|
|
40512
|
+
if (pane !== "files" && pane !== "changes" && pane !== "checks") return "invalid";
|
|
40513
|
+
const scopeRaw = value.scope;
|
|
40514
|
+
const scope = scopeRaw === "uncommitted" || scopeRaw === "commits" ? scopeRaw : void 0;
|
|
40515
|
+
return { op: "pane", chatId, pane, ...scope && pane === "changes" ? { scope } : {} };
|
|
40516
|
+
}
|
|
40517
|
+
async function readPhonePane(thread, pane, scope) {
|
|
40518
|
+
try {
|
|
40519
|
+
if (pane === "files") return await readFiles(thread.id);
|
|
40520
|
+
if (pane === "changes") return await readChanges(thread.id, scope ?? "commits");
|
|
40521
|
+
return await readChecks(thread.id);
|
|
40522
|
+
} catch (err) {
|
|
40523
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40524
|
+
return { rows: [], note: message };
|
|
40525
|
+
}
|
|
40526
|
+
}
|
|
40527
|
+
async function readFiles(threadId) {
|
|
40528
|
+
const paths = await getOrchestrator().listFiles(threadId);
|
|
40529
|
+
const shown = paths.slice(0, MAX_ROWS);
|
|
40530
|
+
return {
|
|
40531
|
+
rows: shown.map((path2) => ({ label: path2, path: path2 })),
|
|
40532
|
+
...paths.length > shown.length ? { note: `${paths.length - shown.length} more files on the Mac` } : {}
|
|
40533
|
+
};
|
|
40534
|
+
}
|
|
40535
|
+
async function readChanges(threadId, scope) {
|
|
40536
|
+
const diff = await getOrchestrator().diff(threadId, { includePatches: false, scope });
|
|
40537
|
+
const shown = diff.files.slice(0, MAX_ROWS);
|
|
40538
|
+
return {
|
|
40539
|
+
rows: shown.map((file) => ({
|
|
40540
|
+
label: file.path,
|
|
40541
|
+
path: file.path,
|
|
40542
|
+
detail: changeDetail(file.additions, file.deletions)
|
|
40543
|
+
})),
|
|
40544
|
+
...diff.files.length > shown.length ? { note: `${diff.files.length - shown.length} more changed files` } : {}
|
|
40545
|
+
};
|
|
40546
|
+
}
|
|
40547
|
+
function changeDetail(additions, deletions) {
|
|
40548
|
+
const add = additions ?? 0;
|
|
40549
|
+
const del = deletions ?? 0;
|
|
40550
|
+
if (add === 0 && del === 0) return void 0;
|
|
40551
|
+
return `+${add} \u2212${del}`;
|
|
40552
|
+
}
|
|
40553
|
+
async function readChecks(threadId) {
|
|
40554
|
+
const checks = await getOrchestrator().getPrChecks(threadId);
|
|
40555
|
+
if (!checks) return { rows: [], note: "No pull request on this branch." };
|
|
40556
|
+
const shown = checks.slice(0, MAX_ROWS);
|
|
40557
|
+
return {
|
|
40558
|
+
rows: shown.map((check) => ({
|
|
40559
|
+
label: check.name,
|
|
40560
|
+
detail: check.bucket || check.state,
|
|
40561
|
+
...check.link ? { href: check.link } : {}
|
|
40562
|
+
}))
|
|
40563
|
+
};
|
|
40564
|
+
}
|
|
40200
40565
|
|
|
40201
40566
|
// src/remote/phone-defaults.ts
|
|
40202
40567
|
init_app_settings();
|
|
@@ -40215,6 +40580,29 @@ function phoneAccountDefaults() {
|
|
|
40215
40580
|
};
|
|
40216
40581
|
}
|
|
40217
40582
|
|
|
40583
|
+
// src/remote/phone-viewer-login.ts
|
|
40584
|
+
var TTL_MS = 5 * 6e4;
|
|
40585
|
+
var cached = "";
|
|
40586
|
+
var fetchedAt = 0;
|
|
40587
|
+
var inflight3 = null;
|
|
40588
|
+
function phoneViewerLogin() {
|
|
40589
|
+
return cached;
|
|
40590
|
+
}
|
|
40591
|
+
function refreshPhoneViewerLogin(force = false) {
|
|
40592
|
+
const now = Date.now();
|
|
40593
|
+
if (!force && fetchedAt && now - fetchedAt < TTL_MS) return Promise.resolve(cached);
|
|
40594
|
+
if (inflight3) return inflight3;
|
|
40595
|
+
const run2 = getGitHubStatus().then((status) => {
|
|
40596
|
+
cached = status.login?.trim() ?? "";
|
|
40597
|
+
fetchedAt = Date.now();
|
|
40598
|
+
return cached;
|
|
40599
|
+
}).catch(() => cached).finally(() => {
|
|
40600
|
+
if (inflight3 === run2) inflight3 = null;
|
|
40601
|
+
});
|
|
40602
|
+
inflight3 = run2;
|
|
40603
|
+
return run2;
|
|
40604
|
+
}
|
|
40605
|
+
|
|
40218
40606
|
// src/remote/phone-transcript.ts
|
|
40219
40607
|
init_turn_live();
|
|
40220
40608
|
|
|
@@ -40279,10 +40667,63 @@ function listPhoneMessages(thread) {
|
|
|
40279
40667
|
return live.slice(-TRANSCRIPT_LIMIT);
|
|
40280
40668
|
}
|
|
40281
40669
|
|
|
40282
|
-
// src/remote/phone-
|
|
40283
|
-
|
|
40670
|
+
// src/remote/phone-history.ts
|
|
40671
|
+
init_global_workspace();
|
|
40672
|
+
init_thread_store();
|
|
40284
40673
|
var PREVIEW_LIMIT = 90;
|
|
40285
40674
|
var HISTORY_PAGE_SIZE = 40;
|
|
40675
|
+
function previewOf(thread) {
|
|
40676
|
+
const messages = listPhoneMessages(thread);
|
|
40677
|
+
const last = messages[messages.length - 1];
|
|
40678
|
+
if (!last) return "";
|
|
40679
|
+
return clip(last.text.replace(/\s+/g, " ").trim(), PREVIEW_LIMIT);
|
|
40680
|
+
}
|
|
40681
|
+
function repoName(repoPath) {
|
|
40682
|
+
const parts = repoPath.replace(/\/$/, "").split("/");
|
|
40683
|
+
return parts[parts.length - 1] || repoPath;
|
|
40684
|
+
}
|
|
40685
|
+
function isProjectPath(path2) {
|
|
40686
|
+
if (!path2 || path2 === "/" || path2 === ".") return false;
|
|
40687
|
+
return !isGlobalThread({ repoPath: path2 });
|
|
40688
|
+
}
|
|
40689
|
+
function historyStamp(thread) {
|
|
40690
|
+
return thread.archivedAt || thread.updatedAt;
|
|
40691
|
+
}
|
|
40692
|
+
function historyCursor(chat) {
|
|
40693
|
+
return `${chat.updatedAt} ${chat.id}`;
|
|
40694
|
+
}
|
|
40695
|
+
function listPhoneHistory(opts) {
|
|
40696
|
+
const query = opts?.query?.trim().toLowerCase() ?? "";
|
|
40697
|
+
const after = opts?.after?.trim() ?? "";
|
|
40698
|
+
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) => ({
|
|
40699
|
+
id: thread.id,
|
|
40700
|
+
title: thread.title?.trim() || "Untitled",
|
|
40701
|
+
preview: previewOf(thread),
|
|
40702
|
+
updatedAt: historyStamp(thread),
|
|
40703
|
+
where: isGlobalThread(thread) ? "Orchestration" : repoName(thread.repoPath),
|
|
40704
|
+
agent: thread.agent
|
|
40705
|
+
})).filter((chat) => {
|
|
40706
|
+
if (!query) return true;
|
|
40707
|
+
return `${chat.title} ${chat.where} ${chat.agent} ${chat.preview}`.toLowerCase().includes(query);
|
|
40708
|
+
});
|
|
40709
|
+
let start = 0;
|
|
40710
|
+
if (after) {
|
|
40711
|
+
const index = all.findIndex((chat) => historyCursor(chat) === after);
|
|
40712
|
+
start = index >= 0 ? index + 1 : all.length;
|
|
40713
|
+
}
|
|
40714
|
+
const chats = all.slice(start, start + HISTORY_PAGE_SIZE);
|
|
40715
|
+
const last = chats.at(-1);
|
|
40716
|
+
const more = start + chats.length < all.length;
|
|
40717
|
+
return {
|
|
40718
|
+
chats,
|
|
40719
|
+
total: all.length,
|
|
40720
|
+
...more && last ? { next: historyCursor(last) } : {}
|
|
40721
|
+
};
|
|
40722
|
+
}
|
|
40723
|
+
|
|
40724
|
+
// src/remote/phone-chats.ts
|
|
40725
|
+
var PHONE_CONTROL_PREFIX = "\0sb.phone\n";
|
|
40726
|
+
var PREVIEW_LIMIT2 = 90;
|
|
40286
40727
|
var AGENT_KINDS = /* @__PURE__ */ new Set(["claude", "codex", "opencode", "brightsy", "cursor"]);
|
|
40287
40728
|
var MAX_PHONE_FILES = 8;
|
|
40288
40729
|
var MAX_PHONE_FILE_CHARS = Math.ceil(8 * 1024 * 1024 * 4 / 3);
|
|
@@ -40312,7 +40753,7 @@ function parsePhoneControl(value) {
|
|
|
40312
40753
|
}
|
|
40313
40754
|
if (op === "sources") {
|
|
40314
40755
|
const repoPath = textField(value, "repoPath");
|
|
40315
|
-
if (!repoPath || !
|
|
40756
|
+
if (!repoPath || !isProjectPath2(repoPath)) return "invalid";
|
|
40316
40757
|
const query = textField(value, "query");
|
|
40317
40758
|
return { op: "sources", repoPath, ...query ? { query } : {} };
|
|
40318
40759
|
}
|
|
@@ -40330,7 +40771,15 @@ function parsePhoneControl(value) {
|
|
|
40330
40771
|
if (op === "create") return parsePhoneCreate(value);
|
|
40331
40772
|
const chatId = typeof value.chatId === "string" ? value.chatId.trim() : "";
|
|
40332
40773
|
if (!chatId) return "invalid";
|
|
40333
|
-
if (op === "open" || op === "archive" || op === "restore" || op === "stop"
|
|
40774
|
+
if (op === "open" || op === "archive" || op === "restore" || op === "stop" || op === "review" || op === "header") {
|
|
40775
|
+
return { op, chatId };
|
|
40776
|
+
}
|
|
40777
|
+
if (op === "git") {
|
|
40778
|
+
const action = textField(value, "action");
|
|
40779
|
+
if (!PHONE_HEADER_GIT_ACTIONS.includes(action)) return "invalid";
|
|
40780
|
+
return { op: "git", chatId, action };
|
|
40781
|
+
}
|
|
40782
|
+
if (op === "pane") return parsePhonePane(value, chatId);
|
|
40334
40783
|
if (op === "open-artifact") return parseOpenArtifact(value, chatId);
|
|
40335
40784
|
if (op === "open-file" || op === "media") {
|
|
40336
40785
|
const path2 = textField(value, "path");
|
|
@@ -40470,7 +40919,7 @@ function parsePhoneCreate(value) {
|
|
|
40470
40919
|
}
|
|
40471
40920
|
if (where === "project") {
|
|
40472
40921
|
const repoPath = textField(value, "repoPath");
|
|
40473
|
-
if (!repoPath || !
|
|
40922
|
+
if (!repoPath || !isProjectPath2(repoPath)) return "invalid";
|
|
40474
40923
|
const sourceRaw = textField(value, "sourceType");
|
|
40475
40924
|
let sourceType;
|
|
40476
40925
|
if (sourceRaw === "branch" || sourceRaw === "pr" || sourceRaw === "ticket") sourceType = sourceRaw;
|
|
@@ -40493,94 +40942,70 @@ function parsePhoneCreate(value) {
|
|
|
40493
40942
|
return "invalid";
|
|
40494
40943
|
}
|
|
40495
40944
|
function summarize(thread) {
|
|
40945
|
+
const pr = phonePrPill(thread);
|
|
40496
40946
|
return {
|
|
40497
40947
|
id: thread.id,
|
|
40498
40948
|
title: thread.title?.trim() || "Untitled",
|
|
40499
40949
|
status: thread.status,
|
|
40500
|
-
preview:
|
|
40950
|
+
preview: previewOf2(thread),
|
|
40501
40951
|
updatedAt: thread.updatedAt,
|
|
40502
|
-
agent: thread.agent
|
|
40952
|
+
agent: thread.agent,
|
|
40953
|
+
...pr?.url ? { prUrl: pr.url } : {},
|
|
40954
|
+
...pr?.number ? { prNumber: pr.number } : {},
|
|
40955
|
+
...pr?.status ? { prStatus: pr.status } : {}
|
|
40503
40956
|
};
|
|
40504
40957
|
}
|
|
40505
|
-
function
|
|
40958
|
+
function previewOf2(thread) {
|
|
40506
40959
|
const messages = listPhoneMessages(thread);
|
|
40507
40960
|
const last = messages[messages.length - 1];
|
|
40508
40961
|
if (!last) return "";
|
|
40509
|
-
return clip(last.text.replace(/\s+/g, " ").trim(),
|
|
40962
|
+
return clip(last.text.replace(/\s+/g, " ").trim(), PREVIEW_LIMIT2);
|
|
40510
40963
|
}
|
|
40511
|
-
function
|
|
40964
|
+
function repoName2(repoPath) {
|
|
40512
40965
|
const parts = repoPath.replace(/\/$/, "").split("/");
|
|
40513
40966
|
return parts[parts.length - 1] || repoPath;
|
|
40514
40967
|
}
|
|
40515
|
-
function
|
|
40968
|
+
function isProjectPath2(path2) {
|
|
40516
40969
|
if (!path2 || path2 === "/" || path2 === ".") return false;
|
|
40517
40970
|
return !isGlobalThread({ repoPath: path2 });
|
|
40518
40971
|
}
|
|
40519
40972
|
function listPhoneChats() {
|
|
40520
40973
|
return listGlobalThreads().slice().sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).map(summarize);
|
|
40521
40974
|
}
|
|
40522
|
-
function listPhoneSidebar() {
|
|
40975
|
+
function listPhoneSidebar(viewerLogin = phoneViewerLogin()) {
|
|
40976
|
+
const settings = loadAppSettings();
|
|
40523
40977
|
const threads = listThreads();
|
|
40524
40978
|
const byRepo = /* @__PURE__ */ new Map();
|
|
40525
40979
|
for (const thread of threads) {
|
|
40526
|
-
if (!
|
|
40980
|
+
if (!isProjectPath2(thread.repoPath) || isRemovedWorkspace(thread.repoPath)) continue;
|
|
40527
40981
|
const list = byRepo.get(thread.repoPath) ?? [];
|
|
40528
40982
|
list.push(thread);
|
|
40529
40983
|
byRepo.set(thread.repoPath, list);
|
|
40530
40984
|
}
|
|
40531
40985
|
for (const workspace of listWorkspaces()) {
|
|
40532
|
-
if (!
|
|
40986
|
+
if (!isProjectPath2(workspace.path) || byRepo.has(workspace.path)) continue;
|
|
40533
40987
|
byRepo.set(workspace.path, []);
|
|
40534
40988
|
}
|
|
40535
|
-
const projects = [...byRepo.entries()].sort(([a], [b]) =>
|
|
40536
|
-
name:
|
|
40989
|
+
const projects = [...byRepo.entries()].sort(([a], [b]) => repoName2(a).localeCompare(repoName2(b))).map(([path2, repoThreads]) => ({
|
|
40990
|
+
name: repoName2(path2),
|
|
40537
40991
|
path: path2,
|
|
40538
40992
|
worktrees: groupHomeBoardWorktrees(repoThreads).map((group) => {
|
|
40539
40993
|
const tags = workspaceTagsFromGroup(group);
|
|
40994
|
+
const badge = classifyWorktreeListBadges(
|
|
40995
|
+
group,
|
|
40996
|
+
viewerLogin,
|
|
40997
|
+
resolveProjectReviewLabel(settings, path2)
|
|
40998
|
+
)[0];
|
|
40540
40999
|
return {
|
|
40541
41000
|
label: worktreeDisplayLabelForGroup(group),
|
|
40542
41001
|
chats: group.map(summarize),
|
|
40543
|
-
...tags.length > 0 ? { tags } : {}
|
|
41002
|
+
...tags.length > 0 ? { tags } : {},
|
|
41003
|
+
...badge ? { badge: { id: badge.id, label: badge.label, title: badge.title } } : {}
|
|
40544
41004
|
};
|
|
40545
41005
|
})
|
|
40546
41006
|
}));
|
|
40547
41007
|
return { orchestration: listPhoneChats(), projects, defaults: phoneAccountDefaults() };
|
|
40548
41008
|
}
|
|
40549
|
-
function historyStamp(thread) {
|
|
40550
|
-
return thread.archivedAt || thread.updatedAt;
|
|
40551
|
-
}
|
|
40552
|
-
function historyCursor(chat) {
|
|
40553
|
-
return `${chat.updatedAt} ${chat.id}`;
|
|
40554
|
-
}
|
|
40555
|
-
function historyQueryMatch(chat, query) {
|
|
40556
|
-
if (!query) return true;
|
|
40557
|
-
return `${chat.title} ${chat.where} ${chat.agent} ${chat.preview}`.toLowerCase().includes(query);
|
|
40558
|
-
}
|
|
40559
|
-
function listPhoneHistory(opts) {
|
|
40560
|
-
const query = opts?.query?.trim().toLowerCase() ?? "";
|
|
40561
|
-
const after = opts?.after?.trim() ?? "";
|
|
40562
|
-
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) => ({
|
|
40563
|
-
id: thread.id,
|
|
40564
|
-
title: thread.title?.trim() || "Untitled",
|
|
40565
|
-
preview: previewOf(thread),
|
|
40566
|
-
updatedAt: historyStamp(thread),
|
|
40567
|
-
where: isGlobalThread(thread) ? "Orchestration" : repoName(thread.repoPath),
|
|
40568
|
-
agent: thread.agent
|
|
40569
|
-
})).filter((chat) => historyQueryMatch(chat, query));
|
|
40570
|
-
let start = 0;
|
|
40571
|
-
if (after) {
|
|
40572
|
-
const index = all.findIndex((chat) => historyCursor(chat) === after);
|
|
40573
|
-
start = index >= 0 ? index + 1 : all.length;
|
|
40574
|
-
}
|
|
40575
|
-
const chats = all.slice(start, start + HISTORY_PAGE_SIZE);
|
|
40576
|
-
const last = chats.at(-1);
|
|
40577
|
-
const more = start + chats.length < all.length;
|
|
40578
|
-
return {
|
|
40579
|
-
chats,
|
|
40580
|
-
total: all.length,
|
|
40581
|
-
...more && last ? { next: historyCursor(last) } : {}
|
|
40582
|
-
};
|
|
40583
|
-
}
|
|
40584
41009
|
function matchPhoneThread(chatId) {
|
|
40585
41010
|
const id = chatId.trim();
|
|
40586
41011
|
if (!id) return null;
|
|
@@ -40593,7 +41018,7 @@ function matchPhoneThread(chatId) {
|
|
|
40593
41018
|
}
|
|
40594
41019
|
function visiblePhoneThread(thread) {
|
|
40595
41020
|
if (isGlobalThread(thread)) return true;
|
|
40596
|
-
return
|
|
41021
|
+
return isProjectPath2(thread.repoPath);
|
|
40597
41022
|
}
|
|
40598
41023
|
function findPhoneChat(chatId) {
|
|
40599
41024
|
const thread = matchPhoneThread(chatId);
|
|
@@ -40659,7 +41084,7 @@ function projectPlace(thread) {
|
|
|
40659
41084
|
}
|
|
40660
41085
|
function assertKnownProject(repoPath) {
|
|
40661
41086
|
const path2 = repoPath.trim();
|
|
40662
|
-
if (!
|
|
41087
|
+
if (!isProjectPath2(path2)) throw new Error("Pick a project on this Mac.");
|
|
40663
41088
|
const known = listWorkspaces().some((workspace) => sameRepoPath3(workspace.path, path2)) || listThreads().some(
|
|
40664
41089
|
(thread) => thread.status !== "archived" && sameRepoPath3(thread.repoPath, path2)
|
|
40665
41090
|
);
|
|
@@ -40762,6 +41187,55 @@ function archivePhoneChat(chatId) {
|
|
|
40762
41187
|
return true;
|
|
40763
41188
|
}
|
|
40764
41189
|
|
|
41190
|
+
// src/remote/phone-workspace-ops.ts
|
|
41191
|
+
function dispatchPhoneWorkspace(cmd, emit, publishOpened) {
|
|
41192
|
+
if (cmd.op === "pane") {
|
|
41193
|
+
const thread = readPhoneThread(cmd.chatId);
|
|
41194
|
+
if (!thread) {
|
|
41195
|
+
emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
41196
|
+
return;
|
|
41197
|
+
}
|
|
41198
|
+
void readPhonePane(thread, cmd.pane, cmd.scope).then((body) => {
|
|
41199
|
+
emit({
|
|
41200
|
+
op: "pane",
|
|
41201
|
+
chatId: cmd.chatId,
|
|
41202
|
+
pane: cmd.pane,
|
|
41203
|
+
rows: body.rows,
|
|
41204
|
+
...body.note ? { note: body.note } : {}
|
|
41205
|
+
});
|
|
41206
|
+
});
|
|
41207
|
+
return;
|
|
41208
|
+
}
|
|
41209
|
+
if (cmd.op === "header") {
|
|
41210
|
+
const thread = readPhoneThread(cmd.chatId);
|
|
41211
|
+
if (!thread) {
|
|
41212
|
+
emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
41213
|
+
return;
|
|
41214
|
+
}
|
|
41215
|
+
void readPhoneHeader(thread).then((header) => {
|
|
41216
|
+
emit({ op: "header", chatId: cmd.chatId, ...header });
|
|
41217
|
+
});
|
|
41218
|
+
return;
|
|
41219
|
+
}
|
|
41220
|
+
if (cmd.op === "git") {
|
|
41221
|
+
void getOrchestrator().askGit(cmd.chatId, cmd.action).then((tab) => publishOpened(tab.id)).catch((err) => {
|
|
41222
|
+
emit({
|
|
41223
|
+
op: "error",
|
|
41224
|
+
chatId: cmd.chatId,
|
|
41225
|
+
message: err instanceof Error ? err.message : String(err)
|
|
41226
|
+
});
|
|
41227
|
+
});
|
|
41228
|
+
return;
|
|
41229
|
+
}
|
|
41230
|
+
void getOrchestrator().requestReview(cmd.chatId).then((tab) => publishOpened(tab.id)).catch((err) => {
|
|
41231
|
+
emit({
|
|
41232
|
+
op: "error",
|
|
41233
|
+
chatId: cmd.chatId,
|
|
41234
|
+
message: err instanceof Error ? err.message : String(err)
|
|
41235
|
+
});
|
|
41236
|
+
});
|
|
41237
|
+
}
|
|
41238
|
+
|
|
40765
41239
|
// src/remote/phone-sources.ts
|
|
40766
41240
|
init_worktree();
|
|
40767
41241
|
function sourceWarning(label, err) {
|
|
@@ -41338,10 +41812,25 @@ function handlePhoneControl(cmd, opts) {
|
|
|
41338
41812
|
emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That file is not in this worktree." });
|
|
41339
41813
|
return;
|
|
41340
41814
|
}
|
|
41341
|
-
|
|
41342
|
-
|
|
41815
|
+
const preview = phoneFilePreview(thread.worktreePath, relative, kind === "dir" ? "dir" : "file");
|
|
41816
|
+
if (!preview) {
|
|
41817
|
+
emitControl(opts, {
|
|
41818
|
+
op: "error",
|
|
41819
|
+
chatId: cmd.chatId,
|
|
41820
|
+
message: "That file could not be opened on the phone."
|
|
41821
|
+
});
|
|
41822
|
+
return;
|
|
41823
|
+
}
|
|
41824
|
+
emitControl(opts, {
|
|
41825
|
+
op: "file",
|
|
41826
|
+
chatId: cmd.chatId,
|
|
41343
41827
|
path: relative,
|
|
41344
|
-
|
|
41828
|
+
content: preview.content,
|
|
41829
|
+
encoding: preview.encoding,
|
|
41830
|
+
...preview.binary ? { binary: true } : {},
|
|
41831
|
+
...preview.directory ? { directory: true } : {},
|
|
41832
|
+
...preview.truncated ? { truncated: true } : {},
|
|
41833
|
+
...preview.mime ? { mime: preview.mime } : {},
|
|
41345
41834
|
...cmd.startLine != null ? { startLine: cmd.startLine } : {},
|
|
41346
41835
|
...cmd.endLine != null ? { endLine: cmd.endLine } : {}
|
|
41347
41836
|
});
|
|
@@ -41356,13 +41845,34 @@ function handlePhoneControl(cmd, opts) {
|
|
|
41356
41845
|
emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
41357
41846
|
return;
|
|
41358
41847
|
}
|
|
41359
|
-
|
|
41360
|
-
|
|
41361
|
-
|
|
41362
|
-
|
|
41848
|
+
const liveParts = readTurnLiveParts(thread.id);
|
|
41849
|
+
const docs = phoneArtifactDocuments([
|
|
41850
|
+
...(thread.messages ?? []).filter((message) => message.role === "agent").map((message) => ({ text: message.text.trim(), parts: message.parts })),
|
|
41851
|
+
{ text: visibleAssistantText(void 0, liveParts), parts: liveParts }
|
|
41852
|
+
]);
|
|
41853
|
+
const body = phoneArtifactReply(docs, { title: cmd.title, ...cmd.hint ? { hint: cmd.hint } : {} });
|
|
41854
|
+
if (!body) {
|
|
41855
|
+
emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That artifact is not in this chat." });
|
|
41856
|
+
return;
|
|
41857
|
+
}
|
|
41858
|
+
emitControl(opts, {
|
|
41859
|
+
op: "artifact",
|
|
41860
|
+
chatId: cmd.chatId,
|
|
41861
|
+
title: body.title,
|
|
41862
|
+
kind: body.kind,
|
|
41863
|
+
content: body.content,
|
|
41864
|
+
...body.truncated ? { truncated: true } : {}
|
|
41363
41865
|
});
|
|
41364
41866
|
return;
|
|
41365
41867
|
}
|
|
41868
|
+
if (cmd.op === "pane" || cmd.op === "header" || cmd.op === "git" || cmd.op === "review") {
|
|
41869
|
+
dispatchPhoneWorkspace(
|
|
41870
|
+
cmd,
|
|
41871
|
+
(reply) => emitControl(opts, reply),
|
|
41872
|
+
(chatId2) => publishOpenedChat(opts, chatId2)
|
|
41873
|
+
);
|
|
41874
|
+
return;
|
|
41875
|
+
}
|
|
41366
41876
|
if (cmd.op === "stop") {
|
|
41367
41877
|
if (!readPhoneThread(cmd.chatId)) {
|
|
41368
41878
|
emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
@@ -41441,7 +41951,7 @@ function handleRemoteInbound(text7, opts) {
|
|
|
41441
41951
|
}
|
|
41442
41952
|
|
|
41443
41953
|
// src/remote/phone-board-sync.ts
|
|
41444
|
-
var
|
|
41954
|
+
var import_node_path70 = require("path");
|
|
41445
41955
|
var import_chokidar = require("chokidar");
|
|
41446
41956
|
init_orchestrator();
|
|
41447
41957
|
init_paths();
|
|
@@ -41453,7 +41963,9 @@ function chatKey(row2) {
|
|
|
41453
41963
|
function phoneBoardKey(sidebar) {
|
|
41454
41964
|
const orchestration = sidebar.orchestration.map(chatKey).join("\n");
|
|
41455
41965
|
const projects = sidebar.projects.map((project) => {
|
|
41456
|
-
const trees = project.worktrees.map(
|
|
41966
|
+
const trees = project.worktrees.map(
|
|
41967
|
+
(tree) => `${tree.label} ${tree.badge?.id ?? ""} ${(tree.tags ?? []).join(",")} ${tree.chats.map(chatKey).join("\n")}`
|
|
41968
|
+
).join("\n");
|
|
41457
41969
|
return `${project.path} ${project.name}
|
|
41458
41970
|
${trees}`;
|
|
41459
41971
|
}).join("\n---\n");
|
|
@@ -41484,7 +41996,7 @@ function subscribePhoneBoard(notify) {
|
|
|
41484
41996
|
threads.on("error", () => void 0);
|
|
41485
41997
|
const data = (0, import_chokidar.watch)(appDataDir(), { ignoreInitial: true, depth: 0 });
|
|
41486
41998
|
const onData = (changed) => {
|
|
41487
|
-
const name = (0,
|
|
41999
|
+
const name = (0, import_node_path70.basename)(changed);
|
|
41488
42000
|
if (name !== "workspaces.json" && name !== "removed-workspaces.json" && name !== "settings.json") return;
|
|
41489
42001
|
notify();
|
|
41490
42002
|
};
|
|
@@ -41531,10 +42043,23 @@ function startPhoneBoardSync(opts) {
|
|
|
41531
42043
|
const unsubscribe = (opts.subscribe ?? subscribePhoneBoard)(schedule);
|
|
41532
42044
|
const offEvents = (opts.events ?? ((listener) => getOrchestrator().on(listener)))(handle);
|
|
41533
42045
|
push();
|
|
42046
|
+
let loginTimer = null;
|
|
42047
|
+
const refreshViewerLogin = opts.refreshViewerLogin;
|
|
42048
|
+
if (refreshViewerLogin) {
|
|
42049
|
+
const afterLogin = () => {
|
|
42050
|
+
void refreshViewerLogin().then(() => {
|
|
42051
|
+
if (!stopped) push();
|
|
42052
|
+
});
|
|
42053
|
+
};
|
|
42054
|
+
afterLogin();
|
|
42055
|
+
loginTimer = setInterval(afterLogin, opts.viewerLoginIntervalMs ?? 5 * 6e4);
|
|
42056
|
+
}
|
|
41534
42057
|
return () => {
|
|
41535
42058
|
stopped = true;
|
|
41536
42059
|
if (timer) clearTimeout(timer);
|
|
41537
42060
|
timer = null;
|
|
42061
|
+
if (loginTimer) clearInterval(loginTimer);
|
|
42062
|
+
loginTimer = null;
|
|
41538
42063
|
unsubscribe();
|
|
41539
42064
|
offEvents();
|
|
41540
42065
|
};
|
|
@@ -41601,7 +42126,8 @@ function runRemoteHost(opts) {
|
|
|
41601
42126
|
},
|
|
41602
42127
|
onRunningChat: (chatId) => {
|
|
41603
42128
|
deliverPhoneTurn({ onOutbound: emit }, chatId);
|
|
41604
|
-
}
|
|
42129
|
+
},
|
|
42130
|
+
refreshViewerLogin: () => refreshPhoneViewerLogin(true)
|
|
41605
42131
|
});
|
|
41606
42132
|
};
|
|
41607
42133
|
const send3 = (msg) => {
|
|
@@ -41781,14 +42307,14 @@ init_fetch();
|
|
|
41781
42307
|
|
|
41782
42308
|
// src/relay/accounts.ts
|
|
41783
42309
|
var import_node_crypto27 = require("crypto");
|
|
41784
|
-
var
|
|
42310
|
+
var import_node_fs72 = require("fs");
|
|
41785
42311
|
var import_node_module7 = require("module");
|
|
41786
|
-
var
|
|
42312
|
+
var import_node_path71 = require("path");
|
|
41787
42313
|
var GITHUB_ACCOUNT_ISSUER = "https://github.com";
|
|
41788
42314
|
var RELAY_DEVICE_CAP = 3;
|
|
41789
42315
|
function thisModuleFile3() {
|
|
41790
42316
|
const cjsFile = typeof __filename !== "undefined" ? __filename : "";
|
|
41791
|
-
return cjsFile || process.argv[1] || (0,
|
|
42317
|
+
return cjsFile || process.argv[1] || (0, import_node_path71.join)(process.cwd(), "package.json");
|
|
41792
42318
|
}
|
|
41793
42319
|
function openDatabase(filePath) {
|
|
41794
42320
|
const req = (0, import_node_module7.createRequire)(thisModuleFile3());
|
|
@@ -41825,8 +42351,8 @@ function assertRelayAccountsDurable(filePath) {
|
|
|
41825
42351
|
if (process.env.SIDEBOARD_RELAY_ACCOUNTS_EPHEMERAL === "1") return;
|
|
41826
42352
|
if (process.env.NODE_ENV !== "production") return;
|
|
41827
42353
|
if (process.platform !== "linux") return;
|
|
41828
|
-
const dir = (0,
|
|
41829
|
-
const mounts = (0,
|
|
42354
|
+
const dir = (0, import_node_fs72.realpathSync)((0, import_node_path71.dirname)(filePath));
|
|
42355
|
+
const mounts = (0, import_node_fs72.readFileSync)("/proc/mounts", "utf8");
|
|
41830
42356
|
const point = longestMountPoint(dir, mounts);
|
|
41831
42357
|
if (point === "/") {
|
|
41832
42358
|
throw new Error(
|
|
@@ -41837,7 +42363,7 @@ function assertRelayAccountsDurable(filePath) {
|
|
|
41837
42363
|
var RelayAccountStore = class {
|
|
41838
42364
|
db;
|
|
41839
42365
|
constructor(filePath) {
|
|
41840
|
-
(0,
|
|
42366
|
+
(0, import_node_fs72.mkdirSync)((0, import_node_path71.dirname)(filePath), { recursive: true });
|
|
41841
42367
|
this.db = openDatabase(filePath);
|
|
41842
42368
|
this.db.exec(`
|
|
41843
42369
|
CREATE TABLE IF NOT EXISTS accounts (
|
|
@@ -41864,9 +42390,15 @@ var RelayAccountStore = class {
|
|
|
41864
42390
|
account_id TEXT NOT NULL,
|
|
41865
42391
|
host_secret_hash TEXT NOT NULL,
|
|
41866
42392
|
device_label TEXT NOT NULL,
|
|
41867
|
-
created_at INTEGER NOT NULL
|
|
42393
|
+
created_at INTEGER NOT NULL,
|
|
42394
|
+
session_token_hash TEXT
|
|
41868
42395
|
);
|
|
41869
42396
|
`);
|
|
42397
|
+
const cols = this.db.prepare("PRAGMA table_info(devices)").all();
|
|
42398
|
+
if (!cols.some((col) => col.name === "session_token_hash")) {
|
|
42399
|
+
this.db.exec("ALTER TABLE devices ADD COLUMN session_token_hash TEXT");
|
|
42400
|
+
}
|
|
42401
|
+
this.db.exec("CREATE INDEX IF NOT EXISTS devices_session ON devices(session_token_hash)");
|
|
41870
42402
|
}
|
|
41871
42403
|
close() {
|
|
41872
42404
|
this.db.close();
|
|
@@ -41995,6 +42527,20 @@ var RelayAccountStore = class {
|
|
|
41995
42527
|
throw err;
|
|
41996
42528
|
}
|
|
41997
42529
|
}
|
|
42530
|
+
/** Store a phone pairing when it is created. A later process looks it up on a memory miss. */
|
|
42531
|
+
savePhoneSession(deviceId, sessionToken) {
|
|
42532
|
+
const id = deviceId.trim();
|
|
42533
|
+
const token = sessionToken.trim();
|
|
42534
|
+
if (!id || !token) return;
|
|
42535
|
+
this.db.prepare("UPDATE devices SET session_token_hash = ? WHERE device_id = ?").run(sha256(token), id);
|
|
42536
|
+
}
|
|
42537
|
+
findPhoneSession(sessionToken) {
|
|
42538
|
+
const token = sessionToken.trim();
|
|
42539
|
+
if (!token) return null;
|
|
42540
|
+
const row2 = this.db.prepare("SELECT device_id, device_label FROM devices WHERE session_token_hash = ?").get(sha256(token));
|
|
42541
|
+
if (!row2) return null;
|
|
42542
|
+
return { deviceId: row2.device_id, deviceLabel: row2.device_label };
|
|
42543
|
+
}
|
|
41998
42544
|
requireAccount(accountId) {
|
|
41999
42545
|
const row2 = this.db.prepare("SELECT id FROM accounts WHERE id = ?").get(accountId);
|
|
42000
42546
|
if (!row2) throw new Error("That Sideboard account is no longer on the relay.");
|
|
@@ -42851,9 +43397,9 @@ var import_node_http5 = require("http");
|
|
|
42851
43397
|
var import_ws6 = require("ws");
|
|
42852
43398
|
|
|
42853
43399
|
// src/relay/static.ts
|
|
42854
|
-
var
|
|
43400
|
+
var import_node_fs73 = require("fs");
|
|
42855
43401
|
var import_promises = require("fs/promises");
|
|
42856
|
-
var
|
|
43402
|
+
var import_node_path72 = __toESM(require("path"), 1);
|
|
42857
43403
|
var TYPES = {
|
|
42858
43404
|
".css": "text/css; charset=utf-8",
|
|
42859
43405
|
".html": "text/html; charset=utf-8",
|
|
@@ -42884,9 +43430,9 @@ function resolveStaticPath(root, requestUrl) {
|
|
|
42884
43430
|
return null;
|
|
42885
43431
|
}
|
|
42886
43432
|
if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
|
|
42887
|
-
const rootResolved =
|
|
42888
|
-
const candidate =
|
|
42889
|
-
if (candidate !== rootResolved && !candidate.startsWith(rootResolved +
|
|
43433
|
+
const rootResolved = import_node_path72.default.resolve(root);
|
|
43434
|
+
const candidate = import_node_path72.default.resolve(rootResolved, `.${pathname}`);
|
|
43435
|
+
if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path72.default.sep)) {
|
|
42890
43436
|
return null;
|
|
42891
43437
|
}
|
|
42892
43438
|
return candidate;
|
|
@@ -42900,7 +43446,7 @@ async function fileSize(file) {
|
|
|
42900
43446
|
}
|
|
42901
43447
|
}
|
|
42902
43448
|
function sendFile(req, res, file, size) {
|
|
42903
|
-
const ext =
|
|
43449
|
+
const ext = import_node_path72.default.extname(file).toLowerCase();
|
|
42904
43450
|
res.writeHead(200, {
|
|
42905
43451
|
"Content-Type": TYPES[ext] ?? "application/octet-stream",
|
|
42906
43452
|
"Content-Length": size,
|
|
@@ -42910,7 +43456,7 @@ function sendFile(req, res, file, size) {
|
|
|
42910
43456
|
res.end();
|
|
42911
43457
|
return true;
|
|
42912
43458
|
}
|
|
42913
|
-
(0,
|
|
43459
|
+
(0, import_node_fs73.createReadStream)(file).pipe(res);
|
|
42914
43460
|
return true;
|
|
42915
43461
|
}
|
|
42916
43462
|
async function tryServeStatic(req, res, root) {
|
|
@@ -42919,7 +43465,7 @@ async function tryServeStatic(req, res, root) {
|
|
|
42919
43465
|
if (!candidate) return false;
|
|
42920
43466
|
const direct = await fileSize(candidate);
|
|
42921
43467
|
if (direct != null) return sendFile(req, res, candidate, direct);
|
|
42922
|
-
const asIndex =
|
|
43468
|
+
const asIndex = import_node_path72.default.join(candidate, "index.html");
|
|
42923
43469
|
const indexSize = await fileSize(asIndex);
|
|
42924
43470
|
if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
|
|
42925
43471
|
return false;
|
|
@@ -42985,6 +43531,10 @@ async function startRelayServer(opts) {
|
|
|
42985
43531
|
const decision = accountStore.authorizeHost(input);
|
|
42986
43532
|
return decision.ok ? { ok: true } : { ok: false, message: decision.message };
|
|
42987
43533
|
} : void 0,
|
|
43534
|
+
phoneSessions: accountStore ? {
|
|
43535
|
+
find: (sessionToken) => accountStore.findPhoneSession(sessionToken),
|
|
43536
|
+
save: (deviceId, sessionToken) => accountStore.savePhoneSession(deviceId, sessionToken)
|
|
43537
|
+
} : void 0,
|
|
42988
43538
|
onAuthorized: (socket) => authMarks.get(socket)?.()
|
|
42989
43539
|
});
|
|
42990
43540
|
const handleCallback = async (reqUrl, res) => {
|
|
@@ -43280,8 +43830,8 @@ async function startRelayServer(opts) {
|
|
|
43280
43830
|
}
|
|
43281
43831
|
|
|
43282
43832
|
// src/slack/outbound-watch.ts
|
|
43283
|
-
var
|
|
43284
|
-
var
|
|
43833
|
+
var import_node_fs74 = require("fs");
|
|
43834
|
+
var import_node_path73 = require("path");
|
|
43285
43835
|
init_paths();
|
|
43286
43836
|
init_private_file();
|
|
43287
43837
|
init_secure_file();
|
|
@@ -43294,7 +43844,7 @@ var POLL_INTERVAL_MS = 12e3;
|
|
|
43294
43844
|
var lastPollMs = 0;
|
|
43295
43845
|
var nameCache = /* @__PURE__ */ new Map();
|
|
43296
43846
|
function storePath4() {
|
|
43297
|
-
return (0,
|
|
43847
|
+
return (0, import_node_path73.join)(appDataDir(), "slack-outbound-watch.json");
|
|
43298
43848
|
}
|
|
43299
43849
|
function watchId(teamId, channelId, ts) {
|
|
43300
43850
|
return `${teamId}:${channelId}:${ts}`;
|
|
@@ -43307,9 +43857,9 @@ function tsNewer(a, b) {
|
|
|
43307
43857
|
}
|
|
43308
43858
|
function readStore4() {
|
|
43309
43859
|
const path2 = storePath4();
|
|
43310
|
-
if (!(0,
|
|
43860
|
+
if (!(0, import_node_fs74.existsSync)(path2)) return [];
|
|
43311
43861
|
try {
|
|
43312
|
-
const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0,
|
|
43862
|
+
const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs74.readFileSync)(path2, "utf8"));
|
|
43313
43863
|
return Array.isArray(parsed?.watches) ? parsed.watches : [];
|
|
43314
43864
|
} catch {
|
|
43315
43865
|
return [];
|
|
@@ -43470,8 +44020,8 @@ function displayName(user) {
|
|
|
43470
44020
|
return fromProfile || user.real_name?.trim() || user.name?.trim() || "";
|
|
43471
44021
|
}
|
|
43472
44022
|
async function resolveUserName(token, userId, fallback, fetchImpl) {
|
|
43473
|
-
const
|
|
43474
|
-
if (
|
|
44023
|
+
const cached2 = nameCache.get(userId);
|
|
44024
|
+
if (cached2) return cached2;
|
|
43475
44025
|
try {
|
|
43476
44026
|
const data = await slackApi(token, "users.info", { user: userId }, fetchImpl);
|
|
43477
44027
|
const name = displayName(data.user ?? {}) || fallback;
|