@knpkv/alchemy-console 0.4.1 → 0.5.0

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.
Files changed (60) hide show
  1. package/README.md +67 -15
  2. package/dist/api.d.ts +87 -16
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +7 -0
  5. package/dist/api.js.map +1 -1
  6. package/dist/client/assets/{index-CHwWf6VU.css → index-D5bP19PT.css} +1 -1
  7. package/dist/client/assets/index-D9T2ytki.js +58 -0
  8. package/dist/client/index.html +2 -2
  9. package/dist/main.js +2 -1
  10. package/dist/main.js.map +1 -1
  11. package/dist/plan.d.ts +109 -0
  12. package/dist/plan.d.ts.map +1 -0
  13. package/dist/plan.js +40 -0
  14. package/dist/plan.js.map +1 -0
  15. package/dist/presentation/PlanView.d.ts +10 -0
  16. package/dist/presentation/PlanView.d.ts.map +1 -0
  17. package/dist/presentation/PlanView.js +70 -0
  18. package/dist/presentation/PlanView.js.map +1 -0
  19. package/dist/presentation/StackDetailView.d.ts +4 -1
  20. package/dist/presentation/StackDetailView.d.ts.map +1 -1
  21. package/dist/presentation/StackDetailView.js +9 -3
  22. package/dist/presentation/StackDetailView.js.map +1 -1
  23. package/dist/presentation/detail-model.d.ts +1 -1
  24. package/dist/presentation/detail-model.d.ts.map +1 -1
  25. package/dist/server/Catalog.d.ts +1 -0
  26. package/dist/server/Catalog.d.ts.map +1 -1
  27. package/dist/server/Catalog.js +7 -1
  28. package/dist/server/Catalog.js.map +1 -1
  29. package/dist/server/HttpApplication.d.ts +2 -1
  30. package/dist/server/HttpApplication.d.ts.map +1 -1
  31. package/dist/server/HttpApplication.js +3 -1
  32. package/dist/server/HttpApplication.js.map +1 -1
  33. package/dist/server/Launch.d.ts +1 -1
  34. package/dist/server/Launch.d.ts.map +1 -1
  35. package/dist/server/PlanReader.d.ts +19 -0
  36. package/dist/server/PlanReader.d.ts.map +1 -0
  37. package/dist/server/PlanReader.js +201 -0
  38. package/dist/server/PlanReader.js.map +1 -0
  39. package/dist/server/Server.d.ts +1 -1
  40. package/dist/server/Server.d.ts.map +1 -1
  41. package/dist/server/plan/archive.d.ts +10 -0
  42. package/dist/server/plan/archive.d.ts.map +1 -0
  43. package/dist/server/plan/archive.js +62 -0
  44. package/dist/server/plan/archive.js.map +1 -0
  45. package/dist/server/plan/github.d.ts +105 -0
  46. package/dist/server/plan/github.d.ts.map +1 -0
  47. package/dist/server/plan/github.js +155 -0
  48. package/dist/server/plan/github.js.map +1 -0
  49. package/dist/server/plan/receipt.d.ts +152 -0
  50. package/dist/server/plan/receipt.d.ts.map +1 -0
  51. package/dist/server/plan/receipt.js +111 -0
  52. package/dist/server/plan/receipt.js.map +1 -0
  53. package/dist/views/index.js +547 -264
  54. package/dist/views/style.css +1 -1
  55. package/dist/views.d.ts +3 -0
  56. package/dist/views.d.ts.map +1 -1
  57. package/dist/views.js +1 -0
  58. package/dist/views.js.map +1 -1
  59. package/package.json +3 -3
  60. package/dist/client/assets/index-C5EGKyzJ.js +0 -58
