@p4code/cli 0.5.5 → 0.5.6
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/bin.mjs +982 -130
- package/dist/client/assets/{DiffPanel-BRp6V4_6.js → DiffPanel-D8eBc-aG.js} +2 -2
- package/dist/client/assets/{FilePreviewPanel-CUBJUojc.js → FilePreviewPanel-BTSTI93L.js} +2 -2
- package/dist/client/assets/{PreviewPanel-DQStLGQW.js → PreviewPanel-BRylbqHA.js} +2 -2
- package/dist/client/assets/{PullRequestCodeTab-S1xN_kez.js → PullRequestCodeTab-Y9jgYJZq.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-B-zJkLkz.js → fileCommentAnnotations-pNlR9x0T.js} +2 -2
- package/dist/client/assets/{index-juoUzzRC.js → index-GS5A_LTK.js} +182 -175
- package/dist/client/assets/{index-Bb0jNtjG.css → index-H5w-6k_f.css} +1 -1
- package/dist/client/assets/previewAssetResource-DGdLZELv.js +48 -0
- package/dist/client/assets/{renderFileChildren-Clc6sdVq.js → renderFileChildren-DPpHhKCk.js} +2 -2
- package/dist/client/assets/{toggle-group-kzQfQIqg.js → toggle-group-JCyq--Le.js} +2 -2
- package/dist/client/index.html +3 -3
- package/package.json +1 -1
- package/dist/client/assets/previewAssetResource-D-dR3avM.js +0 -48
package/dist/bin.mjs
CHANGED
|
@@ -19,8 +19,11 @@ import "effect/LogLevel";
|
|
|
19
19
|
import * as Path$1 from "effect/Path";
|
|
20
20
|
import * as Schema$1 from "effect/Schema";
|
|
21
21
|
import * as NodePath from "node:path";
|
|
22
|
+
import { isAbsolute } from "node:path";
|
|
22
23
|
import * as NodeChildProcess from "node:child_process";
|
|
24
|
+
import { spawn } from "node:child_process";
|
|
23
25
|
import * as NodeFS from "node:fs";
|
|
26
|
+
import { constants } from "node:fs";
|
|
24
27
|
import * as DateTime from "effect/DateTime";
|
|
25
28
|
import * as Duration from "effect/Duration";
|
|
26
29
|
import * as Option from "effect/Option";
|
|
@@ -42,6 +45,7 @@ import * as Config from "effect/Config";
|
|
|
42
45
|
import * as References from "effect/References";
|
|
43
46
|
import * as HttpServerRequest$1 from "effect/unstable/http/HttpServerRequest";
|
|
44
47
|
import * as NodeCrypto from "node:crypto";
|
|
48
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
45
49
|
import * as Encoding from "effect/Encoding";
|
|
46
50
|
import * as Result$1 from "effect/Result";
|
|
47
51
|
import * as Crypto from "effect/Crypto";
|
|
@@ -57,6 +61,7 @@ import * as Cause from "effect/Cause";
|
|
|
57
61
|
import * as Exit from "effect/Exit";
|
|
58
62
|
import * as NodeReadline from "node:readline";
|
|
59
63
|
import * as NodeFSP from "node:fs/promises";
|
|
64
|
+
import { access } from "node:fs/promises";
|
|
60
65
|
import * as Clock from "effect/Clock";
|
|
61
66
|
import * as NodeStream from "@effect/platform-node/NodeStream";
|
|
62
67
|
import * as NodeHttp from "node:http";
|
|
@@ -260,7 +265,7 @@ const make$116 = () => {
|
|
|
260
265
|
const layer$119 = Layer.sync(NetService, make$116);
|
|
261
266
|
//#endregion
|
|
262
267
|
//#region package.json
|
|
263
|
-
var version$1 = "0.5.
|
|
268
|
+
var version$1 = "0.5.6";
|
|
264
269
|
//#endregion
|
|
265
270
|
//#region src/config.ts
|
|
266
271
|
/**
|
|
@@ -7823,6 +7828,7 @@ const TaskProgressPayload = Schema$1.Struct({
|
|
|
7823
7828
|
usage: Schema$1.optional(Schema$1.Unknown),
|
|
7824
7829
|
lastToolName: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
7825
7830
|
toolUseId: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
7831
|
+
model: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
7826
7832
|
subagentType: Schema$1.optional(TrimmedNonEmptyStringSchema$1)
|
|
7827
7833
|
});
|
|
7828
7834
|
const TaskCompletedPayload = Schema$1.Struct({
|
|
@@ -8838,7 +8844,11 @@ const DeviceHostSummary = Schema$1.Struct({
|
|
|
8838
8844
|
label: TrimmedNonEmptyString.check(Schema$1.isMaxLength(MAX_HOST_LABEL_LENGTH)),
|
|
8839
8845
|
platforms: Schema$1.Array(DevicePlatformAvailability),
|
|
8840
8846
|
hubInstalled: Schema$1.Boolean,
|
|
8841
|
-
agentDeviceInstalled: Schema$1.Boolean
|
|
8847
|
+
agentDeviceInstalled: Schema$1.Boolean,
|
|
8848
|
+
automation: Schema$1.optional(Schema$1.Struct({
|
|
8849
|
+
provider: Schema$1.Literals(["apple-xcode", "xcodebuildmcp"]),
|
|
8850
|
+
detail: Schema$1.String
|
|
8851
|
+
}))
|
|
8842
8852
|
});
|
|
8843
8853
|
/**
|
|
8844
8854
|
* Lifecycle of the helper processes on a host. Tools are installed on first
|
|
@@ -13591,6 +13601,45 @@ const ComputerUseDeviceTarget = {
|
|
|
13591
13601
|
deviceId: DeviceId,
|
|
13592
13602
|
leaseId: DeviceId
|
|
13593
13603
|
};
|
|
13604
|
+
const DeviceElementRef = TrimmedNonEmptyString.check(Schema$1.isMaxLength(128));
|
|
13605
|
+
const DeviceGesture = {
|
|
13606
|
+
direction: Schema$1.Literals([
|
|
13607
|
+
"up",
|
|
13608
|
+
"down",
|
|
13609
|
+
"left",
|
|
13610
|
+
"right"
|
|
13611
|
+
]),
|
|
13612
|
+
distance: Schema$1.optional(Schema$1.Number.check(Schema$1.isGreaterThan(0), Schema$1.isLessThanOrEqualTo(1)))
|
|
13613
|
+
};
|
|
13614
|
+
const ComputerUseDeviceAction = Schema$1.Union([
|
|
13615
|
+
Schema$1.Struct({
|
|
13616
|
+
action: Schema$1.Literal("tap"),
|
|
13617
|
+
elementRef: DeviceElementRef
|
|
13618
|
+
}),
|
|
13619
|
+
Schema$1.Struct({
|
|
13620
|
+
action: Schema$1.Literal("type_text"),
|
|
13621
|
+
elementRef: DeviceElementRef,
|
|
13622
|
+
text: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(16e3)),
|
|
13623
|
+
replaceExisting: Schema$1.optional(Schema$1.Boolean)
|
|
13624
|
+
}),
|
|
13625
|
+
Schema$1.Struct({
|
|
13626
|
+
action: Schema$1.Literal("swipe"),
|
|
13627
|
+
withinElementRef: DeviceElementRef,
|
|
13628
|
+
...DeviceGesture
|
|
13629
|
+
}),
|
|
13630
|
+
Schema$1.Struct({
|
|
13631
|
+
action: Schema$1.Literal("drag"),
|
|
13632
|
+
elementRef: DeviceElementRef,
|
|
13633
|
+
...DeviceGesture
|
|
13634
|
+
}),
|
|
13635
|
+
Schema$1.Struct({
|
|
13636
|
+
action: Schema$1.Literal("batch"),
|
|
13637
|
+
steps: Schema$1.Array(Schema$1.Struct({
|
|
13638
|
+
action: Schema$1.Literal("tap"),
|
|
13639
|
+
elementRef: DeviceElementRef
|
|
13640
|
+
})).check(Schema$1.isMinLength(1), Schema$1.isMaxLength(100))
|
|
13641
|
+
})
|
|
13642
|
+
]);
|
|
13594
13643
|
const ComputerUseDeviceOperation = Schema$1.Union([
|
|
13595
13644
|
Schema$1.Struct({ method: Schema$1.Literal("list") }),
|
|
13596
13645
|
Schema$1.Struct({
|
|
@@ -13605,7 +13654,18 @@ const ComputerUseDeviceOperation = Schema$1.Union([
|
|
|
13605
13654
|
"screenshot",
|
|
13606
13655
|
"close"
|
|
13607
13656
|
]),
|
|
13608
|
-
...ComputerUseDeviceTarget
|
|
13657
|
+
...ComputerUseDeviceTarget,
|
|
13658
|
+
sinceScreenHash: Schema$1.optional(TrimmedNonEmptyString)
|
|
13659
|
+
}),
|
|
13660
|
+
Schema$1.Struct({
|
|
13661
|
+
method: Schema$1.Literal("semantic"),
|
|
13662
|
+
...ComputerUseDeviceTarget,
|
|
13663
|
+
action: ComputerUseDeviceAction
|
|
13664
|
+
}),
|
|
13665
|
+
Schema$1.Struct({
|
|
13666
|
+
method: Schema$1.Literal("recording"),
|
|
13667
|
+
...ComputerUseDeviceTarget,
|
|
13668
|
+
action: Schema$1.Literals(["start", "stop"])
|
|
13609
13669
|
}),
|
|
13610
13670
|
Schema$1.Struct({
|
|
13611
13671
|
method: Schema$1.Literal("command"),
|
|
@@ -13717,6 +13777,7 @@ const WS_METHODS = {
|
|
|
13717
13777
|
serverGetProcessDiagnostics: "server.getProcessDiagnostics",
|
|
13718
13778
|
serverGetProcessResourceHistory: "server.getProcessResourceHistory",
|
|
13719
13779
|
serverSignalProcess: "server.signalProcess",
|
|
13780
|
+
threadHandoff: "thread.handoff",
|
|
13720
13781
|
btwAsk: "btw.ask",
|
|
13721
13782
|
btwCancel: "btw.cancel",
|
|
13722
13783
|
pullRequestsList: "pullRequests.list",
|
|
@@ -14360,6 +14421,14 @@ const WsServerGetProviderUsageRpc = Rpc.make(WS_METHODS.serverGetProviderUsage,
|
|
|
14360
14421
|
success: Schema$1.Struct({ report: Schema$1.String }),
|
|
14361
14422
|
error: Schema$1.Union([TextGenerationError, EnvironmentAuthorizationError])
|
|
14362
14423
|
});
|
|
14424
|
+
const WsThreadHandoffRpc = Rpc.make(WS_METHODS.threadHandoff, {
|
|
14425
|
+
payload: Schema$1.Struct({ threadId: ThreadId }),
|
|
14426
|
+
success: Schema$1.Struct({
|
|
14427
|
+
projectId: ProjectId,
|
|
14428
|
+
text: Schema$1.String
|
|
14429
|
+
}),
|
|
14430
|
+
error: Schema$1.Union([TextGenerationError, EnvironmentAuthorizationError])
|
|
14431
|
+
});
|
|
14363
14432
|
const WsBtwAskRpc = Rpc.make(WS_METHODS.btwAsk, {
|
|
14364
14433
|
payload: BtwAskInput,
|
|
14365
14434
|
success: BtwAskResult,
|
|
@@ -14820,7 +14889,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
|
|
|
14820
14889
|
error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
|
|
14821
14890
|
stream: true
|
|
14822
14891
|
});
|
|
14823
|
-
const WsRpcGroup = RpcGroup.make(WsDeviceGetStateRpc, WsDeviceListRpc, WsDeviceConfigureRpc, WsDeviceOpenRpc, WsDeviceCloseRpc, WsDeviceDetailRpc, WsDeviceActionRpc, WsSubscribeDevicesRpc, WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerProviderAuthStartRpc, WsServerProviderAuthCompleteRpc, WsServerProviderAuthCancelRpc, WsServerProviderAuthLogoutRpc, WsServerProviderAuthSubscribeRpc, WsServerProviderInstallStartRpc, WsServerProviderInstallCancelRpc, WsServerProviderInstallRemoveRpc, WsServerProviderInstallSubscribeRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetHostResourcesRpc, WsServerGetResourceTelemetryRpc, WsServerGetResourceTelemetryHistoryRpc, WsServerRetryResourceTelemetryRpc, WsSubscribeResourceTelemetryRpc, WsBtwAskRpc, WsBtwCancelRpc, WsServerGetUsageSummaryRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsPullRequestsListRpc, WsPullRequestsListStatsRpc, WsPullRequestsDetailRpc, WsPullRequestsStackRpc, WsPullRequestsActivityRpc, WsPullRequestsDiffFileContentsRpc, WsPullRequestsRunActionRpc, WsPullRequestsUpdateRpc, WsPullRequestsCommentRpc, WsPullRequestsUpdateCommentRpc, WsPullRequestsSubmitReviewRpc, WsPullRequestsReplyToThreadRpc, WsPullRequestsSetThreadResolutionRpc, WsPullRequestsSetReactionRpc, WsPullRequestsInvalidateRpc, WsPullRequestsReviewerCandidatesRpc, WsPullRequestsRequestReviewersRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchContentsRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsComputerUseConnectRpc, WsComputerUseListRpc, WsComputerUseControlsRpc, WsComputerUseControlRpc, WsComputerUsePreviewRpc, WsComputerUseRespondRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsFeedListRpc, WsFeedRefreshRpc, WsFeedSourcesListRpc, WsFeedSourcesUpsertRpc, WsFeedSourcesDeleteRpc, WsFeedMarkReadRpc, WsFeedCleanupRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsTrackerStatusRpc, WsTrackerSetApiKeyRpc, WsTrackerClearApiKeyRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetWorkflowScriptRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationSearchThreadsRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
14892
|
+
const WsRpcGroup = RpcGroup.make(WsDeviceGetStateRpc, WsDeviceListRpc, WsDeviceConfigureRpc, WsDeviceOpenRpc, WsDeviceCloseRpc, WsDeviceDetailRpc, WsDeviceActionRpc, WsSubscribeDevicesRpc, WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerProviderAuthStartRpc, WsServerProviderAuthCompleteRpc, WsServerProviderAuthCancelRpc, WsServerProviderAuthLogoutRpc, WsServerProviderAuthSubscribeRpc, WsServerProviderInstallStartRpc, WsServerProviderInstallCancelRpc, WsServerProviderInstallRemoveRpc, WsServerProviderInstallSubscribeRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetHostResourcesRpc, WsServerGetResourceTelemetryRpc, WsServerGetResourceTelemetryHistoryRpc, WsServerRetryResourceTelemetryRpc, WsSubscribeResourceTelemetryRpc, WsThreadHandoffRpc, WsBtwAskRpc, WsBtwCancelRpc, WsServerGetUsageSummaryRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsPullRequestsListRpc, WsPullRequestsListStatsRpc, WsPullRequestsDetailRpc, WsPullRequestsStackRpc, WsPullRequestsActivityRpc, WsPullRequestsDiffFileContentsRpc, WsPullRequestsRunActionRpc, WsPullRequestsUpdateRpc, WsPullRequestsCommentRpc, WsPullRequestsUpdateCommentRpc, WsPullRequestsSubmitReviewRpc, WsPullRequestsReplyToThreadRpc, WsPullRequestsSetThreadResolutionRpc, WsPullRequestsSetReactionRpc, WsPullRequestsInvalidateRpc, WsPullRequestsReviewerCandidatesRpc, WsPullRequestsRequestReviewersRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchContentsRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsComputerUseConnectRpc, WsComputerUseListRpc, WsComputerUseControlsRpc, WsComputerUseControlRpc, WsComputerUsePreviewRpc, WsComputerUseRespondRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsFeedListRpc, WsFeedRefreshRpc, WsFeedSourcesListRpc, WsFeedSourcesUpsertRpc, WsFeedSourcesDeleteRpc, WsFeedMarkReadRpc, WsFeedCleanupRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsTrackerStatusRpc, WsTrackerSetApiKeyRpc, WsTrackerClearApiKeyRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetWorkflowScriptRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationSearchThreadsRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
14824
14893
|
//#endregion
|
|
14825
14894
|
//#region ../../packages/shared/src/oauthScope.ts
|
|
14826
14895
|
const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
|
|
@@ -24635,7 +24704,7 @@ const MAX_APNS_REQUEST_BYTES = 4096;
|
|
|
24635
24704
|
const CLEANUP_TIMEOUT_MS$1 = 1e3;
|
|
24636
24705
|
const MAX_REQUEST_BYTES$2 = 8 * 1024;
|
|
24637
24706
|
const MAX_RESPONSE_BYTES$8 = 16 * 1024;
|
|
24638
|
-
const REQUEST_TIMEOUT_MS$
|
|
24707
|
+
const REQUEST_TIMEOUT_MS$7 = 8e3;
|
|
24639
24708
|
const MAX_AUTH_CHARS = 8192;
|
|
24640
24709
|
const unavailable$6 = () => ({
|
|
24641
24710
|
outcome: "retry",
|
|
@@ -24680,7 +24749,7 @@ const sendHubApns = Effect.fn("HubPushTransport.apns")(function* (input) {
|
|
|
24680
24749
|
finish(unavailable$6());
|
|
24681
24750
|
return;
|
|
24682
24751
|
}
|
|
24683
|
-
timer = setTimeout(() => finish(unavailable$6()), REQUEST_TIMEOUT_MS$
|
|
24752
|
+
timer = setTimeout(() => finish(unavailable$6()), REQUEST_TIMEOUT_MS$7);
|
|
24684
24753
|
session = NodeHttp2.connect(input.sandbox ? "https://api.sandbox.push.apple.com" : "https://api.push.apple.com", { maxHeaderListPairs: 32 });
|
|
24685
24754
|
session.on("error", () => finish(unavailable$6()));
|
|
24686
24755
|
session.on("close", () => finish(unavailable$6()));
|
|
@@ -24735,7 +24804,7 @@ const sendHubFcm = Effect.fn("HubPushTransport.fcm")(function* (input, fetcher =
|
|
|
24735
24804
|
const abort = () => owner.abort();
|
|
24736
24805
|
signal.addEventListener("abort", abort, { once: true });
|
|
24737
24806
|
if (signal.aborted) owner.abort();
|
|
24738
|
-
const timeout = setTimeout(abort, REQUEST_TIMEOUT_MS$
|
|
24807
|
+
const timeout = setTimeout(abort, REQUEST_TIMEOUT_MS$7);
|
|
24739
24808
|
let reader;
|
|
24740
24809
|
try {
|
|
24741
24810
|
const response = await fetcher(`https://fcm.googleapis.com/v1/projects/${input.projectId}/messages:send`, {
|
|
@@ -24776,7 +24845,7 @@ const sendHubFcm = Effect.fn("HubPushTransport.fcm")(function* (input, fetcher =
|
|
|
24776
24845
|
}
|
|
24777
24846
|
},
|
|
24778
24847
|
catch: () => unavailable$6()
|
|
24779
|
-
}).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$
|
|
24848
|
+
}).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$7), Effect.catch(() => Effect.succeed(unavailable$6())));
|
|
24780
24849
|
});
|
|
24781
24850
|
//#endregion
|
|
24782
24851
|
//#region src/hub/HubNotificationRuntime.ts
|
|
@@ -41682,6 +41751,72 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
|
|
|
41682
41751
|
if (isPersistenceError(error)) return error;
|
|
41683
41752
|
return toPersistenceSqlError("ProjectionSnapshotQuery.getSnapshot:query")(error);
|
|
41684
41753
|
}));
|
|
41754
|
+
const readHandoffThread = SqlSchema.findOneOption({
|
|
41755
|
+
Request: ThreadIdLookupInput,
|
|
41756
|
+
Result: ProjectionBtwThreadRowSchema,
|
|
41757
|
+
execute: ({ threadId }) => sql`
|
|
41758
|
+
SELECT threads.project_id AS "projectId",
|
|
41759
|
+
COALESCE(projects.workspace_root, threads.worktree_path, '') AS cwd
|
|
41760
|
+
FROM projection_threads AS threads
|
|
41761
|
+
LEFT JOIN projection_projects AS projects ON projects.project_id = threads.project_id
|
|
41762
|
+
WHERE threads.thread_id = ${threadId} AND threads.deleted_at IS NULL
|
|
41763
|
+
AND (threads.project_id = ${P4_CHAT_PROJECT_ID} OR projects.deleted_at IS NULL)
|
|
41764
|
+
`
|
|
41765
|
+
});
|
|
41766
|
+
const readHandoffRows = SqlSchema.findAll({
|
|
41767
|
+
Request: ThreadIdLookupInput,
|
|
41768
|
+
Result: Schema$1.Struct({
|
|
41769
|
+
threadId: ThreadId,
|
|
41770
|
+
role: Schema$1.String,
|
|
41771
|
+
text: Schema$1.String
|
|
41772
|
+
}),
|
|
41773
|
+
execute: ({ threadId }) => sql`
|
|
41774
|
+
WITH latest_pair AS (
|
|
41775
|
+
SELECT implementer_thread_id, watcher_thread_id FROM thread_pairs
|
|
41776
|
+
WHERE implementer_thread_id = ${threadId} OR watcher_thread_id = ${threadId}
|
|
41777
|
+
ORDER BY created_at DESC LIMIT 1
|
|
41778
|
+
), sources AS (
|
|
41779
|
+
SELECT ${threadId} AS id
|
|
41780
|
+
UNION SELECT implementer_thread_id FROM latest_pair
|
|
41781
|
+
UNION SELECT watcher_thread_id FROM latest_pair
|
|
41782
|
+
), ranked AS (
|
|
41783
|
+
SELECT messages.thread_id AS "threadId", messages.role,
|
|
41784
|
+
substr(messages.text, -${BTW_CONTEXT_CHARACTER_LIMIT}) AS text,
|
|
41785
|
+
messages.created_at, messages.message_id,
|
|
41786
|
+
ROW_NUMBER() OVER (PARTITION BY messages.thread_id ORDER BY messages.created_at DESC, messages.message_id DESC) AS recent,
|
|
41787
|
+
ROW_NUMBER() OVER (PARTITION BY messages.thread_id ORDER BY messages.created_at, messages.message_id) AS earliest
|
|
41788
|
+
FROM projection_thread_messages AS messages
|
|
41789
|
+
JOIN projection_threads AS threads ON threads.thread_id = messages.thread_id
|
|
41790
|
+
WHERE messages.thread_id IN (SELECT id FROM sources)
|
|
41791
|
+
AND threads.deleted_at IS NULL AND messages.is_streaming = 0
|
|
41792
|
+
AND messages.role IN ('user', 'assistant')
|
|
41793
|
+
) SELECT "threadId", role, text FROM ranked
|
|
41794
|
+
WHERE recent <= ${BTW_CONTEXT_MESSAGE_LIMIT} OR earliest = 1
|
|
41795
|
+
ORDER BY created_at, message_id
|
|
41796
|
+
`
|
|
41797
|
+
});
|
|
41798
|
+
const getHandoffContext = Effect.fn("ProjectionSnapshotQuery.getHandoffContext")(function* (threadId) {
|
|
41799
|
+
return yield* sql.withTransaction(Effect.gen(function* () {
|
|
41800
|
+
const thread = yield* readHandoffThread({ threadId });
|
|
41801
|
+
if (Option.isNone(thread)) return Option.none();
|
|
41802
|
+
const rows = yield* readHandoffRows({ threadId });
|
|
41803
|
+
const sourceIds = [...new Set(rows.map((row) => row.threadId))];
|
|
41804
|
+
const sourceCount = Math.max(1, sourceIds.length);
|
|
41805
|
+
const budget = Math.floor((BTW_CONTEXT_CHARACTER_LIMIT - 2 * (sourceCount - 1)) / sourceCount);
|
|
41806
|
+
const transcript = sourceIds.map((sourceId) => {
|
|
41807
|
+
const text = rows.filter((row) => row.threadId === sourceId).map((row) => `${row.role}: ${row.text}`).join("\n\n");
|
|
41808
|
+
const heading = `Thread ${sourceId} (bounded history):\n`;
|
|
41809
|
+
const available = budget - heading.length;
|
|
41810
|
+
const omission = "\n[Earlier history omitted]\n";
|
|
41811
|
+
const beginning = Math.floor(available / 4);
|
|
41812
|
+
return heading + (text.length <= available ? text : text.slice(0, beginning) + omission + text.slice(-(available - beginning - 27)));
|
|
41813
|
+
}).join("\n\n");
|
|
41814
|
+
return Option.some({
|
|
41815
|
+
...thread.value,
|
|
41816
|
+
transcript
|
|
41817
|
+
});
|
|
41818
|
+
})).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getHandoffContext:query", "ProjectionSnapshotQuery.getHandoffContext:decodeRows")));
|
|
41819
|
+
});
|
|
41685
41820
|
const getBtwContext = Effect.fn("ProjectionSnapshotQuery.getBtwContext")(function* (threadId) {
|
|
41686
41821
|
const [thread, rows] = yield* Effect.all([getBtwThreadRow({ threadId }), listBtwContextRows({ threadId })]).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getBtwContext:query", "ProjectionSnapshotQuery.getBtwContext:decodeRows")));
|
|
41687
41822
|
if (Option.isNone(thread)) return Option.none();
|
|
@@ -42265,6 +42400,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
|
|
|
42265
42400
|
getThreadPairById,
|
|
42266
42401
|
getThreadShellById,
|
|
42267
42402
|
getThreadDetailById,
|
|
42403
|
+
getHandoffContext,
|
|
42268
42404
|
getBtwContext,
|
|
42269
42405
|
getThreadDetailSnapshot
|
|
42270
42406
|
};
|
|
@@ -45373,7 +45509,7 @@ var DeviceDiscoveryFailure = class extends Data.TaggedError("DeviceDiscoveryFail
|
|
|
45373
45509
|
const MAX_DEVICES = 512;
|
|
45374
45510
|
const MAX_RESPONSE_BYTES$6 = 1024 * 1024;
|
|
45375
45511
|
const MAX_DEVICE_LABEL = 512;
|
|
45376
|
-
const REQUEST_TIMEOUT_MS$
|
|
45512
|
+
const REQUEST_TIMEOUT_MS$6 = 15e3;
|
|
45377
45513
|
const HubDevice = Schema$1.Struct({
|
|
45378
45514
|
id: DeviceId,
|
|
45379
45515
|
name: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_DEVICE_LABEL)),
|
|
@@ -45416,7 +45552,10 @@ const make$87 = Effect.fn("DeviceDiscovery.make")(function* () {
|
|
|
45416
45552
|
});
|
|
45417
45553
|
if (body.invalidUtf8 || body.truncated) return yield* new DeviceDiscoveryFailure({ message: "Invalid device discovery response." });
|
|
45418
45554
|
const result = yield* decodeDeviceDiscovery(input.hostId, body.text);
|
|
45419
|
-
const
|
|
45555
|
+
const adapterDevices = input.ready.xcode ? yield* input.ready.xcode.discover : [];
|
|
45556
|
+
const simulatorIds = new Set(adapterDevices.map((device) => device.id));
|
|
45557
|
+
const devices = [...result.devices.filter((device) => device.platform !== "ios" || device.physical || !simulatorIds.has(device.id)), ...adapterDevices];
|
|
45558
|
+
if (devices.length > MAX_DEVICES || new Set(devices.map((device) => device.id)).size !== devices.length || adapterDevices.some((device) => device.hostId !== input.hostId || device.platform !== "ios" || device.physical)) return yield* new DeviceDiscoveryFailure({ message: "Invalid Xcode simulator identity." });
|
|
45420
45559
|
if (input.androidAvailable) {
|
|
45421
45560
|
const avds = yield* input.ready.run("emulator", ["-list-avds"]);
|
|
45422
45561
|
if (avds.code !== 0) return yield* new DeviceDiscoveryFailure({ message: "Android virtual device discovery failed." });
|
|
@@ -45440,7 +45579,7 @@ const make$87 = Effect.fn("DeviceDiscovery.make")(function* () {
|
|
|
45440
45579
|
devices,
|
|
45441
45580
|
partial: result.partial
|
|
45442
45581
|
};
|
|
45443
|
-
}, Effect.scoped, Effect.timeout(Duration.millis(REQUEST_TIMEOUT_MS$
|
|
45582
|
+
}, Effect.scoped, Effect.timeout(Duration.millis(REQUEST_TIMEOUT_MS$6)), Effect.mapError(() => new DeviceOperationError({
|
|
45444
45583
|
operation: "list",
|
|
45445
45584
|
reason: "request_failed",
|
|
45446
45585
|
cause: "Device discovery unavailable."
|
|
@@ -45744,6 +45883,675 @@ const start = Effect.fn("DeviceHostSettings.start")(function* (input) {
|
|
|
45744
45883
|
return { refresh };
|
|
45745
45884
|
});
|
|
45746
45885
|
//#endregion
|
|
45886
|
+
//#region src/device/DeviceScreenshot.ts
|
|
45887
|
+
const MAX_SCREENSHOT_BYTES = 8 * 1024 * 1024;
|
|
45888
|
+
var DeviceScreenshotFailure = class extends Data.TaggedError("DeviceScreenshotFailure") {};
|
|
45889
|
+
const MAX_DIMENSION = 16384;
|
|
45890
|
+
const MAX_PIXELS = 64 * 1024 * 1024;
|
|
45891
|
+
const SCREENSHOT_TIMEOUT_MS = 2e4;
|
|
45892
|
+
const PNG_HEADER_BYTES = 33;
|
|
45893
|
+
const PNG_CHUNK_OVERHEAD = 12;
|
|
45894
|
+
const PNG_SIGNATURE = Buffer.from([
|
|
45895
|
+
137,
|
|
45896
|
+
80,
|
|
45897
|
+
78,
|
|
45898
|
+
71,
|
|
45899
|
+
13,
|
|
45900
|
+
10,
|
|
45901
|
+
26,
|
|
45902
|
+
10
|
|
45903
|
+
]);
|
|
45904
|
+
function pngDimensions(png) {
|
|
45905
|
+
const buffer = Buffer.from(png.buffer, png.byteOffset, png.byteLength);
|
|
45906
|
+
if (buffer.length < PNG_HEADER_BYTES || buffer.length > 8388608 || !buffer.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE) || buffer.readUInt32BE(8) !== 13 || buffer.toString("ascii", 12, 16) !== "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG screenshot." });
|
|
45907
|
+
const width = buffer.readUInt32BE(16);
|
|
45908
|
+
const height = buffer.readUInt32BE(20);
|
|
45909
|
+
if (!width || !height || width > MAX_DIMENSION || height > MAX_DIMENSION || width * height > MAX_PIXELS) throw new DeviceScreenshotFailure({ message: "Screenshot dimensions exceed limits." });
|
|
45910
|
+
let offset = PNG_HEADER_BYTES;
|
|
45911
|
+
let imageData = false;
|
|
45912
|
+
let ended = false;
|
|
45913
|
+
while (offset + PNG_CHUNK_OVERHEAD <= buffer.length) {
|
|
45914
|
+
const length = buffer.readUInt32BE(offset);
|
|
45915
|
+
const type = buffer.toString("ascii", offset + 4, offset + 8);
|
|
45916
|
+
const next = offset + PNG_CHUNK_OVERHEAD + length;
|
|
45917
|
+
if (next > buffer.length || type === "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG chunk." });
|
|
45918
|
+
if (type === "IDAT") imageData = true;
|
|
45919
|
+
if (type === "IEND") {
|
|
45920
|
+
ended = length === 0 && next === buffer.length;
|
|
45921
|
+
break;
|
|
45922
|
+
}
|
|
45923
|
+
offset = next;
|
|
45924
|
+
}
|
|
45925
|
+
if (!imageData || !ended) throw new DeviceScreenshotFailure({ message: "Incomplete PNG screenshot." });
|
|
45926
|
+
return {
|
|
45927
|
+
width,
|
|
45928
|
+
height
|
|
45929
|
+
};
|
|
45930
|
+
}
|
|
45931
|
+
const make$82 = Effect.fn("DeviceScreenshot.make")(function* () {
|
|
45932
|
+
const http = yield* HttpClient.HttpClient;
|
|
45933
|
+
return Effect.fn("DeviceScreenshot.capture")(function* (ready, session) {
|
|
45934
|
+
const origin = yield* Effect.try(() => new URL(ready.hub.origin));
|
|
45935
|
+
if (origin.protocol !== "http:" || origin.hostname !== "127.0.0.1" || !origin.port || origin.username || origin.password || origin.pathname !== "/" || origin.search || origin.hash) return yield* new DeviceScreenshotFailure({ message: "Invalid owned hub." });
|
|
45936
|
+
const vendor = session.platform === "ios" ? "serve-sim" : "serve-emu";
|
|
45937
|
+
const response = yield* http.execute(HttpClientRequest.post(`${origin.origin}/vendor/${vendor}/api/screenshot?device=${encodeURIComponent(session.deviceId)}`));
|
|
45938
|
+
if (response.status !== 200) return yield* new DeviceScreenshotFailure({ message: "Screenshot rejected." });
|
|
45939
|
+
let bytes = 0;
|
|
45940
|
+
const chunks = [];
|
|
45941
|
+
yield* response.stream.pipe(Stream.runForEach((chunk) => Effect.gen(function* () {
|
|
45942
|
+
bytes += chunk.byteLength;
|
|
45943
|
+
if (bytes > 8388608) return yield* new DeviceScreenshotFailure({ message: "Screenshot too large." });
|
|
45944
|
+
chunks.push(chunk);
|
|
45945
|
+
})));
|
|
45946
|
+
const png = Buffer.concat(chunks, bytes);
|
|
45947
|
+
return {
|
|
45948
|
+
png,
|
|
45949
|
+
...yield* Effect.try(() => pngDimensions(png))
|
|
45950
|
+
};
|
|
45951
|
+
}, Effect.scoped, Effect.timeout(Duration.millis(SCREENSHOT_TIMEOUT_MS)), Effect.mapError(() => new DeviceOperationError({
|
|
45952
|
+
operation: "screenshot",
|
|
45953
|
+
reason: "request_failed",
|
|
45954
|
+
cause: "Device screenshot could not be captured."
|
|
45955
|
+
})));
|
|
45956
|
+
});
|
|
45957
|
+
var DeviceScreenshot = class extends Context.Service()("@p4code/cli/device/DeviceScreenshot") {};
|
|
45958
|
+
const layer$72 = Layer.effect(DeviceScreenshot, make$82().pipe(Effect.map((capture) => ({ capture }))));
|
|
45959
|
+
//#endregion
|
|
45960
|
+
//#region src/device/XcodeMcpClient.ts
|
|
45961
|
+
const XCODE_BUILD_MCP_VERSION = "2.7.0";
|
|
45962
|
+
const PROTOCOL_VERSION = "2025-06-18";
|
|
45963
|
+
const REQUEST_TIMEOUT_MS$5 = 6e4;
|
|
45964
|
+
const MAX_MESSAGE_BYTES = 12 * 1024 * 1024;
|
|
45965
|
+
const MAX_TOOL_PAGES = 32;
|
|
45966
|
+
function objectValue(value) {
|
|
45967
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
45968
|
+
}
|
|
45969
|
+
/** One process per lease keeps semantic refs and simulator defaults isolated. Never invokes npx. */
|
|
45970
|
+
var XcodeMcpClient = class XcodeMcpClient {
|
|
45971
|
+
child;
|
|
45972
|
+
sequence = 0;
|
|
45973
|
+
buffer = "";
|
|
45974
|
+
closed = false;
|
|
45975
|
+
pending = /* @__PURE__ */ new Map();
|
|
45976
|
+
tools = /* @__PURE__ */ new Set();
|
|
45977
|
+
constructor(child) {
|
|
45978
|
+
this.child = child;
|
|
45979
|
+
child.stdout.setEncoding("utf8");
|
|
45980
|
+
child.stdout.on("data", (chunk) => {
|
|
45981
|
+
this.buffer += chunk;
|
|
45982
|
+
if (Buffer.byteLength(this.buffer) > MAX_MESSAGE_BYTES) {
|
|
45983
|
+
this.close(/* @__PURE__ */ new Error("Device MCP response exceeds the supported size."));
|
|
45984
|
+
return;
|
|
45985
|
+
}
|
|
45986
|
+
let boundary;
|
|
45987
|
+
while ((boundary = this.buffer.indexOf("\n")) >= 0) {
|
|
45988
|
+
const line = this.buffer.slice(0, boundary);
|
|
45989
|
+
this.buffer = this.buffer.slice(boundary + 1);
|
|
45990
|
+
try {
|
|
45991
|
+
const message = objectValue(JSON.parse(line));
|
|
45992
|
+
if (typeof message.id !== "number") continue;
|
|
45993
|
+
const call = this.pending.get(message.id);
|
|
45994
|
+
if (!call) continue;
|
|
45995
|
+
this.pending.delete(message.id);
|
|
45996
|
+
if (message.error) {
|
|
45997
|
+
const detail = objectValue(message.error).message;
|
|
45998
|
+
call.reject(new Error(typeof detail === "string" ? detail : "Device MCP request failed."));
|
|
45999
|
+
} else call.resolve(message.result);
|
|
46000
|
+
} catch {
|
|
46001
|
+
this.close(/* @__PURE__ */ new Error("Device MCP returned invalid stdio JSON."));
|
|
46002
|
+
return;
|
|
46003
|
+
}
|
|
46004
|
+
}
|
|
46005
|
+
});
|
|
46006
|
+
child.stderr.resume();
|
|
46007
|
+
child.on("error", () => this.close(/* @__PURE__ */ new Error("Device MCP executable could not start.")));
|
|
46008
|
+
child.on("exit", () => this.close(/* @__PURE__ */ new Error("Device MCP disconnected. Select the device again.")));
|
|
46009
|
+
child.stdin.on("error", () => this.close(/* @__PURE__ */ new Error("Device MCP input disconnected.")));
|
|
46010
|
+
}
|
|
46011
|
+
static async start(executable, cwd, environment, signal, kind = "legacy") {
|
|
46012
|
+
if (!isAbsolute(executable)) throw new Error("Device MCP requires an absolute executable path.");
|
|
46013
|
+
await access(executable, constants.X_OK).catch(() => {
|
|
46014
|
+
throw new Error(kind === "apple" ? "Selected Xcode bridge is missing or not executable. Check the selected Xcode installation." : "Configured XcodeBuildMCP executable is missing or not executable. Install 2.7.0 explicitly.");
|
|
46015
|
+
});
|
|
46016
|
+
signal.throwIfAborted();
|
|
46017
|
+
const client = new XcodeMcpClient(spawn(executable, kind === "apple" ? ["mcpbridge"] : ["mcp"], {
|
|
46018
|
+
cwd,
|
|
46019
|
+
shell: false,
|
|
46020
|
+
stdio: "pipe",
|
|
46021
|
+
env: {
|
|
46022
|
+
...environment,
|
|
46023
|
+
...kind === "legacy" ? {
|
|
46024
|
+
XCODEBUILDMCP_CWD: cwd,
|
|
46025
|
+
XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS: "true",
|
|
46026
|
+
XCODEBUILDMCP_ENABLED_WORKFLOWS: "simulator,ui-automation",
|
|
46027
|
+
XCODEBUILDMCP_SENTRY_DISABLED: "true"
|
|
46028
|
+
} : {}
|
|
46029
|
+
}
|
|
46030
|
+
}));
|
|
46031
|
+
try {
|
|
46032
|
+
const initialized = objectValue(await client.request("initialize", {
|
|
46033
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
46034
|
+
capabilities: {},
|
|
46035
|
+
clientInfo: {
|
|
46036
|
+
name: "p4code-device-adapter",
|
|
46037
|
+
version: "1"
|
|
46038
|
+
}
|
|
46039
|
+
}, signal));
|
|
46040
|
+
if (kind === "apple" && objectValue(initialized.serverInfo).name !== "xcode-tools") throw new Error("Selected Xcode did not expose Apple MCP tools.");
|
|
46041
|
+
if (kind === "legacy" && objectValue(initialized.serverInfo).version !== "2.7.0") throw new Error(`P4 requires XcodeBuildMCP ${XCODE_BUILD_MCP_VERSION}; configured executable reported a different version.`);
|
|
46042
|
+
client.child.stdin.write(JSON.stringify({
|
|
46043
|
+
jsonrpc: "2.0",
|
|
46044
|
+
method: "notifications/initialized"
|
|
46045
|
+
}) + "\n");
|
|
46046
|
+
let cursor;
|
|
46047
|
+
const cursors = /* @__PURE__ */ new Set();
|
|
46048
|
+
do {
|
|
46049
|
+
const page = objectValue(await client.request("tools/list", cursor ? { cursor } : {}, signal));
|
|
46050
|
+
if (!Array.isArray(page.tools)) throw new Error("Device MCP did not advertise tools.");
|
|
46051
|
+
for (const tool of page.tools) {
|
|
46052
|
+
const name = objectValue(tool).name;
|
|
46053
|
+
if (typeof name === "string") client.tools.add(name);
|
|
46054
|
+
}
|
|
46055
|
+
cursor = page.nextCursor;
|
|
46056
|
+
if (typeof cursor === "string" && cursor.length > 0) {
|
|
46057
|
+
if (cursors.has(cursor) || cursors.size >= MAX_TOOL_PAGES) throw new Error("Device MCP tool discovery did not finish within its page limit.");
|
|
46058
|
+
cursors.add(cursor);
|
|
46059
|
+
}
|
|
46060
|
+
} while (typeof cursor === "string" && cursor.length > 0);
|
|
46061
|
+
return client;
|
|
46062
|
+
} catch (error) {
|
|
46063
|
+
client.close();
|
|
46064
|
+
throw error;
|
|
46065
|
+
}
|
|
46066
|
+
}
|
|
46067
|
+
request(method, params, signal) {
|
|
46068
|
+
signal.throwIfAborted();
|
|
46069
|
+
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("Device MCP session is closed. Select the device again."));
|
|
46070
|
+
const id = ++this.sequence;
|
|
46071
|
+
return new Promise((resolve, reject) => {
|
|
46072
|
+
const abort = () => this.close(/* @__PURE__ */ new Error("Device MCP request cancelled. Select the device again."));
|
|
46073
|
+
const timer = setTimeout(() => this.close(/* @__PURE__ */ new Error("Device MCP timed out. Select the device again.")), REQUEST_TIMEOUT_MS$5);
|
|
46074
|
+
const finish = () => {
|
|
46075
|
+
clearTimeout(timer);
|
|
46076
|
+
signal.removeEventListener("abort", abort);
|
|
46077
|
+
};
|
|
46078
|
+
this.pending.set(id, {
|
|
46079
|
+
resolve: (result) => {
|
|
46080
|
+
finish();
|
|
46081
|
+
resolve(result);
|
|
46082
|
+
},
|
|
46083
|
+
reject: (error) => {
|
|
46084
|
+
finish();
|
|
46085
|
+
reject(error);
|
|
46086
|
+
}
|
|
46087
|
+
});
|
|
46088
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
46089
|
+
this.child.stdin.write(JSON.stringify({
|
|
46090
|
+
jsonrpc: "2.0",
|
|
46091
|
+
id,
|
|
46092
|
+
method,
|
|
46093
|
+
params
|
|
46094
|
+
}) + "\n");
|
|
46095
|
+
});
|
|
46096
|
+
}
|
|
46097
|
+
async call(name, args, signal) {
|
|
46098
|
+
if (!this.tools.has(name)) throw new Error(`Selected device MCP service does not expose ${name}. Check adapter capabilities and Xcode setup.`);
|
|
46099
|
+
const redact = (error) => {
|
|
46100
|
+
let message = error instanceof Error ? error.message : String(error);
|
|
46101
|
+
for (const [key, value] of Object.entries(args)) if (/sessionkey$/i.test(key) && typeof value === "string" && value.length > 0) message = message.replaceAll(value, "[redacted]");
|
|
46102
|
+
return new Error(message);
|
|
46103
|
+
};
|
|
46104
|
+
try {
|
|
46105
|
+
const result = objectValue(await this.request("tools/call", {
|
|
46106
|
+
name,
|
|
46107
|
+
arguments: args
|
|
46108
|
+
}, signal));
|
|
46109
|
+
const structured = objectValue(result.structuredContent);
|
|
46110
|
+
if (result.isError === true || structured.didError === true) {
|
|
46111
|
+
const detail = (Array.isArray(result.content) ? result.content : []).map((part) => objectValue(part).text).filter((part) => typeof part === "string").join("\n");
|
|
46112
|
+
throw new Error(detail || String(structured.error ?? `${name} failed. Refresh device state before retrying.`));
|
|
46113
|
+
}
|
|
46114
|
+
return result;
|
|
46115
|
+
} catch (error) {
|
|
46116
|
+
throw redact(error);
|
|
46117
|
+
}
|
|
46118
|
+
}
|
|
46119
|
+
close(error = /* @__PURE__ */ new Error("Device MCP session closed.")) {
|
|
46120
|
+
if (this.closed) return;
|
|
46121
|
+
this.closed = true;
|
|
46122
|
+
for (const call of this.pending.values()) call.reject(error);
|
|
46123
|
+
this.pending.clear();
|
|
46124
|
+
this.child.kill("SIGTERM");
|
|
46125
|
+
}
|
|
46126
|
+
};
|
|
46127
|
+
//#endregion
|
|
46128
|
+
//#region src/device/XcodeDeviceAdapter.ts
|
|
46129
|
+
const SimulatorList = Schema$1.Struct({ simulators: Schema$1.Array(Schema$1.Struct({
|
|
46130
|
+
simulatorId: Schema$1.String,
|
|
46131
|
+
name: Schema$1.String,
|
|
46132
|
+
runtime: Schema$1.String,
|
|
46133
|
+
state: Schema$1.String,
|
|
46134
|
+
isAvailable: Schema$1.Boolean
|
|
46135
|
+
})) });
|
|
46136
|
+
const decodeSimulators = Schema$1.decodeUnknownSync(SimulatorList);
|
|
46137
|
+
const MAX_LEASES = 32;
|
|
46138
|
+
const Frame = Schema$1.Struct({
|
|
46139
|
+
x: Schema$1.Number,
|
|
46140
|
+
y: Schema$1.Number,
|
|
46141
|
+
width: Schema$1.Number,
|
|
46142
|
+
height: Schema$1.Number
|
|
46143
|
+
});
|
|
46144
|
+
const Snapshot = Schema$1.Struct({ elements: Schema$1.Array(Schema$1.Struct({
|
|
46145
|
+
ref: Schema$1.String,
|
|
46146
|
+
role: Schema$1.optional(Schema$1.String),
|
|
46147
|
+
frame: Frame
|
|
46148
|
+
})) });
|
|
46149
|
+
const readSnapshot = Schema$1.decodeUnknownOption(Snapshot);
|
|
46150
|
+
/** Retain structured semantic snapshots; callers never need screenshot round trips for refs. */
|
|
46151
|
+
function xcodeResultData(result) {
|
|
46152
|
+
const structured = objectValue(result.structuredContent);
|
|
46153
|
+
return objectValue(structured.data ?? structured);
|
|
46154
|
+
}
|
|
46155
|
+
function createXcodeDeviceAdapter(executable, cwd, environment) {
|
|
46156
|
+
const clients = /* @__PURE__ */ new Map();
|
|
46157
|
+
const recordings = /* @__PURE__ */ new Map();
|
|
46158
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
46159
|
+
const remember = (session, data) => {
|
|
46160
|
+
const snapshot = readSnapshot(data.capture);
|
|
46161
|
+
if (snapshot._tag === "Some") snapshots.set(session.leaseId, snapshot.value);
|
|
46162
|
+
const viewport = snapshots.get(session.leaseId)?.elements.find((element) => element.role === "window")?.frame;
|
|
46163
|
+
return {
|
|
46164
|
+
...data,
|
|
46165
|
+
...viewport ? { viewport } : {}
|
|
46166
|
+
};
|
|
46167
|
+
};
|
|
46168
|
+
const get = async (key, signal) => {
|
|
46169
|
+
let pending = clients.get(key);
|
|
46170
|
+
if (!pending) {
|
|
46171
|
+
if (clients.size >= MAX_LEASES) throw new Error("Close an existing Xcode device session before opening another.");
|
|
46172
|
+
pending = XcodeMcpClient.start(executable, cwd, environment, signal);
|
|
46173
|
+
clients.set(key, pending);
|
|
46174
|
+
pending.catch(() => {
|
|
46175
|
+
if (clients.get(key) === pending) clients.delete(key);
|
|
46176
|
+
});
|
|
46177
|
+
}
|
|
46178
|
+
return pending;
|
|
46179
|
+
};
|
|
46180
|
+
const run = (operation, execute) => Effect.tryPromise({
|
|
46181
|
+
try: execute,
|
|
46182
|
+
catch: (cause) => new DeviceOperationError({
|
|
46183
|
+
operation,
|
|
46184
|
+
reason: "request_failed",
|
|
46185
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
46186
|
+
})
|
|
46187
|
+
});
|
|
46188
|
+
const forSession = async (session, signal) => {
|
|
46189
|
+
if (session.hostId !== "local" || session.platform !== "ios" || !session.leaseId) throw new Error("Xcode adapter requires an exact local iOS Simulator lease.");
|
|
46190
|
+
return get(session.leaseId, signal);
|
|
46191
|
+
};
|
|
46192
|
+
const closeSession = (leaseId) => {
|
|
46193
|
+
const client = clients.get(leaseId);
|
|
46194
|
+
clients.delete(leaseId);
|
|
46195
|
+
const recording = recordings.get(leaseId);
|
|
46196
|
+
recordings.delete(leaseId);
|
|
46197
|
+
snapshots.delete(leaseId);
|
|
46198
|
+
client?.then(async (client) => {
|
|
46199
|
+
try {
|
|
46200
|
+
if (recording) await client.call("record_sim_video", {
|
|
46201
|
+
...recording,
|
|
46202
|
+
stop: true
|
|
46203
|
+
}, new AbortController().signal);
|
|
46204
|
+
} finally {
|
|
46205
|
+
client.close();
|
|
46206
|
+
}
|
|
46207
|
+
}).catch(() => {});
|
|
46208
|
+
};
|
|
46209
|
+
return {
|
|
46210
|
+
discover: run("list", async (signal) => {
|
|
46211
|
+
const client = await get("discovery", signal);
|
|
46212
|
+
return decodeSimulators(xcodeResultData(await client.call("list_sims", {}, signal))).simulators.filter((sim) => sim.isAvailable && /iOS/i.test(sim.runtime)).map((sim) => ({
|
|
46213
|
+
id: sim.simulatorId,
|
|
46214
|
+
hostId: "local",
|
|
46215
|
+
name: sim.name,
|
|
46216
|
+
version: sim.runtime,
|
|
46217
|
+
booted: sim.state === "Booted",
|
|
46218
|
+
physical: false,
|
|
46219
|
+
platform: "ios"
|
|
46220
|
+
}));
|
|
46221
|
+
}),
|
|
46222
|
+
state: (session, sinceScreenHash) => run("state", async (signal) => {
|
|
46223
|
+
const client = await forSession(session, signal);
|
|
46224
|
+
const data = xcodeResultData(await client.call("snapshot_ui", {
|
|
46225
|
+
simulatorId: session.deviceId,
|
|
46226
|
+
...sinceScreenHash ? { sinceScreenHash } : {}
|
|
46227
|
+
}, signal));
|
|
46228
|
+
return {
|
|
46229
|
+
adapter: "xcodebuildmcp",
|
|
46230
|
+
capabilities: [...client.tools].filter((tool) => [
|
|
46231
|
+
"tap",
|
|
46232
|
+
"type_text",
|
|
46233
|
+
"swipe",
|
|
46234
|
+
"drag",
|
|
46235
|
+
"batch",
|
|
46236
|
+
"snapshot_ui",
|
|
46237
|
+
"screenshot",
|
|
46238
|
+
"record_sim_video"
|
|
46239
|
+
].includes(tool)),
|
|
46240
|
+
...remember(session, data)
|
|
46241
|
+
};
|
|
46242
|
+
}),
|
|
46243
|
+
action: (session, input) => run("command", async (signal) => {
|
|
46244
|
+
const client = await forSession(session, signal);
|
|
46245
|
+
const { action, ...parameters } = input;
|
|
46246
|
+
const snapshot = snapshots.get(session.leaseId);
|
|
46247
|
+
const ref = input.action === "batch" ? input.steps.at(-1)?.elementRef : input.action === "swipe" ? input.withinElementRef : input.elementRef;
|
|
46248
|
+
const bounds = snapshot?.elements.find((element) => element.ref === ref)?.frame;
|
|
46249
|
+
const viewport = snapshot?.elements.find((element) => element.role === "window")?.frame;
|
|
46250
|
+
const data = xcodeResultData(await client.call(action, {
|
|
46251
|
+
...parameters,
|
|
46252
|
+
simulatorId: session.deviceId
|
|
46253
|
+
}, signal));
|
|
46254
|
+
return {
|
|
46255
|
+
...remember(session, data),
|
|
46256
|
+
...bounds && viewport && viewport.width > 0 && viewport.height > 0 ? { pointer: {
|
|
46257
|
+
x: (bounds.x + bounds.width / 2 - viewport.x) / viewport.width,
|
|
46258
|
+
y: (bounds.y + bounds.height / 2 - viewport.y) / viewport.height
|
|
46259
|
+
} } : {}
|
|
46260
|
+
};
|
|
46261
|
+
}),
|
|
46262
|
+
screenshot: (session) => run("screenshot", async (signal) => {
|
|
46263
|
+
const result = await (await forSession(session, signal)).call("screenshot", {
|
|
46264
|
+
simulatorId: session.deviceId,
|
|
46265
|
+
returnFormat: "base64"
|
|
46266
|
+
}, signal);
|
|
46267
|
+
const image = (Array.isArray(result.content) ? result.content : []).map(objectValue).find((part) => part.type === "image" && part.mimeType === "image/png");
|
|
46268
|
+
if (typeof image?.data !== "string") throw new Error("XcodeBuildMCP did not return a PNG screenshot.");
|
|
46269
|
+
const png = Buffer.from(image.data, "base64");
|
|
46270
|
+
return {
|
|
46271
|
+
png,
|
|
46272
|
+
...pngDimensions(png)
|
|
46273
|
+
};
|
|
46274
|
+
}),
|
|
46275
|
+
recording: (session, action) => run("recording", async (signal) => {
|
|
46276
|
+
const client = await forSession(session, signal);
|
|
46277
|
+
const key = session.leaseId;
|
|
46278
|
+
if (action === "start" && recordings.has(key)) throw new Error("This device session is already recording.");
|
|
46279
|
+
if (action === "stop" && !recordings.has(key)) throw new Error("This device session has no active recording.");
|
|
46280
|
+
const outputFile = recordings.get(key)?.outputFile ?? `${cwd}/device-recording-${randomUUID()}.mp4`;
|
|
46281
|
+
const result = xcodeResultData(await client.call("record_sim_video", {
|
|
46282
|
+
simulatorId: session.deviceId,
|
|
46283
|
+
...action === "start" ? { start: true } : {
|
|
46284
|
+
stop: true,
|
|
46285
|
+
outputFile
|
|
46286
|
+
}
|
|
46287
|
+
}, signal));
|
|
46288
|
+
if (action === "start") recordings.set(key, {
|
|
46289
|
+
outputFile,
|
|
46290
|
+
simulatorId: session.deviceId
|
|
46291
|
+
});
|
|
46292
|
+
else recordings.delete(key);
|
|
46293
|
+
return result;
|
|
46294
|
+
}),
|
|
46295
|
+
closeSession,
|
|
46296
|
+
stop: () => {
|
|
46297
|
+
for (const key of clients.keys()) closeSession(key);
|
|
46298
|
+
}
|
|
46299
|
+
};
|
|
46300
|
+
}
|
|
46301
|
+
//#endregion
|
|
46302
|
+
//#region src/device/AppleDeviceAdapter.ts
|
|
46303
|
+
const MAX_HIERARCHY_BYTES = 512 * 1024;
|
|
46304
|
+
const MAX_SESSIONS = 32;
|
|
46305
|
+
const REQUIRED_DEVICE_TOOLS = [
|
|
46306
|
+
"DeviceInteractionStartSession",
|
|
46307
|
+
"DeviceInteractionSynthesize",
|
|
46308
|
+
"DeviceInteractionEndSession"
|
|
46309
|
+
];
|
|
46310
|
+
const FRAME_PATTERN = /\{\{(-?[\d.]+),\s*(-?[\d.]+)\},\s*\{([\d.]+),\s*([\d.]+)\}\}/;
|
|
46311
|
+
const HIT_PATTERN = /hitPoint:\s*\{(-?[\d.]+),\s*(-?[\d.]+)\}/;
|
|
46312
|
+
const Capture = Schema$1.Struct({
|
|
46313
|
+
hierarchyPath: Schema$1.String,
|
|
46314
|
+
screenshotPath: Schema$1.String
|
|
46315
|
+
});
|
|
46316
|
+
const Session = Schema$1.Struct({
|
|
46317
|
+
interactionSessionKey: Schema$1.String,
|
|
46318
|
+
deviceUUID: Schema$1.String,
|
|
46319
|
+
deviceIsSimulator: Schema$1.Boolean
|
|
46320
|
+
});
|
|
46321
|
+
const Simulators = Schema$1.Struct({ devices: Schema$1.Record(Schema$1.String, Schema$1.Array(Schema$1.Struct({
|
|
46322
|
+
udid: Schema$1.String,
|
|
46323
|
+
name: Schema$1.String,
|
|
46324
|
+
state: Schema$1.String,
|
|
46325
|
+
isAvailable: Schema$1.Boolean
|
|
46326
|
+
}))) });
|
|
46327
|
+
function appleMcpData(result) {
|
|
46328
|
+
if (result.structuredContent) return objectValue(result.structuredContent);
|
|
46329
|
+
for (const block of Array.isArray(result.content) ? result.content : []) {
|
|
46330
|
+
const text = objectValue(block).text;
|
|
46331
|
+
if (typeof text !== "string") continue;
|
|
46332
|
+
try {
|
|
46333
|
+
return objectValue(JSON.parse(text));
|
|
46334
|
+
} catch {}
|
|
46335
|
+
}
|
|
46336
|
+
throw new Error("Apple MCP returned no structured device result.");
|
|
46337
|
+
}
|
|
46338
|
+
/** Use Apple's documented hierarchy hit points, never guessed screenshot coordinates. */
|
|
46339
|
+
function appleHierarchySnapshot(text, sequence) {
|
|
46340
|
+
const elements = text.split("\n").flatMap((line, index) => {
|
|
46341
|
+
const frame = line.match(FRAME_PATTERN);
|
|
46342
|
+
const hit = line.match(HIT_PATTERN);
|
|
46343
|
+
if (!frame || !hit || line.includes("isRemoteLeafPlaceholder")) return [];
|
|
46344
|
+
const [x, y, width, height] = frame.slice(1).map(Number);
|
|
46345
|
+
const [hx, hy] = hit.slice(1).map(Number);
|
|
46346
|
+
if (![
|
|
46347
|
+
x,
|
|
46348
|
+
y,
|
|
46349
|
+
width,
|
|
46350
|
+
height,
|
|
46351
|
+
hx,
|
|
46352
|
+
hy
|
|
46353
|
+
].every((value) => value !== void 0 && Number.isFinite(value)) || width <= 0 || height <= 0) return [];
|
|
46354
|
+
return [{
|
|
46355
|
+
ref: `a${sequence}-${index}`,
|
|
46356
|
+
label: line.trim(),
|
|
46357
|
+
frame: {
|
|
46358
|
+
x,
|
|
46359
|
+
y,
|
|
46360
|
+
width,
|
|
46361
|
+
height
|
|
46362
|
+
},
|
|
46363
|
+
hitPoint: {
|
|
46364
|
+
x: hx,
|
|
46365
|
+
y: hy
|
|
46366
|
+
},
|
|
46367
|
+
role: /\b(?:UI)?Window\b/.test(line) ? "window" : "element",
|
|
46368
|
+
actions: ["tap"]
|
|
46369
|
+
}];
|
|
46370
|
+
});
|
|
46371
|
+
return {
|
|
46372
|
+
screenHash: createHash("sha256").update(text).digest("hex"),
|
|
46373
|
+
seq: sequence,
|
|
46374
|
+
elements
|
|
46375
|
+
};
|
|
46376
|
+
}
|
|
46377
|
+
function createAppleDeviceAdapter(input) {
|
|
46378
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
46379
|
+
const fail = (operation) => (cause) => new DeviceOperationError({
|
|
46380
|
+
operation,
|
|
46381
|
+
reason: "request_failed",
|
|
46382
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
46383
|
+
});
|
|
46384
|
+
const call = (client, name, args) => Effect.tryPromise({
|
|
46385
|
+
try: (signal) => client.call(name, args, signal),
|
|
46386
|
+
catch: fail(name)
|
|
46387
|
+
});
|
|
46388
|
+
const closeSession = (leaseId) => {
|
|
46389
|
+
const pending = sessions.get(leaseId);
|
|
46390
|
+
sessions.delete(leaseId);
|
|
46391
|
+
pending?.then(async (owned) => {
|
|
46392
|
+
try {
|
|
46393
|
+
await owned.client.call("DeviceInteractionEndSession", { interactionSessionKey: owned.key }, new AbortController().signal);
|
|
46394
|
+
} finally {
|
|
46395
|
+
owned.client.close();
|
|
46396
|
+
}
|
|
46397
|
+
}).catch(() => {});
|
|
46398
|
+
};
|
|
46399
|
+
const get = (session) => Effect.tryPromise({
|
|
46400
|
+
try: async (signal) => {
|
|
46401
|
+
if (session.hostId !== "local" || session.platform !== "ios" || !session.leaseId) throw new Error("Apple MCP requires an exact local iOS device lease.");
|
|
46402
|
+
let pending = sessions.get(session.leaseId);
|
|
46403
|
+
if (!pending) {
|
|
46404
|
+
if (sessions.size >= MAX_SESSIONS) throw new Error("Close an existing device session first.");
|
|
46405
|
+
pending = (async () => {
|
|
46406
|
+
const client = await XcodeMcpClient.start("/usr/bin/xcrun", input.cwd, {
|
|
46407
|
+
...input.environment,
|
|
46408
|
+
MCP_XCODE_SESSION_ID: randomUUID()
|
|
46409
|
+
}, signal, "apple");
|
|
46410
|
+
try {
|
|
46411
|
+
const missing = REQUIRED_DEVICE_TOOLS.filter((tool) => !client.tools.has(tool));
|
|
46412
|
+
if (missing.length > 0) throw new Error(`Selected Xcode lacks required device tools: ${missing.join(", ")}. No device session was opened.`);
|
|
46413
|
+
if (input.workspacePath) await client.call("XcodeOpenWorkspace", { path: input.workspacePath }, signal);
|
|
46414
|
+
const opened = Schema$1.decodeUnknownSync(Session)(appleMcpData(await client.call("DeviceInteractionStartSession", {
|
|
46415
|
+
deviceIdentifier: session.deviceId,
|
|
46416
|
+
sessionIdentifier: `P4 Device ${session.threadId.slice(0, 8)}`
|
|
46417
|
+
}, signal)));
|
|
46418
|
+
if (opened.deviceUUID.toLowerCase() !== session.deviceId.toLowerCase() || !opened.deviceIsSimulator) {
|
|
46419
|
+
await client.call("DeviceInteractionEndSession", { interactionSessionKey: opened.interactionSessionKey }, signal);
|
|
46420
|
+
throw new Error("Apple selected a different device. No interaction was performed.");
|
|
46421
|
+
}
|
|
46422
|
+
return {
|
|
46423
|
+
client,
|
|
46424
|
+
key: opened.interactionSessionKey,
|
|
46425
|
+
sequence: 0
|
|
46426
|
+
};
|
|
46427
|
+
} catch (error) {
|
|
46428
|
+
client.close();
|
|
46429
|
+
throw error;
|
|
46430
|
+
}
|
|
46431
|
+
})();
|
|
46432
|
+
sessions.set(session.leaseId, pending);
|
|
46433
|
+
const leaseId = session.leaseId;
|
|
46434
|
+
pending.catch(() => {
|
|
46435
|
+
if (sessions.get(leaseId) === pending) sessions.delete(leaseId);
|
|
46436
|
+
});
|
|
46437
|
+
}
|
|
46438
|
+
return pending;
|
|
46439
|
+
},
|
|
46440
|
+
catch: fail("open")
|
|
46441
|
+
});
|
|
46442
|
+
const capture = (owned, interactionCommand) => Effect.gen(function* () {
|
|
46443
|
+
const response = yield* call(owned.client, "DeviceInteractionSynthesize", {
|
|
46444
|
+
interactSessionKey: owned.key,
|
|
46445
|
+
...interactionCommand ? { interactionCommand } : {}
|
|
46446
|
+
});
|
|
46447
|
+
const data = yield* Effect.try({
|
|
46448
|
+
try: () => appleMcpData(response),
|
|
46449
|
+
catch: fail("state")
|
|
46450
|
+
});
|
|
46451
|
+
const result = yield* Schema$1.decodeUnknownEffect(Capture)(data);
|
|
46452
|
+
if ((yield* input.fs.stat(result.hierarchyPath)).size > BigInt(MAX_HIERARCHY_BYTES)) return yield* fail("state")("Apple hierarchy exceeds the supported size.");
|
|
46453
|
+
const snapshot = appleHierarchySnapshot(yield* input.fs.readFileString(result.hierarchyPath), owned.sequence + 1);
|
|
46454
|
+
if (owned.snapshot?.screenHash !== snapshot.screenHash) {
|
|
46455
|
+
owned.snapshot = snapshot;
|
|
46456
|
+
owned.sequence += 1;
|
|
46457
|
+
}
|
|
46458
|
+
const currentSnapshot = owned.snapshot;
|
|
46459
|
+
owned.image = result.screenshotPath;
|
|
46460
|
+
const viewport = currentSnapshot.elements.find((element) => element.role === "window")?.frame;
|
|
46461
|
+
return {
|
|
46462
|
+
adapter: "apple-xcode",
|
|
46463
|
+
capabilities: [
|
|
46464
|
+
"snapshot_ui",
|
|
46465
|
+
"tap",
|
|
46466
|
+
"screenshot"
|
|
46467
|
+
],
|
|
46468
|
+
capture: currentSnapshot,
|
|
46469
|
+
...viewport ? { viewport } : {}
|
|
46470
|
+
};
|
|
46471
|
+
}).pipe(Effect.mapError(fail("state")));
|
|
46472
|
+
return {
|
|
46473
|
+
diagnose: Effect.gen(function* () {
|
|
46474
|
+
const result = yield* input.run("xcrun", [
|
|
46475
|
+
"mcp-server",
|
|
46476
|
+
"status",
|
|
46477
|
+
"--format",
|
|
46478
|
+
"json"
|
|
46479
|
+
]);
|
|
46480
|
+
if (result.code !== 0) return "Apple MCP did not respond. Check the selected Xcode installation and MCP service.";
|
|
46481
|
+
const status = objectValue(yield* Schema$1.decodeUnknownEffect(Schema$1.UnknownFromJsonString)(result.stdout));
|
|
46482
|
+
const permission = objectValue(status.permission);
|
|
46483
|
+
if ((Array.isArray(permission.pendingAgentApprovals) ? permission.pendingAgentApprovals : []).some((item) => objectValue(objectValue(objectValue(objectValue(item).subject).agent)._0).name === "p4code-device-adapter")) return "Apple Xcode is waiting for approval of p4code-device-adapter and its project folder. Approve the pending request in Xcode, then select the device again. P4 cannot grant this approval automatically.";
|
|
46484
|
+
if (status.running === false) return "Apple MCP service is not running. Open the selected Xcode project or configure its headless service, then select the device again.";
|
|
46485
|
+
return "Apple MCP did not respond in time. Check Xcode agent/folder permissions and session logs before selecting the device again.";
|
|
46486
|
+
}).pipe(Effect.orElseSucceed(() => "Apple MCP did not respond. Check Xcode agent/folder permissions and service status.")),
|
|
46487
|
+
discover: Effect.gen(function* () {
|
|
46488
|
+
const result = yield* input.run("xcrun", [
|
|
46489
|
+
"simctl",
|
|
46490
|
+
"list",
|
|
46491
|
+
"devices",
|
|
46492
|
+
"available",
|
|
46493
|
+
"--json"
|
|
46494
|
+
]);
|
|
46495
|
+
if (result.code !== 0) return yield* fail("list")("Selected Xcode cannot list simulators.");
|
|
46496
|
+
const parsed = yield* Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(Simulators))(result.stdout).pipe(Effect.mapError(fail("list")));
|
|
46497
|
+
return Object.entries(parsed.devices).filter(([runtime]) => runtime.includes(".iOS-")).flatMap(([runtime, devices]) => devices.filter((device) => device.isAvailable).map((device) => ({
|
|
46498
|
+
hostId: "local",
|
|
46499
|
+
id: device.udid,
|
|
46500
|
+
platform: "ios",
|
|
46501
|
+
name: device.name,
|
|
46502
|
+
version: runtime.replace("com.apple.CoreSimulator.SimRuntime.", "").replaceAll("-", "."),
|
|
46503
|
+
physical: false,
|
|
46504
|
+
booted: device.state === "Booted"
|
|
46505
|
+
})));
|
|
46506
|
+
}),
|
|
46507
|
+
state: (session, sinceScreenHash) => Effect.gen(function* () {
|
|
46508
|
+
const result = yield* capture(yield* get(session));
|
|
46509
|
+
return sinceScreenHash === result.capture.screenHash ? {
|
|
46510
|
+
...result,
|
|
46511
|
+
capture: {
|
|
46512
|
+
screenHash: result.capture.screenHash,
|
|
46513
|
+
seq: result.capture.seq,
|
|
46514
|
+
unchanged: true
|
|
46515
|
+
}
|
|
46516
|
+
} : result;
|
|
46517
|
+
}),
|
|
46518
|
+
action: (session, action) => Effect.gen(function* () {
|
|
46519
|
+
if (action.action !== "tap") return yield* fail("command")("This Apple MCP integration currently advertises tap only. Use only capabilities returned by getAXState().");
|
|
46520
|
+
const owned = yield* get(session);
|
|
46521
|
+
const previous = owned.snapshot;
|
|
46522
|
+
const element = previous?.elements.find((element) => element.ref === action.elementRef);
|
|
46523
|
+
if (!element) return yield* fail("command")("Stale device ref. Read getAXState() and choose a current element.");
|
|
46524
|
+
const fresh = yield* capture(owned);
|
|
46525
|
+
if (fresh.capture.screenHash !== previous.screenHash) return yield* fail("command")("Device layout changed. Read getAXState() and choose a current element; no action was performed.");
|
|
46526
|
+
return {
|
|
46527
|
+
...yield* capture(owned, `t ${element.hitPoint.x} ${element.hitPoint.y}`),
|
|
46528
|
+
...fresh.viewport ? { pointer: {
|
|
46529
|
+
x: (element.hitPoint.x - fresh.viewport.x) / fresh.viewport.width,
|
|
46530
|
+
y: (element.hitPoint.y - fresh.viewport.y) / fresh.viewport.height
|
|
46531
|
+
} } : {}
|
|
46532
|
+
};
|
|
46533
|
+
}),
|
|
46534
|
+
screenshot: (session) => Effect.gen(function* () {
|
|
46535
|
+
const owned = yield* get(session);
|
|
46536
|
+
yield* capture(owned);
|
|
46537
|
+
if ((yield* input.fs.stat(owned.image)).size > BigInt(8388608)) return yield* fail("screenshot")("Apple screenshot exceeds the supported size.");
|
|
46538
|
+
const png = Buffer.from(yield* input.fs.readFile(owned.image));
|
|
46539
|
+
return {
|
|
46540
|
+
png,
|
|
46541
|
+
...yield* Effect.try({
|
|
46542
|
+
try: () => pngDimensions(png),
|
|
46543
|
+
catch: fail("screenshot")
|
|
46544
|
+
})
|
|
46545
|
+
};
|
|
46546
|
+
}).pipe(Effect.mapError(fail("screenshot"))),
|
|
46547
|
+
recording: (_session, _action) => Effect.fail(fail("recording")("Apple MCP does not advertise a recording tool. Recording is unavailable through this adapter.")),
|
|
46548
|
+
closeSession,
|
|
46549
|
+
stop: () => {
|
|
46550
|
+
for (const key of sessions.keys()) closeSession(key);
|
|
46551
|
+
}
|
|
46552
|
+
};
|
|
46553
|
+
}
|
|
46554
|
+
//#endregion
|
|
45747
46555
|
//#region src/device/DeviceToolchain.ts
|
|
45748
46556
|
/**
|
|
45749
46557
|
* Pinned installs of the two external tools device support is built on.
|
|
@@ -45959,7 +46767,8 @@ function deviceHubPlatform(platforms) {
|
|
|
45959
46767
|
const COMMAND_TIMEOUT_MS$1 = 2e4;
|
|
45960
46768
|
const COMMAND_OUTPUT_LIMIT = 1024 * 1024;
|
|
45961
46769
|
const COMMAND_FAILURE_CODE = 127;
|
|
45962
|
-
const
|
|
46770
|
+
const APPLE_DEVICE_MCP_MINIMUM_XCODE = 27;
|
|
46771
|
+
const make$81 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
45963
46772
|
const config = yield* ServerConfig$1;
|
|
45964
46773
|
const fs = yield* FileSystem.FileSystem;
|
|
45965
46774
|
const path = yield* Path$1.Path;
|
|
@@ -46005,7 +46814,19 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46005
46814
|
stderr: "Device command failed.",
|
|
46006
46815
|
code: COMMAND_FAILURE_CODE
|
|
46007
46816
|
})));
|
|
46817
|
+
const appleVersion = (platform === "darwin" ? yield* run("xcrun", ["--find", "mcpbridge"]) : null)?.code === 0 ? yield* run("xcodebuild", ["-version"]) : null;
|
|
46818
|
+
const appleAvailable = appleVersion?.code === 0 && Number(appleVersion.stdout.match(/^Xcode (\d+)\b/m)?.[1]) >= APPLE_DEVICE_MCP_MINIMUM_XCODE;
|
|
46819
|
+
const legacyPath = environment.P4CODE_XCODEBUILDMCP_PATH?.trim();
|
|
46820
|
+
const useLegacy = environment.P4CODE_DEVICE_ADAPTER === "xcodebuildmcp";
|
|
46821
|
+
const xcode = appleAvailable && !useLegacy ? createAppleDeviceAdapter({
|
|
46822
|
+
cwd: config.stateDir,
|
|
46823
|
+
environment: hostEnvironment,
|
|
46824
|
+
fs,
|
|
46825
|
+
run,
|
|
46826
|
+
...environment.P4CODE_APPLE_MCP_WORKSPACE ? { workspacePath: environment.P4CODE_APPLE_MCP_WORKSPACE } : {}
|
|
46827
|
+
}) : platform === "darwin" && legacyPath ? createXcodeDeviceAdapter(legacyPath, config.stateDir, hostEnvironment) : void 0;
|
|
46008
46828
|
const stopAgent = Effect.suspend(() => {
|
|
46829
|
+
xcode?.stop();
|
|
46009
46830
|
const owned = agent;
|
|
46010
46831
|
agent = null;
|
|
46011
46832
|
return owned?.stop ?? Effect.void;
|
|
@@ -46044,7 +46865,8 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46044
46865
|
nodePath: process.execPath,
|
|
46045
46866
|
hub: { origin: started.origin },
|
|
46046
46867
|
helpers,
|
|
46047
|
-
run
|
|
46868
|
+
run,
|
|
46869
|
+
...xcode ? { xcode } : {}
|
|
46048
46870
|
};
|
|
46049
46871
|
return ready;
|
|
46050
46872
|
}, Effect.onExit((exit) => Exit.isFailure(exit) ? stop : Effect.void));
|
|
@@ -46055,6 +46877,10 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46055
46877
|
id: hostId,
|
|
46056
46878
|
kind: "local",
|
|
46057
46879
|
label: "This machine",
|
|
46880
|
+
...xcode ? { automation: {
|
|
46881
|
+
provider: appleAvailable && !useLegacy ? "apple-xcode" : "xcodebuildmcp",
|
|
46882
|
+
detail: appleAvailable && !useLegacy ? "Apple Xcode MCP. Agent and folder approval is required; P4 never grants it automatically." : "Optional XcodeBuildMCP 2.7.0 executable. No automatic installation."
|
|
46883
|
+
} } : {},
|
|
46058
46884
|
platforms: yield* Effect.all([platformAvailability("ios"), platformAvailability("android")]),
|
|
46059
46885
|
hubInstalled: yield* isDeviceHubInstalled(config.baseDir).pipe(Effect.provideContext(context)),
|
|
46060
46886
|
agentDeviceInstalled: yield* isAgentDeviceInstalled(config.baseDir).pipe(Effect.provideContext(context))
|
|
@@ -46066,8 +46892,9 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46066
46892
|
return ready;
|
|
46067
46893
|
})),
|
|
46068
46894
|
ensureReady: (onPhase) => lock.withPermit(ensureHub(onPhase)),
|
|
46069
|
-
ensureAgentReady: (onPhase) => lock.withPermit(Effect.gen(function* () {
|
|
46895
|
+
ensureAgentReady: (onPhase, devicePlatform) => lock.withPermit(Effect.gen(function* () {
|
|
46070
46896
|
const host = yield* ensureHub(onPhase);
|
|
46897
|
+
if (xcode && devicePlatform === "ios") return host;
|
|
46071
46898
|
if (agent && (yield* agent.isRunning)) return {
|
|
46072
46899
|
...host,
|
|
46073
46900
|
agentDevice: agent.endpoint
|
|
@@ -46094,7 +46921,7 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46094
46921
|
stop: lock.withPermit(stop)
|
|
46095
46922
|
};
|
|
46096
46923
|
});
|
|
46097
|
-
Layer.effect(DeviceHost, make$
|
|
46924
|
+
Layer.effect(DeviceHost, make$81()).pipe(Layer.provide(layer$73));
|
|
46098
46925
|
Data.TaggedError("SshHostDiscoveryError");
|
|
46099
46926
|
var SshInvalidTargetError = class extends Data.TaggedError("SshInvalidTargetError") {};
|
|
46100
46927
|
var SshCommandError = class extends Data.TaggedError("SshCommandError") {};
|
|
@@ -46953,7 +47780,7 @@ const probeSshDeviceHost = Effect.fn("SshDeviceDiagnostics.probe")(function* (co
|
|
|
46953
47780
|
//#endregion
|
|
46954
47781
|
//#region src/device/SshDeviceHost.ts
|
|
46955
47782
|
const DIAGNOSTICS_CACHE_MS = 1e4;
|
|
46956
|
-
const make$
|
|
47783
|
+
const make$80 = Effect.fn("SshDeviceHost.make")(function* (input) {
|
|
46957
47784
|
const config = yield* Schema$1.decodeUnknownEffect(SshDeviceHostConfig)({ ...input }).pipe(Effect.mapError(() => new DeviceHostError({
|
|
46958
47785
|
hostId: input.id,
|
|
46959
47786
|
step: "configuring SSH device host",
|
|
@@ -47095,6 +47922,14 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47095
47922
|
const scope = yield* Scope.Scope;
|
|
47096
47923
|
const registry = yield* make$83(input);
|
|
47097
47924
|
const commands = yield* make$85();
|
|
47925
|
+
const adapterLeases = /* @__PURE__ */ new Map();
|
|
47926
|
+
const closeCommands = (sessions) => Effect.sync(() => {
|
|
47927
|
+
for (const session of sessions) {
|
|
47928
|
+
if (!session.leaseId) continue;
|
|
47929
|
+
adapterLeases.get(session.leaseId)?.();
|
|
47930
|
+
adapterLeases.delete(session.leaseId);
|
|
47931
|
+
}
|
|
47932
|
+
}).pipe(Effect.andThen(commands.close(sessions)));
|
|
47098
47933
|
const threads = yield* make$84();
|
|
47099
47934
|
const changes = yield* Effect.acquireRelease(PubSub.sliding(STATE_BUFFER_SIZE), PubSub.shutdown);
|
|
47100
47935
|
const stateLock = yield* Semaphore.make(1);
|
|
@@ -47132,7 +47967,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47132
47967
|
}));
|
|
47133
47968
|
}
|
|
47134
47969
|
const retireSessions = (ids) => Effect.gen(function* () {
|
|
47135
|
-
yield*
|
|
47970
|
+
yield* closeCommands(state.sessions.filter((session) => ids.includes(session.hostId)));
|
|
47136
47971
|
yield* update((current) => {
|
|
47137
47972
|
for (const id of ids) hostLocks.delete(id);
|
|
47138
47973
|
return {
|
|
@@ -47343,7 +48178,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47343
48178
|
return yield* withHost(value.hostId, (lease) => Effect.gen(function* () {
|
|
47344
48179
|
let found = yield* freshDevice(lease, value);
|
|
47345
48180
|
if (access === "agent") {
|
|
47346
|
-
yield* lease.host.ensureAgentReady(() => Effect.void);
|
|
48181
|
+
yield* lease.host.ensureAgentReady(() => Effect.void, value.platform);
|
|
47347
48182
|
yield* registry.commit(lease, () => void 0);
|
|
47348
48183
|
}
|
|
47349
48184
|
if (!found.device.booted && value.boot !== false) {
|
|
@@ -47382,7 +48217,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47382
48217
|
openedAt,
|
|
47383
48218
|
leaseId: NodeCrypto.randomUUID()
|
|
47384
48219
|
};
|
|
47385
|
-
yield*
|
|
48220
|
+
yield* closeCommands(sessionsToClose(state.sessions, session));
|
|
47386
48221
|
yield* update((current) => ({
|
|
47387
48222
|
...current,
|
|
47388
48223
|
sessions: [...current.sessions.filter((item) => !(item.threadId === session.threadId && item.hostId === session.hostId && item.deviceId === session.deviceId)), session]
|
|
@@ -47393,7 +48228,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47393
48228
|
const shutdownWithin = Effect.fn("DeviceService.shutdownWithin")(function* (lease, value) {
|
|
47394
48229
|
const { ready, device } = yield* freshDevice(lease, value);
|
|
47395
48230
|
yield* input.actions.shutdown(ready, device);
|
|
47396
|
-
yield*
|
|
48231
|
+
yield* closeCommands(state.sessions.filter((session) => session.hostId === value.hostId && session.deviceId === value.deviceId));
|
|
47397
48232
|
yield* update((current) => ({
|
|
47398
48233
|
...current,
|
|
47399
48234
|
devices: current.devices.map((item) => item.hostId === value.hostId && item.id === value.deviceId ? {
|
|
@@ -47417,7 +48252,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47417
48252
|
platform: session.platform
|
|
47418
48253
|
});
|
|
47419
48254
|
else {
|
|
47420
|
-
yield*
|
|
48255
|
+
yield* closeCommands(sessionsToClose(state.sessions, value));
|
|
47421
48256
|
yield* update((current) => {
|
|
47422
48257
|
const closing = sessionsToClose(current.sessions, value);
|
|
47423
48258
|
return {
|
|
@@ -47460,11 +48295,16 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47460
48295
|
});
|
|
47461
48296
|
const validate = registry.isCurrent(lease).pipe(Effect.map((current) => current && state.sessions.includes(session)));
|
|
47462
48297
|
return yield* commands.run(session, validate, Effect.gen(function* () {
|
|
47463
|
-
const ready = yield* lease.host.ensureAgentReady(() => Effect.void);
|
|
48298
|
+
const ready = yield* lease.host.ensureAgentReady(() => Effect.void, session.platform);
|
|
47464
48299
|
if (!(yield* validate)) return yield* new DeviceHostUnavailableError({
|
|
47465
48300
|
hostId: target.hostId,
|
|
47466
48301
|
reason: "Device session or agent authorization changed."
|
|
47467
48302
|
});
|
|
48303
|
+
if (ready.xcode && session.leaseId) {
|
|
48304
|
+
const adapter = ready.xcode;
|
|
48305
|
+
const leaseId = session.leaseId;
|
|
48306
|
+
adapterLeases.set(leaseId, () => adapter.closeSession(leaseId));
|
|
48307
|
+
}
|
|
47468
48308
|
return yield* execute(ready, session);
|
|
47469
48309
|
}));
|
|
47470
48310
|
})).pipe(Effect.mapError((error) => isDeviceHostError(error) || isDeviceHostTimeoutError(error) ? new DeviceHostUnavailableError({
|
|
@@ -47478,7 +48318,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47478
48318
|
agentList: listForAccess("agent"),
|
|
47479
48319
|
configure,
|
|
47480
48320
|
shutdown,
|
|
47481
|
-
retireThread: (threadId) => threads.retire(threadId).pipe(Effect.ensuring(Effect.suspend(() =>
|
|
48321
|
+
retireThread: (threadId) => threads.retire(threadId).pipe(Effect.ensuring(Effect.suspend(() => closeCommands(state.sessions.filter((session) => session.threadId === threadId))).pipe(Effect.ensuring(update((current) => ({
|
|
47482
48322
|
...current,
|
|
47483
48323
|
sessions: current.sessions.filter((session) => session.threadId !== threadId),
|
|
47484
48324
|
bootingDevices: (current.bootingDevices ?? []).filter((device) => device.threadId !== threadId)
|
|
@@ -47486,7 +48326,10 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47486
48326
|
restoreThread: threads.restore,
|
|
47487
48327
|
withAgentDevice: (target, execute) => threads.run(target.threadId, withAgentDevice(target, execute)),
|
|
47488
48328
|
withManualDevice: (target, execute) => threads.run(target.threadId, withManualDevice(target, execute)),
|
|
47489
|
-
withAgentCommand: (target, execute) => threads.run(target.threadId, withAgentDevice(target, (ready, session) => execute(ready.agentDevice, session)
|
|
48329
|
+
withAgentCommand: (target, execute) => threads.run(target.threadId, withAgentDevice(target, (ready, session) => ready.agentDevice ? execute(ready.agentDevice, session) : Effect.fail(new DeviceHostUnavailableError({
|
|
48330
|
+
hostId: target.hostId,
|
|
48331
|
+
reason: "This host uses semantic device control. Use cua.getDevice() and element refs."
|
|
48332
|
+
})))),
|
|
47490
48333
|
open: (value) => threads.run(value.threadId, openWithAccess(value, "manual")),
|
|
47491
48334
|
agentOpen: (value) => threads.run(value.threadId, openWithAccess(value, "agent")),
|
|
47492
48335
|
close: (value) => closeWithAccess(value, "manual"),
|
|
@@ -47498,17 +48341,17 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47498
48341
|
}))
|
|
47499
48342
|
};
|
|
47500
48343
|
});
|
|
47501
|
-
const make$
|
|
48344
|
+
const make$79 = Effect.gen(function* () {
|
|
47502
48345
|
const discover = yield* make$87();
|
|
47503
48346
|
const actions = yield* make$86();
|
|
47504
48347
|
return yield* makeWithHosts({
|
|
47505
|
-
local: make$
|
|
47506
|
-
ssh: make$
|
|
48348
|
+
local: make$81(),
|
|
48349
|
+
ssh: make$80,
|
|
47507
48350
|
discover,
|
|
47508
48351
|
actions
|
|
47509
48352
|
});
|
|
47510
48353
|
}).pipe(Effect.provide(layer$73));
|
|
47511
|
-
const layer$
|
|
48354
|
+
const layer$70 = Layer.effect(DeviceService, make$79);
|
|
47512
48355
|
//#endregion
|
|
47513
48356
|
//#region src/serverRuntimeStartup.ts
|
|
47514
48357
|
var ServerRuntimeStartupError = class extends Schema$1.TaggedErrorClass()("ServerRuntimeStartupError", {
|
|
@@ -47653,7 +48496,7 @@ const startProviderRecovery = Effect.fn("startup.startProviderRecovery")(functio
|
|
|
47653
48496
|
yield* input.recover;
|
|
47654
48497
|
yield* input.startReaper;
|
|
47655
48498
|
});
|
|
47656
|
-
const make$
|
|
48499
|
+
const make$78 = Effect.gen(function* () {
|
|
47657
48500
|
const serverConfig = yield* ServerConfig$1;
|
|
47658
48501
|
const keybindings = yield* Keybindings;
|
|
47659
48502
|
const orchestrationReactor = yield* OrchestrationReactor;
|
|
@@ -47800,7 +48643,7 @@ const make$79 = Effect.gen(function* () {
|
|
|
47800
48643
|
enqueueCommand: commandGate.enqueueCommand
|
|
47801
48644
|
};
|
|
47802
48645
|
});
|
|
47803
|
-
const layer$
|
|
48646
|
+
const layer$69 = Layer.effect(ServerRuntimeStartup, make$78);
|
|
47804
48647
|
//#endregion
|
|
47805
48648
|
//#region src/serverRuntimeState.ts
|
|
47806
48649
|
const PersistedServerRuntimeState = Schema$1.Struct({
|
|
@@ -47957,7 +48800,7 @@ function expandHomePath$2(input, path) {
|
|
|
47957
48800
|
if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
|
|
47958
48801
|
return input;
|
|
47959
48802
|
}
|
|
47960
|
-
const make$
|
|
48803
|
+
const make$77 = Effect.gen(function* () {
|
|
47961
48804
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
47962
48805
|
const path = yield* Path$1.Path;
|
|
47963
48806
|
const statWorkspaceRoot = Effect.fn("WorkspacePaths.statWorkspaceRoot")(function* (workspaceRoot, normalizedWorkspaceRoot, phase) {
|
|
@@ -48014,7 +48857,7 @@ const make$78 = Effect.gen(function* () {
|
|
|
48014
48857
|
resolveRelativePathWithinRoot
|
|
48015
48858
|
});
|
|
48016
48859
|
});
|
|
48017
|
-
const layer$
|
|
48860
|
+
const layer$68 = Layer.effect(WorkspacePaths, make$77);
|
|
48018
48861
|
//#endregion
|
|
48019
48862
|
//#region src/cli/project.ts
|
|
48020
48863
|
const isEnvironmentHttpCommonError = Schema$1.is(EnvironmentHttpCommonError);
|
|
@@ -48120,7 +48963,7 @@ const projectCommandUuid = Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.
|
|
|
48120
48963
|
operation: "generateProjectCommandId",
|
|
48121
48964
|
cause
|
|
48122
48965
|
})));
|
|
48123
|
-
const ProjectCliRuntimeLive = Layer.mergeAll(layer$
|
|
48966
|
+
const ProjectCliRuntimeLive = Layer.mergeAll(layer$68, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$81), Layer.provideMerge(layerConfig)));
|
|
48124
48967
|
const PROJECT_CLI_LIVE_SERVER_TIMEOUT = Duration.seconds(1);
|
|
48125
48968
|
const withProjectCliSessionToken = (environmentAuth, run) => Effect.acquireUseRelease(environmentAuth.issueSession({
|
|
48126
48969
|
scopes: AuthAdministrativeScopes,
|
|
@@ -48222,7 +49065,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
48222
49065
|
});
|
|
48223
49066
|
yield* Console.log(output);
|
|
48224
49067
|
}).pipe(Effect.provide(offlineRuntimeLayer));
|
|
48225
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$
|
|
49068
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$68).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$118(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
48226
49069
|
});
|
|
48227
49070
|
const projectAddCommand = Command.make("add", {
|
|
48228
49071
|
...projectLocationFlags,
|
|
@@ -51938,7 +52781,7 @@ const makeTerminationError$1 = (handle) => Effect.match(handle.exitCode, {
|
|
|
51938
52781
|
//#region ../../packages/effect-acp/src/client.ts
|
|
51939
52782
|
const MAX_STDERR_CALLBACK_CHARS = 32768;
|
|
51940
52783
|
var AcpClient = class extends Context.Service()("effect-acp/client/AcpClient") {};
|
|
51941
|
-
const make$
|
|
52784
|
+
const make$76 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options = {}, terminationError) {
|
|
51942
52785
|
const coreHandlers = {};
|
|
51943
52786
|
const notificationHandlers = {
|
|
51944
52787
|
sessionUpdate: {
|
|
@@ -52105,7 +52948,7 @@ const layerChildProcess$1 = (handle, options = {}) => {
|
|
|
52105
52948
|
})), Stream.drain)) : stdout
|
|
52106
52949
|
};
|
|
52107
52950
|
const terminationError = makeTerminationError$1(handle);
|
|
52108
|
-
return Layer.effect(AcpClient, make$
|
|
52951
|
+
return Layer.effect(AcpClient, make$76(stdio, options, terminationError));
|
|
52109
52952
|
};
|
|
52110
52953
|
//#endregion
|
|
52111
52954
|
//#region src/provider/Drivers/AntigravitySkills.ts
|
|
@@ -53384,7 +54227,7 @@ const Reply = Schema$1.Struct({
|
|
|
53384
54227
|
id: Schema$1.String,
|
|
53385
54228
|
result: Schema$1.Struct({ ok: Schema$1.Boolean })
|
|
53386
54229
|
});
|
|
53387
|
-
const make$
|
|
54230
|
+
const make$75 = Effect.fn("DeviceCommandProxy.make")(function* () {
|
|
53388
54231
|
const http = yield* HttpClient.HttpClient;
|
|
53389
54232
|
const daemonSessions = /* @__PURE__ */ new WeakMap();
|
|
53390
54233
|
return Effect.fn("DeviceCommandProxy.execute")(function* (endpoint, session, payload) {
|
|
@@ -53437,81 +54280,7 @@ const make$76 = Effect.fn("DeviceCommandProxy.make")(function* () {
|
|
|
53437
54280
|
})));
|
|
53438
54281
|
});
|
|
53439
54282
|
var DeviceCommandProxy = class extends Context.Service()("@p4code/cli/device/DeviceCommandProxy") {};
|
|
53440
|
-
const layer$
|
|
53441
|
-
//#endregion
|
|
53442
|
-
//#region src/device/DeviceScreenshot.ts
|
|
53443
|
-
const MAX_SCREENSHOT_BYTES = 8 * 1024 * 1024;
|
|
53444
|
-
var DeviceScreenshotFailure = class extends Data.TaggedError("DeviceScreenshotFailure") {};
|
|
53445
|
-
const MAX_DIMENSION = 16384;
|
|
53446
|
-
const MAX_PIXELS = 64 * 1024 * 1024;
|
|
53447
|
-
const SCREENSHOT_TIMEOUT_MS = 2e4;
|
|
53448
|
-
const PNG_HEADER_BYTES = 33;
|
|
53449
|
-
const PNG_CHUNK_OVERHEAD = 12;
|
|
53450
|
-
const PNG_SIGNATURE = Buffer.from([
|
|
53451
|
-
137,
|
|
53452
|
-
80,
|
|
53453
|
-
78,
|
|
53454
|
-
71,
|
|
53455
|
-
13,
|
|
53456
|
-
10,
|
|
53457
|
-
26,
|
|
53458
|
-
10
|
|
53459
|
-
]);
|
|
53460
|
-
function pngDimensions(png) {
|
|
53461
|
-
const buffer = Buffer.from(png.buffer, png.byteOffset, png.byteLength);
|
|
53462
|
-
if (buffer.length < PNG_HEADER_BYTES || buffer.length > 8388608 || !buffer.subarray(0, PNG_SIGNATURE.length).equals(PNG_SIGNATURE) || buffer.readUInt32BE(8) !== 13 || buffer.toString("ascii", 12, 16) !== "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG screenshot." });
|
|
53463
|
-
const width = buffer.readUInt32BE(16);
|
|
53464
|
-
const height = buffer.readUInt32BE(20);
|
|
53465
|
-
if (!width || !height || width > MAX_DIMENSION || height > MAX_DIMENSION || width * height > MAX_PIXELS) throw new DeviceScreenshotFailure({ message: "Screenshot dimensions exceed limits." });
|
|
53466
|
-
let offset = PNG_HEADER_BYTES;
|
|
53467
|
-
let imageData = false;
|
|
53468
|
-
let ended = false;
|
|
53469
|
-
while (offset + PNG_CHUNK_OVERHEAD <= buffer.length) {
|
|
53470
|
-
const length = buffer.readUInt32BE(offset);
|
|
53471
|
-
const type = buffer.toString("ascii", offset + 4, offset + 8);
|
|
53472
|
-
const next = offset + PNG_CHUNK_OVERHEAD + length;
|
|
53473
|
-
if (next > buffer.length || type === "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG chunk." });
|
|
53474
|
-
if (type === "IDAT") imageData = true;
|
|
53475
|
-
if (type === "IEND") {
|
|
53476
|
-
ended = length === 0 && next === buffer.length;
|
|
53477
|
-
break;
|
|
53478
|
-
}
|
|
53479
|
-
offset = next;
|
|
53480
|
-
}
|
|
53481
|
-
if (!imageData || !ended) throw new DeviceScreenshotFailure({ message: "Incomplete PNG screenshot." });
|
|
53482
|
-
return {
|
|
53483
|
-
width,
|
|
53484
|
-
height
|
|
53485
|
-
};
|
|
53486
|
-
}
|
|
53487
|
-
const make$75 = Effect.fn("DeviceScreenshot.make")(function* () {
|
|
53488
|
-
const http = yield* HttpClient.HttpClient;
|
|
53489
|
-
return Effect.fn("DeviceScreenshot.capture")(function* (ready, session) {
|
|
53490
|
-
const origin = yield* Effect.try(() => new URL(ready.hub.origin));
|
|
53491
|
-
if (origin.protocol !== "http:" || origin.hostname !== "127.0.0.1" || !origin.port || origin.username || origin.password || origin.pathname !== "/" || origin.search || origin.hash) return yield* new DeviceScreenshotFailure({ message: "Invalid owned hub." });
|
|
53492
|
-
const vendor = session.platform === "ios" ? "serve-sim" : "serve-emu";
|
|
53493
|
-
const response = yield* http.execute(HttpClientRequest.post(`${origin.origin}/vendor/${vendor}/api/screenshot?device=${encodeURIComponent(session.deviceId)}`));
|
|
53494
|
-
if (response.status !== 200) return yield* new DeviceScreenshotFailure({ message: "Screenshot rejected." });
|
|
53495
|
-
let bytes = 0;
|
|
53496
|
-
const chunks = [];
|
|
53497
|
-
yield* response.stream.pipe(Stream.runForEach((chunk) => Effect.gen(function* () {
|
|
53498
|
-
bytes += chunk.byteLength;
|
|
53499
|
-
if (bytes > 8388608) return yield* new DeviceScreenshotFailure({ message: "Screenshot too large." });
|
|
53500
|
-
chunks.push(chunk);
|
|
53501
|
-
})));
|
|
53502
|
-
const png = Buffer.concat(chunks, bytes);
|
|
53503
|
-
return {
|
|
53504
|
-
png,
|
|
53505
|
-
...yield* Effect.try(() => pngDimensions(png))
|
|
53506
|
-
};
|
|
53507
|
-
}, Effect.scoped, Effect.timeout(Duration.millis(SCREENSHOT_TIMEOUT_MS)), Effect.mapError(() => new DeviceOperationError({
|
|
53508
|
-
operation: "screenshot",
|
|
53509
|
-
reason: "request_failed",
|
|
53510
|
-
cause: "Device screenshot could not be captured."
|
|
53511
|
-
})));
|
|
53512
|
-
});
|
|
53513
|
-
var DeviceScreenshot = class extends Context.Service()("@p4code/cli/device/DeviceScreenshot") {};
|
|
53514
|
-
const layer$67 = Layer.effect(DeviceScreenshot, make$75().pipe(Effect.map((capture) => ({ capture }))));
|
|
54283
|
+
const layer$67 = Layer.effect(DeviceCommandProxy, make$75().pipe(Effect.map((execute) => ({ execute }))));
|
|
53515
54284
|
//#endregion
|
|
53516
54285
|
//#region src/device/DeviceHubRoute.ts
|
|
53517
54286
|
const DEVICE_HUB_PROXY_PREFIX = "/api/device-hub";
|
|
@@ -53715,12 +54484,12 @@ const requireThreadSpawn = Effect.fn("mcp.requireThreadSpawn")(function* (fusion
|
|
|
53715
54484
|
const isDeviceToolUnavailableError = Schema$1.is(DeviceToolUnavailableError);
|
|
53716
54485
|
const isDeviceActionUnavailableError = Schema$1.is(DeviceActionUnavailableError);
|
|
53717
54486
|
/** Internal bridge; tool payloads never choose thread identity or daemon credentials. */
|
|
53718
|
-
const withDeviceInvocation = (execute) => Effect.gen(function* () {
|
|
54487
|
+
const withDeviceInvocation = (execute, capability = "devices") => Effect.gen(function* () {
|
|
53719
54488
|
const invocation = yield* McpInvocationContext;
|
|
53720
54489
|
const service = yield* DeviceService;
|
|
53721
54490
|
const lifetime = invocation.authorizationLifetime;
|
|
53722
54491
|
const denied = () => new DeviceToolUnavailableError({ reason: "Device command unavailable or authorization changed." });
|
|
53723
|
-
if (!invocation.capabilities.has(
|
|
54492
|
+
if (!invocation.capabilities.has(capability) || !lifetime?.revocation || lifetime.revoked) return yield* denied();
|
|
53724
54493
|
const work = execute(service, invocation.threadId).pipe(Effect.tap(() => lifetime.revoked ? Effect.fail(denied()) : Effect.void), Effect.mapError((error) => isDeviceToolUnavailableError(error) || isDeviceActionUnavailableError(error) ? error : denied()));
|
|
53725
54494
|
return yield* Effect.raceFirst(work, Deferred.await(lifetime.revocation).pipe(Effect.andThen(Effect.fail(denied()))));
|
|
53726
54495
|
});
|
|
@@ -53735,6 +54504,7 @@ const MAX_AX_BYTES = 512 * 1024;
|
|
|
53735
54504
|
const AX_TIMEOUT_MS = 1e4;
|
|
53736
54505
|
const MAX_AX_EXTENT = 32768;
|
|
53737
54506
|
const MIN_AX_EXTENT = 2;
|
|
54507
|
+
const isToolUnavailable = Schema$1.is(DeviceToolUnavailableError);
|
|
53738
54508
|
const AxFrames = Schema$1.Array(Schema$1.Struct({ frame: Schema$1.Struct({
|
|
53739
54509
|
x: Schema$1.Number,
|
|
53740
54510
|
y: Schema$1.Number,
|
|
@@ -53774,6 +54544,7 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
|
|
|
53774
54544
|
const state = yield* service.agentList;
|
|
53775
54545
|
const device = state.devices.find((device) => device.hostId === input.hostId && device.id === input.deviceId && device.platform === input.platform);
|
|
53776
54546
|
if (!device) return yield* stale();
|
|
54547
|
+
if (device.physical) return yield* new DeviceToolUnavailableError({ reason: "Physical-device UI control is not supported by this adapter. Pairing, trust, Developer Mode and signing must be configured separately; use supported device-management workflows." });
|
|
53777
54548
|
const session = yield* service.agentOpen({
|
|
53778
54549
|
threadId,
|
|
53779
54550
|
hostId: input.hostId,
|
|
@@ -53811,8 +54582,16 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
|
|
|
53811
54582
|
deviceId: target.deviceId
|
|
53812
54583
|
}, (ready, session) => Effect.gen(function* () {
|
|
53813
54584
|
if (session.leaseId !== input.leaseId) return yield* stale();
|
|
54585
|
+
if (input.method === "recording") {
|
|
54586
|
+
if (!ready.xcode || session.platform !== "ios") return yield* new DeviceToolUnavailableError({ reason: "Recording is unavailable on this device adapter." });
|
|
54587
|
+
return yield* ready.xcode.recording(session, input.action).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) })));
|
|
54588
|
+
}
|
|
54589
|
+
if (input.method === "semantic") {
|
|
54590
|
+
if (!ready.xcode || session.platform !== "ios") return yield* new DeviceToolUnavailableError({ reason: "Semantic device actions require a supported local Xcode adapter. Use the capabilities returned by getAXState()." });
|
|
54591
|
+
return yield* ready.xcode.action(session, input.action).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) })));
|
|
54592
|
+
}
|
|
53814
54593
|
if (input.method === "screenshot") {
|
|
53815
|
-
const capture = yield* screenshots.capture(ready, session);
|
|
54594
|
+
const capture = yield* ready.xcode && session.platform === "ios" ? ready.xcode.screenshot(session).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) }))) : screenshots.capture(ready, session);
|
|
53816
54595
|
return {
|
|
53817
54596
|
mimeType: "image/png",
|
|
53818
54597
|
data: capture.png.toString("base64"),
|
|
@@ -53820,6 +54599,7 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
|
|
|
53820
54599
|
height: capture.height
|
|
53821
54600
|
};
|
|
53822
54601
|
}
|
|
54602
|
+
if (ready.xcode && session.platform === "ios") return yield* ready.xcode.state(session, input.sinceScreenHash).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) })));
|
|
53823
54603
|
if (session.platform !== "ios") return yield* new DeviceToolUnavailableError({ reason: "CUA accessibility state is currently supported only for iOS Simulator. Use a screenshot for this device." });
|
|
53824
54604
|
const route = resolveDeviceHubRoute({
|
|
53825
54605
|
target: {
|
|
@@ -53847,12 +54627,13 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
|
|
|
53847
54627
|
leaseId: session.leaseId,
|
|
53848
54628
|
accessibility
|
|
53849
54629
|
};
|
|
53850
|
-
}).pipe(Effect.scoped, Effect.
|
|
53851
|
-
|
|
53852
|
-
|
|
53853
|
-
|
|
53854
|
-
|
|
53855
|
-
|
|
54630
|
+
}).pipe(Effect.scoped, Effect.timeoutOrElse({
|
|
54631
|
+
duration: AX_TIMEOUT_MS,
|
|
54632
|
+
orElse: () => Effect.gen(function* () {
|
|
54633
|
+
return yield* new DeviceToolUnavailableError({ reason: ready.xcode && "diagnose" in ready.xcode ? yield* ready.xcode.diagnose : "Device state request timed out. Check device support before selecting the device again." });
|
|
54634
|
+
})
|
|
54635
|
+
}), Effect.mapError((error) => isToolUnavailable(error) ? error : new DeviceToolUnavailableError({ reason: error instanceof Error ? error.message : String(error) }))));
|
|
54636
|
+
}), "computer-use").pipe(Effect.provideService(McpInvocationContext, scope), Effect.provideService(DeviceService, devices), Effect.mapError((error) => new ComputerUseError({
|
|
53856
54637
|
reason: "unavailable",
|
|
53857
54638
|
detail: error.message
|
|
53858
54639
|
})));
|
|
@@ -65806,6 +66587,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
65806
66587
|
context.inFlightTools.delete(index);
|
|
65807
66588
|
}
|
|
65808
66589
|
context.inFlightTools.clear();
|
|
66590
|
+
for (const toolUseId of context.subagentModels.keys()) if (!context.subagentTasks.has(toolUseId)) context.subagentModels.delete(toolUseId);
|
|
65809
66591
|
for (const block of turnState.assistantTextBlockOrder) yield* completeAssistantTextBlock(context, block, {
|
|
65810
66592
|
force: true,
|
|
65811
66593
|
rawMethod: "claude/result",
|
|
@@ -65974,9 +66756,31 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
65974
66756
|
discard: true
|
|
65975
66757
|
});
|
|
65976
66758
|
});
|
|
66759
|
+
const recordSubagentModel = Effect.fn("recordSubagentModel")(function* (context, toolUseId, model) {
|
|
66760
|
+
if (!toolUseId || !model || context.subagentModels.get(toolUseId) === model) return;
|
|
66761
|
+
const task = context.subagentTasks.get(toolUseId);
|
|
66762
|
+
if (!task && !Array.from(context.inFlightTools.values()).some((tool) => tool.itemId === toolUseId)) return;
|
|
66763
|
+
context.subagentModels.set(toolUseId, model);
|
|
66764
|
+
if (!task) return;
|
|
66765
|
+
yield* offerRuntimeEvent({
|
|
66766
|
+
...yield* makeEventStamp(),
|
|
66767
|
+
type: "task.progress",
|
|
66768
|
+
provider: PROVIDER$6,
|
|
66769
|
+
threadId: context.session.threadId,
|
|
66770
|
+
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
66771
|
+
payload: {
|
|
66772
|
+
taskId: RuntimeTaskId.make(task.taskId),
|
|
66773
|
+
description: task.description,
|
|
66774
|
+
toolUseId,
|
|
66775
|
+
...task.subagentType ? { subagentType: task.subagentType } : {},
|
|
66776
|
+
model
|
|
66777
|
+
}
|
|
66778
|
+
});
|
|
66779
|
+
});
|
|
65977
66780
|
const handleStreamEvent = Effect.fn("handleStreamEvent")(function* (context, message) {
|
|
65978
66781
|
if (message.type !== "stream_event") return;
|
|
65979
66782
|
const { event } = message;
|
|
66783
|
+
if (event.type === "message_start") yield* recordSubagentModel(context, message.parent_tool_use_id, event.message.model);
|
|
65980
66784
|
if (event.type === "message_delta") {
|
|
65981
66785
|
if (message.parent_tool_use_id !== null && message.parent_tool_use_id !== void 0) return;
|
|
65982
66786
|
const snapshot = normalizeClaudeActiveTokenUsage(event.usage, context.lastKnownContextWindow, context.lastKnownTotalProcessedTokens);
|
|
@@ -66297,10 +67101,12 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
66297
67101
|
rawPayload: message
|
|
66298
67102
|
});
|
|
66299
67103
|
context.inFlightTools.delete(index);
|
|
67104
|
+
if (!context.subagentTasks.has(tool.itemId)) context.subagentModels.delete(tool.itemId);
|
|
66300
67105
|
}
|
|
66301
67106
|
});
|
|
66302
67107
|
const handleAssistantMessage = Effect.fn("handleAssistantMessage")(function* (context, message) {
|
|
66303
67108
|
if (message.type !== "assistant") return;
|
|
67109
|
+
yield* recordSubagentModel(context, message.parent_tool_use_id, message.message.model);
|
|
66304
67110
|
if (!context.turnState) {
|
|
66305
67111
|
const turnId = TurnId.make(yield* randomUUIDv4);
|
|
66306
67112
|
const startedAt = yield* nowIso;
|
|
@@ -66407,6 +67213,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
66407
67213
|
}
|
|
66408
67214
|
switch (message.subtype) {
|
|
66409
67215
|
case "init":
|
|
67216
|
+
context.currentApiModelId = message.model || context.currentApiModelId;
|
|
66410
67217
|
yield* offerRuntimeEvent({
|
|
66411
67218
|
...base,
|
|
66412
67219
|
type: "session.configured",
|
|
@@ -66477,7 +67284,12 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
66477
67284
|
return;
|
|
66478
67285
|
case "task_started": {
|
|
66479
67286
|
const requestedModel = readString((message.tool_use_id ? Array.from(context.inFlightTools.values()).find((tool) => tool.itemId === message.tool_use_id) : void 0)?.input.model);
|
|
66480
|
-
const model = requestedModel === "inherit" ? context.currentApiModelId : requestedModel;
|
|
67287
|
+
const model = (message.tool_use_id ? context.subagentModels.get(message.tool_use_id) : void 0) ?? (requestedModel === "inherit" ? context.currentApiModelId : requestedModel);
|
|
67288
|
+
if (message.tool_use_id) context.subagentTasks.set(message.tool_use_id, {
|
|
67289
|
+
taskId: message.task_id,
|
|
67290
|
+
description: message.description,
|
|
67291
|
+
...message.subagent_type ? { subagentType: message.subagent_type } : {}
|
|
67292
|
+
});
|
|
66481
67293
|
yield* offerRuntimeEvent({
|
|
66482
67294
|
...base,
|
|
66483
67295
|
type: "task.started",
|
|
@@ -66528,6 +67340,11 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
66528
67340
|
...message.tool_use_id ? { toolUseId: message.tool_use_id } : {}
|
|
66529
67341
|
}
|
|
66530
67342
|
});
|
|
67343
|
+
for (const [toolUseId, task] of context.subagentTasks) if (toolUseId === message.tool_use_id || task.taskId === message.task_id) {
|
|
67344
|
+
context.subagentTasks.delete(toolUseId);
|
|
67345
|
+
context.subagentModels.delete(toolUseId);
|
|
67346
|
+
}
|
|
67347
|
+
if (message.tool_use_id) context.subagentModels.delete(message.tool_use_id);
|
|
66531
67348
|
return;
|
|
66532
67349
|
case "files_persisted":
|
|
66533
67350
|
yield* offerRuntimeEvent({
|
|
@@ -67228,6 +68045,8 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
67228
68045
|
turns: [],
|
|
67229
68046
|
inFlightTools,
|
|
67230
68047
|
claudeTasks,
|
|
68048
|
+
subagentTasks: /* @__PURE__ */ new Map(),
|
|
68049
|
+
subagentModels: /* @__PURE__ */ new Map(),
|
|
67231
68050
|
turnState: void 0,
|
|
67232
68051
|
pendingTurnRequest: void 0,
|
|
67233
68052
|
steeredMessages: /* @__PURE__ */ new Map(),
|
|
@@ -125492,6 +126311,7 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
|
|
|
125492
126311
|
[WS_METHODS.serverUpdateServer, AuthOrchestrationOperateScope],
|
|
125493
126312
|
[WS_METHODS.serverUpsertKeybinding, AuthOrchestrationOperateScope],
|
|
125494
126313
|
[WS_METHODS.serverRemoveKeybinding, AuthOrchestrationOperateScope],
|
|
126314
|
+
[WS_METHODS.threadHandoff, AuthOrchestrationReadScope],
|
|
125495
126315
|
[WS_METHODS.btwAsk, AuthOrchestrationReadScope],
|
|
125496
126316
|
[WS_METHODS.btwCancel, AuthOrchestrationReadScope],
|
|
125497
126317
|
[WS_METHODS.tasksList, AuthOrchestrationReadScope],
|
|
@@ -126344,6 +127164,27 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
|
|
|
126344
127164
|
operation: "getUsageReport",
|
|
126345
127165
|
detail: "This provider does not report account usage."
|
|
126346
127166
|
})), { "rpc.aggregate": "server" }),
|
|
127167
|
+
[WS_METHODS.threadHandoff]: (input) => observeRpcEffect$1(WS_METHODS.threadHandoff, Effect.gen(function* () {
|
|
127168
|
+
const fail = (detail) => new TextGenerationError({
|
|
127169
|
+
operation: "createHandoff",
|
|
127170
|
+
detail
|
|
127171
|
+
});
|
|
127172
|
+
if (!projectionSnapshotQuery.getHandoffContext || !textGeneration.generateBtwAnswer) return yield* fail("Handoff generation is unavailable on this server.");
|
|
127173
|
+
const context = yield* projectionSnapshotQuery.getHandoffContext(input.threadId).pipe(Effect.mapError(() => fail("Could not read thread history.")));
|
|
127174
|
+
if (Option.isNone(context)) return yield* fail("Thread or project is no longer available.");
|
|
127175
|
+
const settings = yield* serverSettings.getSettings.pipe(Effect.mapError(() => fail("Could not read text generation settings.")));
|
|
127176
|
+
const result = yield* textGeneration.generateBtwAnswer({
|
|
127177
|
+
cwd: context.value.projectId === P4_CHAT_PROJECT_ID ? config.chatWorkspaceDir : context.value.cwd,
|
|
127178
|
+
modelSelection: settings.textGenerationModelSelection,
|
|
127179
|
+
context: context.value.transcript,
|
|
127180
|
+
question: "Write a self-contained handoff for a new coding-agent thread continuing this work. Include objective, latest user constraints, decisions, completed changes, verification evidence and its limits, pending work, important paths and identifiers. Reconcile builder and watcher histories without duplicating their shared facts. Later user corrections win. Preserve explicit authorization limits; do not invent approvals or claim missing work is complete. Histories may be bounded: say when earlier context is unavailable. Return only the handoff in concise Markdown, addressed to the next agent."
|
|
127181
|
+
});
|
|
127182
|
+
if (!result.answer.trim()) return yield* fail("Model returned an empty handoff. Try again.");
|
|
127183
|
+
return {
|
|
127184
|
+
projectId: context.value.projectId,
|
|
127185
|
+
text: result.answer.trim()
|
|
127186
|
+
};
|
|
127187
|
+
}), { "rpc.aggregate": "thread" }),
|
|
126347
127188
|
[WS_METHODS.btwAsk]: (input) => observeRpcEffect$1(WS_METHODS.btwAsk, btw ? btw.ask(input) : Effect.fail(new BtwAskError({ detail: "Contextual questions are unavailable on this server." })), { "rpc.aggregate": "server" }),
|
|
126348
127189
|
[WS_METHODS.btwCancel]: (input) => observeRpcEffect$1(WS_METHODS.btwCancel, btw ? btw.cancel(input) : Effect.succeed({}), { "rpc.aggregate": "server" }),
|
|
126349
127190
|
[WS_METHODS.serverGetUsageSummary]: (input) => observeRpcEffect$1(WS_METHODS.serverGetUsageSummary, usage.readSummary(input), { "rpc.aggregate": "server" }),
|
|
@@ -127104,6 +127945,13 @@ const makeProviderAdapterRegistry = Effect.fn("makeProviderAdapterRegistry")(fun
|
|
|
127104
127945
|
});
|
|
127105
127946
|
const ProviderAdapterRegistryLive = Layer.effect(ProviderAdapterRegistry, makeProviderAdapterRegistry());
|
|
127106
127947
|
//#endregion
|
|
127948
|
+
//#region src/provider/ComputerUsePrompts.ts
|
|
127949
|
+
/** Routing guidance is injected only when the session has P4's Computer Use endpoint. */
|
|
127950
|
+
const COMPUTER_USE_ROUTING_PROMPT = "P4 Computer Use is enabled for this session through the cua_repl MCP server. For natural-language requests to inspect or interact with a native desktop app, use its js tool by default, even when the user does not mention P4 or Computer Use. Do not substitute provider-builtin Computer Use, AppleScript, shell automation, or another integration unless the user explicitly requests that path. For a named app, start directly with const app = await cua.getApp(exactNameOrBundleId); read the automatically returned state/image and stop if it answers the request. Reuse the handle and batch safe actions; do not repeat selection or unchanged state. For Device Hub, simulator, emulator, or connected-device requests, use cua.listDevices() once, then cua.getDevice({hostId,deviceId,platform}) with exact discovered IDs. Selection already emits initial device state. Reuse its handle; do not print the handle or repeat that state before acting. Never substitute macOS getApp('Simulator'). Device hosts are scoped to this provider environment and can differ from the desktop host. If device discovery, opening, inspection, or interaction reports missing permission or unavailable control, stop and report the exact limitation. Do not retry through screenshots, shell simctl, Xcode tools, or another MCP server. Do not call getState/getApp, control the P4 desktop app, or open device settings to work around a device API failure. Missing consent, unavailable devices, or unsupported operations are limitations to report, not permission to switch hosts or bypass the tool.";
|
|
127951
|
+
function withComputerUseRouting(input, enabled) {
|
|
127952
|
+
return enabled ? `<p4-computer-use>\n${COMPUTER_USE_ROUTING_PROMPT}\n</p4-computer-use>\n\n${input ?? ""}` : input;
|
|
127953
|
+
}
|
|
127954
|
+
//#endregion
|
|
127107
127955
|
//#region src/mcp/McpSessionRegistry.ts
|
|
127108
127956
|
var McpSessionRegistry = class extends Context.Service()("@p4code/cli/mcp/McpSessionRegistry") {};
|
|
127109
127957
|
/**
|
|
@@ -127877,7 +128725,10 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
|
|
|
127877
128725
|
...input.modelSelection?.model ? { "provider.model": input.modelSelection.model } : {}
|
|
127878
128726
|
});
|
|
127879
128727
|
yield* touchActiveMcpThread(input.threadId);
|
|
127880
|
-
const turn = yield* routed.adapter.sendTurn(
|
|
128728
|
+
const turn = yield* routed.adapter.sendTurn({
|
|
128729
|
+
...input,
|
|
128730
|
+
input: withComputerUseRouting(input.input, !forCompaction && Boolean(readMcpProviderSession(input.threadId)?.computerUse))
|
|
128731
|
+
});
|
|
127881
128732
|
yield* directory.upsert({
|
|
127882
128733
|
threadId: input.threadId,
|
|
127883
128734
|
provider: routed.adapter.provider,
|
|
@@ -128362,7 +129213,7 @@ const mcpToolkit = (toolkit) => Layer.effectDiscard(Effect.gen(function* () {
|
|
|
128362
129213
|
//#endregion
|
|
128363
129214
|
//#region src/mcp/toolkits/computerUse/tools.ts
|
|
128364
129215
|
const ComputerUseJsTool = Tool.make("js", {
|
|
128365
|
-
description: "Run JavaScript in a persistent
|
|
129216
|
+
description: "P4 Code Computer Use: control native apps through P4Code's signed helper. Run JavaScript in a persistent REPL. On the first call, execute exactly one entry-point operation and read its automatic output before continuing. When the user names an app, execute only const app = await cua.getApp(exactNameBundleIdOrPath); this already prints its state and includes a screenshot when AX exposes no app content. Use await cua.getState() only when discovery is needed. Use cua.createBrowserTab('iab', url, {visible:true}) or cua.getTab(tabId) for P4 browser tabs. Tab methods: getState(), goto(url), click({locator}), type({text,locator?,clear?}), press({key,modifiers?}), scroll({deltaY,deltaX?}). Print results with nodeRepl.write(value); emit PNG/JPEG with nodeRepl.emitImage({mimeType,data}) using base64 data. Top-level await and persistent let bindings are supported. Await each operation. Bindings disappear after helper restart, host disconnect, reset, or tray Stop. When resuming a task, guard a remembered binding within the same js call: if (typeof app === 'undefined') { globalThis.app = await cua.getApp('Spotify'); } else { await app.getScreenshot(); }. Substitute the named app and intended operation. The missing-binding branch selects only and prints fresh state; decide the next action after reading it. Never spend a separate tool call checking whether a binding exists. Reset after timeout or host disconnect. For available native apps, use cua.getApp(exactNameBundleIdOrPath), then getAXState(), click(elementIndex), or setValue(elementIndex,text) to replace a field value in the background. Selection already prints numbered accessibility state; reuse the app handle without selecting or reading it again before the first action. Snapshot text is bounded; use getAXState({filter:'Pause'}) for case-insensitive text filtering across the snapshot, including controls omitted from the initial output. Use {emit:false} on getApp(name, options), getAXState(options), or getScreenshot(options) when processing the returned value yourself; do not print auto-emitted output again. Several deterministic actions against distinct unchanged elements may share one snapshot. Each used element index expires after one action. To click the same control again, refresh with getAXState({filter: controlLabel}) first, even inside a batch; never reuse its old index. The host revalidates remaining native handles and rejects changed elements. Read fresh state after the action batch before deciding the next action. If selection includes a screenshot because AX exposes only window controls or menus, read that image immediately and finish if it answers the request. If no image is included, use getScreenshot() to read visual content; do not repeat unchanged snapshots hoping for missing accessibility content. getScreenshot() emits the selected app window; getAXStateAndScreenshot() emits both. Legacy getState(), click({elementId}), type({elementId,text}), windows(), and screenshot(windowId) remain supported. Native control preserves foreground focus and the system pointer. Coordinate, key, drag, scroll, and activation input are unsupported; do not use global input or activate the app as a fallback. P4Code shows the controlled native window and a persistent independent black pointer with a thin contrast edge in its embedded live view. Hide closes only the view; use Show live view in the Computer Use control to reopen it. Stop ends thread control; reset clears the view and handles. Google Chrome can be controlled as a native app using cua.getApp('Google Chrome'); Chrome extension/CDP tab APIs are not exposed by this host. Native app availability and macOS permission states are reported by the host; never assume native apps are supported. The host may be a different machine from the provider server. Device hub simulators are scoped to the provider environment: await cua.listDevices(), then await cua.getDevice({hostId,deviceId,platform}) using exact discovered IDs. Selection automatically emits initial AX state (or a screenshot on other platforms); reuse the handle without repeating that capture or printing the handle. A permission or unavailable-control error requires stopping and reporting it, never shell or alternative-tool fallback. The device handle supports getAXState() (raw iOS accessibility state), getScreenshot(), getAXStateAndScreenshot(), click([x,y]) in device coordinates from fresh AX state, typeText(text), and close(). When getAXState() reports adapter apple-xcode or xcodebuildmcp, use semantic refs from capture.elements and only advertised capabilities: click(elementRef), typeText(text,{elementRef,replaceExisting:true}), swipe({withinElementRef,direction}), drag({elementRef,direction}), batch([{action:\"tap\",elementRef},...]), and recordVideo(\"start\"|\"stop\") only when advertised in capabilities. getAXState({sinceScreenHash}) avoids unchanged snapshots. Batch only same-screen taps; action output includes updated capture. On stale-ref errors inspect fresh state and choose a current ref; never replay an action automatically. Physical-device UI control and device key input are unavailable. Device consent must already be enabled in P4 Settings. Use timeout_ms:60000 for initial device startup. Reset invalidates device handles; device viewer sessions remain under Devices until explicitly closed. Never substitute macOS Simulator.app AX for simulated-app state.",
|
|
128366
129217
|
parameters: Schema$1.Struct({
|
|
128367
129218
|
...ComputerUseJsInput.fields,
|
|
128368
129219
|
hostId: Schema$1.optional(TrimmedNonEmptyString).annotate({ description: "Select an available host when more than one is connected. Reset before changing hosts." })
|
|
@@ -132626,6 +133477,7 @@ function runtimeEventToActivities(event, taskTitle, compressMode) {
|
|
|
132626
133477
|
summary: event.payload.description.trim().length > 0 ? truncateDetail(event.payload.description, 120) : "Reasoning update",
|
|
132627
133478
|
payload: {
|
|
132628
133479
|
taskId: event.payload.taskId,
|
|
133480
|
+
...event.payload.model ? { model: event.payload.model } : {},
|
|
132629
133481
|
...event.provider === "claudeAgent" && event.payload.workflowScript?.instanceId === event.providerInstanceId ? { workflowScript: event.payload.workflowScript } : {},
|
|
132630
133482
|
...event.payload.description.trim().length > 0 ? { title: truncateDetail(event.payload.description, 120) } : {},
|
|
132631
133483
|
detail: event.payload.summary ?? event.payload.description,
|
|
@@ -137341,13 +138193,13 @@ const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$46), La
|
|
|
137341
138193
|
const PortScannerLayerLive = layer$45.pipe(Layer.provide(layer$82));
|
|
137342
138194
|
const TerminalLayerLive = layer$44.pipe(Layer.provide(PtyAdapterLive), Layer.provide(PortScannerLayerLive));
|
|
137343
138195
|
const PreviewLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$19), Layer.provideMerge(PortScannerLayerLive));
|
|
137344
|
-
const WorkspaceEntriesLayerLive = layer$17.pipe(Layer.provide(layer$
|
|
137345
|
-
const WorkspaceFileSystemLayerLive = layer$16.pipe(Layer.provide(layer$
|
|
137346
|
-
const WorkspaceLayerLive = Layer.mergeAll(layer$
|
|
137347
|
-
const ProjectFaviconResolverLayerLive = layer$63.pipe(Layer.provide(layer$
|
|
138196
|
+
const WorkspaceEntriesLayerLive = layer$17.pipe(Layer.provide(layer$68));
|
|
138197
|
+
const WorkspaceFileSystemLayerLive = layer$16.pipe(Layer.provide(layer$68), Layer.provide(WorkspaceEntriesLayerLive));
|
|
138198
|
+
const WorkspaceLayerLive = Layer.mergeAll(layer$68, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
|
|
138199
|
+
const ProjectFaviconResolverLayerLive = layer$63.pipe(Layer.provide(layer$68), Layer.provide(layer$64));
|
|
137348
138200
|
const AuthLayerLive = layer$111.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$115));
|
|
137349
138201
|
const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
|
|
137350
|
-
const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(layer$25), Layer.provideMerge(Layer.mergeAll(PullRequestServiceLive, CheckpointingLayerLive, SourceControlProviderRegistryLayerLive)), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderAuthServiceLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, RoutedTaskRepositoryLive)), Layer.provideMerge(Layer.mergeAll(PersistenceLayerLive, layer$86)), Layer.provideMerge(layer$80), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(layer$58), Layer.provideMerge(Layer.mergeAll(layer$57, layer$
|
|
138202
|
+
const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(layer$25), Layer.provideMerge(Layer.mergeAll(PullRequestServiceLive, CheckpointingLayerLive, SourceControlProviderRegistryLayerLive)), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderAuthServiceLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive, RoutedTaskRepositoryLive)), Layer.provideMerge(Layer.mergeAll(PersistenceLayerLive, layer$86)), Layer.provideMerge(layer$80), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(layer$58), Layer.provideMerge(Layer.mergeAll(layer$57, layer$70.pipe(Layer.provide(layer$82)), layer$72, layer$67)), Layer.provideMerge(layer$52), Layer.provideMerge(layer$53), Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$56)).pipe(Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(AntigravityInstallation.layer), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$60), Layer.provideMerge(layer$90.pipe(Layer.provide(layer$115))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$81), Layer.provideMerge(layer$66), Layer.provideMerge(remoteComputerUseBindingsLayer.pipe(Layer.provide(layer$86))), Layer.provideMerge(layer$74), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$5.pipe(Layer.provideMerge(LinearClientLive), Layer.provideMerge(Layer.mergeAll(LinearMcpClientLive, TrackerSecretsLive)), Layer.provideMerge(Layer.mergeAll(layer$87, layer$21).pipe(Layer.provideMerge(layer$88), Layer.provideMerge(layer$89))))), Layer.provideMerge(layer$115));
|
|
137351
138203
|
const UsageLayerLive = layer$23.pipe(Layer.provide(layer$90.pipe(Layer.provide(layer$115))));
|
|
137352
138204
|
const RuntimeDependenciesLive = layer$22.pipe(Layer.provideMerge(RuntimeCoreDependenciesLive)).pipe(Layer.provideMerge(UsageLayerLive), Layer.provideMerge(layer$12), Layer.provideMerge(layer$11), Layer.provideMerge(layer$10), Layer.provideMerge(layer$77), Layer.provideMerge(layer$79), Layer.provideMerge(layer$78), Layer.provide(layer$119));
|
|
137353
138205
|
/**
|
|
@@ -137358,7 +138210,7 @@ const RuntimeDependenciesLive = layer$22.pipe(Layer.provideMerge(RuntimeCoreDepe
|
|
|
137358
138210
|
* this costs a server with no hub nothing but a layer.
|
|
137359
138211
|
*/
|
|
137360
138212
|
const AssetSyncLive = layer$84.pipe(Layer.provideMerge(layer$85), Layer.provideMerge(layer$86));
|
|
137361
|
-
const RuntimeServicesLive = layer$
|
|
138213
|
+
const RuntimeServicesLive = layer$69.pipe(Layer.provideMerge(layer$1), Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
|
|
137362
138214
|
const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(pullRequestHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer$4.pipe(Layer.provide(layer$6)), layer$65, layer).pipe(Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionThreadRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$64), Layer.provide(layer$25), Layer.provide(PullRequestServiceLive), Layer.provide(layer$20), Layer.provide(layer$75), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
|
|
137363
138215
|
const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
|
|
137364
138216
|
const config = yield* ServerConfig$1;
|