@cmmd-center/forge 0.13.3 → 0.13.4
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/{BunHttpServer-VI75aqJw.mjs → BunHttpServer-CDh4Vioe.mjs} +6 -6
- package/dist/{BunHttpServer-BhE7Szci.cjs → BunHttpServer-DKvSpNd1.cjs} +6 -6
- package/dist/{BunPTY-oVISucl_.mjs → BunPTY-DkAIzLIx.mjs} +3 -3
- package/dist/{BunPTY-86ttlTi6.cjs → BunPTY-uyreNRGp.cjs} +3 -3
- package/dist/{BunServices-D0JTDujL.mjs → BunServices-D6PAheqS.mjs} +3 -3
- package/dist/{BunServices-BwE9KexF.cjs → BunServices-DDh7Whyx.cjs} +3 -3
- package/dist/{Cache-p0X77SKw.mjs → Cache-BVilO50s.mjs} +3 -3
- package/dist/{Cache-CWlxNDbC.cjs → Cache-DCF22Dfk.cjs} +3 -3
- package/dist/{Config-AqUR_lhX.mjs → Config-Cdfpphpo.mjs} +2 -2
- package/dist/{Config-CkEzdD9L.cjs → Config-CwKCCk14.cjs} +2 -2
- package/dist/{HttpServer-DNGCTTdk.mjs → HttpServer-ClsGgAq1.mjs} +3 -3
- package/dist/{HttpServer-DGoLR-kU.cjs → HttpServer-D1gSU7NX.cjs} +3 -3
- package/dist/{NodeHttpServer-Dw7ivXfX.mjs → NodeHttpServer-DT0TF8s4.mjs} +7 -7
- package/dist/{NodeHttpServer-BPjxt_DT.cjs → NodeHttpServer-Dfc7nSMq.cjs} +7 -7
- package/dist/{NodePTY-Cps0jsDA.cjs → NodePTY-S4tPEwZc.cjs} +4 -4
- package/dist/{NodePTY-CVH2yNYl.mjs → NodePTY-loxi8Vku.mjs} +4 -4
- package/dist/{NodeServices-CBNXOYLG.mjs → NodeServices-BahzLhbo.mjs} +3 -3
- package/dist/{NodeServices-Cw6SxEbl.cjs → NodeServices-Cb0rXFb2.cjs} +3 -3
- package/dist/{NodeServices-Ddr-tYRu.cjs → NodeServices-DE8jhdcU.cjs} +1 -1
- package/dist/{NodeSqliteClient-DGInbxQ3.mjs → NodeSqliteClient-DqFjB3u_.mjs} +6 -6
- package/dist/{NodeSqliteClient-CmxPzcBf.cjs → NodeSqliteClient-qjBWAOGq.cjs} +6 -6
- package/dist/{NodeTerminal-BEGtoMSS.mjs → NodeTerminal-BPw7bktz.mjs} +3 -3
- package/dist/{NodeTerminal-5iWbO0B2.cjs → NodeTerminal-rYbifme7.cjs} +3 -3
- package/dist/{PTY-BxYDiW3h.cjs → PTY-B4ZctbwQ.cjs} +2 -2
- package/dist/{PTY-rIPf52QR.mjs → PTY-BzdbZ0Ym.mjs} +2 -2
- package/dist/{Path-C6Zb2WHp.mjs → Path-C851t6sC.mjs} +2 -2
- package/dist/{Path-Be7zSwar.cjs → Path-CGCc_dDx.cjs} +2 -2
- package/dist/{Schema-dLi7Qssy.cjs → Schema-CDCJ3xZo.cjs} +7 -1
- package/dist/{Schema-CJyk7ah1.mjs → Schema-DOBWF8gQ.mjs} +2 -2
- package/dist/{SqlError-cyJT2IRY.mjs → SqlError-3aqibZrj.mjs} +3 -3
- package/dist/{SqlError-Bt0Bcd5C.cjs → SqlError-CzB6p9lL.cjs} +3 -3
- package/dist/{SqliteClient-B08FzZM4.cjs → SqliteClient-Cq1peWN1.cjs} +5 -5
- package/dist/{SqliteClient-CIsITMoQ.mjs → SqliteClient-DTWCGkrj.mjs} +5 -5
- package/dist/bin.cjs +1086 -110
- package/dist/bin.mjs +1109 -136
- package/dist/client/assets/{CanvasPanel-BrVen-8G.js → CanvasPanel-DkUmuXgO.js} +3 -3
- package/dist/client/assets/CompositeItem-DG5YBMcI.js +2 -0
- package/dist/client/assets/{DiffPanel-B_HjSs6Q.js → DiffPanel-DLCCD1ht.js} +3 -3
- package/dist/client/assets/DiffPanel.logic-dIDfHUQm.js +21 -0
- package/dist/client/assets/DiffPanelShell-Ba6NA1s7.js +2531 -0
- package/dist/client/assets/{DiffWorkerPoolProvider-CwUTD5yv.js → DiffWorkerPoolProvider-Bqy6DzWP.js} +52 -52
- package/dist/client/assets/PullRequestCodePanel-8SJWZPM5.js +5 -0
- package/dist/client/assets/canvasStateStore-BWZ8xdXr.js +2 -0
- package/dist/client/assets/{cmmdAuthRedirect-sqR8W6vY.js → cmmdAuthRedirect--srSJjFS.js} +2 -2
- package/dist/client/assets/diffTargetResolution-DA0SlQE0.js +2 -0
- package/dist/client/assets/index-C9Upewz-.js +151 -0
- package/dist/client/assets/index-DlyGyVH6.css +1 -0
- package/dist/client/assets/src-DwOYg6gv.js +3 -0
- package/dist/client/assets/toggle-Ozy-3fZ2.js +2 -0
- package/dist/client/assets/useTriggerFocusGuards-FzV_CNjl.js +2 -0
- package/dist/client/index.html +11 -11
- package/dist/forge-build.json +1 -1
- package/dist/{open-FIgTDosf.mjs → open-D8mGyWmt.mjs} +3 -3
- package/package.json +1 -1
- package/dist/client/assets/CompositeItem-BNZfMUvm.js +0 -2
- package/dist/client/assets/DiffPanel.logic-_zUBJ9_7.js +0 -21
- package/dist/client/assets/DiffPanelShell-DmjIQbsj.js +0 -2531
- package/dist/client/assets/PullRequestCodePanel-CAaS8yoZ.js +0 -5
- package/dist/client/assets/canvasStateStore-DN5mPB5B.js +0 -2
- package/dist/client/assets/diffTargetResolution-IthQ5nAw.js +0 -2
- package/dist/client/assets/index-CgwIqrjs.js +0 -151
- package/dist/client/assets/index-Dk7T0zVg.css +0 -1
- package/dist/client/assets/src-CFqnisEM.js +0 -3
- package/dist/client/assets/toggle-CLUqZXdO.js +0 -2
- package/dist/client/assets/useTriggerFocusGuards-CaUimC-O.js +0 -2
package/dist/bin.cjs
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
4
|
const require_chunk = require("./chunk-BhaIgcZK.cjs");
|
|
5
|
-
const require_Schema$1 = require("./Schema-
|
|
6
|
-
const require_Path = require("./Path-
|
|
7
|
-
const require_NodeTerminal = require("./NodeTerminal-
|
|
8
|
-
const require_NodeServices = require("./NodeServices-
|
|
9
|
-
const require_HttpServer = require("./HttpServer-
|
|
10
|
-
const require_Config = require("./Config-
|
|
11
|
-
const require_Cache = require("./Cache-
|
|
12
|
-
const require_SqlError = require("./SqlError-
|
|
5
|
+
const require_Schema$1 = require("./Schema-CDCJ3xZo.cjs");
|
|
6
|
+
const require_Path = require("./Path-CGCc_dDx.cjs");
|
|
7
|
+
const require_NodeTerminal = require("./NodeTerminal-rYbifme7.cjs");
|
|
8
|
+
const require_NodeServices = require("./NodeServices-Cb0rXFb2.cjs");
|
|
9
|
+
const require_HttpServer = require("./HttpServer-D1gSU7NX.cjs");
|
|
10
|
+
const require_Config = require("./Config-CwKCCk14.cjs");
|
|
11
|
+
const require_Cache = require("./Cache-DCF22Dfk.cjs");
|
|
12
|
+
const require_SqlError = require("./SqlError-CzB6p9lL.cjs");
|
|
13
13
|
const require_Mime = require("./Mime-Bilnh8zE.cjs");
|
|
14
14
|
const require_esm = require("./esm-BZhXwX5p.cjs");
|
|
15
15
|
const require_runtimeControlAuthority = require("./runtimeControlAuthority-B3kNmYRT.cjs");
|
|
16
|
-
const require_PTY = require("./PTY-
|
|
16
|
+
const require_PTY = require("./PTY-B4ZctbwQ.cjs");
|
|
17
17
|
let node_child_process = require("node:child_process");
|
|
18
18
|
node_child_process = require_chunk.__toESM(node_child_process);
|
|
19
19
|
let node_stream = require("node:stream");
|
|
@@ -16544,7 +16544,7 @@ function makeNetServiceShape(createServer = node_net.createServer) {
|
|
|
16544
16544
|
}
|
|
16545
16545
|
//#endregion
|
|
16546
16546
|
//#region ../../packages/shared/src/releaseInfo.generated.ts
|
|
16547
|
-
const FORGE_RELEASE_VERSION = "0.13.
|
|
16547
|
+
const FORGE_RELEASE_VERSION = "0.13.4";
|
|
16548
16548
|
//#endregion
|
|
16549
16549
|
//#region ../../packages/shared/src/hostProcess.ts
|
|
16550
16550
|
/**
|
|
@@ -16668,6 +16668,70 @@ const CheckpointRef = makeEntityId("CheckpointRef");
|
|
|
16668
16668
|
*/
|
|
16669
16669
|
const ThreadEnvMode = require_Schema$1.Literals(["local", "worktree"]);
|
|
16670
16670
|
//#endregion
|
|
16671
|
+
//#region ../../packages/contracts/src/assets.ts
|
|
16672
|
+
/**
|
|
16673
|
+
* Addresses for the bytes a client cannot fetch by guessing.
|
|
16674
|
+
*
|
|
16675
|
+
* Forge already serves the bytes: attachments under `/attachments/*` and
|
|
16676
|
+
* project favicons at `GET /api/project-favicon?cwd=`. What it did not serve
|
|
16677
|
+
* was the question "what is the URL for this thing", which is the only way a
|
|
16678
|
+
* client that holds an attachment id or a workspace root can reach either.
|
|
16679
|
+
* The phone asks that question on the thread list for every project, so
|
|
16680
|
+
* until this existed the answer was an error the phone drew as a spinner.
|
|
16681
|
+
*
|
|
16682
|
+
* These schemas are the Forge side of `assets.*` in the vendored t3 contracts.
|
|
16683
|
+
* They must decode what that package encodes: same tags, same field names.
|
|
16684
|
+
*/
|
|
16685
|
+
const ASSET_PATH_MAX_LENGTH = 1024;
|
|
16686
|
+
const ASSET_ID_MAX_LENGTH = 256;
|
|
16687
|
+
const ASSET_URL_MAX_LENGTH = 4096;
|
|
16688
|
+
const AssetResource = require_Schema$1.Union([
|
|
16689
|
+
require_Schema$1.TaggedStruct("workspace-file", {
|
|
16690
|
+
threadId: ThreadId,
|
|
16691
|
+
path: TrimmedNonEmptyString.check(require_Schema$1.isMaxLength(ASSET_PATH_MAX_LENGTH))
|
|
16692
|
+
}),
|
|
16693
|
+
require_Schema$1.TaggedStruct("attachment", { attachmentId: TrimmedNonEmptyString.check(require_Schema$1.isMaxLength(ASSET_ID_MAX_LENGTH)) }),
|
|
16694
|
+
require_Schema$1.TaggedStruct("project-favicon", {
|
|
16695
|
+
cwd: TrimmedNonEmptyString.check(require_Schema$1.isMaxLength(ASSET_PATH_MAX_LENGTH)),
|
|
16696
|
+
path: require_Schema$1.optional(TrimmedNonEmptyString.check(require_Schema$1.isMaxLength(ASSET_PATH_MAX_LENGTH)))
|
|
16697
|
+
})
|
|
16698
|
+
]);
|
|
16699
|
+
const AssetCreateUrlInput = require_Schema$1.Struct({ resource: AssetResource });
|
|
16700
|
+
const AssetCreateUrlResult = require_Schema$1.Struct({
|
|
16701
|
+
relativeUrl: TrimmedNonEmptyString.check(require_Schema$1.isMaxLength(ASSET_URL_MAX_LENGTH)),
|
|
16702
|
+
expiresAt: require_Schema$1.Number,
|
|
16703
|
+
sourcePath: require_Schema$1.optional(TrimmedNonEmptyString.check(require_Schema$1.isMaxLength(ASSET_PATH_MAX_LENGTH)))
|
|
16704
|
+
});
|
|
16705
|
+
/**
|
|
16706
|
+
* The resource cannot be addressed by URL on this server.
|
|
16707
|
+
*
|
|
16708
|
+
* Tag and message match the vendored client's `AssetAccessError` union so the
|
|
16709
|
+
* phone decodes the failure rather than falling through to a generic transport
|
|
16710
|
+
* error. `workspace-file` is the case that lands here.
|
|
16711
|
+
*/
|
|
16712
|
+
var AssetWorkspaceResolutionError = class extends require_Schema$1.TaggedErrorClass()("AssetWorkspaceResolutionError", {
|
|
16713
|
+
resource: AssetResource,
|
|
16714
|
+
cause: require_Schema$1.optional(require_Schema$1.Defect)
|
|
16715
|
+
}) {
|
|
16716
|
+
get message() {
|
|
16717
|
+
return "Failed to resolve workspace.";
|
|
16718
|
+
}
|
|
16719
|
+
};
|
|
16720
|
+
/** The signing key that makes an asset URL self-authenticating could not be read. */
|
|
16721
|
+
var AssetSigningKeyLoadError = class extends require_Schema$1.TaggedErrorClass()("AssetSigningKeyLoadError", {
|
|
16722
|
+
resource: AssetResource,
|
|
16723
|
+
cause: require_Schema$1.optional(require_Schema$1.Defect)
|
|
16724
|
+
}) {
|
|
16725
|
+
get message() {
|
|
16726
|
+
return "Failed to load the asset signing key.";
|
|
16727
|
+
}
|
|
16728
|
+
};
|
|
16729
|
+
const AssetAccessError = require_Schema$1.Union([AssetWorkspaceResolutionError, AssetSigningKeyLoadError]);
|
|
16730
|
+
const AssetsRpcSchemas = { createUrl: {
|
|
16731
|
+
input: AssetCreateUrlInput,
|
|
16732
|
+
output: AssetCreateUrlResult
|
|
16733
|
+
} };
|
|
16734
|
+
//#endregion
|
|
16671
16735
|
//#region ../../node_modules/.bun/effect@4.0.0-beta.51/node_modules/effect/dist/unstable/rpc/RpcMiddleware.js
|
|
16672
16736
|
/**
|
|
16673
16737
|
* @since 4.0.0
|
|
@@ -18200,6 +18264,7 @@ const ExecutionEnvironmentCapabilities = require_Schema$1.Struct({
|
|
|
18200
18264
|
threadPullRequestLinking: require_Schema$1.optionalKey(require_Schema$1.Boolean),
|
|
18201
18265
|
cloudMode: require_Schema$1.Boolean.pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed(false))),
|
|
18202
18266
|
threadSettlement: require_Schema$1.optionalKey(require_Schema$1.Boolean),
|
|
18267
|
+
threadTitleRegeneration: require_Schema$1.optionalKey(require_Schema$1.Boolean),
|
|
18203
18268
|
posthogPublicKey: require_Schema$1.NullOr(require_Schema$1.String).pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed(null))),
|
|
18204
18269
|
posthogHost: require_Schema$1.NullOr(require_Schema$1.String).pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed(null))),
|
|
18205
18270
|
runtime: require_Schema$1.optionalKey(ExecutionEnvironmentRuntimeCapabilities),
|
|
@@ -19228,6 +19293,51 @@ require_Schema$1.Struct({
|
|
|
19228
19293
|
headRepositoryNameWithOwner: require_Schema$1.optional(require_Schema$1.NullOr(TrimmedNonEmptyString)),
|
|
19229
19294
|
headRepositoryOwnerLogin: require_Schema$1.optional(require_Schema$1.NullOr(TrimmedNonEmptyString))
|
|
19230
19295
|
});
|
|
19296
|
+
const SourceControlCloneProtocol = require_Schema$1.Literals([
|
|
19297
|
+
"auto",
|
|
19298
|
+
"ssh",
|
|
19299
|
+
"https"
|
|
19300
|
+
]);
|
|
19301
|
+
const SourceControlRepositoryInfo = require_Schema$1.Struct({
|
|
19302
|
+
provider: SourceControlProviderKind$1,
|
|
19303
|
+
nameWithOwner: TrimmedNonEmptyString,
|
|
19304
|
+
url: TrimmedNonEmptyString,
|
|
19305
|
+
sshUrl: TrimmedNonEmptyString
|
|
19306
|
+
});
|
|
19307
|
+
const SourceControlRepositoryLookupInput = require_Schema$1.Struct({
|
|
19308
|
+
provider: SourceControlProviderKind$1,
|
|
19309
|
+
repository: TrimmedNonEmptyString,
|
|
19310
|
+
cwd: require_Schema$1.optional(TrimmedNonEmptyString)
|
|
19311
|
+
});
|
|
19312
|
+
/**
|
|
19313
|
+
* Either name the repository (`provider` + `repository`) or paste a clone URL
|
|
19314
|
+
* (`remoteUrl`). Both are optional in the schema because the phone sends one
|
|
19315
|
+
* or the other depending on how the user got here; the server rejects a call
|
|
19316
|
+
* that supplies neither, and rejects a `remoteUrl` it cannot resolve to a
|
|
19317
|
+
* repository it is allowed to fetch a token for.
|
|
19318
|
+
*/
|
|
19319
|
+
const SourceControlCloneRepositoryInput = require_Schema$1.Struct({
|
|
19320
|
+
provider: require_Schema$1.optional(SourceControlProviderKind$1),
|
|
19321
|
+
repository: require_Schema$1.optional(TrimmedNonEmptyString),
|
|
19322
|
+
remoteUrl: require_Schema$1.optional(TrimmedNonEmptyString),
|
|
19323
|
+
destinationPath: TrimmedNonEmptyString,
|
|
19324
|
+
protocol: require_Schema$1.optional(SourceControlCloneProtocol)
|
|
19325
|
+
});
|
|
19326
|
+
const SourceControlCloneRepositoryResult = require_Schema$1.Struct({
|
|
19327
|
+
cwd: TrimmedNonEmptyString,
|
|
19328
|
+
remoteUrl: TrimmedNonEmptyString,
|
|
19329
|
+
repository: require_Schema$1.NullOr(SourceControlRepositoryInfo)
|
|
19330
|
+
});
|
|
19331
|
+
var SourceControlRepositoryError = class extends require_Schema$1.TaggedErrorClass()("SourceControlRepositoryError", {
|
|
19332
|
+
provider: SourceControlProviderKind$1,
|
|
19333
|
+
operation: require_Schema$1.String,
|
|
19334
|
+
detail: require_Schema$1.String,
|
|
19335
|
+
cause: require_Schema$1.optional(require_Schema$1.Defect)
|
|
19336
|
+
}) {
|
|
19337
|
+
get message() {
|
|
19338
|
+
return `Source control repository operation ${this.operation} failed for ${this.provider}: ${this.detail}`;
|
|
19339
|
+
}
|
|
19340
|
+
};
|
|
19231
19341
|
//#endregion
|
|
19232
19342
|
//#region ../../packages/contracts/src/orchestration.ts
|
|
19233
19343
|
const ORCHESTRATION_WS_METHODS = {
|
|
@@ -19239,7 +19349,8 @@ const ORCHESTRATION_WS_METHODS = {
|
|
|
19239
19349
|
replayEvents: "orchestration.replayEvents",
|
|
19240
19350
|
getArchivedShellSnapshot: "orchestration.getArchivedShellSnapshot",
|
|
19241
19351
|
subscribeShell: "orchestration.subscribeShell",
|
|
19242
|
-
subscribeThread: "orchestration.subscribeThread"
|
|
19352
|
+
subscribeThread: "orchestration.subscribeThread",
|
|
19353
|
+
searchThreads: "orchestration.searchThreads"
|
|
19243
19354
|
};
|
|
19244
19355
|
const ProviderKind = require_Schema$1.Literals([
|
|
19245
19356
|
"codex",
|
|
@@ -20623,6 +20734,32 @@ const OrchestrationGetFullThreadDiffInput = require_Schema$1.Struct({
|
|
|
20623
20734
|
const OrchestrationGetFullThreadDiffResult = ThreadTurnDiff;
|
|
20624
20735
|
const OrchestrationReplayEventsInput = require_Schema$1.Struct({ fromSequenceExclusive: NonNegativeInt });
|
|
20625
20736
|
const OrchestrationReplayEventsResult = require_Schema$1.ArraySchema(OrchestrationEvent);
|
|
20737
|
+
/**
|
|
20738
|
+
* Which side of the conversation a search hit came from. `null`
|
|
20739
|
+
* `messageCreatedAt` on a match means it came from the thread's own metadata
|
|
20740
|
+
* (its title or its branch) rather than from a message.
|
|
20741
|
+
*/
|
|
20742
|
+
const OrchestrationThreadSearchSource = require_Schema$1.Literals(["user", "assistant"]);
|
|
20743
|
+
/**
|
|
20744
|
+
* The server's SQLite client is synchronous and single-connection, so both the
|
|
20745
|
+
* scan input and the response size are bounded: a search must not be able to
|
|
20746
|
+
* monopolize the connection every keystroke.
|
|
20747
|
+
*/
|
|
20748
|
+
const OrchestrationSearchThreadsInput = require_Schema$1.Struct({
|
|
20749
|
+
query: TrimmedString.check(require_Schema$1.isMinLength(2), require_Schema$1.isMaxLength(200)),
|
|
20750
|
+
limit: require_Schema$1.optionalKey(require_Schema$1.Int.check(require_Schema$1.isBetween({
|
|
20751
|
+
minimum: 1,
|
|
20752
|
+
maximum: 50
|
|
20753
|
+
})))
|
|
20754
|
+
});
|
|
20755
|
+
const OrchestrationThreadSearchMatch = require_Schema$1.Struct({
|
|
20756
|
+
threadId: ThreadId,
|
|
20757
|
+
projectId: ProjectId,
|
|
20758
|
+
source: OrchestrationThreadSearchSource,
|
|
20759
|
+
snippet: require_Schema$1.String.check(require_Schema$1.isMaxLength(240)),
|
|
20760
|
+
messageCreatedAt: require_Schema$1.NullOr(IsoDateTime)
|
|
20761
|
+
});
|
|
20762
|
+
const OrchestrationSearchThreadsResult = require_Schema$1.Struct({ matches: require_Schema$1.ArraySchema(OrchestrationThreadSearchMatch) });
|
|
20626
20763
|
const ProgressPercent = NonNegativeInt.check(require_Schema$1.isLessThanOrEqualTo(100));
|
|
20627
20764
|
const AgentProtocolProgress = require_Schema$1.Struct({
|
|
20628
20765
|
kind: require_Schema$1.Literal("PROGRESS"),
|
|
@@ -20698,6 +20835,10 @@ const OrchestrationRpcSchemas = {
|
|
|
20698
20835
|
getArchivedShellSnapshot: {
|
|
20699
20836
|
input: require_Schema$1.Struct({}),
|
|
20700
20837
|
output: OrchestrationShellSnapshot
|
|
20838
|
+
},
|
|
20839
|
+
searchThreads: {
|
|
20840
|
+
input: OrchestrationSearchThreadsInput,
|
|
20841
|
+
output: OrchestrationSearchThreadsResult
|
|
20701
20842
|
}
|
|
20702
20843
|
};
|
|
20703
20844
|
var OrchestrationGetSnapshotError = class extends require_Schema$1.TaggedErrorClass()("OrchestrationGetSnapshotError", {
|
|
@@ -20720,6 +20861,10 @@ var OrchestrationReplayEventsError = class extends require_Schema$1.TaggedErrorC
|
|
|
20720
20861
|
message: TrimmedNonEmptyString,
|
|
20721
20862
|
cause: require_Schema$1.optional(require_Schema$1.Defect)
|
|
20722
20863
|
}) {};
|
|
20864
|
+
var OrchestrationSearchThreadsError = class extends require_Schema$1.TaggedErrorClass()("OrchestrationSearchThreadsError", {
|
|
20865
|
+
message: TrimmedNonEmptyString,
|
|
20866
|
+
cause: require_Schema$1.optional(require_Schema$1.Defect)
|
|
20867
|
+
}) {};
|
|
20723
20868
|
//#endregion
|
|
20724
20869
|
//#region ../../packages/contracts/src/provider.ts
|
|
20725
20870
|
const ProviderSessionStatus = require_Schema$1.Literals([
|
|
@@ -20782,6 +20927,31 @@ const ProviderRespondToUserInputInput = require_Schema$1.Struct({
|
|
|
20782
20927
|
requestId: ApprovalRequestId,
|
|
20783
20928
|
answers: ProviderUserInputAnswers
|
|
20784
20929
|
});
|
|
20930
|
+
/**
|
|
20931
|
+
* Sending a thread to the people who maintain Forge.
|
|
20932
|
+
*
|
|
20933
|
+
* The vendored client's `/feedback` slash command calls this. Upstream it
|
|
20934
|
+
* uploads the transcript to the model provider; Forge routes it to the same
|
|
20935
|
+
* CMMD feedback intake the web app's feedback dialog already posts to, so one
|
|
20936
|
+
* report lands in one place no matter which surface filed it.
|
|
20937
|
+
*
|
|
20938
|
+
* `reason` is what the person typed after the command, and is the only part
|
|
20939
|
+
* of the report they wrote. The server fills in the rest.
|
|
20940
|
+
*/
|
|
20941
|
+
const ProviderUploadFeedbackInput = require_Schema$1.Struct({
|
|
20942
|
+
threadId: ThreadId,
|
|
20943
|
+
reason: require_Schema$1.optional(TrimmedNonEmptyString.check(require_Schema$1.isMaxLength(4e3)))
|
|
20944
|
+
});
|
|
20945
|
+
const ProviderUploadFeedbackResult = require_Schema$1.Struct({ feedbackId: TrimmedNonEmptyString });
|
|
20946
|
+
var ProviderUploadFeedbackError = class extends require_Schema$1.TaggedErrorClass()("ProviderUploadFeedbackError", {
|
|
20947
|
+
threadId: ThreadId,
|
|
20948
|
+
reason: require_Schema$1.optional(TrimmedNonEmptyString.check(require_Schema$1.isMaxLength(500))),
|
|
20949
|
+
cause: require_Schema$1.optional(require_Schema$1.Defect)
|
|
20950
|
+
}) {
|
|
20951
|
+
get message() {
|
|
20952
|
+
return this.reason ?? "Feedback could not be sent. Please try again.";
|
|
20953
|
+
}
|
|
20954
|
+
};
|
|
20785
20955
|
const ProviderEventKind = require_Schema$1.Literals([
|
|
20786
20956
|
"session",
|
|
20787
20957
|
"notification",
|
|
@@ -21261,6 +21431,46 @@ const TaskStartedPayload = require_Schema$1.Struct({
|
|
|
21261
21431
|
description: require_Schema$1.optional(TrimmedNonEmptyStringSchema$2),
|
|
21262
21432
|
taskType: require_Schema$1.optional(TrimmedNonEmptyStringSchema$2)
|
|
21263
21433
|
});
|
|
21434
|
+
/**
|
|
21435
|
+
* Watch-loop task types: Monitor-tool tasks plus background shells. A shell
|
|
21436
|
+
* that outlives its turn is in practice a watch loop, not delegated work.
|
|
21437
|
+
*/
|
|
21438
|
+
const MONITOR_TASK_TYPES = new Set([
|
|
21439
|
+
"monitor",
|
|
21440
|
+
"monitor_mcp",
|
|
21441
|
+
"local_bash",
|
|
21442
|
+
"shell"
|
|
21443
|
+
]);
|
|
21444
|
+
/** Task types that are neither agents nor watch loops (plan-mode bookkeeping). */
|
|
21445
|
+
const INERT_TASK_TYPES = new Set(["plan", "dream"]);
|
|
21446
|
+
/**
|
|
21447
|
+
* Agent-vs-background classification for a task row.
|
|
21448
|
+
*
|
|
21449
|
+
* The mobile client reads `agentKind` off the persisted activity payload and
|
|
21450
|
+
* treats a row without it as background, so a server that never stamps the
|
|
21451
|
+
* field leaves the Agents roster permanently empty however much subagent work
|
|
21452
|
+
* is running (FOR-36646). This is the stamp.
|
|
21453
|
+
*
|
|
21454
|
+
* A denylist, not an allowlist, and deliberately so: the agent-flavoured type
|
|
21455
|
+
* names drift with the SDKs (subagent, local_agent, local_workflow, and
|
|
21456
|
+
* whatever lands next), and an allowlist drops real subagents the day a new
|
|
21457
|
+
* name appears. Only the watch loops and the plan-mode bookkeeping types are
|
|
21458
|
+
* named here; everything else is delegated work.
|
|
21459
|
+
*
|
|
21460
|
+
* A task launched from inside a subagent (`agentId` set) is agent-internal
|
|
21461
|
+
* background work unless it is itself agent-flavoured, because a nested agent
|
|
21462
|
+
* can outlive its parent and has to stay in the roster.
|
|
21463
|
+
*
|
|
21464
|
+
* This mirrors `classifyTaskAgentKind` in the contracts the phone speaks. The
|
|
21465
|
+
* two packages do not import each other, so the sets are duplicated on
|
|
21466
|
+
* purpose; they have to agree, and the tests on both sides pin the same cases.
|
|
21467
|
+
*/
|
|
21468
|
+
function classifyTaskAgentKind(input) {
|
|
21469
|
+
const { taskType, agentId } = input;
|
|
21470
|
+
const nonAgentType = taskType !== void 0 && (MONITOR_TASK_TYPES.has(taskType) || INERT_TASK_TYPES.has(taskType));
|
|
21471
|
+
if (agentId !== void 0 && agentId.trim().length > 0) return taskType === void 0 || nonAgentType ? "background" : "agent";
|
|
21472
|
+
return nonAgentType ? "background" : "agent";
|
|
21473
|
+
}
|
|
21264
21474
|
const TaskProgressPayload = require_Schema$1.Struct({
|
|
21265
21475
|
taskId: RuntimeTaskId,
|
|
21266
21476
|
description: TrimmedNonEmptyStringSchema$2,
|
|
@@ -25989,6 +26199,8 @@ const WS_METHODS = {
|
|
|
25989
26199
|
projectsListEntries: "projects.listEntries",
|
|
25990
26200
|
serverDiscoverSourceControl: "server.discoverSourceControl",
|
|
25991
26201
|
serverReportClientActivity: "server.reportClientActivity",
|
|
26202
|
+
sourceControlLookupRepository: "sourceControl.lookupRepository",
|
|
26203
|
+
sourceControlCloneRepository: "sourceControl.cloneRepository",
|
|
25992
26204
|
projectsWriteFile: "projects.writeFile",
|
|
25993
26205
|
projectCheckoutDeletionPreview: "projectCheckoutDeletion.preview",
|
|
25994
26206
|
projectCheckoutDeletionExecute: "projectCheckoutDeletion.execute",
|
|
@@ -26092,7 +26304,9 @@ const WS_METHODS = {
|
|
|
26092
26304
|
subscribeServerConfig: "subscribeServerConfig",
|
|
26093
26305
|
subscribeServerLifecycle: "subscribeServerLifecycle",
|
|
26094
26306
|
subscribeAuthAccess: "subscribeAuthAccess",
|
|
26095
|
-
subscribeWorkspaceReadiness: "subscribeWorkspaceReadiness"
|
|
26307
|
+
subscribeWorkspaceReadiness: "subscribeWorkspaceReadiness",
|
|
26308
|
+
assetsCreateUrl: "assets.createUrl",
|
|
26309
|
+
providerUploadFeedback: "provider.uploadFeedback"
|
|
26096
26310
|
};
|
|
26097
26311
|
const WsServerUpsertKeybindingRpc = make$30(WS_METHODS.serverUpsertKeybinding, {
|
|
26098
26312
|
payload: ServerUpsertKeybindingInput,
|
|
@@ -26621,6 +26835,29 @@ const WsOrchestrationGetArchivedShellSnapshotRpc = make$30(ORCHESTRATION_WS_METH
|
|
|
26621
26835
|
success: OrchestrationRpcSchemas.getArchivedShellSnapshot.output,
|
|
26622
26836
|
error: OrchestrationGetSnapshotError
|
|
26623
26837
|
});
|
|
26838
|
+
const WsOrchestrationSearchThreadsRpc = make$30(ORCHESTRATION_WS_METHODS.searchThreads, {
|
|
26839
|
+
payload: OrchestrationSearchThreadsInput,
|
|
26840
|
+
success: OrchestrationRpcSchemas.searchThreads.output,
|
|
26841
|
+
error: OrchestrationSearchThreadsError
|
|
26842
|
+
});
|
|
26843
|
+
/**
|
|
26844
|
+
* Resolve `owner/repo` to the repository the caller is allowed to clone.
|
|
26845
|
+
*
|
|
26846
|
+
* Both of these run against the same CMMD-brokered GitHub credential the web
|
|
26847
|
+
* add-project flow uses, so a lookup that answers is a lookup whose clone can
|
|
26848
|
+
* also be authorized, and a repository the caller cannot reach is a failure
|
|
26849
|
+
* here rather than a clone that dies half way.
|
|
26850
|
+
*/
|
|
26851
|
+
const WsSourceControlLookupRepositoryRpc = make$30(WS_METHODS.sourceControlLookupRepository, {
|
|
26852
|
+
payload: SourceControlRepositoryLookupInput,
|
|
26853
|
+
success: SourceControlRepositoryInfo,
|
|
26854
|
+
error: SourceControlRepositoryError
|
|
26855
|
+
});
|
|
26856
|
+
const WsSourceControlCloneRepositoryRpc = make$30(WS_METHODS.sourceControlCloneRepository, {
|
|
26857
|
+
payload: SourceControlCloneRepositoryInput,
|
|
26858
|
+
success: SourceControlCloneRepositoryResult,
|
|
26859
|
+
error: SourceControlRepositoryError
|
|
26860
|
+
});
|
|
26624
26861
|
const WsOrchestrationSubscribeShellRpc = make$30(ORCHESTRATION_WS_METHODS.subscribeShell, {
|
|
26625
26862
|
payload: OrchestrationRpcSchemas.subscribeShell.input,
|
|
26626
26863
|
success: OrchestrationRpcSchemas.subscribeShell.output,
|
|
@@ -26701,7 +26938,15 @@ const WsRpcGroup = make$29(WsServerGetConfigRpc, WsServerRefreshProvidersRpc, Ws
|
|
|
26701
26938
|
payload: UsageSummaryInput,
|
|
26702
26939
|
success: UsageSummary,
|
|
26703
26940
|
error: UsageReadError
|
|
26704
|
-
}), WsPullRequestsListRpc, WsPullRequestsListStatsRpc, WsPullRequestsDetailRpc, WsPullRequestsActivityRpc, WsPullRequestsThreadCommentsRpc, WsPullRequestsDiffFileContentsRpc, WsPullRequestsRunActionRpc, WsPullRequestsUpdateRpc, WsPullRequestsCommentRpc, WsPullRequestsUpdateCommentRpc, WsPullRequestsSubmitReviewRpc, WsPullRequestsReplyToThreadRpc, WsPullRequestsSetThreadResolutionRpc, WsPullRequestsSetReactionRpc, WsPullRequestsInvalidateRpc, WsPullRequestsReviewerCandidatesRpc, WsPullRequestsRequestReviewersRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsProjectCheckoutDeletionPreviewRpc, WsProjectCheckoutDeletionExecuteRpc, WsCodeGraphSummaryRpc, WsCodeGraphNeighborhoodRpc, WsCodeGraphSearchRpc, WsWorkspaceReadFileRpc, WsWorkspaceBeginWriteFileRpc, WsWorkspaceWriteFileChunkRpc, WsWorkspaceCommitWriteFileRpc, WsWorkspaceCancelWriteFileRpc, WsShellOpenInEditorRpc, WsSubscribeGitStatusRpc, WsGitPullRpc, WsGitRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsGitListBranchesRpc, WsVcsListRefsRpc, WsVcsRefreshStatusRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsGitCreateWorktreeRpc, WsGitRemoveWorktreeRpc, WsGitCreateBranchRpc, WsGitCheckoutRpc, WsGitInitRpc, WsFilesystemBrowseRpc, WsProjectsReadFileRpc, WsReviewGetDiffPreviewRpc, WsCloudListRuntimesRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalSignalRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsBrowserOpenSessionRpc, WsBrowserListSessionsRpc, WsBrowserCloseSessionRpc, WsBrowserGetSessionRpc, WsBrowserCommandRpc, WsBrowserClearBrowsingDataRpc, WsBrowserAttachDisplayRpc, WsBrowserAddDisplayIceCandidateRpc, WsComputerInputRpc, WsDevTunnelRegisterRpc, WsDevTunnelUnregisterRpc, WsDevTunnelListRpc, WsThreadDevRuntimeGetRpc, WsThreadDevRuntimeAllocatePortRpc, WsThreadDevRuntimeReleaseRpc, WsWorkspaceEnvironmentPreflightRpc, WsWorkspaceEnsureScratchRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsProjectsListEntriesRpc, WsServerDiscoverSourceControlRpc, WsServerReportClientActivityRpc, WsSubscribeBrowserEventsRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsSubscribeWorkspaceReadinessRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationStreamTurnDiffRpc, WsOrchestrationStreamFullThreadDiffRpc, WsOrchestrationReplayEventsRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc
|
|
26941
|
+
}), WsPullRequestsListRpc, WsPullRequestsListStatsRpc, WsPullRequestsDetailRpc, WsPullRequestsActivityRpc, WsPullRequestsThreadCommentsRpc, WsPullRequestsDiffFileContentsRpc, WsPullRequestsRunActionRpc, WsPullRequestsUpdateRpc, WsPullRequestsCommentRpc, WsPullRequestsUpdateCommentRpc, WsPullRequestsSubmitReviewRpc, WsPullRequestsReplyToThreadRpc, WsPullRequestsSetThreadResolutionRpc, WsPullRequestsSetReactionRpc, WsPullRequestsInvalidateRpc, WsPullRequestsReviewerCandidatesRpc, WsPullRequestsRequestReviewersRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsProjectCheckoutDeletionPreviewRpc, WsProjectCheckoutDeletionExecuteRpc, WsCodeGraphSummaryRpc, WsCodeGraphNeighborhoodRpc, WsCodeGraphSearchRpc, WsWorkspaceReadFileRpc, WsWorkspaceBeginWriteFileRpc, WsWorkspaceWriteFileChunkRpc, WsWorkspaceCommitWriteFileRpc, WsWorkspaceCancelWriteFileRpc, WsShellOpenInEditorRpc, WsSubscribeGitStatusRpc, WsGitPullRpc, WsGitRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsGitListBranchesRpc, WsVcsListRefsRpc, WsVcsRefreshStatusRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsGitCreateWorktreeRpc, WsGitRemoveWorktreeRpc, WsGitCreateBranchRpc, WsGitCheckoutRpc, WsGitInitRpc, WsFilesystemBrowseRpc, WsProjectsReadFileRpc, WsReviewGetDiffPreviewRpc, WsCloudListRuntimesRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalSignalRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsBrowserOpenSessionRpc, WsBrowserListSessionsRpc, WsBrowserCloseSessionRpc, WsBrowserGetSessionRpc, WsBrowserCommandRpc, WsBrowserClearBrowsingDataRpc, WsBrowserAttachDisplayRpc, WsBrowserAddDisplayIceCandidateRpc, WsComputerInputRpc, WsDevTunnelRegisterRpc, WsDevTunnelUnregisterRpc, WsDevTunnelListRpc, WsThreadDevRuntimeGetRpc, WsThreadDevRuntimeAllocatePortRpc, WsThreadDevRuntimeReleaseRpc, WsWorkspaceEnvironmentPreflightRpc, WsWorkspaceEnsureScratchRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsProjectsListEntriesRpc, WsServerDiscoverSourceControlRpc, WsServerReportClientActivityRpc, WsSubscribeBrowserEventsRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsSubscribeWorkspaceReadinessRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationStreamTurnDiffRpc, WsOrchestrationStreamFullThreadDiffRpc, WsOrchestrationReplayEventsRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc, make$30(WS_METHODS.assetsCreateUrl, {
|
|
26942
|
+
payload: AssetsRpcSchemas.createUrl.input,
|
|
26943
|
+
success: AssetsRpcSchemas.createUrl.output,
|
|
26944
|
+
error: AssetAccessError
|
|
26945
|
+
}), make$30(WS_METHODS.providerUploadFeedback, {
|
|
26946
|
+
payload: ProviderUploadFeedbackInput,
|
|
26947
|
+
success: ProviderUploadFeedbackResult,
|
|
26948
|
+
error: ProviderUploadFeedbackError
|
|
26949
|
+
}), WsOrchestrationSearchThreadsRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc);
|
|
26705
26950
|
const RelayWsRpcGroup = WsRpcGroup.middleware(RelayAuthorizationMiddleware).middleware(RelayDeliveryMiddleware);
|
|
26706
26951
|
/**
|
|
26707
26952
|
* The direct-WebSocket mount of the same group, with per-method scope
|
|
@@ -31841,6 +32086,31 @@ const RELAY_METHOD_POLICY_BASES = [
|
|
|
31841
32086
|
method: ORCHESTRATION_WS_METHODS.subscribeThread,
|
|
31842
32087
|
requiredScope: "thread.read",
|
|
31843
32088
|
deliveryClass: "subscription"
|
|
32089
|
+
},
|
|
32090
|
+
{
|
|
32091
|
+
method: WS_METHODS.assetsCreateUrl,
|
|
32092
|
+
requiredScope: "thread.read",
|
|
32093
|
+
deliveryClass: "read"
|
|
32094
|
+
},
|
|
32095
|
+
{
|
|
32096
|
+
method: ORCHESTRATION_WS_METHODS.searchThreads,
|
|
32097
|
+
requiredScope: "thread.read",
|
|
32098
|
+
deliveryClass: "read"
|
|
32099
|
+
},
|
|
32100
|
+
{
|
|
32101
|
+
method: WS_METHODS.providerUploadFeedback,
|
|
32102
|
+
requiredScope: "thread.read",
|
|
32103
|
+
deliveryClass: "create-command"
|
|
32104
|
+
},
|
|
32105
|
+
{
|
|
32106
|
+
method: WS_METHODS.sourceControlLookupRepository,
|
|
32107
|
+
requiredScope: "git.read",
|
|
32108
|
+
deliveryClass: "read"
|
|
32109
|
+
},
|
|
32110
|
+
{
|
|
32111
|
+
method: WS_METHODS.sourceControlCloneRepository,
|
|
32112
|
+
requiredScope: "project.write",
|
|
32113
|
+
deliveryClass: "idempotent-command"
|
|
31844
32114
|
}
|
|
31845
32115
|
];
|
|
31846
32116
|
const FRAME_JOURNALED_METHODS = new Set([
|
|
@@ -34008,7 +34278,7 @@ function isRejectedCredential(cause) {
|
|
|
34008
34278
|
//#region src/connect/hiddenInput.ts
|
|
34009
34279
|
/** Ctrl-C arrives as this byte on a raw TTY rather than as a signal. */
|
|
34010
34280
|
const END_OF_TEXT = "";
|
|
34011
|
-
function firstLine(value) {
|
|
34281
|
+
function firstLine$1(value) {
|
|
34012
34282
|
const index = value.indexOf("\n");
|
|
34013
34283
|
const line = index === -1 ? value : value.slice(0, index);
|
|
34014
34284
|
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
@@ -34033,7 +34303,7 @@ function readHiddenLine(input) {
|
|
|
34033
34303
|
settled = true;
|
|
34034
34304
|
detach();
|
|
34035
34305
|
input.output.write("\n");
|
|
34036
|
-
resolve(firstLine(value));
|
|
34306
|
+
resolve(firstLine$1(value));
|
|
34037
34307
|
};
|
|
34038
34308
|
const onData = (chunk) => {
|
|
34039
34309
|
buffer += chunk.toString();
|
|
@@ -35465,7 +35735,7 @@ function stringify(value, fallback = "[unserializable]") {
|
|
|
35465
35735
|
return typeof fallback === "function" ? fallback(value) : fallback;
|
|
35466
35736
|
}
|
|
35467
35737
|
}
|
|
35468
|
-
function truncate$
|
|
35738
|
+
function truncate$2(str, max = 0) {
|
|
35469
35739
|
if (typeof str !== "string" || max === 0) return str;
|
|
35470
35740
|
return str.length <= max ? str : `${str.slice(0, max)}...`;
|
|
35471
35741
|
}
|
|
@@ -36072,7 +36342,7 @@ var Scope = class Scope {
|
|
|
36072
36342
|
const mergedBreadcrumb = {
|
|
36073
36343
|
timestamp: dateTimestampInSeconds(),
|
|
36074
36344
|
...breadcrumb,
|
|
36075
|
-
message: breadcrumb.message ? truncate$
|
|
36345
|
+
message: breadcrumb.message ? truncate$2(breadcrumb.message, 2048) : breadcrumb.message
|
|
36076
36346
|
};
|
|
36077
36347
|
this._breadcrumbs.push(mergedBreadcrumb);
|
|
36078
36348
|
if (this._breadcrumbs.length > maxCrumbs) {
|
|
@@ -38730,9 +39000,9 @@ function applyClientOptions(event, options) {
|
|
|
38730
39000
|
if (!event.release && release) event.release = release;
|
|
38731
39001
|
if (!event.dist && dist) event.dist = dist;
|
|
38732
39002
|
const request = event.request;
|
|
38733
|
-
if (request?.url && maxValueLength) request.url = truncate$
|
|
39003
|
+
if (request?.url && maxValueLength) request.url = truncate$2(request.url, maxValueLength);
|
|
38734
39004
|
if (maxValueLength) event.exception?.values?.forEach((exception) => {
|
|
38735
|
-
if (exception.value) exception.value = truncate$
|
|
39005
|
+
if (exception.value) exception.value = truncate$2(exception.value, maxValueLength);
|
|
38736
39006
|
});
|
|
38737
39007
|
}
|
|
38738
39008
|
function applyDebugIds(event, stackParser) {
|
|
@@ -62349,7 +62619,7 @@ const cacheResponseHook = (span, redisCommand, cmdArgs, response) => {
|
|
|
62349
62619
|
[SEMANTIC_ATTRIBUTE_CACHE_KEY]: safeKey
|
|
62350
62620
|
});
|
|
62351
62621
|
const spanDescription = safeKey.join(", ");
|
|
62352
|
-
span.updateName(_redisOptions.maxCacheKeyLength ? truncate$
|
|
62622
|
+
span.updateName(_redisOptions.maxCacheKeyLength ? truncate$2(spanDescription, _redisOptions.maxCacheKeyLength) : spanDescription);
|
|
62353
62623
|
};
|
|
62354
62624
|
//#endregion
|
|
62355
62625
|
//#region ../../node_modules/.bun/@sentry+node@10.70.0+cfd4f484ffd29da6/node_modules/@sentry/node/build/esm/integrations/tracing/redis/vendored/semconv.js
|
|
@@ -65864,7 +66134,7 @@ function normalizeNumberish(value) {
|
|
|
65864
66134
|
}
|
|
65865
66135
|
//#endregion
|
|
65866
66136
|
//#region package.json
|
|
65867
|
-
var version$1 = "0.13.
|
|
66137
|
+
var version$1 = "0.13.4";
|
|
65868
66138
|
//#endregion
|
|
65869
66139
|
//#region src/sentry.ts
|
|
65870
66140
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -69400,6 +69670,103 @@ var BrowserTraceCollector = class extends require_Schema$1.Service()("forge/obse
|
|
|
69400
69670
|
*/
|
|
69401
69671
|
var ProjectFaviconResolver = class extends require_Schema$1.Service()("forge/project/Services/ProjectFaviconResolver") {};
|
|
69402
69672
|
//#endregion
|
|
69673
|
+
//#region src/auth/Services/ServerSecretStore.ts
|
|
69674
|
+
var SecretStoreError = class extends require_Schema$1.TaggedError("SecretStoreError") {};
|
|
69675
|
+
var ServerSecretStore = class extends require_Schema$1.Service()("forge/auth/Services/ServerSecretStore") {};
|
|
69676
|
+
//#endregion
|
|
69677
|
+
//#region src/auth/assetUrlToken.ts
|
|
69678
|
+
/**
|
|
69679
|
+
* A credential an image tag can carry.
|
|
69680
|
+
*
|
|
69681
|
+
* `/attachments/*` and `GET /api/project-favicon` both require an authenticated
|
|
69682
|
+
* request, and the clients that render those bytes hand a bare URL to an image
|
|
69683
|
+
* loader that sends no cookie and no Authorization header. Web gets away with
|
|
69684
|
+
* it because the browser attaches the session cookie itself. The phone holds a
|
|
69685
|
+
* bearer token it has no way to attach, so every asset it fetched would 401.
|
|
69686
|
+
*
|
|
69687
|
+
* So the URL carries its own proof. The token is signed with the same server
|
|
69688
|
+
* signing key as the session and WebSocket credentials, and it is deliberately
|
|
69689
|
+
* weaker than either of those: it names one resource, and presenting it grants
|
|
69690
|
+
* that resource and nothing else. A leaked favicon URL is a leaked favicon.
|
|
69691
|
+
*/
|
|
69692
|
+
const ASSET_TOKEN_KIND = "asset";
|
|
69693
|
+
const ASSET_URL_TOKEN_QUERY_PARAM = "assetToken";
|
|
69694
|
+
/**
|
|
69695
|
+
* An hour, against a client that re-asks every thirty minutes.
|
|
69696
|
+
*
|
|
69697
|
+
* The vendored client refreshes asset URLs on a thirty-minute interval, so a
|
|
69698
|
+
* shorter life would expire URLs the client still believes are good, and the
|
|
69699
|
+
* failure would show up as an image that worked and then stopped.
|
|
69700
|
+
*/
|
|
69701
|
+
const DEFAULT_ASSET_URL_TOKEN_TTL = require_Schema$1.hours(1);
|
|
69702
|
+
const SIGNING_SECRET_NAME$1 = "server-signing-key";
|
|
69703
|
+
const AssetTokenClaims = require_Schema$1.Struct({
|
|
69704
|
+
v: require_Schema$1.Literal(1),
|
|
69705
|
+
kind: require_Schema$1.Literal(ASSET_TOKEN_KIND),
|
|
69706
|
+
sid: require_Schema$1.String,
|
|
69707
|
+
res: require_Schema$1.String,
|
|
69708
|
+
iat: require_Schema$1.Number,
|
|
69709
|
+
exp: require_Schema$1.Number
|
|
69710
|
+
});
|
|
69711
|
+
const decodeAssetTokenClaims = require_Schema$1.decodeUnknownEffect(require_Schema$1.fromJsonString(AssetTokenClaims));
|
|
69712
|
+
/**
|
|
69713
|
+
* The canonical name of one addressable asset.
|
|
69714
|
+
*
|
|
69715
|
+
* Both the mint and the check compute this from their own side: the RPC from
|
|
69716
|
+
* the resource it was asked about, the route from the request it received. They
|
|
69717
|
+
* have to agree exactly, which is the point. A token minted for one attachment
|
|
69718
|
+
* cannot be replayed against another, or against a favicon.
|
|
69719
|
+
*
|
|
69720
|
+
* The key is built from the address, not the logical id, because the address is
|
|
69721
|
+
* what both sides actually hold. The attachments route reads its id straight
|
|
69722
|
+
* out of the URL path without percent-decoding, so keying on the decoded id
|
|
69723
|
+
* would let the two sides disagree about an id that encodes to something else.
|
|
69724
|
+
*/
|
|
69725
|
+
function assetResourceKey(resource) {
|
|
69726
|
+
return resource.kind === "attachment" ? `attachment:${resource.urlPathSegment}` : `project-favicon:${resource.cwd}`;
|
|
69727
|
+
}
|
|
69728
|
+
const resolveSigningSecret = require_Schema$1.gen(function* () {
|
|
69729
|
+
const serverConfig = yield* ServerConfig;
|
|
69730
|
+
const secretStore = yield* ServerSecretStore;
|
|
69731
|
+
return resolveConfiguredSigningKey(serverConfig.sessionSigningKey) ?? (yield* secretStore.getOrCreateRandom(SIGNING_SECRET_NAME$1, 32));
|
|
69732
|
+
});
|
|
69733
|
+
const issueAssetUrlToken = (input) => require_Schema$1.gen(function* () {
|
|
69734
|
+
const secret = yield* resolveSigningSecret;
|
|
69735
|
+
const issuedAt = yield* require_Schema$1.now;
|
|
69736
|
+
const expiresAt = require_Schema$1.add(issuedAt, { milliseconds: require_Schema$1.toMillis(input.ttl ?? DEFAULT_ASSET_URL_TOKEN_TTL) });
|
|
69737
|
+
const claims = {
|
|
69738
|
+
v: 1,
|
|
69739
|
+
kind: ASSET_TOKEN_KIND,
|
|
69740
|
+
sid: input.sessionId,
|
|
69741
|
+
res: input.resourceKey,
|
|
69742
|
+
iat: issuedAt.epochMilliseconds,
|
|
69743
|
+
exp: expiresAt.epochMilliseconds
|
|
69744
|
+
};
|
|
69745
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(claims));
|
|
69746
|
+
return {
|
|
69747
|
+
token: `${encodedPayload}.${signPayload(encodedPayload, secret)}`,
|
|
69748
|
+
expiresAt: expiresAt.epochMilliseconds
|
|
69749
|
+
};
|
|
69750
|
+
});
|
|
69751
|
+
/**
|
|
69752
|
+
* Whether this token opens this resource, right now.
|
|
69753
|
+
*
|
|
69754
|
+
* Returns a boolean rather than failing, because every negative answer means
|
|
69755
|
+
* the same thing to the caller: fall through to ordinary request
|
|
69756
|
+
* authentication. A malformed token from a stale client should behave like no
|
|
69757
|
+
* token at all, not like a different error.
|
|
69758
|
+
*/
|
|
69759
|
+
const verifyAssetUrlToken = (input) => require_Schema$1.gen(function* () {
|
|
69760
|
+
const [encodedPayload, signature] = input.token.split(".");
|
|
69761
|
+
if (!encodedPayload || !signature) return false;
|
|
69762
|
+
if (!timingSafeEqualBase64Url(signature, signPayload(encodedPayload, yield* resolveSigningSecret))) return false;
|
|
69763
|
+
const claims = yield* decodeAssetTokenClaims(base64UrlDecodeUtf8(encodedPayload)).pipe(require_Schema$1.orElseSucceed(() => null));
|
|
69764
|
+
if (!claims) return false;
|
|
69765
|
+
if (claims.res !== input.resourceKey) return false;
|
|
69766
|
+
const now = yield* require_Schema$1.currentTimeMillis;
|
|
69767
|
+
return claims.exp > now;
|
|
69768
|
+
}).pipe(require_Schema$1.orElseSucceed(() => false));
|
|
69769
|
+
//#endregion
|
|
69403
69770
|
//#region src/environment/Services/ServerEnvironment.ts
|
|
69404
69771
|
var ServerEnvironment = class extends require_Schema$1.Service()("forge/environment/Services/ServerEnvironment") {};
|
|
69405
69772
|
//#endregion
|
|
@@ -69674,6 +70041,28 @@ const requireAuthenticatedRequest$1 = require_Schema$1.gen(function* () {
|
|
|
69674
70041
|
const request = yield* require_HttpServer.HttpServerRequest;
|
|
69675
70042
|
yield* (yield* ServerAuth).authenticateHttpRequest(request);
|
|
69676
70043
|
});
|
|
70044
|
+
/**
|
|
70045
|
+
* Authenticate a request for asset bytes.
|
|
70046
|
+
*
|
|
70047
|
+
* Ordinary request authentication first, so the web app's cookie keeps working
|
|
70048
|
+
* exactly as it did and nothing about these routes changes for it. An asset
|
|
70049
|
+
* token is the fallback, for the clients that render these bytes through an
|
|
70050
|
+
* image loader with no way to attach a credential. See `auth/assetUrlToken.ts`
|
|
70051
|
+
* for why the token is scoped to one resource.
|
|
70052
|
+
*/
|
|
70053
|
+
const requireAssetAccess = (resourceKey) => require_Schema$1.gen(function* () {
|
|
70054
|
+
const request = yield* require_HttpServer.HttpServerRequest;
|
|
70055
|
+
const serverAuth = yield* ServerAuth;
|
|
70056
|
+
const url = require_HttpServer.toURL(request);
|
|
70057
|
+
const token = require_Schema$1.isSome(url) ? url.value.searchParams.get(ASSET_URL_TOKEN_QUERY_PARAM) : null;
|
|
70058
|
+
if (token && token.trim().length > 0) {
|
|
70059
|
+
if (yield* verifyAssetUrlToken({
|
|
70060
|
+
token,
|
|
70061
|
+
resourceKey
|
|
70062
|
+
})) return;
|
|
70063
|
+
}
|
|
70064
|
+
yield* serverAuth.authenticateHttpRequest(request);
|
|
70065
|
+
});
|
|
69677
70066
|
const serverEnvironmentRouteLayer = add$1("GET", "/.well-known/forge/environment", require_Schema$1.gen(function* () {
|
|
69678
70067
|
const descriptor = yield* require_Schema$1.service(ServerEnvironment).pipe(require_Schema$1.flatMap((serverEnvironment) => serverEnvironment.getDescriptor));
|
|
69679
70068
|
return require_HttpServer.jsonUnsafe(descriptor, { status: 200 });
|
|
@@ -69743,13 +70132,16 @@ const otlpTracesProxyRouteLayer = add$1("POST", OTLP_TRACES_PROXY_PATH, require_
|
|
|
69743
70132
|
captureServerError(cause, { route: "/api/observability/v1/traces" });
|
|
69744
70133
|
}).pipe(require_Schema$1.andThen(() => require_Schema$1.logError("otlp-traces unhandled cause", { cause }))))));
|
|
69745
70134
|
const attachmentsRouteLayer = add$1("GET", `${ATTACHMENTS_ROUTE_PREFIX}/*`, require_Schema$1.gen(function* () {
|
|
69746
|
-
yield* requireAuthenticatedRequest$1;
|
|
69747
70135
|
const request = yield* require_HttpServer.HttpServerRequest;
|
|
69748
70136
|
const url = require_HttpServer.toURL(request);
|
|
69749
70137
|
if (require_Schema$1.isNone(url)) return require_HttpServer.text("Bad Request", { status: 400 });
|
|
69750
70138
|
const config = yield* ServerConfig;
|
|
69751
70139
|
const normalizedRelativePath = normalizeAttachmentRelativePath(url.value.pathname.slice(12));
|
|
69752
70140
|
if (!normalizedRelativePath) return require_HttpServer.text("Invalid attachment path", { status: 400 });
|
|
70141
|
+
yield* requireAssetAccess(assetResourceKey({
|
|
70142
|
+
kind: "attachment",
|
|
70143
|
+
urlPathSegment: normalizedRelativePath
|
|
70144
|
+
}));
|
|
69753
70145
|
const isIdLookup = !normalizedRelativePath.includes("/") && !normalizedRelativePath.includes(".");
|
|
69754
70146
|
const filePath = isIdLookup ? resolveAttachmentPathById({
|
|
69755
70147
|
attachmentsDir: config.attachmentsDir,
|
|
@@ -69768,11 +70160,14 @@ const attachmentsRouteLayer = add$1("GET", `${ATTACHMENTS_ROUTE_PREFIX}/*`, requ
|
|
|
69768
70160
|
}).pipe(require_Schema$1.catchTag("AuthError", respondToAuthError)));
|
|
69769
70161
|
const projectFaviconRouteLayer = add$1("GET", "/api/project-favicon", require_Schema$1.gen(function* () {
|
|
69770
70162
|
const request = yield* require_HttpServer.HttpServerRequest;
|
|
69771
|
-
yield* (yield* ServerAuth).authenticateHttpRequest(request);
|
|
69772
70163
|
const url = require_HttpServer.toURL(request);
|
|
69773
70164
|
if (require_Schema$1.isNone(url)) return require_HttpServer.text("Bad Request", { status: 400 });
|
|
69774
70165
|
const projectCwd = url.value.searchParams.get("cwd");
|
|
69775
70166
|
if (!projectCwd) return require_HttpServer.text("Missing cwd parameter", { status: 400 });
|
|
70167
|
+
yield* requireAssetAccess(assetResourceKey({
|
|
70168
|
+
kind: "project-favicon",
|
|
70169
|
+
cwd: projectCwd
|
|
70170
|
+
}));
|
|
69776
70171
|
const faviconFilePath = yield* (yield* ProjectFaviconResolver).resolvePath(projectCwd);
|
|
69777
70172
|
if (!faviconFilePath) return require_HttpServer.empty({
|
|
69778
70173
|
status: 204,
|
|
@@ -77504,6 +77899,195 @@ require_Schema$1.succeed$1(TraceDiagnostics, TraceDiagnostics.of({ read: (option
|
|
|
77504
77899
|
}
|
|
77505
77900
|
})) }));
|
|
77506
77901
|
//#endregion
|
|
77902
|
+
//#region src/assets/createAssetUrl.ts
|
|
77903
|
+
const PROJECT_FAVICON_ROUTE = "/api/project-favicon";
|
|
77904
|
+
/**
|
|
77905
|
+
* Turn a resource into an address a client can fetch.
|
|
77906
|
+
*
|
|
77907
|
+
* Nothing new is served here. `/attachments/*` and `GET /api/project-favicon`
|
|
77908
|
+
* already return these bytes; what did not exist was the step in between, and
|
|
77909
|
+
* without it a client holding an attachment id or a workspace root has no way
|
|
77910
|
+
* to name the URL. The vendored mobile client asks this for every project on
|
|
77911
|
+
* its thread list, so the gap was not an edge case, it was the most visited
|
|
77912
|
+
* screen.
|
|
77913
|
+
*
|
|
77914
|
+
* The URL is relative so it resolves against whichever base the client is
|
|
77915
|
+
* already talking to, and it carries a token because the clients that render
|
|
77916
|
+
* these bytes hand the URL to an image loader that attaches no credential of
|
|
77917
|
+
* its own. See `auth/assetUrlToken.ts`.
|
|
77918
|
+
*/
|
|
77919
|
+
const createAssetUrl = (sessionId, resource) => require_Schema$1.gen(function* () {
|
|
77920
|
+
if (resource._tag === "workspace-file") return yield* new AssetWorkspaceResolutionError({
|
|
77921
|
+
resource,
|
|
77922
|
+
cause: "Workspace files are read over projects.readFile, not by URL."
|
|
77923
|
+
});
|
|
77924
|
+
const address = resolveAddress(resource);
|
|
77925
|
+
const issued = yield* issueAssetUrlToken({
|
|
77926
|
+
sessionId,
|
|
77927
|
+
resourceKey: address.resourceKey
|
|
77928
|
+
}).pipe(require_Schema$1.mapError((cause) => new AssetSigningKeyLoadError({
|
|
77929
|
+
resource,
|
|
77930
|
+
cause
|
|
77931
|
+
})));
|
|
77932
|
+
return {
|
|
77933
|
+
relativeUrl: `${address.path}?${address.query}assetToken=${encodeURIComponent(issued.token)}`,
|
|
77934
|
+
expiresAt: issued.expiresAt,
|
|
77935
|
+
...resource._tag === "project-favicon" && resource.path ? { sourcePath: resource.path } : {}
|
|
77936
|
+
};
|
|
77937
|
+
});
|
|
77938
|
+
function resolveAddress(resource) {
|
|
77939
|
+
if (resource._tag === "attachment") {
|
|
77940
|
+
const urlPathSegment = encodeURIComponent(resource.attachmentId);
|
|
77941
|
+
return {
|
|
77942
|
+
resourceKey: assetResourceKey({
|
|
77943
|
+
kind: "attachment",
|
|
77944
|
+
urlPathSegment
|
|
77945
|
+
}),
|
|
77946
|
+
path: `${ATTACHMENTS_ROUTE_PREFIX}/${urlPathSegment}`,
|
|
77947
|
+
query: ""
|
|
77948
|
+
};
|
|
77949
|
+
}
|
|
77950
|
+
return {
|
|
77951
|
+
resourceKey: assetResourceKey({
|
|
77952
|
+
kind: "project-favicon",
|
|
77953
|
+
cwd: resource.cwd
|
|
77954
|
+
}),
|
|
77955
|
+
path: PROJECT_FAVICON_ROUTE,
|
|
77956
|
+
query: `cwd=${encodeURIComponent(resource.cwd)}&`
|
|
77957
|
+
};
|
|
77958
|
+
}
|
|
77959
|
+
//#endregion
|
|
77960
|
+
//#region src/cmmd/submitFeedback.ts
|
|
77961
|
+
/** The intake the web app's feedback dialog already posts to, reached directly. */
|
|
77962
|
+
const CMMD_FEEDBACK_PATH = "/api/forge/context/feedback";
|
|
77963
|
+
/**
|
|
77964
|
+
* The intake answers with the task it created.
|
|
77965
|
+
*
|
|
77966
|
+
* Web reaches it through the proxy registry, which unwraps `data` before the
|
|
77967
|
+
* caller sees it; this calls the gateway directly and does not, so both shapes
|
|
77968
|
+
* are read here rather than pinning the one that happens to arrive today.
|
|
77969
|
+
*/
|
|
77970
|
+
function readFeedbackId(response) {
|
|
77971
|
+
if (!response || typeof response !== "object") return null;
|
|
77972
|
+
const record = response;
|
|
77973
|
+
const envelope = record["data"];
|
|
77974
|
+
const id = (envelope && typeof envelope === "object" ? envelope : record)["id"];
|
|
77975
|
+
if (typeof id === "number") return String(id);
|
|
77976
|
+
if (typeof id === "string" && id.trim().length > 0) return id.trim();
|
|
77977
|
+
return null;
|
|
77978
|
+
}
|
|
77979
|
+
/**
|
|
77980
|
+
* File feedback against the caller's own CMMD account.
|
|
77981
|
+
*
|
|
77982
|
+
* The session's CMMD JWT is what scopes it. A server with no CMMD connection
|
|
77983
|
+
* has nowhere to send feedback and says so in a sentence, because the caller
|
|
77984
|
+
* draws this text straight into the thread the person is looking at.
|
|
77985
|
+
*/
|
|
77986
|
+
function submitCmmdFeedbackForSession(input) {
|
|
77987
|
+
return require_Schema$1.gen(function* () {
|
|
77988
|
+
const jwt = yield* resolveCmmdJwtForSession({
|
|
77989
|
+
sessionId: input.sessionId,
|
|
77990
|
+
...input.subject === void 0 ? {} : { subject: input.subject },
|
|
77991
|
+
logPrefix: "cmmd-feedback"
|
|
77992
|
+
}).pipe(require_Schema$1.orElseSucceed(() => null));
|
|
77993
|
+
if (!jwt) return {
|
|
77994
|
+
ok: false,
|
|
77995
|
+
reason: "This server is not connected to CMMD, so there is nowhere to send feedback."
|
|
77996
|
+
};
|
|
77997
|
+
const body = {
|
|
77998
|
+
type: "bug",
|
|
77999
|
+
title: input.title,
|
|
78000
|
+
source: "forge",
|
|
78001
|
+
priority: "medium",
|
|
78002
|
+
...input.description ? { description: input.description } : {},
|
|
78003
|
+
...input.senderName ? { senderName: input.senderName } : {}
|
|
78004
|
+
};
|
|
78005
|
+
const response = yield* require_Schema$1.tryPromise(() => cmmdGateway.request({
|
|
78006
|
+
method: "POST",
|
|
78007
|
+
jwt,
|
|
78008
|
+
path: CMMD_FEEDBACK_PATH,
|
|
78009
|
+
body
|
|
78010
|
+
})).pipe(require_Schema$1.orElseSucceed(() => null));
|
|
78011
|
+
if (response === null) return {
|
|
78012
|
+
ok: false,
|
|
78013
|
+
reason: "The feedback service did not accept the report. Please try again."
|
|
78014
|
+
};
|
|
78015
|
+
const feedbackId = readFeedbackId(response);
|
|
78016
|
+
if (feedbackId === null) return {
|
|
78017
|
+
ok: false,
|
|
78018
|
+
reason: "The feedback service answered without recording a report."
|
|
78019
|
+
};
|
|
78020
|
+
return {
|
|
78021
|
+
ok: true,
|
|
78022
|
+
feedbackId
|
|
78023
|
+
};
|
|
78024
|
+
});
|
|
78025
|
+
}
|
|
78026
|
+
//#endregion
|
|
78027
|
+
//#region src/provider/threadFeedback.ts
|
|
78028
|
+
const FEEDBACK_TITLE_MAX_CHARS = 120;
|
|
78029
|
+
function firstLine(text) {
|
|
78030
|
+
return text.split("\n", 1)[0]?.trim() ?? "";
|
|
78031
|
+
}
|
|
78032
|
+
function truncate$1(text, max) {
|
|
78033
|
+
return text.length <= max ? text : `${text.slice(0, max - 1).trimEnd()}…`;
|
|
78034
|
+
}
|
|
78035
|
+
/**
|
|
78036
|
+
* A title someone triaging the intake can act on.
|
|
78037
|
+
*
|
|
78038
|
+
* What the person typed after `/feedback` first, because it is the only part
|
|
78039
|
+
* they wrote. The thread's title is the fallback, and a bare thread id is the
|
|
78040
|
+
* last resort, since a queue of "Forge feedback" rows tells a triager nothing.
|
|
78041
|
+
*/
|
|
78042
|
+
function feedbackTitle(input) {
|
|
78043
|
+
const reason = input.reason ? firstLine(input.reason) : "";
|
|
78044
|
+
if (reason.length > 0) return truncate$1(reason, FEEDBACK_TITLE_MAX_CHARS);
|
|
78045
|
+
const threadTitle = input.threadTitle?.trim() ?? "";
|
|
78046
|
+
if (threadTitle.length > 0) return truncate$1(`Forge feedback: ${threadTitle}`, FEEDBACK_TITLE_MAX_CHARS);
|
|
78047
|
+
return truncate$1(`Forge feedback: thread ${input.threadId}`, FEEDBACK_TITLE_MAX_CHARS);
|
|
78048
|
+
}
|
|
78049
|
+
/** The body, which always names the thread so the report can be traced back to it. */
|
|
78050
|
+
function feedbackDescription(input) {
|
|
78051
|
+
const lines = [`Filed from Forge thread \`${input.threadId}\`.`];
|
|
78052
|
+
if (input.threadTitle?.trim()) lines.push(`Thread title: ${input.threadTitle.trim()}`);
|
|
78053
|
+
if (input.reason?.trim()) lines.push("", input.reason.trim());
|
|
78054
|
+
return lines.join("\n");
|
|
78055
|
+
}
|
|
78056
|
+
/**
|
|
78057
|
+
* `/feedback`, served.
|
|
78058
|
+
*
|
|
78059
|
+
* The vendored client declared this method and called it; Forge answered
|
|
78060
|
+
* nothing, so the slash command printed the transport's own "unknown request
|
|
78061
|
+
* tag" string into the thread. Routing it to the CMMD intake the web feedback
|
|
78062
|
+
* dialog already uses means one report format and one destination, whichever
|
|
78063
|
+
* surface it came from.
|
|
78064
|
+
*
|
|
78065
|
+
* Every failure carries a sentence rather than a cause, because the client
|
|
78066
|
+
* draws `message` straight into the thread as an assistant reply.
|
|
78067
|
+
*/
|
|
78068
|
+
const uploadThreadFeedback = (orchestrationEngine, sessionId, subject, input) => require_Schema$1.gen(function* () {
|
|
78069
|
+
const threadTitle = (yield* orchestrationEngine.getReadModel().pipe(require_Schema$1.orElseSucceed(() => null)))?.threads.find((thread) => thread.id === input.threadId)?.title;
|
|
78070
|
+
const outcome = yield* submitCmmdFeedbackForSession({
|
|
78071
|
+
sessionId,
|
|
78072
|
+
...subject === void 0 ? {} : { subject },
|
|
78073
|
+
title: feedbackTitle({
|
|
78074
|
+
...input,
|
|
78075
|
+
threadId: input.threadId,
|
|
78076
|
+
...threadTitle ? { threadTitle } : {}
|
|
78077
|
+
}),
|
|
78078
|
+
description: feedbackDescription({
|
|
78079
|
+
...input,
|
|
78080
|
+
threadId: input.threadId,
|
|
78081
|
+
...threadTitle ? { threadTitle } : {}
|
|
78082
|
+
})
|
|
78083
|
+
});
|
|
78084
|
+
if (!outcome.ok) return yield* new ProviderUploadFeedbackError({
|
|
78085
|
+
threadId: input.threadId,
|
|
78086
|
+
reason: outcome.reason
|
|
78087
|
+
});
|
|
78088
|
+
return { feedbackId: outcome.feedbackId };
|
|
78089
|
+
});
|
|
78090
|
+
//#endregion
|
|
77507
78091
|
//#region src/cloudRuntime/listCloudRuntimes.ts
|
|
77508
78092
|
/**
|
|
77509
78093
|
* List the caller's Forge Cloud runtimes, through CMMD, as the caller.
|
|
@@ -87559,95 +88143,278 @@ function resolveGitHubCloneDir(baseDir, repoFullName, targetDir) {
|
|
|
87559
88143
|
const repoName = repoFullName.split("/")[1] ?? repoFullName;
|
|
87560
88144
|
return targetDir ?? (0, node_path.join)(baseDir, "Projects", repoName);
|
|
87561
88145
|
}
|
|
87562
|
-
|
|
87563
|
-
|
|
87564
|
-
|
|
87565
|
-
|
|
87566
|
-
|
|
87567
|
-
|
|
87568
|
-
|
|
87569
|
-
|
|
87570
|
-
|
|
87571
|
-
|
|
87572
|
-
|
|
87573
|
-
|
|
87574
|
-
|
|
87575
|
-
|
|
87576
|
-
|
|
87577
|
-
|
|
87578
|
-
|
|
87579
|
-
|
|
87580
|
-
|
|
87581
|
-
|
|
87582
|
-
|
|
87583
|
-
|
|
88146
|
+
/**
|
|
88147
|
+
* The public clone URLs for a repository.
|
|
88148
|
+
*
|
|
88149
|
+
* Never derived from an authenticated remote: `buildAuthenticatedRemoteUrl`
|
|
88150
|
+
* embeds an installation token in the URL, and a token that reaches a client is
|
|
88151
|
+
* a token that reaches wherever the client writes it down.
|
|
88152
|
+
*/
|
|
88153
|
+
function describeGitHubRepository(nameWithOwner) {
|
|
88154
|
+
return {
|
|
88155
|
+
nameWithOwner,
|
|
88156
|
+
url: `https://github.com/${nameWithOwner}`,
|
|
88157
|
+
sshUrl: `git@github.com:${nameWithOwner}.git`
|
|
88158
|
+
};
|
|
88159
|
+
}
|
|
88160
|
+
/**
|
|
88161
|
+
* Resolve `owner/repo` against the repositories CMMD says this caller may use.
|
|
88162
|
+
*
|
|
88163
|
+
* This asks the same brokered listing the web repository picker is populated
|
|
88164
|
+
* from, rather than github.com directly, so "found" here means the same thing
|
|
88165
|
+
* it means everywhere else in Forge: CMMD will vend this caller a token for it.
|
|
88166
|
+
* A repository that exists but that the caller cannot reach is a 404, which is
|
|
88167
|
+
* the answer they can act on.
|
|
88168
|
+
*/
|
|
88169
|
+
function lookupGitHubRepositoryForSession(input) {
|
|
88170
|
+
return require_Schema$1.gen(function* () {
|
|
88171
|
+
const requested = input.repository.trim().replace(/^\/+|\/+$/g, "");
|
|
88172
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(requested)) return {
|
|
88173
|
+
status: 400,
|
|
88174
|
+
repository: null,
|
|
88175
|
+
error: "Enter a repository as owner/name."
|
|
88176
|
+
};
|
|
88177
|
+
const jwt = input.runtimeGrantAuth ? null : yield* resolveJwt(input.sessionId, input.subject);
|
|
88178
|
+
if (!input.runtimeGrantAuth && !jwt) return {
|
|
88179
|
+
status: 401,
|
|
88180
|
+
repository: null,
|
|
88181
|
+
error: "CMMD not connected"
|
|
88182
|
+
};
|
|
88183
|
+
const auth = input.runtimeGrantAuth ?? {
|
|
87584
88184
|
kind: "cmmd-jwt",
|
|
87585
|
-
jwt
|
|
87586
|
-
}
|
|
87587
|
-
|
|
87588
|
-
|
|
87589
|
-
|
|
87590
|
-
|
|
87591
|
-
|
|
87592
|
-
|
|
87593
|
-
|
|
88185
|
+
jwt
|
|
88186
|
+
};
|
|
88187
|
+
const searchTerm = requested.split("/")[1] ?? requested;
|
|
88188
|
+
let result = yield* require_Schema$1.promise(() => fetchGitHubReposForAuth(auth, {
|
|
88189
|
+
search: searchTerm,
|
|
88190
|
+
organizationId: input.organizationId ?? ""
|
|
88191
|
+
}));
|
|
88192
|
+
if (!input.runtimeGrantAuth && shouldRetryWithRefreshedCmmdJwt(result.status)) {
|
|
88193
|
+
const refreshedJwt = yield* resolveJwt(input.sessionId, input.subject, true);
|
|
88194
|
+
if (refreshedJwt && refreshedJwt !== jwt) result = yield* require_Schema$1.promise(() => fetchGitHubReposForAuth({
|
|
88195
|
+
kind: "cmmd-jwt",
|
|
88196
|
+
jwt: refreshedJwt
|
|
88197
|
+
}, {
|
|
88198
|
+
search: searchTerm,
|
|
88199
|
+
organizationId: input.organizationId ?? ""
|
|
88200
|
+
}));
|
|
88201
|
+
}
|
|
88202
|
+
if (result.status !== 200) {
|
|
88203
|
+
const body = result.body;
|
|
88204
|
+
return {
|
|
88205
|
+
status: result.status,
|
|
88206
|
+
repository: null,
|
|
88207
|
+
error: typeof body.error === "string" ? body.error : "GitHub repositories are unavailable."
|
|
88208
|
+
};
|
|
88209
|
+
}
|
|
88210
|
+
const match = selectGitHubRepositoryMatch(result.body, requested);
|
|
88211
|
+
if (match === null) return {
|
|
88212
|
+
status: 404,
|
|
88213
|
+
repository: null,
|
|
88214
|
+
error: `Repository ${requested} was not found in the GitHub accounts connected to Forge.`
|
|
88215
|
+
};
|
|
88216
|
+
return {
|
|
88217
|
+
status: 200,
|
|
88218
|
+
repository: describeGitHubRepository(match),
|
|
88219
|
+
error: null
|
|
88220
|
+
};
|
|
88221
|
+
});
|
|
88222
|
+
}
|
|
88223
|
+
/**
|
|
88224
|
+
* Find the one repository in a brokered listing whose full name is the one
|
|
88225
|
+
* asked for.
|
|
88226
|
+
*
|
|
88227
|
+
* Exact and case-insensitive. The listing is a *search*, so it comes back with
|
|
88228
|
+
* near misses in it: asking for `owner/forge` and being handed `someone-else/
|
|
88229
|
+
* forge` because it sorted first is how a person ends up cloning a stranger's
|
|
88230
|
+
* repository, and a prefix match would do exactly that.
|
|
88231
|
+
*/
|
|
88232
|
+
function selectGitHubRepositoryMatch(body, requested) {
|
|
88233
|
+
const repos = body?.repos ?? [];
|
|
88234
|
+
const wanted = requested.trim().toLowerCase();
|
|
88235
|
+
for (const repo of repos) if (typeof repo.full_name === "string" && repo.full_name.toLowerCase() === wanted) return repo.full_name;
|
|
88236
|
+
return null;
|
|
88237
|
+
}
|
|
88238
|
+
/**
|
|
88239
|
+
* The repository a clone request is really for, or `null` if there is not one.
|
|
88240
|
+
*
|
|
88241
|
+
* A caller may name `owner/repo`, or paste a clone URL and let the server work
|
|
88242
|
+
* it out. Anything that does not resolve to a GitHub repository returns `null`
|
|
88243
|
+
* and must be refused rather than cloned: Forge clones through a CMMD-brokered
|
|
88244
|
+
* installation token for a named repository, and "just run `git clone` on this
|
|
88245
|
+
* URL instead" would be a second, unbrokered way to write to disk.
|
|
88246
|
+
*/
|
|
88247
|
+
function resolveGitHubCloneTarget(input) {
|
|
88248
|
+
const named = input.repository?.trim();
|
|
88249
|
+
if (named && /^[^/\s]+\/[^/\s]+$/.test(named)) return named;
|
|
88250
|
+
if (named) return null;
|
|
88251
|
+
const remoteUrl = input.remoteUrl?.trim();
|
|
88252
|
+
return remoteUrl ? parseRepoFullNameFromRemoteUrl(remoteUrl) : null;
|
|
88253
|
+
}
|
|
88254
|
+
/**
|
|
88255
|
+
* Clone `repoFullName` for an authenticated session.
|
|
88256
|
+
*
|
|
88257
|
+
* Authorization is the caller's own CMMD connection: the session's JWT (or a
|
|
88258
|
+
* runtime grant, when this VM is running under one) is exchanged with CMMD for
|
|
88259
|
+
* an installation token scoped to that one repository. A session with no CMMD
|
|
88260
|
+
* connection gets 401 and no clone; a repository CMMD will not issue a token
|
|
88261
|
+
* for gets CMMD's own refusal, not a `git clone` attempt.
|
|
88262
|
+
*/
|
|
88263
|
+
function runGitHubCloneForSession(input) {
|
|
88264
|
+
return require_Schema$1.gen(function* () {
|
|
88265
|
+
const { config, repoFullName, targetDir } = input;
|
|
88266
|
+
const token = yield* acquireInstallationTokenForSession(input);
|
|
88267
|
+
if (!token.ok) return token.outcome;
|
|
88268
|
+
const cloneDir = resolveGitHubCloneDir(config.baseDir, repoFullName, targetDir);
|
|
88269
|
+
yield* require_Schema$1.promise(() => (0, node_fs_promises.mkdir)((0, node_path.join)(cloneDir, ".."), { recursive: true }).catch(() => void 0));
|
|
88270
|
+
const target = {
|
|
88271
|
+
cloneDir,
|
|
88272
|
+
repoFullName,
|
|
88273
|
+
token: token.token,
|
|
88274
|
+
config
|
|
88275
|
+
};
|
|
88276
|
+
return (0, node_fs.existsSync)(cloneDir) ? yield* adoptExistingGitHubClone(target) : yield* cloneGitHubRepositoryInto(target);
|
|
88277
|
+
});
|
|
88278
|
+
}
|
|
88279
|
+
/**
|
|
88280
|
+
* The installation token for one repository, on the caller's own authority.
|
|
88281
|
+
*
|
|
88282
|
+
* A runtime grant is used as-is. A session resolves its CMMD JWT and retries
|
|
88283
|
+
* once with a refreshed one when CMMD answers 401 or 403, because the cached
|
|
88284
|
+
* JWT may simply have expired. The failure branch is already the HTTP outcome,
|
|
88285
|
+
* so a caller returns it rather than translating it.
|
|
88286
|
+
*/
|
|
88287
|
+
function acquireInstallationTokenForSession(input) {
|
|
88288
|
+
return require_Schema$1.gen(function* () {
|
|
88289
|
+
const { runtimeGrantAuth, repoFullName } = input;
|
|
88290
|
+
const jwt = runtimeGrantAuth ? null : yield* resolveJwt(input.sessionId, input.subject);
|
|
88291
|
+
if (!runtimeGrantAuth && !jwt) return {
|
|
88292
|
+
ok: false,
|
|
88293
|
+
outcome: {
|
|
88294
|
+
status: 401,
|
|
88295
|
+
body: { error: "CMMD not connected" }
|
|
88296
|
+
}
|
|
88297
|
+
};
|
|
88298
|
+
let token = yield* fetchInstallationTokenForAuth(runtimeGrantAuth ?? {
|
|
88299
|
+
kind: "cmmd-jwt",
|
|
88300
|
+
jwt
|
|
88301
|
+
}, repoFullName);
|
|
88302
|
+
if (!runtimeGrantAuth && !token.ok && shouldRetryWithRefreshedCmmdJwt(token.status)) {
|
|
88303
|
+
const refreshedJwt = yield* resolveJwt(input.sessionId, input.subject, true);
|
|
88304
|
+
if (refreshedJwt && refreshedJwt !== jwt) token = yield* fetchInstallationTokenForAuth({
|
|
88305
|
+
kind: "cmmd-jwt",
|
|
88306
|
+
jwt: refreshedJwt
|
|
88307
|
+
}, repoFullName);
|
|
88308
|
+
}
|
|
88309
|
+
if (!token.ok) return {
|
|
88310
|
+
ok: false,
|
|
88311
|
+
outcome: {
|
|
88312
|
+
status: token.status,
|
|
88313
|
+
body: gitHubTokenFailureBody(token.status, token.error)
|
|
88314
|
+
}
|
|
88315
|
+
};
|
|
88316
|
+
return {
|
|
88317
|
+
ok: true,
|
|
88318
|
+
token: token.token
|
|
88319
|
+
};
|
|
88320
|
+
});
|
|
88321
|
+
}
|
|
88322
|
+
/**
|
|
88323
|
+
* A directory already sits where the clone would go. Adopt it when it is a
|
|
88324
|
+
* checkout of the same repository, so a second "add project" is a no-op with
|
|
88325
|
+
* fresh credentials rather than a failure; refuse otherwise, naming the
|
|
88326
|
+
* folder, because overwriting it would destroy something the person made.
|
|
88327
|
+
*/
|
|
88328
|
+
function adoptExistingGitHubClone(target) {
|
|
88329
|
+
return require_Schema$1.gen(function* () {
|
|
88330
|
+
const { cloneDir, repoFullName } = target;
|
|
88331
|
+
const git = (args) => require_Schema$1.promise(() => runProcess("git", [
|
|
87594
88332
|
"-C",
|
|
87595
88333
|
cloneDir,
|
|
87596
|
-
|
|
87597
|
-
"--is-inside-work-tree"
|
|
88334
|
+
...args
|
|
87598
88335
|
], {
|
|
87599
88336
|
timeoutMs: 1e4,
|
|
87600
88337
|
allowNonZeroExit: true,
|
|
87601
88338
|
env: process.env
|
|
87602
88339
|
}));
|
|
87603
|
-
const
|
|
87604
|
-
|
|
87605
|
-
cloneDir,
|
|
88340
|
+
const workTree = yield* git(["rev-parse", "--is-inside-work-tree"]);
|
|
88341
|
+
const remote = yield* git([
|
|
87606
88342
|
"remote",
|
|
87607
88343
|
"get-url",
|
|
87608
88344
|
"origin"
|
|
87609
|
-
]
|
|
87610
|
-
|
|
87611
|
-
|
|
87612
|
-
|
|
87613
|
-
|
|
87614
|
-
|
|
87615
|
-
|
|
87616
|
-
|
|
87617
|
-
|
|
87618
|
-
|
|
87619
|
-
|
|
87620
|
-
|
|
87621
|
-
return json$6({
|
|
88345
|
+
]);
|
|
88346
|
+
if (!(workTree.code === 0 && workTree.stdout.trim() === "true" && remote.code === 0 && isGitHubRemoteForRepo(remote.stdout.trim(), repoFullName))) return {
|
|
88347
|
+
status: 409,
|
|
88348
|
+
body: {
|
|
88349
|
+
error: "A project folder with that name already exists, but it is not the selected GitHub repository. Rename the import, remove the VM folder, or choose a different repository.",
|
|
88350
|
+
local_path: cloneDir
|
|
88351
|
+
}
|
|
88352
|
+
};
|
|
88353
|
+
yield* applyGitCredentialsFor(target);
|
|
88354
|
+
return {
|
|
88355
|
+
status: 200,
|
|
88356
|
+
body: {
|
|
87622
88357
|
local_path: cloneDir,
|
|
87623
88358
|
existing: true
|
|
87624
|
-
}
|
|
87625
|
-
}
|
|
87626
|
-
|
|
87627
|
-
|
|
87628
|
-
|
|
87629
|
-
|
|
87630
|
-
|
|
87631
|
-
|
|
87632
|
-
|
|
87633
|
-
|
|
87634
|
-
|
|
87635
|
-
|
|
87636
|
-
|
|
87637
|
-
|
|
88359
|
+
}
|
|
88360
|
+
};
|
|
88361
|
+
});
|
|
88362
|
+
}
|
|
88363
|
+
function cloneGitHubRepositoryInto(target) {
|
|
88364
|
+
return require_Schema$1.gen(function* () {
|
|
88365
|
+
const { cloneDir, repoFullName, token } = target;
|
|
88366
|
+
const result = yield* require_Schema$1.promise(() => runProcess("git", buildAuthenticatedGitCloneArgs(repoFullName, cloneDir), {
|
|
88367
|
+
timeoutMs: 12e4,
|
|
88368
|
+
allowNonZeroExit: true,
|
|
88369
|
+
env: {
|
|
88370
|
+
...process.env,
|
|
88371
|
+
CMMD_FORGE_GIT_TOKEN: token,
|
|
88372
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
88373
|
+
}
|
|
88374
|
+
}));
|
|
88375
|
+
if (result.code !== 0) {
|
|
88376
|
+
const stderr = sanitizeGitCredentialText(result.stderr).trim().slice(0, 500);
|
|
88377
|
+
return {
|
|
88378
|
+
status: 500,
|
|
88379
|
+
body: { error: `git clone failed${stderr.length > 0 ? `: ${stderr}` : ""}` }
|
|
88380
|
+
};
|
|
87638
88381
|
}
|
|
87639
|
-
|
|
87640
|
-
|
|
87641
|
-
|
|
87642
|
-
|
|
88382
|
+
yield* applyGitCredentialsFor(target);
|
|
88383
|
+
return {
|
|
88384
|
+
status: 200,
|
|
88385
|
+
body: { local_path: cloneDir }
|
|
88386
|
+
};
|
|
88387
|
+
});
|
|
88388
|
+
}
|
|
88389
|
+
function applyGitCredentialsFor(target) {
|
|
88390
|
+
return applyGitCredentials({
|
|
88391
|
+
cloneDir: target.cloneDir,
|
|
88392
|
+
repoFullName: target.repoFullName,
|
|
88393
|
+
token: target.token,
|
|
88394
|
+
mode: target.config.forgeGitCredentialCache ? "cache" : "embed"
|
|
88395
|
+
});
|
|
88396
|
+
}
|
|
88397
|
+
const githubCloneRouteLayer = add$1("POST", "/.well-known/forge/github/clone", require_Schema$1.gen(function* () {
|
|
88398
|
+
const request = yield* require_HttpServer.HttpServerRequest;
|
|
88399
|
+
const session = yield* (yield* ServerAuth).authenticateHttpRequest(request);
|
|
88400
|
+
const config = yield* ServerConfig;
|
|
88401
|
+
if (session.method === "proxy-asserted") {
|
|
88402
|
+
const forwardedJwt = normalizeForwardedCmmdJwt(request.headers);
|
|
88403
|
+
if (forwardedJwt !== void 0) yield* setCommandJwtForSession(session.sessionId, forwardedJwt).pipe(require_Schema$1.ignore({ log: true }));
|
|
87643
88404
|
}
|
|
87644
|
-
yield*
|
|
87645
|
-
|
|
88405
|
+
const rawBody = yield* request.json;
|
|
88406
|
+
const parsed = require_Schema$1.decodeUnknownExit(CloneBodySchema)(rawBody);
|
|
88407
|
+
if (parsed._tag === "Failure") return json$6({ error: "repo_full_name is required" }, 400);
|
|
88408
|
+
const { repo_full_name, target_dir } = parsed.value;
|
|
88409
|
+
const outcome = yield* runGitHubCloneForSession({
|
|
88410
|
+
config,
|
|
88411
|
+
sessionId: session.sessionId,
|
|
88412
|
+
subject: session.subject,
|
|
88413
|
+
runtimeGrantAuth: resolveRuntimeGitHubGrantAuth(config, request.headers),
|
|
87646
88414
|
repoFullName: repo_full_name,
|
|
87647
|
-
|
|
87648
|
-
mode: config.forgeGitCredentialCache ? "cache" : "embed"
|
|
88415
|
+
targetDir: target_dir
|
|
87649
88416
|
});
|
|
87650
|
-
return json$6(
|
|
88417
|
+
return json$6(outcome.body, outcome.status);
|
|
87651
88418
|
}).pipe(require_Schema$1.catchTag("AuthError", respondToAuthError)));
|
|
87652
88419
|
const githubCloneJobStartRouteLayer = add$1("POST", "/.well-known/forge/github/clone-jobs", require_Schema$1.gen(function* () {
|
|
87653
88420
|
const request = yield* require_HttpServer.HttpServerRequest;
|
|
@@ -104469,7 +105236,9 @@ const REJECT_UNCERTAIN_METHODS = new Set([
|
|
|
104469
105236
|
WS_METHODS.pullRequestsComment,
|
|
104470
105237
|
WS_METHODS.pullRequestsSubmitReview,
|
|
104471
105238
|
WS_METHODS.pullRequestsReplyToThread,
|
|
104472
|
-
WS_METHODS.projectCheckoutDeletionExecute
|
|
105239
|
+
WS_METHODS.projectCheckoutDeletionExecute,
|
|
105240
|
+
WS_METHODS.providerUploadFeedback,
|
|
105241
|
+
WS_METHODS.sourceControlCloneRepository
|
|
104473
105242
|
]);
|
|
104474
105243
|
function classifyInterruptedRelayCommand(input) {
|
|
104475
105244
|
if ((input.method === WS_METHODS.gitCreateBranch || input.method === WS_METHODS.vcsCreateRef) && input.deliveryClass === "create-command") return { kind: "reconcile-git-create-branch" };
|
|
@@ -105401,7 +106170,77 @@ const makeWsRpcLayer = (currentSessionId, currentUserId) => WsRpcGroup.toLayer(r
|
|
|
105401
106170
|
payload: initialSnapshot
|
|
105402
106171
|
}), liveEvents);
|
|
105403
106172
|
}), { "rpc.aggregate": "auth" }),
|
|
105404
|
-
[WS_METHODS.subscribeWorkspaceReadiness]: (_input) => currentUserId === void 0 ? require_Path.empty : workspaceReadinessState.subscribe(currentUserId)
|
|
106173
|
+
[WS_METHODS.subscribeWorkspaceReadiness]: (_input) => currentUserId === void 0 ? require_Path.empty : workspaceReadinessState.subscribe(currentUserId),
|
|
106174
|
+
[WS_METHODS.assetsCreateUrl]: (input) => observeRpcEffect(WS_METHODS.assetsCreateUrl, createAssetUrl(currentSessionId, input.resource), { "rpc.aggregate": "assets" }),
|
|
106175
|
+
[WS_METHODS.providerUploadFeedback]: (input) => observeRpcEffect(WS_METHODS.providerUploadFeedback, uploadThreadFeedback(orchestrationEngine, currentSessionId, currentUserId, input), { "rpc.aggregate": "provider" }),
|
|
106176
|
+
[ORCHESTRATION_WS_METHODS.searchThreads]: (input) => observeRpcEffect(ORCHESTRATION_WS_METHODS.searchThreads, require_Schema$1.gen(function* () {
|
|
106177
|
+
yield* startup.awaitCommandReady;
|
|
106178
|
+
return yield* projectionSnapshotQuery.searchThreads(input);
|
|
106179
|
+
}).pipe(require_Schema$1.mapError((cause) => new OrchestrationSearchThreadsError({
|
|
106180
|
+
message: "Failed to search threads",
|
|
106181
|
+
cause
|
|
106182
|
+
}))), { "rpc.aggregate": "orchestration" }),
|
|
106183
|
+
[WS_METHODS.sourceControlLookupRepository]: (input) => observeRpcEffect(WS_METHODS.sourceControlLookupRepository, require_Schema$1.gen(function* () {
|
|
106184
|
+
if (input.provider !== "github") return yield* new SourceControlRepositoryError({
|
|
106185
|
+
provider: input.provider,
|
|
106186
|
+
operation: "lookupRepository",
|
|
106187
|
+
detail: `Forge can only look up GitHub repositories. ${input.provider} is not connected.`
|
|
106188
|
+
});
|
|
106189
|
+
if (currentUserId === void 0) return yield* new SourceControlRepositoryError({
|
|
106190
|
+
provider: "github",
|
|
106191
|
+
operation: "lookupRepository",
|
|
106192
|
+
detail: "Sign in to Forge before looking up a repository."
|
|
106193
|
+
});
|
|
106194
|
+
const outcome = yield* lookupGitHubRepositoryForSession({
|
|
106195
|
+
sessionId: currentSessionId,
|
|
106196
|
+
subject: currentUserId,
|
|
106197
|
+
runtimeGrantAuth: null,
|
|
106198
|
+
repository: input.repository
|
|
106199
|
+
});
|
|
106200
|
+
if (outcome.repository === null) return yield* new SourceControlRepositoryError({
|
|
106201
|
+
provider: "github",
|
|
106202
|
+
operation: "lookupRepository",
|
|
106203
|
+
detail: outcome.error ?? "Repository lookup failed."
|
|
106204
|
+
});
|
|
106205
|
+
return {
|
|
106206
|
+
provider: "github",
|
|
106207
|
+
...outcome.repository
|
|
106208
|
+
};
|
|
106209
|
+
}), { "rpc.aggregate": "sourceControl" }),
|
|
106210
|
+
[WS_METHODS.sourceControlCloneRepository]: (input) => observeRpcEffect(WS_METHODS.sourceControlCloneRepository, require_Schema$1.gen(function* () {
|
|
106211
|
+
const provider = input.provider ?? "github";
|
|
106212
|
+
const repoFullName = resolveGitHubCloneTarget(input);
|
|
106213
|
+
const refuse = (detail) => new SourceControlRepositoryError({
|
|
106214
|
+
provider,
|
|
106215
|
+
operation: "cloneRepository",
|
|
106216
|
+
detail
|
|
106217
|
+
});
|
|
106218
|
+
if (provider !== "github") return yield* refuse(`Forge can only clone GitHub repositories. ${provider} is not connected.`);
|
|
106219
|
+
if (currentUserId === void 0) return yield* refuse("Sign in to Forge before cloning a repository.");
|
|
106220
|
+
if (repoFullName === null) return yield* refuse("Name the repository as owner/name, or paste a github.com clone URL.");
|
|
106221
|
+
const outcome = yield* runGitHubCloneForSession({
|
|
106222
|
+
config,
|
|
106223
|
+
sessionId: currentSessionId,
|
|
106224
|
+
subject: currentUserId,
|
|
106225
|
+
runtimeGrantAuth: null,
|
|
106226
|
+
repoFullName,
|
|
106227
|
+
targetDir: input.destinationPath
|
|
106228
|
+
});
|
|
106229
|
+
const localPath = outcome.body.local_path;
|
|
106230
|
+
if (outcome.status !== 200 || typeof localPath !== "string") {
|
|
106231
|
+
const detail = outcome.body.error;
|
|
106232
|
+
return yield* refuse(typeof detail === "string" ? detail : "Cloning the repository failed.");
|
|
106233
|
+
}
|
|
106234
|
+
const repository = describeGitHubRepository(repoFullName);
|
|
106235
|
+
return {
|
|
106236
|
+
cwd: localPath,
|
|
106237
|
+
remoteUrl: repository.url,
|
|
106238
|
+
repository: {
|
|
106239
|
+
provider: "github",
|
|
106240
|
+
...repository
|
|
106241
|
+
}
|
|
106242
|
+
};
|
|
106243
|
+
}), { "rpc.aggregate": "sourceControl" })
|
|
105405
106244
|
});
|
|
105406
106245
|
}));
|
|
105407
106246
|
const websocketRpcRouteLayer = require_Schema$1.unwrap(require_Schema$1.succeed(require_Schema$1.mergeAll(add$1("GET", "/ws", require_Schema$1.gen(function* () {
|
|
@@ -116489,8 +117328,8 @@ require_Schema$1.effectDiscard(runMigrations());
|
|
|
116489
117328
|
//#endregion
|
|
116490
117329
|
//#region src/persistence/Layers/Sqlite.ts
|
|
116491
117330
|
const defaultSqliteClientLoaders = {
|
|
116492
|
-
bun: () => Promise.resolve().then(() => require("./SqliteClient-
|
|
116493
|
-
node: () => Promise.resolve().then(() => require("./NodeSqliteClient-
|
|
117331
|
+
bun: () => Promise.resolve().then(() => require("./SqliteClient-Cq1peWN1.cjs")),
|
|
117332
|
+
node: () => Promise.resolve().then(() => require("./NodeSqliteClient-qjBWAOGq.cjs"))
|
|
116494
117333
|
};
|
|
116495
117334
|
const makeRuntimeSqliteLayer = require_Schema$1.fn("makeRuntimeSqliteLayer")(function* (config) {
|
|
116496
117335
|
const loader = defaultSqliteClientLoaders[process.versions.bun !== void 0 ? "bun" : "node"];
|
|
@@ -287450,6 +288289,7 @@ function taskStartedToActivities(event) {
|
|
|
287450
288289
|
summary: taskStartedSummary(event.payload.taskType),
|
|
287451
288290
|
payload: {
|
|
287452
288291
|
taskId: event.payload.taskId,
|
|
288292
|
+
agentKind: classifyTaskAgentKind({ taskType: event.payload.taskType }),
|
|
287453
288293
|
...event.payload.taskType ? { taskType: event.payload.taskType } : {},
|
|
287454
288294
|
...event.payload.description ? { detail: truncateDetail(event.payload.description) } : {}
|
|
287455
288295
|
}
|
|
@@ -295309,7 +296149,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
295309
296149
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
295310
296150
|
*/
|
|
295311
296151
|
function readBakedBuildCommit() {
|
|
295312
|
-
return "
|
|
296152
|
+
return "c220fa73834b4651c7fc65f272c171028edc3e81";
|
|
295313
296153
|
}
|
|
295314
296154
|
async function resolveServerBuildCommit(input) {
|
|
295315
296155
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -295426,6 +296266,7 @@ function executionEnvironmentCapabilities(serverConfig) {
|
|
|
295426
296266
|
pullRequests: isExecutionPlane,
|
|
295427
296267
|
threadPullRequestLinking: isExecutionPlane,
|
|
295428
296268
|
threadSettlement: isExecutionPlane,
|
|
296269
|
+
threadTitleRegeneration: isExecutionPlane,
|
|
295429
296270
|
cloudMode: serverConfig.forgeCmmdRuntimeMode === true || isCloudShell,
|
|
295430
296271
|
runtime: {
|
|
295431
296272
|
topology: runtimeTopology(serverConfig),
|
|
@@ -295711,10 +296552,6 @@ const pulseSignalStateUpsertRouteLayer = add$1("POST", "/api/pulse/signals/state
|
|
|
295711
296552
|
});
|
|
295712
296553
|
}).pipe(require_Schema$1.catchTag("AuthError", respondToAuthError)));
|
|
295713
296554
|
//#endregion
|
|
295714
|
-
//#region src/auth/Services/ServerSecretStore.ts
|
|
295715
|
-
var SecretStoreError = class extends require_Schema$1.TaggedError("SecretStoreError") {};
|
|
295716
|
-
var ServerSecretStore = class extends require_Schema$1.Service()("forge/auth/Services/ServerSecretStore") {};
|
|
295717
|
-
//#endregion
|
|
295718
296555
|
//#region src/auth/Layers/ServerSecretStore.ts
|
|
295719
296556
|
const OWNED_FILE_RECEIPT_PREFIX = "forge-owned-secret-receipt:v1:";
|
|
295720
296557
|
const LEGACY_FILE_SECRET_NAMESPACE_PREFIX = "forge-secret-namespace:v1:";
|
|
@@ -302087,6 +302924,55 @@ const ProjectionCountsRowSchema = require_Schema$1.Struct({
|
|
|
302087
302924
|
const WorkspaceRootLookupInput = require_Schema$1.Struct({ workspaceRoot: require_Schema$1.String });
|
|
302088
302925
|
const ProjectIdLookupInput = require_Schema$1.Struct({ projectId: ProjectId });
|
|
302089
302926
|
const ThreadIdLookupInput = require_Schema$1.Struct({ threadId: ThreadId });
|
|
302927
|
+
const ThreadSearchQueryInput = require_Schema$1.Struct({
|
|
302928
|
+
pattern: require_Schema$1.String,
|
|
302929
|
+
limit: require_Schema$1.Number
|
|
302930
|
+
});
|
|
302931
|
+
const ThreadSearchMessageRowSchema = require_Schema$1.Struct({
|
|
302932
|
+
threadId: ThreadId,
|
|
302933
|
+
projectId: ProjectId,
|
|
302934
|
+
role: require_Schema$1.String,
|
|
302935
|
+
text: require_Schema$1.String,
|
|
302936
|
+
createdAt: IsoDateTime
|
|
302937
|
+
});
|
|
302938
|
+
const ThreadSearchMetadataRowSchema = require_Schema$1.Struct({
|
|
302939
|
+
threadId: ThreadId,
|
|
302940
|
+
projectId: ProjectId,
|
|
302941
|
+
title: require_Schema$1.String,
|
|
302942
|
+
branch: require_Schema$1.NullOr(require_Schema$1.String)
|
|
302943
|
+
});
|
|
302944
|
+
/** The contract caps a snippet at this length, so the server must too. */
|
|
302945
|
+
const THREAD_SEARCH_SNIPPET_LIMIT = 240;
|
|
302946
|
+
/** How many matches a search returns when the caller does not say. */
|
|
302947
|
+
const THREAD_SEARCH_DEFAULT_LIMIT = 20;
|
|
302948
|
+
/**
|
|
302949
|
+
* `LIKE` treats `%` and `_` as wildcards, so a person searching for a literal
|
|
302950
|
+
* underscore (every other identifier in a codebase) would otherwise match any
|
|
302951
|
+
* character. Escaped with a backslash, declared to SQL as `ESCAPE '\'`, and
|
|
302952
|
+
* lowercased on both sides because Postgres `LIKE` is case-sensitive where
|
|
302953
|
+
* SQLite's is not, and Forge runs on both.
|
|
302954
|
+
*/
|
|
302955
|
+
function toThreadSearchPattern(query) {
|
|
302956
|
+
return `%${query.toLowerCase().replace(/[\\%_]/g, (character) => `\\${character}`)}%`;
|
|
302957
|
+
}
|
|
302958
|
+
/**
|
|
302959
|
+
* A window of the text around the first hit, not the head of it.
|
|
302960
|
+
*
|
|
302961
|
+
* A match 4000 characters into an assistant message is invisible in a snippet
|
|
302962
|
+
* that always starts at character zero, which is the difference between a
|
|
302963
|
+
* result a person can recognize and a result they have to open to understand.
|
|
302964
|
+
*/
|
|
302965
|
+
function buildThreadSearchSnippet(text, lowerQuery) {
|
|
302966
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
302967
|
+
if (collapsed.length <= THREAD_SEARCH_SNIPPET_LIMIT) return collapsed;
|
|
302968
|
+
const matchIndex = collapsed.toLowerCase().indexOf(lowerQuery);
|
|
302969
|
+
if (matchIndex < 0) return `${collapsed.slice(0, THREAD_SEARCH_SNIPPET_LIMIT - 1)}…`;
|
|
302970
|
+
const leading = Math.floor((THREAD_SEARCH_SNIPPET_LIMIT - lowerQuery.length) / 2);
|
|
302971
|
+
const start = Math.max(0, matchIndex - Math.max(leading, 0));
|
|
302972
|
+
const windowed = collapsed.slice(start, start + THREAD_SEARCH_SNIPPET_LIMIT);
|
|
302973
|
+
const prefixed = start > 0 ? `…${windowed}` : windowed;
|
|
302974
|
+
return prefixed.length > THREAD_SEARCH_SNIPPET_LIMIT ? prefixed.slice(0, THREAD_SEARCH_SNIPPET_LIMIT) : prefixed;
|
|
302975
|
+
}
|
|
302090
302976
|
const ProjectionProjectLookupRowSchema = ProjectionProjectDbRowSchema;
|
|
302091
302977
|
const ProjectionThreadIdLookupRowSchema = require_Schema$1.Struct({ threadId: ThreadId });
|
|
302092
302978
|
const ProjectionThreadCheckpointContextThreadRowSchema = require_Schema$1.Struct({
|
|
@@ -302332,6 +303218,61 @@ const makeProjectionSnapshotQuery = require_Schema$1.gen(function* () {
|
|
|
302332
303218
|
AND deleted_at IS NULL
|
|
302333
303219
|
AND archived_at IS NULL
|
|
302334
303220
|
ORDER BY created_at ASC, thread_id ASC
|
|
303221
|
+
`
|
|
303222
|
+
});
|
|
303223
|
+
/**
|
|
303224
|
+
* Message bodies containing the query.
|
|
303225
|
+
*
|
|
303226
|
+
* `role IN ('user', 'assistant')` is not a filter for tidiness: the match
|
|
303227
|
+
* shape on the wire names one of those two as the side the hit came from,
|
|
303228
|
+
* and a system message has no side to report. Ordered newest first because a
|
|
303229
|
+
* capped search should keep what the person is most likely still thinking
|
|
303230
|
+
* about.
|
|
303231
|
+
*/
|
|
303232
|
+
const searchThreadMessageRows = findAll({
|
|
303233
|
+
Request: ThreadSearchQueryInput,
|
|
303234
|
+
Result: ThreadSearchMessageRowSchema,
|
|
303235
|
+
execute: ({ pattern, limit }) => sql`
|
|
303236
|
+
SELECT
|
|
303237
|
+
m.thread_id AS "threadId",
|
|
303238
|
+
t.project_id AS "projectId",
|
|
303239
|
+
m.role AS "role",
|
|
303240
|
+
m.text AS "text",
|
|
303241
|
+
m.created_at AS "createdAt"
|
|
303242
|
+
FROM projection_thread_messages AS m
|
|
303243
|
+
JOIN projection_threads AS t ON t.thread_id = m.thread_id
|
|
303244
|
+
WHERE t.deleted_at IS NULL
|
|
303245
|
+
AND m.role IN ('user', 'assistant')
|
|
303246
|
+
AND LOWER(m.text) LIKE ${pattern} ESCAPE '\\'
|
|
303247
|
+
ORDER BY m.created_at DESC, m.message_id ASC
|
|
303248
|
+
LIMIT ${limit}
|
|
303249
|
+
`
|
|
303250
|
+
});
|
|
303251
|
+
/**
|
|
303252
|
+
* Threads whose own metadata contains the query.
|
|
303253
|
+
*
|
|
303254
|
+
* Title and branch, which is what the web command palette matches on. Title
|
|
303255
|
+
* is duplicated work for a client that already filters its in-memory list,
|
|
303256
|
+
* and harmless because matches are deduplicated by thread; branch is not,
|
|
303257
|
+
* because no client holds every branch.
|
|
303258
|
+
*/
|
|
303259
|
+
const searchThreadMetadataRows = findAll({
|
|
303260
|
+
Request: ThreadSearchQueryInput,
|
|
303261
|
+
Result: ThreadSearchMetadataRowSchema,
|
|
303262
|
+
execute: ({ pattern, limit }) => sql`
|
|
303263
|
+
SELECT
|
|
303264
|
+
thread_id AS "threadId",
|
|
303265
|
+
project_id AS "projectId",
|
|
303266
|
+
title,
|
|
303267
|
+
branch
|
|
303268
|
+
FROM projection_threads
|
|
303269
|
+
WHERE deleted_at IS NULL
|
|
303270
|
+
AND (
|
|
303271
|
+
LOWER(title) LIKE ${pattern} ESCAPE '\\'
|
|
303272
|
+
OR LOWER(branch) LIKE ${pattern} ESCAPE '\\'
|
|
303273
|
+
)
|
|
303274
|
+
ORDER BY updated_at DESC, thread_id ASC
|
|
303275
|
+
LIMIT ${limit}
|
|
302335
303276
|
`
|
|
302336
303277
|
});
|
|
302337
303278
|
/**
|
|
@@ -303078,6 +304019,40 @@ const makeProjectionSnapshotQuery = require_Schema$1.gen(function* () {
|
|
|
303078
304019
|
};
|
|
303079
304020
|
const getShellSnapshot = () => readShellSnapshot("getShellSnapshot", listThreadRows);
|
|
303080
304021
|
const getArchivedShellSnapshot = () => readShellSnapshot("getArchivedShellSnapshot", listArchivedThreadRows);
|
|
304022
|
+
const searchThreads = (input) => {
|
|
304023
|
+
const limit = input.limit ?? THREAD_SEARCH_DEFAULT_LIMIT;
|
|
304024
|
+
const lowerQuery = input.query.toLowerCase();
|
|
304025
|
+
const request = {
|
|
304026
|
+
pattern: toThreadSearchPattern(input.query),
|
|
304027
|
+
limit
|
|
304028
|
+
};
|
|
304029
|
+
return require_Schema$1.all([searchThreadMessageRows(request).pipe(require_Schema$1.mapError(toPersistenceSqlOrDecodeError("ProjectionSnapshotQuery.searchThreads:messages:query", "ProjectionSnapshotQuery.searchThreads:messages:decodeRows"))), searchThreadMetadataRows(request).pipe(require_Schema$1.mapError(toPersistenceSqlOrDecodeError("ProjectionSnapshotQuery.searchThreads:metadata:query", "ProjectionSnapshotQuery.searchThreads:metadata:decodeRows")))]).pipe(require_Schema$1.map(([messageRows, metadataRows]) => {
|
|
304030
|
+
const byThread = /* @__PURE__ */ new Map();
|
|
304031
|
+
for (const row of messageRows) {
|
|
304032
|
+
if (byThread.has(row.threadId)) continue;
|
|
304033
|
+
byThread.set(row.threadId, {
|
|
304034
|
+
threadId: row.threadId,
|
|
304035
|
+
projectId: row.projectId,
|
|
304036
|
+
source: row.role === "assistant" ? "assistant" : "user",
|
|
304037
|
+
snippet: buildThreadSearchSnippet(row.text, lowerQuery),
|
|
304038
|
+
messageCreatedAt: row.createdAt
|
|
304039
|
+
});
|
|
304040
|
+
}
|
|
304041
|
+
for (const row of metadataRows) {
|
|
304042
|
+
if (byThread.has(row.threadId)) continue;
|
|
304043
|
+
const branchMatched = row.branch?.toLowerCase().includes(lowerQuery) === true;
|
|
304044
|
+
const titleMatched = row.title.toLowerCase().includes(lowerQuery);
|
|
304045
|
+
byThread.set(row.threadId, {
|
|
304046
|
+
threadId: row.threadId,
|
|
304047
|
+
projectId: row.projectId,
|
|
304048
|
+
source: "user",
|
|
304049
|
+
snippet: buildThreadSearchSnippet(titleMatched || !branchMatched ? row.title : row.branch ?? row.title, lowerQuery),
|
|
304050
|
+
messageCreatedAt: null
|
|
304051
|
+
});
|
|
304052
|
+
}
|
|
304053
|
+
return { matches: [...byThread.values()].slice(0, limit) };
|
|
304054
|
+
}));
|
|
304055
|
+
};
|
|
303081
304056
|
const listActiveWorkerShells = () => sql.withTransaction(require_Schema$1.all([
|
|
303082
304057
|
listActiveWorkerThreadRows(void 0).pipe(require_Schema$1.mapError(toPersistenceSqlOrDecodeError("ProjectionSnapshotQuery.listActiveWorkerShells:listThreads:query", "ProjectionSnapshotQuery.listActiveWorkerShells:listThreads:decodeRows"))),
|
|
303083
304058
|
listActiveWorkerSessionRows(void 0).pipe(require_Schema$1.mapError(toPersistenceSqlOrDecodeError("ProjectionSnapshotQuery.listActiveWorkerShells:listThreadSessions:query", "ProjectionSnapshotQuery.listActiveWorkerShells:listThreadSessions:decodeRows"))),
|
|
@@ -303246,6 +304221,7 @@ const makeProjectionSnapshotQuery = require_Schema$1.gen(function* () {
|
|
|
303246
304221
|
getSnapshot,
|
|
303247
304222
|
getShellSnapshot,
|
|
303248
304223
|
getArchivedShellSnapshot,
|
|
304224
|
+
searchThreads,
|
|
303249
304225
|
listActiveWorkerShells,
|
|
303250
304226
|
listStaleRunningSessionShells,
|
|
303251
304227
|
getCounts,
|
|
@@ -318510,13 +319486,13 @@ const gitUrlCloneRouteLayer = add$1("POST", "/api/cloud/git/clone-url", require_
|
|
|
318510
319486
|
//#endregion
|
|
318511
319487
|
//#region src/server.ts
|
|
318512
319488
|
const PtyAdapterLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
|
|
318513
|
-
if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunPTY-
|
|
318514
|
-
else return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodePTY-
|
|
319489
|
+
if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunPTY-uyreNRGp.cjs")))).layer;
|
|
319490
|
+
else return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodePTY-S4tPEwZc.cjs")))).layer;
|
|
318515
319491
|
}));
|
|
318516
319492
|
const HttpServerLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
|
|
318517
319493
|
const config = yield* ServerConfig;
|
|
318518
|
-
if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunHttpServer-
|
|
318519
|
-
const [NodeHttpServer, NodeHttp] = yield* require_Schema$1.all([require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeHttpServer-
|
|
319494
|
+
if (typeof Bun !== "undefined") return (yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunHttpServer-DKvSpNd1.cjs")))).layer(bunHttpServerOptions(config));
|
|
319495
|
+
const [NodeHttpServer, NodeHttp] = yield* require_Schema$1.all([require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeHttpServer-Dfc7nSMq.cjs"))), require_Schema$1.promise(() => import("node:http"))]);
|
|
318520
319496
|
return NodeHttpServer.layer(NodeHttp.createServer, {
|
|
318521
319497
|
host: config.host,
|
|
318522
319498
|
port: config.port
|
|
@@ -318524,10 +319500,10 @@ const HttpServerLive = require_Schema$1.unwrap(require_Schema$1.gen(function* ()
|
|
|
318524
319500
|
}));
|
|
318525
319501
|
const PlatformServicesLive = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
|
|
318526
319502
|
if (typeof Bun !== "undefined") {
|
|
318527
|
-
const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunServices-
|
|
319503
|
+
const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./BunServices-DDh7Whyx.cjs")));
|
|
318528
319504
|
return layer;
|
|
318529
319505
|
} else {
|
|
318530
|
-
const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeServices-
|
|
319506
|
+
const { layer } = yield* require_Schema$1.promise(() => Promise.resolve().then(() => require("./NodeServices-DE8jhdcU.cjs")));
|
|
318531
319507
|
return layer;
|
|
318532
319508
|
}
|
|
318533
319509
|
}));
|