@p4code/cli 0.0.49 → 0.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +796 -47
- package/dist/client/assets/{DiffPanel-Byg3lT0_.js → DiffPanel-4ZPfw-bt.js} +3 -3
- package/dist/client/assets/{FilePreviewPanel-CRsMuFPM.js → FilePreviewPanel-DPl3V4qz.js} +5 -5
- package/dist/client/assets/PreviewPanel-DENxqydi.js +2 -0
- package/dist/client/assets/{angular-html-CvITUhcj.js → angular-html-SfFqn_n1.js} +2 -2
- package/dist/client/assets/{angular-ts-CI8oh2Bi.js → angular-ts-TqTMcSgN.js} +2 -2
- package/dist/client/assets/{apl-BewkNmrv.js → apl-DvTVLZcC.js} +2 -2
- package/dist/client/assets/arrow-right-BHEVGFLQ.js +2 -0
- package/dist/client/assets/{astro-CNN681lo.js → astro-DEpzE1sb.js} +2 -2
- package/dist/client/assets/{blade-X4RszKFf.js → blade-Brgi-xhV.js} +2 -2
- package/dist/client/assets/{c-C8AJe2jK.js → c-7H0fCJJT.js} +2 -2
- package/dist/client/assets/{cobol-DQjALrRk.js → cobol-Cq_E3Ld9.js} +2 -2
- package/dist/client/assets/{coffee-uHbL69mO.js → coffee-DpUX5Xcz.js} +2 -2
- package/dist/client/assets/{cpp-D3rb3O4A.js → cpp-BEjZmHLc.js} +2 -2
- package/dist/client/assets/{crystal-spyqFjYj.js → crystal-CycEJw4X.js} +2 -2
- package/dist/client/assets/{css-CbxXkRaq.js → css-y3Oj4fDi.js} +2 -2
- package/dist/client/assets/{edge-YESgrh6X.js → edge-DIFYHFsA.js} +2 -2
- package/dist/client/assets/{elixir-7069ioPL.js → elixir-raLzjUMQ.js} +2 -2
- package/dist/client/assets/{elm-Eb4zNBLt.js → elm-CZsXcOy1.js} +2 -2
- package/dist/client/assets/{erb-Ddb27xlO.js → erb-DhXjIste.js} +2 -2
- package/dist/client/assets/{fileCommentAnnotations-DEIuuBiQ.js → fileCommentAnnotations-DkadV_kZ.js} +2 -2
- package/dist/client/assets/{git-rebase-CdERns2w.js → git-rebase-CZvi6-AX.js} +2 -2
- package/dist/client/assets/{glimmer-js-C6s5Isxr.js → glimmer-js-N_UF0Pmi.js} +2 -2
- package/dist/client/assets/{glimmer-ts-BV7NBmEc.js → glimmer-ts-Cxd1tQNi.js} +2 -2
- package/dist/client/assets/{glsl-jd9QEV4q.js → glsl-B8RUupmi.js} +2 -2
- package/dist/client/assets/{graphql-CXqIl6Zm.js → graphql-Cz2mi8GF.js} +2 -2
- package/dist/client/assets/{hack-lLvPp6HR.js → hack-DD3q-ECT.js} +2 -2
- package/dist/client/assets/{haml-DfPUmRcz.js → haml-C75rIu1p.js} +2 -2
- package/dist/client/assets/{handlebars-QzjSM_We.js → handlebars-C1zz9h1i.js} +2 -2
- package/dist/client/assets/{html-derivative-CkX2Wmz3.js → html-derivative-HEAA-Z2a.js} +2 -2
- package/dist/client/assets/{html-BySpIsKh.js → html-mXOatteF.js} +2 -2
- package/dist/client/assets/{http-CD7dw1li.js → http-DO0ZHyk0.js} +2 -2
- package/dist/client/assets/{hurl-nzp7hSsy.js → hurl-DyDQ6Iuv.js} +2 -2
- package/dist/client/assets/index-30avYZEg.js +663 -0
- package/dist/client/assets/index-C7UXI10J.css +1 -0
- package/dist/client/assets/{java-D2TcotRb.js → java-DicQ_x8H.js} +2 -2
- package/dist/client/assets/{javascript-CpW0WD7L.js → javascript-CTx-kiCR.js} +2 -2
- package/dist/client/assets/{jinja-BL1anCEN.js → jinja-ERQPZ9pQ.js} +2 -2
- package/dist/client/assets/{jison-B-sCtmJb.js → jison-BEedW5zW.js} +2 -2
- package/dist/client/assets/{json-BXeO84aE.js → json-MMdgCPnh.js} +2 -2
- package/dist/client/assets/{jsx-CgueyhKv.js → jsx-CJ--w_e-.js} +2 -2
- package/dist/client/assets/{julia-PMog-fIJ.js → julia-BvGAE4O0.js} +2 -2
- package/dist/client/assets/{just-Bi73ZAuY.js → just-BvBn61hL.js} +2 -2
- package/dist/client/assets/{latex-D6Hw5PyK.js → latex-P2_b6CEl.js} +2 -2
- package/dist/client/assets/{liquid-CuvsknsU.js → liquid-Dm2xzeBN.js} +2 -2
- package/dist/client/assets/{lua-6tqLQzzk.js → lua-DYQLFKMA.js} +2 -2
- package/dist/client/assets/{marko-Bv1DHr8G.js → marko-DCc0fplW.js} +2 -2
- package/dist/client/assets/{mdc-DIKkjN2a.js → mdc-BI6uqWRL.js} +2 -2
- package/dist/client/assets/{nginx-B2JFBlIu.js → nginx-ijGdX1bh.js} +2 -2
- package/dist/client/assets/{nim-D8qC29Zr.js → nim-CUp-JGxu.js} +2 -2
- package/dist/client/assets/{perl-De_VpaHg.js → perl-BKpwnNRr.js} +2 -2
- package/dist/client/assets/{php-zSI5z7ZS.js → php-DDStFT9W.js} +2 -2
- package/dist/client/assets/{pug-rUGxU82h.js → pug-HXcSxf00.js} +2 -2
- package/dist/client/assets/{qml-CXAY6cUl.js → qml-CbzgrBYo.js} +2 -2
- package/dist/client/assets/{r-Cm15VoHH.js → r-CgMrZP2h.js} +2 -2
- package/dist/client/assets/{razor-DkcxdUAy.js → razor-0ZAHbKMz.js} +2 -2
- package/dist/client/assets/{regexp-BOilP6p2.js → regexp-Bzi1hKAb.js} +2 -2
- package/dist/client/assets/{rst-BfN2oPCh.js → rst-qWyAhT77.js} +2 -2
- package/dist/client/assets/{ruby-B1jLvIIs.js → ruby-LCEp4zKA.js} +2 -2
- package/dist/client/assets/{sas-CF03ALyS.js → sas-BFhxcd3b.js} +2 -2
- package/dist/client/assets/{scss-BrFzdYUJ.js → scss-BK0-84uz.js} +2 -2
- package/dist/client/assets/{shellscript-B_6ownRV.js → shellscript-CyZVCH9y.js} +2 -2
- package/dist/client/assets/{shellsession-D9grZ02W.js → shellsession-BMyVGgke.js} +2 -2
- package/dist/client/assets/{soy-Ca44AVp-.js → soy-C5EqpU05.js} +2 -2
- package/dist/client/assets/{sql-CkHELsvP.js → sql-D51Kht_s.js} +2 -2
- package/dist/client/assets/{stata-C9ALGlol.js → stata-ClgcFk9O.js} +2 -2
- package/dist/client/assets/{surrealql-Cta1X2o7.js → surrealql-DcFIgIgS.js} +2 -2
- package/dist/client/assets/{svelte-BogBuioE.js → svelte-5uir_iV_.js} +2 -2
- package/dist/client/assets/{templ-C4v7sOsC.js → templ-IHhNUhu5.js} +2 -2
- package/dist/client/assets/{tex-KXY3LBOg.js → tex-DbjEQBr6.js} +2 -2
- package/dist/client/assets/textarea-C6kz4NLj.js +46 -0
- package/dist/client/assets/{ts-tags-BUzdov0d.js → ts-tags-xML8Swir.js} +2 -2
- package/dist/client/assets/{tsx-DCEZNbvU.js → tsx-BwMUW3gO.js} +2 -2
- package/dist/client/assets/{twig-lX5bLK2U.js → twig-BRlCEyLn.js} +2 -2
- package/dist/client/assets/{typescript-D0XyCzdv.js → typescript-DC1qlCSF.js} +2 -2
- package/dist/client/assets/{vue-BVUKhxXR.js → vue-Cta_ijHF.js} +2 -2
- package/dist/client/assets/{vue-html-BFZ9Hu9Q.js → vue-html-C6cjjUjR.js} +2 -2
- package/dist/client/assets/{vue-vine-DwK9p9S2.js → vue-vine-DMiSqDlT.js} +2 -2
- package/dist/client/assets/{xml-BZnktucc.js → xml-DJpzo_Kg.js} +2 -2
- package/dist/client/assets/{xsl-BQvXVv3K.js → xsl-DAqFroi4.js} +2 -2
- package/dist/client/assets/{yaml-CjmvH72q.js → yaml-Brab7tCG.js} +2 -2
- package/dist/client/index.html +3 -3
- package/package.json +1 -1
- package/dist/client/assets/PreviewPanel-DtVNEw5j.js +0 -2
- package/dist/client/assets/arrow-right-BG7cUHWK.js +0 -2
- package/dist/client/assets/index-C0FwegCm.css +0 -1
- package/dist/client/assets/index-CNTBEOVF.js +0 -640
- package/dist/client/assets/textarea-DHdA8g53.js +0 -46
package/dist/bin.mjs
CHANGED
|
@@ -55,6 +55,7 @@ import * as Exit from "effect/Exit";
|
|
|
55
55
|
import * as SchemaGetter from "effect/SchemaGetter";
|
|
56
56
|
import * as Config from "effect/Config";
|
|
57
57
|
import * as NodeReadline from "node:readline";
|
|
58
|
+
import * as Clock from "effect/Clock";
|
|
58
59
|
import * as Cache from "effect/Cache";
|
|
59
60
|
import * as Deferred from "effect/Deferred";
|
|
60
61
|
import * as Equal from "effect/Equal";
|
|
@@ -62,7 +63,6 @@ import * as Semaphore from "effect/Semaphore";
|
|
|
62
63
|
import * as Scope from "effect/Scope";
|
|
63
64
|
import { parse } from "yaml";
|
|
64
65
|
import * as HttpApiClient from "effect/unstable/httpapi/HttpApiClient";
|
|
65
|
-
import * as Clock from "effect/Clock";
|
|
66
66
|
import * as Metric from "effect/Metric";
|
|
67
67
|
import * as Queue from "effect/Queue";
|
|
68
68
|
import { cast, dual } from "effect/Function";
|
|
@@ -121,7 +121,7 @@ const closeServer = (server) => {
|
|
|
121
121
|
* NetService - Service tag for startup networking helpers.
|
|
122
122
|
*/
|
|
123
123
|
var NetService = class extends Context.Service()("@p4code/shared/Net/NetService") {};
|
|
124
|
-
const make$
|
|
124
|
+
const make$72 = () => {
|
|
125
125
|
/**
|
|
126
126
|
* Returns true when a TCP server can bind to {host, port}.
|
|
127
127
|
* `EADDRNOTAVAIL` is treated as available so IPv6-absent hosts don't fail
|
|
@@ -230,10 +230,10 @@ const make$71 = () => {
|
|
|
230
230
|
})
|
|
231
231
|
};
|
|
232
232
|
};
|
|
233
|
-
const layer$
|
|
233
|
+
const layer$68 = Layer.sync(NetService, make$72);
|
|
234
234
|
//#endregion
|
|
235
235
|
//#region package.json
|
|
236
|
-
var version = "0.0.
|
|
236
|
+
var version = "0.0.51";
|
|
237
237
|
//#endregion
|
|
238
238
|
//#region src/config.ts
|
|
239
239
|
/**
|
|
@@ -254,8 +254,8 @@ var ServerConfig$1 = class extends Context.Service()("@p4code/cli/config/ServerC
|
|
|
254
254
|
/** @deprecated Import and use `layerTest` from this module. */
|
|
255
255
|
static layerTest = (cwd, baseDirOrPrefix) => layerTest$2(cwd, baseDirOrPrefix);
|
|
256
256
|
};
|
|
257
|
-
const make$
|
|
258
|
-
const layer$
|
|
257
|
+
const make$71 = (config) => ServerConfig$1.of(config);
|
|
258
|
+
const layer$67 = (config) => Layer.succeed(ServerConfig$1, make$71(config));
|
|
259
259
|
const deriveServerPaths = Effect.fn(function* (baseDir, devUrl, options = {}) {
|
|
260
260
|
const { join } = yield* Path.Path;
|
|
261
261
|
const stateDir = join(baseDir, devUrl !== void 0 && !options.baseDirIsExplicit ? "dev" : "userdata");
|
|
@@ -869,7 +869,7 @@ function compareSemverVersions(left, right) {
|
|
|
869
869
|
* timeout, every failure swallowed, and never anything that changes an exit
|
|
870
870
|
* code. A version check that can break a command is worse than no check.
|
|
871
871
|
*/
|
|
872
|
-
const PACKAGE_NAME = "@p4code/cli";
|
|
872
|
+
const PACKAGE_NAME$1 = "@p4code/cli";
|
|
873
873
|
const CHECK_INTERVAL = Duration.hours(24);
|
|
874
874
|
const REQUEST_TIMEOUT = Duration.seconds(3);
|
|
875
875
|
/**
|
|
@@ -889,7 +889,7 @@ const encodeCacheEntry = Schema$1.encodeEffect(CacheEntryJson);
|
|
|
889
889
|
const NpmLatest = Schema$1.Struct({ version: Schema$1.optional(Schema$1.String) });
|
|
890
890
|
const fetchLatestVersion = Effect.fn("cli.updateCheck.fetch")(function* () {
|
|
891
891
|
const client = yield* HttpClient.HttpClient;
|
|
892
|
-
const request = HttpClientRequest.get(`https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`).pipe(HttpClientRequest.setHeader("accept", "application/json"));
|
|
892
|
+
const request = HttpClientRequest.get(`https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME$1)}/latest`).pipe(HttpClientRequest.setHeader("accept", "application/json"));
|
|
893
893
|
const response = yield* client.execute(request).pipe(Effect.timeout(REQUEST_TIMEOUT));
|
|
894
894
|
if (response.status < 200 || response.status >= 300) return null;
|
|
895
895
|
const version = (yield* response.json.pipe(Effect.flatMap(Schema$1.decodeUnknownEffect(NpmLatest)))).version?.trim();
|
|
@@ -923,7 +923,7 @@ const resolveLatestVersion = Effect.fn("cli.updateCheck.resolve")(function* (sta
|
|
|
923
923
|
* `service update` restarts it onto the new one.
|
|
924
924
|
*/
|
|
925
925
|
function formatUpdateReminder(input) {
|
|
926
|
-
return [`Update available: ${PACKAGE_NAME}@${input.latestVersion} (running ${input.currentVersion}).`, ` npm i -g ${PACKAGE_NAME}@latest && p4c service update`].join("\n");
|
|
926
|
+
return [`Update available: ${PACKAGE_NAME$1}@${input.latestVersion} (running ${input.currentVersion}).`, ` npm i -g ${PACKAGE_NAME$1}@latest && p4c service update`].join("\n");
|
|
927
927
|
}
|
|
928
928
|
/**
|
|
929
929
|
* The reminder line, or null when there is nothing to say. Callers print it;
|
|
@@ -4819,6 +4819,87 @@ const ThreadMetadataUpdatedPayload = Schema$1.Struct({
|
|
|
4819
4819
|
name: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
4820
4820
|
metadata: Schema$1.optional(UnknownRecordSchema)
|
|
4821
4821
|
});
|
|
4822
|
+
/**
|
|
4823
|
+
* Per-source attribution for what is occupying the context window, as reported
|
|
4824
|
+
* by a provider that can answer the question. Only the Claude adapter fills this
|
|
4825
|
+
* in today; every field is optional so a provider can report the parts it knows.
|
|
4826
|
+
*
|
|
4827
|
+
* `isDeferred` / `isLoaded` are the interesting half. Providers defer most tool
|
|
4828
|
+
* definitions behind tool search, so a deferred entry's tokens are what it
|
|
4829
|
+
* *would* cost rather than what it costs right now, and presenting the two as
|
|
4830
|
+
* one number overstates usage.
|
|
4831
|
+
*/
|
|
4832
|
+
const ContextWindowNamedCost = Schema$1.Struct({
|
|
4833
|
+
name: TrimmedNonEmptyStringSchema$1,
|
|
4834
|
+
tokens: NonNegativeInt
|
|
4835
|
+
});
|
|
4836
|
+
const ContextWindowDeferrableTool = Schema$1.Struct({
|
|
4837
|
+
name: TrimmedNonEmptyStringSchema$1,
|
|
4838
|
+
tokens: NonNegativeInt,
|
|
4839
|
+
isLoaded: Schema$1.optional(Schema$1.Boolean)
|
|
4840
|
+
});
|
|
4841
|
+
const ContextWindowBreakdown = Schema$1.Struct({
|
|
4842
|
+
/** The model the breakdown was measured against, which is not always the thread's current selection. */
|
|
4843
|
+
model: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
4844
|
+
/** Top-level buckets the provider itself divides the window into. */
|
|
4845
|
+
categories: Schema$1.optional(Schema$1.Array(Schema$1.Struct({
|
|
4846
|
+
name: TrimmedNonEmptyStringSchema$1,
|
|
4847
|
+
tokens: NonNegativeInt,
|
|
4848
|
+
isDeferred: Schema$1.optional(Schema$1.Boolean)
|
|
4849
|
+
}))),
|
|
4850
|
+
mcpTools: Schema$1.optional(Schema$1.Array(Schema$1.Struct({
|
|
4851
|
+
name: TrimmedNonEmptyStringSchema$1,
|
|
4852
|
+
serverName: TrimmedNonEmptyStringSchema$1,
|
|
4853
|
+
tokens: NonNegativeInt,
|
|
4854
|
+
isLoaded: Schema$1.optional(Schema$1.Boolean)
|
|
4855
|
+
}))),
|
|
4856
|
+
skills: Schema$1.optional(Schema$1.Struct({
|
|
4857
|
+
totalSkills: NonNegativeInt,
|
|
4858
|
+
includedSkills: NonNegativeInt,
|
|
4859
|
+
tokens: NonNegativeInt,
|
|
4860
|
+
entries: Schema$1.optional(Schema$1.Array(Schema$1.Struct({
|
|
4861
|
+
name: TrimmedNonEmptyStringSchema$1,
|
|
4862
|
+
source: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
4863
|
+
tokens: NonNegativeInt
|
|
4864
|
+
})))
|
|
4865
|
+
})),
|
|
4866
|
+
memoryFiles: Schema$1.optional(Schema$1.Array(Schema$1.Struct({
|
|
4867
|
+
path: TrimmedNonEmptyStringSchema$1,
|
|
4868
|
+
type: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
4869
|
+
tokens: NonNegativeInt
|
|
4870
|
+
}))),
|
|
4871
|
+
agents: Schema$1.optional(Schema$1.Array(Schema$1.Struct({
|
|
4872
|
+
agentType: TrimmedNonEmptyStringSchema$1,
|
|
4873
|
+
source: Schema$1.optional(TrimmedNonEmptyStringSchema$1),
|
|
4874
|
+
tokens: NonNegativeInt
|
|
4875
|
+
}))),
|
|
4876
|
+
systemTools: Schema$1.optional(Schema$1.Array(ContextWindowNamedCost)),
|
|
4877
|
+
deferredBuiltinTools: Schema$1.optional(Schema$1.Array(ContextWindowDeferrableTool)),
|
|
4878
|
+
systemPromptSections: Schema$1.optional(Schema$1.Array(ContextWindowNamedCost)),
|
|
4879
|
+
slashCommands: Schema$1.optional(Schema$1.Struct({
|
|
4880
|
+
totalCommands: NonNegativeInt,
|
|
4881
|
+
includedCommands: NonNegativeInt,
|
|
4882
|
+
tokens: NonNegativeInt
|
|
4883
|
+
})),
|
|
4884
|
+
/** How the conversation itself divides up, as opposed to the prompt scaffolding above. */
|
|
4885
|
+
messages: Schema$1.optional(Schema$1.Struct({
|
|
4886
|
+
toolCallTokens: NonNegativeInt,
|
|
4887
|
+
toolResultTokens: NonNegativeInt,
|
|
4888
|
+
attachmentTokens: NonNegativeInt,
|
|
4889
|
+
assistantMessageTokens: NonNegativeInt,
|
|
4890
|
+
userMessageTokens: NonNegativeInt,
|
|
4891
|
+
redirectedContextTokens: NonNegativeInt,
|
|
4892
|
+
unattributedTokens: NonNegativeInt,
|
|
4893
|
+
toolCallsByType: Schema$1.optional(Schema$1.Array(Schema$1.Struct({
|
|
4894
|
+
name: TrimmedNonEmptyStringSchema$1,
|
|
4895
|
+
callTokens: NonNegativeInt,
|
|
4896
|
+
resultTokens: NonNegativeInt
|
|
4897
|
+
}))),
|
|
4898
|
+
attachmentsByType: Schema$1.optional(Schema$1.Array(ContextWindowNamedCost))
|
|
4899
|
+
})),
|
|
4900
|
+
/** Token count at which the provider compacts on its own, when it does. */
|
|
4901
|
+
autoCompactThreshold: Schema$1.optional(NonNegativeInt)
|
|
4902
|
+
});
|
|
4822
4903
|
const ThreadTokenUsageSnapshot = Schema$1.Struct({
|
|
4823
4904
|
usedTokens: NonNegativeInt,
|
|
4824
4905
|
totalProcessedTokens: Schema$1.optional(NonNegativeInt),
|
|
@@ -4834,7 +4915,8 @@ const ThreadTokenUsageSnapshot = Schema$1.Struct({
|
|
|
4834
4915
|
lastReasoningOutputTokens: Schema$1.optional(NonNegativeInt),
|
|
4835
4916
|
toolUses: Schema$1.optional(NonNegativeInt),
|
|
4836
4917
|
durationMs: Schema$1.optional(NonNegativeInt),
|
|
4837
|
-
compactsAutomatically: Schema$1.optional(Schema$1.Boolean)
|
|
4918
|
+
compactsAutomatically: Schema$1.optional(Schema$1.Boolean),
|
|
4919
|
+
breakdown: Schema$1.optional(ContextWindowBreakdown)
|
|
4838
4920
|
});
|
|
4839
4921
|
const ThreadTokenUsageUpdatedPayload = Schema$1.Struct({ usage: ThreadTokenUsageSnapshot });
|
|
4840
4922
|
const ThreadRealtimeStartedPayload = Schema$1.Struct({ realtimeSessionId: Schema$1.optional(TrimmedNonEmptyStringSchema$1) });
|
|
@@ -7235,6 +7317,59 @@ const TaskStreamEvent = Schema$1.Union([
|
|
|
7235
7317
|
taskId: TaskId
|
|
7236
7318
|
})
|
|
7237
7319
|
]);
|
|
7320
|
+
/**
|
|
7321
|
+
* Start an agent on a task.
|
|
7322
|
+
*
|
|
7323
|
+
* The input is only the task, deliberately. Everything else the thread needs —
|
|
7324
|
+
* which project, which model, what prompt — is resolved server-side from the
|
|
7325
|
+
* task's own project, because the caller that matters next is not a person
|
|
7326
|
+
* clicking a button but Phase 12's orchestrator, and a client that supplies the
|
|
7327
|
+
* model is a client that can start a task on a provider the user does not use.
|
|
7328
|
+
*
|
|
7329
|
+
* Worktrees are absent for a harder reason: `prepareWorktree` needs a base
|
|
7330
|
+
* branch, a task carries none, and nothing resolves one server-side. A thread
|
|
7331
|
+
* started from a task therefore runs in the project's own checkout.
|
|
7332
|
+
*/
|
|
7333
|
+
const TaskStartThreadInput = Schema$1.Struct({ taskId: TaskIdField });
|
|
7334
|
+
/**
|
|
7335
|
+
* The started thread and the task as it now stands.
|
|
7336
|
+
*
|
|
7337
|
+
* Both are returned because both changed: the caller navigates to the thread,
|
|
7338
|
+
* and the board would otherwise show the old status until the change event
|
|
7339
|
+
* arrived.
|
|
7340
|
+
*/
|
|
7341
|
+
const TaskStartThreadResult = Schema$1.Struct({
|
|
7342
|
+
task: Task,
|
|
7343
|
+
threadId: ThreadId
|
|
7344
|
+
});
|
|
7345
|
+
/**
|
|
7346
|
+
* A task cannot be started, and the reason is a property of the task or the
|
|
7347
|
+
* server rather than a failure.
|
|
7348
|
+
*
|
|
7349
|
+
* Each reason is a different thing for the user to fix, which is why this is a
|
|
7350
|
+
* union and not one message. `no-model` is the one worth explaining: a task
|
|
7351
|
+
* carries no model selection, so the thread runs on the project's default. The
|
|
7352
|
+
* server-side fallback that exists for first-run bootstrap is deliberately not
|
|
7353
|
+
* used here — it hardcodes a provider, so reaching for it would silently start
|
|
7354
|
+
* someone who works in Claude on Codex. Failing is the honest answer.
|
|
7355
|
+
*/
|
|
7356
|
+
const TaskNotStartableReason = Schema$1.Literals([
|
|
7357
|
+
"no-project",
|
|
7358
|
+
"project-missing",
|
|
7359
|
+
"no-model"
|
|
7360
|
+
]);
|
|
7361
|
+
var TaskNotStartableError = class extends Schema$1.TaggedErrorClass()("TaskNotStartableError", {
|
|
7362
|
+
taskId: TaskId,
|
|
7363
|
+
reason: TaskNotStartableReason
|
|
7364
|
+
}) {
|
|
7365
|
+
get message() {
|
|
7366
|
+
switch (this.reason) {
|
|
7367
|
+
case "no-project": return "This task has no project, so there is nowhere to run it. Set a project on the task first.";
|
|
7368
|
+
case "project-missing": return "This task's project is not registered on this server. Point it at a project that is.";
|
|
7369
|
+
case "no-model": return "This task's project has no default model. Choose one in the project's settings, then start the task again.";
|
|
7370
|
+
}
|
|
7371
|
+
}
|
|
7372
|
+
};
|
|
7238
7373
|
/** Result shape for the board's list RPC. */
|
|
7239
7374
|
const TaskListResult$1 = Schema$1.Struct({ tasks: Schema$1.Array(Task) });
|
|
7240
7375
|
/** Result shape for every RPC that resolves to a single task. */
|
|
@@ -8004,6 +8139,7 @@ const WS_METHODS = {
|
|
|
8004
8139
|
tasksCreate: "tasks.create",
|
|
8005
8140
|
tasksUpdate: "tasks.update",
|
|
8006
8141
|
tasksDelete: "tasks.delete",
|
|
8142
|
+
tasksStartThread: "tasks.startThread",
|
|
8007
8143
|
hubGetSyncStatus: "hub.getSyncStatus",
|
|
8008
8144
|
hubConnect: "hub.connect",
|
|
8009
8145
|
hubDisconnect: "hub.disconnect",
|
|
@@ -8078,6 +8214,30 @@ const WsTasksDeleteRpc = Rpc.make(WS_METHODS.tasksDelete, {
|
|
|
8078
8214
|
error: TaskRpcError
|
|
8079
8215
|
});
|
|
8080
8216
|
/**
|
|
8217
|
+
* Start an agent on a task.
|
|
8218
|
+
*
|
|
8219
|
+
* This is the one task RPC that is not CRUD: it creates a thread, seeds it from
|
|
8220
|
+
* the task, and links the two. It lives in the `tasks.*` namespace because the
|
|
8221
|
+
* task is what it reads and writes — the thread is the side effect, not the
|
|
8222
|
+
* subject — and because the caller always names a task and never a thread.
|
|
8223
|
+
*
|
|
8224
|
+
* The error union is wider than the other task methods for the same reason. A
|
|
8225
|
+
* start can fail because the task is not startable, which is a property of the
|
|
8226
|
+
* row, or because the turn could not be dispatched, which is the orchestration
|
|
8227
|
+
* engine's own failure and already has a type.
|
|
8228
|
+
*/
|
|
8229
|
+
const WsTasksStartThreadRpc = Rpc.make(WS_METHODS.tasksStartThread, {
|
|
8230
|
+
payload: TaskStartThreadInput,
|
|
8231
|
+
success: TaskStartThreadResult,
|
|
8232
|
+
error: Schema$1.Union([
|
|
8233
|
+
TaskNotFoundError,
|
|
8234
|
+
TaskNotStartableError,
|
|
8235
|
+
TaskStoreError,
|
|
8236
|
+
OrchestrationDispatchCommandError,
|
|
8237
|
+
EnvironmentAuthorizationError
|
|
8238
|
+
])
|
|
8239
|
+
});
|
|
8240
|
+
/**
|
|
8081
8241
|
* Hub link and skill sync.
|
|
8082
8242
|
*
|
|
8083
8243
|
* Every one of these answers with the whole status rather than an
|
|
@@ -8590,7 +8750,7 @@ const WsSubscribeAuthAccessRpc = Rpc.make(WS_METHODS.subscribeAuthAccess, {
|
|
|
8590
8750
|
error: Schema$1.Union([AuthAccessStreamError, EnvironmentAuthorizationError]),
|
|
8591
8751
|
stream: true
|
|
8592
8752
|
});
|
|
8593
|
-
const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
8753
|
+
const WsRpcGroup = RpcGroup.make(WsServerProbeRpc, WsServerGetConfigRpc, WsServerRefreshProvidersRpc, WsServerUpdateProviderRpc, WsServerUpdateServerRpc, WsServerUpsertKeybindingRpc, WsServerRemoveKeybindingRpc, WsServerGetSettingsRpc, WsServerUpdateSettingsRpc, WsServerDiscoverSourceControlRpc, WsServerGetTraceDiagnosticsRpc, WsServerGetProcessDiagnosticsRpc, WsServerGetProcessResourceHistoryRpc, WsServerSignalProcessRpc, WsSourceControlLookupRepositoryRpc, WsSourceControlCloneRepositoryRpc, WsSourceControlPublishRepositoryRpc, WsProjectsListEntriesRpc, WsProjectsReadFileRpc, WsProjectsSearchEntriesRpc, WsProjectsWriteFileRpc, WsShellOpenInEditorRpc, WsFilesystemBrowseRpc, WsAssetsCreateUrlRpc, WsSubscribeVcsStatusRpc, WsVcsPullRpc, WsVcsRefreshStatusRpc, WsGitRunStackedActionRpc, WsGitResolvePullRequestRpc, WsGitPreparePullRequestThreadRpc, WsVcsListRefsRpc, WsVcsCreateWorktreeRpc, WsVcsRemoveWorktreeRpc, WsVcsCreateRefRpc, WsVcsSwitchRefRpc, WsVcsInitRpc, WsReviewGetDiffPreviewRpc, WsTerminalOpenRpc, WsTerminalAttachRpc, WsTerminalWriteRpc, WsTerminalResizeRpc, WsTerminalClearRpc, WsTerminalRestartRpc, WsTerminalCloseRpc, WsSubscribeTerminalEventsRpc, WsSubscribeTerminalMetadataRpc, WsPreviewOpenRpc, WsPreviewNavigateRpc, WsPreviewResizeRpc, WsPreviewRefreshRpc, WsPreviewCloseRpc, WsPreviewListRpc, WsPreviewReportStatusRpc, WsPreviewAutomationConnectRpc, WsPreviewAutomationRespondRpc, WsPreviewAutomationFocusHostRpc, WsSubscribePreviewEventsRpc, WsSubscribeDiscoveredLocalServersRpc, WsSubscribeServerConfigRpc, WsSubscribeServerLifecycleRpc, WsSubscribeAuthAccessRpc, WsTasksListRpc, WsTasksGetRpc, WsTasksCreateRpc, WsTasksUpdateRpc, WsTasksDeleteRpc, WsTasksStartThreadRpc, WsSubscribeTasksRpc, WsHubGetSyncStatusRpc, WsHubConnectRpc, WsHubDisconnectRpc, WsHubSetSyncModeRpc, WsHubSetShareModeRpc, WsHubMintTokenRpc, WsSkillsSyncRpc, WsSkillsPublishRpc, WsSkillsPublishAllRpc, WsSkillsUnpublishRpc, WsAssetsReadRpc, WsAssetsSaveRpc, WsAssetsDeleteRpc, WsMcpListRpc, WsMcpSaveRpc, WsMcpRemoveRpc, WsMcpSetSecretRpc, WsOrchestrationDispatchCommandRpc, WsOrchestrationGetTurnDiffRpc, WsOrchestrationGetFullThreadDiffRpc, WsOrchestrationGetArchivedShellSnapshotRpc, WsOrchestrationSubscribeShellRpc, WsOrchestrationSubscribeThreadRpc);
|
|
8594
8754
|
//#endregion
|
|
8595
8755
|
//#region ../../packages/shared/src/oauthScope.ts
|
|
8596
8756
|
const OAUTH_SCOPE_TOKEN = /^[\u0021\u0023-\u005b\u005d-\u007e]+$/u;
|
|
@@ -8736,7 +8896,7 @@ function deriveAuthClientMetadata(input) {
|
|
|
8736
8896
|
//#endregion
|
|
8737
8897
|
//#region src/auth/EnvironmentAuthPolicy.ts
|
|
8738
8898
|
var EnvironmentAuthPolicy = class extends Context.Service()("@p4code/cli/auth/EnvironmentAuthPolicy") {};
|
|
8739
|
-
const make$
|
|
8899
|
+
const make$70 = Effect.gen(function* () {
|
|
8740
8900
|
const config = yield* ServerConfig$1;
|
|
8741
8901
|
const isRemoteReachable = isRemoteReachableHost(config.host);
|
|
8742
8902
|
const policy = config.mode === "desktop" ? isRemoteReachable ? "remote-reachable" : "desktop-managed-local" : isRemoteReachable ? "remote-reachable" : "loopback-browser";
|
|
@@ -8754,7 +8914,7 @@ const make$69 = Effect.gen(function* () {
|
|
|
8754
8914
|
};
|
|
8755
8915
|
return EnvironmentAuthPolicy.of({ getDescriptor: () => Effect.succeed(descriptor).pipe(Effect.withSpan("EnvironmentAuthPolicy.getDescriptor")) });
|
|
8756
8916
|
});
|
|
8757
|
-
const layer$
|
|
8917
|
+
const layer$66 = Layer.effect(EnvironmentAuthPolicy, make$70);
|
|
8758
8918
|
//#endregion
|
|
8759
8919
|
//#region src/persistence/Errors.ts
|
|
8760
8920
|
function summarizeSchemaIssue(issue) {
|
|
@@ -8935,7 +9095,7 @@ function toPersistenceSqlOrDecodeError$6(sqlOperation, decodeOperation, correlat
|
|
|
8935
9095
|
cause
|
|
8936
9096
|
});
|
|
8937
9097
|
}
|
|
8938
|
-
const make$
|
|
9098
|
+
const make$69 = Effect.gen(function* () {
|
|
8939
9099
|
const sql = yield* SqlClient.SqlClient;
|
|
8940
9100
|
const createSessionRow = SqlSchema.void({
|
|
8941
9101
|
Request: CreateAuthSessionInput,
|
|
@@ -9069,7 +9229,7 @@ const make$68 = Effect.gen(function* () {
|
|
|
9069
9229
|
setLastConnectedAt
|
|
9070
9230
|
};
|
|
9071
9231
|
});
|
|
9072
|
-
const layer$
|
|
9232
|
+
const layer$65 = Layer.effect(AuthSessionRepository, make$69);
|
|
9073
9233
|
//#endregion
|
|
9074
9234
|
//#region src/auth/ServerSecretStore.ts
|
|
9075
9235
|
const secretStoreErrorContext = {
|
|
@@ -9136,7 +9296,7 @@ const isSecretStoreError = Schema$1.is(SecretStoreError);
|
|
|
9136
9296
|
const isPlatformError = (value) => Predicate.isTagged(value, "PlatformError");
|
|
9137
9297
|
const isSecretAlreadyExistsError = (error) => "cause" in error && isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists";
|
|
9138
9298
|
var ServerSecretStore = class extends Context.Service()("@p4code/cli/auth/ServerSecretStore") {};
|
|
9139
|
-
const make$
|
|
9299
|
+
const make$68 = Effect.gen(function* () {
|
|
9140
9300
|
const crypto = yield* Crypto.Crypto;
|
|
9141
9301
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
9142
9302
|
const path = yield* Path.Path;
|
|
@@ -9206,7 +9366,7 @@ const make$67 = Effect.gen(function* () {
|
|
|
9206
9366
|
remove
|
|
9207
9367
|
});
|
|
9208
9368
|
});
|
|
9209
|
-
const layer$
|
|
9369
|
+
const layer$64 = Layer.effect(ServerSecretStore, make$68);
|
|
9210
9370
|
//#endregion
|
|
9211
9371
|
//#region src/auth/SessionStore.ts
|
|
9212
9372
|
var MalformedSessionTokenError = class extends Schema$1.TaggedErrorClass()("MalformedSessionTokenError", {}) {
|
|
@@ -9444,7 +9604,7 @@ function toAuthClientSession(input) {
|
|
|
9444
9604
|
current: false
|
|
9445
9605
|
};
|
|
9446
9606
|
}
|
|
9447
|
-
const make$
|
|
9607
|
+
const make$67 = Effect.gen(function* () {
|
|
9448
9608
|
const crypto = yield* Crypto.Crypto;
|
|
9449
9609
|
const serverConfig = yield* ServerConfig$1;
|
|
9450
9610
|
const secretStore = yield* ServerSecretStore;
|
|
@@ -9758,7 +9918,7 @@ const make$66 = Effect.gen(function* () {
|
|
|
9758
9918
|
markDisconnected
|
|
9759
9919
|
});
|
|
9760
9920
|
});
|
|
9761
|
-
const layer$
|
|
9921
|
+
const layer$63 = Layer.effect(SessionStore, make$67).pipe(Layer.provideMerge(layer$65));
|
|
9762
9922
|
//#endregion
|
|
9763
9923
|
//#region src/persistence/AuthPairingLinks.ts
|
|
9764
9924
|
const AuthPairingLinkRecord = Schema$1.Struct({
|
|
@@ -9819,7 +9979,7 @@ function toPersistenceSqlOrDecodeError$5(sqlOperation, decodeOperation, correlat
|
|
|
9819
9979
|
cause
|
|
9820
9980
|
});
|
|
9821
9981
|
}
|
|
9822
|
-
const make$
|
|
9982
|
+
const make$66 = Effect.gen(function* () {
|
|
9823
9983
|
const sql = yield* SqlClient.SqlClient;
|
|
9824
9984
|
const createPairingLinkRow = SqlSchema.void({
|
|
9825
9985
|
Request: CreateAuthPairingLinkInput,
|
|
@@ -9954,7 +10114,7 @@ const make$65 = Effect.gen(function* () {
|
|
|
9954
10114
|
getByCredential
|
|
9955
10115
|
};
|
|
9956
10116
|
});
|
|
9957
|
-
const layer$
|
|
10117
|
+
const layer$62 = Layer.effect(AuthPairingLinkRepository, make$66);
|
|
9958
10118
|
//#endregion
|
|
9959
10119
|
//#region src/auth/PairingGrantStore.ts
|
|
9960
10120
|
var UnknownBootstrapCredentialError = class extends Schema$1.TaggedErrorClass()("UnknownBootstrapCredentialError", {}) {
|
|
@@ -10049,7 +10209,7 @@ const DEV_STARTUP_TTL_HOURS = Duration.hours(24);
|
|
|
10049
10209
|
const PAIRING_TOKEN_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
10050
10210
|
const PAIRING_TOKEN_LENGTH = 12;
|
|
10051
10211
|
const PAIRING_TOKEN_REJECTION_LIMIT = Math.floor(256 / 32) * 32;
|
|
10052
|
-
const make$
|
|
10212
|
+
const make$65 = Effect.gen(function* () {
|
|
10053
10213
|
const crypto = yield* Crypto.Crypto;
|
|
10054
10214
|
const config = yield* ServerConfig$1;
|
|
10055
10215
|
const pairingLinks = yield* AuthPairingLinkRepository;
|
|
@@ -10247,7 +10407,7 @@ const make$64 = Effect.gen(function* () {
|
|
|
10247
10407
|
consume
|
|
10248
10408
|
});
|
|
10249
10409
|
});
|
|
10250
|
-
const layer$
|
|
10410
|
+
const layer$61 = Layer.effect(PairingGrantStore, make$65).pipe(Layer.provideMerge(layer$62));
|
|
10251
10411
|
//#endregion
|
|
10252
10412
|
//#region src/persistence/Migrations/001_OrchestrationEvents.ts
|
|
10253
10413
|
var _001_OrchestrationEvents_default = Effect.gen(function* () {
|
|
@@ -12020,7 +12180,7 @@ function parseBearerToken(request) {
|
|
|
12020
12180
|
const token = header.slice(7).trim();
|
|
12021
12181
|
return token.length > 0 ? token : null;
|
|
12022
12182
|
}
|
|
12023
|
-
const make$
|
|
12183
|
+
const make$64 = Effect.gen(function* () {
|
|
12024
12184
|
const policy = yield* EnvironmentAuthPolicy;
|
|
12025
12185
|
const bootstrapCredentials = yield* PairingGrantStore;
|
|
12026
12186
|
const sessions = yield* SessionStore;
|
|
@@ -12215,9 +12375,9 @@ const make$63 = Effect.gen(function* () {
|
|
|
12215
12375
|
issueStartupPairingUrl
|
|
12216
12376
|
});
|
|
12217
12377
|
});
|
|
12218
|
-
const layer$
|
|
12219
|
-
const storageLayer = Layer.mergeAll(layer$
|
|
12220
|
-
const runtimeLayer = layer$
|
|
12378
|
+
const layer$60 = Layer.effect(EnvironmentAuth, make$64).pipe(Layer.provideMerge(layer$61), Layer.provideMerge(layer$63), Layer.provideMerge(layer$66));
|
|
12379
|
+
const storageLayer = Layer.mergeAll(layer$64, layerConfig);
|
|
12380
|
+
const runtimeLayer = layer$60.pipe(Layer.provideMerge(storageLayer));
|
|
12221
12381
|
//#endregion
|
|
12222
12382
|
//#region src/cliAuthFormat.ts
|
|
12223
12383
|
const newline = "\n";
|
|
@@ -12972,7 +13132,7 @@ const runWithEnvironmentAuth = (flags, run, options) => Effect.gen(function* ()
|
|
|
12972
13132
|
}
|
|
12973
13133
|
return yield* Effect.gen(function* () {
|
|
12974
13134
|
return yield* run(yield* EnvironmentAuth);
|
|
12975
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$
|
|
13135
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer).pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
12976
13136
|
});
|
|
12977
13137
|
const ttlFlag = Flag.string("ttl").pipe(Flag.withSchema(DurationFromString), Flag.withDescription("TTL, for example `5m`, `1h`, `30d`, or `15 minutes`."), Flag.optional);
|
|
12978
13138
|
const jsonFlag = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON instead of human-readable output."), Flag.withDefault(false));
|
|
@@ -13069,6 +13229,64 @@ const authCommand = Command.make("auth").pipe(Command.withDescription("Manage th
|
|
|
13069
13229
|
* @module HubConfig
|
|
13070
13230
|
*/
|
|
13071
13231
|
var HubConfig = class extends Context.Service()("@p4code/cli/hub/HubConfig") {};
|
|
13232
|
+
const FAILURE_WINDOW_MS = 6e4;
|
|
13233
|
+
const BLOCK_DURATION_MS = 6e4;
|
|
13234
|
+
const initialThrottleState = {
|
|
13235
|
+
windowStartMillis: 0,
|
|
13236
|
+
failures: 0,
|
|
13237
|
+
blockedUntilMillis: 0
|
|
13238
|
+
};
|
|
13239
|
+
/** Whether a *failing* request should be refused outright rather than checked. */
|
|
13240
|
+
const isBlocked = (state, nowMillis) => nowMillis < state.blockedUntilMillis;
|
|
13241
|
+
/** Seconds a caller should wait, for `retry-after`. Always at least one. */
|
|
13242
|
+
const retryAfterSeconds = (state, nowMillis) => Math.max(1, Math.ceil((state.blockedUntilMillis - nowMillis) / 1e3));
|
|
13243
|
+
/**
|
|
13244
|
+
* Count a failure, and start blocking once the window fills.
|
|
13245
|
+
*
|
|
13246
|
+
* The window is fixed rather than sliding: a sliding window needs the timestamp
|
|
13247
|
+
* of every attempt, which is unbounded memory for an unbounded attacker. A
|
|
13248
|
+
* fixed window lets a burst straddle a boundary and land up to twice the limit,
|
|
13249
|
+
* which for this purpose is a rounding error against the size of the search
|
|
13250
|
+
* space.
|
|
13251
|
+
*/
|
|
13252
|
+
const recordFailure = (state, nowMillis, limits = {
|
|
13253
|
+
maxFailures: 10,
|
|
13254
|
+
windowMillis: FAILURE_WINDOW_MS,
|
|
13255
|
+
blockMillis: BLOCK_DURATION_MS
|
|
13256
|
+
}) => {
|
|
13257
|
+
if (nowMillis - state.windowStartMillis >= limits.windowMillis) return {
|
|
13258
|
+
windowStartMillis: nowMillis,
|
|
13259
|
+
failures: 1,
|
|
13260
|
+
blockedUntilMillis: 0
|
|
13261
|
+
};
|
|
13262
|
+
const failures = state.failures + 1;
|
|
13263
|
+
return {
|
|
13264
|
+
windowStartMillis: state.windowStartMillis,
|
|
13265
|
+
failures,
|
|
13266
|
+
blockedUntilMillis: failures >= limits.maxFailures ? nowMillis + limits.blockMillis : state.blockedUntilMillis
|
|
13267
|
+
};
|
|
13268
|
+
};
|
|
13269
|
+
var HubAuthThrottle = class extends Context.Service()("@p4code/cli/hub/HubAuthThrottle") {};
|
|
13270
|
+
const DEFAULT_LIMITS = {
|
|
13271
|
+
maxFailures: 10,
|
|
13272
|
+
windowMillis: FAILURE_WINDOW_MS,
|
|
13273
|
+
blockMillis: BLOCK_DURATION_MS
|
|
13274
|
+
};
|
|
13275
|
+
const make$63 = Effect.fn("HubAuthThrottle.make")(function* (limits = DEFAULT_LIMITS) {
|
|
13276
|
+
const state = yield* Ref.make(initialThrottleState);
|
|
13277
|
+
return HubAuthThrottle.of({
|
|
13278
|
+
shouldRefuse: Effect.gen(function* () {
|
|
13279
|
+
const now = yield* Clock.currentTimeMillis;
|
|
13280
|
+
return isBlocked(yield* Ref.get(state), now);
|
|
13281
|
+
}),
|
|
13282
|
+
recordFailure: Effect.gen(function* () {
|
|
13283
|
+
const now = yield* Clock.currentTimeMillis;
|
|
13284
|
+
const next = yield* Ref.updateAndGet(state, (current) => recordFailure(current, now, limits));
|
|
13285
|
+
return retryAfterSeconds(next, now);
|
|
13286
|
+
})
|
|
13287
|
+
});
|
|
13288
|
+
});
|
|
13289
|
+
const layer$59 = Layer.effect(HubAuthThrottle, make$63());
|
|
13072
13290
|
//#endregion
|
|
13073
13291
|
//#region src/hub/HubAuth.ts
|
|
13074
13292
|
/**
|
|
@@ -13127,6 +13345,31 @@ var HubUnauthorizedError = class {
|
|
|
13127
13345
|
_tag = "HubUnauthorizedError";
|
|
13128
13346
|
};
|
|
13129
13347
|
/**
|
|
13348
|
+
* Too many failed attempts recently; this one was not even checked.
|
|
13349
|
+
*
|
|
13350
|
+
* Distinct from `HubUnauthorizedError` so the response can differ. A 429 is the
|
|
13351
|
+
* honest answer — the credential was not evaluated, so calling it invalid would
|
|
13352
|
+
* be a claim the hub did not make — and it tells a legitimate server retrying a
|
|
13353
|
+
* stale token to back off rather than hammer.
|
|
13354
|
+
*/
|
|
13355
|
+
var HubThrottledError = class {
|
|
13356
|
+
_tag = "HubThrottledError";
|
|
13357
|
+
retryAfterSeconds;
|
|
13358
|
+
constructor(retryAfterSeconds) {
|
|
13359
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
13360
|
+
}
|
|
13361
|
+
};
|
|
13362
|
+
const hubThrottledResponse = (retryAfterSeconds) => HttpServerResponse.jsonUnsafe({
|
|
13363
|
+
error: "too_many_hub_credential_failures",
|
|
13364
|
+
message: "Too many failed credential attempts. Try again shortly."
|
|
13365
|
+
}, {
|
|
13366
|
+
status: 429,
|
|
13367
|
+
headers: {
|
|
13368
|
+
"cache-control": "no-store",
|
|
13369
|
+
"retry-after": String(retryAfterSeconds)
|
|
13370
|
+
}
|
|
13371
|
+
});
|
|
13372
|
+
/**
|
|
13130
13373
|
* Fails with {@link HubUnauthorizedError} unless the request carries a
|
|
13131
13374
|
* configured bearer token. Route handlers run this first and let
|
|
13132
13375
|
* `respondToHubAuthFailure` turn the failure into a 401.
|
|
@@ -13134,11 +13377,17 @@ var HubUnauthorizedError = class {
|
|
|
13134
13377
|
const authenticateHubRequest = Effect.fn("HubAuth.authenticateHubRequest")(function* () {
|
|
13135
13378
|
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
13136
13379
|
const { tokenHashes } = yield* HubConfig;
|
|
13380
|
+
const throttle = yield* HubAuthThrottle;
|
|
13137
13381
|
const token = readBearerToken(request.headers.authorization);
|
|
13138
|
-
if (
|
|
13139
|
-
|
|
13140
|
-
|
|
13382
|
+
if (isAuthorizedHubToken(token, tokenHashes)) return;
|
|
13383
|
+
if (yield* throttle.shouldRefuse) {
|
|
13384
|
+
const retryAfter = yield* throttle.recordFailure;
|
|
13385
|
+
yield* Effect.logWarning("refused hub request without checking; too many recent failures", { reason: token.length === 0 ? "missing_bearer_token" : "unknown_token" });
|
|
13386
|
+
return yield* Effect.fail(new HubThrottledError(retryAfter));
|
|
13141
13387
|
}
|
|
13388
|
+
yield* throttle.recordFailure;
|
|
13389
|
+
yield* Effect.logWarning("rejected hub request with an unusable credential", { reason: token.length === 0 ? "missing_bearer_token" : "unknown_token" });
|
|
13390
|
+
return yield* Effect.fail(new HubUnauthorizedError());
|
|
13142
13391
|
});
|
|
13143
13392
|
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
13144
13393
|
/**
|
|
@@ -13547,6 +13796,7 @@ const storeUnavailable = HttpServerResponse.jsonUnsafe({
|
|
|
13547
13796
|
*/
|
|
13548
13797
|
const respondToHubFailures = (handler) => handler.pipe(Effect.catch((cause) => {
|
|
13549
13798
|
if (cause._tag === "HubUnauthorizedError") return Effect.succeed(hubUnauthorizedResponse);
|
|
13799
|
+
if (cause._tag === "HubThrottledError") return Effect.succeed(hubThrottledResponse(cause.retryAfterSeconds));
|
|
13550
13800
|
if (cause._tag === "InvalidHubRequest") return Effect.succeed(badRequest(cause.detail));
|
|
13551
13801
|
return Effect.logWarning("hub request failed", { cause }).pipe(Effect.as(storeUnavailable));
|
|
13552
13802
|
}));
|
|
@@ -13942,7 +14192,7 @@ const announce = Layer.effectDiscard(Effect.gen(function* () {
|
|
|
13942
14192
|
});
|
|
13943
14193
|
if (isRemoteReachableHost(config.host)) yield* Effect.logWarning("Hub is reachable beyond loopback. The bearer token is the only control — repeated auth failures are not rate limited.", { host: config.host });
|
|
13944
14194
|
}));
|
|
13945
|
-
const makeHubLayer = HttpRouter.serve(layer$58).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
|
|
14195
|
+
const makeHubLayer = HttpRouter.serve(layer$58).pipe(Layer.provideMerge(announce), Layer.provideMerge(HubHttpServerLive), Layer.provideMerge(layer$59), Layer.provideMerge(TaskRepositoryLive), Layer.provideMerge(AgentAssetRepositoryLive), Layer.provideMerge(HubPersistenceLive));
|
|
13946
14196
|
const runHub = Layer.launch(makeHubLayer);
|
|
13947
14197
|
//#endregion
|
|
13948
14198
|
//#region src/atomicWrite.ts
|
|
@@ -16126,7 +16376,7 @@ const make$58 = Effect.gen(function* () {
|
|
|
16126
16376
|
const layer$53 = Layer.effect(AssetSync, make$58);
|
|
16127
16377
|
//#endregion
|
|
16128
16378
|
//#region src/cli/assets.ts
|
|
16129
|
-
const SyncServicesLive = layer$53.pipe(Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$
|
|
16379
|
+
const SyncServicesLive = layer$53.pipe(Layer.provideMerge(layer$54), Layer.provideMerge(layer$55), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$64))), Layer.provideMerge(layer$64), Layer.provideMerge(FetchHttpClient.layer));
|
|
16130
16380
|
const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
16131
16381
|
const config = yield* resolveCliAuthConfig(flags, yield* GlobalFlag.LogLevel);
|
|
16132
16382
|
return yield* Effect.gen(function* () {
|
|
@@ -16134,7 +16384,7 @@ const runWithSync = (flags, run) => Effect.gen(function* () {
|
|
|
16134
16384
|
sync: yield* AssetSync,
|
|
16135
16385
|
link: yield* HubLink
|
|
16136
16386
|
});
|
|
16137
|
-
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$
|
|
16387
|
+
}).pipe(Effect.provide(SyncServicesLive.pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, "Error")))));
|
|
16138
16388
|
});
|
|
16139
16389
|
const STATE_LABEL = {
|
|
16140
16390
|
synced: "synced",
|
|
@@ -24582,7 +24832,7 @@ const RESTART_DELAY = Duration.seconds(2);
|
|
|
24582
24832
|
/** Exact npm versions only — never dist-tags — so the acknowledgement names
|
|
24583
24833
|
the version that was actually installed. Also keeps the value safe to
|
|
24584
24834
|
pass to npm and embed in filesystem paths. */
|
|
24585
|
-
const EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
24835
|
+
const EXACT_VERSION_PATTERN$1 = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
24586
24836
|
function normalizeEntryPath(entryPath) {
|
|
24587
24837
|
return entryPath.replaceAll("\\", "/");
|
|
24588
24838
|
}
|
|
@@ -24698,7 +24948,7 @@ const make$50 = Effect.fn("cloud.server_self_update.make")(function* (options) {
|
|
|
24698
24948
|
if (capability === null) return yield* failWith("This server cannot update itself; relaunch it manually with the new version.");
|
|
24699
24949
|
const activeMethod = capability;
|
|
24700
24950
|
const targetVersion = input.targetVersion.trim();
|
|
24701
|
-
if (!EXACT_VERSION_PATTERN.test(targetVersion)) return yield* failWith(`'${targetVersion}' is not an exact p4 version.`);
|
|
24951
|
+
if (!EXACT_VERSION_PATTERN$1.test(targetVersion)) return yield* failWith(`'${targetVersion}' is not an exact p4 version.`);
|
|
24702
24952
|
if (yield* Ref.getAndSet(inFlight, true)) return yield* failWith("A server update is already in progress.");
|
|
24703
24953
|
return yield* Effect.gen(function* () {
|
|
24704
24954
|
const runtimePaths = yield* ensurePinnedRuntimeInstalled({
|
|
@@ -24727,6 +24977,7 @@ const make$50 = Effect.fn("cloud.server_self_update.make")(function* (options) {
|
|
|
24727
24977
|
const homeDir = env.HOME ?? "";
|
|
24728
24978
|
const unitPath = path.join(homeDir, ".config", "systemd", "user", BOOT_SERVICE_UNIT_FILE);
|
|
24729
24979
|
const previousUnit = yield* fs.readFileString(unitPath).pipe(Effect.mapError((cause) => failWith("Could not read the current systemd unit.", cause)));
|
|
24980
|
+
const serviceOptions = yield* readServiceOptions({ baseDir: serverConfig.baseDir }).pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path));
|
|
24730
24981
|
const unit = renderBootServiceUnit({
|
|
24731
24982
|
nodePath: host.execPath,
|
|
24732
24983
|
p4EntryPath: runtimePaths.entryPath,
|
|
@@ -24734,7 +24985,7 @@ const make$50 = Effect.fn("cloud.server_self_update.make")(function* (options) {
|
|
|
24734
24985
|
logPath: path.join(serverConfig.logsDir, "boot-service.log"),
|
|
24735
24986
|
unitPath,
|
|
24736
24987
|
workingDirectory: homeDir,
|
|
24737
|
-
serviceEnvironment:
|
|
24988
|
+
serviceEnvironment: serviceEnvironmentFor(serviceOptions)
|
|
24738
24989
|
});
|
|
24739
24990
|
yield* writeUnitAtomically(unitPath, unit).pipe(Effect.mapError((cause) => failWith("Could not update the systemd unit.", cause)));
|
|
24740
24991
|
const reloadSystemd = Effect.fn("cloud.server_self_update.reload_systemd")(function* () {
|
|
@@ -26603,7 +26854,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
26603
26854
|
});
|
|
26604
26855
|
yield* Console.log(output);
|
|
26605
26856
|
}));
|
|
26606
|
-
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$
|
|
26857
|
+
const offlineRuntimeLayer = ProjectCliRuntimeLive.pipe(Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)));
|
|
26607
26858
|
return yield* Effect.gen(function* () {
|
|
26608
26859
|
const snapshot = yield* getOfflineSnapshot();
|
|
26609
26860
|
const orchestrationEngine = yield* OrchestrationEngineService;
|
|
@@ -26614,7 +26865,7 @@ const runProjectMutation = Effect.fn("runProjectMutation")(function* (flags, run
|
|
|
26614
26865
|
});
|
|
26615
26866
|
yield* Console.log(output);
|
|
26616
26867
|
}).pipe(Effect.provide(offlineRuntimeLayer));
|
|
26617
|
-
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$42).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$
|
|
26868
|
+
}).pipe(Effect.provide(Layer.mergeAll(runtimeLayer, layer$42).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provide(layer$67(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, minimumLogLevel)))));
|
|
26618
26869
|
});
|
|
26619
26870
|
const projectAddCommand = Command.make("add", {
|
|
26620
26871
|
...projectLocationFlags,
|
|
@@ -30873,11 +31124,25 @@ function isResolvableContextWindowActivity(activity) {
|
|
|
30873
31124
|
* newer updates still stream through and supersede the retained rows on the
|
|
30874
31125
|
* client.
|
|
30875
31126
|
*/
|
|
31127
|
+
function withoutContextWindowBreakdown$1(activity) {
|
|
31128
|
+
const payload = asRecord$1(activity.payload);
|
|
31129
|
+
if (!payload || payload.breakdown === void 0) return activity;
|
|
31130
|
+
const { breakdown: _breakdown, ...rest } = payload;
|
|
31131
|
+
return {
|
|
31132
|
+
...activity,
|
|
31133
|
+
payload: rest
|
|
31134
|
+
};
|
|
31135
|
+
}
|
|
30876
31136
|
function dropStaleContextWindowActivities(activities) {
|
|
30877
31137
|
const latestIndexByTurn = /* @__PURE__ */ new Map();
|
|
30878
31138
|
for (let index = 0; index < activities.length; index += 1) if (isResolvableContextWindowActivity(activities[index])) latestIndexByTurn.set(activities[index].turnId, index);
|
|
30879
31139
|
if (latestIndexByTurn.size === 0) return activities;
|
|
30880
|
-
|
|
31140
|
+
const newestIndex = Math.max(...latestIndexByTurn.values());
|
|
31141
|
+
return activities.flatMap((activity, index) => {
|
|
31142
|
+
if (!isResolvableContextWindowActivity(activity)) return [activity];
|
|
31143
|
+
if (latestIndexByTurn.get(activity.turnId) !== index) return [];
|
|
31144
|
+
return [index === newestIndex ? activity : withoutContextWindowBreakdown$1(activity)];
|
|
31145
|
+
});
|
|
30881
31146
|
}
|
|
30882
31147
|
function projectThreadDetailSnapshot(snapshot) {
|
|
30883
31148
|
return {
|
|
@@ -31025,6 +31290,39 @@ const resolveTaskRepositoryKey = Effect.fn("taskBoard.resolveRepositoryKey")(fun
|
|
|
31025
31290
|
return (yield* (yield* RepositoryIdentityResolver).resolve(project.value.workspaceRoot))?.canonicalKey ?? null;
|
|
31026
31291
|
});
|
|
31027
31292
|
/**
|
|
31293
|
+
* Everything that decides whether a task can be started, as one pure function.
|
|
31294
|
+
*
|
|
31295
|
+
* Kept out of the RPC handler because the three refusals are the whole of the
|
|
31296
|
+
* interesting behaviour and each is a different thing for the user to fix,
|
|
31297
|
+
* while the handler around them is two store calls and a dispatch. Phase 12's
|
|
31298
|
+
* orchestrator has to make the same three decisions before it may claim a task,
|
|
31299
|
+
* and it should reach the same verdicts by calling this rather than by reading
|
|
31300
|
+
* the handler and reimplementing it.
|
|
31301
|
+
*
|
|
31302
|
+
* The project is passed in as the repository returns it — including soft-deleted
|
|
31303
|
+
* rows, which is why `deletedAt` is checked here rather than assumed away.
|
|
31304
|
+
*/
|
|
31305
|
+
const resolveTaskStart = (task, project) => {
|
|
31306
|
+
if (task.projectId === null) return {
|
|
31307
|
+
startable: false,
|
|
31308
|
+
reason: "no-project"
|
|
31309
|
+
};
|
|
31310
|
+
if (Option.isNone(project) || project.value.deletedAt !== null) return {
|
|
31311
|
+
startable: false,
|
|
31312
|
+
reason: "project-missing"
|
|
31313
|
+
};
|
|
31314
|
+
const model = project.value.defaultModelSelection;
|
|
31315
|
+
if (model === null) return {
|
|
31316
|
+
startable: false,
|
|
31317
|
+
reason: "no-model"
|
|
31318
|
+
};
|
|
31319
|
+
return {
|
|
31320
|
+
startable: true,
|
|
31321
|
+
projectId: task.projectId,
|
|
31322
|
+
model
|
|
31323
|
+
};
|
|
31324
|
+
};
|
|
31325
|
+
/**
|
|
31028
31326
|
* Map a persistence failure to the caller-facing error.
|
|
31029
31327
|
*
|
|
31030
31328
|
* The underlying error carries SQL detail, which is noise to a client and, for
|
|
@@ -31036,6 +31334,54 @@ const toTaskStoreError = (operation) => (cause) => Effect.logWarning("task store
|
|
|
31036
31334
|
cause
|
|
31037
31335
|
}).pipe(Effect.andThen(Effect.fail(new TaskStoreError({ operation }))));
|
|
31038
31336
|
//#endregion
|
|
31337
|
+
//#region ../../packages/shared/src/taskExecution.ts
|
|
31338
|
+
/**
|
|
31339
|
+
* The `auto` rendering: here is the task, do it.
|
|
31340
|
+
*
|
|
31341
|
+
* Two instructions carry their weight. The agent is told its thread is already
|
|
31342
|
+
* linked to the task, because otherwise its first useful move is to guess which
|
|
31343
|
+
* board row it is on. And it is told to leave `status` alone: the server moved
|
|
31344
|
+
* the task to `in_progress` when it started the thread, and where the work goes
|
|
31345
|
+
* after that is the user's call — the same rule the scoping prompt follows, and
|
|
31346
|
+
* the reason the board's columns still mean something.
|
|
31347
|
+
*/
|
|
31348
|
+
const buildAutoPrompt = (task) => {
|
|
31349
|
+
const body = task.body.trim();
|
|
31350
|
+
return `You are picking up a task from the board. Do the work.
|
|
31351
|
+
|
|
31352
|
+
## ${task.title}
|
|
31353
|
+
|
|
31354
|
+
${body.length === 0 ? "_The task has no description. Read enough of the repository to work out what it means, and ask if it is genuinely ambiguous._" : body}
|
|
31355
|
+
|
|
31356
|
+
This thread is linked to task \`${task.taskId}\`, which is now In progress. \`task_current\` re-reads it if you need the fields again.
|
|
31357
|
+
|
|
31358
|
+
Record what you find with \`task_update\` as you go — a decision you made, a constraint you hit, something the task turned out to be wrong about. That is what makes the row worth reading afterwards. Leave \`status\` alone: moving work across the board is the user's call.
|
|
31359
|
+
|
|
31360
|
+
Start by reading the code the task concerns. If it turns out to be specified too poorly to act on, say so and stop rather than guessing at what was meant.`;
|
|
31361
|
+
};
|
|
31362
|
+
/**
|
|
31363
|
+
* Render a task into the prompt that seeds its thread.
|
|
31364
|
+
*
|
|
31365
|
+
* Exhaustive over the mode on purpose: adding a member to [TaskExecutionMode]
|
|
31366
|
+
* without a rendering should fail to compile rather than fall through to `auto`.
|
|
31367
|
+
*/
|
|
31368
|
+
const buildTaskExecutionPrompt = (input) => {
|
|
31369
|
+
switch (input.mode) {
|
|
31370
|
+
case "auto": return buildAutoPrompt(input.task);
|
|
31371
|
+
}
|
|
31372
|
+
};
|
|
31373
|
+
const TASK_THREAD_TITLE_MAX_LENGTH = 60;
|
|
31374
|
+
/**
|
|
31375
|
+
* The thread's title, which is the task's own — a thread started from a task is
|
|
31376
|
+
* that task, and a derived-from-the-prompt title would read as a sentence of
|
|
31377
|
+
* instructions in the sidebar.
|
|
31378
|
+
*/
|
|
31379
|
+
const buildTaskThreadTitle = (title) => {
|
|
31380
|
+
const compact = title.trim().replace(/\s+/gu, " ");
|
|
31381
|
+
if (compact.length === 0) return "Task";
|
|
31382
|
+
return compact.length > TASK_THREAD_TITLE_MAX_LENGTH ? `${compact.slice(0, TASK_THREAD_TITLE_MAX_LENGTH - 1).trimEnd()}…` : compact;
|
|
31383
|
+
};
|
|
31384
|
+
//#endregion
|
|
31039
31385
|
//#region src/observability/RpcInstrumentation.ts
|
|
31040
31386
|
const RPC_SPAN_PREFIX = "ws.rpc";
|
|
31041
31387
|
const DEFAULT_RPC_SPAN_ATTRIBUTES = {
|
|
@@ -40762,6 +41108,7 @@ const RPC_REQUIRED_SCOPE = /* @__PURE__ */ new Map([
|
|
|
40762
41108
|
[WS_METHODS.tasksCreate, AuthOrchestrationOperateScope],
|
|
40763
41109
|
[WS_METHODS.tasksUpdate, AuthOrchestrationOperateScope],
|
|
40764
41110
|
[WS_METHODS.tasksDelete, AuthOrchestrationOperateScope],
|
|
41111
|
+
[WS_METHODS.tasksStartThread, AuthOrchestrationOperateScope],
|
|
40765
41112
|
[WS_METHODS.subscribeTasks, AuthOrchestrationReadScope],
|
|
40766
41113
|
[WS_METHODS.hubGetSyncStatus, AuthOrchestrationReadScope],
|
|
40767
41114
|
[WS_METHODS.hubConnect, AuthOrchestrationOperateScope],
|
|
@@ -40874,7 +41221,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
|
|
|
40874
41221
|
const checkpointDiffQuery = yield* CheckpointDiffQuery;
|
|
40875
41222
|
const keybindings = yield* Keybindings;
|
|
40876
41223
|
const taskRepository = yield* TaskRepository;
|
|
40877
|
-
yield* ProjectionProjectRepository;
|
|
41224
|
+
const projectionProjects = yield* ProjectionProjectRepository;
|
|
40878
41225
|
const externalLauncher = yield* ExternalLauncher;
|
|
40879
41226
|
const gitWorkflow = yield* GitWorkflowService;
|
|
40880
41227
|
const review = yield* ReviewService;
|
|
@@ -41352,6 +41699,64 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker) => WsRpcGroup.t
|
|
|
41352
41699
|
return { task: patched.value };
|
|
41353
41700
|
}), { "rpc.aggregate": "tasks" }),
|
|
41354
41701
|
[WS_METHODS.tasksDelete]: ({ taskId }) => observeRpcEffect$1(WS_METHODS.tasksDelete, taskRepository.deleteById({ taskId }).pipe(Effect.catch(toTaskStoreError("tasks.delete")), Effect.as({})), { "rpc.aggregate": "tasks" }),
|
|
41702
|
+
[WS_METHODS.tasksStartThread]: ({ taskId }) => observeRpcEffect$1(WS_METHODS.tasksStartThread, Effect.gen(function* () {
|
|
41703
|
+
const found = yield* taskRepository.getById({ taskId }).pipe(Effect.catch(toTaskStoreError("tasks.startThread")));
|
|
41704
|
+
if (Option.isNone(found)) return yield* new TaskNotFoundError({ taskId });
|
|
41705
|
+
const task = found.value;
|
|
41706
|
+
const plan = resolveTaskStart(task, task.projectId === null ? Option.none() : yield* projectionProjects.getById({ projectId: task.projectId }).pipe(Effect.catch(toTaskStoreError("tasks.startThread.project"))));
|
|
41707
|
+
if (!plan.startable) return yield* new TaskNotStartableError({
|
|
41708
|
+
taskId,
|
|
41709
|
+
reason: plan.reason
|
|
41710
|
+
});
|
|
41711
|
+
const modelSelection = plan.model;
|
|
41712
|
+
const threadId = ThreadId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
|
|
41713
|
+
const createdAt = yield* nowIso$5;
|
|
41714
|
+
const threadTitle = buildTaskThreadTitle(task.title);
|
|
41715
|
+
const normalizedCommand = yield* normalizeDispatchCommand({
|
|
41716
|
+
type: "thread.turn.start",
|
|
41717
|
+
commandId: yield* serverCommandId("task-start-thread"),
|
|
41718
|
+
threadId,
|
|
41719
|
+
message: {
|
|
41720
|
+
messageId: MessageId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie)),
|
|
41721
|
+
role: "user",
|
|
41722
|
+
text: buildTaskExecutionPrompt({
|
|
41723
|
+
mode: "auto",
|
|
41724
|
+
task: {
|
|
41725
|
+
taskId,
|
|
41726
|
+
title: task.title,
|
|
41727
|
+
body: task.body
|
|
41728
|
+
}
|
|
41729
|
+
}),
|
|
41730
|
+
attachments: []
|
|
41731
|
+
},
|
|
41732
|
+
modelSelection,
|
|
41733
|
+
titleSeed: threadTitle,
|
|
41734
|
+
runtimeMode: DEFAULT_RUNTIME_MODE$2,
|
|
41735
|
+
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
|
|
41736
|
+
bootstrap: { createThread: {
|
|
41737
|
+
projectId: plan.projectId,
|
|
41738
|
+
title: threadTitle,
|
|
41739
|
+
modelSelection,
|
|
41740
|
+
runtimeMode: DEFAULT_RUNTIME_MODE$2,
|
|
41741
|
+
interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
|
|
41742
|
+
branch: null,
|
|
41743
|
+
worktreePath: null,
|
|
41744
|
+
createdAt
|
|
41745
|
+
} },
|
|
41746
|
+
createdAt
|
|
41747
|
+
});
|
|
41748
|
+
yield* dispatchNormalizedCommand(normalizedCommand);
|
|
41749
|
+
const patched = yield* taskRepository.patch({
|
|
41750
|
+
taskId,
|
|
41751
|
+
threadId,
|
|
41752
|
+
status: "in_progress"
|
|
41753
|
+
}, yield* nowIso$5).pipe(Effect.catch(toTaskStoreError("tasks.startThread.link")));
|
|
41754
|
+
if (Option.isNone(patched)) return yield* new TaskNotFoundError({ taskId });
|
|
41755
|
+
return {
|
|
41756
|
+
task: patched.value,
|
|
41757
|
+
threadId
|
|
41758
|
+
};
|
|
41759
|
+
}), { "rpc.aggregate": "tasks" }),
|
|
41355
41760
|
[WS_METHODS.subscribeTasks]: (_input) => observeRpcStreamEffect$1(WS_METHODS.subscribeTasks, Effect.succeed(taskRepository.streamWithSnapshot.pipe(Stream.catch((cause) => Stream.fromEffect(toTaskStoreError("tasks.subscribe")(cause))))), { "rpc.aggregate": "tasks" }),
|
|
41356
41761
|
[WS_METHODS.hubGetSyncStatus]: (_input) => observeRpcEffect$1(WS_METHODS.hubGetSyncStatus, assetSync.status, { "rpc.aggregate": "hub" }),
|
|
41357
41762
|
[WS_METHODS.hubConnect]: (input) => observeRpcEffect$1(WS_METHODS.hubConnect, hubLink.connect(input).pipe(Effect.mapError((cause) => new HubLinkError({ detail: cause.message })), Effect.andThen(assetSync.status)), { "rpc.aggregate": "hub" }),
|
|
@@ -43610,7 +44015,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () {
|
|
|
43610
44015
|
};
|
|
43611
44016
|
});
|
|
43612
44017
|
var OpenCodeRuntime = class extends Context.Service()("@p4code/cli/provider/opencodeRuntime") {};
|
|
43613
|
-
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$
|
|
44018
|
+
const OpenCodeRuntimeLive = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe(Layer.provide(layer$68));
|
|
43614
44019
|
//#endregion
|
|
43615
44020
|
//#region src/textGeneration/TextGenerationUtils.ts
|
|
43616
44021
|
const isTextGenerationError$2 = Schema$1.is(TextGenerationError);
|
|
@@ -45149,13 +45554,178 @@ function normalizeClaudeActiveTokenUsage(value, contextWindow, totalProcessedTok
|
|
|
45149
45554
|
...totalProcessedTokens !== void 0 ? { totalProcessedTokens } : {}
|
|
45150
45555
|
});
|
|
45151
45556
|
}
|
|
45557
|
+
/**
|
|
45558
|
+
* The contract's string fields are non-empty, and this payload is decoded on the
|
|
45559
|
+
* way out, so an entry the SDK reports with a blank name would fail the whole
|
|
45560
|
+
* event rather than lose one row. Drop those instead.
|
|
45561
|
+
*/
|
|
45562
|
+
function trimmedName(value) {
|
|
45563
|
+
if (typeof value !== "string") return;
|
|
45564
|
+
const trimmed = value.trim();
|
|
45565
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
45566
|
+
}
|
|
45567
|
+
function mapNamedCosts(entries) {
|
|
45568
|
+
if (!entries || entries.length === 0) return;
|
|
45569
|
+
const mapped = entries.flatMap((entry) => {
|
|
45570
|
+
const name = trimmedName(entry.name);
|
|
45571
|
+
const tokens = finiteNonNegativeInteger(entry.tokens);
|
|
45572
|
+
return name !== void 0 && tokens !== void 0 ? [{
|
|
45573
|
+
name,
|
|
45574
|
+
tokens
|
|
45575
|
+
}] : [];
|
|
45576
|
+
});
|
|
45577
|
+
return mapped.length > 0 ? mapped : void 0;
|
|
45578
|
+
}
|
|
45579
|
+
/**
|
|
45580
|
+
* Keeps the per-source attribution the SDK already computes on every turn. The
|
|
45581
|
+
* `gridRows` and `color` fields are dropped: they describe how the vendor CLI
|
|
45582
|
+
* paints its own meter, and this UI has its own.
|
|
45583
|
+
*/
|
|
45584
|
+
function claudeContextWindowBreakdown(value) {
|
|
45585
|
+
const model = trimmedName(value.model);
|
|
45586
|
+
const categories = value.categories.flatMap((category) => {
|
|
45587
|
+
const name = trimmedName(category.name);
|
|
45588
|
+
const tokens = finiteNonNegativeInteger(category.tokens);
|
|
45589
|
+
if (name === void 0 || tokens === void 0) return [];
|
|
45590
|
+
return [{
|
|
45591
|
+
name,
|
|
45592
|
+
tokens,
|
|
45593
|
+
...category.isDeferred !== void 0 ? { isDeferred: category.isDeferred } : {}
|
|
45594
|
+
}];
|
|
45595
|
+
});
|
|
45596
|
+
const mcpTools = value.mcpTools.flatMap((tool) => {
|
|
45597
|
+
const name = trimmedName(tool.name);
|
|
45598
|
+
const serverName = trimmedName(tool.serverName);
|
|
45599
|
+
const tokens = finiteNonNegativeInteger(tool.tokens);
|
|
45600
|
+
if (name === void 0 || serverName === void 0 || tokens === void 0) return [];
|
|
45601
|
+
return [{
|
|
45602
|
+
name,
|
|
45603
|
+
serverName,
|
|
45604
|
+
tokens,
|
|
45605
|
+
...tool.isLoaded !== void 0 ? { isLoaded: tool.isLoaded } : {}
|
|
45606
|
+
}];
|
|
45607
|
+
});
|
|
45608
|
+
const memoryFiles = value.memoryFiles.flatMap((file) => {
|
|
45609
|
+
const path = trimmedName(file.path);
|
|
45610
|
+
const tokens = finiteNonNegativeInteger(file.tokens);
|
|
45611
|
+
if (path === void 0 || tokens === void 0) return [];
|
|
45612
|
+
const type = trimmedName(file.type);
|
|
45613
|
+
return [{
|
|
45614
|
+
path,
|
|
45615
|
+
tokens,
|
|
45616
|
+
...type !== void 0 ? { type } : {}
|
|
45617
|
+
}];
|
|
45618
|
+
});
|
|
45619
|
+
const agents = value.agents.flatMap((agent) => {
|
|
45620
|
+
const agentType = trimmedName(agent.agentType);
|
|
45621
|
+
const tokens = finiteNonNegativeInteger(agent.tokens);
|
|
45622
|
+
if (agentType === void 0 || tokens === void 0) return [];
|
|
45623
|
+
const source = trimmedName(agent.source);
|
|
45624
|
+
return [{
|
|
45625
|
+
agentType,
|
|
45626
|
+
tokens,
|
|
45627
|
+
...source !== void 0 ? { source } : {}
|
|
45628
|
+
}];
|
|
45629
|
+
});
|
|
45630
|
+
const deferredBuiltinTools = value.deferredBuiltinTools?.flatMap((tool) => {
|
|
45631
|
+
const name = trimmedName(tool.name);
|
|
45632
|
+
const tokens = finiteNonNegativeInteger(tool.tokens);
|
|
45633
|
+
return name !== void 0 && tokens !== void 0 ? [{
|
|
45634
|
+
name,
|
|
45635
|
+
tokens,
|
|
45636
|
+
isLoaded: tool.isLoaded
|
|
45637
|
+
}] : [];
|
|
45638
|
+
});
|
|
45639
|
+
const skillsSource = value.skills;
|
|
45640
|
+
const skillEntries = skillsSource?.skillFrontmatter.flatMap((skill) => {
|
|
45641
|
+
const name = trimmedName(skill.name);
|
|
45642
|
+
const tokens = finiteNonNegativeInteger(skill.tokens);
|
|
45643
|
+
if (name === void 0 || tokens === void 0) return [];
|
|
45644
|
+
const source = trimmedName(skill.source);
|
|
45645
|
+
return [{
|
|
45646
|
+
name,
|
|
45647
|
+
tokens,
|
|
45648
|
+
...source !== void 0 ? { source } : {}
|
|
45649
|
+
}];
|
|
45650
|
+
});
|
|
45651
|
+
const skills = skillsSource === void 0 ? void 0 : {
|
|
45652
|
+
totalSkills: finiteNonNegativeInteger(skillsSource.totalSkills) ?? 0,
|
|
45653
|
+
includedSkills: finiteNonNegativeInteger(skillsSource.includedSkills) ?? 0,
|
|
45654
|
+
tokens: finiteNonNegativeInteger(skillsSource.tokens) ?? 0,
|
|
45655
|
+
...skillEntries && skillEntries.length > 0 ? { entries: skillEntries } : {}
|
|
45656
|
+
};
|
|
45657
|
+
const slashCommandsSource = value.slashCommands;
|
|
45658
|
+
const slashCommands = slashCommandsSource === void 0 ? void 0 : {
|
|
45659
|
+
totalCommands: finiteNonNegativeInteger(slashCommandsSource.totalCommands) ?? 0,
|
|
45660
|
+
includedCommands: finiteNonNegativeInteger(slashCommandsSource.includedCommands) ?? 0,
|
|
45661
|
+
tokens: finiteNonNegativeInteger(slashCommandsSource.tokens) ?? 0
|
|
45662
|
+
};
|
|
45663
|
+
const messagesSource = value.messageBreakdown;
|
|
45664
|
+
const toolCallsByType = messagesSource?.toolCallsByType.flatMap((entry) => {
|
|
45665
|
+
const name = trimmedName(entry.name);
|
|
45666
|
+
const callTokens = finiteNonNegativeInteger(entry.callTokens);
|
|
45667
|
+
const resultTokens = finiteNonNegativeInteger(entry.resultTokens);
|
|
45668
|
+
return name !== void 0 && callTokens !== void 0 && resultTokens !== void 0 ? [{
|
|
45669
|
+
name,
|
|
45670
|
+
callTokens,
|
|
45671
|
+
resultTokens
|
|
45672
|
+
}] : [];
|
|
45673
|
+
});
|
|
45674
|
+
const attachmentsByType = mapNamedCosts(messagesSource?.attachmentsByType);
|
|
45675
|
+
const messages = messagesSource === void 0 ? void 0 : {
|
|
45676
|
+
toolCallTokens: finiteNonNegativeInteger(messagesSource.toolCallTokens) ?? 0,
|
|
45677
|
+
toolResultTokens: finiteNonNegativeInteger(messagesSource.toolResultTokens) ?? 0,
|
|
45678
|
+
attachmentTokens: finiteNonNegativeInteger(messagesSource.attachmentTokens) ?? 0,
|
|
45679
|
+
assistantMessageTokens: finiteNonNegativeInteger(messagesSource.assistantMessageTokens) ?? 0,
|
|
45680
|
+
userMessageTokens: finiteNonNegativeInteger(messagesSource.userMessageTokens) ?? 0,
|
|
45681
|
+
redirectedContextTokens: finiteNonNegativeInteger(messagesSource.redirectedContextTokens) ?? 0,
|
|
45682
|
+
unattributedTokens: finiteNonNegativeInteger(messagesSource.unattributedTokens) ?? 0,
|
|
45683
|
+
...toolCallsByType && toolCallsByType.length > 0 ? { toolCallsByType } : {},
|
|
45684
|
+
...attachmentsByType !== void 0 ? { attachmentsByType } : {}
|
|
45685
|
+
};
|
|
45686
|
+
const systemTools = mapNamedCosts(value.systemTools);
|
|
45687
|
+
const systemPromptSections = mapNamedCosts(value.systemPromptSections);
|
|
45688
|
+
const autoCompactThreshold = finiteNonNegativeInteger(value.autoCompactThreshold);
|
|
45689
|
+
const breakdown = {
|
|
45690
|
+
...model !== void 0 ? { model } : {},
|
|
45691
|
+
...categories.length > 0 ? { categories } : {},
|
|
45692
|
+
...mcpTools.length > 0 ? { mcpTools } : {},
|
|
45693
|
+
...skills !== void 0 ? { skills } : {},
|
|
45694
|
+
...memoryFiles.length > 0 ? { memoryFiles } : {},
|
|
45695
|
+
...agents.length > 0 ? { agents } : {},
|
|
45696
|
+
...systemTools !== void 0 ? { systemTools } : {},
|
|
45697
|
+
...deferredBuiltinTools && deferredBuiltinTools.length > 0 ? { deferredBuiltinTools } : {},
|
|
45698
|
+
...systemPromptSections !== void 0 ? { systemPromptSections } : {},
|
|
45699
|
+
...slashCommands !== void 0 ? { slashCommands } : {},
|
|
45700
|
+
...messages !== void 0 ? { messages } : {},
|
|
45701
|
+
...autoCompactThreshold !== void 0 ? { autoCompactThreshold } : {}
|
|
45702
|
+
};
|
|
45703
|
+
return Object.keys(breakdown).length > 0 ? breakdown : void 0;
|
|
45704
|
+
}
|
|
45705
|
+
/**
|
|
45706
|
+
* Drops the attribution from a snapshot that is being carried forward. The token
|
|
45707
|
+
* counts still describe the thread, but the breakdown was measured against a
|
|
45708
|
+
* turn that has since moved on. A stale breakdown presented as the current one
|
|
45709
|
+
* is worse than none, because nothing about it looks stale.
|
|
45710
|
+
*/
|
|
45711
|
+
function withoutContextWindowBreakdown(usage) {
|
|
45712
|
+
if (!usage || usage.breakdown === void 0) return usage;
|
|
45713
|
+
const { breakdown: _breakdown, ...rest } = usage;
|
|
45714
|
+
return rest;
|
|
45715
|
+
}
|
|
45152
45716
|
function normalizeClaudeContextUsageApiSnapshot(value, totalProcessedTokens) {
|
|
45153
|
-
|
|
45717
|
+
const snapshot = makeClaudeTokenUsageSnapshot({
|
|
45154
45718
|
activeTokens: value.totalTokens,
|
|
45155
45719
|
contextWindow: value.maxTokens,
|
|
45156
45720
|
...totalProcessedTokens !== void 0 ? { totalProcessedTokens } : {},
|
|
45157
45721
|
compactsAutomatically: value.isAutoCompactEnabled
|
|
45158
45722
|
});
|
|
45723
|
+
if (!snapshot) return;
|
|
45724
|
+
const breakdown = claudeContextWindowBreakdown(value);
|
|
45725
|
+
return breakdown !== void 0 ? {
|
|
45726
|
+
...snapshot,
|
|
45727
|
+
breakdown
|
|
45728
|
+
} : snapshot;
|
|
45159
45729
|
}
|
|
45160
45730
|
function compactBoundaryTokenUsageSnapshot(message, contextWindow, totalProcessedTokens) {
|
|
45161
45731
|
const metadata = message.compact_metadata;
|
|
@@ -45948,7 +46518,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
|
|
|
45948
46518
|
const resultHasActiveUsage = resultUsageRecord !== void 0 && (hasResultUsageIteration || claudeUsageInputTokens(resultUsageRecord) + claudeUsageOutputTokens(resultUsageRecord) > 0);
|
|
45949
46519
|
const resultTotalOnly = resultUsageRecord !== void 0 && !resultHasActiveUsage && claudeTotalProcessedTokens(resultUsageRecord) !== void 0;
|
|
45950
46520
|
const resultIterationSnapshot = resultUsageRecord ? normalizeClaudeActiveTokenUsage(resultUsageRecord, maxTokens, accumulatedTotalProcessedTokens ?? context.lastKnownTotalProcessedTokens) : void 0;
|
|
45951
|
-
const lastGoodUsage = context.lastKnownTokenUsage;
|
|
46521
|
+
const lastGoodUsage = withoutContextWindowBreakdown(context.lastKnownTokenUsage);
|
|
45952
46522
|
const usageSnapshot = contextUsageSnapshot ?? (resultTotalOnly && lastGoodUsage ? {
|
|
45953
46523
|
...lastGoodUsage,
|
|
45954
46524
|
...typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0 ? { maxTokens } : {},
|
|
@@ -82703,9 +83273,9 @@ const WorkspaceEntriesLayerLive = layer$26.pipe(Layer.provide(layer$42));
|
|
|
82703
83273
|
const WorkspaceFileSystemLayerLive = layer$25.pipe(Layer.provide(layer$42), Layer.provide(WorkspaceEntriesLayerLive));
|
|
82704
83274
|
const WorkspaceLayerLive = Layer.mergeAll(layer$42, WorkspaceEntriesLayerLive, WorkspaceFileSystemLayerLive);
|
|
82705
83275
|
const ProjectFaviconResolverLayerLive = layer$40.pipe(Layer.provide(layer$42), Layer.provide(layer$41));
|
|
82706
|
-
const AuthLayerLive = layer$
|
|
83276
|
+
const AuthLayerLive = layer$60.pipe(Layer.provideMerge(PersistenceLayerLive), Layer.provide(layer$64));
|
|
82707
83277
|
const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(Layer.provideMerge(ProviderLayerLive), Layer.provideMerge(OrchestrationLayerLive));
|
|
82708
|
-
const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$50), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$
|
|
83278
|
+
const RuntimeDependenciesLive = ReactorLayerLive.pipe(Layer.provideMerge(CheckpointingLayerLive), Layer.provideMerge(SourceControlProviderRegistryLayerLive), Layer.provideMerge(GitLayerLive), Layer.provideMerge(VcsLayerLive), Layer.provideMerge(ProviderRuntimeLayerLive), Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)), Layer.provideMerge(PersistenceLayerLive), Layer.provideMerge(layer$50), Layer.provideMerge(ProviderRegistryLive), Layer.provideMerge(ProviderInstanceRegistryHydrationLive), Layer.provideMerge(ProviderEventLoggersLive), Layer.provideMerge(OpenCodeRuntimeLive), Layer.provideMerge(layer$57.pipe(Layer.provide(layer$64))), Layer.provideMerge(WorkspaceLayerLive), Layer.provideMerge(ProjectFaviconResolverLayerLive), Layer.provideMerge(layer$51), Layer.provideMerge(layer$44), Layer.provideMerge(AuthLayerLive), Layer.provideMerge(layer$56), Layer.provideMerge(layer$64)).pipe(Layer.provideMerge(layer$8), Layer.provideMerge(layer$7), Layer.provideMerge(layer$6), Layer.provideMerge(layer$47), Layer.provideMerge(layer$49), Layer.provideMerge(layer$48), Layer.provide(layer$68));
|
|
82709
83279
|
/**
|
|
82710
83280
|
* Hub asset sync.
|
|
82711
83281
|
*
|
|
@@ -82773,6 +83343,151 @@ const serveCommand = Command.make("serve", { ...sharedServerCommandFlags }).pipe
|
|
|
82773
83343
|
forceAutoBootstrapProjectFromCwd: false
|
|
82774
83344
|
})));
|
|
82775
83345
|
//#endregion
|
|
83346
|
+
//#region src/service/cliSelfUpdate.ts
|
|
83347
|
+
/**
|
|
83348
|
+
* Upgrading the globally installed CLI, then the service that runs it.
|
|
83349
|
+
*
|
|
83350
|
+
* Two steps that have to happen in that order and from different binaries, and
|
|
83351
|
+
* getting the order wrong is silent: `service update` renders the unit from the
|
|
83352
|
+
* *running* CLI, and pins that version into the runtime directory. Run the
|
|
83353
|
+
* reconcile from the old process and you install a new CLI and then carefully
|
|
83354
|
+
* point the service back at the old one.
|
|
83355
|
+
*
|
|
83356
|
+
* So the second step re-executes the newly installed binary. That is also why
|
|
83357
|
+
* this is not `ServerSelfUpdate`: that path respawns the server in place, which
|
|
83358
|
+
* it explicitly refuses to do under launchd — KeepAlive would restart the
|
|
83359
|
+
* process being replaced and leave two servers on one port. Under a supervisor
|
|
83360
|
+
* the honest sequence is install, then let the supervisor restart, which is
|
|
83361
|
+
* exactly what `service update` does.
|
|
83362
|
+
*
|
|
83363
|
+
* @module service/cliSelfUpdate
|
|
83364
|
+
*/
|
|
83365
|
+
/** npm can take minutes when a native dependency compiles from source. */
|
|
83366
|
+
const CLI_INSTALL_TIMEOUT_MS = 600 * 1e3;
|
|
83367
|
+
const PACKAGE_NAME = "@p4code/cli";
|
|
83368
|
+
/**
|
|
83369
|
+
* Exact versions or the two dist-tags, and nothing else.
|
|
83370
|
+
*
|
|
83371
|
+
* The value is handed to npm and printed back as "installed X". An arbitrary
|
|
83372
|
+
* string could be a git URL or a local path, which npm would happily install
|
|
83373
|
+
* and which would make the acknowledgement a lie.
|
|
83374
|
+
*/
|
|
83375
|
+
const EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?$/u;
|
|
83376
|
+
const isInstallableVersion = (value) => value === "latest" || value === "next" || EXACT_VERSION_PATTERN.test(value);
|
|
83377
|
+
var CliSelfUpdateError = class extends Schema$1.TaggedErrorClass()("CliSelfUpdateError", {
|
|
83378
|
+
step: Schema$1.String,
|
|
83379
|
+
detail: Schema$1.optional(Schema$1.String)
|
|
83380
|
+
}) {
|
|
83381
|
+
get message() {
|
|
83382
|
+
return this.detail === void 0 ? `Self-update failed while ${this.step}.` : `Self-update failed while ${this.step}: ${this.detail}`;
|
|
83383
|
+
}
|
|
83384
|
+
};
|
|
83385
|
+
/**
|
|
83386
|
+
* Where `npm install --global` put the executable.
|
|
83387
|
+
*
|
|
83388
|
+
* Asked of npm rather than assumed, because the answer differs between a
|
|
83389
|
+
* Homebrew node, an nvm one and a system one — and the whole point of this step
|
|
83390
|
+
* is to run the binary that was just written, not the one on this process's
|
|
83391
|
+
* PATH, which a shell may have cached.
|
|
83392
|
+
*/
|
|
83393
|
+
const resolveGlobalBinary = Effect.fn("cli.selfUpdate.resolveGlobalBinary")(function* (binaryName) {
|
|
83394
|
+
const result = yield* (yield* ProcessRunner).run({
|
|
83395
|
+
command: "npm",
|
|
83396
|
+
args: ["prefix", "--global"]
|
|
83397
|
+
}).pipe(Effect.mapError((cause) => new CliSelfUpdateError({
|
|
83398
|
+
step: "locating the npm global prefix",
|
|
83399
|
+
detail: String(cause)
|
|
83400
|
+
})));
|
|
83401
|
+
if (result.code !== 0) return yield* new CliSelfUpdateError({
|
|
83402
|
+
step: "locating the npm global prefix",
|
|
83403
|
+
detail: `npm exited with code ${String(result.code)}`
|
|
83404
|
+
});
|
|
83405
|
+
const prefix = result.stdout.trim();
|
|
83406
|
+
if (prefix.length === 0) return yield* new CliSelfUpdateError({
|
|
83407
|
+
step: "locating the npm global prefix",
|
|
83408
|
+
detail: "npm printed no prefix"
|
|
83409
|
+
});
|
|
83410
|
+
return `${prefix}/bin/${binaryName}`;
|
|
83411
|
+
});
|
|
83412
|
+
const installGlobalCli = Effect.fn("cli.selfUpdate.installGlobalCli")(function* (input) {
|
|
83413
|
+
const runner = yield* ProcessRunner;
|
|
83414
|
+
const step = `installing ${PACKAGE_NAME}@${input.version}`;
|
|
83415
|
+
const result = yield* runner.run({
|
|
83416
|
+
command: "npm",
|
|
83417
|
+
args: [
|
|
83418
|
+
"install",
|
|
83419
|
+
"--global",
|
|
83420
|
+
"--no-fund",
|
|
83421
|
+
"--no-audit",
|
|
83422
|
+
`${PACKAGE_NAME}@${input.version}`
|
|
83423
|
+
],
|
|
83424
|
+
timeout: CLI_INSTALL_TIMEOUT_MS
|
|
83425
|
+
}).pipe(Effect.mapError((cause) => new CliSelfUpdateError({
|
|
83426
|
+
step,
|
|
83427
|
+
detail: String(cause)
|
|
83428
|
+
})));
|
|
83429
|
+
if (result.code !== 0) return yield* new CliSelfUpdateError({
|
|
83430
|
+
step,
|
|
83431
|
+
detail: result.stderr.trim().length > 0 ? result.stderr.trim() : `exit code ${String(result.code)}`
|
|
83432
|
+
});
|
|
83433
|
+
});
|
|
83434
|
+
/**
|
|
83435
|
+
* Ask the freshly installed binary what version it is.
|
|
83436
|
+
*
|
|
83437
|
+
* A completed npm install can still be unusable — a native dependency that
|
|
83438
|
+
* failed to build, a Node too old — and it is better to find that out here,
|
|
83439
|
+
* while the service is still running the version that works, than after
|
|
83440
|
+
* pointing the unit at it.
|
|
83441
|
+
*/
|
|
83442
|
+
const readInstalledVersion = Effect.fn("cli.selfUpdate.readInstalledVersion")(function* (binaryPath) {
|
|
83443
|
+
const runner = yield* ProcessRunner;
|
|
83444
|
+
const step = "verifying the installed CLI";
|
|
83445
|
+
const result = yield* runner.run({
|
|
83446
|
+
command: binaryPath,
|
|
83447
|
+
args: ["--version"]
|
|
83448
|
+
}).pipe(Effect.mapError((cause) => new CliSelfUpdateError({
|
|
83449
|
+
step,
|
|
83450
|
+
detail: String(cause)
|
|
83451
|
+
})));
|
|
83452
|
+
if (result.code !== 0) return yield* new CliSelfUpdateError({
|
|
83453
|
+
step,
|
|
83454
|
+
detail: `the installed CLI exited with code ${String(result.code)}`
|
|
83455
|
+
});
|
|
83456
|
+
const match = /(\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?)/u.exec(result.stdout);
|
|
83457
|
+
if (match?.[1] === void 0) return yield* new CliSelfUpdateError({
|
|
83458
|
+
step,
|
|
83459
|
+
detail: "the installed CLI did not report a version"
|
|
83460
|
+
});
|
|
83461
|
+
return match[1];
|
|
83462
|
+
});
|
|
83463
|
+
/**
|
|
83464
|
+
* Hand the rest of the work to the new binary.
|
|
83465
|
+
*
|
|
83466
|
+
* `service update` from *this* process would pin this process's version. The
|
|
83467
|
+
* new binary reconciles to itself, which is the only way the unit ends up
|
|
83468
|
+
* pointing at what was just installed.
|
|
83469
|
+
*/
|
|
83470
|
+
const reconcileWithNewBinary = Effect.fn("cli.selfUpdate.reconcileWithNewBinary")(function* (input) {
|
|
83471
|
+
const runner = yield* ProcessRunner;
|
|
83472
|
+
const step = "updating the service to the new version";
|
|
83473
|
+
const result = yield* runner.run({
|
|
83474
|
+
command: input.binaryPath,
|
|
83475
|
+
args: [
|
|
83476
|
+
"service",
|
|
83477
|
+
"update",
|
|
83478
|
+
...input.baseDir === void 0 ? [] : ["--base-dir", input.baseDir]
|
|
83479
|
+
],
|
|
83480
|
+
timeout: CLI_INSTALL_TIMEOUT_MS
|
|
83481
|
+
}).pipe(Effect.mapError((cause) => new CliSelfUpdateError({
|
|
83482
|
+
step,
|
|
83483
|
+
detail: String(cause)
|
|
83484
|
+
})));
|
|
83485
|
+
if (result.code !== 0) return yield* new CliSelfUpdateError({
|
|
83486
|
+
step,
|
|
83487
|
+
detail: result.stderr.trim().length > 0 ? result.stderr.trim() : `exit code ${String(result.code)}`
|
|
83488
|
+
});
|
|
83489
|
+
});
|
|
83490
|
+
//#endregion
|
|
82776
83491
|
//#region src/cli/service.ts
|
|
82777
83492
|
const isBootServiceCommandError = Schema$1.is(BootServiceCommandError);
|
|
82778
83493
|
const bootServiceLayer = (config) => layer$46({
|
|
@@ -82918,6 +83633,39 @@ const serviceRestartCommand = Command.make("restart", projectLocationFlags).pipe
|
|
|
82918
83633
|
yield* service.restart;
|
|
82919
83634
|
yield* Console.log("Restarted the P4Code service.");
|
|
82920
83635
|
}))));
|
|
83636
|
+
/**
|
|
83637
|
+
* Install a newer CLI, then point the service at it.
|
|
83638
|
+
*
|
|
83639
|
+
* Deliberately not `ServerSelfUpdate`, which the server uses to replace itself
|
|
83640
|
+
* in place: that path refuses to run under launchd, because KeepAlive would
|
|
83641
|
+
* restart the process being replaced and leave two servers on one port. Under a
|
|
83642
|
+
* supervisor the honest sequence is install, then reconcile and let the
|
|
83643
|
+
* supervisor restart — which is what this does, from the new binary.
|
|
83644
|
+
*/
|
|
83645
|
+
const serviceSelfUpdateCommand = Command.make("self-update", {
|
|
83646
|
+
...projectLocationFlags,
|
|
83647
|
+
version: Flag.string("version").pipe(Flag.withDescription("Version or dist-tag to install (default latest)."), Flag.optional)
|
|
83648
|
+
}).pipe(Command.withDescription("Install the newest @p4code/cli and update the service to run it."), Command.withHandler((flags) => runServiceCommand(flags, Effect.gen(function* () {
|
|
83649
|
+
const requested = Option.getOrElse(flags.version, () => "latest");
|
|
83650
|
+
if (!isInstallableVersion(requested)) {
|
|
83651
|
+
yield* Console.error(`'${requested}' is not a version or dist-tag. Use an exact version, or latest.`);
|
|
83652
|
+
return;
|
|
83653
|
+
}
|
|
83654
|
+
yield* Console.log(`Installing ${PACKAGE_NAME}@${requested}…`);
|
|
83655
|
+
yield* installGlobalCli({ version: requested });
|
|
83656
|
+
const binaryPath = yield* resolveGlobalBinary("p4c");
|
|
83657
|
+
const installed = yield* readInstalledVersion(binaryPath);
|
|
83658
|
+
if (installed === version) {
|
|
83659
|
+
yield* Console.log(`Already running @p4code/cli@${installed}; nothing to update.`);
|
|
83660
|
+
return;
|
|
83661
|
+
}
|
|
83662
|
+
yield* Console.log(`Installed @p4code/cli@${installed}; updating the service…`);
|
|
83663
|
+
yield* reconcileWithNewBinary({
|
|
83664
|
+
binaryPath,
|
|
83665
|
+
baseDir: void 0
|
|
83666
|
+
});
|
|
83667
|
+
yield* Console.log(`Service now runs @p4code/cli@${installed}.`);
|
|
83668
|
+
}))));
|
|
82921
83669
|
const serviceStatusCommand = Command.make("status", projectLocationFlags).pipe(Command.withDescription("Show whether the P4Code background service is installed."), Command.withHandler((flags) => runServiceCommand(flags, Effect.gen(function* () {
|
|
82922
83670
|
const service = yield* BootService;
|
|
82923
83671
|
yield* Console.log(formatServiceStatus(yield* service.status, version));
|
|
@@ -82942,11 +83690,12 @@ const serviceCommand = Command.make("service").pipe(Command.withDescription("Man
|
|
|
82942
83690
|
serviceUninstallCommand,
|
|
82943
83691
|
serviceUpdateCommand,
|
|
82944
83692
|
serviceRestartCommand,
|
|
83693
|
+
serviceSelfUpdateCommand,
|
|
82945
83694
|
serviceStatusCommand
|
|
82946
83695
|
]));
|
|
82947
83696
|
//#endregion
|
|
82948
83697
|
//#region src/bin.ts
|
|
82949
|
-
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$
|
|
83698
|
+
const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, layer$68, FetchHttpClient.layer);
|
|
82950
83699
|
/**
|
|
82951
83700
|
* Prints an update reminder before dispatching, at most once a day.
|
|
82952
83701
|
*
|