@@ -0,0 +1,19 @@
1
+ /** Reads trusted CI publications as data. Never imports a stack, runs Alchemy or touches provider state. */
2
+ import { Config, Context, Crypto, Effect, Layer, Schema } from "effect";
3
+ import type { PlanState } from "../plan.js";
4
+ import { PlanGithub } from "./plan/github.js";
5
+ declare const PlanProvenanceInvalid_base: Schema.Class<PlanProvenanceInvalid, Schema.TaggedStruct<"PlanProvenanceInvalid", {}>, import("effect/Cause").YieldableError>;
6
+ export declare class PlanProvenanceInvalid extends PlanProvenanceInvalid_base {
7
+ }
8
+ declare const PlanReader_base: Context.ServiceClass<PlanReader, "@knpkv/alchemy-console/PlanReader", {
9
+ readonly read: (app: string, stage: string) => Effect.Effect<PlanState>;
10
+ }>;
11
+ export declare class PlanReader extends PlanReader_base {
12
+ }
13
+ /** One typed failure class per refusal, transport, metadata, provenance, archive, digest and receipt boundary. */
14
+ export declare const readerLayer: Layer.Layer<PlanReader, never, Crypto.Crypto | PlanGithub>;
15
+ export declare const disconnectedLayer: Layer.Layer<PlanReader, never, never>;
16
+ /** Dedicated read-only Actions token is optional and server-held. No host gh token discovery or client credential configuration. */
17
+ export declare const configuredPlanLayer: Layer.Layer<PlanReader, Config.ConfigError, Crypto.Crypto | import("effect/http/HttpClient").HttpClient>;
18
+ export {};
19
+ //# sourceMappingURL=PlanReader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlanReader.d.ts","sourceRoot":"","sources":["../../src/server/PlanReader.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,OAAO,EAAS,MAAM,EAAE,OAAO,EAAE,MAAM,EAAY,MAAM,EAAE,KAAK,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAA;AAChG,OAAO,KAAK,EAAc,SAAS,EAAiB,MAAM,YAAY,CAAA;AAEtE,OAAO,EAML,UAAU,EAIX,MAAM,kBAAkB,CAAA;;AAUzB,qBAAa,qBAAsB,SAAQ,0BAAwE;CAAG;;mBAGrG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;;AADzE,qBAAa,UAAW,SAAQ,eAES;CAAG;AAmE5C,kHAAkH;AAClH,eAAO,MAAM,WAAW,4DA0KvB,CAAA;AAED,eAAO,MAAM,iBAAiB,uCAG7B,CAAA;AAED,oIAAoI;AACpI,eAAO,MAAM,mBAAmB,0GAG7B,CAAA"}
@@ -0,0 +1,201 @@
1
+ /** Reads trusted CI publications as data. Never imports a stack, runs Alchemy or touches provider state. */
2
+ import { Clock, Config, Context, Crypto, DateTime, Effect, Layer, Option, Schema } from "effect";
3
+ import { archiveLimit, archiveText } from "./plan/archive.js";
4
+ import { githubLayer, PlanCiUnreachable, PlanGithub, PlanMetadataInvalid, publishedMillis } from "./plan/github.js";
5
+ import { decodeReceipt, hashBytes, PlanDigestMismatch, stageFor } from "./plan/receipt.js";
6
+ export class PlanProvenanceInvalid extends Schema.TaggedError()("PlanProvenanceInvalid", {}) {
7
+ }
8
+ export class PlanReader extends Context.Service()("@knpkv/alchemy-console/PlanReader") {
9
+ }
10
+ /** Explicit app/stage allowlist. Similar names and unrelated stages do not inherit a CI source. */
11
+ const mappedStack = (app, stage) => app === "site" && stage === "prod" ? "site" : app === "aws-org" && stage === "org" ? "aws-org" : null;
12
+ const repoId = 1288997422;
13
+ const compareProducerRuns = (a, b) => DateTime.toEpochMillis(b.created_at) - DateTime.toEpochMillis(a.created_at) || b.id - a.id;
14
+ const runHref = (id) => `https://github.com/knpkv/infra/actions/runs/${id}`;
15
+ const sourceFor = (artifact, head, now, producer) => ({
16
+ pr: producer?.pull_requests.find((pr) => pr.head.sha === head)?.number ?? null,
17
+ head: head.slice(0, 7),
18
+ base: null,
19
+ publishedAt: DateTime.formatIso(artifact.created_at),
20
+ ageDays: Math.max(0, Math.floor(now / 86_400_000) - Math.floor(publishedMillis(artifact) / 86_400_000)),
21
+ runHref: runHref(artifact.workflow_run.id)
22
+ });
23
+ const QualifiedWorkflowPath = Schema.TemplateLiteralParser([
24
+ ".github/workflows/",
25
+ Schema.Literals(["infra.yml", "infra-publish.yml"]),
26
+ "@",
27
+ Schema.NonEmptyString.check(Schema.isPattern(/^[^@]+$/u))
28
+ ]);
29
+ /** GitHub can qualify a workflow filename with a branch or PR merge ref; neither part is a prefix match. */
30
+ const workflowPathMatches = (run, file) => {
31
+ if (run.path === `.github/workflows/${file}`)
32
+ return true;
33
+ const qualified = Option.getOrNull(Schema.decodeUnknownOption(QualifiedWorkflowPath)(run.path));
34
+ if (qualified === null || qualified[1] !== file)
35
+ return false;
36
+ const ref = qualified[3];
37
+ return ref === "main" || ref === "refs/heads/main" ||
38
+ (file === "infra.yml" && (ref === run.head_branch || ref === `refs/heads/${run.head_branch}` ||
39
+ run.pull_requests.some((pr) => ref === `refs/pull/${pr.number}/merge`)));
40
+ };
41
+ const privateRunMatches = (run, workflowId, file, event) => run.workflow_id === workflowId && workflowPathMatches(run, file) && run.event === event &&
42
+ run.repository.id === repoId && run.head_repository.id === repoId &&
43
+ run.actor.id === 833092 && run.triggering_actor.id === 833092;
44
+ const project = (receipt, source, latestMainSeenByCi, now, artifact) => ({
45
+ source: { ...source, pr: receipt.pr, base: receipt.base.slice(0, 7) },
46
+ digest: `${receipt.native.digest.slice(0, 4)}…${receipt.native.digest.slice(-4)}`,
47
+ rows: receipt.native.semantic.rows.map((row) => ({
48
+ fqn: row.fqn,
49
+ resourceType: row.type,
50
+ action: row.action,
51
+ changedKeys: [...row.changedKeys]
52
+ })),
53
+ stale: latestMainSeenByCi !== null && receipt.base !== latestMainSeenByCi
54
+ ? "base"
55
+ : now - publishedMillis(artifact) > 86_400_000
56
+ ? "age"
57
+ : null
58
+ });
59
+ /** One typed failure class per refusal, transport, metadata, provenance, archive, digest and receipt boundary. */
60
+ export const readerLayer = Layer.effect(PlanReader, Effect.gen(function* () {
61
+ const github = yield* PlanGithub;
62
+ const cryptoService = yield* Crypto.Crypto;
63
+ const read = Effect.fn("AlchemyConsole.readCiPlan")(function* (app, stage) {
64
+ const stack = mappedStack(app, stage);
65
+ if (stack === null)
66
+ return { _tag: "NotConnected" };
67
+ const now = yield* Clock.currentTimeMillis;
68
+ const publisherWorkflow = yield* github.workflow("infra-publish.yml");
69
+ const producerWorkflow = yield* github.workflow("infra.yml");
70
+ if (publisherWorkflow.path !== ".github/workflows/infra-publish.yml" ||
71
+ producerWorkflow.path !== ".github/workflows/infra.yml") {
72
+ return yield* new PlanProvenanceInvalid({});
73
+ }
74
+ const publishing = publisherWorkflow.state === "active" && producerWorkflow.state === "active";
75
+ if (!publishing)
76
+ return { _tag: "NotPublishing" };
77
+ const producerRuns = (yield* github.runs("infra.yml")).filter((run) => privateRunMatches(run, producerWorkflow.id, "infra.yml", "pull_request")).sort(compareProducerRuns);
78
+ const latest = producerRuns[0];
79
+ const jobs = latest === undefined ? [] : yield* github.jobs(latest.id);
80
+ const paused = jobs.length > 0 && jobs.every((job) => job.conclusion === "skipped");
81
+ if (paused)
82
+ return { _tag: "NotPublishing" };
83
+ // A successful matrix job can skip this stack. Only the actual native-plan step proves an attempt.
84
+ let attempt;
85
+ for (const [index, run] of producerRuns.entries()) {
86
+ const runJobs = index === 0 ? jobs : yield* github.jobs(run.id);
87
+ const nativeJobs = runJobs.filter((job) => job.name === `${stack} / native`);
88
+ if (nativeJobs.length > 1)
89
+ return yield* new PlanMetadataInvalid({});
90
+ const job = nativeJobs[0];
91
+ if (job === undefined)
92
+ continue;
93
+ if (job.run_attempt !== run.run_attempt)
94
+ return yield* new PlanProvenanceInvalid({});
95
+ const nativeSteps = job.steps.filter((step) => step.name === "Native read-only plan");
96
+ if (nativeSteps.length > 1)
97
+ return yield* new PlanMetadataInvalid({});
98
+ const step = nativeSteps[0];
99
+ if (step === undefined || step.started_at === undefined || step.started_at === null ||
100
+ step.status === "queued" || step.conclusion === "skipped")
101
+ continue;
102
+ if (step.status !== "completed" && step.status !== "in_progress")
103
+ return yield* new PlanMetadataInvalid({});
104
+ attempt = { run, job };
105
+ break;
106
+ }
107
+ const failed = attempt?.job.status === "completed" &&
108
+ ["failure", "cancelled", "timed_out", "action_required", "stale"].includes(attempt.job.conclusion ?? "")
109
+ ? attempt.run :
110
+ undefined;
111
+ const artifacts = (yield* github.artifacts(stack)).filter((artifact) => artifact.name.startsWith(`infra-plan-${stack}-`))
112
+ .sort((a, b) => publishedMillis(b) - publishedMillis(a) || b.id - a.id);
113
+ const selected = artifacts.find((artifact) => !artifact.expired && DateTime.toEpochMillis(artifact.expires_at) > now);
114
+ if (selected === undefined) {
115
+ if (failed !== undefined)
116
+ return { _tag: "Failed", runHref: runHref(failed.id), lastPublished: null };
117
+ const expired = artifacts[0];
118
+ if (expired === undefined)
119
+ return { _tag: "NotPublished" };
120
+ const match = expired.name.match(new RegExp(`^infra-plan-${stack}-([a-f0-9]{40})$`));
121
+ const head = match?.[1];
122
+ return {
123
+ _tag: "Expired",
124
+ source: head === undefined
125
+ ? null
126
+ : sourceFor(expired, head, now, producerRuns.find((run) => run.head_sha === head))
127
+ };
128
+ }
129
+ const artifact = selected;
130
+ const head = artifact.name.slice(`infra-plan-${stack}-`.length);
131
+ const producer = producerRuns.find((run) => run.head_sha === head);
132
+ const source = /^[a-f0-9]{40}$/.test(head) ? sourceFor(artifact, head, now, producer) : null;
133
+ const verified = Effect.gen(function* () {
134
+ if (source === null || artifact.digest === null || artifact.size_in_bytes >= archiveLimit ||
135
+ artifact.workflow_run.repository_id !== repoId || artifact.workflow_run.head_repository_id !== repoId ||
136
+ artifact.workflow_run.head_branch !== "main")
137
+ return yield* new PlanProvenanceInvalid({});
138
+ const publisher = yield* github.run(artifact.workflow_run.id);
139
+ if (!privateRunMatches(publisher, publisherWorkflow.id, "infra-publish.yml", "workflow_run") ||
140
+ publisher.head_branch !== "main" || publisher.status !== "completed" ||
141
+ publisher.conclusion !== "success" ||
142
+ publisher.head_sha !== artifact.workflow_run.head_sha)
143
+ return yield* new PlanProvenanceInvalid({});
144
+ const bytes = yield* github.download(artifact.id);
145
+ if (bytes.length !== artifact.size_in_bytes || `sha256:${yield* hashBytes(bytes)}` !== artifact.digest) {
146
+ return yield* new PlanDigestMismatch({});
147
+ }
148
+ const receipt = yield* decodeReceipt(yield* archiveText(bytes), stack);
149
+ const producingRun = yield* github.run(receipt.producerRunId);
150
+ const publicationJobs = yield* github.jobs(publisher.id);
151
+ const producingJobs = (yield* github.jobs(producingRun.id)).filter((job) => job.name === `${stack} / native`);
152
+ const producingJob = producingJobs[0];
153
+ const producingSteps = producingJob?.steps.filter((step) => step.name === "Native read-only plan") ?? [];
154
+ const producingStep = producingSteps[0];
155
+ if (receipt.runId !== publisher.id || receipt.base !== publisher.head_sha || receipt.head !== head ||
156
+ receipt.native.stage !== stageFor(stack) ||
157
+ !privateRunMatches(producingRun, producerWorkflow.id, "infra.yml", "pull_request") ||
158
+ producingRun.head_sha !== receipt.head || producingRun.status !== "completed" ||
159
+ producingRun.conclusion !== "success" ||
160
+ producingJobs.length !== 1 || producingJob === undefined ||
161
+ producingJob.run_attempt !== producingRun.run_attempt || producingJob.status !== "completed" ||
162
+ producingJob.conclusion !== "success" || producingSteps.length !== 1 || producingStep === undefined ||
163
+ producingStep.status !== "completed" || producingStep.conclusion !== "success" ||
164
+ producingStep.started_at === undefined || producingStep.started_at === null ||
165
+ producingRun.referenced_workflows?.some((workflow) => [
166
+ "knpkv/infra/.github/workflows/_infra-plan.yml@main",
167
+ "knpkv/infra/.github/workflows/_infra-plan.yml@refs/heads/main"
168
+ ].includes(workflow.path) && workflow.ref === "refs/heads/main" && workflow.sha === receipt.base) !== true ||
169
+ !producingRun.pull_requests.some((pr) => pr.number === receipt.pr && pr.head.sha === receipt.head && pr.base.sha === receipt.base) ||
170
+ !publicationJobs.some((job) => job.id === receipt.publisherJobId && job.name === `${stack} / publish` &&
171
+ job.run_attempt === publisher.run_attempt &&
172
+ job.status === "completed" && job.conclusion === "success")) {
173
+ return yield* new PlanProvenanceInvalid({});
174
+ }
175
+ return { plan: project(receipt, source, yield* github.latestMainSeenByCi(), now, artifact), producingRun };
176
+ }).pipe(Effect.map(({ plan, producingRun }) => failed !== undefined && compareProducerRuns(failed, producingRun) < 0
177
+ ? { _tag: "Failed", runHref: runHref(failed.id), lastPublished: { ...plan, stale: "failed" } }
178
+ : { _tag: "Ready", plan }), Effect.catchTags({
179
+ PlanProvenanceInvalid: () => Effect.succeed({ _tag: "Unverified", reason: "provenance", source }),
180
+ PlanArchiveInvalid: () => Effect.succeed({ _tag: "Unverified", reason: "archive", source }),
181
+ PlanDigestMismatch: () => Effect.succeed({ _tag: "Unverified", reason: "digest", source }),
182
+ PlanReceiptInvalid: () => Effect.succeed({ _tag: "Unverified", reason: "receipt", source })
183
+ }));
184
+ return yield* verified;
185
+ }, (effect) => effect.pipe(Effect.timeoutOrElse({ duration: "30 seconds", orElse: () => Effect.fail(new PlanCiUnreachable({})) }), Effect.catchTags({
186
+ PlanProvenanceInvalid: () => Effect.succeed({ _tag: "Unverified", reason: "provenance", source: null }),
187
+ PlanMetadataInvalid: () => Effect.succeed({ _tag: "Unverified", reason: "metadata", source: null }),
188
+ PlanCiUnreachable: () => Effect.succeed({ _tag: "Unreachable" }),
189
+ PlanCiRefused: () => Effect.succeed({ _tag: "Refused" })
190
+ }), Effect.withTracerEnabled(false)));
191
+ return PlanReader.of({
192
+ read: (app, stage) => read(app, stage).pipe(Effect.provideService(Crypto.Crypto, cryptoService))
193
+ });
194
+ }));
195
+ export const disconnectedLayer = Layer.succeed(PlanReader, PlanReader.of({ read: () => Effect.succeed({ _tag: "NotConnected" }) }));
196
+ /** Dedicated read-only Actions token is optional and server-held. No host gh token discovery or client credential configuration. */
197
+ export const configuredPlanLayer = Layer.unwrap(Effect.gen(function* () {
198
+ const token = yield* Config.option(Config.Redacted("ALCHEMY_CONSOLE_CI_TOKEN"));
199
+ return Option.isNone(token) ? disconnectedLayer : readerLayer.pipe(Layer.provide(githubLayer(token.value)));
200
+ }));
201
+ //# sourceMappingURL=PlanReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlanReader.js","sourceRoot":"","sources":["../../src/server/PlanReader.ts"],"names":[],"mappings":"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"}
@@ -2,5 +2,5 @@ import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
2
2
  import { Deferred, Layer } from "effect";
