@p4code/cli 0.1.13 → 0.1.15
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 +921 -52
- package/dist/client/assets/{DiffPanel-Bpvhj9Zk.js → DiffPanel-Btlwh6tZ.js} +2 -2
- package/dist/client/assets/{FilePreviewPanel-C2_gi2li.js → FilePreviewPanel-CJRGrUq9.js} +2 -2
- package/dist/client/assets/{PreviewPanel-CXrvKPAn.js → PreviewPanel-CM8chpxZ.js} +2 -2
- package/dist/client/assets/{angular-html-tWdnCR5u.js → angular-html-BjmAeE7T.js} +2 -2
- package/dist/client/assets/{angular-ts-DHHrhqEn.js → angular-ts-BBpxlOlP.js} +2 -2
- package/dist/client/assets/{apl-Dttzd1My.js → apl-BbgMrCuX.js} +2 -2
- package/dist/client/assets/arrow-right-DYd55OqT.js +2 -0
- package/dist/client/assets/{astro-BhhLcbHg.js → astro-mUvWYN-n.js} +2 -2
- package/dist/client/assets/{blade-D1XdhGNZ.js → blade-B5QWyKCP.js} +2 -2
- package/dist/client/assets/{c-DU3so7RH.js → c-BfHpmPOy.js} +2 -2
- package/dist/client/assets/{cobol-C5SwCVtJ.js → cobol-DLZOS_vG.js} +2 -2
- package/dist/client/assets/{coffee-D8JHirJ4.js → coffee-9TkBNWbZ.js} +2 -2
- package/dist/client/assets/{cpp-BrxgqiPv.js → cpp-qjIc-2IN.js} +2 -2
- package/dist/client/assets/{crystal-LnjbHrAj.js → crystal-DCOpXRVe.js} +2 -2
- package/dist/client/assets/{css-BhvHnfwC.js → css-Xrxx3evf.js} +2 -2
- package/dist/client/assets/{edge-BIAZ2O1m.js → edge-BJXKMMao.js} +2 -2
- package/dist/client/assets/{elixir-C7fzptdf.js → elixir-_uRFN4Aw.js} +2 -2
- package/dist/client/assets/{elm-24neygAB.js → elm-fwfp6NdI.js} +2 -2
- package/dist/client/assets/{erb-DpLDFI3P.js → erb-DfSw8n3G.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-D9LoOqKz.js → fileCommentAnnotations-BCnpdEVp.js} +2 -2
- package/dist/client/assets/{git-rebase-nk76LFrn.js → git-rebase-CwGd8ZXk.js} +2 -2
- package/dist/client/assets/{glimmer-js-D4faXOGy.js → glimmer-js-DIl7yS9j.js} +2 -2
- package/dist/client/assets/{glimmer-ts-CUIVPmuw.js → glimmer-ts-ClbRblrS.js} +2 -2
- package/dist/client/assets/{glsl-CHEysBk5.js → glsl-DPbTpA_H.js} +2 -2
- package/dist/client/assets/{graphql-DxDXWgtu.js → graphql-Ugw-cmPb.js} +2 -2
- package/dist/client/assets/{hack-SE0rPM7F.js → hack-BNaGlFKY.js} +2 -2
- package/dist/client/assets/{haml-CZNVJ1t_.js → haml-CADPiQL8.js} +2 -2
- package/dist/client/assets/{handlebars-D3H0z0i1.js → handlebars-lMMkQPVI.js} +2 -2
- package/dist/client/assets/{html-BXh14Kiu.js → html-dV4yJf1E.js} +2 -2
- package/dist/client/assets/{html-derivative-Dcx75xJi.js → html-derivative-DC417Zuq.js} +2 -2
- package/dist/client/assets/{http-CHN_K5kg.js → http-DHNl1S4G.js} +2 -2
- package/dist/client/assets/{hurl-DcyaHxXE.js → hurl-MeTzE8YN.js} +2 -2
- package/dist/client/assets/{index-DesIBOH0.js → index-BmkbvCRH.js} +200 -200
- package/dist/client/assets/index-DvMVFBig.css +1 -0
- package/dist/client/assets/{java-CibfJb97.js → java-_z6VtRIC.js} +2 -2
- package/dist/client/assets/{javascript-CGKYJrgO.js → javascript-mzH0UoeT.js} +2 -2
- package/dist/client/assets/{jinja-hMi6h2V1.js → jinja-CcSfwgQN.js} +2 -2
- package/dist/client/assets/{jison-D5hwL1Vj.js → jison-Dnr6DQYK.js} +2 -2
- package/dist/client/assets/{json-DBBzorBt.js → json-xiKHc2eN.js} +2 -2
- package/dist/client/assets/{jsx-CfjpnlWf.js → jsx-B0uBKQ5P.js} +2 -2
- package/dist/client/assets/{julia-DY-wGzwr.js → julia-Du1DH_tJ.js} +2 -2
- package/dist/client/assets/{just-Aiaxo0L3.js → just-DlyxJRKQ.js} +2 -2
- package/dist/client/assets/{latex-Cl7y6v9p.js → latex-FzyaW41z.js} +2 -2
- package/dist/client/assets/{liquid-tjIYQaDR.js → liquid-n6xLDAue.js} +2 -2
- package/dist/client/assets/{lua-DeDHmNSA.js → lua-DL20UMyO.js} +2 -2
- package/dist/client/assets/{marko-BLEE3RS_.js → marko-OhxSvhVO.js} +2 -2
- package/dist/client/assets/{mdc-DBLy0ju2.js → mdc-JFr_O-f6.js} +2 -2
- package/dist/client/assets/{nginx-BpS56zJM.js → nginx-DO3MRFFZ.js} +2 -2
- package/dist/client/assets/{nim-BP_5ZWs4.js → nim-gxiT3Pjh.js} +2 -2
- package/dist/client/assets/{perl-BNoghH8w.js → perl-uO1ltvnm.js} +2 -2
- package/dist/client/assets/{php-fikhFdsH.js → php-DkSF6XhT.js} +2 -2
- package/dist/client/assets/{pug-BmML_p4L.js → pug-D6ks186t.js} +2 -2
- package/dist/client/assets/{qml-WOQTdjfD.js → qml-CsabseTB.js} +2 -2
- package/dist/client/assets/{r-BuLVFJ9C.js → r-UjYqKhF6.js} +2 -2
- package/dist/client/assets/{razor-CsZ5mXvp.js → razor-Dr34iB-_.js} +2 -2
- package/dist/client/assets/{regexp-BbB_Z0k0.js → regexp-BG9MLSbj.js} +2 -2
- package/dist/client/assets/{rst-CGogIoWE.js → rst-I9GYrav4.js} +2 -2
- package/dist/client/assets/{ruby-Drt9dpIY.js → ruby-DQZcmBTL.js} +2 -2
- package/dist/client/assets/{sas-mvidrzzg.js → sas-To8Ja79N.js} +2 -2
- package/dist/client/assets/{scss-DcqXsy4k.js → scss-KhIYRETT.js} +2 -2
- package/dist/client/assets/{shellscript-C6nla6y6.js → shellscript-B3M9HD4S.js} +2 -2
- package/dist/client/assets/{shellsession-Dhkmvk-h.js → shellsession-DCI3i_rX.js} +2 -2
- package/dist/client/assets/{soy-CA1GOdLw.js → soy-11gkdBZx.js} +2 -2
- package/dist/client/assets/{sql-CaDtCgQd.js → sql-Dc2GulG4.js} +2 -2
- package/dist/client/assets/{stata-r5dYDO1a.js → stata-Br5SUE30.js} +2 -2
- package/dist/client/assets/{surrealql-CreiDVJS.js → surrealql-Rg04dQNn.js} +2 -2
- package/dist/client/assets/{svelte-LQf52vP7.js → svelte-CtlB9-BL.js} +2 -2
- package/dist/client/assets/{templ-BAiCCrhW.js → templ-Du0y714B.js} +2 -2
- package/dist/client/assets/{tex-Bi4veNnI.js → tex-DujbYFg6.js} +2 -2
- package/dist/client/assets/textarea-C3IX8FEW.js +46 -0
- package/dist/client/assets/{ts-tags-CmONaW11.js → ts-tags-D1giNT28.js} +2 -2
- package/dist/client/assets/{tsx-DEGaq1SP.js → tsx-6OJBHQve.js} +2 -2
- package/dist/client/assets/{twig-DR3myS6w.js → twig-CsGDI-1m.js} +2 -2
- package/dist/client/assets/{typescript-pTX3WTUF.js → typescript-BK1jdq6h.js} +2 -2
- package/dist/client/assets/{vue-_Gh228sM.js → vue-CcdGezrU.js} +2 -2
- package/dist/client/assets/{vue-html-DqI2SuCG.js → vue-html-Cki3bF1b.js} +2 -2
- package/dist/client/assets/{vue-vine-DJ2u5TuR.js → vue-vine-CqNi4Hn_.js} +2 -2
- package/dist/client/assets/{xml-DtWHD1Mk.js → xml-By8mAu2E.js} +2 -2
- package/dist/client/assets/{xsl--uIbb82K.js → xsl-DWgCtTr-.js} +2 -2
- package/dist/client/assets/{yaml-BYZt09An.js → yaml-DZTshk4U.js} +2 -2
- package/dist/client/index.html +3 -3
- package/package.json +1 -1
- package/dist/client/assets/arrow-right-DL8xTI3l.js +0 -2
- package/dist/client/assets/index-D7hXE3Wh.css +0 -1
- package/dist/client/assets/textarea-8UeQ85tZ.js +0 -46
package/dist/bin.mjs
CHANGED
|
@@ -121,7 +121,7 @@ const closeServer = (server) => {
|
|
|
121
121
|
* NetService - Service tag for startup networking helpers.
|
|
122
122
|
*/
|
|
123
123
|
var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
|
|
124
|
-
const make$
|
|
124
|
+
const make$75 = () => {
|
|
125
125
|
/**
|
|
126
126
|
* Returns true when a TCP server can bind to {host, port}.
|
|
127
127
|
* `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
|
|
@@ -230,10 +230,10 @@ const make$73 = () => {
|
|
|
230
230
|
})
|
|
231
231
|
};
|
|
232
232
|
};
|
|
233
|
-
const layer$
|
|
233
|
+
const layer$71 = Layer.sync(NetService, make$75);
|
|
234
234
|
//#endregion
|
|
235
235
|
//#region package.json
|
|
236
|
-
var version = "0.1.
|
|
236
|
+
var version = "0.1.15";
|
|
237
237
|
//#endregion
|
|
238
238
|
//#region src/config.ts
|
|
239
239
|
/**
|
|
@@ -254,8 +254,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
|
|
|
254
254
|
/** @deprecated Import and use `layerTest` from this module. */
|
|
255
255
|
static layerTest = (cwd, baseDirOrPrefix) => layerTest$2(cwd, baseDirOrPrefix);
|
|
256
256
|
};
|
|
257
|
-
const make$
|
|
258
|
-
const layer$
|
|
257
|
+
const make$74 = (config) => ServerConfig$1.of(config);
|
|
258
|
+
const layer$70 = (config) => Layer.succeed(ServerConfig$1, make$74(config));
|
|
259
259
|
const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
|
|
260
260
|
const { join } = yield* Path.Path;
|
|
261
261
|
const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
|
|
@@ -8146,6 +8146,15 @@ const McpSecretSlot = Schema$1.Struct({
|
|
|
8146
8146
|
* have to compute the same string or the value is written where nothing looks.
|
|
8147
8147
|
*/
|
|
8148
8148
|
const mcpSecretStorageName = (input) => `mcp.${input.server}.${input.target}.${input.key}`;
|
|
8149
|
+
/**
|
|
8150
|
+
* The storage name for a server's OAuth record — client id, endpoints, tokens.
|
|
8151
|
+
*
|
|
8152
|
+
* Beside `mcpSecretStorageName` for the same reason that one is here: the
|
|
8153
|
+
* server writing tokens and the registry cleaning up on remove must compute
|
|
8154
|
+
* the same string. Cannot collide with a slot name: slots always carry a
|
|
8155
|
+
* `header.` or `env.` segment where this has `oauth`.
|
|
8156
|
+
*/
|
|
8157
|
+
const mcpOAuthStorageName = (server) => `mcp.${server}.oauth`;
|
|
8149
8158
|
const commonFields = {
|
|
8150
8159
|
name: McpServerName,
|
|
8151
8160
|
/**
|
|
@@ -8187,6 +8196,20 @@ const McpRemoteServer = Schema$1.Struct({
|
|
|
8187
8196
|
*/
|
|
8188
8197
|
const McpServerRegistration = Schema$1.Union([McpStdioServer, McpRemoteServer]);
|
|
8189
8198
|
/**
|
|
8199
|
+
* OAuth state of a remote server, on this machine.
|
|
8200
|
+
*
|
|
8201
|
+
* Present only for a server that answered a readiness probe with a 401
|
|
8202
|
+
* challenge or that already holds a token here. Like `missingSecrets`, this is
|
|
8203
|
+
* per-machine truth about a registration that syncs everywhere: the token never
|
|
8204
|
+
* travels, so each machine reports its own `connected`.
|
|
8205
|
+
*/
|
|
8206
|
+
const McpServerOAuthStatus = Schema$1.Struct({
|
|
8207
|
+
/** The server demanded a bearer token this machine does not send. */
|
|
8208
|
+
required: Schema$1.Boolean,
|
|
8209
|
+
/** This machine holds a token for it. */
|
|
8210
|
+
connected: Schema$1.Boolean
|
|
8211
|
+
});
|
|
8212
|
+
/**
|
|
8190
8213
|
* What a panel needs to show, including what this machine cannot run.
|
|
8191
8214
|
*
|
|
8192
8215
|
* `missingSecrets` is the field that earns this type. A registration syncs
|
|
@@ -8194,15 +8217,62 @@ const McpServerRegistration = Schema$1.Union([McpStdioServer, McpRemoteServer]);
|
|
|
8194
8217
|
* unusable *here*, and saying which slot is empty is the difference between a
|
|
8195
8218
|
* fixable state and a mystery.
|
|
8196
8219
|
*/
|
|
8220
|
+
/**
|
|
8221
|
+
* Where a listed server lives, which is also where an edit goes.
|
|
8222
|
+
*
|
|
8223
|
+
* `claude` is the CLI's user scope (`~/.claude.json`) — the primary home,
|
|
8224
|
+
* because every Claude Code session reads it, inside p4code and out.
|
|
8225
|
+
* `project` is one workspace project's `.mcp.json`, committed with the repo.
|
|
8226
|
+
* `registry` is p4code's own store, the only one that syncs through the hub
|
|
8227
|
+
* and carries secret slots.
|
|
8228
|
+
*/
|
|
8229
|
+
const McpServerSource = Schema$1.Literals([
|
|
8230
|
+
"registry",
|
|
8231
|
+
"claude",
|
|
8232
|
+
"project"
|
|
8233
|
+
]);
|
|
8197
8234
|
const McpServerStatus = Schema$1.Struct({
|
|
8198
8235
|
registration: McpServerRegistration,
|
|
8199
8236
|
missingSecrets: Schema$1.Array(McpSecretSlot),
|
|
8200
8237
|
/** True when this machine holds everything needed to start it. */
|
|
8201
|
-
ready: Schema$1.Boolean
|
|
8238
|
+
ready: Schema$1.Boolean,
|
|
8239
|
+
/** Absent for servers that never challenged and hold no token here. */
|
|
8240
|
+
oauth: Schema$1.optional(McpServerOAuthStatus),
|
|
8241
|
+
/** Absent means `registry`, which every row was before this field existed. */
|
|
8242
|
+
source: Schema$1.optional(McpServerSource),
|
|
8243
|
+
/** The workspace root whose `.mcp.json` holds this row. `project` rows only. */
|
|
8244
|
+
projectDir: Schema$1.optional(Schema$1.String)
|
|
8245
|
+
});
|
|
8246
|
+
/** A workspace project the panel can scope a server to. */
|
|
8247
|
+
const McpProjectRef = Schema$1.Struct({
|
|
8248
|
+
dir: Schema$1.String,
|
|
8249
|
+
title: Schema$1.String
|
|
8250
|
+
});
|
|
8251
|
+
const McpServerList = Schema$1.Struct({
|
|
8252
|
+
servers: Schema$1.Array(McpServerStatus),
|
|
8253
|
+
/**
|
|
8254
|
+
* The projects whose `.mcp.json` can be edited, for the add dialog's scope
|
|
8255
|
+
* picker. Carried with the list so the panel needs no second request.
|
|
8256
|
+
*/
|
|
8257
|
+
projects: Schema$1.optional(Schema$1.Array(McpProjectRef))
|
|
8258
|
+
});
|
|
8259
|
+
/**
|
|
8260
|
+
* `source` routes the write: `claude` edits the `mcpServers` key of the
|
|
8261
|
+
* CLI's user-scope file, `registry` (or absent) writes p4code's own store.
|
|
8262
|
+
* The user-scope file is the primary home — it is what every Claude Code
|
|
8263
|
+
* session reads, in and out of p4code — so the panel defaults to it.
|
|
8264
|
+
*/
|
|
8265
|
+
const McpServerSaveInput = Schema$1.Struct({
|
|
8266
|
+
registration: McpServerRegistration,
|
|
8267
|
+
source: Schema$1.optional(McpServerSource),
|
|
8268
|
+
/** Required when `source` is `project`: the workspace root to write into. */
|
|
8269
|
+
projectDir: Schema$1.optional(Schema$1.String)
|
|
8270
|
+
});
|
|
8271
|
+
const McpServerNameInput = Schema$1.Struct({
|
|
8272
|
+
name: McpServerName,
|
|
8273
|
+
source: Schema$1.optional(McpServerSource),
|
|
8274
|
+
projectDir: Schema$1.optional(Schema$1.String)
|
|
8202
8275
|
});
|
|
8203
|
-
const McpServerList = Schema$1.Struct({ servers: Schema$1.Array(McpServerStatus) });
|
|
8204
|
-
const McpServerSaveInput = Schema$1.Struct({ registration: McpServerRegistration });
|
|
8205
|
-
const McpServerNameInput = Schema$1.Struct({ name: McpServerName });
|
|
8206
8276
|
/**
|
|
8207
8277
|
* Setting one secret value, on this machine only.
|
|
8208
8278
|
*
|
|
@@ -8216,6 +8286,12 @@ const McpServerSecretInput = Schema$1.Struct({
|
|
|
8216
8286
|
slot: McpSecretSlot,
|
|
8217
8287
|
value: Schema$1.String
|
|
8218
8288
|
});
|
|
8289
|
+
/**
|
|
8290
|
+
* The one thing the panel needs to continue a sign-in: where to send the
|
|
8291
|
+
* browser. Everything else the flow holds — verifier, client id, endpoints —
|
|
8292
|
+
* stays on the server, because the callback lands there, not on the panel.
|
|
8293
|
+
*/
|
|
8294
|
+
const McpOAuthBeginResult = Schema$1.Struct({ authorizeUrl: Schema$1.String });
|
|
8219
8295
|
var McpRegistryError = class extends Schema$1.TaggedErrorClass()("McpRegistryError", { detail: Schema$1.String }) {
|
|
8220
8296
|
get message() {
|
|
8221
8297
|
return this.detail;
|
|
@@ -8565,6 +8641,8 @@ const WS_METHODS = {
|
|
|
8565
8641
|
mcpSave: "mcp.save",
|
|
8566
8642
|
mcpRemove: "mcp.remove",
|
|
8567
8643
|
mcpSetSecret: "mcp.setSecret",
|
|
8644
|
+
mcpOAuthBegin: "mcp.oauthBegin",
|
|
8645
|
+
mcpOAuthDisconnect: "mcp.oauthDisconnect",
|
|
8568
8646
|
sourceControlLookupRepository: "sourceControl.lookupRepository",
|
|
8569
8647
|
sourceControlCloneRepository: "sourceControl.cloneRepository",
|
|
8570
8648
|
sourceControlPublishRepository: "sourceControl.publishRepository",
|
|
@@ -8834,6 +8912,21 @@ const WsMcpSetSecretRpc = Rpc.make(WS_METHODS.mcpSetSecret, {
|
|
|
8834
8912
|
error: Schema$1.Union([McpRegistryError, EnvironmentAuthorizationError])
|
|
8835
8913
|
});
|
|
8836
8914
|
/**
|
|
8915
|
+
* Starts an OAuth sign-in for a remote server and answers with the authorize
|
|
8916
|
+
* URL. The panel opens it in a browser; the redirect lands on this server's
|
|
8917
|
+
* own `/oauth/callback`, so nothing else crosses the wire to the client.
|
|
8918
|
+
*/
|
|
8919
|
+
const WsMcpOAuthBeginRpc = Rpc.make(WS_METHODS.mcpOAuthBegin, {
|
|
8920
|
+
payload: McpServerNameInput,
|
|
8921
|
+
success: McpOAuthBeginResult,
|
|
8922
|
+
error: Schema$1.Union([McpRegistryError, EnvironmentAuthorizationError])
|
|
8923
|
+
});
|
|
8924
|
+
const WsMcpOAuthDisconnectRpc = Rpc.make(WS_METHODS.mcpOAuthDisconnect, {
|
|
8925
|
+
payload: McpServerNameInput,
|
|
8926
|
+
success: McpServerList,
|
|
8927
|
+
error: Schema$1.Union([McpRegistryError, EnvironmentAuthorizationError])
|
|
8928
|
+
});
|
|
8929
|
+
/**
|
|
8837
8930
|
* The board, then every change to it, for as long as the client stays
|
|
8838
8931
|
* subscribed. The leading snapshot is what makes this sufficient on its own —
|
|
8839
8932
|
* a client does not list first, so there is no window between the two in which
|
|
@@ -9211,7 +9304,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
|
|
|
9211
9304
|
error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
|
|
9212
9305
|
stream: true
|
|
9213
9306
|
});
|
|
9214
|
-
const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, 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, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
9307
|
+
const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetProviderUsageRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, 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, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsAssetsCreateLocalRpc, WsAssetsRemoveLocalRpc, WsSkillRegistrySearchRpc, WsSkillRegistryFetchRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsMcpOAuthBeginRpc, WsMcpOAuthDisconnectRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
9215
9308
|
//#endregion
|
|
9216
9309
|
//#region ../../packages/shared/src/oauthScope.ts
|
|
9217
9310
|
const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
|
|
@@ -9357,7 +9450,7 @@ function deriveAuthClientMetadata(input) {
|
|
|
9357
9450
|
//#endregion
|
|
9358
9451
|
//#region src/auth/EnvironmentAuthPolicy.ts
|
|
9359
9452
|
var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
|
|
9360
|
-
const make$
|
|
9453
|
+
const make$73 = Effect.gen(function* () {
|
|
9361
9454
|
const config = yield* ServerConfig$1;
|
|
9362
9455
|
const isRemoteReachable = isRemoteReachableHost(config.host);
|
|
9363
9456
|
const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
|
|
@@ -9375,7 +9468,7 @@ const make$71 = Effect.gen(function* () {
|
|
|
9375
9468
|
};
|
|
9376
9469
|
return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
|
|
9377
9470
|
});
|
|
9378
|
-
const layer$
|
|
9471
|
+
const layer$69 = Layer.effect(EnvironmentAuthPolicy, make$73);
|
|
9379
9472
|
//#endregion
|
|
9380
9473
|
//#region src/persistence/Errors.ts
|
|
9381
9474
|
function summarizeSchemaIssue(issue) {
|
|
@@ -9556,7 +9649,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
|
|
|
9556
9649
|
cause
|
|
9557
9650
|
});
|
|
9558
9651
|
}
|
|
9559
|
-
const make$
|
|
9652
|
+
const make$72 = Effect.gen(function* () {
|
|
9560
9653
|
const sql = yield* SqlClient.SqlClient;
|
|
9561
9654
|
const createSessionRow = SqlSchema.void({
|
|
9562
9655
|
Request: CreateAuthSessionInput,
|
|
@@ -9690,7 +9783,7 @@ const make$70 = Effect.gen(function* () {
|
|
|
9690
9783
|
setLastConnectedAt
|
|
9691
9784
|
};
|
|
9692
9785
|
});
|
|
9693
|
-
const layer$
|
|
9786
|
+
const layer$68 = Layer.effect(AuthSessionRepository, make$72);
|
|
9694
9787
|
//#endregion
|
|
9695
9788
|
//#region src/auth/ServerSecretStore.ts
|
|
9696
9789
|
const secretStoreErrorContext = {
|
|
@@ -9757,7 +9850,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
|
|
|
9757
9850
|
const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
|
|
9758
9851
|
const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
|
|
9759
9852
|
var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
|
|
9760
|
-
const make$
|
|
9853
|
+
const make$71 = Effect.gen(function* () {
|
|
9761
9854
|
const crypto = yield* Crypto.Crypto;
|
|
9762
9855
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
9763
9856
|
const path = yield* Path.Path;
|
|
@@ -9827,7 +9920,7 @@ const make$69 = Effect.gen(function* () {
|
|
|
9827
9920
|
remove
|
|
9828
9921
|
});
|
|
9829
9922
|
});
|
|
9830
|
-
const layer$
|
|
9923
|
+
const layer$67 = Layer.effect(ServerSecretStore, make$71);
|
|
9831
9924
|
//#endregion
|
|
9832
9925
|
//#region src/auth/SessionStore.ts
|
|
9833
9926
|
var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
|
|
@@ -10065,7 +10158,7 @@ function toAuthClientSession(input) {
|
|
|
10065
10158
|
current: false
|
|
10066
10159
|
};
|
|
10067
10160
|
}
|
|
10068
|
-
const make$
|
|
10161
|
+
const make$70 = Effect.gen(function* () {
|
|
10069
10162
|
const crypto = yield* Crypto.Crypto;
|
|
10070
10163
|
const serverConfig = yield* ServerConfig$1;
|
|
10071
10164
|
const secretStore = yield* ServerSecretStore;
|
|
@@ -10379,7 +10472,7 @@ const make$68 = Effect.gen(function* () {
|
|
|
10379
10472
|
markDisconnected
|
|
10380
10473
|
});
|
|
10381
10474
|
});
|
|
10382
|
-
const layer$
|
|
10475
|
+
const layer$66 = Layer.effect(SessionStore, make$70).pipe(Layer.provideMerge(layer$68));
|
|
10383
10476
|
//#endregion
|
|
10384
10477
|
//#region src/persistence/AuthPairingLinks.ts
|
|
10385
10478
|
const AuthPairingLinkRecord = Schema$1.Struct({
|
|
@@ -10440,7 +10533,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
|
|
|
10440
10533
|
cause
|
|
10441
10534
|
});
|
|
10442
10535
|
}
|
|
10443
|
-
const make$
|
|
10536
|
+
const make$69 = Effect.gen(function* () {
|
|
10444
10537
|
const sql = yield* SqlClient.SqlClient;
|
|
10445
10538
|
const createPairingLinkRow = SqlSchema.void({
|
|
10446
10539
|
Request: CreateAuthPairingLinkInput,
|
|
@@ -10575,7 +10668,7 @@ const make$67 = Effect.gen(function* () {
|
|
|
10575
10668
|
getByCredential
|
|
10576
10669
|
};
|
|
10577
10670
|
});
|
|
10578
|
-
const layer$
|
|
10671
|
+
const layer$65 = Layer.effect(AuthPairingLinkRepository, make$69);
|
|
10579
10672
|
//#endregion
|
|
10580
10673
|
//#region src/auth/PairingGrantStore.ts
|
|
10581
10674
|
var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
|
|
@@ -10670,7 +10763,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
|
|
|
10670
10763
|
const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
10671
10764
|
const PAIRING_TOKEN_LENGTH = 12;
|
|
10672
10765
|
const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
|
|
10673
|
-
const make$
|
|
10766
|
+
const make$68 = Effect.gen(function* () {
|
|
10674
10767
|
const crypto = yield* Crypto.Crypto;
|
|
10675
10768
|
const config = yield* ServerConfig$1;
|
|
10676
10769
|
const pairingLinks = yield* AuthPairingLinkRepository;
|
|
@@ -10868,7 +10961,7 @@ const make$66 = Effect.gen(function* () {
|
|
|
10868
10961
|
consume
|
|
10869
10962
|
});
|
|
10870
10963
|
});
|
|
10871
|
-
const layer$
|
|
10964
|
+
const layer$64 = Layer.effect(PairingGrantStore, make$68).pipe(Layer.provideMerge(layer$65));
|
|
10872
10965
|
//#endregion
|
|
10873
10966
|
//#region src/persistence/Migrations/001_OrchestrationEvents.ts
|
|
10874
10967
|
var _001_OrchestrationEvents_default = Effect.gen(function* () {
|
|
@@ -12723,7 +12816,7 @@ function parseBearerToken(request) {
|
|
|
12723
12816
|
const token = header.slice(7).trim();
|
|
12724
12817
|
return token.length > 0 ? token : null;
|
|
12725
12818
|
}
|
|
12726
|
-
const make$
|
|
12819
|
+
const make$67 = Effect.gen(function* () {
|
|
12727
12820
|
const policy = yield* EnvironmentAuthPolicy;
|
|
12728
12821
|
const bootstrapCredentials = yield* PairingGrantStore;
|
|
12729
12822
|
const sessions = yield* SessionStore;
|
|
@@ -12918,9 +13011,9 @@ const make$65 = Effect.gen(function* () {
|
|
|
12918
13011
|
issueStartupPairingUrl
|
|
12919
13012
|
});
|
|
12920
13013
|
});
|
|
12921
|
-
const layer$
|
|
12922
|
-
const storageLayer = Layer.mergeAll(layer$
|
|
12923
|
-
const runtimeLayer = layer$
|
|
13014
|
+
const layer$63 = Layer.effect(EnvironmentAuth, make$67).pipe(Layer.provideMerge(layer$64), Layer.provideMerge(layer$66), Layer.provideMerge(layer$69));
|
|
13015
|
+
const storageLayer = Layer.mergeAll(layer$67, layerConfig);
|
|
13016
|
+
const runtimeLayer = layer$63.pipe(Layer.provideMerge(storageLayer));
|
|
12924
13017
|
//#endregion
|
|
12925
13018
|
//#region src/cliAuthFormat.ts
|
|
12926
13019
|
const newline = "\n";
|
|
@@ -13675,7 +13768,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
|
|
|
13675
13768
|
}
|
|
13676
13769
|
return yield* Effect.gen(function* () {
|
|
13677
13770
|
return yield* run(yield* EnvironmentAuth);
|
|
13678
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$
|
|
13771
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$70(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
13679
13772
|
});
|
|
13680
13773
|
const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
|
|
13681
13774
|
const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
|
|
@@ -13815,7 +13908,7 @@ const DEFAULT_LIMITS = {
|
|
|
13815
13908
|
windowMillis: FAILURE_WINDOW_MS,
|
|
13816
13909
|
blockMillis: BLOCK_DURATION_MS
|
|
13817
13910
|
};
|
|
13818
|
-
const make$
|
|
13911
|
+
const make$66 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
|
|
13819
13912
|
const state = yield* Ref.make(initialThrottleState);
|
|
13820
13913
|
return HubAuthThrottle.of({
|
|
13821
13914
|
shouldRefuse: Effect.gen(function* () {
|
|
@@ -13829,7 +13922,7 @@ const make$64 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LI
|
|
|
13829
13922
|
})
|
|
13830
13923
|
});
|
|
13831
13924
|
});
|
|
13832
|
-
const layer$
|
|
13925
|
+
const layer$62 = Layer.effect(HubAuthThrottle, make$66());
|
|
13833
13926
|
//#endregion
|
|
13834
13927
|
//#region src/hub/HubAuth.ts
|
|
13835
13928
|
/**
|
|
@@ -14628,7 +14721,7 @@ const deleteAssetRoute = HttpRouter.add("DELETE", "/assets/:kind/:name", respond
|
|
|
14628
14721
|
const outcome = yield* (yield* AgentAssetRepository).deleteByName(requested);
|
|
14629
14722
|
return outcome._tag === "conflict" ? assetConflict(Option.getOrNull(outcome.current)) : HttpServerResponse.empty({ status: 204 });
|
|
14630
14723
|
})));
|
|
14631
|
-
const layer$
|
|
14724
|
+
const layer$61 = Layer.mergeAll(healthRoute, listTasksRoute, getTaskRoute, createTaskRoute, updateTaskRoute, deleteTaskRoute, listAssetsRoute, getAssetRoute, putAssetRoute, deleteAssetRoute);
|
|
14632
14725
|
//#endregion
|
|
14633
14726
|
//#region src/hub/Migrations/001_Tasks.ts
|
|
14634
14727
|
/**
|
|
@@ -14907,7 +15000,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
|
|
|
14907
15000
|
});
|
|
14908
15001
|
if (isRemoteReachableHost(config.host)) yield* Effect.logWarning("Hub is reachable beyond loopback. The bearer token is the only control — repeated auth failures are not rate limited.", { host: config.host });
|
|
14909
15002
|
}));
|
|
14910
|
-
const makeHubLayer = HttpRouter.serve(layer$
|
|
15003
|
+
const makeHubLayer = HttpRouter.serve(layer$61).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$62), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
|
|
14911
15004
|
const runHub = Layer.launch(makeHubLayer);
|
|
14912
15005
|
//#endregion
|
|
14913
15006
|
//#region src/atomicWrite.ts
|
|
@@ -15031,7 +15124,7 @@ function stripDefaultServerSettings(current, defaults) {
|
|
|
15031
15124
|
}
|
|
15032
15125
|
return Object.is(current, defaults) ? void 0 : current;
|
|
15033
15126
|
}
|
|
15034
|
-
const make$
|
|
15127
|
+
const make$65 = Effect.gen(function* () {
|
|
15035
15128
|
const { settingsPath } = yield* ServerConfig$1;
|
|
15036
15129
|
const fs = yield* FileSystem.FileSystem;
|
|
15037
15130
|
const pathService = yield* Path.Path;
|
|
@@ -15252,7 +15345,7 @@ const make$63 = Effect.gen(function* () {
|
|
|
15252
15345
|
}
|
|
15253
15346
|
};
|
|
15254
15347
|
});
|
|
15255
|
-
const layer$
|
|
15348
|
+
const layer$60 = Layer.effect(ServerSettingsService, make$65);
|
|
15256
15349
|
//#endregion
|
|
15257
15350
|
//#region src/pathExpansion.ts
|
|
15258
15351
|
/**
|
|
@@ -15461,6 +15554,650 @@ const discoverClaudeSkills = Effect.fn("discoverClaudeSkills")(function* (config
|
|
|
15461
15554
|
}
|
|
15462
15555
|
return [...skillsByName.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
15463
15556
|
});
|
|
15557
|
+
//#endregion
|
|
15558
|
+
//#region src/mcp/ClaudeMcpFiles.ts
|
|
15559
|
+
/**
|
|
15560
|
+
* The MCP servers in Claude Code's own configuration files.
|
|
15561
|
+
*
|
|
15562
|
+
* Two homes, both the CLI's and both primary over p4code's registry:
|
|
15563
|
+
*
|
|
15564
|
+
* - **User scope**: the `mcpServers` key of `~/.claude.json`. Every Claude
|
|
15565
|
+
* Code session reads it, inside p4code and out.
|
|
15566
|
+
* - **Project scope**: `<project>/.mcp.json`, committed with the repository
|
|
15567
|
+
* and read by every session working in it.
|
|
15568
|
+
*
|
|
15569
|
+
* The settings panel manages both directly — list, add, edit, remove — so
|
|
15570
|
+
* this service writes them. **A user-scope write touches only the
|
|
15571
|
+
* `mcpServers` key**; `~/.claude.json` also holds `oauthAccount`, `machineID`
|
|
15572
|
+
* and a `projects` map, and everything outside the one key is carried
|
|
15573
|
+
* through a write as parsed. This is the same read-modify-write the
|
|
15574
|
+
* `claude mcp add` command performs, with the same last-writer-wins caveat
|
|
15575
|
+
* when a CLI session saves concurrently.
|
|
15576
|
+
*
|
|
15577
|
+
* A malformed entry, a name that would not survive as a registry filename, or
|
|
15578
|
+
* a missing file all cost only what they describe: that entry, or that one
|
|
15579
|
+
* file's list, never the panel.
|
|
15580
|
+
*
|
|
15581
|
+
* @module mcp/ClaudeMcpFiles
|
|
15582
|
+
*/
|
|
15583
|
+
const isServerName = Schema$1.is(McpServerName);
|
|
15584
|
+
/** The file a project's scope lives in, beside its lockfiles and dotfiles. */
|
|
15585
|
+
const PROJECT_MCP_FILENAME = ".mcp.json";
|
|
15586
|
+
/**
|
|
15587
|
+
* One entry as the CLI writes it. `type` is absent for the CLI's original
|
|
15588
|
+
* stdio-only shape, so it defaults rather than discriminates.
|
|
15589
|
+
*/
|
|
15590
|
+
const ClaudeMcpEntry = Schema$1.Struct({
|
|
15591
|
+
type: Schema$1.optional(Schema$1.Literals([
|
|
15592
|
+
"stdio",
|
|
15593
|
+
"http",
|
|
15594
|
+
"sse"
|
|
15595
|
+
])),
|
|
15596
|
+
command: Schema$1.optional(Schema$1.String),
|
|
15597
|
+
args: Schema$1.optional(Schema$1.Array(Schema$1.String)),
|
|
15598
|
+
env: Schema$1.optional(Schema$1.Record(Schema$1.String, Schema$1.String)),
|
|
15599
|
+
url: Schema$1.optional(Schema$1.String),
|
|
15600
|
+
headers: Schema$1.optional(Schema$1.Record(Schema$1.String, Schema$1.String))
|
|
15601
|
+
});
|
|
15602
|
+
/**
|
|
15603
|
+
* A config file as this service needs to see it: the one key it owns, with
|
|
15604
|
+
* every other key carried as parsed. Both `~/.claude.json` and `.mcp.json`
|
|
15605
|
+
* decode through this — the former has many other keys, the latter none.
|
|
15606
|
+
*/
|
|
15607
|
+
const McpConfigFile = Schema$1.StructWithRest(Schema$1.Struct({ mcpServers: Schema$1.optional(Schema$1.Record(Schema$1.String, Schema$1.Unknown)) }), [Schema$1.Record(Schema$1.String, Schema$1.Unknown)]);
|
|
15608
|
+
const McpConfigFileFromJson = Schema$1.fromJsonString(McpConfigFile);
|
|
15609
|
+
const decodeConfigFile = Schema$1.decodeUnknownExit(McpConfigFileFromJson);
|
|
15610
|
+
const encodeConfigFile = Schema$1.encodeSync(McpConfigFileFromJson);
|
|
15611
|
+
const decodeEntry = Schema$1.decodeUnknownExit(ClaudeMcpEntry);
|
|
15612
|
+
/** Where the CLI keeps user scope: `$CLAUDE_CONFIG_DIR/.claude.json` when set, else `~/.claude.json`. */
|
|
15613
|
+
const claudeUserConfigPath = Effect.fn("claudeUserConfigPath")(function* () {
|
|
15614
|
+
const path = yield* Path.Path;
|
|
15615
|
+
const configDir = process.env["CLAUDE_CONFIG_DIR"]?.trim() ?? "";
|
|
15616
|
+
return configDir.length > 0 ? path.join(path.resolve(configDir), ".claude.json") : path.join(NodeOS.homedir(), ".claude.json");
|
|
15617
|
+
});
|
|
15618
|
+
/**
|
|
15619
|
+
* Map one CLI entry to a registration, or nothing when it cannot be one.
|
|
15620
|
+
*
|
|
15621
|
+
* `enabled: true` because presence in the CLI's file is what enabled means
|
|
15622
|
+
* there. Exported for tests.
|
|
15623
|
+
*/
|
|
15624
|
+
function registrationFromClaudeEntry(name, entry) {
|
|
15625
|
+
if (!isServerName(name)) return void 0;
|
|
15626
|
+
const decoded = decodeEntry(entry);
|
|
15627
|
+
if (decoded._tag !== "Success") return void 0;
|
|
15628
|
+
const value = decoded.value;
|
|
15629
|
+
const type = value.type ?? "stdio";
|
|
15630
|
+
if (type === "stdio") {
|
|
15631
|
+
if (value.command === void 0 || value.command.trim().length === 0) return void 0;
|
|
15632
|
+
return {
|
|
15633
|
+
name,
|
|
15634
|
+
transport: "stdio",
|
|
15635
|
+
enabled: true,
|
|
15636
|
+
command: value.command,
|
|
15637
|
+
...value.args !== void 0 && value.args.length > 0 ? { args: value.args } : {},
|
|
15638
|
+
...value.env !== void 0 && Object.keys(value.env).length > 0 ? { env: value.env } : {}
|
|
15639
|
+
};
|
|
15640
|
+
}
|
|
15641
|
+
if (value.url === void 0 || value.url.trim().length === 0) return void 0;
|
|
15642
|
+
return {
|
|
15643
|
+
name,
|
|
15644
|
+
transport: type,
|
|
15645
|
+
enabled: true,
|
|
15646
|
+
url: value.url,
|
|
15647
|
+
...value.headers !== void 0 && Object.keys(value.headers).length > 0 ? { headers: value.headers } : {}
|
|
15648
|
+
};
|
|
15649
|
+
}
|
|
15650
|
+
/**
|
|
15651
|
+
* The inverse mapping, for writes. `enabled` is dropped: the CLI has no such
|
|
15652
|
+
* flag, and presence in the file is what enabled means there. Exported for
|
|
15653
|
+
* tests, so the two mappings can be checked as a round trip.
|
|
15654
|
+
*/
|
|
15655
|
+
function claudeEntryFromRegistration(registration) {
|
|
15656
|
+
if (registration.transport === "stdio") return {
|
|
15657
|
+
type: "stdio",
|
|
15658
|
+
command: registration.command,
|
|
15659
|
+
...registration.args !== void 0 && registration.args.length > 0 ? { args: registration.args } : {},
|
|
15660
|
+
...registration.env !== void 0 && Object.keys(registration.env).length > 0 ? { env: registration.env } : {}
|
|
15661
|
+
};
|
|
15662
|
+
return {
|
|
15663
|
+
type: registration.transport,
|
|
15664
|
+
url: registration.url,
|
|
15665
|
+
...registration.headers !== void 0 && Object.keys(registration.headers).length > 0 ? { headers: registration.headers } : {}
|
|
15666
|
+
};
|
|
15667
|
+
}
|
|
15668
|
+
var ClaudeMcpFiles = class extends Context.Service()("@p4code/cli/mcp/ClaudeMcpFiles") {};
|
|
15669
|
+
const make$64 = Effect.gen(function* () {
|
|
15670
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
15671
|
+
const path = yield* Path.Path;
|
|
15672
|
+
const services = yield* Effect.context();
|
|
15673
|
+
const readEntries = Effect.fn("ClaudeMcpFiles.readEntries")(function* (filePath) {
|
|
15674
|
+
const raw = yield* fileSystem.readFileString(filePath).pipe(Effect.orElseSucceed(() => void 0));
|
|
15675
|
+
if (raw === void 0) return [];
|
|
15676
|
+
const decoded = decodeConfigFile(raw);
|
|
15677
|
+
if (decoded._tag !== "Success") return [];
|
|
15678
|
+
const found = [];
|
|
15679
|
+
for (const [name, entry] of Object.entries(decoded.value.mcpServers ?? {})) {
|
|
15680
|
+
const registration = registrationFromClaudeEntry(name, entry);
|
|
15681
|
+
if (registration !== void 0) found.push(registration);
|
|
15682
|
+
}
|
|
15683
|
+
return found;
|
|
15684
|
+
});
|
|
15685
|
+
const readAt = (filePath) => filePath.pipe(Effect.flatMap((resolved) => readEntries(resolved)), Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("claude mcp config read failed", { cause }).pipe(Effect.as([]))));
|
|
15686
|
+
/**
|
|
15687
|
+
* Read, change the one key, write back atomically.
|
|
15688
|
+
*
|
|
15689
|
+
* A file that exists but will not parse fails the write loudly instead of
|
|
15690
|
+
* being replaced with `{ mcpServers }`: whatever is in it — including a
|
|
15691
|
+
* `~/.claude.json` full of the CLI's own state — is worth more than the
|
|
15692
|
+
* edit. A missing file starts from empty, which is how `.mcp.json` is born.
|
|
15693
|
+
*/
|
|
15694
|
+
const changeFile = Effect.fn("ClaudeMcpFiles.changeFile")(function* (filePath, change) {
|
|
15695
|
+
const raw = yield* fileSystem.readFileString(filePath).pipe(Effect.orElseSucceed(() => void 0), Effect.provide(services));
|
|
15696
|
+
const decoded = raw === void 0 ? void 0 : decodeConfigFile(raw);
|
|
15697
|
+
if (decoded !== void 0 && decoded._tag !== "Success") return yield* new McpRegistryError({ detail: `${filePath} exists but could not be parsed, so it was left alone.` });
|
|
15698
|
+
const root = decoded === void 0 ? {} : decoded.value;
|
|
15699
|
+
const nextServers = change({ ...root.mcpServers });
|
|
15700
|
+
yield* fileSystem.makeDirectory(path.dirname(filePath), { recursive: true }).pipe(Effect.ignore, Effect.provide(services));
|
|
15701
|
+
yield* writeFileStringAtomically({
|
|
15702
|
+
filePath,
|
|
15703
|
+
contents: `${encodeConfigFile({
|
|
15704
|
+
...root,
|
|
15705
|
+
mcpServers: nextServers
|
|
15706
|
+
})}\n`
|
|
15707
|
+
}).pipe(Effect.provide(services), Effect.mapError(() => new McpRegistryError({ detail: `Could not write ${filePath}.` })));
|
|
15708
|
+
});
|
|
15709
|
+
const projectFile = (projectDir) => path.join(path.resolve(projectDir), PROJECT_MCP_FILENAME);
|
|
15710
|
+
const upsertAt = (filePath) => Effect.fn("ClaudeMcpFiles.upsertAt")(function* (registration) {
|
|
15711
|
+
const resolved = yield* filePath.pipe(Effect.provide(services));
|
|
15712
|
+
yield* changeFile(resolved, (entries) => ({
|
|
15713
|
+
...entries,
|
|
15714
|
+
[registration.name]: claudeEntryFromRegistration(registration)
|
|
15715
|
+
}));
|
|
15716
|
+
});
|
|
15717
|
+
const removeAt = (filePath) => Effect.fn("ClaudeMcpFiles.removeAt")(function* (name) {
|
|
15718
|
+
const resolved = yield* filePath.pipe(Effect.provide(services));
|
|
15719
|
+
yield* changeFile(resolved, (entries) => {
|
|
15720
|
+
const { [name]: _removed, ...rest } = entries;
|
|
15721
|
+
return rest;
|
|
15722
|
+
});
|
|
15723
|
+
});
|
|
15724
|
+
const userPath = claudeUserConfigPath();
|
|
15725
|
+
return {
|
|
15726
|
+
readUser: readAt(userPath),
|
|
15727
|
+
upsertUser: (registration) => upsertAt(userPath)(registration),
|
|
15728
|
+
removeUser: (name) => removeAt(userPath)(name),
|
|
15729
|
+
readProject: (projectDir) => readAt(Effect.succeed(projectFile(projectDir))),
|
|
15730
|
+
upsertProject: (projectDir, registration) => upsertAt(Effect.succeed(projectFile(projectDir)))(registration),
|
|
15731
|
+
removeProject: (projectDir, name) => removeAt(Effect.succeed(projectFile(projectDir)))(name)
|
|
15732
|
+
};
|
|
15733
|
+
});
|
|
15734
|
+
const layer$59 = Layer.effect(ClaudeMcpFiles, make$64);
|
|
15735
|
+
Layer.succeed(ClaudeMcpFiles, {
|
|
15736
|
+
readUser: Effect.succeed([]),
|
|
15737
|
+
upsertUser: () => Effect.fail(new McpRegistryError({ detail: "No Claude config in tests." })),
|
|
15738
|
+
removeUser: () => Effect.fail(new McpRegistryError({ detail: "No Claude config in tests." })),
|
|
15739
|
+
readProject: () => Effect.succeed([]),
|
|
15740
|
+
upsertProject: () => Effect.fail(new McpRegistryError({ detail: "No Claude config in tests." })),
|
|
15741
|
+
removeProject: () => Effect.fail(new McpRegistryError({ detail: "No Claude config in tests." }))
|
|
15742
|
+
});
|
|
15743
|
+
//#endregion
|
|
15744
|
+
//#region src/mcp/McpOAuth.ts
|
|
15745
|
+
/**
|
|
15746
|
+
* OAuth sign-in for remote MCP servers, per the MCP authorization spec.
|
|
15747
|
+
*
|
|
15748
|
+
* The whole flow runs on this server: RFC 9728 protected-resource discovery,
|
|
15749
|
+
* RFC 8414 authorization-server metadata, RFC 7591 dynamic client
|
|
15750
|
+
* registration, PKCE, and the token exchange when the browser lands on
|
|
15751
|
+
* `/oauth/callback`. The panel only ever sees one string — the authorize URL —
|
|
15752
|
+
* because everything else in the flow is either a secret or only meaningful to
|
|
15753
|
+
* the machine the callback returns to.
|
|
15754
|
+
*
|
|
15755
|
+
* **Everything learned per server is one JSON secret** in `ServerSecretStore`
|
|
15756
|
+
* (`mcp.<server>.oauth`): client id, endpoints, tokens. Registrations sync
|
|
15757
|
+
* between machines; this record deliberately does not, which is what makes
|
|
15758
|
+
* "each machine signs in for itself" true by construction rather than policy.
|
|
15759
|
+
*
|
|
15760
|
+
* @module mcp/McpOAuth
|
|
15761
|
+
*/
|
|
15762
|
+
/** How long a probe's verdict is trusted before the server is asked again. */
|
|
15763
|
+
const PROBE_CACHE_TTL_MS = 300 * 1e3;
|
|
15764
|
+
/** An authorize URL nobody returned from within this window is abandoned. */
|
|
15765
|
+
const PENDING_FLOW_TTL_MS = 600 * 1e3;
|
|
15766
|
+
/** A token this close to expiry is refreshed rather than handed to a session. */
|
|
15767
|
+
const TOKEN_EXPIRY_MARGIN_MS = 60 * 1e3;
|
|
15768
|
+
/** Outbound discovery, registration and token requests all use this cap. */
|
|
15769
|
+
const OAUTH_REQUEST_TIMEOUT_MS = 10 * 1e3;
|
|
15770
|
+
const CODE_VERIFIER_BYTES = 32;
|
|
15771
|
+
const STATE_BYTES = 16;
|
|
15772
|
+
/** Sent in the probe's initialize request; any spec revision the server accepts will do. */
|
|
15773
|
+
const MCP_PROBE_PROTOCOL_VERSION = "2025-06-18";
|
|
15774
|
+
/** What the authorization server's consent screen shows. See `registerClient`. */
|
|
15775
|
+
const OAUTH_CLIENT_NAME = "Claude Code (p4code)";
|
|
15776
|
+
/** The path the environment server answers on; also sent as the redirect URI. */
|
|
15777
|
+
const OAUTH_CALLBACK_PATH = "/oauth/callback";
|
|
15778
|
+
const StoredTokens = Schema$1.Struct({
|
|
15779
|
+
accessToken: Schema$1.String,
|
|
15780
|
+
refreshToken: Schema$1.optional(Schema$1.String),
|
|
15781
|
+
/** Epoch milliseconds. Absent when the server did not say. */
|
|
15782
|
+
expiresAt: Schema$1.optional(Schema$1.Number)
|
|
15783
|
+
});
|
|
15784
|
+
/**
|
|
15785
|
+
* Everything this machine holds about one server's OAuth relationship.
|
|
15786
|
+
*
|
|
15787
|
+
* Endpoints and client id live beside the tokens so that disconnect can drop
|
|
15788
|
+
* the tokens and a later reconnect can skip re-registration — a client id is
|
|
15789
|
+
* not a credential, but re-registering on every sign-in litters the
|
|
15790
|
+
* authorization server with one-use clients.
|
|
15791
|
+
*/
|
|
15792
|
+
const StoredOAuth = Schema$1.Struct({
|
|
15793
|
+
clientId: Schema$1.String,
|
|
15794
|
+
clientSecret: Schema$1.optional(Schema$1.String),
|
|
15795
|
+
authorizationEndpoint: Schema$1.String,
|
|
15796
|
+
tokenEndpoint: Schema$1.String,
|
|
15797
|
+
/** RFC 8707 resource indicator: the canonical MCP server URL. */
|
|
15798
|
+
resource: Schema$1.optional(Schema$1.String),
|
|
15799
|
+
scope: Schema$1.optional(Schema$1.String),
|
|
15800
|
+
tokens: Schema$1.optional(StoredTokens)
|
|
15801
|
+
});
|
|
15802
|
+
const StoredOAuthFromJson = Schema$1.fromJsonString(StoredOAuth);
|
|
15803
|
+
const decodeStored = Schema$1.decodeUnknownExit(StoredOAuthFromJson);
|
|
15804
|
+
const encodeStored = Schema$1.encodeSync(StoredOAuthFromJson);
|
|
15805
|
+
/**
|
|
15806
|
+
* Parse a `WWW-Authenticate` challenge into its scheme and parameters.
|
|
15807
|
+
*
|
|
15808
|
+
* Handles the shape MCP servers actually send — `Bearer` followed by
|
|
15809
|
+
* comma-separated `key="value"` or bare `key=value` pairs. Exported for tests;
|
|
15810
|
+
* a wrong parse here turns "needs sign-in" into "broken server".
|
|
15811
|
+
*/
|
|
15812
|
+
function parseWwwAuthenticate(header) {
|
|
15813
|
+
const trimmed = header.trim();
|
|
15814
|
+
const spaceIndex = trimmed.indexOf(" ");
|
|
15815
|
+
const scheme = (spaceIndex === -1 ? trimmed : trimmed.slice(0, spaceIndex)).toLowerCase();
|
|
15816
|
+
const rest = spaceIndex === -1 ? "" : trimmed.slice(spaceIndex + 1);
|
|
15817
|
+
const params = {};
|
|
15818
|
+
for (const match of rest.matchAll(/([a-zA-Z0-9_-]+)\s*=\s*(?:"([^"]*)"|([^\s,]+))/gu)) {
|
|
15819
|
+
const key = match[1];
|
|
15820
|
+
const value = match[2] ?? match[3];
|
|
15821
|
+
if (key !== void 0 && value !== void 0) params[key.toLowerCase()] = value;
|
|
15822
|
+
}
|
|
15823
|
+
return {
|
|
15824
|
+
scheme,
|
|
15825
|
+
params
|
|
15826
|
+
};
|
|
15827
|
+
}
|
|
15828
|
+
/**
|
|
15829
|
+
* The RFC 8414 lookup locations for an issuer, in the order worth trying.
|
|
15830
|
+
*
|
|
15831
|
+
* Path insertion first — `https://a/.well-known/oauth-authorization-server/x`
|
|
15832
|
+
* for issuer `https://a/x` — then the OIDC variants, because real servers are
|
|
15833
|
+
* split between the two specs and a miss costs one request.
|
|
15834
|
+
*/
|
|
15835
|
+
function authServerMetadataUrls(issuer) {
|
|
15836
|
+
const url = new URL(issuer);
|
|
15837
|
+
const path = url.pathname === "/" ? "" : url.pathname.replace(/\/$/u, "");
|
|
15838
|
+
return [
|
|
15839
|
+
`${url.origin}/.well-known/oauth-authorization-server${path}`,
|
|
15840
|
+
`${url.origin}/.well-known/openid-configuration${path}`,
|
|
15841
|
+
...path === "" ? [] : [`${url.origin}${path}/.well-known/openid-configuration`]
|
|
15842
|
+
];
|
|
15843
|
+
}
|
|
15844
|
+
const AuthServerMetadata = Schema$1.Struct({
|
|
15845
|
+
authorization_endpoint: Schema$1.String,
|
|
15846
|
+
token_endpoint: Schema$1.String,
|
|
15847
|
+
registration_endpoint: Schema$1.optional(Schema$1.String),
|
|
15848
|
+
scopes_supported: Schema$1.optional(Schema$1.Array(Schema$1.String))
|
|
15849
|
+
});
|
|
15850
|
+
const ProtectedResourceMetadata = Schema$1.Struct({
|
|
15851
|
+
authorization_servers: Schema$1.optional(Schema$1.Array(Schema$1.String)),
|
|
15852
|
+
scopes_supported: Schema$1.optional(Schema$1.Array(Schema$1.String))
|
|
15853
|
+
});
|
|
15854
|
+
const ClientRegistrationResponse = Schema$1.Struct({
|
|
15855
|
+
client_id: Schema$1.String,
|
|
15856
|
+
client_secret: Schema$1.optional(Schema$1.String)
|
|
15857
|
+
});
|
|
15858
|
+
const TokenResponse = Schema$1.Struct({
|
|
15859
|
+
access_token: Schema$1.String,
|
|
15860
|
+
refresh_token: Schema$1.optional(Schema$1.String),
|
|
15861
|
+
expires_in: Schema$1.optional(Schema$1.Number)
|
|
15862
|
+
});
|
|
15863
|
+
const decodeJsonBody = Schema$1.decodeUnknownExit(Schema$1.UnknownFromJsonString);
|
|
15864
|
+
const decodeAuthServerMetadata = Schema$1.decodeUnknownExit(AuthServerMetadata);
|
|
15865
|
+
const decodeProtectedResourceMetadata = Schema$1.decodeUnknownExit(ProtectedResourceMetadata);
|
|
15866
|
+
const decodeClientRegistration = Schema$1.decodeUnknownExit(ClientRegistrationResponse);
|
|
15867
|
+
const decodeTokenResponse = Schema$1.decodeUnknownExit(TokenResponse);
|
|
15868
|
+
var McpOAuth = class extends Context.Service()("@p4code/cli/mcp/McpOAuth") {};
|
|
15869
|
+
const registryError = (detail) => new McpRegistryError({ detail });
|
|
15870
|
+
const make$63 = Effect.gen(function* () {
|
|
15871
|
+
const config = yield* ServerConfig$1;
|
|
15872
|
+
const secrets = yield* ServerSecretStore;
|
|
15873
|
+
const http = yield* HttpClient.HttpClient;
|
|
15874
|
+
const crypto = yield* Crypto.Crypto;
|
|
15875
|
+
const pendingFlows = yield* Ref.make(/* @__PURE__ */ new Map());
|
|
15876
|
+
const probeCache = yield* Ref.make(/* @__PURE__ */ new Map());
|
|
15877
|
+
const redirectUri = `http://127.0.0.1:${config.port}${OAUTH_CALLBACK_PATH}`;
|
|
15878
|
+
const nowMillis = Effect.map(DateTime.now, DateTime.toEpochMillis);
|
|
15879
|
+
const readRecord = Effect.fn("McpOAuth.readRecord")(function* (server) {
|
|
15880
|
+
const stored = yield* secrets.get(mcpOAuthStorageName(server)).pipe(Effect.orElseSucceed(() => Option.none()));
|
|
15881
|
+
if (Option.isNone(stored)) return Option.none();
|
|
15882
|
+
const decoded = decodeStored(new TextDecoder().decode(stored.value));
|
|
15883
|
+
return decoded._tag === "Success" ? Option.some(decoded.value) : Option.none();
|
|
15884
|
+
});
|
|
15885
|
+
const writeRecord = Effect.fn("McpOAuth.writeRecord")(function* (server, record) {
|
|
15886
|
+
yield* secrets.set(mcpOAuthStorageName(server), new TextEncoder().encode(encodeStored(record))).pipe(Effect.catchCause((cause) => Effect.logWarning("mcp oauth record write failed", {
|
|
15887
|
+
cause,
|
|
15888
|
+
server
|
|
15889
|
+
})));
|
|
15890
|
+
});
|
|
15891
|
+
const fetchJson = Effect.fn("McpOAuth.fetchJson")(function* (request) {
|
|
15892
|
+
const response = yield* http.execute(HttpClientRequest.setHeader(request, "accept", "application/json")).pipe(Effect.timeout(OAUTH_REQUEST_TIMEOUT_MS));
|
|
15893
|
+
const text = yield* response.text;
|
|
15894
|
+
const parsed = decodeJsonBody(text);
|
|
15895
|
+
return {
|
|
15896
|
+
status: response.status,
|
|
15897
|
+
json: parsed._tag === "Success" ? parsed.value : void 0
|
|
15898
|
+
};
|
|
15899
|
+
});
|
|
15900
|
+
/**
|
|
15901
|
+
* Ask the server, without credentials, whether it wants any.
|
|
15902
|
+
*
|
|
15903
|
+
* A real MCP initialize request rather than a bare GET, because the GET is
|
|
15904
|
+
* not part of the streamable-http contract and real servers 405 it — Figma
|
|
15905
|
+
* does — while answering the initialize POST with the 401 challenge this
|
|
15906
|
+
* probe exists to see.
|
|
15907
|
+
*/
|
|
15908
|
+
const probe = Effect.fn("McpOAuth.probe")(function* (registration) {
|
|
15909
|
+
const now = yield* nowMillis;
|
|
15910
|
+
const cached = (yield* Ref.get(probeCache)).get(registration.url);
|
|
15911
|
+
if (cached !== void 0 && now - cached.at < PROBE_CACHE_TTL_MS) return cached;
|
|
15912
|
+
const verdict = yield* http.execute(HttpClientRequest.bodyJsonUnsafe(HttpClientRequest.setHeaders(HttpClientRequest.post(registration.url), {
|
|
15913
|
+
...registration.headers,
|
|
15914
|
+
accept: "application/json, text/event-stream"
|
|
15915
|
+
}), {
|
|
15916
|
+
jsonrpc: "2.0",
|
|
15917
|
+
id: 0,
|
|
15918
|
+
method: "initialize",
|
|
15919
|
+
params: {
|
|
15920
|
+
protocolVersion: MCP_PROBE_PROTOCOL_VERSION,
|
|
15921
|
+
capabilities: {},
|
|
15922
|
+
clientInfo: {
|
|
15923
|
+
name: "p4code",
|
|
15924
|
+
version: "0.0.0"
|
|
15925
|
+
}
|
|
15926
|
+
}
|
|
15927
|
+
})).pipe(Effect.timeout(OAUTH_REQUEST_TIMEOUT_MS), Effect.map((response) => {
|
|
15928
|
+
const challengeHeader = response.headers["www-authenticate"];
|
|
15929
|
+
const challenge = response.status === 401 && challengeHeader !== void 0 ? parseWwwAuthenticate(challengeHeader) : void 0;
|
|
15930
|
+
return {
|
|
15931
|
+
at: now,
|
|
15932
|
+
requiresAuth: response.status === 401,
|
|
15933
|
+
resourceMetadataUrl: challenge?.params["resource_metadata"],
|
|
15934
|
+
scope: challenge?.params["scope"]
|
|
15935
|
+
};
|
|
15936
|
+
}), Effect.orElseSucceed(() => ({
|
|
15937
|
+
at: now,
|
|
15938
|
+
requiresAuth: false,
|
|
15939
|
+
resourceMetadataUrl: void 0,
|
|
15940
|
+
scope: void 0
|
|
15941
|
+
})));
|
|
15942
|
+
yield* Ref.update(probeCache, (cache) => {
|
|
15943
|
+
const next = new Map(cache);
|
|
15944
|
+
next.set(registration.url, verdict);
|
|
15945
|
+
return next;
|
|
15946
|
+
});
|
|
15947
|
+
return verdict;
|
|
15948
|
+
});
|
|
15949
|
+
const forgetProbe = (url) => Ref.update(probeCache, (cache) => {
|
|
15950
|
+
const next = new Map(cache);
|
|
15951
|
+
next.delete(url);
|
|
15952
|
+
return next;
|
|
15953
|
+
});
|
|
15954
|
+
const statusFor = (registration) => Effect.gen(function* () {
|
|
15955
|
+
const record = yield* readRecord(registration.name);
|
|
15956
|
+
if (Option.isSome(record) && record.value.tokens !== void 0) return Option.some({
|
|
15957
|
+
required: true,
|
|
15958
|
+
connected: true
|
|
15959
|
+
});
|
|
15960
|
+
if ((yield* probe(registration)).requiresAuth || Option.isSome(record)) return Option.some({
|
|
15961
|
+
required: true,
|
|
15962
|
+
connected: false
|
|
15963
|
+
});
|
|
15964
|
+
return Option.none();
|
|
15965
|
+
}).pipe(Effect.catchCause((cause) => Effect.logWarning("mcp oauth status failed", {
|
|
15966
|
+
cause,
|
|
15967
|
+
server: registration.name
|
|
15968
|
+
}).pipe(Effect.as(Option.none()))));
|
|
15969
|
+
/**
|
|
15970
|
+
* Find the authorization server for an MCP endpoint.
|
|
15971
|
+
*
|
|
15972
|
+
* RFC 9728 first: the 401 challenge names a protected-resource metadata URL,
|
|
15973
|
+
* which names the authorization servers. When the challenge does not say —
|
|
15974
|
+
* the spec's fallback — the MCP origin itself is tried as the issuer.
|
|
15975
|
+
*/
|
|
15976
|
+
const discover = Effect.fn("McpOAuth.discover")(function* (registration) {
|
|
15977
|
+
yield* forgetProbe(registration.url);
|
|
15978
|
+
const verdict = yield* probe(registration);
|
|
15979
|
+
let issuers = [new URL(registration.url).origin];
|
|
15980
|
+
let resourceScopes;
|
|
15981
|
+
const metadataUrl = verdict.resourceMetadataUrl ?? `${new URL(registration.url).origin}/.well-known/oauth-protected-resource${new URL(registration.url).pathname.replace(/\/$/u, "")}`;
|
|
15982
|
+
const resourceMetadata = yield* fetchJson(HttpClientRequest.get(metadataUrl)).pipe(Effect.map(({ status, json }) => {
|
|
15983
|
+
if (status !== 200) return void 0;
|
|
15984
|
+
const decoded = decodeProtectedResourceMetadata(json);
|
|
15985
|
+
return decoded._tag === "Success" ? decoded.value : void 0;
|
|
15986
|
+
}), Effect.orElseSucceed(() => void 0));
|
|
15987
|
+
if (resourceMetadata !== void 0) {
|
|
15988
|
+
if ((resourceMetadata.authorization_servers?.length ?? 0) > 0) issuers = resourceMetadata.authorization_servers ?? issuers;
|
|
15989
|
+
resourceScopes = resourceMetadata.scopes_supported;
|
|
15990
|
+
}
|
|
15991
|
+
for (const issuer of issuers) for (const candidate of authServerMetadataUrls(issuer)) {
|
|
15992
|
+
const metadata = yield* fetchJson(HttpClientRequest.get(candidate)).pipe(Effect.map(({ status, json }) => {
|
|
15993
|
+
if (status !== 200) return void 0;
|
|
15994
|
+
const decoded = decodeAuthServerMetadata(json);
|
|
15995
|
+
return decoded._tag === "Success" ? decoded.value : void 0;
|
|
15996
|
+
}), Effect.orElseSucceed(() => void 0));
|
|
15997
|
+
if (metadata !== void 0) return {
|
|
15998
|
+
metadata,
|
|
15999
|
+
scope: verdict.scope ?? (resourceScopes !== void 0 && resourceScopes.length > 0 ? resourceScopes.join(" ") : void 0)
|
|
16000
|
+
};
|
|
16001
|
+
}
|
|
16002
|
+
return yield* registryError(`Could not discover an OAuth authorization server for ${registration.url}.`);
|
|
16003
|
+
});
|
|
16004
|
+
const registerClient = Effect.fn("McpOAuth.registerClient")(function* (metadata) {
|
|
16005
|
+
if (metadata.registration_endpoint === void 0) return yield* registryError("The authorization server does not support dynamic client registration.");
|
|
16006
|
+
const { status, json } = yield* fetchJson(HttpClientRequest.bodyJsonUnsafe(HttpClientRequest.post(metadata.registration_endpoint), {
|
|
16007
|
+
client_name: OAUTH_CLIENT_NAME,
|
|
16008
|
+
redirect_uris: [redirectUri],
|
|
16009
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
16010
|
+
response_types: ["code"],
|
|
16011
|
+
token_endpoint_auth_method: "none"
|
|
16012
|
+
})).pipe(Effect.mapError(() => registryError("Client registration request failed.")));
|
|
16013
|
+
if (status !== 200 && status !== 201) return yield* registryError(`Client registration was rejected with status ${status}.`);
|
|
16014
|
+
const decoded = decodeClientRegistration(json);
|
|
16015
|
+
if (decoded._tag !== "Success") return yield* registryError("Client registration answered an unexpected shape.");
|
|
16016
|
+
return decoded.value;
|
|
16017
|
+
});
|
|
16018
|
+
const randomUrlSafe = (bytes) => crypto.randomBytes(bytes).pipe(Effect.map(Encoding.encodeBase64Url));
|
|
16019
|
+
const begin = (registration) => Effect.gen(function* () {
|
|
16020
|
+
const { metadata, scope } = yield* discover(registration);
|
|
16021
|
+
const existing = yield* readRecord(registration.name);
|
|
16022
|
+
const reusable = Option.filter(existing, (record) => record.tokenEndpoint === metadata.token_endpoint && record.authorizationEndpoint === metadata.authorization_endpoint);
|
|
16023
|
+
const client = Option.isSome(reusable) ? {
|
|
16024
|
+
client_id: reusable.value.clientId,
|
|
16025
|
+
client_secret: reusable.value.clientSecret
|
|
16026
|
+
} : yield* registerClient(metadata);
|
|
16027
|
+
const codeVerifier = yield* randomUrlSafe(CODE_VERIFIER_BYTES).pipe(Effect.mapError(() => registryError("Could not generate a PKCE verifier.")));
|
|
16028
|
+
const challengeBytes = yield* crypto.digest("SHA-256", new TextEncoder().encode(codeVerifier)).pipe(Effect.mapError(() => registryError("Could not hash the PKCE verifier.")));
|
|
16029
|
+
const state = yield* randomUrlSafe(STATE_BYTES).pipe(Effect.mapError(() => registryError("Could not generate a state value.")));
|
|
16030
|
+
const record = {
|
|
16031
|
+
clientId: client.client_id,
|
|
16032
|
+
...client.client_secret === void 0 ? {} : { clientSecret: client.client_secret },
|
|
16033
|
+
authorizationEndpoint: metadata.authorization_endpoint,
|
|
16034
|
+
tokenEndpoint: metadata.token_endpoint,
|
|
16035
|
+
resource: registration.url,
|
|
16036
|
+
...scope === void 0 ? {} : { scope }
|
|
16037
|
+
};
|
|
16038
|
+
yield* writeRecord(registration.name, record);
|
|
16039
|
+
const startedAt = yield* nowMillis;
|
|
16040
|
+
yield* Ref.update(pendingFlows, (flows) => {
|
|
16041
|
+
const next = new Map([...flows].filter(([, flow]) => startedAt - flow.startedAt < PENDING_FLOW_TTL_MS));
|
|
16042
|
+
next.set(state, {
|
|
16043
|
+
server: registration.name,
|
|
16044
|
+
codeVerifier,
|
|
16045
|
+
redirectUri,
|
|
16046
|
+
record,
|
|
16047
|
+
startedAt
|
|
16048
|
+
});
|
|
16049
|
+
return next;
|
|
16050
|
+
});
|
|
16051
|
+
const authorizeUrl = new URL(metadata.authorization_endpoint);
|
|
16052
|
+
authorizeUrl.searchParams.set("response_type", "code");
|
|
16053
|
+
authorizeUrl.searchParams.set("client_id", client.client_id);
|
|
16054
|
+
authorizeUrl.searchParams.set("redirect_uri", redirectUri);
|
|
16055
|
+
authorizeUrl.searchParams.set("state", state);
|
|
16056
|
+
authorizeUrl.searchParams.set("code_challenge", Encoding.encodeBase64Url(challengeBytes));
|
|
16057
|
+
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
16058
|
+
authorizeUrl.searchParams.set("resource", registration.url);
|
|
16059
|
+
if (scope !== void 0) authorizeUrl.searchParams.set("scope", scope);
|
|
16060
|
+
return { authorizeUrl: authorizeUrl.toString() };
|
|
16061
|
+
});
|
|
16062
|
+
const exchangeToken = Effect.fn("McpOAuth.exchangeToken")(function* (flow, code) {
|
|
16063
|
+
const { status, json } = yield* fetchJson(HttpClientRequest.bodyUrlParams(HttpClientRequest.post(flow.record.tokenEndpoint), {
|
|
16064
|
+
grant_type: "authorization_code",
|
|
16065
|
+
code,
|
|
16066
|
+
redirect_uri: flow.redirectUri,
|
|
16067
|
+
client_id: flow.record.clientId,
|
|
16068
|
+
code_verifier: flow.codeVerifier,
|
|
16069
|
+
...flow.record.clientSecret === void 0 ? {} : { client_secret: flow.record.clientSecret },
|
|
16070
|
+
...flow.record.resource === void 0 ? {} : { resource: flow.record.resource }
|
|
16071
|
+
}));
|
|
16072
|
+
if (status !== 200) return yield* Effect.fail(`token endpoint answered status ${status}`);
|
|
16073
|
+
const decoded = decodeTokenResponse(json);
|
|
16074
|
+
if (decoded._tag !== "Success") return yield* Effect.fail("token endpoint answered an unexpected shape");
|
|
16075
|
+
return decoded.value;
|
|
16076
|
+
});
|
|
16077
|
+
const handleCallback = (params) => Effect.gen(function* () {
|
|
16078
|
+
if (params.state === void 0) return {
|
|
16079
|
+
ok: false,
|
|
16080
|
+
detail: "The redirect carried no state value."
|
|
16081
|
+
};
|
|
16082
|
+
const flow = (yield* Ref.get(pendingFlows)).get(params.state);
|
|
16083
|
+
if (flow === void 0) return {
|
|
16084
|
+
ok: false,
|
|
16085
|
+
detail: "This sign-in is unknown or has expired. Start it again from settings."
|
|
16086
|
+
};
|
|
16087
|
+
yield* Ref.update(pendingFlows, (current) => {
|
|
16088
|
+
const next = new Map(current);
|
|
16089
|
+
next.delete(params.state);
|
|
16090
|
+
return next;
|
|
16091
|
+
});
|
|
16092
|
+
if (params.error !== void 0) return {
|
|
16093
|
+
ok: false,
|
|
16094
|
+
detail: params.errorDescription ?? `The authorization server answered: ${params.error}.`
|
|
16095
|
+
};
|
|
16096
|
+
if (params.code === void 0) return {
|
|
16097
|
+
ok: false,
|
|
16098
|
+
detail: "The redirect carried no authorization code."
|
|
16099
|
+
};
|
|
16100
|
+
const now = yield* nowMillis;
|
|
16101
|
+
const exchanged = yield* exchangeToken(flow, params.code).pipe(Effect.map((response) => ({
|
|
16102
|
+
failed: false,
|
|
16103
|
+
response
|
|
16104
|
+
})), Effect.catch((detail) => Effect.succeed({
|
|
16105
|
+
failed: true,
|
|
16106
|
+
detail: typeof detail === "string" ? detail : "the token request failed"
|
|
16107
|
+
})));
|
|
16108
|
+
if (exchanged.failed) {
|
|
16109
|
+
yield* Effect.logWarning("mcp oauth token exchange failed", {
|
|
16110
|
+
server: flow.server,
|
|
16111
|
+
detail: exchanged.detail
|
|
16112
|
+
});
|
|
16113
|
+
return {
|
|
16114
|
+
ok: false,
|
|
16115
|
+
detail: `Token exchange failed: ${exchanged.detail}.`
|
|
16116
|
+
};
|
|
16117
|
+
}
|
|
16118
|
+
yield* writeRecord(flow.server, {
|
|
16119
|
+
...flow.record,
|
|
16120
|
+
tokens: {
|
|
16121
|
+
accessToken: exchanged.response.access_token,
|
|
16122
|
+
...exchanged.response.refresh_token === void 0 ? {} : { refreshToken: exchanged.response.refresh_token },
|
|
16123
|
+
...exchanged.response.expires_in === void 0 ? {} : { expiresAt: now + exchanged.response.expires_in * 1e3 }
|
|
16124
|
+
}
|
|
16125
|
+
});
|
|
16126
|
+
return {
|
|
16127
|
+
ok: true,
|
|
16128
|
+
server: flow.server
|
|
16129
|
+
};
|
|
16130
|
+
}).pipe(Effect.catchCause((cause) => Effect.logWarning("mcp oauth callback failed", { cause }).pipe(Effect.as({
|
|
16131
|
+
ok: false,
|
|
16132
|
+
detail: "The sign-in failed. See the server log."
|
|
16133
|
+
}))));
|
|
16134
|
+
const disconnect = (name) => Effect.gen(function* () {
|
|
16135
|
+
const record = yield* readRecord(name);
|
|
16136
|
+
if (Option.isNone(record)) return;
|
|
16137
|
+
const { tokens: _dropped, ...rest } = record.value;
|
|
16138
|
+
yield* writeRecord(name, rest);
|
|
16139
|
+
}).pipe(Effect.catchCause((cause) => Effect.logWarning("mcp oauth disconnect failed", {
|
|
16140
|
+
cause,
|
|
16141
|
+
server: name
|
|
16142
|
+
}).pipe(Effect.asVoid)));
|
|
16143
|
+
const refreshTokens = Effect.fn("McpOAuth.refreshTokens")(function* (server, record, refreshToken) {
|
|
16144
|
+
const { status, json } = yield* fetchJson(HttpClientRequest.bodyUrlParams(HttpClientRequest.post(record.tokenEndpoint), {
|
|
16145
|
+
grant_type: "refresh_token",
|
|
16146
|
+
refresh_token: refreshToken,
|
|
16147
|
+
client_id: record.clientId,
|
|
16148
|
+
...record.clientSecret === void 0 ? {} : { client_secret: record.clientSecret },
|
|
16149
|
+
...record.resource === void 0 ? {} : { resource: record.resource }
|
|
16150
|
+
}));
|
|
16151
|
+
if (status !== 200) return yield* Effect.fail(`status ${status}`);
|
|
16152
|
+
const decoded = decodeTokenResponse(json);
|
|
16153
|
+
if (decoded._tag !== "Success") return yield* Effect.fail("unexpected shape");
|
|
16154
|
+
const response = decoded.value;
|
|
16155
|
+
const now = yield* nowMillis;
|
|
16156
|
+
const tokens = {
|
|
16157
|
+
accessToken: response.access_token,
|
|
16158
|
+
refreshToken: response.refresh_token ?? refreshToken,
|
|
16159
|
+
...response.expires_in === void 0 ? {} : { expiresAt: now + response.expires_in * 1e3 }
|
|
16160
|
+
};
|
|
16161
|
+
yield* writeRecord(server, {
|
|
16162
|
+
...record,
|
|
16163
|
+
tokens
|
|
16164
|
+
});
|
|
16165
|
+
return tokens.accessToken;
|
|
16166
|
+
});
|
|
16167
|
+
const accessTokenFor = (registration) => Effect.gen(function* () {
|
|
16168
|
+
const record = yield* readRecord(registration.name);
|
|
16169
|
+
if (Option.isNone(record) || record.value.tokens === void 0) return Option.none();
|
|
16170
|
+
const tokens = record.value.tokens;
|
|
16171
|
+
const now = yield* nowMillis;
|
|
16172
|
+
if (!(tokens.expiresAt !== void 0 && now >= tokens.expiresAt - TOKEN_EXPIRY_MARGIN_MS)) return Option.some(tokens.accessToken);
|
|
16173
|
+
if (tokens.refreshToken === void 0) return Option.none();
|
|
16174
|
+
return yield* refreshTokens(registration.name, record.value, tokens.refreshToken).pipe(Effect.map(Option.some), Effect.catchCause((cause) => Effect.logWarning("mcp oauth refresh failed", {
|
|
16175
|
+
cause,
|
|
16176
|
+
server: registration.name
|
|
16177
|
+
}).pipe(Effect.as(Option.none()))));
|
|
16178
|
+
}).pipe(Effect.catchCause((cause) => Effect.logWarning("mcp oauth token lookup failed", {
|
|
16179
|
+
cause,
|
|
16180
|
+
server: registration.name
|
|
16181
|
+
}).pipe(Effect.as(Option.none()))));
|
|
16182
|
+
return {
|
|
16183
|
+
statusFor,
|
|
16184
|
+
begin,
|
|
16185
|
+
handleCallback,
|
|
16186
|
+
disconnect,
|
|
16187
|
+
accessTokenFor
|
|
16188
|
+
};
|
|
16189
|
+
});
|
|
16190
|
+
const layer$58 = Layer.effect(McpOAuth, make$63);
|
|
16191
|
+
Layer.succeed(McpOAuth, {
|
|
16192
|
+
statusFor: () => Effect.succeed(Option.none()),
|
|
16193
|
+
begin: () => Effect.fail(new McpRegistryError({ detail: "OAuth sign-in is not available." })),
|
|
16194
|
+
handleCallback: () => Effect.succeed({
|
|
16195
|
+
ok: false,
|
|
16196
|
+
detail: "OAuth sign-in is not available."
|
|
16197
|
+
}),
|
|
16198
|
+
disconnect: () => Effect.void,
|
|
16199
|
+
accessTokenFor: () => Effect.succeed(Option.none())
|
|
16200
|
+
});
|
|
15464
16201
|
/** Extension for a registration file, so the sync's asset name is `<server>.json`. */
|
|
15465
16202
|
const MCP_REGISTRATION_SUFFIX = ".json";
|
|
15466
16203
|
const RegistrationFromJson$1 = Schema$1.fromJsonString(McpServerRegistration);
|
|
@@ -15471,6 +16208,8 @@ const slotsOf = (registration) => registration.secrets ?? [];
|
|
|
15471
16208
|
const make$62 = Effect.gen(function* () {
|
|
15472
16209
|
const config = yield* ServerConfig$1;
|
|
15473
16210
|
const secrets = yield* ServerSecretStore;
|
|
16211
|
+
const oauth = yield* McpOAuth;
|
|
16212
|
+
const claudeFiles = yield* ClaudeMcpFiles;
|
|
15474
16213
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
15475
16214
|
const path = yield* Path.Path;
|
|
15476
16215
|
const services = yield* Effect.context();
|
|
@@ -15513,10 +16252,27 @@ const make$62 = Effect.gen(function* () {
|
|
|
15513
16252
|
const value = yield* readSecret(registration.name, slot);
|
|
15514
16253
|
if (Option.isNone(value)) missingSecrets.push(slot);
|
|
15515
16254
|
}
|
|
16255
|
+
const oauthStatus = registration.transport === "stdio" || !registration.enabled ? Option.none() : yield* oauth.statusFor(registration);
|
|
16256
|
+
const needsSignIn = Option.isSome(oauthStatus) && !oauthStatus.value.connected;
|
|
15516
16257
|
statuses.push({
|
|
15517
16258
|
registration,
|
|
15518
16259
|
missingSecrets,
|
|
15519
|
-
ready: registration.enabled && missingSecrets.length === 0
|
|
16260
|
+
ready: registration.enabled && missingSecrets.length === 0 && !needsSignIn,
|
|
16261
|
+
...Option.isSome(oauthStatus) ? { oauth: oauthStatus.value } : {},
|
|
16262
|
+
source: "registry"
|
|
16263
|
+
});
|
|
16264
|
+
}
|
|
16265
|
+
const registryNames = new Set(statuses.map((status) => status.registration.name));
|
|
16266
|
+
for (const userScope of yield* claudeFiles.readUser) {
|
|
16267
|
+
if (registryNames.has(userScope.name)) continue;
|
|
16268
|
+
const oauthStatus = userScope.transport === "stdio" ? Option.none() : yield* oauth.statusFor(userScope);
|
|
16269
|
+
const needsSignIn = Option.isSome(oauthStatus) && !oauthStatus.value.connected;
|
|
16270
|
+
statuses.push({
|
|
16271
|
+
registration: userScope,
|
|
16272
|
+
missingSecrets: [],
|
|
16273
|
+
ready: !needsSignIn,
|
|
16274
|
+
...Option.isSome(oauthStatus) ? { oauth: oauthStatus.value } : {},
|
|
16275
|
+
source: "claude"
|
|
15520
16276
|
});
|
|
15521
16277
|
}
|
|
15522
16278
|
return statuses;
|
|
@@ -15536,6 +16292,7 @@ const make$62 = Effect.gen(function* () {
|
|
|
15536
16292
|
target: slot.target,
|
|
15537
16293
|
key: slot.key
|
|
15538
16294
|
})).pipe(Effect.ignore);
|
|
16295
|
+
yield* secrets.remove(mcpOAuthStorageName(name)).pipe(Effect.ignore);
|
|
15539
16296
|
}).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("mcp registry remove failed", { cause }).pipe(Effect.asVoid)));
|
|
15540
16297
|
const setSecret = ({ name, slot, value }) => secrets.set(mcpSecretStorageName({
|
|
15541
16298
|
server: name,
|
|
@@ -15565,6 +16322,11 @@ const make$62 = Effect.gen(function* () {
|
|
|
15565
16322
|
else env[slot.key] = value.value;
|
|
15566
16323
|
}
|
|
15567
16324
|
if (!complete) continue;
|
|
16325
|
+
const hasExplicitAuthorization = Object.keys(headers).some((key) => key.toLowerCase() === "authorization");
|
|
16326
|
+
if (registration.transport !== "stdio" && !hasExplicitAuthorization) {
|
|
16327
|
+
const token = yield* oauth.accessTokenFor(registration);
|
|
16328
|
+
if (Option.isSome(token)) headers["Authorization"] = `Bearer ${token.value}`;
|
|
16329
|
+
}
|
|
15568
16330
|
resolved[registration.name] = registration.transport === "stdio" ? {
|
|
15569
16331
|
type: "stdio",
|
|
15570
16332
|
command: registration.command,
|
|
@@ -15576,6 +16338,20 @@ const make$62 = Effect.gen(function* () {
|
|
|
15576
16338
|
headers
|
|
15577
16339
|
};
|
|
15578
16340
|
}
|
|
16341
|
+
for (const userScope of yield* claudeFiles.readUser) {
|
|
16342
|
+
if (userScope.transport === "stdio") continue;
|
|
16343
|
+
if (userScope.name in resolved) continue;
|
|
16344
|
+
const token = yield* oauth.accessTokenFor(userScope);
|
|
16345
|
+
if (Option.isNone(token)) continue;
|
|
16346
|
+
resolved[userScope.name] = {
|
|
16347
|
+
type: userScope.transport,
|
|
16348
|
+
url: userScope.url,
|
|
16349
|
+
headers: {
|
|
16350
|
+
...userScope.headers,
|
|
16351
|
+
Authorization: `Bearer ${token.value}`
|
|
16352
|
+
}
|
|
16353
|
+
};
|
|
16354
|
+
}
|
|
15579
16355
|
return resolved;
|
|
15580
16356
|
}).pipe(Effect.provide(services), Effect.catchCause((cause) => Effect.logWarning("mcp registry resolve failed", { cause }).pipe(Effect.as({}))))
|
|
15581
16357
|
};
|
|
@@ -17297,7 +18073,7 @@ const make$59 = Effect.gen(function* () {
|
|
|
17297
18073
|
const layer$54 = Layer.effect(AssetSync, make$59);
|
|
17298
18074
|
//#endregion
|
|
17299
18075
|
//#region src/cli/assets.ts
|
|
17300
|
-
const SyncServicesLive = layer$54.pipe(Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$
|
|
18076
|
+
const SyncServicesLive = layer$54.pipe(Layer.provideMerge(layer$55), Layer.provideMerge(layer$56), Layer.provideMerge(layer$60.pipe(Layer.provide(layer$67))), Layer.provideMerge(layer$67), Layer.provideMerge(FetchHttpClient.layer));
|
|
17301
18077
|
const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
17302
18078
|
const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
|
|
17303
18079
|
return yield* Effect.gen(function* () {
|
|
@@ -17305,7 +18081,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
|
17305
18081
|
sync: yield* AssetSync,
|
|
17306
18082
|
link: yield* HubLink
|
|
17307
18083
|
});
|
|
17308
|
-
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$
|
|
18084
|
+
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$70(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
|
|
17309
18085
|
});
|
|
17310
18086
|
const STATE_LABEL = {
|
|
17311
18087
|
synced: "synced",
|
|
@@ -27891,7 +28667,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
27891
28667
|
});
|
|
27892
28668
|
yield* Console.log(output);
|
|
27893
28669
|
}));
|
|
27894
|
-
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$
|
|
28670
|
+
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$70(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
|
|
27895
28671
|
return yield* Effect.gen(function* () {
|
|
27896
28672
|
const snapshot = yield* getOfflineSnapshot();
|
|
27897
28673
|
const orchestrationEngine = yield* OrchestrationEngineService;
|
|
@@ -27902,7 +28678,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
27902
28678
|
});
|
|
27903
28679
|
yield* Console.log(output);
|
|
27904
28680
|
}).pipe(Effect.provide(offlineRuntimeLayer));
|
|
27905
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$43).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$
|
|
28681
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$43).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$70(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
27906
28682
|
});
|
|
27907
28683
|
const projectAddCommand = Command.make("add", {
|
|
27908
28684
|
...projectLocationFlags,
|
|
@@ -29222,6 +29998,40 @@ const otlpTracesProxyRouteLayer = HttpRouter.add("POST", OTLP_TRACES_PROXY_PATH,
|
|
|
29222
29998
|
EnvironmentInternalError: HttpServerRespondable.toResponse,
|
|
29223
29999
|
EnvironmentScopeRequiredError: HttpServerRespondable.toResponse
|
|
29224
30000
|
})));
|
|
30001
|
+
const escapeHtml = (input) => input.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
30002
|
+
const oauthResultPage = (input) => HttpServerResponse.text([
|
|
30003
|
+
"<!doctype html>",
|
|
30004
|
+
`<html><head><meta charset="utf-8"><title>${escapeHtml(input.title)}</title></head>`,
|
|
30005
|
+
"<body style=\"font-family: system-ui, sans-serif; max-width: 32rem; margin: 4rem auto;\">",
|
|
30006
|
+
`<h1 style="font-size: 1.25rem;">${escapeHtml(input.title)}</h1>`,
|
|
30007
|
+
`<p>${escapeHtml(input.detail)}</p>`,
|
|
30008
|
+
"</body></html>"
|
|
30009
|
+
].join(""), { contentType: "text/html; charset=utf-8" });
|
|
30010
|
+
/**
|
|
30011
|
+
* Where the browser lands after an MCP server sign-in.
|
|
30012
|
+
*
|
|
30013
|
+
* Deliberately unauthenticated: the redirect comes from an authorization
|
|
30014
|
+
* server that holds no p4code credential. The `state` value — minted by
|
|
30015
|
+
* `McpOAuth.begin`, single-use, expiring — is what ties the request to a flow
|
|
30016
|
+
* this server started; a request without a live one changes nothing.
|
|
30017
|
+
*/
|
|
30018
|
+
const mcpOAuthCallbackRouteLayer = HttpRouter.add("GET", OAUTH_CALLBACK_PATH, Effect.gen(function* () {
|
|
30019
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
30020
|
+
const url = new URL(request.url, "http://localhost.invalid");
|
|
30021
|
+
const outcome = yield* (yield* McpOAuth).handleCallback({
|
|
30022
|
+
state: url.searchParams.get("state") ?? void 0,
|
|
30023
|
+
code: url.searchParams.get("code") ?? void 0,
|
|
30024
|
+
error: url.searchParams.get("error") ?? void 0,
|
|
30025
|
+
errorDescription: url.searchParams.get("error_description") ?? void 0
|
|
30026
|
+
});
|
|
30027
|
+
return outcome.ok ? oauthResultPage({
|
|
30028
|
+
title: "Signed in",
|
|
30029
|
+
detail: `${outcome.server} is connected. You can close this tab and return to p4code.`
|
|
30030
|
+
}) : oauthResultPage({
|
|
30031
|
+
title: "Sign-in failed",
|
|
30032
|
+
detail: outcome.detail
|
|
30033
|
+
});
|
|
30034
|
+
}));
|
|
29225
30035
|
const assetRouteLayer = HttpRouter.add("GET", `${ASSET_ROUTE_PREFIX}/*`, Effect.gen(function* () {
|
|
29226
30036
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
29227
30037
|
const url = HttpServerRequest.toURL(request);
|
|
@@ -33376,15 +34186,15 @@ const toTaskStoreError = (operation) => (cause) => Effect.logWarning("task store
|
|
|
33376
34186
|
*/
|
|
33377
34187
|
const buildAutoPrompt = (task) => {
|
|
33378
34188
|
const reference = task.readableId ?? task.taskId;
|
|
33379
|
-
return `
|
|
34189
|
+
return `Picking up board task \`${reference}\`: ${task.title}
|
|
33380
34190
|
|
|
33381
|
-
|
|
34191
|
+
Read the task first via \`task_current\` (or \`task_get\` with \`${reference}\`). Body, labels, project and priority live on the row, not in this message; the row may have changed since this thread started - the current row is the source of truth. Re-read it the same way whenever you need the fields again.
|
|
33382
34192
|
|
|
33383
|
-
This thread is already linked to the task,
|
|
34193
|
+
This thread is already linked to the task, status In progress. Do the work.
|
|
33384
34194
|
|
|
33385
|
-
Record
|
|
34195
|
+
Record findings via \`task_update\` as you go - decisions made, constraints hit, things the task turned out to be wrong about. That is what makes the row worth reading afterwards. Leave \`status\` alone: moving work across the board is the user's call.
|
|
33386
34196
|
|
|
33387
|
-
After reading the task, read the code it concerns. If it
|
|
34197
|
+
After reading the task, read the code it concerns. If it is specified too poorly to act on, say so and stop rather than guessing.`;
|
|
33388
34198
|
};
|
|
33389
34199
|
/**
|
|
33390
34200
|
* Render a task into the prompt that seeds its thread.
|
|
@@ -43408,6 +44218,8 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
|
|
|
43408
44218
|
[WS_METHODS.mcpSave, AuthOrchestrationOperateScope],
|
|
43409
44219
|
[WS_METHODS.mcpRemove, AuthOrchestrationOperateScope],
|
|
43410
44220
|
[WS_METHODS.mcpSetSecret, AuthOrchestrationOperateScope],
|
|
44221
|
+
[WS_METHODS.mcpOAuthBegin, AuthOrchestrationOperateScope],
|
|
44222
|
+
[WS_METHODS.mcpOAuthDisconnect, AuthOrchestrationOperateScope],
|
|
43411
44223
|
[WS_METHODS.serverGetSettings, AuthOrchestrationReadScope],
|
|
43412
44224
|
[WS_METHODS.serverUpdateSettings, AuthOrchestrationOperateScope],
|
|
43413
44225
|
[WS_METHODS.serverDiscoverSourceControl, AuthOrchestrationReadScope],
|
|
@@ -43494,6 +44306,22 @@ function toAuthAccessStreamEvent(change, revision, currentSessionId) {
|
|
|
43494
44306
|
};
|
|
43495
44307
|
}
|
|
43496
44308
|
}
|
|
44309
|
+
/**
|
|
44310
|
+
* Route one MCP write to the store its `source` names.
|
|
44311
|
+
*
|
|
44312
|
+
* `claude` and `project` write the CLI's own files; `registry` (or absent,
|
|
44313
|
+
* which every older client sends) writes p4code's store. A project write
|
|
44314
|
+
* without a directory is a caller bug worth a loud answer, not a fallback.
|
|
44315
|
+
*/
|
|
44316
|
+
const routeMcpWrite = (input, handlers) => {
|
|
44317
|
+
if (input.source === "claude") return handlers.claude();
|
|
44318
|
+
if (input.source === "project") {
|
|
44319
|
+
const dir = input.projectDir?.trim() ?? "";
|
|
44320
|
+
if (dir.length === 0) return Effect.fail(new McpRegistryError({ detail: "A project-scope write needs its project directory." }));
|
|
44321
|
+
return handlers.project(dir);
|
|
44322
|
+
}
|
|
44323
|
+
return handlers.registry();
|
|
44324
|
+
};
|
|
43497
44325
|
const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.toLayer(Effect.gen(function* () {
|
|
43498
44326
|
const currentSessionId = currentSession.sessionId;
|
|
43499
44327
|
const crypto = yield* Crypto.Crypto;
|
|
@@ -43521,7 +44349,27 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
|
|
|
43521
44349
|
const hubLink = yield* HubLink;
|
|
43522
44350
|
const assetSync = yield* AssetSync;
|
|
43523
44351
|
const mcpRegistry = yield* McpRegistry;
|
|
44352
|
+
const mcpOAuth = yield* McpOAuth;
|
|
44353
|
+
const claudeMcpFiles = yield* ClaudeMcpFiles;
|
|
43524
44354
|
const skillRegistry = yield* SkillRegistry;
|
|
44355
|
+
const listMcpServersEverywhere = Effect.gen(function* () {
|
|
44356
|
+
const servers = [...yield* mcpRegistry.list];
|
|
44357
|
+
const projectRows = yield* projectionProjects.listAll().pipe(Effect.map((rows) => rows.filter((row) => row.deletedAt === null)), Effect.orElseSucceed(() => []));
|
|
44358
|
+
for (const project of projectRows) for (const registration of yield* claudeMcpFiles.readProject(project.workspaceRoot)) servers.push({
|
|
44359
|
+
registration,
|
|
44360
|
+
missingSecrets: [],
|
|
44361
|
+
ready: true,
|
|
44362
|
+
source: "project",
|
|
44363
|
+
projectDir: project.workspaceRoot
|
|
44364
|
+
});
|
|
44365
|
+
return {
|
|
44366
|
+
servers,
|
|
44367
|
+
projects: projectRows.map((project) => ({
|
|
44368
|
+
dir: project.workspaceRoot,
|
|
44369
|
+
title: project.title
|
|
44370
|
+
}))
|
|
44371
|
+
};
|
|
44372
|
+
});
|
|
43525
44373
|
const startup = yield* ServerRuntimeStartup;
|
|
43526
44374
|
const workspaceEntries = yield* WorkspaceEntries;
|
|
43527
44375
|
const workspaceFileSystem = yield* WorkspaceFileSystem;
|
|
@@ -44114,14 +44962,35 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
|
|
|
44114
44962
|
}), { "rpc.aggregate": "skills" }),
|
|
44115
44963
|
[WS_METHODS.skillRegistrySearch]: ({ query }) => observeRpcEffect$1(WS_METHODS.skillRegistrySearch, skillRegistry.search(query), { "rpc.aggregate": "skills" }),
|
|
44116
44964
|
[WS_METHODS.skillRegistryFetch]: ({ id }) => observeRpcEffect$1(WS_METHODS.skillRegistryFetch, skillRegistry.fetch(id), { "rpc.aggregate": "skills" }),
|
|
44117
|
-
[WS_METHODS.mcpList]: (_input) => observeRpcEffect$1(WS_METHODS.mcpList,
|
|
44118
|
-
[WS_METHODS.mcpSave]: ({ registration }) => observeRpcEffect$1(WS_METHODS.mcpSave,
|
|
44119
|
-
|
|
44965
|
+
[WS_METHODS.mcpList]: (_input) => observeRpcEffect$1(WS_METHODS.mcpList, listMcpServersEverywhere, { "rpc.aggregate": "mcp" }),
|
|
44966
|
+
[WS_METHODS.mcpSave]: ({ registration, source, projectDir }) => observeRpcEffect$1(WS_METHODS.mcpSave, routeMcpWrite({
|
|
44967
|
+
source,
|
|
44968
|
+
projectDir
|
|
44969
|
+
}, {
|
|
44970
|
+
claude: () => claudeMcpFiles.upsertUser(registration),
|
|
44971
|
+
project: (dir) => claudeMcpFiles.upsertProject(dir, registration),
|
|
44972
|
+
registry: () => mcpRegistry.save(registration)
|
|
44973
|
+
}).pipe(Effect.andThen(listMcpServersEverywhere)), { "rpc.aggregate": "mcp" }),
|
|
44974
|
+
[WS_METHODS.mcpRemove]: ({ name, source, projectDir }) => observeRpcEffect$1(WS_METHODS.mcpRemove, routeMcpWrite({
|
|
44975
|
+
source,
|
|
44976
|
+
projectDir
|
|
44977
|
+
}, {
|
|
44978
|
+
claude: () => claudeMcpFiles.removeUser(name),
|
|
44979
|
+
project: (dir) => claudeMcpFiles.removeProject(dir, name),
|
|
44980
|
+
registry: () => mcpRegistry.remove(name)
|
|
44981
|
+
}).pipe(Effect.andThen(listMcpServersEverywhere)), { "rpc.aggregate": "mcp" }),
|
|
44120
44982
|
[WS_METHODS.mcpSetSecret]: ({ name, slot, value }) => observeRpcEffect$1(WS_METHODS.mcpSetSecret, mcpRegistry.setSecret({
|
|
44121
44983
|
name,
|
|
44122
44984
|
slot,
|
|
44123
44985
|
value
|
|
44124
|
-
}).pipe(Effect.andThen(
|
|
44986
|
+
}).pipe(Effect.andThen(listMcpServersEverywhere)), { "rpc.aggregate": "mcp" }),
|
|
44987
|
+
[WS_METHODS.mcpOAuthBegin]: ({ name }) => observeRpcEffect$1(WS_METHODS.mcpOAuthBegin, Effect.gen(function* () {
|
|
44988
|
+
const found = (yield* mcpRegistry.list).find((status) => status.registration.name === name);
|
|
44989
|
+
if (found === void 0) return yield* new McpRegistryError({ detail: `No server is named ${name}.` });
|
|
44990
|
+
if (found.registration.transport === "stdio") return yield* new McpRegistryError({ detail: "A stdio server is launched locally and has no sign-in." });
|
|
44991
|
+
return yield* mcpOAuth.begin(found.registration);
|
|
44992
|
+
}), { "rpc.aggregate": "mcp" }),
|
|
44993
|
+
[WS_METHODS.mcpOAuthDisconnect]: ({ name }) => observeRpcEffect$1(WS_METHODS.mcpOAuthDisconnect, mcpOAuth.disconnect(name).pipe(Effect.andThen(listMcpServersEverywhere)), { "rpc.aggregate": "mcp" }),
|
|
44125
44994
|
[WS_METHODS.serverGetSettings]: (_input) => observeRpcEffect$1(WS_METHODS.serverGetSettings, serverSettings.getSettings.pipe(Effect.map(redactServerSettingsForClient)), { "rpc.aggregate": "server" }),
|
|
44126
44995
|
[WS_METHODS.serverUpdateSettings]: ({ patch }) => observeRpcEffect$1(WS_METHODS.serverUpdateSettings, serverSettings.updateSettings(patch).pipe(Effect.map(redactServerSettingsForClient)), { "rpc.aggregate": "server" }),
|
|
44127
44996
|
[WS_METHODS.serverDiscoverSourceControl]: (_input) => observeRpcEffect$1(WS_METHODS.serverDiscoverSourceControl, sourceControlDiscovery.discover, { "rpc.aggregate": "server" }),
|
|
@@ -46619,7 +47488,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
|
|
|
46619
47488
|
};
|
|
46620
47489
|
});
|
|
46621
47490
|
var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
|
|
46622
|
-
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$
|
|
47491
|
+
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$71));
|
|
46623
47492
|
//#endregion
|
|
46624
47493
|
//#region src/textGeneration/TextGenerationUtils.ts
|
|
46625
47494
|
const isTextGenerationError$2 = Schema$1.is(TextGenerationError);
|
|
@@ -85904,9 +86773,9 @@ const WorkspaceEntriesLayerLive = layer$25.pipe(Layer.provide(layer$43));
|
|
|
85904
86773
|
const WorkspaceFileSystemLayerLive = layer$24.pipe(Layer.provide(layer$43), Layer.provide(WorkspaceEntriesLayerLive));
|
|
85905
86774
|
const WorkspaceLayerLive = Layer.mergeAll(layer$43, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
|
|
85906
86775
|
const ProjectFaviconResolverLayerLive = layer$41.pipe(Layer.provide(layer$43), Layer.provide(layer$42));
|
|
85907
|
-
const AuthLayerLive = layer$
|
|
86776
|
+
const AuthLayerLive = layer$63.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$67));
|
|
85908
86777
|
const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
|
|
85909
|
-
const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$51), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$
|
|
86778
|
+
const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$51), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$60.pipe(Layer.provide(layer$67))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$52), Layer.provideMerge(layer$45), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(Layer.mergeAll(layer$57, layer$31).pipe(Layer.provideMerge(layer$58), Layer.provideMerge(layer$59))), Layer.provideMerge(layer$67)).pipe(Layer.provideMerge(layer$8), Layer.provideMerge(layer$7), Layer.provideMerge(layer$6), Layer.provideMerge(layer$48), Layer.provideMerge(layer$50), Layer.provideMerge(layer$49), Layer.provide(layer$71));
|
|
85910
86779
|
/**
|
|
85911
86780
|
* Hub asset sync.
|
|
85912
86781
|
*
|
|
@@ -85916,7 +86785,7 @@ const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(Checkpo
|
|
|
85916
86785
|
*/
|
|
85917
86786
|
const AssetSyncLive = layer$54.pipe(Layer.provideMerge(layer$55), Layer.provideMerge(layer$56));
|
|
85918
86787
|
const RuntimeServicesLive = layer$44.pipe(Layer.provideMerge(AssetSyncLive), Layer.provideMerge(RuntimeDependenciesLive));
|
|
85919
|
-
const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer.pipe(Layer.provide(layer$2))).pipe(Layer.provide(RoutedTaskRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$42), Layer.provide(layer$28), Layer.provide(layer$46), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
|
|
86788
|
+
const makeRoutesLayer = Layer.mergeAll(Layer.mergeAll(HttpApiBuilder.layer(EnvironmentHttpApi).pipe(Layer.provide(authHttpApiLayer), Layer.provide(orchestrationHttpApiLayer), Layer.provide(serverEnvironmentHttpApiLayer), Layer.provide(environmentAuthenticatedAuthLayer)), otlpTracesProxyRouteLayer, assetRouteLayer, mcpOAuthCallbackRouteLayer, staticAndDevRouteLayer, websocketRpcRouteLayer), layer.pipe(Layer.provide(layer$2))).pipe(Layer.provide(RoutedTaskRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(ProjectionProjectRepositoryLive.pipe(Layer.provide(PersistenceLayerLive))), Layer.provide(layer$42), Layer.provide(layer$28), Layer.provide(layer$46), Layer.provide(browserApiCorsLayer), Layer.provide(httpCompressionLayer));
|
|
85920
86789
|
const makeServerLayer = Layer.unwrap(Effect.gen(function* () {
|
|
85921
86790
|
const config = yield* ServerConfig$1;
|
|
85922
86791
|
yield* fixPath();
|
|
@@ -86326,7 +87195,7 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
|
|
|
86326
87195
|
]));
|
|
86327
87196
|
//#endregion
|
|
86328
87197
|
//#region src/bin.ts
|
|
86329
|
-
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$
|
|
87198
|
+
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$71, FetchHttpClient.layer);
|
|
86330
87199
|
/**
|
|
86331
87200
|
* Prints an update reminder before dispatching, at most once a day.
|
|
86332
87201
|
*
|