@sideboard-ai/core 0.1.301 → 0.1.303
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/claude-runner.cjs +28 -7
- package/dist/agents/claude-runner.js +22 -8
- package/dist/{agents-V2SRC2LH.js → agents-3GN4E2X3.js} +5 -5
- package/dist/{agents-ETD2LWP4.js → agents-XLHFVC64.js} +4 -4
- package/dist/{chunk-FUXOZHHF.js → chunk-3TZRSKFJ.js} +4 -4
- package/dist/{chunk-JTW4CZEQ.js → chunk-545DY7CQ.js} +13 -13
- package/dist/{chunk-3XDU7RIE.js → chunk-5MVOZLN7.js} +1 -1
- package/dist/{chunk-2KJE53ZJ.js → chunk-7AYOOG77.js} +5 -4
- package/dist/{chunk-7SEWZXPT.js → chunk-BRFDPV7Z.js} +2 -2
- package/dist/{chunk-EP4IIKP4.js → chunk-DW6NNYUJ.js} +4 -4
- package/dist/{chunk-FO3NKZVJ.js → chunk-F2PVRF3B.js} +9 -0
- package/dist/{chunk-KIWICTLW.js → chunk-GEFH4QFX.js} +1 -1
- package/dist/{chunk-FPJFK4LB.js → chunk-GS6O65Q2.js} +11 -11
- package/dist/{chunk-WV33I2GC.js → chunk-P2GHFJOB.js} +1 -1
- package/dist/{chunk-OJKSDRZU.js → chunk-RAGDMEIB.js} +4 -4
- package/dist/{chunk-LZ744H2I.js → chunk-YCDHLK2Z.js} +2 -2
- package/dist/{conductor-57UKACQA.js → conductor-2AVFGSQJ.js} +4 -4
- package/dist/{conductor-57KZLWTB.js → conductor-ATST6OMC.js} +5 -5
- package/dist/{coordinator-prompt-VOWUJRVJ.js → coordinator-prompt-CQPUNMRX.js} +4 -4
- package/dist/{coordinator-prompt-2TBRFJZ5.js → coordinator-prompt-TIAI6A47.js} +3 -3
- package/dist/{global-workspace-V6SOEETA.js → global-workspace-7YABMLQG.js} +4 -4
- package/dist/{global-workspace-T5C7EMVV.js → global-workspace-UZJUHGQP.js} +3 -3
- package/dist/index.cjs +851 -367
- package/dist/index.d.cts +104 -2
- package/dist/index.d.ts +104 -2
- package/dist/index.js +825 -354
- package/dist/mcp/run-stdio.js +6 -6
- package/dist/{orchestrator-C43M3JDK.js → orchestrator-COX6QO2B.js} +6 -6
- package/dist/{orchestrator-JEF7JC3S.js → orchestrator-CZPRDLHV.js} +7 -7
- package/dist/{thread-store-POAYB2ID.js → thread-store-PNX4HVLO.js} +2 -2
- package/dist/{thread-store-BMNJEJUH.js → thread-store-VC4AIKN3.js} +1 -1
- package/dist/{workspaces-EWVIEJ4O.js → workspaces-6DJL4IST.js} +4 -4
- package/dist/{workspaces-7ZYTTB4A.js → workspaces-GZ7F5EJR.js} +3 -3
- package/dist/{worktree-2Q6J2VWZ.js → worktree-2BYVRYPJ.js} +3 -3
- package/dist/{worktree-ZAJGYKCB.js → worktree-VXPOY2LE.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-FBZODCO4.js → chunk-FA3V56KF.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -196,7 +196,7 @@ import {
|
|
|
196
196
|
worktreeCleanupSettings,
|
|
197
197
|
writeAgentBlock,
|
|
198
198
|
writeWorktreeFile
|
|
199
|
-
} from "./chunk-
|
|
199
|
+
} from "./chunk-545DY7CQ.js";
|
|
200
200
|
import {
|
|
201
201
|
LEGACY_PLAN_FILE_REL,
|
|
202
202
|
PLAN_FILE_NAME,
|
|
@@ -261,6 +261,7 @@ import {
|
|
|
261
261
|
listCursorModels,
|
|
262
262
|
listModelsForAgent,
|
|
263
263
|
listOpencodeModels,
|
|
264
|
+
listUserClaudeMcpServerEntries,
|
|
264
265
|
mcpStatusThinkingFromClaudeInit,
|
|
265
266
|
mergeSideboardIntoMcpServersJson,
|
|
266
267
|
normalizeTurnInput,
|
|
@@ -283,7 +284,7 @@ import {
|
|
|
283
284
|
userMcpNamesToDisable,
|
|
284
285
|
worktreeMcpToolNames,
|
|
285
286
|
writeInjectedMcpConfig
|
|
286
|
-
} from "./chunk-
|
|
287
|
+
} from "./chunk-7AYOOG77.js";
|
|
287
288
|
import {
|
|
288
289
|
brightsyMcpServerName,
|
|
289
290
|
connectBrightsyTeam,
|
|
@@ -411,7 +412,7 @@ import {
|
|
|
411
412
|
workspaceAgentChatSummaries,
|
|
412
413
|
worktreeRunPortsPath,
|
|
413
414
|
wrapReviewSkillMarkdown
|
|
414
|
-
} from "./chunk-
|
|
415
|
+
} from "./chunk-RAGDMEIB.js";
|
|
415
416
|
import {
|
|
416
417
|
STDERR_EVENT_MAX_CHARS,
|
|
417
418
|
STDOUT_EVENT_MAX_CHARS,
|
|
@@ -611,7 +612,7 @@ import {
|
|
|
611
612
|
stageBuffersAsAttachments,
|
|
612
613
|
syncWorkspacesFromThreads,
|
|
613
614
|
takenTeamSlugsForOrchestration
|
|
614
|
-
} from "./chunk-
|
|
615
|
+
} from "./chunk-BRFDPV7Z.js";
|
|
615
616
|
import {
|
|
616
617
|
GITHUB_PR_BODY_MAX_CHARS,
|
|
617
618
|
GITHUB_PR_BODY_SAFE_CHARS,
|
|
@@ -699,7 +700,7 @@ import {
|
|
|
699
700
|
suggestSlug,
|
|
700
701
|
switchWorktreeBranch,
|
|
701
702
|
warmGithubAgentAuth
|
|
702
|
-
} from "./chunk-
|
|
703
|
+
} from "./chunk-GEFH4QFX.js";
|
|
703
704
|
import {
|
|
704
705
|
FAMOUS_SOCCER_TEAMS,
|
|
705
706
|
allocateTeamName,
|
|
@@ -732,7 +733,7 @@ import {
|
|
|
732
733
|
worktreeNameFromPath,
|
|
733
734
|
worktreeSlugForTicket,
|
|
734
735
|
writeThread
|
|
735
|
-
} from "./chunk-
|
|
736
|
+
} from "./chunk-P2GHFJOB.js";
|
|
736
737
|
import "./chunk-2P4XKWV2.js";
|
|
737
738
|
import {
|
|
738
739
|
BRIGHTSY_OAUTH_CLIENT_ID,
|
|
@@ -758,6 +759,7 @@ import {
|
|
|
758
759
|
CLAUDE_CHROME_ALLOWED_TOOLS,
|
|
759
760
|
CLAUDE_POSTHOG_CONNECTOR_ALLOWED_TOOLS,
|
|
760
761
|
CLAUDE_PRINT_BG_WAIT_CEILING_MS,
|
|
762
|
+
isClaudeAiConnectorMcp,
|
|
761
763
|
mcpAllowTools,
|
|
762
764
|
mcpAllowToolsFromNames,
|
|
763
765
|
mcpAuthWarnings,
|
|
@@ -765,7 +767,7 @@ import {
|
|
|
765
767
|
mcpServerPermissionUpdate,
|
|
766
768
|
parseMcpList,
|
|
767
769
|
sanitizeMcpServerName
|
|
768
|
-
} from "./chunk-
|
|
770
|
+
} from "./chunk-F2PVRF3B.js";
|
|
769
771
|
import {
|
|
770
772
|
clipMcpToolResult,
|
|
771
773
|
cursorToolErrorText
|
|
@@ -5413,7 +5415,7 @@ async function handleJobStop(params, _ctx) {
|
|
|
5413
5415
|
return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
|
|
5414
5416
|
}
|
|
5415
5417
|
async function defaultTurnWait(ref, timeoutMs) {
|
|
5416
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
5418
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-CZPRDLHV.js");
|
|
5417
5419
|
const orch = getOrchestrator2();
|
|
5418
5420
|
const thread = await orch.waitForTurn(ref, timeoutMs, {
|
|
5419
5421
|
resolveIfStillRunning: true
|
|
@@ -7505,6 +7507,359 @@ async function runCloudConnect(opts) {
|
|
|
7505
7507
|
}
|
|
7506
7508
|
}
|
|
7507
7509
|
|
|
7510
|
+
// src/mcp-ui/meta.ts
|
|
7511
|
+
var HTML_MAX = 15e5;
|
|
7512
|
+
function matchClaudeMcpTool(toolName, serverNames) {
|
|
7513
|
+
const name = toolName.trim();
|
|
7514
|
+
if (!name.startsWith("mcp__")) return null;
|
|
7515
|
+
const ranked = [...serverNames].filter((s) => s.trim()).sort((a, b) => b.length - a.length);
|
|
7516
|
+
for (const server of ranked) {
|
|
7517
|
+
const prefix = `mcp__${server}__`;
|
|
7518
|
+
if (!name.startsWith(prefix)) continue;
|
|
7519
|
+
const tool = name.slice(prefix.length).trim();
|
|
7520
|
+
if (!tool) continue;
|
|
7521
|
+
return { server, tool };
|
|
7522
|
+
}
|
|
7523
|
+
return null;
|
|
7524
|
+
}
|
|
7525
|
+
function parseMcpServerLaunch(entry) {
|
|
7526
|
+
const command = typeof entry.command === "string" ? entry.command.trim() : "";
|
|
7527
|
+
if (command) {
|
|
7528
|
+
const args = Array.isArray(entry.args) ? entry.args.filter((a) => typeof a === "string") : [];
|
|
7529
|
+
const env = stringRecord(entry.env);
|
|
7530
|
+
const cwd = typeof entry.cwd === "string" && entry.cwd.trim() ? entry.cwd.trim() : void 0;
|
|
7531
|
+
return {
|
|
7532
|
+
kind: "stdio",
|
|
7533
|
+
command,
|
|
7534
|
+
args,
|
|
7535
|
+
...env ? { env } : {},
|
|
7536
|
+
...cwd ? { cwd } : {}
|
|
7537
|
+
};
|
|
7538
|
+
}
|
|
7539
|
+
const url = typeof entry.url === "string" ? entry.url.trim() : "";
|
|
7540
|
+
if (!url) return null;
|
|
7541
|
+
let parsed;
|
|
7542
|
+
try {
|
|
7543
|
+
parsed = new URL(url);
|
|
7544
|
+
} catch {
|
|
7545
|
+
return null;
|
|
7546
|
+
}
|
|
7547
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
7548
|
+
const type = typeof entry.type === "string" ? entry.type.trim().toLowerCase() : "";
|
|
7549
|
+
const headers = stringRecord(entry.headers);
|
|
7550
|
+
return {
|
|
7551
|
+
kind: type === "sse" ? "sse" : "http",
|
|
7552
|
+
url: parsed.toString(),
|
|
7553
|
+
...headers ? { headers } : {}
|
|
7554
|
+
};
|
|
7555
|
+
}
|
|
7556
|
+
function uiResourceUriFromMeta(meta) {
|
|
7557
|
+
const rec = asRecord3(meta);
|
|
7558
|
+
const nested = asRecord3(rec.ui);
|
|
7559
|
+
const fromNested = typeof nested.resourceUri === "string" ? nested.resourceUri.trim() : "";
|
|
7560
|
+
const legacy = typeof rec["ui/resourceUri"] === "string" ? rec["ui/resourceUri"].trim() : "";
|
|
7561
|
+
const uri = fromNested || legacy;
|
|
7562
|
+
return uri.startsWith("ui://") ? uri : null;
|
|
7563
|
+
}
|
|
7564
|
+
function cspFromMeta(meta) {
|
|
7565
|
+
const ui = asRecord3(asRecord3(meta).ui);
|
|
7566
|
+
const csp = asRecord3(ui.csp);
|
|
7567
|
+
const connectDomains = originList(csp.connectDomains);
|
|
7568
|
+
const resourceDomains = originList(csp.resourceDomains);
|
|
7569
|
+
const frameDomains = originList(csp.frameDomains);
|
|
7570
|
+
const baseUriDomains = originList(csp.baseUriDomains);
|
|
7571
|
+
if (!connectDomains?.length && !resourceDomains?.length && !frameDomains?.length && !baseUriDomains?.length) {
|
|
7572
|
+
return void 0;
|
|
7573
|
+
}
|
|
7574
|
+
return {
|
|
7575
|
+
...connectDomains?.length ? { connectDomains } : {},
|
|
7576
|
+
...resourceDomains?.length ? { resourceDomains } : {},
|
|
7577
|
+
...frameDomains?.length ? { frameDomains } : {},
|
|
7578
|
+
...baseUriDomains?.length ? { baseUriDomains } : {}
|
|
7579
|
+
};
|
|
7580
|
+
}
|
|
7581
|
+
function htmlFromResourceContent(content) {
|
|
7582
|
+
const uri = content.uri?.trim() ?? "";
|
|
7583
|
+
if (!uri.startsWith("ui://")) return null;
|
|
7584
|
+
const mimeType = content.mimeType?.trim() || "text/html";
|
|
7585
|
+
if (!isHtmlMime(mimeType)) return null;
|
|
7586
|
+
const html = htmlText(content.text, content.blob);
|
|
7587
|
+
if (!html) return null;
|
|
7588
|
+
return { uri, mimeType, html };
|
|
7589
|
+
}
|
|
7590
|
+
function mcpUiContentSecurityPolicy(csp) {
|
|
7591
|
+
const resource = (csp?.resourceDomains ?? []).join(" ");
|
|
7592
|
+
const connect = (csp?.connectDomains ?? []).join(" ");
|
|
7593
|
+
const frames = csp?.frameDomains?.length ? csp.frameDomains.join(" ") : "'none'";
|
|
7594
|
+
const base = csp?.baseUriDomains?.length ? csp.baseUriDomains.join(" ") : "'none'";
|
|
7595
|
+
return [
|
|
7596
|
+
"default-src 'none'",
|
|
7597
|
+
`script-src 'self' 'unsafe-inline' 'unsafe-eval' ${resource}`.trim(),
|
|
7598
|
+
`style-src 'self' 'unsafe-inline' ${resource}`.trim(),
|
|
7599
|
+
`img-src 'self' data: blob: ${resource}`.trim(),
|
|
7600
|
+
`font-src 'self' data: ${resource}`.trim(),
|
|
7601
|
+
`connect-src 'self' ${connect}`.trim(),
|
|
7602
|
+
`media-src 'self' data: blob: ${resource}`.trim(),
|
|
7603
|
+
`frame-src ${frames}`,
|
|
7604
|
+
`base-uri ${base}`,
|
|
7605
|
+
"object-src 'none'",
|
|
7606
|
+
"form-action 'self'"
|
|
7607
|
+
].join("; ");
|
|
7608
|
+
}
|
|
7609
|
+
function htmlText(text5, blob) {
|
|
7610
|
+
if (typeof text5 === "string" && text5.trim()) {
|
|
7611
|
+
return text5.length <= HTML_MAX ? text5 : null;
|
|
7612
|
+
}
|
|
7613
|
+
if (typeof blob !== "string" || !blob.trim()) return null;
|
|
7614
|
+
try {
|
|
7615
|
+
const decoded = decodeBase64(blob.trim());
|
|
7616
|
+
if (!decoded.trim() || decoded.length > HTML_MAX) return null;
|
|
7617
|
+
return decoded;
|
|
7618
|
+
} catch {
|
|
7619
|
+
return null;
|
|
7620
|
+
}
|
|
7621
|
+
}
|
|
7622
|
+
function decodeBase64(blob) {
|
|
7623
|
+
if (typeof Buffer !== "undefined") return Buffer.from(blob, "base64").toString("utf8");
|
|
7624
|
+
const bin = atob(blob);
|
|
7625
|
+
const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
7626
|
+
return new TextDecoder().decode(bytes);
|
|
7627
|
+
}
|
|
7628
|
+
function isHtmlMime(mime) {
|
|
7629
|
+
const base = mime.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
7630
|
+
return base === "text/html";
|
|
7631
|
+
}
|
|
7632
|
+
function originList(value) {
|
|
7633
|
+
if (!Array.isArray(value)) return void 0;
|
|
7634
|
+
const out = [];
|
|
7635
|
+
for (const item of value) {
|
|
7636
|
+
if (typeof item !== "string") continue;
|
|
7637
|
+
const origin = originToken(item);
|
|
7638
|
+
if (origin && !out.includes(origin)) out.push(origin);
|
|
7639
|
+
}
|
|
7640
|
+
return out;
|
|
7641
|
+
}
|
|
7642
|
+
function originToken(value) {
|
|
7643
|
+
try {
|
|
7644
|
+
const url = new URL(value.trim());
|
|
7645
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "wss:" && url.protocol !== "ws:") {
|
|
7646
|
+
return null;
|
|
7647
|
+
}
|
|
7648
|
+
return url.origin;
|
|
7649
|
+
} catch {
|
|
7650
|
+
return null;
|
|
7651
|
+
}
|
|
7652
|
+
}
|
|
7653
|
+
function stringRecord(value) {
|
|
7654
|
+
const rec = asRecord3(value);
|
|
7655
|
+
const out = {};
|
|
7656
|
+
for (const [key, item] of Object.entries(rec)) {
|
|
7657
|
+
if (typeof item === "string") out[key] = item;
|
|
7658
|
+
}
|
|
7659
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
7660
|
+
}
|
|
7661
|
+
function asRecord3(value) {
|
|
7662
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
7663
|
+
return value;
|
|
7664
|
+
}
|
|
7665
|
+
|
|
7666
|
+
// src/mcp-ui/session.ts
|
|
7667
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7668
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
7669
|
+
import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
7670
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7671
|
+
var REQUEST_TIMEOUT_MS = 2e4;
|
|
7672
|
+
var UI_CAPABILITIES = {
|
|
7673
|
+
extensions: {
|
|
7674
|
+
"io.modelcontextprotocol/ui": {
|
|
7675
|
+
mimeTypes: ["text/html;profile=mcp-app"]
|
|
7676
|
+
}
|
|
7677
|
+
}
|
|
7678
|
+
};
|
|
7679
|
+
async function openMcpUiSession(launch) {
|
|
7680
|
+
return openMcpUiSessionFromTransport(transportForLaunch(launch));
|
|
7681
|
+
}
|
|
7682
|
+
async function openMcpUiSessionFromTransport(transport) {
|
|
7683
|
+
const client = new Client(
|
|
7684
|
+
{ name: "sideboard", version: "1.0.0" },
|
|
7685
|
+
{ capabilities: UI_CAPABILITIES }
|
|
7686
|
+
);
|
|
7687
|
+
await client.connect(transport, { timeout: REQUEST_TIMEOUT_MS });
|
|
7688
|
+
return {
|
|
7689
|
+
async listTools() {
|
|
7690
|
+
const listed = await client.listTools(void 0, { timeout: REQUEST_TIMEOUT_MS });
|
|
7691
|
+
return listed.tools.map((tool) => {
|
|
7692
|
+
const meta = tool._meta;
|
|
7693
|
+
return {
|
|
7694
|
+
name: tool.name,
|
|
7695
|
+
...tool.description ? { description: tool.description } : {},
|
|
7696
|
+
...tool.inputSchema ? { inputSchema: tool.inputSchema } : {},
|
|
7697
|
+
resourceUri: uiResourceUriFromMeta(meta),
|
|
7698
|
+
...cspFromMeta(meta) ? { csp: cspFromMeta(meta) } : {}
|
|
7699
|
+
};
|
|
7700
|
+
});
|
|
7701
|
+
},
|
|
7702
|
+
async readResource(uri) {
|
|
7703
|
+
const result = await client.readResource({ uri }, { timeout: REQUEST_TIMEOUT_MS });
|
|
7704
|
+
const content = result.contents?.[0];
|
|
7705
|
+
if (!content) return null;
|
|
7706
|
+
const text5 = "text" in content && typeof content.text === "string" ? content.text : void 0;
|
|
7707
|
+
const blob = "blob" in content && typeof content.blob === "string" ? content.blob : void 0;
|
|
7708
|
+
return {
|
|
7709
|
+
uri: content.uri,
|
|
7710
|
+
...content.mimeType ? { mimeType: content.mimeType } : {},
|
|
7711
|
+
...text5 != null ? { text: text5 } : {},
|
|
7712
|
+
...blob != null ? { blob } : {},
|
|
7713
|
+
...cspFromMeta(content._meta) ? { csp: cspFromMeta(content._meta) } : {}
|
|
7714
|
+
};
|
|
7715
|
+
},
|
|
7716
|
+
async callTool(name, args) {
|
|
7717
|
+
return client.callTool({ name, arguments: args }, void 0, { timeout: REQUEST_TIMEOUT_MS });
|
|
7718
|
+
},
|
|
7719
|
+
async close() {
|
|
7720
|
+
await client.close();
|
|
7721
|
+
}
|
|
7722
|
+
};
|
|
7723
|
+
}
|
|
7724
|
+
function transportForLaunch(launch) {
|
|
7725
|
+
if (launch.kind === "stdio") {
|
|
7726
|
+
return new StdioClientTransport({
|
|
7727
|
+
command: launch.command,
|
|
7728
|
+
args: launch.args,
|
|
7729
|
+
env: { ...getDefaultEnvironment(), ...launch.env },
|
|
7730
|
+
stderr: "ignore",
|
|
7731
|
+
...launch.cwd ? { cwd: launch.cwd } : {}
|
|
7732
|
+
});
|
|
7733
|
+
}
|
|
7734
|
+
const url = new URL(launch.url);
|
|
7735
|
+
const requestInit = launch.headers ? { headers: launch.headers } : void 0;
|
|
7736
|
+
if (launch.kind === "sse") {
|
|
7737
|
+
return new SSEClientTransport(url, requestInit ? { requestInit } : void 0);
|
|
7738
|
+
}
|
|
7739
|
+
return new StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0);
|
|
7740
|
+
}
|
|
7741
|
+
|
|
7742
|
+
// src/mcp-ui/host.ts
|
|
7743
|
+
var TOOL_TTL_MS = 5 * 60 * 1e3;
|
|
7744
|
+
var McpUiHost = class {
|
|
7745
|
+
constructor(opts) {
|
|
7746
|
+
this.opts = opts;
|
|
7747
|
+
}
|
|
7748
|
+
opts;
|
|
7749
|
+
sessions = /* @__PURE__ */ new Map();
|
|
7750
|
+
toolLists = /* @__PURE__ */ new Map();
|
|
7751
|
+
async viewForClaudeTool(toolName) {
|
|
7752
|
+
const entries = this.userEntries();
|
|
7753
|
+
const match = matchClaudeMcpTool(toolName, Object.keys(entries));
|
|
7754
|
+
if (!match) return null;
|
|
7755
|
+
const launch = parseMcpServerLaunch(entries[match.server] ?? {});
|
|
7756
|
+
if (!launch) return null;
|
|
7757
|
+
try {
|
|
7758
|
+
const session = await this.sessionFor(match.server, launch);
|
|
7759
|
+
const tools = await this.toolsFor(match.server, launch, session);
|
|
7760
|
+
const tool = tools.find((item) => item.name === match.tool);
|
|
7761
|
+
if (!tool?.resourceUri) return null;
|
|
7762
|
+
const resource = await session.readResource(tool.resourceUri);
|
|
7763
|
+
if (!resource) return null;
|
|
7764
|
+
const html = htmlFromResourceContent(resource);
|
|
7765
|
+
if (!html) return null;
|
|
7766
|
+
const csp = resource.csp ?? tool.csp;
|
|
7767
|
+
return {
|
|
7768
|
+
server: match.server,
|
|
7769
|
+
tool: match.tool,
|
|
7770
|
+
uri: html.uri,
|
|
7771
|
+
mimeType: html.mimeType,
|
|
7772
|
+
html: html.html,
|
|
7773
|
+
csp: mcpUiContentSecurityPolicy(csp)
|
|
7774
|
+
};
|
|
7775
|
+
} catch (err) {
|
|
7776
|
+
console.error(
|
|
7777
|
+
`[mcp-ui] ${match.server}/${match.tool}:`,
|
|
7778
|
+
err instanceof Error ? err.message : err
|
|
7779
|
+
);
|
|
7780
|
+
return null;
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7783
|
+
async callTool(server, tool, args) {
|
|
7784
|
+
const session = await this.sessionForName(server);
|
|
7785
|
+
if (!session) throw new Error(`MCP server ${server} is not available`);
|
|
7786
|
+
return session.callTool(tool, args);
|
|
7787
|
+
}
|
|
7788
|
+
async readResource(server, uri) {
|
|
7789
|
+
const session = await this.sessionForName(server);
|
|
7790
|
+
if (!session) throw new Error(`MCP server ${server} is not available`);
|
|
7791
|
+
const resource = await session.readResource(uri);
|
|
7792
|
+
if (!resource) throw new Error(`Resource ${uri} was empty`);
|
|
7793
|
+
return {
|
|
7794
|
+
contents: [
|
|
7795
|
+
{
|
|
7796
|
+
uri: resource.uri,
|
|
7797
|
+
...resource.mimeType ? { mimeType: resource.mimeType } : {},
|
|
7798
|
+
...resource.text != null ? { text: resource.text } : {},
|
|
7799
|
+
...resource.blob != null ? { blob: resource.blob } : {}
|
|
7800
|
+
}
|
|
7801
|
+
]
|
|
7802
|
+
};
|
|
7803
|
+
}
|
|
7804
|
+
async closeAll() {
|
|
7805
|
+
const pending = [...this.sessions.values()];
|
|
7806
|
+
this.sessions.clear();
|
|
7807
|
+
this.toolLists.clear();
|
|
7808
|
+
await Promise.all(
|
|
7809
|
+
pending.map((session) => session.then((open) => open.close()).catch(() => void 0))
|
|
7810
|
+
);
|
|
7811
|
+
}
|
|
7812
|
+
userEntries() {
|
|
7813
|
+
const out = {};
|
|
7814
|
+
for (const [name, entry] of Object.entries(this.opts.entries())) {
|
|
7815
|
+
if (!name.trim() || isInjectedOrchMcpName(name) || !entry) continue;
|
|
7816
|
+
out[name] = entry;
|
|
7817
|
+
}
|
|
7818
|
+
return out;
|
|
7819
|
+
}
|
|
7820
|
+
async sessionForName(server) {
|
|
7821
|
+
const entry = this.userEntries()[server];
|
|
7822
|
+
if (!entry) return null;
|
|
7823
|
+
const launch = parseMcpServerLaunch(entry);
|
|
7824
|
+
if (!launch) return null;
|
|
7825
|
+
return this.sessionFor(server, launch);
|
|
7826
|
+
}
|
|
7827
|
+
sessionFor(server, launch) {
|
|
7828
|
+
const key = `${server}
|
|
7829
|
+
${JSON.stringify(launch)}`;
|
|
7830
|
+
const existing = this.sessions.get(key);
|
|
7831
|
+
if (existing) return existing;
|
|
7832
|
+
const open = this.opts.openSession ?? openMcpUiSession;
|
|
7833
|
+
const pending = open(launch).catch((err) => {
|
|
7834
|
+
this.sessions.delete(key);
|
|
7835
|
+
throw err;
|
|
7836
|
+
});
|
|
7837
|
+
this.sessions.set(key, pending);
|
|
7838
|
+
return pending;
|
|
7839
|
+
}
|
|
7840
|
+
async toolsFor(server, launch, session) {
|
|
7841
|
+
const key = `${server}
|
|
7842
|
+
${JSON.stringify(launch)}`;
|
|
7843
|
+
const cached2 = this.toolLists.get(key);
|
|
7844
|
+
if (cached2 && cached2.key === key && Date.now() - cached2.at < TOOL_TTL_MS) return cached2.tools;
|
|
7845
|
+
const tools = await session.listTools();
|
|
7846
|
+
this.toolLists.set(key, { at: Date.now(), key, tools });
|
|
7847
|
+
return tools;
|
|
7848
|
+
}
|
|
7849
|
+
};
|
|
7850
|
+
var singleton;
|
|
7851
|
+
function getMcpUiHost() {
|
|
7852
|
+
if (!singleton) {
|
|
7853
|
+
singleton = new McpUiHost({ entries: () => listUserClaudeMcpServerEntries() });
|
|
7854
|
+
}
|
|
7855
|
+
return singleton;
|
|
7856
|
+
}
|
|
7857
|
+
function closeMcpUiHost() {
|
|
7858
|
+
const host = singleton;
|
|
7859
|
+
singleton = void 0;
|
|
7860
|
+
if (host) void host.closeAll();
|
|
7861
|
+
}
|
|
7862
|
+
|
|
7508
7863
|
// src/slack/workspaces.ts
|
|
7509
7864
|
import { join as join4 } from "path";
|
|
7510
7865
|
|
|
@@ -9572,126 +9927,8 @@ var RemoteHub = class {
|
|
|
9572
9927
|
// src/remote/client.ts
|
|
9573
9928
|
import { WebSocket as WsWebSocket3 } from "ws";
|
|
9574
9929
|
|
|
9575
|
-
// src/remote/phone-
|
|
9576
|
-
import {
|
|
9577
|
-
import { join as join6 } from "path";
|
|
9578
|
-
var PHONE_IMAGE_MAX_BYTES = 15e5;
|
|
9579
|
-
var PHONE_TEXT_MAX_BYTES = 18e4;
|
|
9580
|
-
var PHONE_DIR_MAX = 200;
|
|
9581
|
-
var IMAGE_MIME = {
|
|
9582
|
-
png: "image/png",
|
|
9583
|
-
jpg: "image/jpeg",
|
|
9584
|
-
jpeg: "image/jpeg",
|
|
9585
|
-
gif: "image/gif",
|
|
9586
|
-
webp: "image/webp",
|
|
9587
|
-
bmp: "image/bmp"
|
|
9588
|
-
};
|
|
9589
|
-
function phoneWorktreeRelative(worktreePath, raw) {
|
|
9590
|
-
let text5 = raw.trim();
|
|
9591
|
-
if (!text5) return null;
|
|
9592
|
-
if (/^file:\/\//i.test(text5)) {
|
|
9593
|
-
try {
|
|
9594
|
-
text5 = decodeURIComponent(new URL(text5).pathname);
|
|
9595
|
-
} catch {
|
|
9596
|
-
return null;
|
|
9597
|
-
}
|
|
9598
|
-
}
|
|
9599
|
-
text5 = (text5.split("#")[0] ?? text5).replace(/\\/g, "/");
|
|
9600
|
-
if (!text5 || text5.includes("\0") || text5.split("/").includes("..")) return null;
|
|
9601
|
-
const root = worktreePath.replace(/\/+$/, "");
|
|
9602
|
-
if (!root || root === "/") return null;
|
|
9603
|
-
if (text5.startsWith("/")) {
|
|
9604
|
-
if (text5 !== root && !text5.startsWith(`${root}/`)) return null;
|
|
9605
|
-
text5 = text5 === root ? "" : text5.slice(root.length + 1);
|
|
9606
|
-
}
|
|
9607
|
-
text5 = text5.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
9608
|
-
if (!text5 || text5 === "." || text5.startsWith("/")) return null;
|
|
9609
|
-
return text5;
|
|
9610
|
-
}
|
|
9611
|
-
function phoneImageMime(relativePath) {
|
|
9612
|
-
const ext = relativePath.split(".").pop()?.toLowerCase() ?? "";
|
|
9613
|
-
return IMAGE_MIME[ext] ?? null;
|
|
9614
|
-
}
|
|
9615
|
-
function phoneImageDataUrl(worktreePath, raw) {
|
|
9616
|
-
const relative = phoneWorktreeRelative(worktreePath, raw);
|
|
9617
|
-
if (!relative) return null;
|
|
9618
|
-
const mime = phoneImageMime(relative);
|
|
9619
|
-
if (!mime) return null;
|
|
9620
|
-
try {
|
|
9621
|
-
const file = readWorktreeFileForUpload(worktreePath, relative, { maxBytes: PHONE_IMAGE_MAX_BYTES });
|
|
9622
|
-
return `data:${mime};base64,${file.contentBase64}`;
|
|
9623
|
-
} catch {
|
|
9624
|
-
return null;
|
|
9625
|
-
}
|
|
9626
|
-
}
|
|
9627
|
-
function phoneDirectoryListing(worktreePath, relative) {
|
|
9628
|
-
try {
|
|
9629
|
-
const entries = readdirSync2(join6(worktreePath, relative), { withFileTypes: true });
|
|
9630
|
-
const dirs = new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
9631
|
-
const names = entries.map((entry) => entry.name).filter((name) => name && !name.includes("/") && !name.includes("\0")).sort((a, b) => a.localeCompare(b));
|
|
9632
|
-
const shown = names.slice(0, PHONE_DIR_MAX);
|
|
9633
|
-
const lines = shown.map((name) => dirs.has(name) ? `${name}/` : name);
|
|
9634
|
-
const truncated = names.length > shown.length;
|
|
9635
|
-
if (truncated) lines.push(`\u2026 ${names.length - shown.length} more`);
|
|
9636
|
-
return { content: lines.join("\n"), truncated };
|
|
9637
|
-
} catch {
|
|
9638
|
-
return null;
|
|
9639
|
-
}
|
|
9640
|
-
}
|
|
9641
|
-
function phoneFilePreview(worktreePath, relative, kind) {
|
|
9642
|
-
if (kind === "dir") {
|
|
9643
|
-
const listing = phoneDirectoryListing(worktreePath, relative);
|
|
9644
|
-
if (!listing) return null;
|
|
9645
|
-
return {
|
|
9646
|
-
content: listing.content,
|
|
9647
|
-
encoding: "utf8",
|
|
9648
|
-
binary: false,
|
|
9649
|
-
directory: true,
|
|
9650
|
-
truncated: listing.truncated
|
|
9651
|
-
};
|
|
9652
|
-
}
|
|
9653
|
-
if (relative.toLowerCase().endsWith(".svg")) {
|
|
9654
|
-
try {
|
|
9655
|
-
const buf = readFileSync5(join6(worktreePath, relative));
|
|
9656
|
-
const truncated = buf.length > PHONE_TEXT_MAX_BYTES;
|
|
9657
|
-
return {
|
|
9658
|
-
content: buf.subarray(0, PHONE_TEXT_MAX_BYTES).toString("utf8"),
|
|
9659
|
-
encoding: "utf8",
|
|
9660
|
-
binary: false,
|
|
9661
|
-
directory: false,
|
|
9662
|
-
truncated,
|
|
9663
|
-
mime: "image/svg+xml"
|
|
9664
|
-
};
|
|
9665
|
-
} catch {
|
|
9666
|
-
return null;
|
|
9667
|
-
}
|
|
9668
|
-
}
|
|
9669
|
-
const mime = phoneImageMime(relative);
|
|
9670
|
-
if (mime) {
|
|
9671
|
-
const dataUrl = phoneImageDataUrl(worktreePath, relative);
|
|
9672
|
-
if (!dataUrl) return null;
|
|
9673
|
-
return {
|
|
9674
|
-
content: dataUrl,
|
|
9675
|
-
encoding: "base64",
|
|
9676
|
-
binary: true,
|
|
9677
|
-
directory: false,
|
|
9678
|
-
truncated: false,
|
|
9679
|
-
mime
|
|
9680
|
-
};
|
|
9681
|
-
}
|
|
9682
|
-
try {
|
|
9683
|
-
const file = readWorktreeFile(worktreePath, relative, { maxBytes: PHONE_TEXT_MAX_BYTES });
|
|
9684
|
-
return {
|
|
9685
|
-
content: file.content,
|
|
9686
|
-
encoding: file.encoding,
|
|
9687
|
-
binary: file.binary,
|
|
9688
|
-
directory: false,
|
|
9689
|
-
truncated: file.truncated
|
|
9690
|
-
};
|
|
9691
|
-
} catch {
|
|
9692
|
-
return null;
|
|
9693
|
-
}
|
|
9694
|
-
}
|
|
9930
|
+
// src/remote/phone-chats.ts
|
|
9931
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
9695
9932
|
|
|
9696
9933
|
// src/remote/phone-artifact.ts
|
|
9697
9934
|
var FENCE_RE = /```([a-zA-Z0-9_+-]*)[^\n]*\n([\s\S]*?)(?:```|$)/g;
|
|
@@ -9701,10 +9938,10 @@ var MIN_MD_CHARS = 120;
|
|
|
9701
9938
|
function oneLine(value, max) {
|
|
9702
9939
|
return value.replace(/[`\r\n]/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
|
|
9703
9940
|
}
|
|
9704
|
-
function
|
|
9941
|
+
function asRecord4(value) {
|
|
9705
9942
|
if (typeof value === "string") {
|
|
9706
9943
|
try {
|
|
9707
|
-
return
|
|
9944
|
+
return asRecord4(JSON.parse(value));
|
|
9708
9945
|
} catch {
|
|
9709
9946
|
return void 0;
|
|
9710
9947
|
}
|
|
@@ -9779,7 +10016,7 @@ function toolContent(input) {
|
|
|
9779
10016
|
if (!input) return "";
|
|
9780
10017
|
const direct = input.content;
|
|
9781
10018
|
if (typeof direct === "string") return direct;
|
|
9782
|
-
const nested =
|
|
10019
|
+
const nested = asRecord4(direct);
|
|
9783
10020
|
if (!nested) return "";
|
|
9784
10021
|
for (const key of ["html", "markup", "text", "source", "content", "code"]) {
|
|
9785
10022
|
if (typeof nested[key] === "string") return nested[key];
|
|
@@ -9802,7 +10039,7 @@ function phoneArtifactText(text5, parts, limit = Number.POSITIVE_INFINITY) {
|
|
|
9802
10039
|
const extras = [];
|
|
9803
10040
|
for (const part of parts ?? []) {
|
|
9804
10041
|
if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
|
|
9805
|
-
const input =
|
|
10042
|
+
const input = asRecord4(part.input);
|
|
9806
10043
|
const content = toolContent(input);
|
|
9807
10044
|
const hint = artifactHint(content);
|
|
9808
10045
|
if (hint && hints.has(hint)) continue;
|
|
@@ -9821,7 +10058,7 @@ function phoneArtifactText(text5, parts, limit = Number.POSITIVE_INFINITY) {
|
|
|
9821
10058
|
${tail}` : tail;
|
|
9822
10059
|
}
|
|
9823
10060
|
function parseOpenArtifact(value, chatId) {
|
|
9824
|
-
const record =
|
|
10061
|
+
const record = asRecord4(value);
|
|
9825
10062
|
if (!record) return "invalid";
|
|
9826
10063
|
const title = oneLine(str4(record.title), 200);
|
|
9827
10064
|
if (!title) return "invalid";
|
|
@@ -9844,7 +10081,7 @@ function phoneArtifactDocuments(sources) {
|
|
|
9844
10081
|
}
|
|
9845
10082
|
for (const part of source.parts ?? []) {
|
|
9846
10083
|
if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
|
|
9847
|
-
const input =
|
|
10084
|
+
const input = asRecord4(part.input);
|
|
9848
10085
|
const content = toolContent(input);
|
|
9849
10086
|
const hint = artifactHint(content);
|
|
9850
10087
|
if (hint && hints.has(hint)) continue;
|
|
@@ -9894,121 +10131,6 @@ function pickArtifactMatch(items, request) {
|
|
|
9894
10131
|
return null;
|
|
9895
10132
|
}
|
|
9896
10133
|
|
|
9897
|
-
// src/remote/phone-panes.ts
|
|
9898
|
-
var PHONE_CHANGE_SCOPES = ["last_turn", "uncommitted", "staged", "unstaged", "commits"];
|
|
9899
|
-
var PHONE_CHANGE_SCOPE_DEFAULT = "commits";
|
|
9900
|
-
function phoneChangeScope(value) {
|
|
9901
|
-
return typeof value === "string" && PHONE_CHANGE_SCOPES.includes(value) ? value : void 0;
|
|
9902
|
-
}
|
|
9903
|
-
function phoneCommitSha(value) {
|
|
9904
|
-
return typeof value === "string" && /^[0-9a-f]{4,40}$/i.test(value) ? value : void 0;
|
|
9905
|
-
}
|
|
9906
|
-
var MAX_ROWS = 200;
|
|
9907
|
-
var MAX_QUERY = 200;
|
|
9908
|
-
function parsePhonePane(value, chatId) {
|
|
9909
|
-
const pane = value && typeof value === "object" ? value.pane : void 0;
|
|
9910
|
-
if (pane !== "files" && pane !== "changes" && pane !== "checks") return "invalid";
|
|
9911
|
-
const scope = phoneChangeScope(value.scope);
|
|
9912
|
-
const commitSha = phoneCommitSha(value.commitSha);
|
|
9913
|
-
const queryRaw = value.query;
|
|
9914
|
-
const query = typeof queryRaw === "string" ? queryRaw.trim().slice(0, MAX_QUERY) : "";
|
|
9915
|
-
return {
|
|
9916
|
-
op: "pane",
|
|
9917
|
-
chatId,
|
|
9918
|
-
pane,
|
|
9919
|
-
...scope && pane === "changes" ? { scope } : {},
|
|
9920
|
-
...commitSha && pane === "changes" && scope === "commits" ? { commitSha } : {},
|
|
9921
|
-
...query && pane === "files" ? { query } : {}
|
|
9922
|
-
};
|
|
9923
|
-
}
|
|
9924
|
-
function filterPhoneFiles(paths, query) {
|
|
9925
|
-
const q = query.trim().toLowerCase();
|
|
9926
|
-
if (!q) return paths;
|
|
9927
|
-
return paths.filter((path2) => path2.toLowerCase().includes(q));
|
|
9928
|
-
}
|
|
9929
|
-
async function readPhonePane(thread, pane, scope, query, commitSha) {
|
|
9930
|
-
try {
|
|
9931
|
-
if (pane === "files") return await readFiles(thread.id, query);
|
|
9932
|
-
if (pane === "changes") return await readChanges(thread.id, scope ?? PHONE_CHANGE_SCOPE_DEFAULT, commitSha);
|
|
9933
|
-
return await readChecks(thread.id);
|
|
9934
|
-
} catch (err) {
|
|
9935
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
9936
|
-
return { rows: [], note: message };
|
|
9937
|
-
}
|
|
9938
|
-
}
|
|
9939
|
-
async function readFiles(threadId, query) {
|
|
9940
|
-
const paths = filterPhoneFiles(await getOrchestrator().listFiles(threadId), query ?? "");
|
|
9941
|
-
const shown = paths.slice(0, MAX_ROWS);
|
|
9942
|
-
const extra = paths.length - shown.length;
|
|
9943
|
-
const note = extra > 0 ? query?.trim() ? `${extra} more matching files` : `${extra} more files on the Mac` : void 0;
|
|
9944
|
-
return {
|
|
9945
|
-
rows: shown.map((path2) => ({ label: path2, path: path2 })),
|
|
9946
|
-
...note ? { note } : {}
|
|
9947
|
-
};
|
|
9948
|
-
}
|
|
9949
|
-
async function readChanges(threadId, scope, commitSha) {
|
|
9950
|
-
const diff = await getOrchestrator().diff(threadId, {
|
|
9951
|
-
includePatches: false,
|
|
9952
|
-
scope,
|
|
9953
|
-
commitSha: scope === "commits" ? commitSha ?? null : null
|
|
9954
|
-
});
|
|
9955
|
-
const shown = diff.files.slice(0, MAX_ROWS);
|
|
9956
|
-
const selected = diff.commits.find((commit) => commit.sha === commitSha || commit.shortSha === commitSha);
|
|
9957
|
-
return {
|
|
9958
|
-
rows: shown.map((file) => {
|
|
9959
|
-
const detail = changeDetail(file.additions, file.deletions);
|
|
9960
|
-
return {
|
|
9961
|
-
label: file.path,
|
|
9962
|
-
path: file.path,
|
|
9963
|
-
...file.status ? { status: file.status } : {},
|
|
9964
|
-
...detail ? { detail } : {}
|
|
9965
|
-
};
|
|
9966
|
-
}),
|
|
9967
|
-
...diff.files.length > shown.length ? { note: `${diff.files.length - shown.length} more changed files` } : {},
|
|
9968
|
-
scope: diff.scope,
|
|
9969
|
-
commitSha: selected?.sha ?? null,
|
|
9970
|
-
...diff.base ? { base: diff.base } : {},
|
|
9971
|
-
hasLastTurnBase: diff.hasLastTurnBase,
|
|
9972
|
-
commits: diff.commits.slice(0, 40),
|
|
9973
|
-
scopeStats: diff.scopeStats
|
|
9974
|
-
};
|
|
9975
|
-
}
|
|
9976
|
-
function changeDetail(additions, deletions) {
|
|
9977
|
-
const add = additions ?? 0;
|
|
9978
|
-
const del = deletions ?? 0;
|
|
9979
|
-
if (add === 0 && del === 0) return void 0;
|
|
9980
|
-
return `+${add} \u2212${del}`;
|
|
9981
|
-
}
|
|
9982
|
-
async function readPhoneChangePatch(threadId, path2, scope = PHONE_CHANGE_SCOPE_DEFAULT, commitSha) {
|
|
9983
|
-
const diff = await getOrchestrator().diff(threadId, {
|
|
9984
|
-
path: path2,
|
|
9985
|
-
scope,
|
|
9986
|
-
commitSha: scope === "commits" ? commitSha ?? null : null,
|
|
9987
|
-
includePatches: true,
|
|
9988
|
-
includeMeta: false,
|
|
9989
|
-
includeUntracked: scope === "uncommitted" || scope === "unstaged" || scope === "last_turn" || scope === "commits" && !commitSha
|
|
9990
|
-
});
|
|
9991
|
-
const file = diff.files.find((row2) => row2.path === path2) ?? diff.files[0];
|
|
9992
|
-
if (!file) return null;
|
|
9993
|
-
const content = file.patch ?? "";
|
|
9994
|
-
return { content, truncated: content.includes("\u2026 truncated") };
|
|
9995
|
-
}
|
|
9996
|
-
async function readChecks(threadId) {
|
|
9997
|
-
const checks = await getOrchestrator().getPrChecks(threadId);
|
|
9998
|
-
if (!checks) return { rows: [], note: "No pull request on this branch." };
|
|
9999
|
-
const shown = checks.slice(0, MAX_ROWS);
|
|
10000
|
-
return {
|
|
10001
|
-
rows: shown.map((check) => ({
|
|
10002
|
-
label: check.name,
|
|
10003
|
-
detail: check.bucket || check.state,
|
|
10004
|
-
...check.link ? { href: check.link } : {}
|
|
10005
|
-
}))
|
|
10006
|
-
};
|
|
10007
|
-
}
|
|
10008
|
-
|
|
10009
|
-
// src/remote/phone-chats.ts
|
|
10010
|
-
import { randomUUID as randomUUID7 } from "crypto";
|
|
10011
|
-
|
|
10012
10134
|
// src/remote/phone-header.ts
|
|
10013
10135
|
var PHONE_HEADER_GIT_ACTIONS = [
|
|
10014
10136
|
"commit-push",
|
|
@@ -10170,6 +10292,118 @@ function parsePhoneCodeRefs(value) {
|
|
|
10170
10292
|
return refs;
|
|
10171
10293
|
}
|
|
10172
10294
|
|
|
10295
|
+
// src/remote/phone-panes.ts
|
|
10296
|
+
var PHONE_CHANGE_SCOPES = ["last_turn", "uncommitted", "staged", "unstaged", "commits"];
|
|
10297
|
+
var PHONE_CHANGE_SCOPE_DEFAULT = "commits";
|
|
10298
|
+
function phoneChangeScope(value) {
|
|
10299
|
+
return typeof value === "string" && PHONE_CHANGE_SCOPES.includes(value) ? value : void 0;
|
|
10300
|
+
}
|
|
10301
|
+
function phoneCommitSha(value) {
|
|
10302
|
+
return typeof value === "string" && /^[0-9a-f]{4,40}$/i.test(value) ? value : void 0;
|
|
10303
|
+
}
|
|
10304
|
+
var MAX_ROWS = 200;
|
|
10305
|
+
var MAX_QUERY = 200;
|
|
10306
|
+
function parsePhonePane(value, chatId) {
|
|
10307
|
+
const pane = value && typeof value === "object" ? value.pane : void 0;
|
|
10308
|
+
if (pane !== "files" && pane !== "changes" && pane !== "checks") return "invalid";
|
|
10309
|
+
const scope = phoneChangeScope(value.scope);
|
|
10310
|
+
const commitSha = phoneCommitSha(value.commitSha);
|
|
10311
|
+
const queryRaw = value.query;
|
|
10312
|
+
const query = typeof queryRaw === "string" ? queryRaw.trim().slice(0, MAX_QUERY) : "";
|
|
10313
|
+
return {
|
|
10314
|
+
op: "pane",
|
|
10315
|
+
chatId,
|
|
10316
|
+
pane,
|
|
10317
|
+
...scope && pane === "changes" ? { scope } : {},
|
|
10318
|
+
...commitSha && pane === "changes" && scope === "commits" ? { commitSha } : {},
|
|
10319
|
+
...query && pane === "files" ? { query } : {}
|
|
10320
|
+
};
|
|
10321
|
+
}
|
|
10322
|
+
function filterPhoneFiles(paths, query) {
|
|
10323
|
+
const q = query.trim().toLowerCase();
|
|
10324
|
+
if (!q) return paths;
|
|
10325
|
+
return paths.filter((path2) => path2.toLowerCase().includes(q));
|
|
10326
|
+
}
|
|
10327
|
+
async function readPhonePane(thread, pane, scope, query, commitSha) {
|
|
10328
|
+
try {
|
|
10329
|
+
if (pane === "files") return await readFiles(thread.id, query);
|
|
10330
|
+
if (pane === "changes") return await readChanges(thread.id, scope ?? PHONE_CHANGE_SCOPE_DEFAULT, commitSha);
|
|
10331
|
+
return await readChecks(thread.id);
|
|
10332
|
+
} catch (err) {
|
|
10333
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10334
|
+
return { rows: [], note: message };
|
|
10335
|
+
}
|
|
10336
|
+
}
|
|
10337
|
+
async function readFiles(threadId, query) {
|
|
10338
|
+
const paths = filterPhoneFiles(await getOrchestrator().listFiles(threadId), query ?? "");
|
|
10339
|
+
const shown = paths.slice(0, MAX_ROWS);
|
|
10340
|
+
const extra = paths.length - shown.length;
|
|
10341
|
+
const note = extra > 0 ? query?.trim() ? `${extra} more matching files` : `${extra} more files on the Mac` : void 0;
|
|
10342
|
+
return {
|
|
10343
|
+
rows: shown.map((path2) => ({ label: path2, path: path2 })),
|
|
10344
|
+
...note ? { note } : {}
|
|
10345
|
+
};
|
|
10346
|
+
}
|
|
10347
|
+
async function readChanges(threadId, scope, commitSha) {
|
|
10348
|
+
const diff = await getOrchestrator().diff(threadId, {
|
|
10349
|
+
includePatches: false,
|
|
10350
|
+
scope,
|
|
10351
|
+
commitSha: scope === "commits" ? commitSha ?? null : null
|
|
10352
|
+
});
|
|
10353
|
+
const shown = diff.files.slice(0, MAX_ROWS);
|
|
10354
|
+
const selected = diff.commits.find((commit) => commit.sha === commitSha || commit.shortSha === commitSha);
|
|
10355
|
+
return {
|
|
10356
|
+
rows: shown.map((file) => {
|
|
10357
|
+
const detail = changeDetail(file.additions, file.deletions);
|
|
10358
|
+
return {
|
|
10359
|
+
label: file.path,
|
|
10360
|
+
path: file.path,
|
|
10361
|
+
...file.status ? { status: file.status } : {},
|
|
10362
|
+
...detail ? { detail } : {}
|
|
10363
|
+
};
|
|
10364
|
+
}),
|
|
10365
|
+
...diff.files.length > shown.length ? { note: `${diff.files.length - shown.length} more changed files` } : {},
|
|
10366
|
+
scope: diff.scope,
|
|
10367
|
+
commitSha: selected?.sha ?? null,
|
|
10368
|
+
...diff.base ? { base: diff.base } : {},
|
|
10369
|
+
hasLastTurnBase: diff.hasLastTurnBase,
|
|
10370
|
+
commits: diff.commits.slice(0, 40),
|
|
10371
|
+
scopeStats: diff.scopeStats
|
|
10372
|
+
};
|
|
10373
|
+
}
|
|
10374
|
+
function changeDetail(additions, deletions) {
|
|
10375
|
+
const add = additions ?? 0;
|
|
10376
|
+
const del = deletions ?? 0;
|
|
10377
|
+
if (add === 0 && del === 0) return void 0;
|
|
10378
|
+
return `+${add} \u2212${del}`;
|
|
10379
|
+
}
|
|
10380
|
+
async function readPhoneChangePatch(threadId, path2, scope = PHONE_CHANGE_SCOPE_DEFAULT, commitSha) {
|
|
10381
|
+
const diff = await getOrchestrator().diff(threadId, {
|
|
10382
|
+
path: path2,
|
|
10383
|
+
scope,
|
|
10384
|
+
commitSha: scope === "commits" ? commitSha ?? null : null,
|
|
10385
|
+
includePatches: true,
|
|
10386
|
+
includeMeta: false,
|
|
10387
|
+
includeUntracked: scope === "uncommitted" || scope === "unstaged" || scope === "last_turn" || scope === "commits" && !commitSha
|
|
10388
|
+
});
|
|
10389
|
+
const file = diff.files.find((row2) => row2.path === path2) ?? diff.files[0];
|
|
10390
|
+
if (!file) return null;
|
|
10391
|
+
const content = file.patch ?? "";
|
|
10392
|
+
return { content, truncated: content.includes("\u2026 truncated") };
|
|
10393
|
+
}
|
|
10394
|
+
async function readChecks(threadId) {
|
|
10395
|
+
const checks = await getOrchestrator().getPrChecks(threadId);
|
|
10396
|
+
if (!checks) return { rows: [], note: "No pull request on this branch." };
|
|
10397
|
+
const shown = checks.slice(0, MAX_ROWS);
|
|
10398
|
+
return {
|
|
10399
|
+
rows: shown.map((check) => ({
|
|
10400
|
+
label: check.name,
|
|
10401
|
+
detail: check.bucket || check.state,
|
|
10402
|
+
...check.link ? { href: check.link } : {}
|
|
10403
|
+
}))
|
|
10404
|
+
};
|
|
10405
|
+
}
|
|
10406
|
+
|
|
10173
10407
|
// src/remote/phone-defaults.ts
|
|
10174
10408
|
function row(resolved) {
|
|
10175
10409
|
return {
|
|
@@ -10209,6 +10443,63 @@ function refreshPhoneViewerLogin(force = false) {
|
|
|
10209
10443
|
return run2;
|
|
10210
10444
|
}
|
|
10211
10445
|
|
|
10446
|
+
// src/remote/phone-mcp-ui.ts
|
|
10447
|
+
var MAX_APPS = 4;
|
|
10448
|
+
function phoneMcpApps(agent, parts) {
|
|
10449
|
+
if (agent !== "claude" || !parts?.length) return [];
|
|
10450
|
+
const out = [];
|
|
10451
|
+
for (const part of parts) {
|
|
10452
|
+
if (part.type !== "tool" || part.parentId || part.status === "running") continue;
|
|
10453
|
+
if (!part.name.startsWith("mcp__") || part.name.startsWith("mcp__sideboard")) continue;
|
|
10454
|
+
out.push({ id: part.id, tool: part.name });
|
|
10455
|
+
if (out.length >= MAX_APPS) break;
|
|
10456
|
+
}
|
|
10457
|
+
return out;
|
|
10458
|
+
}
|
|
10459
|
+
function controlText(value, key) {
|
|
10460
|
+
const raw = value[key];
|
|
10461
|
+
return typeof raw === "string" ? raw.trim() : "";
|
|
10462
|
+
}
|
|
10463
|
+
function parsePhoneMcpUi(value, op, chatId) {
|
|
10464
|
+
if (op === "mcp-ui") {
|
|
10465
|
+
const tool2 = controlText(value, "tool");
|
|
10466
|
+
const callId = controlText(value, "callId");
|
|
10467
|
+
const reqId2 = controlText(value, "reqId");
|
|
10468
|
+
if (!tool2.startsWith("mcp__") || tool2.length > 200 || !callId || !reqId2 || reqId2.length > 40) {
|
|
10469
|
+
return "invalid";
|
|
10470
|
+
}
|
|
10471
|
+
return { op: "mcp-ui", chatId, tool: tool2, callId, reqId: reqId2 };
|
|
10472
|
+
}
|
|
10473
|
+
if (op !== "mcp-ui-call" && op !== "mcp-ui-read") return "invalid";
|
|
10474
|
+
const reqId = controlText(value, "reqId");
|
|
10475
|
+
const server = controlText(value, "server");
|
|
10476
|
+
if (!reqId || reqId.length > 40 || !server || server.length > 120) return "invalid";
|
|
10477
|
+
if (op === "mcp-ui-read") {
|
|
10478
|
+
const uri = controlText(value, "uri");
|
|
10479
|
+
if (!uri.startsWith("ui://") || uri.length > 500) return "invalid";
|
|
10480
|
+
return { op, chatId, reqId, server, uri };
|
|
10481
|
+
}
|
|
10482
|
+
const tool = controlText(value, "tool");
|
|
10483
|
+
const args = value.args;
|
|
10484
|
+
if (!tool || tool.length > 200 || !args || typeof args !== "object" || Array.isArray(args)) {
|
|
10485
|
+
return "invalid";
|
|
10486
|
+
}
|
|
10487
|
+
return { op, chatId, reqId, server, tool, args };
|
|
10488
|
+
}
|
|
10489
|
+
function phoneMcpToolPayload(parts, tool, callId) {
|
|
10490
|
+
if (!parts) return null;
|
|
10491
|
+
const matches = parts.filter(
|
|
10492
|
+
(part2) => part2.type === "tool" && part2.name === tool && !part2.parentId
|
|
10493
|
+
);
|
|
10494
|
+
const part = matches.find((row2) => row2.id === callId) ?? matches[matches.length - 1];
|
|
10495
|
+
if (!part) return null;
|
|
10496
|
+
return {
|
|
10497
|
+
input: part.input ?? {},
|
|
10498
|
+
resultText: part.result ?? "",
|
|
10499
|
+
isError: part.status === "error"
|
|
10500
|
+
};
|
|
10501
|
+
}
|
|
10502
|
+
|
|
10212
10503
|
// src/remote/phone-stream.ts
|
|
10213
10504
|
var PHONE_STREAM_FLUSH_MS = 80;
|
|
10214
10505
|
function phoneStreamFrame(parts) {
|
|
@@ -10267,7 +10558,8 @@ function listPhoneMessages(thread) {
|
|
|
10267
10558
|
const visible = [phoneVisibleText(message.role, message.text), names.length ? names.join(", ") : ""].filter(Boolean).join("\n");
|
|
10268
10559
|
const text5 = message.role === "agent" ? phoneArtifactText(visible, message.parts, MESSAGE_LIMIT) : clip(visible, MESSAGE_LIMIT);
|
|
10269
10560
|
if (!text5) continue;
|
|
10270
|
-
|
|
10561
|
+
const apps = message.role === "agent" ? phoneMcpApps(thread.agent, message.parts) : [];
|
|
10562
|
+
out.push({ role: message.role, text: text5, ...apps.length ? { apps } : {} });
|
|
10271
10563
|
}
|
|
10272
10564
|
const live = withLiveTail(thread, out);
|
|
10273
10565
|
for (const prompt of thread.queue) {
|
|
@@ -10394,6 +10686,7 @@ function parsePhoneControl(value) {
|
|
|
10394
10686
|
}
|
|
10395
10687
|
if (op === "pane") return parsePhonePane(value, chatId);
|
|
10396
10688
|
if (op === "open-artifact") return parseOpenArtifact(value, chatId);
|
|
10689
|
+
if (op === "mcp-ui" || op === "mcp-ui-call" || op === "mcp-ui-read") return parsePhoneMcpUi(value, op, chatId);
|
|
10397
10690
|
if (op === "open-file" || op === "media") {
|
|
10398
10691
|
const path2 = textField2(value, "path");
|
|
10399
10692
|
if (!path2 || path2.length > 2e3) return "invalid";
|
|
@@ -10809,108 +11102,6 @@ function archivePhoneChat(chatId) {
|
|
|
10809
11102
|
return true;
|
|
10810
11103
|
}
|
|
10811
11104
|
|
|
10812
|
-
// src/remote/phone-workspace-ops.ts
|
|
10813
|
-
function dispatchPhoneWorkspace(cmd, emit, publishOpened) {
|
|
10814
|
-
if (cmd.op === "pane") {
|
|
10815
|
-
const thread = readPhoneThread(cmd.chatId);
|
|
10816
|
-
if (!thread) {
|
|
10817
|
-
emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
10818
|
-
return;
|
|
10819
|
-
}
|
|
10820
|
-
void readPhonePane(thread, cmd.pane, cmd.scope, cmd.query, cmd.commitSha).then((body) => {
|
|
10821
|
-
emit({
|
|
10822
|
-
op: "pane",
|
|
10823
|
-
chatId: cmd.chatId,
|
|
10824
|
-
pane: cmd.pane,
|
|
10825
|
-
rows: body.rows,
|
|
10826
|
-
...body.note ? { note: body.note } : {},
|
|
10827
|
-
...cmd.pane === "files" ? { query: cmd.query ?? "" } : {},
|
|
10828
|
-
...cmd.pane === "changes" ? {
|
|
10829
|
-
scope: body.scope,
|
|
10830
|
-
commitSha: body.commitSha ?? null,
|
|
10831
|
-
...body.base ? { base: body.base } : {},
|
|
10832
|
-
hasLastTurnBase: body.hasLastTurnBase,
|
|
10833
|
-
commits: body.commits ?? [],
|
|
10834
|
-
...body.scopeStats ? { scopeStats: body.scopeStats } : {}
|
|
10835
|
-
} : {}
|
|
10836
|
-
});
|
|
10837
|
-
});
|
|
10838
|
-
return;
|
|
10839
|
-
}
|
|
10840
|
-
if (cmd.op === "header") {
|
|
10841
|
-
const thread = readPhoneThread(cmd.chatId);
|
|
10842
|
-
if (!thread) {
|
|
10843
|
-
emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
10844
|
-
return;
|
|
10845
|
-
}
|
|
10846
|
-
void readPhoneHeader(thread).then((header) => {
|
|
10847
|
-
emit({ op: "header", chatId: cmd.chatId, ...header });
|
|
10848
|
-
});
|
|
10849
|
-
return;
|
|
10850
|
-
}
|
|
10851
|
-
if (cmd.op === "git") {
|
|
10852
|
-
void getOrchestrator().askGit(cmd.chatId, cmd.action).then((tab) => publishOpened(tab.id)).catch((err) => {
|
|
10853
|
-
emit({
|
|
10854
|
-
op: "error",
|
|
10855
|
-
chatId: cmd.chatId,
|
|
10856
|
-
message: err instanceof Error ? err.message : String(err)
|
|
10857
|
-
});
|
|
10858
|
-
});
|
|
10859
|
-
return;
|
|
10860
|
-
}
|
|
10861
|
-
void getOrchestrator().requestReview(cmd.chatId).then((tab) => publishOpened(tab.id)).catch((err) => {
|
|
10862
|
-
emit({
|
|
10863
|
-
op: "error",
|
|
10864
|
-
chatId: cmd.chatId,
|
|
10865
|
-
message: err instanceof Error ? err.message : String(err)
|
|
10866
|
-
});
|
|
10867
|
-
});
|
|
10868
|
-
}
|
|
10869
|
-
|
|
10870
|
-
// src/remote/phone-sources.ts
|
|
10871
|
-
function sourceWarning(label, err) {
|
|
10872
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
10873
|
-
return message ? `${label}: ${message}` : `${label} failed.`;
|
|
10874
|
-
}
|
|
10875
|
-
async function listPhoneSources(repoPath, query) {
|
|
10876
|
-
const path2 = assertKnownProject(repoPath);
|
|
10877
|
-
const q = query?.trim() ?? "";
|
|
10878
|
-
const warnings = [];
|
|
10879
|
-
const [prs, branches, issues] = await Promise.all([
|
|
10880
|
-
listPrs(path2, { state: "open", limit: 40, ...q ? { query: q } : {} }).catch((err) => {
|
|
10881
|
-
warnings.push(sourceWarning("Pull requests", err));
|
|
10882
|
-
return [];
|
|
10883
|
-
}),
|
|
10884
|
-
listBranches(path2).catch((err) => {
|
|
10885
|
-
warnings.push(sourceWarning("Branches", err));
|
|
10886
|
-
return [];
|
|
10887
|
-
}),
|
|
10888
|
-
listIssues(path2, { limit: 40, ...q ? { query: q } : {} }).catch((err) => {
|
|
10889
|
-
warnings.push(sourceWarning("Issues", err));
|
|
10890
|
-
return { issues: [] };
|
|
10891
|
-
})
|
|
10892
|
-
]);
|
|
10893
|
-
const needle = q.toLowerCase();
|
|
10894
|
-
const branchRows = branches.filter((branch) => !needle || branch.name.toLowerCase().includes(needle)).slice(0, 50).map((branch) => ({
|
|
10895
|
-
ref: branch.name,
|
|
10896
|
-
title: branch.current ? `${branch.name} (current)` : branch.name
|
|
10897
|
-
}));
|
|
10898
|
-
return {
|
|
10899
|
-
prs: prs.slice(0, 40).map((pr) => ({
|
|
10900
|
-
ref: String(pr.number),
|
|
10901
|
-
title: clip(pr.title || `PR #${pr.number}`, 140),
|
|
10902
|
-
...pr.url ? { url: pr.url } : {}
|
|
10903
|
-
})),
|
|
10904
|
-
branches: branchRows,
|
|
10905
|
-
issues: (issues.issues ?? []).slice(0, 40).map((issue) => ({
|
|
10906
|
-
ref: issue.identifier,
|
|
10907
|
-
title: clip(issue.title || issue.identifier, 140),
|
|
10908
|
-
...issue.url ? { url: issue.url } : {}
|
|
10909
|
-
})),
|
|
10910
|
-
...warnings.length ? { warnings } : {}
|
|
10911
|
-
};
|
|
10912
|
-
}
|
|
10913
|
-
|
|
10914
11105
|
// src/remote/phone-live.ts
|
|
10915
11106
|
var REMOTE_STOPPED_REPLY = "Sideboard stopped the in-progress turn.";
|
|
10916
11107
|
var chatChains = /* @__PURE__ */ new Map();
|
|
@@ -11037,10 +11228,12 @@ function emitChatTurn(opts, chatId, threadId) {
|
|
|
11037
11228
|
failed: (detail) => detail ? `Sideboard failed: ${detail}` : "Sideboard failed before producing a result."
|
|
11038
11229
|
});
|
|
11039
11230
|
const agent = [...thread.messages].reverse().find((m) => m.role === "agent");
|
|
11231
|
+
const apps = phoneMcpApps(thread.agent, agent?.parts);
|
|
11040
11232
|
emitControl(opts, {
|
|
11041
11233
|
op: "assistant",
|
|
11042
11234
|
chatId,
|
|
11043
|
-
text: phoneArtifactText(reply.trim(), agent?.parts)
|
|
11235
|
+
text: phoneArtifactText(reply.trim(), agent?.parts),
|
|
11236
|
+
...apps.length ? { apps } : {}
|
|
11044
11237
|
});
|
|
11045
11238
|
}
|
|
11046
11239
|
function phoneError(err) {
|
|
@@ -11073,6 +11266,279 @@ function deliverPhoneTurn(opts, chatId) {
|
|
|
11073
11266
|
});
|
|
11074
11267
|
}
|
|
11075
11268
|
|
|
11269
|
+
// src/remote/phone-mcp-ui-listen.ts
|
|
11270
|
+
var PHONE_HTML_CAP = 7e5;
|
|
11271
|
+
function dispatchPhoneMcpUi(cmd, opts) {
|
|
11272
|
+
if (cmd.op !== "mcp-ui" && cmd.op !== "mcp-ui-call" && cmd.op !== "mcp-ui-read") return false;
|
|
11273
|
+
const thread = readPhoneThread(cmd.chatId);
|
|
11274
|
+
const reqId = cmd.reqId;
|
|
11275
|
+
const chatId = cmd.chatId;
|
|
11276
|
+
if (!thread || thread.agent !== "claude") {
|
|
11277
|
+
if (cmd.op === "mcp-ui") emitControl(opts, { op: "mcp-ui", chatId, reqId, view: null });
|
|
11278
|
+
else emitControl(opts, { op: cmd.op, chatId, reqId, error: "This chat is not using Claude." });
|
|
11279
|
+
return true;
|
|
11280
|
+
}
|
|
11281
|
+
if (cmd.op === "mcp-ui-call") {
|
|
11282
|
+
void getMcpUiHost().callTool(cmd.server, cmd.tool, cmd.args).then((result) => emitControl(opts, { op: "mcp-ui-call", chatId, reqId, result })).catch(
|
|
11283
|
+
(err) => emitControl(opts, { op: "mcp-ui-call", chatId, reqId, error: phoneError(err) })
|
|
11284
|
+
);
|
|
11285
|
+
return true;
|
|
11286
|
+
}
|
|
11287
|
+
if (cmd.op === "mcp-ui-read") {
|
|
11288
|
+
void getMcpUiHost().readResource(cmd.server, cmd.uri).then((result) => emitControl(opts, { op: "mcp-ui-read", chatId, reqId, result })).catch(
|
|
11289
|
+
(err) => emitControl(opts, { op: "mcp-ui-read", chatId, reqId, error: phoneError(err) })
|
|
11290
|
+
);
|
|
11291
|
+
return true;
|
|
11292
|
+
}
|
|
11293
|
+
const agentMessage = [...thread.messages].reverse().find((message) => message.role === "agent");
|
|
11294
|
+
const payload = phoneMcpToolPayload(agentMessage?.parts, cmd.tool, cmd.callId);
|
|
11295
|
+
void getMcpUiHost().viewForClaudeTool(cmd.tool).then((view) => {
|
|
11296
|
+
if (!view || view.html.length > PHONE_HTML_CAP) {
|
|
11297
|
+
emitControl(opts, { op: "mcp-ui", chatId, reqId, view: null });
|
|
11298
|
+
return;
|
|
11299
|
+
}
|
|
11300
|
+
emitControl(opts, {
|
|
11301
|
+
op: "mcp-ui",
|
|
11302
|
+
chatId,
|
|
11303
|
+
reqId,
|
|
11304
|
+
view: {
|
|
11305
|
+
server: view.server,
|
|
11306
|
+
tool: view.tool,
|
|
11307
|
+
html: view.html,
|
|
11308
|
+
csp: view.csp,
|
|
11309
|
+
uri: view.uri,
|
|
11310
|
+
input: payload?.input ?? {},
|
|
11311
|
+
resultText: payload?.resultText ?? "",
|
|
11312
|
+
isError: payload?.isError ?? false
|
|
11313
|
+
}
|
|
11314
|
+
});
|
|
11315
|
+
}).catch((err) => emitControl(opts, { op: "error", chatId, message: phoneError(err) }));
|
|
11316
|
+
return true;
|
|
11317
|
+
}
|
|
11318
|
+
|
|
11319
|
+
// src/remote/phone-media.ts
|
|
11320
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
11321
|
+
import { join as join6 } from "path";
|
|
11322
|
+
var PHONE_IMAGE_MAX_BYTES = 15e5;
|
|
11323
|
+
var PHONE_TEXT_MAX_BYTES = 18e4;
|
|
11324
|
+
var PHONE_DIR_MAX = 200;
|
|
11325
|
+
var IMAGE_MIME = {
|
|
11326
|
+
png: "image/png",
|
|
11327
|
+
jpg: "image/jpeg",
|
|
11328
|
+
jpeg: "image/jpeg",
|
|
11329
|
+
gif: "image/gif",
|
|
11330
|
+
webp: "image/webp",
|
|
11331
|
+
bmp: "image/bmp"
|
|
11332
|
+
};
|
|
11333
|
+
function phoneWorktreeRelative(worktreePath, raw) {
|
|
11334
|
+
let text5 = raw.trim();
|
|
11335
|
+
if (!text5) return null;
|
|
11336
|
+
if (/^file:\/\//i.test(text5)) {
|
|
11337
|
+
try {
|
|
11338
|
+
text5 = decodeURIComponent(new URL(text5).pathname);
|
|
11339
|
+
} catch {
|
|
11340
|
+
return null;
|
|
11341
|
+
}
|
|
11342
|
+
}
|
|
11343
|
+
text5 = (text5.split("#")[0] ?? text5).replace(/\\/g, "/");
|
|
11344
|
+
if (!text5 || text5.includes("\0") || text5.split("/").includes("..")) return null;
|
|
11345
|
+
const root = worktreePath.replace(/\/+$/, "");
|
|
11346
|
+
if (!root || root === "/") return null;
|
|
11347
|
+
if (text5.startsWith("/")) {
|
|
11348
|
+
if (text5 !== root && !text5.startsWith(`${root}/`)) return null;
|
|
11349
|
+
text5 = text5 === root ? "" : text5.slice(root.length + 1);
|
|
11350
|
+
}
|
|
11351
|
+
text5 = text5.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
11352
|
+
if (!text5 || text5 === "." || text5.startsWith("/")) return null;
|
|
11353
|
+
return text5;
|
|
11354
|
+
}
|
|
11355
|
+
function phoneImageMime(relativePath) {
|
|
11356
|
+
const ext = relativePath.split(".").pop()?.toLowerCase() ?? "";
|
|
11357
|
+
return IMAGE_MIME[ext] ?? null;
|
|
11358
|
+
}
|
|
11359
|
+
function phoneImageDataUrl(worktreePath, raw) {
|
|
11360
|
+
const relative = phoneWorktreeRelative(worktreePath, raw);
|
|
11361
|
+
if (!relative) return null;
|
|
11362
|
+
const mime = phoneImageMime(relative);
|
|
11363
|
+
if (!mime) return null;
|
|
11364
|
+
try {
|
|
11365
|
+
const file = readWorktreeFileForUpload(worktreePath, relative, { maxBytes: PHONE_IMAGE_MAX_BYTES });
|
|
11366
|
+
return `data:${mime};base64,${file.contentBase64}`;
|
|
11367
|
+
} catch {
|
|
11368
|
+
return null;
|
|
11369
|
+
}
|
|
11370
|
+
}
|
|
11371
|
+
function phoneDirectoryListing(worktreePath, relative) {
|
|
11372
|
+
try {
|
|
11373
|
+
const entries = readdirSync2(join6(worktreePath, relative), { withFileTypes: true });
|
|
11374
|
+
const dirs = new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
11375
|
+
const names = entries.map((entry) => entry.name).filter((name) => name && !name.includes("/") && !name.includes("\0")).sort((a, b) => a.localeCompare(b));
|
|
11376
|
+
const shown = names.slice(0, PHONE_DIR_MAX);
|
|
11377
|
+
const lines = shown.map((name) => dirs.has(name) ? `${name}/` : name);
|
|
11378
|
+
const truncated = names.length > shown.length;
|
|
11379
|
+
if (truncated) lines.push(`\u2026 ${names.length - shown.length} more`);
|
|
11380
|
+
return { content: lines.join("\n"), truncated };
|
|
11381
|
+
} catch {
|
|
11382
|
+
return null;
|
|
11383
|
+
}
|
|
11384
|
+
}
|
|
11385
|
+
function phoneFilePreview(worktreePath, relative, kind) {
|
|
11386
|
+
if (kind === "dir") {
|
|
11387
|
+
const listing = phoneDirectoryListing(worktreePath, relative);
|
|
11388
|
+
if (!listing) return null;
|
|
11389
|
+
return {
|
|
11390
|
+
content: listing.content,
|
|
11391
|
+
encoding: "utf8",
|
|
11392
|
+
binary: false,
|
|
11393
|
+
directory: true,
|
|
11394
|
+
truncated: listing.truncated
|
|
11395
|
+
};
|
|
11396
|
+
}
|
|
11397
|
+
if (relative.toLowerCase().endsWith(".svg")) {
|
|
11398
|
+
try {
|
|
11399
|
+
const buf = readFileSync5(join6(worktreePath, relative));
|
|
11400
|
+
const truncated = buf.length > PHONE_TEXT_MAX_BYTES;
|
|
11401
|
+
return {
|
|
11402
|
+
content: buf.subarray(0, PHONE_TEXT_MAX_BYTES).toString("utf8"),
|
|
11403
|
+
encoding: "utf8",
|
|
11404
|
+
binary: false,
|
|
11405
|
+
directory: false,
|
|
11406
|
+
truncated,
|
|
11407
|
+
mime: "image/svg+xml"
|
|
11408
|
+
};
|
|
11409
|
+
} catch {
|
|
11410
|
+
return null;
|
|
11411
|
+
}
|
|
11412
|
+
}
|
|
11413
|
+
const mime = phoneImageMime(relative);
|
|
11414
|
+
if (mime) {
|
|
11415
|
+
const dataUrl = phoneImageDataUrl(worktreePath, relative);
|
|
11416
|
+
if (!dataUrl) return null;
|
|
11417
|
+
return {
|
|
11418
|
+
content: dataUrl,
|
|
11419
|
+
encoding: "base64",
|
|
11420
|
+
binary: true,
|
|
11421
|
+
directory: false,
|
|
11422
|
+
truncated: false,
|
|
11423
|
+
mime
|
|
11424
|
+
};
|
|
11425
|
+
}
|
|
11426
|
+
try {
|
|
11427
|
+
const file = readWorktreeFile(worktreePath, relative, { maxBytes: PHONE_TEXT_MAX_BYTES });
|
|
11428
|
+
return {
|
|
11429
|
+
content: file.content,
|
|
11430
|
+
encoding: file.encoding,
|
|
11431
|
+
binary: file.binary,
|
|
11432
|
+
directory: false,
|
|
11433
|
+
truncated: file.truncated
|
|
11434
|
+
};
|
|
11435
|
+
} catch {
|
|
11436
|
+
return null;
|
|
11437
|
+
}
|
|
11438
|
+
}
|
|
11439
|
+
|
|
11440
|
+
// src/remote/phone-workspace-ops.ts
|
|
11441
|
+
function dispatchPhoneWorkspace(cmd, emit, publishOpened) {
|
|
11442
|
+
if (cmd.op === "pane") {
|
|
11443
|
+
const thread = readPhoneThread(cmd.chatId);
|
|
11444
|
+
if (!thread) {
|
|
11445
|
+
emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
11446
|
+
return;
|
|
11447
|
+
}
|
|
11448
|
+
void readPhonePane(thread, cmd.pane, cmd.scope, cmd.query, cmd.commitSha).then((body) => {
|
|
11449
|
+
emit({
|
|
11450
|
+
op: "pane",
|
|
11451
|
+
chatId: cmd.chatId,
|
|
11452
|
+
pane: cmd.pane,
|
|
11453
|
+
rows: body.rows,
|
|
11454
|
+
...body.note ? { note: body.note } : {},
|
|
11455
|
+
...cmd.pane === "files" ? { query: cmd.query ?? "" } : {},
|
|
11456
|
+
...cmd.pane === "changes" ? {
|
|
11457
|
+
scope: body.scope,
|
|
11458
|
+
commitSha: body.commitSha ?? null,
|
|
11459
|
+
...body.base ? { base: body.base } : {},
|
|
11460
|
+
hasLastTurnBase: body.hasLastTurnBase,
|
|
11461
|
+
commits: body.commits ?? [],
|
|
11462
|
+
...body.scopeStats ? { scopeStats: body.scopeStats } : {}
|
|
11463
|
+
} : {}
|
|
11464
|
+
});
|
|
11465
|
+
});
|
|
11466
|
+
return;
|
|
11467
|
+
}
|
|
11468
|
+
if (cmd.op === "header") {
|
|
11469
|
+
const thread = readPhoneThread(cmd.chatId);
|
|
11470
|
+
if (!thread) {
|
|
11471
|
+
emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
|
|
11472
|
+
return;
|
|
11473
|
+
}
|
|
11474
|
+
void readPhoneHeader(thread).then((header) => {
|
|
11475
|
+
emit({ op: "header", chatId: cmd.chatId, ...header });
|
|
11476
|
+
});
|
|
11477
|
+
return;
|
|
11478
|
+
}
|
|
11479
|
+
if (cmd.op === "git") {
|
|
11480
|
+
void getOrchestrator().askGit(cmd.chatId, cmd.action).then((tab) => publishOpened(tab.id)).catch((err) => {
|
|
11481
|
+
emit({
|
|
11482
|
+
op: "error",
|
|
11483
|
+
chatId: cmd.chatId,
|
|
11484
|
+
message: err instanceof Error ? err.message : String(err)
|
|
11485
|
+
});
|
|
11486
|
+
});
|
|
11487
|
+
return;
|
|
11488
|
+
}
|
|
11489
|
+
void getOrchestrator().requestReview(cmd.chatId).then((tab) => publishOpened(tab.id)).catch((err) => {
|
|
11490
|
+
emit({
|
|
11491
|
+
op: "error",
|
|
11492
|
+
chatId: cmd.chatId,
|
|
11493
|
+
message: err instanceof Error ? err.message : String(err)
|
|
11494
|
+
});
|
|
11495
|
+
});
|
|
11496
|
+
}
|
|
11497
|
+
|
|
11498
|
+
// src/remote/phone-sources.ts
|
|
11499
|
+
function sourceWarning(label, err) {
|
|
11500
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11501
|
+
return message ? `${label}: ${message}` : `${label} failed.`;
|
|
11502
|
+
}
|
|
11503
|
+
async function listPhoneSources(repoPath, query) {
|
|
11504
|
+
const path2 = assertKnownProject(repoPath);
|
|
11505
|
+
const q = query?.trim() ?? "";
|
|
11506
|
+
const warnings = [];
|
|
11507
|
+
const [prs, branches, issues] = await Promise.all([
|
|
11508
|
+
listPrs(path2, { state: "open", limit: 40, ...q ? { query: q } : {} }).catch((err) => {
|
|
11509
|
+
warnings.push(sourceWarning("Pull requests", err));
|
|
11510
|
+
return [];
|
|
11511
|
+
}),
|
|
11512
|
+
listBranches(path2).catch((err) => {
|
|
11513
|
+
warnings.push(sourceWarning("Branches", err));
|
|
11514
|
+
return [];
|
|
11515
|
+
}),
|
|
11516
|
+
listIssues(path2, { limit: 40, ...q ? { query: q } : {} }).catch((err) => {
|
|
11517
|
+
warnings.push(sourceWarning("Issues", err));
|
|
11518
|
+
return { issues: [] };
|
|
11519
|
+
})
|
|
11520
|
+
]);
|
|
11521
|
+
const needle = q.toLowerCase();
|
|
11522
|
+
const branchRows = branches.filter((branch) => !needle || branch.name.toLowerCase().includes(needle)).slice(0, 50).map((branch) => ({
|
|
11523
|
+
ref: branch.name,
|
|
11524
|
+
title: branch.current ? `${branch.name} (current)` : branch.name
|
|
11525
|
+
}));
|
|
11526
|
+
return {
|
|
11527
|
+
prs: prs.slice(0, 40).map((pr) => ({
|
|
11528
|
+
ref: String(pr.number),
|
|
11529
|
+
title: clip(pr.title || `PR #${pr.number}`, 140),
|
|
11530
|
+
...pr.url ? { url: pr.url } : {}
|
|
11531
|
+
})),
|
|
11532
|
+
branches: branchRows,
|
|
11533
|
+
issues: (issues.issues ?? []).slice(0, 40).map((issue) => ({
|
|
11534
|
+
ref: issue.identifier,
|
|
11535
|
+
title: clip(issue.title || issue.identifier, 140),
|
|
11536
|
+
...issue.url ? { url: issue.url } : {}
|
|
11537
|
+
})),
|
|
11538
|
+
...warnings.length ? { warnings } : {}
|
|
11539
|
+
};
|
|
11540
|
+
}
|
|
11541
|
+
|
|
11076
11542
|
// src/remote/phone-turn.ts
|
|
11077
11543
|
function formatRemotePrompt(text5) {
|
|
11078
11544
|
return `Phone
|
|
@@ -11421,6 +11887,7 @@ function handlePhoneControl(cmd, opts) {
|
|
|
11421
11887
|
});
|
|
11422
11888
|
return;
|
|
11423
11889
|
}
|
|
11890
|
+
if (dispatchPhoneMcpUi(cmd, opts)) return;
|
|
11424
11891
|
if (cmd.op === "open-file") {
|
|
11425
11892
|
const thread = readPhoneThread(cmd.chatId);
|
|
11426
11893
|
const relative = thread ? phoneWorktreeRelative(thread.worktreePath, cmd.path) : null;
|
|
@@ -13551,7 +14018,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
13551
14018
|
await continueOnReply(threadId, prompt);
|
|
13552
14019
|
return;
|
|
13553
14020
|
}
|
|
13554
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
14021
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-CZPRDLHV.js");
|
|
13555
14022
|
await getOrchestrator2().send(threadId, prompt);
|
|
13556
14023
|
} catch {
|
|
13557
14024
|
}
|
|
@@ -14083,6 +14550,7 @@ export {
|
|
|
14083
14550
|
clipAgentEventForPaint,
|
|
14084
14551
|
clipThinkingForStore,
|
|
14085
14552
|
cloneRepoIntoSideboard,
|
|
14553
|
+
closeMcpUiHost,
|
|
14086
14554
|
codeRefRangeLabel,
|
|
14087
14555
|
codexAdapter,
|
|
14088
14556
|
codexSandboxWritableRootsArgs,
|
|
@@ -14305,6 +14773,7 @@ export {
|
|
|
14305
14773
|
getLinearApiKey,
|
|
14306
14774
|
getLinearAuthToken,
|
|
14307
14775
|
getLinearIssue,
|
|
14776
|
+
getMcpUiHost,
|
|
14308
14777
|
getOrchestrator,
|
|
14309
14778
|
getPr,
|
|
14310
14779
|
getPrChecks,
|
|
@@ -14370,6 +14839,7 @@ export {
|
|
|
14370
14839
|
isBrightsyConnected,
|
|
14371
14840
|
isBrightsyNdjsonLine,
|
|
14372
14841
|
isChatTextScale,
|
|
14842
|
+
isClaudeAiConnectorMcp,
|
|
14373
14843
|
isCloudCoordinatorThread,
|
|
14374
14844
|
isCompactToolName,
|
|
14375
14845
|
isConductorBundledCli,
|
|
@@ -14524,6 +14994,7 @@ export {
|
|
|
14524
14994
|
mcpServerIdForPermission,
|
|
14525
14995
|
mcpServerPermissionUpdate,
|
|
14526
14996
|
mcpStatusThinkingFromClaudeInit,
|
|
14997
|
+
mcpUiContentSecurityPolicy,
|
|
14527
14998
|
mentionsPrGoal,
|
|
14528
14999
|
mergeAgentGitAuthEnv,
|
|
14529
15000
|
mergePr,
|