3
3
  import { type ControlOptions } from "./ControlSocket.js";
4
4
  /** Catalog and platform services are supplied by the executable. This layer owns both listeners. */
5
- export declare const makeServer: (options: ControlOptions, ready: Deferred.Deferred<string>) => Layer.Layer<never, import("effect/PlatformError").BadArgument | import("@knpkv/browser-pairing").BrowserPairingError | import("./ControlSocket.js").ControlError | import("@knpkv/browser-pairing").CredentialCookieError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | OwnerSession.UnsafeLoopbackAddressError, import("./Catalog.ts").Catalog | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
5
+ export declare const makeServer: (options: ControlOptions, ready: Deferred.Deferred<string>) => Layer.Layer<never, import("effect/PlatformError").BadArgument | import("@knpkv/browser-pairing").BrowserPairingError | import("./ControlSocket.js").ControlError | import("@knpkv/browser-pairing").CredentialCookieError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | OwnerSession.UnsafeLoopbackAddressError, import("./Catalog.ts").Catalog | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/Path").Path | import("./PlanReader.ts").PlanReader>;
6
6
  //# sourceMappingURL=Server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Server.d.ts","sourceRoot":"","sources":["../../src/server/Server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAA;AAGhD,OAAO,EAAE,KAAK,cAAc,EAAiB,MAAM,oBAAoB,CAAA;AAIvE,oGAAoG;AACpG,eAAO,MAAM,UAAU,YAAa,cAAc,SAAS,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,kfAuBwB,CAAA"}
