@knpkv/alchemy-console 0.1.0 → 0.2.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.
- package/README.md +71 -4
- package/dist/api.d.ts +126 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +62 -0
- package/dist/api.js.map +1 -0
- package/dist/client/bootstrap.js +2834 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +39 -0
- package/dist/main.js.map +1 -0
- package/dist/s3-live.d.ts +7 -2
- package/dist/s3-live.d.ts.map +1 -1
- package/dist/s3-live.js +3 -2
- package/dist/s3-live.js.map +1 -1
- package/dist/server/Bootstrap.d.ts +4 -0
- package/dist/server/Bootstrap.d.ts.map +1 -0
- package/dist/server/Bootstrap.js +12 -0
- package/dist/server/Bootstrap.js.map +1 -0
- package/dist/server/Catalog.d.ts +27 -0
- package/dist/server/Catalog.d.ts.map +1 -0
- package/dist/server/Catalog.js +83 -0
- package/dist/server/Catalog.js.map +1 -0
- package/dist/server/Config.d.ts +17 -0
- package/dist/server/Config.d.ts.map +1 -0
- package/dist/server/Config.js +23 -0
- package/dist/server/Config.js.map +1 -0
- package/dist/server/ControlSocket.d.ts +19 -0
- package/dist/server/ControlSocket.d.ts.map +1 -0
- package/dist/server/ControlSocket.js +147 -0
- package/dist/server/ControlSocket.js.map +1 -0
- package/dist/server/HttpApplication.d.ts +7 -0
- package/dist/server/HttpApplication.d.ts.map +1 -0
- package/dist/server/HttpApplication.js +46 -0
- package/dist/server/HttpApplication.js.map +1 -0
- package/dist/server/Launch.d.ts +7 -0
- package/dist/server/Launch.d.ts.map +1 -0
- package/dist/server/Launch.js +17 -0
- package/dist/server/Launch.js.map +1 -0
- package/dist/server/OwnerSession.d.ts +9 -0
- package/dist/server/OwnerSession.d.ts.map +1 -0
- package/dist/server/OwnerSession.js +27 -0
- package/dist/server/OwnerSession.js.map +1 -0
- package/dist/server/Server.d.ts +6 -0
- package/dist/server/Server.d.ts.map +1 -0
- package/dist/server/Server.js +22 -0
- package/dist/server/Server.js.map +1 -0
- package/dist/server/Sources.d.ts +12 -0
- package/dist/server/Sources.d.ts.map +1 -0
- package/dist/server/Sources.js +118 -0
- package/dist/server/Sources.js.map +1 -0
- package/package.json +17 -6
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Loopback executable boundary: no-arg/login reuse or start; serve stays foreground and print-only. */
|
|
3
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
4
|
+
import { browserLaunch, BrowserOpener } from "@knpkv/browser-pairing/launch";
|
|
5
|
+
import { Effect, Layer, Schema } from "effect";
|
|
6
|
+
import { Command, Flag } from "effect/cli";
|
|
7
|
+
import { FetchHttpClient } from "effect/http";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import process from "node:process";
|
|
10
|
+
import { Catalog } from "./server/Catalog.js";
|
|
11
|
+
import { loadConfig, Port, readPackageVersion, StartupError } from "./server/Config.js";
|
|
12
|
+
import { runConsole } from "./server/Launch.js";
|
|
13
|
+
import { sourceLayer } from "./server/Sources.js";
|
|
14
|
+
import { ssoCredentialsLayer } from "./sso-credentials.js";
|
|
15
|
+
const home = homedir();
|
|
16
|
+
const uid = process.geteuid?.() ?? -1;
|
|
17
|
+
const observations = {
|
|
18
|
+
browser: process.env.BROWSER,
|
|
19
|
+
ci: process.env.CI,
|
|
20
|
+
interactive: process.stdout.isTTY === true
|
|
21
|
+
};
|
|
22
|
+
const flags = {
|
|
23
|
+
port: Flag.Int("port").pipe(Flag.withDefault(3114)),
|
|
24
|
+
noOpen: Flag.Boolean("no-open").pipe(Flag.withDefault(false))
|
|
25
|
+
};
|
|
26
|
+
const run = Effect.fn("AlchemyConsole.run")(function* (port, noOpen, printOnly) {
|
|
27
|
+
yield* Schema.decodeUnknownEffect(Port)(port).pipe(Effect.mapError(() => new StartupError({ reason: "invalid-port" })));
|
|
28
|
+
const config = yield* loadConfig(home).pipe(Effect.mapError(() => new StartupError({ reason: "config" })));
|
|
29
|
+
const options = { directory: config.directory, port, uid };
|
|
30
|
+
return yield* runConsole(options, browserLaunch({ ...observations, noOpen }), printOnly);
|
|
31
|
+
});
|
|
32
|
+
const login = Command.make("login", flags, ({ noOpen, port }) => run(port, noOpen, false));
|
|
33
|
+
const serve = Command.make("serve", flags, ({ noOpen, port }) => run(port, noOpen, true));
|
|
34
|
+
const command = Command.make("alchemy-console", flags, ({ noOpen, port }) => run(port, noOpen, false)).pipe(Command.withDescription("Read-only Alchemy stack API on loopback"), Command.withSubcommands([login, serve]));
|
|
35
|
+
const sources = Layer.unwrap(loadConfig(home).pipe(Effect.mapError(() => new StartupError({ reason: "config" })), Effect.map(sourceLayer))).pipe(Layer.provide(FetchHttpClient.layer), Layer.provide(ssoCredentialsLayer()));
|
|
36
|
+
const services = Layer.merge(Catalog.layer.pipe(Layer.provide(sources)), BrowserOpener.layer).pipe(Layer.provideMerge(NodeServices.layer));
|
|
37
|
+
const cli = readPackageVersion().pipe(Effect.flatMap((version) => Command.run(command, { version })));
|
|
38
|
+
NodeRuntime.runMain(cli.pipe(Effect.withTracerEnabled(false), Effect.provide(services)));
|
|
39
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,wGAAwG;AACxG,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAC5E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,OAAO,MAAM,cAAc,CAAA;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAE1D,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;AACtB,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC,CAAA;AACrC,MAAM,YAAY,GAAG;IACnB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO;IAC5B,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE;IAClB,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI;CAC3C,CAAA;AAED,MAAM,KAAK,GAAG;IACZ,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;CAC9D,CAAA;AAED,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,EAAC,IAAY,EAAE,MAAe,EAAE,SAAkB;IACrG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAChD,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC,CACpE,CAAA;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAA;IAC1G,MAAM,OAAO,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAA;IAC1D,OAAO,KAAK,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,GAAG,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,CAAC,CAAA;AAC1F,CAAC,CAAC,CAAA;AAEF,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;AAC1F,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;AACzF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CACzG,OAAO,CAAC,eAAe,CAAC,yCAAyC,CAAC,EAClE,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CACxC,CAAA;AAED,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAC1B,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CACnB,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EAC7D,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CACxB,CACF,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAA;AAClF,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,CAChG,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,CACvC,CAAA;AACD,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;AAErG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAC1B,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAC/B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CACzB,CAAC,CAAA"}
|
package/dist/s3-live.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { Layer } from "effect";
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
2
|
import * as HttpClient from "effect/http/HttpClient";
|
|
3
3
|
import type { SsoProfile } from "./profiles.js";
|
|
4
|
-
import { S3Reader } from "./s3-reader.js";
|
|
4
|
+
import { type S3Bucket, S3Reader, S3ReadError, type VersionCursor } from "./s3-reader.js";
|
|
5
5
|
import { SsoCredentials } from "./sso-credentials.js";
|
|
6
6
|
/** Independent read-only adapter. ExpectedBucketOwner binds requests to the selected SSO account. */
|
|
7
|
+
export declare const makeS3Reader: (profile: SsoProfile) => Effect.Effect<{
|
|
8
|
+
readonly buckets: (cursor: string | null) => Effect.Effect<import("./s3-reader.js").S3BucketPage, S3ReadError>;
|
|
9
|
+
readonly versions: (bucket: S3Bucket, prefix: string, cursor: VersionCursor | null) => Effect.Effect<import("./s3-reader.js").S3VersionPage, S3ReadError>;
|
|
10
|
+
readonly resource: (bucket: S3Bucket, key: string, versionId: string) => Effect.Effect<import("./schema.ts").ResourceSummary | null, S3ReadError | import("./schema.ts").StateReadError>;
|
|
11
|
+
}, never, HttpClient.HttpClient | SsoCredentials>;
|
|
7
12
|
export declare const s3ReaderLayer: (profile: SsoProfile) => Layer.Layer<S3Reader, never, HttpClient.HttpClient | SsoCredentials>;
|
|
8
13
|
//# sourceMappingURL=s3-live.d.ts.map
|
package/dist/s3-live.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3-live.d.ts","sourceRoot":"","sources":["../src/s3-live.ts"],"names":[],"mappings":"AAcA,OAAO,
|
|
1
|
+
{"version":3,"file":"s3-live.d.ts","sourceRoot":"","sources":["../src/s3-live.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAa,MAAM,QAAQ,CAAA;AACjD,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,KAAK,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAkB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACzG,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAsBrD,qGAAqG;AACrG,eAAO,MAAM,YAAY;;;;iDAiHvB,CAAA;AAEF,eAAO,MAAM,aAAa,YAAa,UAAU,yEAAkD,CAAA"}
|
package/dist/s3-live.js
CHANGED
|
@@ -22,7 +22,7 @@ const providerError = (error) => {
|
|
|
22
22
|
return new S3ReadError({ reason: "provider" });
|
|
23
23
|
};
|
|
24
24
|
/** Independent read-only adapter. ExpectedBucketOwner binds requests to the selected SSO account. */
|
|
25
|
-
export const
|
|
25
|
+
export const makeS3Reader = Effect.fn("AlchemyConsole.makeS3Reader")(function* (profile) {
|
|
26
26
|
const client = yield* HttpClient.HttpClient;
|
|
27
27
|
const sso = yield* SsoCredentials;
|
|
28
28
|
const call = Effect.fn("AlchemyConsole.callS3")(function* (effect, region) {
|
|
@@ -107,5 +107,6 @@ export const s3ReaderLayer = (profile) => Layer.effect(S3Reader, Effect.gen(func
|
|
|
107
107
|
return yield* decodeResource(text);
|
|
108
108
|
})
|
|
109
109
|
});
|
|
110
|
-
})
|
|
110
|
+
});
|
|
111
|
+
export const s3ReaderLayer = (profile) => Layer.effect(S3Reader, makeS3Reader(profile));
|
|
111
112
|
//# sourceMappingURL=s3-live.js.map
|
package/dist/s3-live.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3-live.js","sourceRoot":"","sources":["../src/s3-live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAA;AACpD,OAAO,EAEL,SAAS,EAET,WAAW,EAGX,kBAAkB,EAGnB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAA;AAEpD,OAAO,EAAiB,QAAQ,EAAE,WAAW,EAAsC,MAAM,gBAAgB,CAAA;AACzG,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAI3C,MAAM,aAAa,GAAG,CAAC,KAAsB,EAAe,EAAE;IAC5D,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa;QAAE,OAAO,KAAK,CAAA;IAC9C,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAA;IAC1E,IAAI,GAAG,KAAK,cAAc,IAAI,GAAG,KAAK,uBAAuB,EAAE,CAAC;QAC9D,OAAO,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC9C,CAAC;IACD,IAAI,CAAC,WAAW,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;IACjH,IACE,CAAC,cAAc,EAAE,uBAAuB,EAAE,cAAc,EAAE,oBAAoB,EAAE,uBAAuB,CAAC,CAAC,QAAQ,CAC/G,GAAG,CACJ,EACD,CAAC;QACD,OAAO,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAA;IACtD,CAAC;IACD,OAAO,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;AAChD,CAAC,CAAA;AAED,qGAAqG;AACrG,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"s3-live.js","sourceRoot":"","sources":["../src/s3-live.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Layer, Path } from "effect";
|
|
2
|
+
import { HttpRouter } from "effect/http";
|
|
3
|
+
export declare const BootstrapRoutes: Layer.Layer<never, import("effect/PlatformError").BadArgument | import("effect/PlatformError").PlatformError, import("effect/FileSystem").FileSystem | import("effect/http/HttpPlatform").HttpPlatform | HttpRouter.HttpRouter | Path.Path>;
|
|
4
|
+
//# sourceMappingURL=Bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Bootstrap.d.ts","sourceRoot":"","sources":["../../src/server/Bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAwC,MAAM,aAAa,CAAA;AAM9E,eAAO,MAAM,eAAe,6OAU3B,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Effect, Layer, Path } from "effect";
|
|
2
|
+
import { HttpRouter, HttpServerResponse, HttpStaticServer } from "effect/http";
|
|
3
|
+
/** Auth handoff only. The design-backed stack UI will replace the JSON navigation in a later PR. */
|
|
4
|
+
const document = `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Alchemy console</title><body><main><p id="status">Signing in…</p><noscript>Enable JavaScript to sign in.</noscript></main><script type="module" src="/bootstrap.js"></script></body></html>`;
|
|
5
|
+
export const BootstrapRoutes = Layer.mergeAll(HttpRouter.add("GET", "/", HttpServerResponse.html(document)), HttpRouter.add("GET", "/favicon.ico", HttpServerResponse.empty({ status: 204 })), Layer.unwrap(Effect.gen(function* () {
|
|
6
|
+
const path = yield* Path.Path;
|
|
7
|
+
return HttpStaticServer.layer({
|
|
8
|
+
root: yield* path.fromFileUrl(new URL("../../dist/client", import.meta.url)),
|
|
9
|
+
cacheControl: "no-store"
|
|
10
|
+
});
|
|
11
|
+
})));
|
|
12
|
+
//# sourceMappingURL=Bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../../src/server/Bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE9E,oGAAoG;AACpG,MAAM,QAAQ,GACZ,4SAA4S,CAAA;AAE9S,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC,QAAQ,CAC3C,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAC7D,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAChF,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;IAC7B,OAAO,gBAAgB,CAAC,KAAK,CAAC;QAC5B,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,mBAAmB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5E,YAAY,EAAE,UAAU;KACzB,CAAC,CAAA;AACJ,CAAC,CAAC,CAAC,CACJ,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Context, Crypto, Effect, Layer } from "effect";
|
|
2
|
+
import { CatalogReadError, StackNotFound, StackSummary } from "../api.js";
|
|
3
|
+
import type { DiscoveryIssue, StackDetail, StackId, StackList } from "../api.js";
|
|
4
|
+
import { type StackState } from "../schema.js";
|
|
5
|
+
/** Server-private locator identity and read closure. Never serialize or retain in logs or browser storage. */
|
|
6
|
+
export interface DiscoveredStack {
|
|
7
|
+
readonly key: string;
|
|
8
|
+
readonly summary: Omit<StackSummary, "id">;
|
|
9
|
+
readonly read: Effect.Effect<StackState, CatalogReadError>;
|
|
10
|
+
}
|
|
11
|
+
declare const StackSource_base: Context.ServiceClass<StackSource, "@knpkv/alchemy-console/StackSource", {
|
|
12
|
+
readonly discover: Effect.Effect<{
|
|
13
|
+
readonly stacks: ReadonlyArray<DiscoveredStack>;
|
|
14
|
+
readonly issues: ReadonlyArray<DiscoveryIssue>;
|
|
15
|
+
}, CatalogReadError>;
|
|
16
|
+
}>;
|
|
17
|
+
export declare class StackSource extends StackSource_base {
|
|
18
|
+
}
|
|
19
|
+
declare const Catalog_base: Context.ServiceClass<Catalog, "@knpkv/alchemy-console/Catalog", {
|
|
20
|
+
readonly list: Effect.Effect<StackList, CatalogReadError>;
|
|
21
|
+
readonly get: (id: StackId) => Effect.Effect<StackDetail, StackNotFound | CatalogReadError>;
|
|
22
|
+
}>;
|
|
23
|
+
export declare class Catalog extends Catalog_base {
|
|
24
|
+
static readonly layer: Layer.Layer<Catalog, never, Crypto.Crypto | StackSource>;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=Catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Catalog.d.ts","sourceRoot":"","sources":["../../src/server/Catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAA0B,MAAM,QAAQ,CAAA;AAC/E,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAChF,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAEpE,8GAA8G;AAC9G,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;CAC3D;;uBAGoB,MAAM,CAAC,MAAM,CAAC;QAC/B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,CAAA;QAC/C,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;KAC/C,EAAE,gBAAgB,CAAC;;AAJtB,qBAAa,WAAY,SAAQ,gBAKS;CAAG;;mBAG5B,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,gBAAgB,CAAC;kBAC3C,CAAC,EAAE,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,aAAa,GAAG,gBAAgB,CAAC;;AAF7F,qBAAa,OAAQ,SAAQ,YAGS;IACpC,MAAM,CAAC,QAAQ,CAAC,KAAK,2DAqEpB;CACF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Context, Crypto, Effect, Layer, Ref, Schema, Semaphore } from "effect";
|
|
2
|
+
import { CatalogReadError, StackNotFound, StackSummary } from "../api.js";
|
|
3
|
+
import {} from "../schema.js";
|
|
4
|
+
export class StackSource extends Context.Service()("@knpkv/alchemy-console/StackSource") {
|
|
5
|
+
}
|
|
6
|
+
export class Catalog extends Context.Service()("@knpkv/alchemy-console/Catalog") {
|
|
7
|
+
static layer = Layer.effect(Catalog, Effect.gen(function* () {
|
|
8
|
+
const source = yield* StackSource;
|
|
9
|
+
const cryptoService = yield* Crypto.Crypto;
|
|
10
|
+
const entries = yield* Ref.make(new Map());
|
|
11
|
+
const lock = yield* Semaphore.make(1);
|
|
12
|
+
const list = Effect.fn("AlchemyConsole.catalogList")(function* () {
|
|
13
|
+
const discovered = yield* source.discover;
|
|
14
|
+
if (discovered.stacks.length > 1000 || discovered.issues.length > 1000) {
|
|
15
|
+
return yield* new CatalogReadError({ reason: "limit" });
|
|
16
|
+
}
|
|
17
|
+
const current = yield* Ref.get(entries);
|
|
18
|
+
const previous = new Map([...current].map(([id, entry]) => [entry.key, id]));
|
|
19
|
+
const next = new Map();
|
|
20
|
+
const keys = new Set();
|
|
21
|
+
const summaries = [];
|
|
22
|
+
for (const entry of discovered.stacks) {
|
|
23
|
+
if (keys.has(entry.key))
|
|
24
|
+
return yield* new CatalogReadError({ reason: "invalid-source" });
|
|
25
|
+
keys.add(entry.key);
|
|
26
|
+
const id = previous.get(entry.key) ?? (yield* cryptoService.randomUUIDv4.pipe(Effect.mapError(() => new CatalogReadError({ reason: "io" }))));
|
|
27
|
+
const summary = yield* Schema.decodeUnknownEffect(StackSummary)({
|
|
28
|
+
id,
|
|
29
|
+
app: entry.summary.app,
|
|
30
|
+
stage: entry.summary.stage,
|
|
31
|
+
backend: entry.summary.backend,
|
|
32
|
+
alchemyVersion: entry.summary.alchemyVersion,
|
|
33
|
+
lastDeploy: entry.summary.lastDeploy,
|
|
34
|
+
resourceCount: entry.summary.resourceCount
|
|
35
|
+
}).pipe(Effect.mapError(() => new CatalogReadError({ reason: "invalid-source" })));
|
|
36
|
+
next.set(id, entry);
|
|
37
|
+
summaries.push(summary);
|
|
38
|
+
}
|
|
39
|
+
const issues = discovered.issues.map((issue) => ({ source: issue.source, reason: issue.reason }));
|
|
40
|
+
yield* Ref.set(entries, next);
|
|
41
|
+
return { stacks: summaries, issues };
|
|
42
|
+
}, lock.withPermit, Effect.timeoutOrElse({
|
|
43
|
+
duration: "2 minutes",
|
|
44
|
+
orElse: () => Effect.fail(new CatalogReadError({ reason: "limit" }))
|
|
45
|
+
}));
|
|
46
|
+
const get = Effect.fn("AlchemyConsole.catalogGet")(function* (id) {
|
|
47
|
+
const entry = (yield* Ref.get(entries)).get(id);
|
|
48
|
+
if (entry === undefined)
|
|
49
|
+
return yield* new StackNotFound({});
|
|
50
|
+
const state = yield* entry.read;
|
|
51
|
+
if (state.app !== entry.summary.app || state.stage !== entry.summary.stage ||
|
|
52
|
+
state.backend !== entry.summary.backend)
|
|
53
|
+
return yield* new CatalogReadError({ reason: "invalid-source" });
|
|
54
|
+
if (state.resources.length > 50_000)
|
|
55
|
+
return yield* new CatalogReadError({ reason: "limit" });
|
|
56
|
+
return {
|
|
57
|
+
id,
|
|
58
|
+
state: {
|
|
59
|
+
app: state.app,
|
|
60
|
+
stage: state.stage,
|
|
61
|
+
backend: state.backend,
|
|
62
|
+
alchemyVersion: state.alchemyVersion,
|
|
63
|
+
lastDeploy: state.lastDeploy,
|
|
64
|
+
resources: state.resources.map(publicResource)
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
return Catalog.of({ list: list(), get });
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
/** Explicit projection prevents a private reader's extra fields from reaching a transport encoder. */
|
|
72
|
+
const publicResource = (resource) => ({
|
|
73
|
+
fqn: resource.fqn,
|
|
74
|
+
logicalId: resource.logicalId,
|
|
75
|
+
resourceType: resource.resourceType,
|
|
76
|
+
status: resource.status,
|
|
77
|
+
parent: resource.parent,
|
|
78
|
+
providerId: resource.providerId,
|
|
79
|
+
account: resource.account,
|
|
80
|
+
region: resource.region,
|
|
81
|
+
consoleUrl: resource.consoleUrl
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=Catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Catalog.js","sourceRoot":"","sources":["../../src/server/Catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Config, Effect, Path, Schema } from "effect";
|
|
2
|
+
export declare const Port: Schema.Int;
|
|
3
|
+
declare const StartupError_base: Schema.Class<StartupError, Schema.TaggedStruct<"StartupError", {
|
|
4
|
+
readonly reason: Schema.Literals<readonly ["config", "invalid-port", "package"]>;
|
|
5
|
+
}>, import("effect/Cause").YieldableError>;
|
|
6
|
+
export declare class StartupError extends StartupError_base {
|
|
7
|
+
}
|
|
8
|
+
/** Resolve the manifest beside the built executable; release version edits need no source change. */
|
|
9
|
+
export declare const readPackageVersion: () => Effect.Effect<string, StartupError, import("effect/FileSystem").FileSystem | Path.Path>;
|
|
10
|
+
/** Read paths once at executable startup. None of this configuration crosses HTTP or browser storage. */
|
|
11
|
+
export declare const loadConfig: (home: string) => Effect.Effect<{
|
|
12
|
+
root: string;
|
|
13
|
+
awsConfigFile: string;
|
|
14
|
+
directory: string;
|
|
15
|
+
}, Config.ConfigError, Path.Path>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=Config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../src/server/Config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAGrD,eAAO,MAAM,IAAI,YAAwE,CAAA;;;;AAEzF,qBAAa,YAAa,SAAQ,iBAEhC;CAAG;AAEL,qGAAqG;AACrG,eAAO,MAAM,kBAAkB,+FAY7B,CAAA;AAEF,yGAAyG;AACzG,eAAO,MAAM,UAAU;;;;iCASrB,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Config, Effect, Path, Schema } from "effect";
|
|
2
|
+
import { readText } from "../internal/files.js";
|
|
3
|
+
export const Port = Schema.Int.check(Schema.isBetween({ minimum: 1024, maximum: 65535 }));
|
|
4
|
+
export class StartupError extends Schema.TaggedError()("StartupError", {
|
|
5
|
+
reason: Schema.Literals(["config", "invalid-port", "package"])
|
|
6
|
+
}) {
|
|
7
|
+
}
|
|
8
|
+
/** Resolve the manifest beside the built executable; release version edits need no source change. */
|
|
9
|
+
export const readPackageVersion = Effect.fn("AlchemyConsole.readPackageVersion")(function* () {
|
|
10
|
+
const path = yield* Path.Path;
|
|
11
|
+
const file = yield* path.fromFileUrl(new URL("../../package.json", import.meta.url)).pipe(Effect.mapError(() => new StartupError({ reason: "package" })));
|
|
12
|
+
return yield* readText(file).pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Struct({ version: Schema.NonEmptyString })))), Effect.map(({ version }) => version), Effect.mapError(() => new StartupError({ reason: "package" })));
|
|
13
|
+
});
|
|
14
|
+
/** Read paths once at executable startup. None of this configuration crosses HTTP or browser storage. */
|
|
15
|
+
export const loadConfig = Effect.fn("AlchemyConsole.loadConfig")(function* (home) {
|
|
16
|
+
const path = yield* Path.Path;
|
|
17
|
+
return {
|
|
18
|
+
root: yield* Config.String("ALCHEMY_CONSOLE_ROOT").pipe(Config.withDefault(path.join(home, "Work", "dev"))),
|
|
19
|
+
awsConfigFile: yield* Config.String("AWS_CONFIG_FILE").pipe(Config.withDefault(path.join(home, ".aws", "config"))),
|
|
20
|
+
directory: yield* Config.String("ALCHEMY_CONSOLE_HOME").pipe(Config.withDefault(path.join(home, ".cache", "alchemy-console")))
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=Config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Config.js","sourceRoot":"","sources":["../../src/server/Config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;AAEzF,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,WAAW,EAAgB,CAAC,cAAc,EAAE;IACnF,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;CAC/D,CAAC;CAAG;AAEL,qGAAqG;AACrG,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,EAAE,CAAC,mCAAmC,CAAC,CAAC,QAAQ,CAAC;IACxF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;IAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CACvF,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAC/D,CAAA;IACD,OAAO,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAC/B,MAAM,CAAC,OAAO,CACZ,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CACrG,EACD,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,EACpC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAC/D,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,yGAAyG;AACzG,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAC,QAAQ,CAAC,EAAC,IAAY;IACrF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;IAC7B,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3G,aAAa,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QAClH,SAAS,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAC1D,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CACjE;KACF,CAAA;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { OwnerSessionService } from "@knpkv/browser-pairing/owner-session";
|
|
2
|
+
import { Effect, FileSystem, Option, Path, Schema } from "effect";
|
|
3
|
+
declare const ControlError_base: Schema.Class<ControlError, Schema.TaggedStruct<"ControlError", {
|
|
4
|
+
readonly reason: Schema.Literals<readonly ["unsafe", "io", "occupied", "path-limit", "invalid-reply", "timeout", "unsupported-platform"]>;
|
|
5
|
+
}>, import("effect/Cause").YieldableError>;
|
|
6
|
+
export declare class ControlError extends ControlError_base {
|
|
7
|
+
}
|
|
8
|
+
/** Paths are server-private. The effective UID is read once at the executable boundary. */
|
|
9
|
+
export interface ControlOptions {
|
|
10
|
+
readonly directory: string;
|
|
11
|
+
readonly port: number;
|
|
12
|
+
readonly uid: number;
|
|
13
|
+
}
|
|
14
|
+
/** Bind atomically; refuse all pre-existing entries. Never unlink a possibly live server during startup. */
|
|
15
|
+
export declare const controlSocket: (options: ControlOptions, session: OwnerSessionService, listening: Effect.Effect<void, never, never>) => Effect.Effect<undefined, ControlError, FileSystem.FileSystem | Path.Path | import("effect/Scope").Scope>;
|
|
16
|
+
/** None means no socket exists. Unsafe paths, stale sockets and bad replies propagate without starting another server. */
|
|
17
|
+
export declare const requestLoginUrl: (options: ControlOptions) => Effect.Effect<Option.Option<string>, ControlError, FileSystem.FileSystem | Path.Path>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=ControlSocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ControlSocket.d.ts","sourceRoot":"","sources":["../../src/server/ControlSocket.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAA;AAC/E,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAkB,MAAM,EAAE,MAAM,QAAQ,CAAA;;;;AAKjF,qBAAa,YAAa,SAAQ,iBAUhC;CAAG;AAEL,2FAA2F;AAC3F,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AA6ED,4GAA4G;AAC5G,eAAO,MAAM,aAAa,mNA4CxB,CAAA;AAMF,0HAA0H;AAC1H,eAAO,MAAM,eAAe,oHA0B1B,CAAA"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/** Owner-only local login channel. Credential URLs never enter diagnostics, logs, or telemetry. */
|
|
2
|
+
import { NodeSocket, NodeSocketServer } from "@effect/platform-node";
|
|
3
|
+
import { PairingCode } from "@knpkv/browser-pairing";
|
|
4
|
+
import { Effect, FileSystem, Option, Path, Predicate, Ref, Schema } from "effect";
|
|
5
|
+
import { mintBootstrapUrl } from "./OwnerSession.js";
|
|
6
|
+
export class ControlError extends Schema.TaggedError()("ControlError", {
|
|
7
|
+
reason: Schema.Literals([
|
|
8
|
+
"unsafe",
|
|
9
|
+
"io",
|
|
10
|
+
"occupied",
|
|
11
|
+
"path-limit",
|
|
12
|
+
"invalid-reply",
|
|
13
|
+
"timeout",
|
|
14
|
+
"unsupported-platform"
|
|
15
|
+
])
|
|
16
|
+
}) {
|
|
17
|
+
}
|
|
18
|
+
const errnoSchema = Schema.Struct({ code: Schema.String });
|
|
19
|
+
const errno = (error) => Option.getOrUndefined(Schema.decodeUnknownOption(errnoSchema)(error._tag === "PlatformError" ? error.cause : error.reason.cause))?.code;
|
|
20
|
+
const unsafe = () => new ControlError({ reason: "unsafe" });
|
|
21
|
+
const io = () => new ControlError({ reason: "io" });
|
|
22
|
+
/** Check the final component without following a symlink. EINVAL alone proves it is not a link. */
|
|
23
|
+
const refuseLink = (fs, path) => fs.readLink(path).pipe(Effect.matchEffect({
|
|
24
|
+
onSuccess: () => Effect.fail(unsafe()),
|
|
25
|
+
onFailure: (error) => error.reason._tag === "NotFound" || errno(error) === "EINVAL" ?
|
|
26
|
+
Effect.void :
|
|
27
|
+
Effect.fail(unsafe())
|
|
28
|
+
}));
|
|
29
|
+
const location = Effect.fnUntraced(function* (options) {
|
|
30
|
+
const path = yield* Path.Path;
|
|
31
|
+
if (path.sep !== "/" || options.uid < 0)
|
|
32
|
+
return yield* new ControlError({ reason: "unsupported-platform" });
|
|
33
|
+
const socket = path.join(options.directory, `serve-${options.port}.sock`);
|
|
34
|
+
if (new TextEncoder().encode(socket).byteLength > 103)
|
|
35
|
+
return yield* new ControlError({ reason: "path-limit" });
|
|
36
|
+
return socket;
|
|
37
|
+
});
|
|
38
|
+
/** Existing directories must already be owned by this user and 0700; never tighten someone else's directory. */
|
|
39
|
+
const inspectDirectory = Effect.fnUntraced(function* (options, create) {
|
|
40
|
+
const fs = yield* FileSystem.FileSystem;
|
|
41
|
+
yield* refuseLink(fs, options.directory);
|
|
42
|
+
const present = yield* fs.exists(options.directory).pipe(Effect.mapError(io));
|
|
43
|
+
if (!present) {
|
|
44
|
+
if (!create)
|
|
45
|
+
return false;
|
|
46
|
+
yield* fs.makeDirectory(options.directory, { recursive: true, mode: 0o700 }).pipe(Effect.mapError(io));
|
|
47
|
+
}
|
|
48
|
+
const info = yield* fs.stat(options.directory).pipe(Effect.mapError(io));
|
|
49
|
+
if (info.type !== "Directory" || Option.getOrUndefined(info.uid) !== options.uid || (info.mode & 0o777) !== 0o700)
|
|
50
|
+
return yield* unsafe();
|
|
51
|
+
return true;
|
|
52
|
+
});
|
|
53
|
+
const inspectSocket = Effect.fnUntraced(function* (options, socket) {
|
|
54
|
+
const fs = yield* FileSystem.FileSystem;
|
|
55
|
+
yield* refuseLink(fs, socket);
|
|
56
|
+
if (!(yield* fs.exists(socket).pipe(Effect.mapError(io))))
|
|
57
|
+
return undefined;
|
|
58
|
+
const info = yield* fs.stat(socket).pipe(Effect.mapError(io));
|
|
59
|
+
if (info.type !== "Socket" || Option.getOrUndefined(info.uid) !== options.uid || (info.mode & 0o777) !== 0o600) {
|
|
60
|
+
return yield* unsafe();
|
|
61
|
+
}
|
|
62
|
+
const inode = Option.getOrUndefined(info.ino);
|
|
63
|
+
if (inode === undefined)
|
|
64
|
+
return yield* unsafe();
|
|
65
|
+
return inode;
|
|
66
|
+
});
|
|
67
|
+
/** Bound both bytes and time, including peers that disconnect or never finish a line. */
|
|
68
|
+
const readLine = Effect.fnUntraced(function* (reader) {
|
|
69
|
+
const decoder = new TextDecoder();
|
|
70
|
+
let text = "";
|
|
71
|
+
let bytes = 0;
|
|
72
|
+
while (!text.includes("\n")) {
|
|
73
|
+
const chunks = yield* reader.pull.pipe(Effect.mapError(io));
|
|
74
|
+
for (const chunk of chunks) {
|
|
75
|
+
bytes += Predicate.isString(chunk) ? new TextEncoder().encode(chunk).byteLength : chunk.byteLength;
|
|
76
|
+
if (bytes > 4096)
|
|
77
|
+
return yield* new ControlError({ reason: "invalid-reply" });
|
|
78
|
+
text += Predicate.isString(chunk) ? chunk : decoder.decode(chunk, { stream: true });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (text.indexOf("\n") !== text.length - 1)
|
|
82
|
+
return yield* new ControlError({ reason: "invalid-reply" });
|
|
83
|
+
return text.slice(0, -1);
|
|
84
|
+
});
|
|
85
|
+
/** Bind atomically; refuse all pre-existing entries. Never unlink a possibly live server during startup. */
|
|
86
|
+
export const controlSocket = Effect.fn("AlchemyConsole.controlSocket")(function* (options, session, listening) {
|
|
87
|
+
const fs = yield* FileSystem.FileSystem;
|
|
88
|
+
const socketPath = yield* location(options);
|
|
89
|
+
yield* inspectDirectory(options, true);
|
|
90
|
+
yield* refuseLink(fs, socketPath);
|
|
91
|
+
if (yield* fs.exists(socketPath).pipe(Effect.mapError(io)))
|
|
92
|
+
return yield* new ControlError({ reason: "occupied" });
|
|
93
|
+
const identity = yield* Ref.make(undefined);
|
|
94
|
+
const server = yield* NodeSocketServer.make({ path: socketPath }).pipe(Effect.mapError((error) => new ControlError({ reason: errno(error) === "EADDRINUSE" ? "occupied" : "io" })));
|
|
95
|
+
// Register cleanup immediately after successful bind; identity checks prevent deleting a replacement.
|
|
96
|
+
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
97
|
+
const inode = yield* Ref.get(identity);
|
|
98
|
+
const current = yield* fs.stat(socketPath);
|
|
99
|
+
if (inode !== undefined && Option.getOrUndefined(current.ino) === inode && current.type === "Socket") {
|
|
100
|
+
yield* refuseLink(fs, socketPath);
|
|
101
|
+
yield* fs.remove(socketPath);
|
|
102
|
+
}
|
|
103
|
+
}).pipe(Effect.catch(() => Effect.logWarning("Alchemy console control cleanup failed; a stale socket may need removal."))));
|
|
104
|
+
const bound = yield* fs.stat(socketPath).pipe(Effect.mapError(io));
|
|
105
|
+
yield* Ref.set(identity, Option.getOrUndefined(bound.ino));
|
|
106
|
+
yield* fs.chmod(socketPath, 0o600).pipe(Effect.mapError(io));
|
|
107
|
+
yield* inspectSocket(options, socketPath);
|
|
108
|
+
const answer = (socket) => Effect.scoped(Effect.gen(function* () {
|
|
109
|
+
const reader = yield* socket.reader;
|
|
110
|
+
const writer = yield* socket.writer;
|
|
111
|
+
if ((yield* readLine(reader)) !== "mint")
|
|
112
|
+
return;
|
|
113
|
+
yield* listening;
|
|
114
|
+
const url = yield* mintBootstrapUrl(session);
|
|
115
|
+
yield* writer.write(`${JSON.stringify({ url })}\n`);
|
|
116
|
+
})).pipe(Effect.timeout("5 seconds"), Effect.catch(() => Effect.logWarning("Alchemy console control exchange failed.")));
|
|
117
|
+
yield* Effect.forkScoped(server.run(answer).pipe(Effect.mapError(io)));
|
|
118
|
+
});
|
|
119
|
+
/** Credential-bearing response: one loopback URL containing a single-use PairingCode. */
|
|
120
|
+
const LoginReply = Schema.fromJsonString(Schema.Struct({ url: Schema.String }));
|
|
121
|
+
const isCode = Schema.is(PairingCode);
|
|
122
|
+
/** None means no socket exists. Unsafe paths, stale sockets and bad replies propagate without starting another server. */
|
|
123
|
+
export const requestLoginUrl = Effect.fn("AlchemyConsole.requestLoginUrl")(function* (options) {
|
|
124
|
+
const socketPath = yield* location(options);
|
|
125
|
+
if (!(yield* inspectDirectory(options, false)))
|
|
126
|
+
return Option.none();
|
|
127
|
+
if ((yield* inspectSocket(options, socketPath)) === undefined)
|
|
128
|
+
return Option.none();
|
|
129
|
+
const reply = yield* Effect.scoped(Effect.gen(function* () {
|
|
130
|
+
const socket = yield* NodeSocket.makeNet({ path: socketPath });
|
|
131
|
+
const reader = yield* socket.reader;
|
|
132
|
+
const writer = yield* socket.writer;
|
|
133
|
+
yield* writer.write("mint\n");
|
|
134
|
+
return yield* readLine(reader);
|
|
135
|
+
})).pipe(Effect.mapError(io), Effect.timeoutOrElse({ duration: "5 seconds", orElse: () => Effect.fail(new ControlError({ reason: "timeout" })) }));
|
|
136
|
+
const decoded = yield* Schema.decodeUnknownEffect(LoginReply)(reply).pipe(Effect.mapError(() => new ControlError({ reason: "invalid-reply" })));
|
|
137
|
+
if (!URL.canParse(decoded.url))
|
|
138
|
+
return yield* new ControlError({ reason: "invalid-reply" });
|
|
139
|
+
const url = new URL(decoded.url);
|
|
140
|
+
const origin = `http://127.0.0.1:${options.port}`;
|
|
141
|
+
const prefix = "#bootstrap_token=";
|
|
142
|
+
if (url.origin !== origin || url.pathname !== "/" || url.search !== "" || url.username !== "" ||
|
|
143
|
+
url.password !== "" || !url.hash.startsWith(prefix) || !isCode(url.hash.slice(prefix.length)))
|
|
144
|
+
return yield* new ControlError({ reason: "invalid-reply" });
|
|
145
|
+
return Option.some(decoded.url);
|
|
146
|
+
});
|
|
147
|
+
//# sourceMappingURL=ControlSocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ControlSocket.js","sourceRoot":"","sources":["../../src/server/ControlSocket.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
|
|
2
|
+
import { Layer } from "effect";
|
|
3
|
+
import { HttpRouter } from "effect/http";
|
|
4
|
+
import { Catalog } from "./Catalog.js";
|
|
5
|
+
/** Listener, source readers and owner session belong to the executable's runtime layer. */
|
|
6
|
+
export declare const application: Layer.Layer<never, import("effect/PlatformError").BadArgument | import("effect/PlatformError").PlatformError, Catalog | import("effect/FileSystem").FileSystem | import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpRouter.HttpRouter | OwnerSession.OwnerSession | import("effect/Path").Path | HttpRouter.Request<"Error", import("effect/http/HttpBody").HttpBodyError>>;
|
|
7
|
+
//# sourceMappingURL=HttpApplication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpApplication.d.ts","sourceRoot":"","sources":["../../src/server/HttpApplication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,UAAU,EAAyC,MAAM,aAAa,CAAA;AAI/E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AA0CtC,2FAA2F;AAC3F,eAAO,MAAM,WAAW,uZAKvB,CAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
|
+
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
|
|
4
|
+
import { HttpApiBuilder } from "effect/http-api";
|
|
5
|
+
import { AlchemyConsoleApi } from "../api.js";
|
|
6
|
+
import { BootstrapRoutes } from "./Bootstrap.js";
|
|
7
|
+
import { Catalog } from "./Catalog.js";
|
|
8
|
+
import { ownerAuthLayer } from "./OwnerSession.js";
|
|
9
|
+
const handlers = HttpApiBuilder.group(AlchemyConsoleApi, "stacks", (handlers) => Effect.gen(function* () {
|
|
10
|
+
const catalog = yield* Catalog;
|
|
11
|
+
return handlers.handle("list", () => catalog.list).handle("get", ({ params }) => catalog.get(params.id));
|
|
12
|
+
}));
|
|
13
|
+
/** Refuse unsafe/unmatched API requests before handlers; even refusals and missing routes cannot be cached. */
|
|
14
|
+
const boundary = Layer.unwrap(Effect.gen(function* () {
|
|
15
|
+
const session = yield* OwnerSession.OwnerSession;
|
|
16
|
+
const host = new URL(session.authorityOrigin).host;
|
|
17
|
+
return HttpRouter.middleware((httpEffect) => Effect.gen(function* () {
|
|
18
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
19
|
+
if (request.headers.host !== host)
|
|
20
|
+
return HttpServerResponse.empty({ status: 403 });
|
|
21
|
+
if (!URL.canParse(request.url, session.authorityOrigin))
|
|
22
|
+
return HttpServerResponse.empty({ status: 400 });
|
|
23
|
+
const url = new URL(request.url, session.authorityOrigin);
|
|
24
|
+
if (url.origin !== session.authorityOrigin)
|
|
25
|
+
return HttpServerResponse.empty({ status: 403 });
|
|
26
|
+
const pathname = url.pathname;
|
|
27
|
+
if (pathname === "/api" || pathname.startsWith("/api/")) {
|
|
28
|
+
const authorized = yield* Effect.result(session.authorizeHttp(request.cookies[session.cookieName] ?? ""));
|
|
29
|
+
if (authorized._tag === "Failure") {
|
|
30
|
+
return HttpServerResponse.empty({
|
|
31
|
+
status: authorized.failure._tag === "OwnerSessionUnauthorizedError" ? 401 : 403
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return yield* httpEffect;
|
|
36
|
+
}).pipe(Effect.map((response) => response.pipe(HttpServerResponse.setHeaders({
|
|
37
|
+
"cache-control": "private, no-store",
|
|
38
|
+
"referrer-policy": "no-referrer",
|
|
39
|
+
"x-content-type-options": "nosniff",
|
|
40
|
+
"x-frame-options": "DENY",
|
|
41
|
+
"content-security-policy": "default-src 'none'; script-src 'self'; connect-src 'self'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'"
|
|
42
|
+
})))), { global: true });
|
|
43
|
+
}));
|
|
44
|
+
/** Listener, source readers and owner session belong to the executable's runtime layer. */
|
|
45
|
+
export const application = Layer.mergeAll(HttpApiBuilder.layer(AlchemyConsoleApi).pipe(Layer.provide(handlers), Layer.provide(ownerAuthLayer)), OwnerSession.BootstrapRouter, BootstrapRoutes, boundary);
|
|
46
|
+
//# sourceMappingURL=HttpApplication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpApplication.js","sourceRoot":"","sources":["../../src/server/HttpApplication.ts"],"names":[],"mappings":"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"}
|