@p4code/cli 0.5.5 → 0.5.7
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 +985 -130
- package/dist/client/assets/{DiffPanel-BRp6V4_6.js → DiffPanel-BS0biQw-.js} +2 -2
- package/dist/client/assets/{FilePreviewPanel-CUBJUojc.js → FilePreviewPanel-Dn9DSGk3.js} +2 -2
- package/dist/client/assets/{PreviewPanel-DQStLGQW.js → PreviewPanel-DWNwzOqs.js} +2 -2
- package/dist/client/assets/{PullRequestCodeTab-S1xN_kez.js → PullRequestCodeTab-fnd5EeZp.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-B-zJkLkz.js → fileCommentAnnotations-BDibf4o7.js} +2 -2
- package/dist/client/assets/{index-juoUzzRC.js → index-Cj8u-oxt.js} +187 -175
- package/dist/client/assets/{index-Bb0jNtjG.css → index-qUvk851Q.css} +1 -1
- package/dist/client/assets/previewAssetResource-FVg-QUYZ.js +48 -0
- package/dist/client/assets/{renderFileChildren-Clc6sdVq.js → renderFileChildren-BnxsBYhc.js} +2 -2
- package/dist/client/assets/{toggle-group-kzQfQIqg.js → toggle-group-B7_HMe_x.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.7";
|
|
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
|
|
@@ -13550,6 +13560,7 @@ const ComputerUseHostReport = Schema$1.Struct({
|
|
|
13550
13560
|
"stopped"
|
|
13551
13561
|
]),
|
|
13552
13562
|
previewApp: Schema$1.optional(Schema$1.String),
|
|
13563
|
+
previewAppIcon: Schema$1.optional(Schema$1.NullOr(Schema$1.String)),
|
|
13553
13564
|
deviceControl: Schema$1.optional(Schema$1.NullOr(ComputerUseDeviceControl))
|
|
13554
13565
|
});
|
|
13555
13566
|
const ComputerUseControlState = Schema$1.Struct({
|
|
@@ -13562,6 +13573,7 @@ const ComputerUseControlState = Schema$1.Struct({
|
|
|
13562
13573
|
"stop-unconfirmed"
|
|
13563
13574
|
]),
|
|
13564
13575
|
previewApp: Schema$1.optional(Schema$1.String),
|
|
13576
|
+
previewAppIcon: Schema$1.optional(Schema$1.NullOr(Schema$1.String)),
|
|
13565
13577
|
deviceControl: Schema$1.optional(Schema$1.NullOr(ComputerUseDeviceControl))
|
|
13566
13578
|
});
|
|
13567
13579
|
const ComputerUseControlInput = Schema$1.Struct({
|
|
@@ -13591,6 +13603,45 @@ const ComputerUseDeviceTarget = {
|
|
|
13591
13603
|
deviceId: DeviceId,
|
|
13592
13604
|
leaseId: DeviceId
|
|
13593
13605
|
};
|
|
13606
|
+
const DeviceElementRef = TrimmedNonEmptyString.check(Schema$1.isMaxLength(128));
|
|
13607
|
+
const DeviceGesture = {
|
|
13608
|
+
direction: Schema$1.Literals([
|
|
13609
|
+
"up",
|
|
13610
|
+
"down",
|
|
13611
|
+
"left",
|
|
13612
|
+
"right"
|
|
13613
|
+
]),
|
|
13614
|
+
distance: Schema$1.optional(Schema$1.Number.check(Schema$1.isGreaterThan(0), Schema$1.isLessThanOrEqualTo(1)))
|
|
13615
|
+
};
|
|
13616
|
+
const ComputerUseDeviceAction = Schema$1.Union([
|
|
13617
|
+
Schema$1.Struct({
|
|
13618
|
+
action: Schema$1.Literal("tap"),
|
|
13619
|
+
elementRef: DeviceElementRef
|
|
13620
|
+
}),
|
|
13621
|
+
Schema$1.Struct({
|
|
13622
|
+
action: Schema$1.Literal("type_text"),
|
|
13623
|
+
elementRef: DeviceElementRef,
|
|
13624
|
+
text: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(16e3)),
|
|
13625
|
+
replaceExisting: Schema$1.optional(Schema$1.Boolean)
|
|
13626
|
+
}),
|
|
13627
|
+
Schema$1.Struct({
|
|
13628
|
+
action: Schema$1.Literal("swipe"),
|
|
13629
|
+
withinElementRef: DeviceElementRef,
|
|
13630
|
+
...DeviceGesture
|
|
13631
|
+
}),
|
|
13632
|
+
Schema$1.Struct({
|
|
13633
|
+
action: Schema$1.Literal("drag"),
|
|
13634
|
+
elementRef: DeviceElementRef,
|
|
13635
|
+
...DeviceGesture
|
|
13636
|
+
}),
|
|
13637
|
+
Schema$1.Struct({
|
|
13638
|
+
action: Schema$1.Literal("batch"),
|
|
13639
|
+
steps: Schema$1.Array(Schema$1.Struct({
|
|
13640
|
+
action: Schema$1.Literal("tap"),
|
|
13641
|
+
elementRef: DeviceElementRef
|
|
13642
|
+
})).check(Schema$1.isMinLength(1), Schema$1.isMaxLength(100))
|
|
13643
|
+
})
|
|
13644
|
+
]);
|
|
13594
13645
|
const ComputerUseDeviceOperation = Schema$1.Union([
|
|
13595
13646
|
Schema$1.Struct({ method: Schema$1.Literal("list") }),
|
|
13596
13647
|
Schema$1.Struct({
|
|
@@ -13605,7 +13656,18 @@ const ComputerUseDeviceOperation = Schema$1.Union([
|
|
|
13605
13656
|
"screenshot",
|
|
13606
13657
|
"close"
|
|
13607
13658
|
]),
|
|
13608
|
-
...ComputerUseDeviceTarget
|
|
13659
|
+
...ComputerUseDeviceTarget,
|
|
13660
|
+
sinceScreenHash: Schema$1.optional(TrimmedNonEmptyString)
|
|
13661
|
+
}),
|
|
13662
|
+
Schema$1.Struct({
|
|
13663
|
+
method: Schema$1.Literal("semantic"),
|
|
13664
|
+
...ComputerUseDeviceTarget,
|
|
13665
|
+
action: ComputerUseDeviceAction
|
|
13666
|
+
}),
|
|
13667
|
+
Schema$1.Struct({
|
|
13668
|
+
method: Schema$1.Literal("recording"),
|
|
13669
|
+
...ComputerUseDeviceTarget,
|
|
13670
|
+
action: Schema$1.Literals(["start", "stop"])
|
|
13609
13671
|
}),
|
|
13610
13672
|
Schema$1.Struct({
|
|
13611
13673
|
method: Schema$1.Literal("command"),
|
|
@@ -13717,6 +13779,7 @@ const WS_METHODS = {
|
|
|
13717
13779
|
serverGetProcessDiagnostics: "server.getProcessDiagnostics",
|
|
13718
13780
|
serverGetProcessResourceHistory: "server.getProcessResourceHistory",
|
|
13719
13781
|
serverSignalProcess: "server.signalProcess",
|
|
13782
|
+
threadHandoff: "thread.handoff",
|
|
13720
13783
|
btwAsk: "btw.ask",
|
|
13721
13784
|
btwCancel: "btw.cancel",
|
|
13722
13785
|
pullRequestsList: "pullRequests.list",
|
|
@@ -14360,6 +14423,14 @@ const WsServerGetProviderUsageRpc = Rpc.make(WS_METHODS.serverGetProviderUsage,
|
|
|
14360
14423
|
success: Schema$1.Struct({ report: Schema$1.String }),
|
|
14361
14424
|
error: Schema$1.Union([TextGenerationError, EnvironmentAuthorizationError])
|
|
14362
14425
|
});
|
|
14426
|
+
const WsThreadHandoffRpc = Rpc.make(WS_METHODS.threadHandoff, {
|
|
14427
|
+
payload: Schema$1.Struct({ threadId: ThreadId }),
|
|
14428
|
+
success: Schema$1.Struct({
|
|
14429
|
+
projectId: ProjectId,
|
|
14430
|
+
text: Schema$1.String
|
|
14431
|
+
}),
|
|
14432
|
+
error: Schema$1.Union([TextGenerationError, EnvironmentAuthorizationError])
|
|
14433
|
+
});
|
|
14363
14434
|
const WsBtwAskRpc = Rpc.make(WS_METHODS.btwAsk, {
|
|
14364
14435
|
payload: BtwAskInput,
|
|
14365
14436
|
success: BtwAskResult,
|
|
@@ -14820,7 +14891,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
|
|
|
14820
14891
|
error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
|
|
14821
14892
|
stream: true
|
|
14822
14893
|
});
|
|
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);
|
|
14894
|
+
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
14895
|
//#endregion
|
|
14825
14896
|
//#region ../../packages/shared/src/oauthScope.ts
|
|
14826
14897
|
const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
|
|
@@ -24635,7 +24706,7 @@ const MAX_APNS_REQUEST_BYTES = 4096;
|
|
|
24635
24706
|
const CLEANUP_TIMEOUT_MS$1 = 1e3;
|
|
24636
24707
|
const MAX_REQUEST_BYTES$2 = 8 * 1024;
|
|
24637
24708
|
const MAX_RESPONSE_BYTES$8 = 16 * 1024;
|
|
24638
|
-
const REQUEST_TIMEOUT_MS$
|
|
24709
|
+
const REQUEST_TIMEOUT_MS$7 = 8e3;
|
|
24639
24710
|
const MAX_AUTH_CHARS = 8192;
|
|
24640
24711
|
const unavailable$6 = () => ({
|
|
24641
24712
|
outcome: "retry",
|
|
@@ -24680,7 +24751,7 @@ const sendHubApns = Effect.fn("HubPushTransport.apns")(function* (input) {
|
|
|
24680
24751
|
finish(unavailable$6());
|
|
24681
24752
|
return;
|
|
24682
24753
|
}
|
|
24683
|
-
timer = setTimeout(() => finish(unavailable$6()), REQUEST_TIMEOUT_MS$
|
|
24754
|
+
timer = setTimeout(() => finish(unavailable$6()), REQUEST_TIMEOUT_MS$7);
|
|
24684
24755
|
session = NodeHttp2.connect(input.sandbox ? "https://api.sandbox.push.apple.com" : "https://api.push.apple.com", { maxHeaderListPairs: 32 });
|
|
24685
24756
|
session.on("error", () => finish(unavailable$6()));
|
|
24686
24757
|
session.on("close", () => finish(unavailable$6()));
|
|
@@ -24735,7 +24806,7 @@ const sendHubFcm = Effect.fn("HubPushTransport.fcm")(function* (input, fetcher =
|
|
|
24735
24806
|
const abort = () => owner.abort();
|
|
24736
24807
|
signal.addEventListener("abort", abort, { once: true });
|
|
24737
24808
|
if (signal.aborted) owner.abort();
|
|
24738
|
-
const timeout = setTimeout(abort, REQUEST_TIMEOUT_MS$
|
|
24809
|
+
const timeout = setTimeout(abort, REQUEST_TIMEOUT_MS$7);
|
|
24739
24810
|
let reader;
|
|
24740
24811
|
try {
|
|
24741
24812
|
const response = await fetcher(`https://fcm.googleapis.com/v1/projects/${input.projectId}/messages:send`, {
|
|
@@ -24776,7 +24847,7 @@ const sendHubFcm = Effect.fn("HubPushTransport.fcm")(function* (input, fetcher =
|
|
|
24776
24847
|
}
|
|
24777
24848
|
},
|
|
24778
24849
|
catch: () => unavailable$6()
|
|
24779
|
-
}).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$
|
|
24850
|
+
}).pipe(Effect.timeout(REQUEST_TIMEOUT_MS$7), Effect.catch(() => Effect.succeed(unavailable$6())));
|
|
24780
24851
|
});
|
|
24781
24852
|
//#endregion
|
|
24782
24853
|
//#region src/hub/HubNotificationRuntime.ts
|
|
@@ -41682,6 +41753,72 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
|
|
|
41682
41753
|
if (isPersistenceError(error)) return error;
|
|
41683
41754
|
return toPersistenceSqlError("ProjectionSnapshotQuery.getSnapshot:query")(error);
|
|
41684
41755
|
}));
|
|
41756
|
+
const readHandoffThread = SqlSchema.findOneOption({
|
|
41757
|
+
Request: ThreadIdLookupInput,
|
|
41758
|
+
Result: ProjectionBtwThreadRowSchema,
|
|
41759
|
+
execute: ({ threadId }) => sql`
|
|
41760
|
+
SELECT threads.project_id AS "projectId",
|
|
41761
|
+
COALESCE(projects.workspace_root, threads.worktree_path, '') AS cwd
|
|
41762
|
+
FROM projection_threads AS threads
|
|
41763
|
+
LEFT JOIN projection_projects AS projects ON projects.project_id = threads.project_id
|
|
41764
|
+
WHERE threads.thread_id = ${threadId} AND threads.deleted_at IS NULL
|
|
41765
|
+
AND (threads.project_id = ${P4_CHAT_PROJECT_ID} OR projects.deleted_at IS NULL)
|
|
41766
|
+
`
|
|
41767
|
+
});
|
|
41768
|
+
const readHandoffRows = SqlSchema.findAll({
|
|
41769
|
+
Request: ThreadIdLookupInput,
|
|
41770
|
+
Result: Schema$1.Struct({
|
|
41771
|
+
threadId: ThreadId,
|
|
41772
|
+
role: Schema$1.String,
|
|
41773
|
+
text: Schema$1.String
|
|
41774
|
+
}),
|
|
41775
|
+
execute: ({ threadId }) => sql`
|
|
41776
|
+
WITH latest_pair AS (
|
|
41777
|
+
SELECT implementer_thread_id, watcher_thread_id FROM thread_pairs
|
|
41778
|
+
WHERE implementer_thread_id = ${threadId} OR watcher_thread_id = ${threadId}
|
|
41779
|
+
ORDER BY created_at DESC LIMIT 1
|
|
41780
|
+
), sources AS (
|
|
41781
|
+
SELECT ${threadId} AS id
|
|
41782
|
+
UNION SELECT implementer_thread_id FROM latest_pair
|
|
41783
|
+
UNION SELECT watcher_thread_id FROM latest_pair
|
|
41784
|
+
), ranked AS (
|
|
41785
|
+
SELECT messages.thread_id AS "threadId", messages.role,
|
|
41786
|
+
substr(messages.text, -${BTW_CONTEXT_CHARACTER_LIMIT}) AS text,
|
|
41787
|
+
messages.created_at, messages.message_id,
|
|
41788
|
+
ROW_NUMBER() OVER (PARTITION BY messages.thread_id ORDER BY messages.created_at DESC, messages.message_id DESC) AS recent,
|
|
41789
|
+
ROW_NUMBER() OVER (PARTITION BY messages.thread_id ORDER BY messages.created_at, messages.message_id) AS earliest
|
|
41790
|
+
FROM projection_thread_messages AS messages
|
|
41791
|
+
JOIN projection_threads AS threads ON threads.thread_id = messages.thread_id
|
|
41792
|
+
WHERE messages.thread_id IN (SELECT id FROM sources)
|
|
41793
|
+
AND threads.deleted_at IS NULL AND messages.is_streaming = 0
|
|
41794
|
+
AND messages.role IN ('user', 'assistant')
|
|
41795
|
+
) SELECT "threadId", role, text FROM ranked
|
|
41796
|
+
WHERE recent <= ${BTW_CONTEXT_MESSAGE_LIMIT} OR earliest = 1
|
|
41797
|
+
ORDER BY created_at, message_id
|
|
41798
|
+
`
|
|
41799
|
+
});
|
|
41800
|
+
const getHandoffContext = Effect.fn("ProjectionSnapshotQuery.getHandoffContext")(function* (threadId) {
|
|
41801
|
+
return yield* sql.withTransaction(Effect.gen(function* () {
|
|
41802
|
+
const thread = yield* readHandoffThread({ threadId });
|
|
41803
|
+
if (Option.isNone(thread)) return Option.none();
|
|
41804
|
+
const rows = yield* readHandoffRows({ threadId });
|
|
41805
|
+
const sourceIds = [...new Set(rows.map((row) => row.threadId))];
|
|
41806
|
+
const sourceCount = Math.max(1, sourceIds.length);
|
|
41807
|
+
const budget = Math.floor((BTW_CONTEXT_CHARACTER_LIMIT - 2 * (sourceCount - 1)) / sourceCount);
|
|
41808
|
+
const transcript = sourceIds.map((sourceId) => {
|
|
41809
|
+
const text = rows.filter((row) => row.threadId === sourceId).map((row) => `${row.role}: ${row.text}`).join("\n\n");
|
|
41810
|
+
const heading = `Thread ${sourceId} (bounded history):\n`;
|
|
41811
|
+
const available = budget - heading.length;
|
|
41812
|
+
const omission = "\n[Earlier history omitted]\n";
|
|
41813
|
+
const beginning = Math.floor(available / 4);
|
|
41814
|
+
return heading + (text.length <= available ? text : text.slice(0, beginning) + omission + text.slice(-(available - beginning - 27)));
|
|
41815
|
+
}).join("\n\n");
|
|
41816
|
+
return Option.some({
|
|
41817
|
+
...thread.value,
|
|
41818
|
+
transcript
|
|
41819
|
+
});
|
|
41820
|
+
})).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getHandoffContext:query", "ProjectionSnapshotQuery.getHandoffContext:decodeRows")));
|
|
41821
|
+
});
|
|
41685
41822
|
const getBtwContext = Effect.fn("ProjectionSnapshotQuery.getBtwContext")(function* (threadId) {
|
|
41686
41823
|
const [thread, rows] = yield* Effect.all([getBtwThreadRow({ threadId }), listBtwContextRows({ threadId })]).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getBtwContext:query", "ProjectionSnapshotQuery.getBtwContext:decodeRows")));
|
|
41687
41824
|
if (Option.isNone(thread)) return Option.none();
|
|
@@ -42265,6 +42402,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
|
|
|
42265
42402
|
getThreadPairById,
|
|
42266
42403
|
getThreadShellById,
|
|
42267
42404
|
getThreadDetailById,
|
|
42405
|
+
getHandoffContext,
|
|
42268
42406
|
getBtwContext,
|
|
42269
42407
|
getThreadDetailSnapshot
|
|
42270
42408
|
};
|
|
@@ -45373,7 +45511,7 @@ var DeviceDiscoveryFailure = class extends Data.TaggedError("DeviceDiscoveryFail
|
|
|
45373
45511
|
const MAX_DEVICES = 512;
|
|
45374
45512
|
const MAX_RESPONSE_BYTES$6 = 1024 * 1024;
|
|
45375
45513
|
const MAX_DEVICE_LABEL = 512;
|
|
45376
|
-
const REQUEST_TIMEOUT_MS$
|
|
45514
|
+
const REQUEST_TIMEOUT_MS$6 = 15e3;
|
|
45377
45515
|
const HubDevice = Schema$1.Struct({
|
|
45378
45516
|
id: DeviceId,
|
|
45379
45517
|
name: Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(MAX_DEVICE_LABEL)),
|
|
@@ -45416,7 +45554,10 @@ const make$87 = Effect.fn("DeviceDiscovery.make")(function* () {
|
|
|
45416
45554
|
});
|
|
45417
45555
|
if (body.invalidUtf8 || body.truncated) return yield* new DeviceDiscoveryFailure({ message: "Invalid device discovery response." });
|
|
45418
45556
|
const result = yield* decodeDeviceDiscovery(input.hostId, body.text);
|
|
45419
|
-
const
|
|
45557
|
+
const adapterDevices = input.ready.xcode ? yield* input.ready.xcode.discover : [];
|
|
45558
|
+
const simulatorIds = new Set(adapterDevices.map((device) => device.id));
|
|
45559
|
+
const devices = [...result.devices.filter((device) => device.platform !== "ios" || device.physical || !simulatorIds.has(device.id)), ...adapterDevices];
|
|
45560
|
+
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
45561
|
if (input.androidAvailable) {
|
|
45421
45562
|
const avds = yield* input.ready.run("emulator", ["-list-avds"]);
|
|
45422
45563
|
if (avds.code !== 0) return yield* new DeviceDiscoveryFailure({ message: "Android virtual device discovery failed." });
|
|
@@ -45440,7 +45581,7 @@ const make$87 = Effect.fn("DeviceDiscovery.make")(function* () {
|
|
|
45440
45581
|
devices,
|
|
45441
45582
|
partial: result.partial
|
|
45442
45583
|
};
|
|
45443
|
-
}, Effect.scoped, Effect.timeout(Duration.millis(REQUEST_TIMEOUT_MS$
|
|
45584
|
+
}, Effect.scoped, Effect.timeout(Duration.millis(REQUEST_TIMEOUT_MS$6)), Effect.mapError(() => new DeviceOperationError({
|
|
45444
45585
|
operation: "list",
|
|
45445
45586
|
reason: "request_failed",
|
|
45446
45587
|
cause: "Device discovery unavailable."
|
|
@@ -45744,6 +45885,675 @@ const start = Effect.fn("DeviceHostSettings.start")(function* (input) {
|
|
|
45744
45885
|
return { refresh };
|
|
45745
45886
|
});
|
|
45746
45887
|
//#endregion
|
|
45888
|
+
//#region src/device/DeviceScreenshot.ts
|
|
45889
|
+
const MAX_SCREENSHOT_BYTES = 8 * 1024 * 1024;
|
|
45890
|
+
var DeviceScreenshotFailure = class extends Data.TaggedError("DeviceScreenshotFailure") {};
|
|
45891
|
+
const MAX_DIMENSION = 16384;
|
|
45892
|
+
const MAX_PIXELS = 64 * 1024 * 1024;
|
|
45893
|
+
const SCREENSHOT_TIMEOUT_MS = 2e4;
|
|
45894
|
+
const PNG_HEADER_BYTES = 33;
|
|
45895
|
+
const PNG_CHUNK_OVERHEAD = 12;
|
|
45896
|
+
const PNG_SIGNATURE = Buffer.from([
|
|
45897
|
+
137,
|
|
45898
|
+
80,
|
|
45899
|
+
78,
|
|
45900
|
+
71,
|
|
45901
|
+
13,
|
|
45902
|
+
10,
|
|
45903
|
+
26,
|
|
45904
|
+
10
|
|
45905
|
+
]);
|
|
45906
|
+
function pngDimensions(png) {
|
|
45907
|
+
const buffer = Buffer.from(png.buffer, png.byteOffset, png.byteLength);
|
|
45908
|
+
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." });
|
|
45909
|
+
const width = buffer.readUInt32BE(16);
|
|
45910
|
+
const height = buffer.readUInt32BE(20);
|
|
45911
|
+
if (!width || !height || width > MAX_DIMENSION || height > MAX_DIMENSION || width * height > MAX_PIXELS) throw new DeviceScreenshotFailure({ message: "Screenshot dimensions exceed limits." });
|
|
45912
|
+
let offset = PNG_HEADER_BYTES;
|
|
45913
|
+
let imageData = false;
|
|
45914
|
+
let ended = false;
|
|
45915
|
+
while (offset + PNG_CHUNK_OVERHEAD <= buffer.length) {
|
|
45916
|
+
const length = buffer.readUInt32BE(offset);
|
|
45917
|
+
const type = buffer.toString("ascii", offset + 4, offset + 8);
|
|
45918
|
+
const next = offset + PNG_CHUNK_OVERHEAD + length;
|
|
45919
|
+
if (next > buffer.length || type === "IHDR") throw new DeviceScreenshotFailure({ message: "Invalid PNG chunk." });
|
|
45920
|
+
if (type === "IDAT") imageData = true;
|
|
45921
|
+
if (type === "IEND") {
|
|
45922
|
+
ended = length === 0 && next === buffer.length;
|
|
45923
|
+
break;
|
|
45924
|
+
}
|
|
45925
|
+
offset = next;
|
|
45926
|
+
}
|
|
45927
|
+
if (!imageData || !ended) throw new DeviceScreenshotFailure({ message: "Incomplete PNG screenshot." });
|
|
45928
|
+
return {
|
|
45929
|
+
width,
|
|
45930
|
+
height
|
|
45931
|
+
};
|
|
45932
|
+
}
|
|
45933
|
+
const make$82 = Effect.fn("DeviceScreenshot.make")(function* () {
|
|
45934
|
+
const http = yield* HttpClient.HttpClient;
|
|
45935
|
+
return Effect.fn("DeviceScreenshot.capture")(function* (ready, session) {
|
|
45936
|
+
const origin = yield* Effect.try(() => new URL(ready.hub.origin));
|
|
45937
|
+
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." });
|
|
45938
|
+
const vendor = session.platform === "ios" ? "serve-sim" : "serve-emu";
|
|
45939
|
+
const response = yield* http.execute(HttpClientRequest.post(`${origin.origin}/vendor/${vendor}/api/screenshot?device=${encodeURIComponent(session.deviceId)}`));
|
|
45940
|
+
if (response.status !== 200) return yield* new DeviceScreenshotFailure({ message: "Screenshot rejected." });
|
|
45941
|
+
let bytes = 0;
|
|
45942
|
+
const chunks = [];
|
|
45943
|
+
yield* response.stream.pipe(Stream.runForEach((chunk) => Effect.gen(function* () {
|
|
45944
|
+
bytes += chunk.byteLength;
|
|
45945
|
+
if (bytes > 8388608) return yield* new DeviceScreenshotFailure({ message: "Screenshot too large." });
|
|
45946
|
+
chunks.push(chunk);
|
|
45947
|
+
})));
|
|
45948
|
+
const png = Buffer.concat(chunks, bytes);
|
|
45949
|
+
return {
|
|
45950
|
+
png,
|
|
45951
|
+
...yield* Effect.try(() => pngDimensions(png))
|
|
45952
|
+
};
|
|
45953
|
+
}, Effect.scoped, Effect.timeout(Duration.millis(SCREENSHOT_TIMEOUT_MS)), Effect.mapError(() => new DeviceOperationError({
|
|
45954
|
+
operation: "screenshot",
|
|
45955
|
+
reason: "request_failed",
|
|
45956
|
+
cause: "Device screenshot could not be captured."
|
|
45957
|
+
})));
|
|
45958
|
+
});
|
|
45959
|
+
var DeviceScreenshot = class extends Context.Service()("@p4code/cli/device/DeviceScreenshot") {};
|
|
45960
|
+
const layer$72 = Layer.effect(DeviceScreenshot, make$82().pipe(Effect.map((capture) => ({ capture }))));
|
|
45961
|
+
//#endregion
|
|
45962
|
+
//#region src/device/XcodeMcpClient.ts
|
|
45963
|
+
const XCODE_BUILD_MCP_VERSION = "2.7.0";
|
|
45964
|
+
const PROTOCOL_VERSION = "2025-06-18";
|
|
45965
|
+
const REQUEST_TIMEOUT_MS$5 = 6e4;
|
|
45966
|
+
const MAX_MESSAGE_BYTES = 12 * 1024 * 1024;
|
|
45967
|
+
const MAX_TOOL_PAGES = 32;
|
|
45968
|
+
function objectValue(value) {
|
|
45969
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
45970
|
+
}
|
|
45971
|
+
/** One process per lease keeps semantic refs and simulator defaults isolated. Never invokes npx. */
|
|
45972
|
+
var XcodeMcpClient = class XcodeMcpClient {
|
|
45973
|
+
child;
|
|
45974
|
+
sequence = 0;
|
|
45975
|
+
buffer = "";
|
|
45976
|
+
closed = false;
|
|
45977
|
+
pending = /* @__PURE__ */ new Map();
|
|
45978
|
+
tools = /* @__PURE__ */ new Set();
|
|
45979
|
+
constructor(child) {
|
|
45980
|
+
this.child = child;
|
|
45981
|
+
child.stdout.setEncoding("utf8");
|
|
45982
|
+
child.stdout.on("data", (chunk) => {
|
|
45983
|
+
this.buffer += chunk;
|
|
45984
|
+
if (Buffer.byteLength(this.buffer) > MAX_MESSAGE_BYTES) {
|
|
45985
|
+
this.close(/* @__PURE__ */ new Error("Device MCP response exceeds the supported size."));
|
|
45986
|
+
return;
|
|
45987
|
+
}
|
|
45988
|
+
let boundary;
|
|
45989
|
+
while ((boundary = this.buffer.indexOf("\n")) >= 0) {
|
|
45990
|
+
const line = this.buffer.slice(0, boundary);
|
|
45991
|
+
this.buffer = this.buffer.slice(boundary + 1);
|
|
45992
|
+
try {
|
|
45993
|
+
const message = objectValue(JSON.parse(line));
|
|
45994
|
+
if (typeof message.id !== "number") continue;
|
|
45995
|
+
const call = this.pending.get(message.id);
|
|
45996
|
+
if (!call) continue;
|
|
45997
|
+
this.pending.delete(message.id);
|
|
45998
|
+
if (message.error) {
|
|
45999
|
+
const detail = objectValue(message.error).message;
|
|
46000
|
+
call.reject(new Error(typeof detail === "string" ? detail : "Device MCP request failed."));
|
|
46001
|
+
} else call.resolve(message.result);
|
|
46002
|
+
} catch {
|
|
46003
|
+
this.close(/* @__PURE__ */ new Error("Device MCP returned invalid stdio JSON."));
|
|
46004
|
+
return;
|
|
46005
|
+
}
|
|
46006
|
+
}
|
|
46007
|
+
});
|
|
46008
|
+
child.stderr.resume();
|
|
46009
|
+
child.on("error", () => this.close(/* @__PURE__ */ new Error("Device MCP executable could not start.")));
|
|
46010
|
+
child.on("exit", () => this.close(/* @__PURE__ */ new Error("Device MCP disconnected. Select the device again.")));
|
|
46011
|
+
child.stdin.on("error", () => this.close(/* @__PURE__ */ new Error("Device MCP input disconnected.")));
|
|
46012
|
+
}
|
|
46013
|
+
static async start(executable, cwd, environment, signal, kind = "legacy") {
|
|
46014
|
+
if (!isAbsolute(executable)) throw new Error("Device MCP requires an absolute executable path.");
|
|
46015
|
+
await access(executable, constants.X_OK).catch(() => {
|
|
46016
|
+
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.");
|
|
46017
|
+
});
|
|
46018
|
+
signal.throwIfAborted();
|
|
46019
|
+
const client = new XcodeMcpClient(spawn(executable, kind === "apple" ? ["mcpbridge"] : ["mcp"], {
|
|
46020
|
+
cwd,
|
|
46021
|
+
shell: false,
|
|
46022
|
+
stdio: "pipe",
|
|
46023
|
+
env: {
|
|
46024
|
+
...environment,
|
|
46025
|
+
...kind === "legacy" ? {
|
|
46026
|
+
XCODEBUILDMCP_CWD: cwd,
|
|
46027
|
+
XCODEBUILDMCP_DISABLE_SESSION_DEFAULTS: "true",
|
|
46028
|
+
XCODEBUILDMCP_ENABLED_WORKFLOWS: "simulator,ui-automation",
|
|
46029
|
+
XCODEBUILDMCP_SENTRY_DISABLED: "true"
|
|
46030
|
+
} : {}
|
|
46031
|
+
}
|
|
46032
|
+
}));
|
|
46033
|
+
try {
|
|
46034
|
+
const initialized = objectValue(await client.request("initialize", {
|
|
46035
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
46036
|
+
capabilities: {},
|
|
46037
|
+
clientInfo: {
|
|
46038
|
+
name: "p4code-device-adapter",
|
|
46039
|
+
version: "1"
|
|
46040
|
+
}
|
|
46041
|
+
}, signal));
|
|
46042
|
+
if (kind === "apple" && objectValue(initialized.serverInfo).name !== "xcode-tools") throw new Error("Selected Xcode did not expose Apple MCP tools.");
|
|
46043
|
+
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.`);
|
|
46044
|
+
client.child.stdin.write(JSON.stringify({
|
|
46045
|
+
jsonrpc: "2.0",
|
|
46046
|
+
method: "notifications/initialized"
|
|
46047
|
+
}) + "\n");
|
|
46048
|
+
let cursor;
|
|
46049
|
+
const cursors = /* @__PURE__ */ new Set();
|
|
46050
|
+
do {
|
|
46051
|
+
const page = objectValue(await client.request("tools/list", cursor ? { cursor } : {}, signal));
|
|
46052
|
+
if (!Array.isArray(page.tools)) throw new Error("Device MCP did not advertise tools.");
|
|
46053
|
+
for (const tool of page.tools) {
|
|
46054
|
+
const name = objectValue(tool).name;
|
|
46055
|
+
if (typeof name === "string") client.tools.add(name);
|
|
46056
|
+
}
|
|
46057
|
+
cursor = page.nextCursor;
|
|
46058
|
+
if (typeof cursor === "string" && cursor.length > 0) {
|
|
46059
|
+
if (cursors.has(cursor) || cursors.size >= MAX_TOOL_PAGES) throw new Error("Device MCP tool discovery did not finish within its page limit.");
|
|
46060
|
+
cursors.add(cursor);
|
|
46061
|
+
}
|
|
46062
|
+
} while (typeof cursor === "string" && cursor.length > 0);
|
|
46063
|
+
return client;
|
|
46064
|
+
} catch (error) {
|
|
46065
|
+
client.close();
|
|
46066
|
+
throw error;
|
|
46067
|
+
}
|
|
46068
|
+
}
|
|
46069
|
+
request(method, params, signal) {
|
|
46070
|
+
signal.throwIfAborted();
|
|
46071
|
+
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("Device MCP session is closed. Select the device again."));
|
|
46072
|
+
const id = ++this.sequence;
|
|
46073
|
+
return new Promise((resolve, reject) => {
|
|
46074
|
+
const abort = () => this.close(/* @__PURE__ */ new Error("Device MCP request cancelled. Select the device again."));
|
|
46075
|
+
const timer = setTimeout(() => this.close(/* @__PURE__ */ new Error("Device MCP timed out. Select the device again.")), REQUEST_TIMEOUT_MS$5);
|
|
46076
|
+
const finish = () => {
|
|
46077
|
+
clearTimeout(timer);
|
|
46078
|
+
signal.removeEventListener("abort", abort);
|
|
46079
|
+
};
|
|
46080
|
+
this.pending.set(id, {
|
|
46081
|
+
resolve: (result) => {
|
|
46082
|
+
finish();
|
|
46083
|
+
resolve(result);
|
|
46084
|
+
},
|
|
46085
|
+
reject: (error) => {
|
|
46086
|
+
finish();
|
|
46087
|
+
reject(error);
|
|
46088
|
+
}
|
|
46089
|
+
});
|
|
46090
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
46091
|
+
this.child.stdin.write(JSON.stringify({
|
|
46092
|
+
jsonrpc: "2.0",
|
|
46093
|
+
id,
|
|
46094
|
+
method,
|
|
46095
|
+
params
|
|
46096
|
+
}) + "\n");
|
|
46097
|
+
});
|
|
46098
|
+
}
|
|
46099
|
+
async call(name, args, signal) {
|
|
46100
|
+
if (!this.tools.has(name)) throw new Error(`Selected device MCP service does not expose ${name}. Check adapter capabilities and Xcode setup.`);
|
|
46101
|
+
const redact = (error) => {
|
|
46102
|
+
let message = error instanceof Error ? error.message : String(error);
|
|
46103
|
+
for (const [key, value] of Object.entries(args)) if (/sessionkey$/i.test(key) && typeof value === "string" && value.length > 0) message = message.replaceAll(value, "[redacted]");
|
|
46104
|
+
return new Error(message);
|
|
46105
|
+
};
|
|
46106
|
+
try {
|
|
46107
|
+
const result = objectValue(await this.request("tools/call", {
|
|
46108
|
+
name,
|
|
46109
|
+
arguments: args
|
|
46110
|
+
}, signal));
|
|
46111
|
+
const structured = objectValue(result.structuredContent);
|
|
46112
|
+
if (result.isError === true || structured.didError === true) {
|
|
46113
|
+
const detail = (Array.isArray(result.content) ? result.content : []).map((part) => objectValue(part).text).filter((part) => typeof part === "string").join("\n");
|
|
46114
|
+
throw new Error(detail || String(structured.error ?? `${name} failed. Refresh device state before retrying.`));
|
|
46115
|
+
}
|
|
46116
|
+
return result;
|
|
46117
|
+
} catch (error) {
|
|
46118
|
+
throw redact(error);
|
|
46119
|
+
}
|
|
46120
|
+
}
|
|
46121
|
+
close(error = /* @__PURE__ */ new Error("Device MCP session closed.")) {
|
|
46122
|
+
if (this.closed) return;
|
|
46123
|
+
this.closed = true;
|
|
46124
|
+
for (const call of this.pending.values()) call.reject(error);
|
|
46125
|
+
this.pending.clear();
|
|
46126
|
+
this.child.kill("SIGTERM");
|
|
46127
|
+
}
|
|
46128
|
+
};
|
|
46129
|
+
//#endregion
|
|
46130
|
+
//#region src/device/XcodeDeviceAdapter.ts
|
|
46131
|
+
const SimulatorList = Schema$1.Struct({ simulators: Schema$1.Array(Schema$1.Struct({
|
|
46132
|
+
simulatorId: Schema$1.String,
|
|
46133
|
+
name: Schema$1.String,
|
|
46134
|
+
runtime: Schema$1.String,
|
|
46135
|
+
state: Schema$1.String,
|
|
46136
|
+
isAvailable: Schema$1.Boolean
|
|
46137
|
+
})) });
|
|
46138
|
+
const decodeSimulators = Schema$1.decodeUnknownSync(SimulatorList);
|
|
46139
|
+
const MAX_LEASES = 32;
|
|
46140
|
+
const Frame = Schema$1.Struct({
|
|
46141
|
+
x: Schema$1.Number,
|
|
46142
|
+
y: Schema$1.Number,
|
|
46143
|
+
width: Schema$1.Number,
|
|
46144
|
+
height: Schema$1.Number
|
|
46145
|
+
});
|
|
46146
|
+
const Snapshot = Schema$1.Struct({ elements: Schema$1.Array(Schema$1.Struct({
|
|
46147
|
+
ref: Schema$1.String,
|
|
46148
|
+
role: Schema$1.optional(Schema$1.String),
|
|
46149
|
+
frame: Frame
|
|
46150
|
+
})) });
|
|
46151
|
+
const readSnapshot = Schema$1.decodeUnknownOption(Snapshot);
|
|
46152
|
+
/** Retain structured semantic snapshots; callers never need screenshot round trips for refs. */
|
|
46153
|
+
function xcodeResultData(result) {
|
|
46154
|
+
const structured = objectValue(result.structuredContent);
|
|
46155
|
+
return objectValue(structured.data ?? structured);
|
|
46156
|
+
}
|
|
46157
|
+
function createXcodeDeviceAdapter(executable, cwd, environment) {
|
|
46158
|
+
const clients = /* @__PURE__ */ new Map();
|
|
46159
|
+
const recordings = /* @__PURE__ */ new Map();
|
|
46160
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
46161
|
+
const remember = (session, data) => {
|
|
46162
|
+
const snapshot = readSnapshot(data.capture);
|
|
46163
|
+
if (snapshot._tag === "Some") snapshots.set(session.leaseId, snapshot.value);
|
|
46164
|
+
const viewport = snapshots.get(session.leaseId)?.elements.find((element) => element.role === "window")?.frame;
|
|
46165
|
+
return {
|
|
46166
|
+
...data,
|
|
46167
|
+
...viewport ? { viewport } : {}
|
|
46168
|
+
};
|
|
46169
|
+
};
|
|
46170
|
+
const get = async (key, signal) => {
|
|
46171
|
+
let pending = clients.get(key);
|
|
46172
|
+
if (!pending) {
|
|
46173
|
+
if (clients.size >= MAX_LEASES) throw new Error("Close an existing Xcode device session before opening another.");
|
|
46174
|
+
pending = XcodeMcpClient.start(executable, cwd, environment, signal);
|
|
46175
|
+
clients.set(key, pending);
|
|
46176
|
+
pending.catch(() => {
|
|
46177
|
+
if (clients.get(key) === pending) clients.delete(key);
|
|
46178
|
+
});
|
|
46179
|
+
}
|
|
46180
|
+
return pending;
|
|
46181
|
+
};
|
|
46182
|
+
const run = (operation, execute) => Effect.tryPromise({
|
|
46183
|
+
try: execute,
|
|
46184
|
+
catch: (cause) => new DeviceOperationError({
|
|
46185
|
+
operation,
|
|
46186
|
+
reason: "request_failed",
|
|
46187
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
46188
|
+
})
|
|
46189
|
+
});
|
|
46190
|
+
const forSession = async (session, signal) => {
|
|
46191
|
+
if (session.hostId !== "local" || session.platform !== "ios" || !session.leaseId) throw new Error("Xcode adapter requires an exact local iOS Simulator lease.");
|
|
46192
|
+
return get(session.leaseId, signal);
|
|
46193
|
+
};
|
|
46194
|
+
const closeSession = (leaseId) => {
|
|
46195
|
+
const client = clients.get(leaseId);
|
|
46196
|
+
clients.delete(leaseId);
|
|
46197
|
+
const recording = recordings.get(leaseId);
|
|
46198
|
+
recordings.delete(leaseId);
|
|
46199
|
+
snapshots.delete(leaseId);
|
|
46200
|
+
client?.then(async (client) => {
|
|
46201
|
+
try {
|
|
46202
|
+
if (recording) await client.call("record_sim_video", {
|
|
46203
|
+
...recording,
|
|
46204
|
+
stop: true
|
|
46205
|
+
}, new AbortController().signal);
|
|
46206
|
+
} finally {
|
|
46207
|
+
client.close();
|
|
46208
|
+
}
|
|
46209
|
+
}).catch(() => {});
|
|
46210
|
+
};
|
|
46211
|
+
return {
|
|
46212
|
+
discover: run("list", async (signal) => {
|
|
46213
|
+
const client = await get("discovery", signal);
|
|
46214
|
+
return decodeSimulators(xcodeResultData(await client.call("list_sims", {}, signal))).simulators.filter((sim) => sim.isAvailable && /iOS/i.test(sim.runtime)).map((sim) => ({
|
|
46215
|
+
id: sim.simulatorId,
|
|
46216
|
+
hostId: "local",
|
|
46217
|
+
name: sim.name,
|
|
46218
|
+
version: sim.runtime,
|
|
46219
|
+
booted: sim.state === "Booted",
|
|
46220
|
+
physical: false,
|
|
46221
|
+
platform: "ios"
|
|
46222
|
+
}));
|
|
46223
|
+
}),
|
|
46224
|
+
state: (session, sinceScreenHash) => run("state", async (signal) => {
|
|
46225
|
+
const client = await forSession(session, signal);
|
|
46226
|
+
const data = xcodeResultData(await client.call("snapshot_ui", {
|
|
46227
|
+
simulatorId: session.deviceId,
|
|
46228
|
+
...sinceScreenHash ? { sinceScreenHash } : {}
|
|
46229
|
+
}, signal));
|
|
46230
|
+
return {
|
|
46231
|
+
adapter: "xcodebuildmcp",
|
|
46232
|
+
capabilities: [...client.tools].filter((tool) => [
|
|
46233
|
+
"tap",
|
|
46234
|
+
"type_text",
|
|
46235
|
+
"swipe",
|
|
46236
|
+
"drag",
|
|
46237
|
+
"batch",
|
|
46238
|
+
"snapshot_ui",
|
|
46239
|
+
"screenshot",
|
|
46240
|
+
"record_sim_video"
|
|
46241
|
+
].includes(tool)),
|
|
46242
|
+
...remember(session, data)
|
|
46243
|
+
};
|
|
46244
|
+
}),
|
|
46245
|
+
action: (session, input) => run("command", async (signal) => {
|
|
46246
|
+
const client = await forSession(session, signal);
|
|
46247
|
+
const { action, ...parameters } = input;
|
|
46248
|
+
const snapshot = snapshots.get(session.leaseId);
|
|
46249
|
+
const ref = input.action === "batch" ? input.steps.at(-1)?.elementRef : input.action === "swipe" ? input.withinElementRef : input.elementRef;
|
|
46250
|
+
const bounds = snapshot?.elements.find((element) => element.ref === ref)?.frame;
|
|
46251
|
+
const viewport = snapshot?.elements.find((element) => element.role === "window")?.frame;
|
|
46252
|
+
const data = xcodeResultData(await client.call(action, {
|
|
46253
|
+
...parameters,
|
|
46254
|
+
simulatorId: session.deviceId
|
|
46255
|
+
}, signal));
|
|
46256
|
+
return {
|
|
46257
|
+
...remember(session, data),
|
|
46258
|
+
...bounds && viewport && viewport.width > 0 && viewport.height > 0 ? { pointer: {
|
|
46259
|
+
x: (bounds.x + bounds.width / 2 - viewport.x) / viewport.width,
|
|
46260
|
+
y: (bounds.y + bounds.height / 2 - viewport.y) / viewport.height
|
|
46261
|
+
} } : {}
|
|
46262
|
+
};
|
|
46263
|
+
}),
|
|
46264
|
+
screenshot: (session) => run("screenshot", async (signal) => {
|
|
46265
|
+
const result = await (await forSession(session, signal)).call("screenshot", {
|
|
46266
|
+
simulatorId: session.deviceId,
|
|
46267
|
+
returnFormat: "base64"
|
|
46268
|
+
}, signal);
|
|
46269
|
+
const image = (Array.isArray(result.content) ? result.content : []).map(objectValue).find((part) => part.type === "image" && part.mimeType === "image/png");
|
|
46270
|
+
if (typeof image?.data !== "string") throw new Error("XcodeBuildMCP did not return a PNG screenshot.");
|
|
46271
|
+
const png = Buffer.from(image.data, "base64");
|
|
46272
|
+
return {
|
|
46273
|
+
png,
|
|
46274
|
+
...pngDimensions(png)
|
|
46275
|
+
};
|
|
46276
|
+
}),
|
|
46277
|
+
recording: (session, action) => run("recording", async (signal) => {
|
|
46278
|
+
const client = await forSession(session, signal);
|
|
46279
|
+
const key = session.leaseId;
|
|
46280
|
+
if (action === "start" && recordings.has(key)) throw new Error("This device session is already recording.");
|
|
46281
|
+
if (action === "stop" && !recordings.has(key)) throw new Error("This device session has no active recording.");
|
|
46282
|
+
const outputFile = recordings.get(key)?.outputFile ?? `${cwd}/device-recording-${randomUUID()}.mp4`;
|
|
46283
|
+
const result = xcodeResultData(await client.call("record_sim_video", {
|
|
46284
|
+
simulatorId: session.deviceId,
|
|
46285
|
+
...action === "start" ? { start: true } : {
|
|
46286
|
+
stop: true,
|
|
46287
|
+
outputFile
|
|
46288
|
+
}
|
|
46289
|
+
}, signal));
|
|
46290
|
+
if (action === "start") recordings.set(key, {
|
|
46291
|
+
outputFile,
|
|
46292
|
+
simulatorId: session.deviceId
|
|
46293
|
+
});
|
|
46294
|
+
else recordings.delete(key);
|
|
46295
|
+
return result;
|
|
46296
|
+
}),
|
|
46297
|
+
closeSession,
|
|
46298
|
+
stop: () => {
|
|
46299
|
+
for (const key of clients.keys()) closeSession(key);
|
|
46300
|
+
}
|
|
46301
|
+
};
|
|
46302
|
+
}
|
|
46303
|
+
//#endregion
|
|
46304
|
+
//#region src/device/AppleDeviceAdapter.ts
|
|
46305
|
+
const MAX_HIERARCHY_BYTES = 512 * 1024;
|
|
46306
|
+
const MAX_SESSIONS = 32;
|
|
46307
|
+
const REQUIRED_DEVICE_TOOLS = [
|
|
46308
|
+
"DeviceInteractionStartSession",
|
|
46309
|
+
"DeviceInteractionSynthesize",
|
|
46310
|
+
"DeviceInteractionEndSession"
|
|
46311
|
+
];
|
|
46312
|
+
const FRAME_PATTERN = /\{\{(-?[\d.]+),\s*(-?[\d.]+)\},\s*\{([\d.]+),\s*([\d.]+)\}\}/;
|
|
46313
|
+
const HIT_PATTERN = /hitPoint:\s*\{(-?[\d.]+),\s*(-?[\d.]+)\}/;
|
|
46314
|
+
const Capture = Schema$1.Struct({
|
|
46315
|
+
hierarchyPath: Schema$1.String,
|
|
46316
|
+
screenshotPath: Schema$1.String
|
|
46317
|
+
});
|
|
46318
|
+
const Session = Schema$1.Struct({
|
|
46319
|
+
interactionSessionKey: Schema$1.String,
|
|
46320
|
+
deviceUUID: Schema$1.String,
|
|
46321
|
+
deviceIsSimulator: Schema$1.Boolean
|
|
46322
|
+
});
|
|
46323
|
+
const Simulators = Schema$1.Struct({ devices: Schema$1.Record(Schema$1.String, Schema$1.Array(Schema$1.Struct({
|
|
46324
|
+
udid: Schema$1.String,
|
|
46325
|
+
name: Schema$1.String,
|
|
46326
|
+
state: Schema$1.String,
|
|
46327
|
+
isAvailable: Schema$1.Boolean
|
|
46328
|
+
}))) });
|
|
46329
|
+
function appleMcpData(result) {
|
|
46330
|
+
if (result.structuredContent) return objectValue(result.structuredContent);
|
|
46331
|
+
for (const block of Array.isArray(result.content) ? result.content : []) {
|
|
46332
|
+
const text = objectValue(block).text;
|
|
46333
|
+
if (typeof text !== "string") continue;
|
|
46334
|
+
try {
|
|
46335
|
+
return objectValue(JSON.parse(text));
|
|
46336
|
+
} catch {}
|
|
46337
|
+
}
|
|
46338
|
+
throw new Error("Apple MCP returned no structured device result.");
|
|
46339
|
+
}
|
|
46340
|
+
/** Use Apple's documented hierarchy hit points, never guessed screenshot coordinates. */
|
|
46341
|
+
function appleHierarchySnapshot(text, sequence) {
|
|
46342
|
+
const elements = text.split("\n").flatMap((line, index) => {
|
|
46343
|
+
const frame = line.match(FRAME_PATTERN);
|
|
46344
|
+
const hit = line.match(HIT_PATTERN);
|
|
46345
|
+
if (!frame || !hit || line.includes("isRemoteLeafPlaceholder")) return [];
|
|
46346
|
+
const [x, y, width, height] = frame.slice(1).map(Number);
|
|
46347
|
+
const [hx, hy] = hit.slice(1).map(Number);
|
|
46348
|
+
if (![
|
|
46349
|
+
x,
|
|
46350
|
+
y,
|
|
46351
|
+
width,
|
|
46352
|
+
height,
|
|
46353
|
+
hx,
|
|
46354
|
+
hy
|
|
46355
|
+
].every((value) => value !== void 0 && Number.isFinite(value)) || width <= 0 || height <= 0) return [];
|
|
46356
|
+
return [{
|
|
46357
|
+
ref: `a${sequence}-${index}`,
|
|
46358
|
+
label: line.trim(),
|
|
46359
|
+
frame: {
|
|
46360
|
+
x,
|
|
46361
|
+
y,
|
|
46362
|
+
width,
|
|
46363
|
+
height
|
|
46364
|
+
},
|
|
46365
|
+
hitPoint: {
|
|
46366
|
+
x: hx,
|
|
46367
|
+
y: hy
|
|
46368
|
+
},
|
|
46369
|
+
role: /\b(?:UI)?Window\b/.test(line) ? "window" : "element",
|
|
46370
|
+
actions: ["tap"]
|
|
46371
|
+
}];
|
|
46372
|
+
});
|
|
46373
|
+
return {
|
|
46374
|
+
screenHash: createHash("sha256").update(text).digest("hex"),
|
|
46375
|
+
seq: sequence,
|
|
46376
|
+
elements
|
|
46377
|
+
};
|
|
46378
|
+
}
|
|
46379
|
+
function createAppleDeviceAdapter(input) {
|
|
46380
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
46381
|
+
const fail = (operation) => (cause) => new DeviceOperationError({
|
|
46382
|
+
operation,
|
|
46383
|
+
reason: "request_failed",
|
|
46384
|
+
cause: cause instanceof Error ? cause.message : String(cause)
|
|
46385
|
+
});
|
|
46386
|
+
const call = (client, name, args) => Effect.tryPromise({
|
|
46387
|
+
try: (signal) => client.call(name, args, signal),
|
|
46388
|
+
catch: fail(name)
|
|
46389
|
+
});
|
|
46390
|
+
const closeSession = (leaseId) => {
|
|
46391
|
+
const pending = sessions.get(leaseId);
|
|
46392
|
+
sessions.delete(leaseId);
|
|
46393
|
+
pending?.then(async (owned) => {
|
|
46394
|
+
try {
|
|
46395
|
+
await owned.client.call("DeviceInteractionEndSession", { interactionSessionKey: owned.key }, new AbortController().signal);
|
|
46396
|
+
} finally {
|
|
46397
|
+
owned.client.close();
|
|
46398
|
+
}
|
|
46399
|
+
}).catch(() => {});
|
|
46400
|
+
};
|
|
46401
|
+
const get = (session) => Effect.tryPromise({
|
|
46402
|
+
try: async (signal) => {
|
|
46403
|
+
if (session.hostId !== "local" || session.platform !== "ios" || !session.leaseId) throw new Error("Apple MCP requires an exact local iOS device lease.");
|
|
46404
|
+
let pending = sessions.get(session.leaseId);
|
|
46405
|
+
if (!pending) {
|
|
46406
|
+
if (sessions.size >= MAX_SESSIONS) throw new Error("Close an existing device session first.");
|
|
46407
|
+
pending = (async () => {
|
|
46408
|
+
const client = await XcodeMcpClient.start("/usr/bin/xcrun", input.cwd, {
|
|
46409
|
+
...input.environment,
|
|
46410
|
+
MCP_XCODE_SESSION_ID: randomUUID()
|
|
46411
|
+
}, signal, "apple");
|
|
46412
|
+
try {
|
|
46413
|
+
const missing = REQUIRED_DEVICE_TOOLS.filter((tool) => !client.tools.has(tool));
|
|
46414
|
+
if (missing.length > 0) throw new Error(`Selected Xcode lacks required device tools: ${missing.join(", ")}. No device session was opened.`);
|
|
46415
|
+
if (input.workspacePath) await client.call("XcodeOpenWorkspace", { path: input.workspacePath }, signal);
|
|
46416
|
+
const opened = Schema$1.decodeUnknownSync(Session)(appleMcpData(await client.call("DeviceInteractionStartSession", {
|
|
46417
|
+
deviceIdentifier: session.deviceId,
|
|
46418
|
+
sessionIdentifier: `P4 Device ${session.threadId.slice(0, 8)}`
|
|
46419
|
+
}, signal)));
|
|
46420
|
+
if (opened.deviceUUID.toLowerCase() !== session.deviceId.toLowerCase() || !opened.deviceIsSimulator) {
|
|
46421
|
+
await client.call("DeviceInteractionEndSession", { interactionSessionKey: opened.interactionSessionKey }, signal);
|
|
46422
|
+
throw new Error("Apple selected a different device. No interaction was performed.");
|
|
46423
|
+
}
|
|
46424
|
+
return {
|
|
46425
|
+
client,
|
|
46426
|
+
key: opened.interactionSessionKey,
|
|
46427
|
+
sequence: 0
|
|
46428
|
+
};
|
|
46429
|
+
} catch (error) {
|
|
46430
|
+
client.close();
|
|
46431
|
+
throw error;
|
|
46432
|
+
}
|
|
46433
|
+
})();
|
|
46434
|
+
sessions.set(session.leaseId, pending);
|
|
46435
|
+
const leaseId = session.leaseId;
|
|
46436
|
+
pending.catch(() => {
|
|
46437
|
+
if (sessions.get(leaseId) === pending) sessions.delete(leaseId);
|
|
46438
|
+
});
|
|
46439
|
+
}
|
|
46440
|
+
return pending;
|
|
46441
|
+
},
|
|
46442
|
+
catch: fail("open")
|
|
46443
|
+
});
|
|
46444
|
+
const capture = (owned, interactionCommand) => Effect.gen(function* () {
|
|
46445
|
+
const response = yield* call(owned.client, "DeviceInteractionSynthesize", {
|
|
46446
|
+
interactSessionKey: owned.key,
|
|
46447
|
+
...interactionCommand ? { interactionCommand } : {}
|
|
46448
|
+
});
|
|
46449
|
+
const data = yield* Effect.try({
|
|
46450
|
+
try: () => appleMcpData(response),
|
|
46451
|
+
catch: fail("state")
|
|
46452
|
+
});
|
|
46453
|
+
const result = yield* Schema$1.decodeUnknownEffect(Capture)(data);
|
|
46454
|
+
if ((yield* input.fs.stat(result.hierarchyPath)).size > BigInt(MAX_HIERARCHY_BYTES)) return yield* fail("state")("Apple hierarchy exceeds the supported size.");
|
|
46455
|
+
const snapshot = appleHierarchySnapshot(yield* input.fs.readFileString(result.hierarchyPath), owned.sequence + 1);
|
|
46456
|
+
if (owned.snapshot?.screenHash !== snapshot.screenHash) {
|
|
46457
|
+
owned.snapshot = snapshot;
|
|
46458
|
+
owned.sequence += 1;
|
|
46459
|
+
}
|
|
46460
|
+
const currentSnapshot = owned.snapshot;
|
|
46461
|
+
owned.image = result.screenshotPath;
|
|
46462
|
+
const viewport = currentSnapshot.elements.find((element) => element.role === "window")?.frame;
|
|
46463
|
+
return {
|
|
46464
|
+
adapter: "apple-xcode",
|
|
46465
|
+
capabilities: [
|
|
46466
|
+
"snapshot_ui",
|
|
46467
|
+
"tap",
|
|
46468
|
+
"screenshot"
|
|
46469
|
+
],
|
|
46470
|
+
capture: currentSnapshot,
|
|
46471
|
+
...viewport ? { viewport } : {}
|
|
46472
|
+
};
|
|
46473
|
+
}).pipe(Effect.mapError(fail("state")));
|
|
46474
|
+
return {
|
|
46475
|
+
diagnose: Effect.gen(function* () {
|
|
46476
|
+
const result = yield* input.run("xcrun", [
|
|
46477
|
+
"mcp-server",
|
|
46478
|
+
"status",
|
|
46479
|
+
"--format",
|
|
46480
|
+
"json"
|
|
46481
|
+
]);
|
|
46482
|
+
if (result.code !== 0) return "Apple MCP did not respond. Check the selected Xcode installation and MCP service.";
|
|
46483
|
+
const status = objectValue(yield* Schema$1.decodeUnknownEffect(Schema$1.UnknownFromJsonString)(result.stdout));
|
|
46484
|
+
const permission = objectValue(status.permission);
|
|
46485
|
+
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.";
|
|
46486
|
+
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.";
|
|
46487
|
+
return "Apple MCP did not respond in time. Check Xcode agent/folder permissions and session logs before selecting the device again.";
|
|
46488
|
+
}).pipe(Effect.orElseSucceed(() => "Apple MCP did not respond. Check Xcode agent/folder permissions and service status.")),
|
|
46489
|
+
discover: Effect.gen(function* () {
|
|
46490
|
+
const result = yield* input.run("xcrun", [
|
|
46491
|
+
"simctl",
|
|
46492
|
+
"list",
|
|
46493
|
+
"devices",
|
|
46494
|
+
"available",
|
|
46495
|
+
"--json"
|
|
46496
|
+
]);
|
|
46497
|
+
if (result.code !== 0) return yield* fail("list")("Selected Xcode cannot list simulators.");
|
|
46498
|
+
const parsed = yield* Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(Simulators))(result.stdout).pipe(Effect.mapError(fail("list")));
|
|
46499
|
+
return Object.entries(parsed.devices).filter(([runtime]) => runtime.includes(".iOS-")).flatMap(([runtime, devices]) => devices.filter((device) => device.isAvailable).map((device) => ({
|
|
46500
|
+
hostId: "local",
|
|
46501
|
+
id: device.udid,
|
|
46502
|
+
platform: "ios",
|
|
46503
|
+
name: device.name,
|
|
46504
|
+
version: runtime.replace("com.apple.CoreSimulator.SimRuntime.", "").replaceAll("-", "."),
|
|
46505
|
+
physical: false,
|
|
46506
|
+
booted: device.state === "Booted"
|
|
46507
|
+
})));
|
|
46508
|
+
}),
|
|
46509
|
+
state: (session, sinceScreenHash) => Effect.gen(function* () {
|
|
46510
|
+
const result = yield* capture(yield* get(session));
|
|
46511
|
+
return sinceScreenHash === result.capture.screenHash ? {
|
|
46512
|
+
...result,
|
|
46513
|
+
capture: {
|
|
46514
|
+
screenHash: result.capture.screenHash,
|
|
46515
|
+
seq: result.capture.seq,
|
|
46516
|
+
unchanged: true
|
|
46517
|
+
}
|
|
46518
|
+
} : result;
|
|
46519
|
+
}),
|
|
46520
|
+
action: (session, action) => Effect.gen(function* () {
|
|
46521
|
+
if (action.action !== "tap") return yield* fail("command")("This Apple MCP integration currently advertises tap only. Use only capabilities returned by getAXState().");
|
|
46522
|
+
const owned = yield* get(session);
|
|
46523
|
+
const previous = owned.snapshot;
|
|
46524
|
+
const element = previous?.elements.find((element) => element.ref === action.elementRef);
|
|
46525
|
+
if (!element) return yield* fail("command")("Stale device ref. Read getAXState() and choose a current element.");
|
|
46526
|
+
const fresh = yield* capture(owned);
|
|
46527
|
+
if (fresh.capture.screenHash !== previous.screenHash) return yield* fail("command")("Device layout changed. Read getAXState() and choose a current element; no action was performed.");
|
|
46528
|
+
return {
|
|
46529
|
+
...yield* capture(owned, `t ${element.hitPoint.x} ${element.hitPoint.y}`),
|
|
46530
|
+
...fresh.viewport ? { pointer: {
|
|
46531
|
+
x: (element.hitPoint.x - fresh.viewport.x) / fresh.viewport.width,
|
|
46532
|
+
y: (element.hitPoint.y - fresh.viewport.y) / fresh.viewport.height
|
|
46533
|
+
} } : {}
|
|
46534
|
+
};
|
|
46535
|
+
}),
|
|
46536
|
+
screenshot: (session) => Effect.gen(function* () {
|
|
46537
|
+
const owned = yield* get(session);
|
|
46538
|
+
yield* capture(owned);
|
|
46539
|
+
if ((yield* input.fs.stat(owned.image)).size > BigInt(8388608)) return yield* fail("screenshot")("Apple screenshot exceeds the supported size.");
|
|
46540
|
+
const png = Buffer.from(yield* input.fs.readFile(owned.image));
|
|
46541
|
+
return {
|
|
46542
|
+
png,
|
|
46543
|
+
...yield* Effect.try({
|
|
46544
|
+
try: () => pngDimensions(png),
|
|
46545
|
+
catch: fail("screenshot")
|
|
46546
|
+
})
|
|
46547
|
+
};
|
|
46548
|
+
}).pipe(Effect.mapError(fail("screenshot"))),
|
|
46549
|
+
recording: (_session, _action) => Effect.fail(fail("recording")("Apple MCP does not advertise a recording tool. Recording is unavailable through this adapter.")),
|
|
46550
|
+
closeSession,
|
|
46551
|
+
stop: () => {
|
|
46552
|
+
for (const key of sessions.keys()) closeSession(key);
|
|
46553
|
+
}
|
|
46554
|
+
};
|
|
46555
|
+
}
|
|
46556
|
+
//#endregion
|
|
45747
46557
|
//#region src/device/DeviceToolchain.ts
|
|
45748
46558
|
/**
|
|
45749
46559
|
* Pinned installs of the two external tools device support is built on.
|
|
@@ -45959,7 +46769,8 @@ function deviceHubPlatform(platforms) {
|
|
|
45959
46769
|
const COMMAND_TIMEOUT_MS$1 = 2e4;
|
|
45960
46770
|
const COMMAND_OUTPUT_LIMIT = 1024 * 1024;
|
|
45961
46771
|
const COMMAND_FAILURE_CODE = 127;
|
|
45962
|
-
const
|
|
46772
|
+
const APPLE_DEVICE_MCP_MINIMUM_XCODE = 27;
|
|
46773
|
+
const make$81 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
45963
46774
|
const config = yield* ServerConfig$1;
|
|
45964
46775
|
const fs = yield* FileSystem.FileSystem;
|
|
45965
46776
|
const path = yield* Path$1.Path;
|
|
@@ -46005,7 +46816,19 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46005
46816
|
stderr: "Device command failed.",
|
|
46006
46817
|
code: COMMAND_FAILURE_CODE
|
|
46007
46818
|
})));
|
|
46819
|
+
const appleVersion = (platform === "darwin" ? yield* run("xcrun", ["--find", "mcpbridge"]) : null)?.code === 0 ? yield* run("xcodebuild", ["-version"]) : null;
|
|
46820
|
+
const appleAvailable = appleVersion?.code === 0 && Number(appleVersion.stdout.match(/^Xcode (\d+)\b/m)?.[1]) >= APPLE_DEVICE_MCP_MINIMUM_XCODE;
|
|
46821
|
+
const legacyPath = environment.P4CODE_XCODEBUILDMCP_PATH?.trim();
|
|
46822
|
+
const useLegacy = environment.P4CODE_DEVICE_ADAPTER === "xcodebuildmcp";
|
|
46823
|
+
const xcode = appleAvailable && !useLegacy ? createAppleDeviceAdapter({
|
|
46824
|
+
cwd: config.stateDir,
|
|
46825
|
+
environment: hostEnvironment,
|
|
46826
|
+
fs,
|
|
46827
|
+
run,
|
|
46828
|
+
...environment.P4CODE_APPLE_MCP_WORKSPACE ? { workspacePath: environment.P4CODE_APPLE_MCP_WORKSPACE } : {}
|
|
46829
|
+
}) : platform === "darwin" && legacyPath ? createXcodeDeviceAdapter(legacyPath, config.stateDir, hostEnvironment) : void 0;
|
|
46008
46830
|
const stopAgent = Effect.suspend(() => {
|
|
46831
|
+
xcode?.stop();
|
|
46009
46832
|
const owned = agent;
|
|
46010
46833
|
agent = null;
|
|
46011
46834
|
return owned?.stop ?? Effect.void;
|
|
@@ -46044,7 +46867,8 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46044
46867
|
nodePath: process.execPath,
|
|
46045
46868
|
hub: { origin: started.origin },
|
|
46046
46869
|
helpers,
|
|
46047
|
-
run
|
|
46870
|
+
run,
|
|
46871
|
+
...xcode ? { xcode } : {}
|
|
46048
46872
|
};
|
|
46049
46873
|
return ready;
|
|
46050
46874
|
}, Effect.onExit((exit) => Exit.isFailure(exit) ? stop : Effect.void));
|
|
@@ -46055,6 +46879,10 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46055
46879
|
id: hostId,
|
|
46056
46880
|
kind: "local",
|
|
46057
46881
|
label: "This machine",
|
|
46882
|
+
...xcode ? { automation: {
|
|
46883
|
+
provider: appleAvailable && !useLegacy ? "apple-xcode" : "xcodebuildmcp",
|
|
46884
|
+
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."
|
|
46885
|
+
} } : {},
|
|
46058
46886
|
platforms: yield* Effect.all([platformAvailability("ios"), platformAvailability("android")]),
|
|
46059
46887
|
hubInstalled: yield* isDeviceHubInstalled(config.baseDir).pipe(Effect.provideContext(context)),
|
|
46060
46888
|
agentDeviceInstalled: yield* isAgentDeviceInstalled(config.baseDir).pipe(Effect.provideContext(context))
|
|
@@ -46066,8 +46894,9 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46066
46894
|
return ready;
|
|
46067
46895
|
})),
|
|
46068
46896
|
ensureReady: (onPhase) => lock.withPermit(ensureHub(onPhase)),
|
|
46069
|
-
ensureAgentReady: (onPhase) => lock.withPermit(Effect.gen(function* () {
|
|
46897
|
+
ensureAgentReady: (onPhase, devicePlatform) => lock.withPermit(Effect.gen(function* () {
|
|
46070
46898
|
const host = yield* ensureHub(onPhase);
|
|
46899
|
+
if (xcode && devicePlatform === "ios") return host;
|
|
46071
46900
|
if (agent && (yield* agent.isRunning)) return {
|
|
46072
46901
|
...host,
|
|
46073
46902
|
agentDevice: agent.endpoint
|
|
@@ -46094,7 +46923,7 @@ const make$82 = Effect.fn("LocalDeviceHost.make")(function* () {
|
|
|
46094
46923
|
stop: lock.withPermit(stop)
|
|
46095
46924
|
};
|
|
46096
46925
|
});
|
|
46097
|
-
Layer.effect(DeviceHost, make$
|
|
46926
|
+
Layer.effect(DeviceHost, make$81()).pipe(Layer.provide(layer$73));
|
|
46098
46927
|
Data.TaggedError("SshHostDiscoveryError");
|
|
46099
46928
|
var SshInvalidTargetError = class extends Data.TaggedError("SshInvalidTargetError") {};
|
|
46100
46929
|
var SshCommandError = class extends Data.TaggedError("SshCommandError") {};
|
|
@@ -46953,7 +47782,7 @@ const probeSshDeviceHost = Effect.fn("SshDeviceDiagnostics.probe")(function* (co
|
|
|
46953
47782
|
//#endregion
|
|
46954
47783
|
//#region src/device/SshDeviceHost.ts
|
|
46955
47784
|
const DIAGNOSTICS_CACHE_MS = 1e4;
|
|
46956
|
-
const make$
|
|
47785
|
+
const make$80 = Effect.fn("SshDeviceHost.make")(function* (input) {
|
|
46957
47786
|
const config = yield* Schema$1.decodeUnknownEffect(SshDeviceHostConfig)({ ...input }).pipe(Effect.mapError(() => new DeviceHostError({
|
|
46958
47787
|
hostId: input.id,
|
|
46959
47788
|
step: "configuring SSH device host",
|
|
@@ -47095,6 +47924,14 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47095
47924
|
const scope = yield* Scope.Scope;
|
|
47096
47925
|
const registry = yield* make$83(input);
|
|
47097
47926
|
const commands = yield* make$85();
|
|
47927
|
+
const adapterLeases = /* @__PURE__ */ new Map();
|
|
47928
|
+
const closeCommands = (sessions) => Effect.sync(() => {
|
|
47929
|
+
for (const session of sessions) {
|
|
47930
|
+
if (!session.leaseId) continue;
|
|
47931
|
+
adapterLeases.get(session.leaseId)?.();
|
|
47932
|
+
adapterLeases.delete(session.leaseId);
|
|
47933
|
+
}
|
|
47934
|
+
}).pipe(Effect.andThen(commands.close(sessions)));
|
|
47098
47935
|
const threads = yield* make$84();
|
|
47099
47936
|
const changes = yield* Effect.acquireRelease(PubSub.sliding(STATE_BUFFER_SIZE), PubSub.shutdown);
|
|
47100
47937
|
const stateLock = yield* Semaphore.make(1);
|
|
@@ -47132,7 +47969,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47132
47969
|
}));
|
|
47133
47970
|
}
|
|
47134
47971
|
const retireSessions = (ids) => Effect.gen(function* () {
|
|
47135
|
-
yield*
|
|
47972
|
+
yield* closeCommands(state.sessions.filter((session) => ids.includes(session.hostId)));
|
|
47136
47973
|
yield* update((current) => {
|
|
47137
47974
|
for (const id of ids) hostLocks.delete(id);
|
|
47138
47975
|
return {
|
|
@@ -47343,7 +48180,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47343
48180
|
return yield* withHost(value.hostId, (lease) => Effect.gen(function* () {
|
|
47344
48181
|
let found = yield* freshDevice(lease, value);
|
|
47345
48182
|
if (access === "agent") {
|
|
47346
|
-
yield* lease.host.ensureAgentReady(() => Effect.void);
|
|
48183
|
+
yield* lease.host.ensureAgentReady(() => Effect.void, value.platform);
|
|
47347
48184
|
yield* registry.commit(lease, () => void 0);
|
|
47348
48185
|
}
|
|
47349
48186
|
if (!found.device.booted && value.boot !== false) {
|
|
@@ -47382,7 +48219,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47382
48219
|
openedAt,
|
|
47383
48220
|
leaseId: NodeCrypto.randomUUID()
|
|
47384
48221
|
};
|
|
47385
|
-
yield*
|
|
48222
|
+
yield* closeCommands(sessionsToClose(state.sessions, session));
|
|
47386
48223
|
yield* update((current) => ({
|
|
47387
48224
|
...current,
|
|
47388
48225
|
sessions: [...current.sessions.filter((item) => !(item.threadId === session.threadId && item.hostId === session.hostId && item.deviceId === session.deviceId)), session]
|
|
@@ -47393,7 +48230,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47393
48230
|
const shutdownWithin = Effect.fn("DeviceService.shutdownWithin")(function* (lease, value) {
|
|
47394
48231
|
const { ready, device } = yield* freshDevice(lease, value);
|
|
47395
48232
|
yield* input.actions.shutdown(ready, device);
|
|
47396
|
-
yield*
|
|
48233
|
+
yield* closeCommands(state.sessions.filter((session) => session.hostId === value.hostId && session.deviceId === value.deviceId));
|
|
47397
48234
|
yield* update((current) => ({
|
|
47398
48235
|
...current,
|
|
47399
48236
|
devices: current.devices.map((item) => item.hostId === value.hostId && item.id === value.deviceId ? {
|
|
@@ -47417,7 +48254,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47417
48254
|
platform: session.platform
|
|
47418
48255
|
});
|
|
47419
48256
|
else {
|
|
47420
|
-
yield*
|
|
48257
|
+
yield* closeCommands(sessionsToClose(state.sessions, value));
|
|
47421
48258
|
yield* update((current) => {
|
|
47422
48259
|
const closing = sessionsToClose(current.sessions, value);
|
|
47423
48260
|
return {
|
|
@@ -47460,11 +48297,16 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47460
48297
|
});
|
|
47461
48298
|
const validate = registry.isCurrent(lease).pipe(Effect.map((current) => current && state.sessions.includes(session)));
|
|
47462
48299
|
return yield* commands.run(session, validate, Effect.gen(function* () {
|
|
47463
|
-
const ready = yield* lease.host.ensureAgentReady(() => Effect.void);
|
|
48300
|
+
const ready = yield* lease.host.ensureAgentReady(() => Effect.void, session.platform);
|
|
47464
48301
|
if (!(yield* validate)) return yield* new DeviceHostUnavailableError({
|
|
47465
48302
|
hostId: target.hostId,
|
|
47466
48303
|
reason: "Device session or agent authorization changed."
|
|
47467
48304
|
});
|
|
48305
|
+
if (ready.xcode && session.leaseId) {
|
|
48306
|
+
const adapter = ready.xcode;
|
|
48307
|
+
const leaseId = session.leaseId;
|
|
48308
|
+
adapterLeases.set(leaseId, () => adapter.closeSession(leaseId));
|
|
48309
|
+
}
|
|
47468
48310
|
return yield* execute(ready, session);
|
|
47469
48311
|
}));
|
|
47470
48312
|
})).pipe(Effect.mapError((error) => isDeviceHostError(error) || isDeviceHostTimeoutError(error) ? new DeviceHostUnavailableError({
|
|
@@ -47478,7 +48320,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47478
48320
|
agentList: listForAccess("agent"),
|
|
47479
48321
|
configure,
|
|
47480
48322
|
shutdown,
|
|
47481
|
-
retireThread: (threadId) => threads.retire(threadId).pipe(Effect.ensuring(Effect.suspend(() =>
|
|
48323
|
+
retireThread: (threadId) => threads.retire(threadId).pipe(Effect.ensuring(Effect.suspend(() => closeCommands(state.sessions.filter((session) => session.threadId === threadId))).pipe(Effect.ensuring(update((current) => ({
|
|
47482
48324
|
...current,
|
|
47483
48325
|
sessions: current.sessions.filter((session) => session.threadId !== threadId),
|
|
47484
48326
|
bootingDevices: (current.bootingDevices ?? []).filter((device) => device.threadId !== threadId)
|
|
@@ -47486,7 +48328,10 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47486
48328
|
restoreThread: threads.restore,
|
|
47487
48329
|
withAgentDevice: (target, execute) => threads.run(target.threadId, withAgentDevice(target, execute)),
|
|
47488
48330
|
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)
|
|
48331
|
+
withAgentCommand: (target, execute) => threads.run(target.threadId, withAgentDevice(target, (ready, session) => ready.agentDevice ? execute(ready.agentDevice, session) : Effect.fail(new DeviceHostUnavailableError({
|
|
48332
|
+
hostId: target.hostId,
|
|
48333
|
+
reason: "This host uses semantic device control. Use cua.getDevice() and element refs."
|
|
48334
|
+
})))),
|
|
47490
48335
|
open: (value) => threads.run(value.threadId, openWithAccess(value, "manual")),
|
|
47491
48336
|
agentOpen: (value) => threads.run(value.threadId, openWithAccess(value, "agent")),
|
|
47492
48337
|
close: (value) => closeWithAccess(value, "manual"),
|
|
@@ -47498,17 +48343,17 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
|
|
|
47498
48343
|
}))
|
|
47499
48344
|
};
|
|
47500
48345
|
});
|
|
47501
|
-
const make$
|
|
48346
|
+
const make$79 = Effect.gen(function* () {
|
|
47502
48347
|
const discover = yield* make$87();
|
|
47503
48348
|
const actions = yield* make$86();
|
|
47504
48349
|
return yield* makeWithHosts({
|
|
47505
|
-
local: make$
|
|
47506
|
-
ssh: make$
|
|
48350
|
+
local: make$81(),
|
|
48351
|
+
ssh: make$80,
|
|
47507
48352
|
discover,
|
|
47508
48353
|
actions
|
|
47509
48354
|
});
|
|
47510
48355
|
}).pipe(Effect.provide(layer$73));
|
|
47511
|
-
const layer$
|
|
48356
|
+
const layer$70 = Layer.effect(DeviceService, make$79);
|
|
47512
48357
|
//#endregion
|
|
47513
48358
|
//#region src/serverRuntimeStartup.ts
|
|
47514
48359
|
var ServerRuntimeStartupError = class extends Schema$1.TaggedErrorClass()("ServerRuntimeStartupError", {
|
|
@@ -47653,7 +48498,7 @@ const startProviderRecovery = Effect.fn("startup.startProviderRecovery")(functio
|
|
|
47653
48498
|
yield* input.recover;
|
|
47654
48499
|
yield* input.startReaper;
|
|
47655
48500
|
});
|
|
47656
|
-
const make$
|
|
48501
|
+
const make$78 = Effect.gen(function* () {
|
|
47657
48502
|
const serverConfig = yield* ServerConfig$1;
|
|
47658
48503
|
const keybindings = yield* Keybindings;
|
|
47659
48504
|
const orchestrationReactor = yield* OrchestrationReactor;
|
|
@@ -47800,7 +48645,7 @@ const make$79 = Effect.gen(function* () {
|
|
|
47800
48645
|
enqueueCommand: commandGate.enqueueCommand
|
|
47801
48646
|
};
|
|
47802
48647
|
});
|
|
47803
|
-
const layer$
|
|
48648
|
+
const layer$69 = Layer.effect(ServerRuntimeStartup, make$78);
|
|
47804
48649
|
//#endregion
|
|
47805
48650
|
//#region src/serverRuntimeState.ts
|
|
47806
48651
|
const PersistedServerRuntimeState = Schema$1.Struct({
|
|
@@ -47957,7 +48802,7 @@ function expandHomePath$2(input, path) {
|
|
|
47957
48802
|
if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(NodeOS.homedir(), input.slice(2));
|
|
47958
48803
|
return input;
|
|
47959
48804
|
}
|
|
47960
|
-
const make$
|
|
48805
|
+
const make$77 = Effect.gen(function* () {
|
|
47961
48806
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
47962
48807
|
const path = yield* Path$1.Path;
|
|
47963
48808
|
const statWorkspaceRoot = Effect.fn("WorkspacePaths.statWorkspaceRoot")(function* (workspaceRoot, normalizedWorkspaceRoot, phase) {
|
|
@@ -48014,7 +48859,7 @@ const make$78 = Effect.gen(function* () {
|
|
|
48014
48859
|
resolveRelativePathWithinRoot
|
|
48015
48860
|
});
|
|
48016
48861
|
});
|
|
48017
|
-
const layer$
|
|
48862
|
+
const layer$68 = Layer.effect(WorkspacePaths, make$77);
|
|
48018
48863
|
//#endregion
|
|
48019
48864
|
//#region src/cli/project.ts
|
|
48020
48865
|
const isEnvironmentHttpCommonError = Schema$1.is(EnvironmentHttpCommonError);
|
|
@@ -48120,7 +48965,7 @@ const projectCommandUuid = Crypto.Crypto.pipe(Effect.flatMap((crypto) => crypto.
|
|
|
48120
48965
|
operation: "generateProjectCommandId",
|
|
48121
48966
|
cause
|
|
48122
48967
|
})));
|
|
48123
|
-
const ProjectCliRuntimeLive = Layer.mergeAll(layer$
|
|
48968
|
+
const ProjectCliRuntimeLive = Layer.mergeAll(layer$68, OrchestrationLayerLive.pipe(Layer.provideMerge(layer$81), Layer.provideMerge(layerConfig)));
|
|
48124
48969
|
const PROJECT_CLI_LIVE_SERVER_TIMEOUT = Duration.seconds(1);
|
|
48125
48970
|
const withProjectCliSessionToken = (environmentAuth, run) => Effect.acquireUseRelease(environmentAuth.issueSession({
|
|
48126
48971
|
scopes: AuthAdministrativeScopes,
|
|
@@ -48222,7 +49067,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
48222
49067
|
});
|
|
48223
49068
|
yield* Console.log(output);
|
|
48224
49069
|
}).pipe(Effect.provide(offlineRuntimeLayer));
|
|
48225
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$
|
|
49070
|
+
}).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
49071
|
});
|
|
48227
49072
|
const projectAddCommand = Command.make("add", {
|
|
48228
49073
|
...projectLocationFlags,
|
|
@@ -51938,7 +52783,7 @@ const makeTerminationError$1 = (handle) => Effect.match(handle.exitCode, {
|
|
|
51938
52783
|
//#region ../../packages/effect-acp/src/client.ts
|
|
51939
52784
|
const MAX_STDERR_CALLBACK_CHARS = 32768;
|
|
51940
52785
|
var AcpClient = class extends Context.Service()("effect-acp/client/AcpClient") {};
|
|
51941
|
-
const make$
|
|
52786
|
+
const make$76 = Effect.fn("effect-acp/AcpClient.make")(function* (stdio, options = {}, terminationError) {
|
|
51942
52787
|
const coreHandlers = {};
|
|
51943
52788
|
const notificationHandlers = {
|
|
51944
52789
|
sessionUpdate: {
|
|
@@ -52105,7 +52950,7 @@ const layerChildProcess$1 = (handle, options = {}) => {
|
|
|
52105
52950
|
})), Stream.drain)) : stdout
|
|
52106
52951
|
};
|
|
52107
52952
|
const terminationError = makeTerminationError$1(handle);
|
|
52108
|
-
return Layer.effect(AcpClient, make$
|
|
52953
|
+
return Layer.effect(AcpClient, make$76(stdio, options, terminationError));
|
|
52109
52954
|
};
|
|
52110
52955
|
//#endregion
|
|
52111
52956
|
//#region src/provider/Drivers/AntigravitySkills.ts
|
|
@@ -53384,7 +54229,7 @@ const Reply = Schema$1.Struct({
|
|
|
53384
54229
|
id: Schema$1.String,
|
|
53385
54230
|
result: Schema$1.Struct({ ok: Schema$1.Boolean })
|
|
53386
54231
|
});
|
|
53387
|
-
const make$
|
|
54232
|
+
const make$75 = Effect.fn("DeviceCommandProxy.make")(function* () {
|
|
53388
54233
|
const http = yield* HttpClient.HttpClient;
|
|
53389
54234
|
const daemonSessions = /* @__PURE__ */ new WeakMap();
|
|
53390
54235
|
return Effect.fn("DeviceCommandProxy.execute")(function* (endpoint, session, payload) {
|
|
@@ -53437,81 +54282,7 @@ const make$76 = Effect.fn("DeviceCommandProxy.make")(function* () {
|
|
|
53437
54282
|
})));
|
|
53438
54283
|
});
|
|
53439
54284
|
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 }))));
|
|
54285
|
+
const layer$67 = Layer.effect(DeviceCommandProxy, make$75().pipe(Effect.map((execute) => ({ execute }))));
|
|
53515
54286
|
//#endregion
|
|
53516
54287
|
//#region src/device/DeviceHubRoute.ts
|
|
53517
54288
|
const DEVICE_HUB_PROXY_PREFIX = "/api/device-hub";
|
|
@@ -53715,12 +54486,12 @@ const requireThreadSpawn = Effect.fn("mcp.requireThreadSpawn")(function* (fusion
|
|
|
53715
54486
|
const isDeviceToolUnavailableError = Schema$1.is(DeviceToolUnavailableError);
|
|
53716
54487
|
const isDeviceActionUnavailableError = Schema$1.is(DeviceActionUnavailableError);
|
|
53717
54488
|
/** Internal bridge; tool payloads never choose thread identity or daemon credentials. */
|
|
53718
|
-
const withDeviceInvocation = (execute) => Effect.gen(function* () {
|
|
54489
|
+
const withDeviceInvocation = (execute, capability = "devices") => Effect.gen(function* () {
|
|
53719
54490
|
const invocation = yield* McpInvocationContext;
|
|
53720
54491
|
const service = yield* DeviceService;
|
|
53721
54492
|
const lifetime = invocation.authorizationLifetime;
|
|
53722
54493
|
const denied = () => new DeviceToolUnavailableError({ reason: "Device command unavailable or authorization changed." });
|
|
53723
|
-
if (!invocation.capabilities.has(
|
|
54494
|
+
if (!invocation.capabilities.has(capability) || !lifetime?.revocation || lifetime.revoked) return yield* denied();
|
|
53724
54495
|
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
54496
|
return yield* Effect.raceFirst(work, Deferred.await(lifetime.revocation).pipe(Effect.andThen(Effect.fail(denied()))));
|
|
53726
54497
|
});
|
|
@@ -53735,6 +54506,7 @@ const MAX_AX_BYTES = 512 * 1024;
|
|
|
53735
54506
|
const AX_TIMEOUT_MS = 1e4;
|
|
53736
54507
|
const MAX_AX_EXTENT = 32768;
|
|
53737
54508
|
const MIN_AX_EXTENT = 2;
|
|
54509
|
+
const isToolUnavailable = Schema$1.is(DeviceToolUnavailableError);
|
|
53738
54510
|
const AxFrames = Schema$1.Array(Schema$1.Struct({ frame: Schema$1.Struct({
|
|
53739
54511
|
x: Schema$1.Number,
|
|
53740
54512
|
y: Schema$1.Number,
|
|
@@ -53774,6 +54546,7 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
|
|
|
53774
54546
|
const state = yield* service.agentList;
|
|
53775
54547
|
const device = state.devices.find((device) => device.hostId === input.hostId && device.id === input.deviceId && device.platform === input.platform);
|
|
53776
54548
|
if (!device) return yield* stale();
|
|
54549
|
+
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
54550
|
const session = yield* service.agentOpen({
|
|
53778
54551
|
threadId,
|
|
53779
54552
|
hostId: input.hostId,
|
|
@@ -53811,8 +54584,16 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
|
|
|
53811
54584
|
deviceId: target.deviceId
|
|
53812
54585
|
}, (ready, session) => Effect.gen(function* () {
|
|
53813
54586
|
if (session.leaseId !== input.leaseId) return yield* stale();
|
|
54587
|
+
if (input.method === "recording") {
|
|
54588
|
+
if (!ready.xcode || session.platform !== "ios") return yield* new DeviceToolUnavailableError({ reason: "Recording is unavailable on this device adapter." });
|
|
54589
|
+
return yield* ready.xcode.recording(session, input.action).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) })));
|
|
54590
|
+
}
|
|
54591
|
+
if (input.method === "semantic") {
|
|
54592
|
+
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()." });
|
|
54593
|
+
return yield* ready.xcode.action(session, input.action).pipe(Effect.mapError((error) => new DeviceToolUnavailableError({ reason: String(error.cause) })));
|
|
54594
|
+
}
|
|
53814
54595
|
if (input.method === "screenshot") {
|
|
53815
|
-
const capture = yield* screenshots.capture(ready, session);
|
|
54596
|
+
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
54597
|
return {
|
|
53817
54598
|
mimeType: "image/png",
|
|
53818
54599
|
data: capture.png.toString("base64"),
|
|
@@ -53820,6 +54601,7 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
|
|
|
53820
54601
|
height: capture.height
|
|
53821
54602
|
};
|
|
53822
54603
|
}
|
|
54604
|
+
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
54605
|
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
54606
|
const route = resolveDeviceHubRoute({
|
|
53825
54607
|
target: {
|
|
@@ -53847,12 +54629,13 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
|
|
|
53847
54629
|
leaseId: session.leaseId,
|
|
53848
54630
|
accessibility
|
|
53849
54631
|
};
|
|
53850
|
-
}).pipe(Effect.scoped, Effect.
|
|
53851
|
-
|
|
53852
|
-
|
|
53853
|
-
|
|
53854
|
-
|
|
53855
|
-
|
|
54632
|
+
}).pipe(Effect.scoped, Effect.timeoutOrElse({
|
|
54633
|
+
duration: AX_TIMEOUT_MS,
|
|
54634
|
+
orElse: () => Effect.gen(function* () {
|
|
54635
|
+
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." });
|
|
54636
|
+
})
|
|
54637
|
+
}), Effect.mapError((error) => isToolUnavailable(error) ? error : new DeviceToolUnavailableError({ reason: error instanceof Error ? error.message : String(error) }))));
|
|
54638
|
+
}), "computer-use").pipe(Effect.provideService(McpInvocationContext, scope), Effect.provideService(DeviceService, devices), Effect.mapError((error) => new ComputerUseError({
|
|
53856
54639
|
reason: "unavailable",
|
|
53857
54640
|
detail: error.message
|
|
53858
54641
|
})));
|
|
@@ -54070,6 +54853,7 @@ const layer$66 = Layer.effect(ComputerUseBroker, Effect.gen(function* () {
|
|
|
54070
54853
|
if (report.state === "active" && state.state === "active" && (report.previewApp !== void 0 || report.deviceControl !== void 0)) states.set(key, {
|
|
54071
54854
|
...state,
|
|
54072
54855
|
...report.previewApp !== void 0 ? { previewApp: report.previewApp } : {},
|
|
54856
|
+
...report.previewApp !== void 0 ? { previewAppIcon: report.previewAppIcon ?? null } : {},
|
|
54073
54857
|
...report.deviceControl !== void 0 ? { deviceControl: report.deviceControl } : {}
|
|
54074
54858
|
});
|
|
54075
54859
|
if (report.state !== "active" || report.previewApp !== void 0 || report.deviceControl !== void 0) yield* publish();
|
|
@@ -65806,6 +66590,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
65806
66590
|
context.inFlightTools.delete(index);
|
|
65807
66591
|
}
|
|
65808
66592
|
context.inFlightTools.clear();
|
|
66593
|
+
for (const toolUseId of context.subagentModels.keys()) if (!context.subagentTasks.has(toolUseId)) context.subagentModels.delete(toolUseId);
|
|
65809
66594
|
for (const block of turnState.assistantTextBlockOrder) yield* completeAssistantTextBlock(context, block, {
|
|
65810
66595
|
force: true,
|
|
65811
66596
|
rawMethod: "claude/result",
|
|
@@ -65974,9 +66759,31 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
65974
66759
|
discard: true
|
|
65975
66760
|
});
|
|
65976
66761
|
});
|
|
66762
|
+
const recordSubagentModel = Effect.fn("recordSubagentModel")(function* (context, toolUseId, model) {
|
|
66763
|
+
if (!toolUseId || !model || context.subagentModels.get(toolUseId) === model) return;
|
|
66764
|
+
const task = context.subagentTasks.get(toolUseId);
|
|
66765
|
+
if (!task && !Array.from(context.inFlightTools.values()).some((tool) => tool.itemId === toolUseId)) return;
|
|
66766
|
+
context.subagentModels.set(toolUseId, model);
|
|
66767
|
+
if (!task) return;
|
|
66768
|
+
yield* offerRuntimeEvent({
|
|
66769
|
+
...yield* makeEventStamp(),
|
|
66770
|
+
type: "task.progress",
|
|
66771
|
+
provider: PROVIDER$6,
|
|
66772
|
+
threadId: context.session.threadId,
|
|
66773
|
+
...context.turnState ? { turnId: asCanonicalTurnId(context.turnState.turnId) } : {},
|
|
66774
|
+
payload: {
|
|
66775
|
+
taskId: RuntimeTaskId.make(task.taskId),
|
|
66776
|
+
description: task.description,
|
|
66777
|
+
toolUseId,
|
|
66778
|
+
...task.subagentType ? { subagentType: task.subagentType } : {},
|
|
66779
|
+
model
|
|
66780
|
+
}
|
|
66781
|
+
});
|
|
66782
|
+
});
|
|
65977
66783
|
const handleStreamEvent = Effect.fn("handleStreamEvent")(function* (context, message) {
|
|
65978
66784
|
if (message.type !== "stream_event") return;
|
|
65979
66785
|
const { event } = message;
|
|
66786
|
+
if (event.type === "message_start") yield* recordSubagentModel(context, message.parent_tool_use_id, event.message.model);
|
|
65980
66787
|
if (event.type === "message_delta") {
|
|
65981
66788
|
if (message.parent_tool_use_id !== null && message.parent_tool_use_id !== void 0) return;
|
|
65982
66789
|
const snapshot = normalizeClaudeActiveTokenUsage(event.usage, context.lastKnownContextWindow, context.lastKnownTotalProcessedTokens);
|
|
@@ -66297,10 +67104,12 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
66297
67104
|
rawPayload: message
|
|
66298
67105
|
});
|
|
66299
67106
|
context.inFlightTools.delete(index);
|
|
67107
|
+
if (!context.subagentTasks.has(tool.itemId)) context.subagentModels.delete(tool.itemId);
|
|
66300
67108
|
}
|
|
66301
67109
|
});
|
|
66302
67110
|
const handleAssistantMessage = Effect.fn("handleAssistantMessage")(function* (context, message) {
|
|
66303
67111
|
if (message.type !== "assistant") return;
|
|
67112
|
+
yield* recordSubagentModel(context, message.parent_tool_use_id, message.message.model);
|
|
66304
67113
|
if (!context.turnState) {
|
|
66305
67114
|
const turnId = TurnId.make(yield* randomUUIDv4);
|
|
66306
67115
|
const startedAt = yield* nowIso;
|
|
@@ -66407,6 +67216,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
66407
67216
|
}
|
|
66408
67217
|
switch (message.subtype) {
|
|
66409
67218
|
case "init":
|
|
67219
|
+
context.currentApiModelId = message.model || context.currentApiModelId;
|
|
66410
67220
|
yield* offerRuntimeEvent({
|
|
66411
67221
|
...base,
|
|
66412
67222
|
type: "session.configured",
|
|
@@ -66477,7 +67287,12 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
66477
67287
|
return;
|
|
66478
67288
|
case "task_started": {
|
|
66479
67289
|
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;
|
|
67290
|
+
const model = (message.tool_use_id ? context.subagentModels.get(message.tool_use_id) : void 0) ?? (requestedModel === "inherit" ? context.currentApiModelId : requestedModel);
|
|
67291
|
+
if (message.tool_use_id) context.subagentTasks.set(message.tool_use_id, {
|
|
67292
|
+
taskId: message.task_id,
|
|
67293
|
+
description: message.description,
|
|
67294
|
+
...message.subagent_type ? { subagentType: message.subagent_type } : {}
|
|
67295
|
+
});
|
|
66481
67296
|
yield* offerRuntimeEvent({
|
|
66482
67297
|
...base,
|
|
66483
67298
|
type: "task.started",
|
|
@@ -66528,6 +67343,11 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
66528
67343
|
...message.tool_use_id ? { toolUseId: message.tool_use_id } : {}
|
|
66529
67344
|
}
|
|
66530
67345
|
});
|
|
67346
|
+
for (const [toolUseId, task] of context.subagentTasks) if (toolUseId === message.tool_use_id || task.taskId === message.task_id) {
|
|
67347
|
+
context.subagentTasks.delete(toolUseId);
|
|
67348
|
+
context.subagentModels.delete(toolUseId);
|
|
67349
|
+
}
|
|
67350
|
+
if (message.tool_use_id) context.subagentModels.delete(message.tool_use_id);
|
|
66531
67351
|
return;
|
|
66532
67352
|
case "files_persisted":
|
|
66533
67353
|
yield* offerRuntimeEvent({
|
|
@@ -67228,6 +68048,8 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
67228
68048
|
turns: [],
|
|
67229
68049
|
inFlightTools,
|
|
67230
68050
|
claudeTasks,
|
|
68051
|
+
subagentTasks: /* @__PURE__ */ new Map(),
|
|
68052
|
+
subagentModels: /* @__PURE__ */ new Map(),
|
|
67231
68053
|
turnState: void 0,
|
|
67232
68054
|
pendingTurnRequest: void 0,
|
|
67233
68055
|
steeredMessages: /* @__PURE__ */ new Map(),
|
|
@@ -125492,6 +126314,7 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
|
|
|
125492
126314
|
[WS_METHODS.serverUpdateServer, AuthOrchestrationOperateScope],
|
|
125493
126315
|
[WS_METHODS.serverUpsertKeybinding, AuthOrchestrationOperateScope],
|
|
125494
126316
|
[WS_METHODS.serverRemoveKeybinding, AuthOrchestrationOperateScope],
|
|
126317
|
+
[WS_METHODS.threadHandoff, AuthOrchestrationReadScope],
|
|
125495
126318
|
[WS_METHODS.btwAsk, AuthOrchestrationReadScope],
|
|
125496
126319
|
[WS_METHODS.btwCancel, AuthOrchestrationReadScope],
|
|
125497
126320
|
[WS_METHODS.tasksList, AuthOrchestrationReadScope],
|
|
@@ -126344,6 +127167,27 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
|
|
|
126344
127167
|
operation: "getUsageReport",
|
|
126345
127168
|
detail: "This provider does not report account usage."
|
|
126346
127169
|
})), { "rpc.aggregate": "server" }),
|
|
127170
|
+
[WS_METHODS.threadHandoff]: (input) => observeRpcEffect$1(WS_METHODS.threadHandoff, Effect.gen(function* () {
|
|
127171
|
+
const fail = (detail) => new TextGenerationError({
|
|
127172
|
+
operation: "createHandoff",
|
|
127173
|
+
detail
|
|
127174
|
+
});
|
|
127175
|
+
if (!projectionSnapshotQuery.getHandoffContext || !textGeneration.generateBtwAnswer) return yield* fail("Handoff generation is unavailable on this server.");
|
|
127176
|
+
const context = yield* projectionSnapshotQuery.getHandoffContext(input.threadId).pipe(Effect.mapError(() => fail("Could not read thread history.")));
|
|
127177
|
+
if (Option.isNone(context)) return yield* fail("Thread or project is no longer available.");
|
|
127178
|
+
const settings = yield* serverSettings.getSettings.pipe(Effect.mapError(() => fail("Could not read text generation settings.")));
|
|
127179
|
+
const result = yield* textGeneration.generateBtwAnswer({
|
|
127180
|
+
cwd: context.value.projectId === P4_CHAT_PROJECT_ID ? config.chatWorkspaceDir : context.value.cwd,
|
|
127181
|
+
modelSelection: settings.textGenerationModelSelection,
|
|
127182
|
+
context: context.value.transcript,
|
|
127183
|
+
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."
|
|
127184
|
+
});
|
|
127185
|
+
if (!result.answer.trim()) return yield* fail("Model returned an empty handoff. Try again.");
|
|
127186
|
+
return {
|
|
127187
|
+
projectId: context.value.projectId,
|
|
127188
|
+
text: result.answer.trim()
|
|
127189
|
+
};
|
|
127190
|
+
}), { "rpc.aggregate": "thread" }),
|
|
126347
127191
|
[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
127192
|
[WS_METHODS.btwCancel]: (input) => observeRpcEffect$1(WS_METHODS.btwCancel, btw ? btw.cancel(input) : Effect.succeed({}), { "rpc.aggregate": "server" }),
|
|
126349
127193
|
[WS_METHODS.serverGetUsageSummary]: (input) => observeRpcEffect$1(WS_METHODS.serverGetUsageSummary, usage.readSummary(input), { "rpc.aggregate": "server" }),
|
|
@@ -127104,6 +127948,13 @@ const makeProviderAdapterRegistry = Effect.fn("makeProviderAdapterRegistry")(fun
|
|
|
127104
127948
|
});
|
|
127105
127949
|
const ProviderAdapterRegistryLive = Layer.effect(ProviderAdapterRegistry, makeProviderAdapterRegistry());
|
|
127106
127950
|
//#endregion
|
|
127951
|
+
//#region src/provider/ComputerUsePrompts.ts
|
|
127952
|
+
/** Routing guidance is injected only when the session has P4's Computer Use endpoint. */
|
|
127953
|
+
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.";
|
|
127954
|
+
function withComputerUseRouting(input, enabled) {
|
|
127955
|
+
return enabled ? `<p4-computer-use>\n${COMPUTER_USE_ROUTING_PROMPT}\n</p4-computer-use>\n\n${input ?? ""}` : input;
|
|
127956
|
+
}
|
|
127957
|
+
//#endregion
|
|
127107
127958
|
//#region src/mcp/McpSessionRegistry.ts
|
|
127108
127959
|
var McpSessionRegistry = class extends Context.Service()("@p4code/cli/mcp/McpSessionRegistry") {};
|
|
127109
127960
|
/**
|
|
@@ -127877,7 +128728,10 @@ const makeProviderService = Effect.fn("makeProviderService")(function* (options)
|
|
|
127877
128728
|
...input.modelSelection?.model ? { "provider.model": input.modelSelection.model } : {}
|
|
127878
128729
|
});
|
|
127879
128730
|
yield* touchActiveMcpThread(input.threadId);
|
|
127880
|
-
const turn = yield* routed.adapter.sendTurn(
|
|
128731
|
+
const turn = yield* routed.adapter.sendTurn({
|
|
128732
|
+
...input,
|
|
128733
|
+
input: withComputerUseRouting(input.input, !forCompaction && Boolean(readMcpProviderSession(input.threadId)?.computerUse))
|
|
128734
|
+
});
|
|
127881
128735
|
yield* directory.upsert({
|
|
127882
128736
|
threadId: input.threadId,
|
|
127883
128737
|
provider: routed.adapter.provider,
|
|
@@ -128362,7 +129216,7 @@ const mcpToolkit = (toolkit) => Layer.effectDiscard(Effect.gen(function* () {
|
|
|
128362
129216
|
//#endregion
|
|
128363
129217
|
//#region src/mcp/toolkits/computerUse/tools.ts
|
|
128364
129218
|
const ComputerUseJsTool = Tool.make("js", {
|
|
128365
|
-
description: "Run JavaScript in a persistent
|
|
129219
|
+
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 in its embedded live view. A persistent independent black pointer marks actions on the host window, outside the preview. 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
129220
|
parameters: Schema$1.Struct({
|
|
128367
129221
|
...ComputerUseJsInput.fields,
|
|
128368
129222
|
hostId: Schema$1.optional(TrimmedNonEmptyString).annotate({ description: "Select an available host when more than one is connected. Reset before changing hosts." })
|
|
@@ -132626,6 +133480,7 @@ function runtimeEventToActivities(event, taskTitle, compressMode) {
|
|
|
132626
133480
|
summary: event.payload.description.trim().length > 0 ? truncateDetail(event.payload.description, 120) : "Reasoning update",
|
|
132627
133481
|
payload: {
|
|
132628
133482
|
taskId: event.payload.taskId,
|
|
133483
|
+
...event.payload.model ? { model: event.payload.model } : {},
|
|
132629
133484
|
...event.provider === "claudeAgent" && event.payload.workflowScript?.instanceId === event.providerInstanceId ? { workflowScript: event.payload.workflowScript } : {},
|
|
132630
133485
|
...event.payload.description.trim().length > 0 ? { title: truncateDetail(event.payload.description, 120) } : {},
|
|
132631
133486
|
detail: event.payload.summary ?? event.payload.description,
|
|
@@ -137341,13 +138196,13 @@ const CheckpointingLayerLive = Layer.empty.pipe(Layer.provideMerge(layer$46), La
|
|
|
137341
138196
|
const PortScannerLayerLive = layer$45.pipe(Layer.provide(layer$82));
|
|
137342
138197
|
const TerminalLayerLive = layer$44.pipe(Layer.provide(PtyAdapterLive), Layer.provide(PortScannerLayerLive));
|
|
137343
138198
|
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$
|
|
138199
|
+
const WorkspaceEntriesLayerLive = layer$17.pipe(Layer.provide(layer$68));
|
|
138200
|
+
const WorkspaceFileSystemLayerLive = layer$16.pipe(Layer.provide(layer$68), Layer.provide(WorkspaceEntriesLayerLive));
|
|
138201
|
+
const WorkspaceLayerLive = Layer.mergeAll(layer$68, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
|
|
138202
|
+
const ProjectFaviconResolverLayerLive = layer$63.pipe(Layer.provide(layer$68), Layer.provide(layer$64));
|
|
137348
138203
|
const AuthLayerLive = layer$111.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$115));
|
|
137349
138204
|
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$
|
|
138205
|
+
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
138206
|
const UsageLayerLive = layer$23.pipe(Layer.provide(layer$90.pipe(Layer.provide(layer$115))));
|
|
137352
138207
|
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
138208
|
/**
|
|
@@ -137358,7 +138213,7 @@ const RuntimeDependenciesLive = layer$22.pipe(Layer.provideMerge(RuntimeCoreDepe
|
|
|
137358
138213
|
* this costs a server with no hub nothing but a layer.
|
|
137359
138214
|
*/
|
|
137360
138215
|
const AssetSyncLive = layer$84.pipe(Layer.provideMerge(layer$85), Layer.provideMerge(layer$86));
|
|
137361
|
-
const RuntimeServicesLive = layer$
|
|
138216
|
+
const RuntimeServicesLive = layer$69.pipe(Layer.provideMerge(layer$1), Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
|
|
137362
138217
|
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
138218
|
const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
|
|
137364
138219
|
const config = yield* ServerConfig$1;
|