1
+ {"version":3,"file":"Server.d.ts","sourceRoot":"","sources":["../../src/server/Server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAA;AAGhD,OAAO,EAAE,KAAK,cAAc,EAAiB,MAAM,oBAAoB,CAAA;AAIvE,oGAAoG;AACpG,eAAO,MAAM,UAAU,YAAa,cAAc,SAAS,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,yhBAuBwB,CAAA"}
@@ -0,0 +1,10 @@
1
+ /** Node compression adapter. Inspects one regular plan.json member in memory, without filesystem extraction. */
2
+ import { Effect, Schema } from "effect";
3
+ export declare const archiveLimit = 1000000;
4
+ declare const PlanArchiveInvalid_base: Schema.Class<PlanArchiveInvalid, Schema.TaggedStruct<"PlanArchiveInvalid", {}>, import("effect/Cause").YieldableError>;
5
+ export declare class PlanArchiveInvalid extends PlanArchiveInvalid_base {
6
+ }
7
+ /** Both ZIP metadata and actual expanded bytes must fit the cap. Local and central identities must agree. */
8
+ export declare const archiveText: (bytes: Uint8Array) => Effect.Effect<string, PlanArchiveInvalid, never>;
9
+ export {};
10
+ //# sourceMappingURL=archive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../../src/server/plan/archive.ts"],"names":[],"mappings":"AAAA,gHAAgH;AAChH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAGvC,eAAO,MAAM,YAAY,UAAY,CAAA;;AACrC,qBAAa,kBAAmB,SAAQ,uBAAkE;CAAG;AAM7G,6GAA6G;AAC7G,eAAO,MAAM,WAAW,UAAW,UAAU,qDAsDzC,CAAA"}
@@ -0,0 +1,62 @@
1
+ /** Node compression adapter. Inspects one regular plan.json member in memory, without filesystem extraction. */
2
+ import { Effect, Schema } from "effect";
3
+ import { crc32, inflateRawSync } from "node:zlib";
4
+ export const archiveLimit = 1_000_000;
5
+ export class PlanArchiveInvalid extends Schema.TaggedError()("PlanArchiveInvalid", {}) {
6
+ }
7
+ const requireArchive = (condition) => {
8
+ if (!condition)
9
+ throw new PlanArchiveInvalid({});
10
+ };
11
+ /** Both ZIP metadata and actual expanded bytes must fit the cap. Local and central identities must agree. */
12
+ export const archiveText = (bytes) => Effect.try({
13
+ try: () => {
14
+ requireArchive(bytes.length > 0 && bytes.length < archiveLimit);
15
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
16
+ const u16 = (offset) => view.getUint16(offset, true);
17
+ const u32 = (offset) => view.getUint32(offset, true);
18
+ let end = bytes.length - 22;
19
+ while (end >= Math.max(0, bytes.length - 65557) && u32(end) !== 0x06054b50)
20
+ end--;
21
+ requireArchive(end >= 0 && end + 22 + u16(end + 20) === bytes.length);
22
+ requireArchive(u16(end + 4) === 0 && u16(end + 6) === 0 && u16(end + 8) === 1 && u16(end + 10) === 1);
23
+ const directory = u32(end + 16);
24
+ requireArchive(directory + u32(end + 12) === end && u32(directory) === 0x02014b50);
25
+ const flags = u16(directory + 8);
26
+ const method = u16(directory + 10);
27
+ const checksum = u32(directory + 16);
28
+ const compressed = u32(directory + 20);
29
+ const expanded = u32(directory + 24);
30
+ const nameLength = u16(directory + 28);
31
+ const name = new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(directory + 46, directory + 46 + nameLength));
32
+ const mode = u32(directory + 38) >>> 16;
33
+ requireArchive(name === "plan.json" && nameLength === 9 && u16(directory + 34) === 0);
34
+ requireArchive((mode & 0xf000) === 0 || (mode & 0xf000) === 0x8000);
35
+ requireArchive((flags & ~0x808) === 0 && (method === 0 || method === 8));
36
+ requireArchive(expanded > 0 && expanded < archiveLimit && compressed > 0 && compressed < archiveLimit);
37
+ requireArchive(directory + 46 + nameLength + u16(directory + 30) + u16(directory + 32) === end);
38
+ const local = u32(directory + 42);
39
+ requireArchive(local === 0 && u32(local) === 0x04034b50);
40
+ requireArchive(u16(local + 6) === flags && u16(local + 8) === method && u16(local + 26) === nameLength);
41
+ const localName = bytes.subarray(local + 30, local + 30 + nameLength);
42
+ requireArchive(new TextDecoder("utf-8", { fatal: true }).decode(localName) === name);
43
+ const start = local + 30 + nameLength + u16(local + 28);
44
+ const dataEnd = start + compressed;
45
+ if ((flags & 8) === 0) {
46
+ requireArchive(u32(local + 14) === checksum && u32(local + 18) === compressed && u32(local + 22) === expanded &&
47
+ dataEnd === directory);
48
+ }
49
+ else {
50
+ const descriptor = u32(dataEnd) === 0x08074b50 ? dataEnd + 4 : dataEnd;
51
+ requireArchive(descriptor + 12 === directory && u32(descriptor) === checksum && u32(descriptor + 4) === compressed &&
52
+ u32(descriptor + 8) === expanded);
53
+ }
54
+ requireArchive(start < dataEnd && dataEnd <= directory);
55
+ const data = bytes.subarray(start, dataEnd);
56
+ const text = method === 0 ? data : inflateRawSync(data, { maxOutputLength: archiveLimit - 1 });
57
+ requireArchive(text.length === expanded && crc32(text) === checksum);
58
+ return new TextDecoder("utf-8", { fatal: true }).decode(text);
59
+ },
60
+ catch: () => new PlanArchiveInvalid({})
61
+ });
62
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../../src/server/plan/archive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import { Context, Effect, Layer, Redacted, Schema } from "effect";
2
+ import { HttpClient } from "effect/http";
3
+ declare const PlanCiUnreachable_base: Schema.Class<PlanCiUnreachable, Schema.TaggedStruct<"PlanCiUnreachable", {}>, import("effect/Cause").YieldableError>;
4
+ export declare class PlanCiUnreachable extends PlanCiUnreachable_base {
5
+ }
6
+ declare const PlanCiRefused_base: Schema.Class<PlanCiRefused, Schema.TaggedStruct<"PlanCiRefused", {}>, import("effect/Cause").YieldableError>;
7
+ export declare class PlanCiRefused extends PlanCiRefused_base {
8
+ }
9
+ declare const PlanMetadataInvalid_base: Schema.Class<PlanMetadataInvalid, Schema.TaggedStruct<"PlanMetadataInvalid", {}>, import("effect/Cause").YieldableError>;
10
+ export declare class PlanMetadataInvalid extends PlanMetadataInvalid_base {
11
+ }
12
+ export type GithubFailure = PlanCiUnreachable | PlanCiRefused | PlanMetadataInvalid;
13
+ export declare const Run: Schema.Struct<{
14
+ readonly id: Schema.Int;
15
+ readonly workflow_id: Schema.Int;
16
+ readonly path: Schema.String;
17
+ readonly event: Schema.String;
18
+ readonly head_sha: Schema.String;
19
+ readonly head_branch: Schema.String;
20
+ readonly status: Schema.String;
21
+ readonly conclusion: Schema.NullOr<Schema.String>;
22
+ readonly run_attempt: Schema.Int;
23
+ readonly created_at: Schema.DateTimeUtcFromString;
24
+ readonly updated_at: Schema.DateTimeUtcFromString;
25
+ readonly actor: Schema.Struct<{
26
+ readonly id: Schema.Int;
27
+ }>;
28
+ readonly triggering_actor: Schema.Struct<{
29
+ readonly id: Schema.Int;
30
+ }>;
31
+ readonly referenced_workflows: Schema.optionalKey<Schema.$Array<Schema.Struct<{
32
+ readonly path: Schema.String;
33
+ readonly ref: Schema.String;
34
+ readonly sha: Schema.String;
35
+ }>>>;
36
+ readonly repository: Schema.Struct<{
37
+ readonly id: Schema.Int;
38
+ }>;
39
+ readonly head_repository: Schema.Struct<{
40
+ readonly id: Schema.Int;
41
+ }>;
42
+ readonly pull_requests: Schema.$Array<Schema.Struct<{
43
+ readonly number: Schema.Int;
44
+ readonly head: Schema.Struct<{
45
+ readonly sha: Schema.String;
46
+ }>;
47
+ readonly base: Schema.Struct<{
48
+ readonly sha: Schema.String;
49
+ }>;
50
+ }>>;
51
+ }>;
52
+ export type Run = typeof Run.Type;
53
+ export declare const Artifact: Schema.Struct<{
54
+ readonly id: Schema.Int;
55
+ readonly name: Schema.String;
56
+ readonly size_in_bytes: Schema.Int;
57
+ readonly digest: Schema.NullOr<Schema.String>;
58
+ readonly expired: Schema.Boolean;
59
+ readonly created_at: Schema.DateTimeUtcFromString;
60
+ readonly expires_at: Schema.DateTimeUtcFromString;
61
+ readonly workflow_run: Schema.Struct<{
62
+ readonly id: Schema.Int;
63
+ readonly repository_id: Schema.Int;
64
+ readonly head_repository_id: Schema.Int;
65
+ readonly head_branch: Schema.String;
66
+ readonly head_sha: Schema.String;
67
+ }>;
68
+ }>;
69
+ export type Artifact = typeof Artifact.Type;
70
+ export declare const Job: Schema.Struct<{
71
+ readonly id: Schema.Int;
72
+ readonly name: Schema.String;
73
+ readonly conclusion: Schema.NullOr<Schema.String>;
74
+ readonly status: Schema.String;
75
+ readonly run_attempt: Schema.Int;
76
+ readonly steps: Schema.$Array<Schema.Struct<{
77
+ readonly started_at: Schema.optionalKey<Schema.NullOr<Schema.DateTimeUtcFromString>>;
78
+ readonly name: Schema.String;
79
+ readonly status: Schema.String;
80
+ readonly conclusion: Schema.NullOr<Schema.String>;
81
+ }>>;
82
+ }>;
83
+ export type Job = typeof Job.Type;
84
+ declare const Workflow: Schema.Struct<{
85
+ readonly id: Schema.Int;
86
+ readonly path: Schema.String;
87
+ readonly state: Schema.String;
88
+ }>;
89
+ export type Workflow = typeof Workflow.Type;
90
+ declare const PlanGithub_base: Context.ServiceClass<PlanGithub, "@knpkv/alchemy-console/PlanGithub", {
91
+ readonly workflow: (file: "infra.yml" | "infra-publish.yml") => Effect.Effect<Workflow, GithubFailure>;
92
+ readonly runs: (file: "infra.yml" | "infra-publish.yml") => Effect.Effect<ReadonlyArray<Run>, GithubFailure>;
93
+ readonly artifacts: (stack: string) => Effect.Effect<ReadonlyArray<Artifact>, GithubFailure>;
94
+ readonly run: (id: number) => Effect.Effect<Run, GithubFailure>;
95
+ readonly jobs: (id: number) => Effect.Effect<ReadonlyArray<Job>, GithubFailure>;
96
+ readonly latestMainSeenByCi: () => Effect.Effect<string | null, GithubFailure>;
97
+ readonly download: (id: number) => Effect.Effect<Uint8Array, GithubFailure>;
98
+ }>;
99
+ export declare class PlanGithub extends PlanGithub_base {
100
+ }
101
+ /** Disable redirects on authenticated requests. Only the validated HTTPS blob redirect is followed, without Authorization. */
102
+ export declare const githubLayer: (token: Redacted.Redacted<string>) => Layer.Layer<PlanGithub, never, HttpClient.HttpClient>;
103
+ export declare const publishedMillis: (artifact: Artifact) => number;
104
+ export {};
105
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../src/server/plan/github.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAY,MAAM,EAAE,KAAK,EAAU,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACnF,OAAO,EAAmB,UAAU,EAA2B,MAAM,aAAa,CAAA;;AAIlF,qBAAa,iBAAkB,SAAQ,sBAAgE;CAAG;;AAC1G,qBAAa,aAAc,SAAQ,kBAAwD;CAAG;;AAC9F,qBAAa,mBAAoB,SAAQ,wBAAoE;CAAG;AAChH,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG,aAAa,GAAG,mBAAmB,CAAA;AAInF,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBd,CAAA;AACF,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,CAAC,IAAI,CAAA;AACjC,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;EAenB,CAAA;AACF,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAC3C,eAAO,MAAM,GAAG;;;;;;;;;;;;EAcd,CAAA;AACF,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,CAAC,IAAI,CAAA;AACjC,QAAA,MAAM,QAAQ;;;;EAA6E,CAAA;AAC3F,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;;uBAMtB,CAAC,IAAI,EAAE,WAAW,GAAG,mBAAmB,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC;mBACvF,CAAC,IAAI,EAAE,WAAW,GAAG,mBAAmB,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;wBACxF,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;kBAC9E,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC;mBAChD,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;iCAClD,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,aAAa,CAAC;uBAC3D,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC;;AAP7E,qBAAa,UAAW,SAAQ,eAQS;CAAG;AAE5C,8HAA8H;AAC9H,eAAO,MAAM,WAAW,UAAW,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,0DAmHzD,CAAA;AAEH,eAAO,MAAM,eAAe,aAAc,QAAQ,KAAG,MAAqD,CAAA"}
@@ -0,0 +1,155 @@
1
+ /** Server-private GitHub transport. Token and signed download URLs stay here; body limits apply before buffering. */
2
+ import { collectBounded } from "@knpkv/bounded-io";
3
+ import { Context, DateTime, Effect, Layer, Option, Redacted, Schema } from "effect";
4
+ import { FetchHttpClient, HttpClient } from "effect/http";
5
+ import { archiveLimit } from "./archive.js";
6
+ import { Digest, Positive, Sha } from "./receipt.js";
7
+ export class PlanCiUnreachable extends Schema.TaggedError()("PlanCiUnreachable", {}) {
8
+ }
9
+ export class PlanCiRefused extends Schema.TaggedError()("PlanCiRefused", {}) {
10
+ }
11
+ export class PlanMetadataInvalid extends Schema.TaggedError()("PlanMetadataInvalid", {}) {
12
+ }
13
+ const Timestamp = Schema.DateTimeUtcFromString;
14
+ const Repository = Schema.Struct({ id: Positive });
15
+ export const Run = Schema.Struct({
16
+ id: Positive,
17
+ workflow_id: Positive,
18
+ path: Schema.String,
19
+ event: Schema.String,
20
+ head_sha: Sha,
21
+ head_branch: Schema.String,
22
+ status: Schema.String,
23
+ conclusion: Schema.NullOr(Schema.String),
24
+ run_attempt: Positive,
25
+ created_at: Timestamp,
26
+ updated_at: Timestamp,
27
+ actor: Repository,
28
+ triggering_actor: Repository,
29
+ referenced_workflows: Schema.optionalKey(Schema.Array(Schema.Struct({ path: Schema.String, ref: Schema.String, sha: Sha })).check(Schema.isMaxLength(10))),
30
+ repository: Repository,
31
+ head_repository: Repository,
32
+ pull_requests: Schema.Array(Schema.Struct({ number: Positive, head: Schema.Struct({ sha: Sha }), base: Schema.Struct({ sha: Sha }) })).check(Schema.isMaxLength(100))
33
+ });
34
+ export const Artifact = Schema.Struct({
35
+ id: Positive,
36
+ name: Schema.String.check(Schema.isMaxLength(256)),
37
+ size_in_bytes: Positive,
38
+ digest: Schema.NullOr(Digest),
39
+ expired: Schema.Boolean,
40
+ created_at: Timestamp,
41
+ expires_at: Timestamp,
42
+ workflow_run: Schema.Struct({
43
+ id: Positive,
44
+ repository_id: Positive,
45
+ head_repository_id: Positive,
46
+ head_branch: Schema.String,
47
+ head_sha: Sha
48
+ })
49
+ });
50
+ export const Job = Schema.Struct({
51
+ id: Positive,
52
+ name: Schema.String,
53
+ conclusion: Schema.NullOr(Schema.String),
54
+ status: Schema.String,
55
+ run_attempt: Positive,
56
+ steps: Schema.Array(Schema.Struct({
57
+ started_at: Schema.optionalKey(Schema.NullOr(Timestamp)),
58
+ name: Schema.String,
59
+ status: Schema.String,
60
+ conclusion: Schema.NullOr(Schema.String)
61
+ })).check(Schema.isMaxLength(1000))
62
+ });
63
+ const Workflow = Schema.Struct({ id: Positive, path: Schema.String, state: Schema.String });
64
+ const RefusalBody = Schema.fromJsonString(Schema.Struct({ message: Schema.String }));
65
+ // A 100-run page includes repository/user/commit envelopes before schema projection (~12 KiB per run).
66
+ const metadataLimit = 2 * 1024 * 1024;
67
+ export class PlanGithub extends Context.Service()("@knpkv/alchemy-console/PlanGithub") {
68
+ }
69
+ /** Disable redirects on authenticated requests. Only the validated HTTPS blob redirect is followed, without Authorization. */
70
+ export const githubLayer = (token) => Layer.effect(PlanGithub, Effect.gen(function* () {
71
+ const client = yield* HttpClient.HttpClient;
72
+ const request = Effect.fn("AlchemyConsole.planGithubRequest")(function* (path) {
73
+ const response = yield* client.get(`https://api.github.com/repos/knpkv/infra/${path}`, {
74
+ headers: {
75
+ authorization: `Bearer ${Redacted.value(token)}`,
76
+ accept: "application/vnd.github+json",
77
+ "x-github-api-version": "2022-11-28"
78
+ }
79
+ }).pipe(Effect.provideService(FetchHttpClient.RequestInit, { redirect: "manual", cache: "no-store" }), Effect.mapError(() => new PlanCiUnreachable({})));
80
+ if (response.status === 401)
81
+ return yield* new PlanCiRefused({});
82
+ if (response.status === 403) {
83
+ if (response.headers["x-ratelimit-remaining"] === "0" || response.headers["retry-after"] !== undefined) {
84
+ return yield* new PlanCiUnreachable({});
85
+ }
86
+ const bytes = yield* collectBounded(response.stream, 8192).pipe(Effect.mapError(() => new PlanCiUnreachable({})));
87
+ const refusal = Option.getOrNull(Schema.decodeUnknownOption(RefusalBody)(new TextDecoder().decode(bytes)));
88
+ if (refusal !== null &&
89
+ /\b(?:API rate limit exceeded|(?:primary|secondary) rate limit)\b/iu.test(refusal.message)) {
90
+ return yield* new PlanCiUnreachable({});
91
+ }
92
+ return yield* new PlanCiRefused({});
93
+ }
94
+ if (response.status !== 200 && response.status !== 302)
95
+ return yield* new PlanCiUnreachable({});
96
+ return response;
97
+ });
98
+ const body = (response, limit) => collectBounded(response.stream, limit).pipe(Effect.catchTags({
99
+ ByteLimitExceeded: () => Effect.fail(new PlanMetadataInvalid({})),
100
+ HttpClientError: () => Effect.fail(new PlanCiUnreachable({}))
101
+ }));
102
+ const json = (path, schema) => request(path).pipe(Effect.flatMap((response) => response.status === 200
103
+ ? body(response, metadataLimit).pipe(Effect.flatMap((bytes) => Schema.decodeUnknownEffect(Schema.fromJsonString(schema))(new TextDecoder().decode(bytes)).pipe(Effect.mapError(() => new PlanMetadataInvalid({})))))
104
+ : Effect.fail(new PlanMetadataInvalid({}))));
105
+ const list = (path, field, schema) => Effect.gen(function* () {
106
+ const all = [];
107
+ for (let page = 1; page <= 10; page++) {
108
+ const envelope = Schema.Struct({ [field]: Schema.Array(schema).check(Schema.isMaxLength(100)) });
109
+ const body = yield* json(`${path}${path.includes("?") ? "&" : "?"}per_page=100&page=${page}`, envelope);
110
+ const rows = body[field];
111
+ if (rows === undefined)
112
+ return yield* new PlanMetadataInvalid({});
113
+ for (const row of rows)
114
+ all.push(row);
115
+ if (rows.length < 100)
116
+ return all;
117
+ }
118
+ return yield* new PlanMetadataInvalid({});
119
+ });
120
+ return PlanGithub.of({
121
+ workflow: (file) => json(`actions/workflows/${file}`, Workflow),
122
+ runs: (file) => list(`actions/workflows/${file}/runs?event=${file === "infra.yml" ? "pull_request" : "workflow_run"}`, "workflow_runs", Run),
123
+ artifacts: () => list("actions/artifacts", "artifacts", Artifact),
124
+ run: (id) => json(`actions/runs/${id}`, Run),
125
+ jobs: (id) => list(`actions/runs/${id}/jobs?filter=latest`, "jobs", Job),
126
+ latestMainSeenByCi: () => json("actions/runs?branch=main&event=push&per_page=1", Schema.Struct({
127
+ workflow_runs: Schema.Array(Schema.Struct({
128
+ head_sha: Sha,
129
+ head_branch: Schema.Literal("main"),
130
+ event: Schema.Literal("push"),
131
+ repository: Schema.Struct({ id: Schema.Literal(1288997422) }),
132
+ head_repository: Schema.Struct({ id: Schema.Literal(1288997422) })
133
+ })).check(Schema.isMaxLength(1))
134
+ })).pipe(Effect.map((body) => body.workflow_runs[0]?.head_sha ?? null)),
135
+ download: Effect.fn("AlchemyConsole.downloadCiPlan")(function* (id) {
136
+ const response = yield* request(`actions/artifacts/${id}/zip`);
137
+ if (response.status !== 302)
138
+ return yield* new PlanMetadataInvalid({});
139
+ const location = response.headers.location;
140
+ if (location === undefined || !URL.canParse(location))
141
+ return yield* new PlanMetadataInvalid({});
142
+ const url = new URL(location);
143
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "" ||
144
+ !(url.hostname.endsWith(".blob.core.windows.net") || url.hostname === "objects.githubusercontent.com")) {
145
+ return yield* new PlanMetadataInvalid({});
146
+ }
147
+ const blob = yield* client.get(location).pipe(Effect.provideService(FetchHttpClient.RequestInit, { redirect: "manual", cache: "no-store" }), Effect.mapError(() => new PlanCiUnreachable({})));
148
+ if (blob.status !== 200)
149
+ return yield* new PlanCiUnreachable({});
150
+ return yield* body(blob, archiveLimit - 1);
151
+ })
152
+ });
153
+ }));
154
+ export const publishedMillis = (artifact) => DateTime.toEpochMillis(artifact.created_at);
155
+ //# sourceMappingURL=github.js.map