@knpkv/alchemy-console 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +185 -2
- 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/discovery.d.ts +12 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +39 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/files.d.ts +9 -0
- package/dist/internal/files.d.ts.map +1 -0
- package/dist/internal/files.js +34 -0
- package/dist/internal/files.js.map +1 -0
- package/dist/local-state.d.ts +8 -0
- package/dist/local-state.d.ts.map +1 -0
- package/dist/local-state.js +53 -0
- package/dist/local-state.js.map +1 -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/profiles.d.ts +24 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +82 -0
- package/dist/profiles.js.map +1 -0
- package/dist/s3-live.d.ts +13 -0
- package/dist/s3-live.d.ts.map +1 -0
- package/dist/s3-live.js +112 -0
- package/dist/s3-live.js.map +1 -0
- package/dist/s3-reader.d.ts +46 -0
- package/dist/s3-reader.d.ts.map +1 -0
- package/dist/s3-reader.js +10 -0
- package/dist/s3-reader.js.map +1 -0
- package/dist/s3-state.d.ts +21 -0
- package/dist/s3-state.d.ts.map +1 -0
- package/dist/s3-state.js +129 -0
- package/dist/s3-state.js.map +1 -0
- package/dist/schema.d.ts +43 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +27 -0
- package/dist/schema.js.map +1 -0
- 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/dist/sso-credentials.d.ts +17 -0
- package/dist/sso-credentials.d.ts.map +1 -0
- package/dist/sso-credentials.js +43 -0
- package/dist/sso-credentials.js.map +1 -0
- package/dist/state.d.ts +15 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +45 -0
- package/dist/state.js.map +1 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +61 -0
- package/dist/version.js.map +1 -0
- package/package.json +60 -3
|
@@ -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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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type BrowserLaunch, BrowserLaunchOutputError } from "@knpkv/browser-pairing/launch";
|
|
2
|
+
import { Effect, Stdio } from "effect";
|
|
3
|
+
import { Catalog } from "./Catalog.js";
|
|
4
|
+
import { type ControlOptions } from "./ControlSocket.js";
|
|
5
|
+
/** The launch helper owns printing/opening; the catalog and server lifetime stay with this foreground process. */
|
|
6
|
+
export declare const runConsole: (options: ControlOptions, launch: BrowserLaunch, printOnly: boolean) => Effect.Effect<void, import("effect/PlatformError").BadArgument | BrowserLaunchOutputError | import("@knpkv/browser-pairing").BrowserPairingError | import("./ControlSocket.js").ControlError | import("@knpkv/browser-pairing").CredentialCookieError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | import("@knpkv/browser-pairing/owner-session").UnsafeLoopbackAddressError, import("@knpkv/browser-pairing/launch").BrowserOpener | Catalog | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/Path").Path | Stdio.Stdio>;
|
|
7
|
+
//# sourceMappingURL=Launch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Launch.d.ts","sourceRoot":"","sources":["../../src/server/Launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,wBAAwB,EAAe,MAAM,+BAA+B,CAAA;AAEzG,OAAO,EAAE,MAAM,EAAS,KAAK,EAAU,MAAM,QAAQ,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,oBAAoB,CAAA;AAGzE,kHAAkH;AAClH,eAAO,MAAM,UAAU,oqBAqBrB,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BrowserLaunchOutputError, openOrServe } from "@knpkv/browser-pairing/launch";
|
|
2
|
+
import { serveWithBootstrapUrl } from "@knpkv/browser-pairing/owner-session";
|
|
3
|
+
import { Effect, Layer, Stdio, Stream } from "effect";
|
|
4
|
+
import { Catalog } from "./Catalog.js";
|
|
5
|
+
import { requestLoginUrl } from "./ControlSocket.js";
|
|
6
|
+
import { makeServer } from "./Server.js";
|
|
7
|
+
/** The launch helper owns printing/opening; the catalog and server lifetime stay with this foreground process. */
|
|
8
|
+
export const runConsole = Effect.fn("AlchemyConsole.runConsole")(function* (options, launch, printOnly) {
|
|
9
|
+
const catalog = yield* Catalog;
|
|
10
|
+
const serve = (onReady) => serveWithBootstrapUrl((ready) => makeServer(options, ready).pipe(Layer.provide(Layer.succeed(Catalog, catalog))), onReady);
|
|
11
|
+
if (printOnly) {
|
|
12
|
+
const stdio = yield* Stdio.Stdio;
|
|
13
|
+
return yield* serve((url) => Stream.make(`${url}\n`).pipe(Stream.run(stdio.stdout({ endOnDone: false })), Effect.mapError(() => new BrowserLaunchOutputError({}))));
|
|
14
|
+
}
|
|
15
|
+
return yield* openOrServe({ login: requestLoginUrl(options), serve, launch });
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=Launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Launch.js","sourceRoot":"","sources":["../../src/server/Launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,wBAAwB,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AACzG,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAC5E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAuB,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,kHAAkH;AAClH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAC,QAAQ,CAAC,EACxE,OAAuB,EACvB,MAAqB,EACrB,SAAkB;IAElB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,OAAO,CAAA;IAC9B,MAAM,KAAK,GAAG,CAAC,OAAuE,EAAE,EAAE,CACxF,qBAAqB,CACnB,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAC1F,OAAO,CACR,CAAA;IACH,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAA;QAChC,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAC1B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,EAC9C,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,wBAAwB,CAAC,EAAE,CAAC,CAAC,CACxD,CACF,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;AAC/E,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
|
+
import { OwnerAuth } from "../api.js";
|
|
4
|
+
/** Fresh process session. Codes are minted only after the authenticated HTTP listener has built. */
|
|
5
|
+
export declare const makeOwnerSession: (port: number) => Effect.Effect<OwnerSession.OwnerSessionService, import("@knpkv/browser-pairing").BrowserPairingError | import("@knpkv/browser-pairing").CredentialCookieError | OwnerSession.UnsafeLoopbackAddressError, import("effect/Crypto").Crypto>;
|
|
6
|
+
/** Credential-bearing URL. Stdout and browser launch are its only announcement boundaries. */
|
|
7
|
+
export declare const mintBootstrapUrl: (session: OwnerSession.OwnerSessionService) => Effect.Effect<string, import("@knpkv/browser-pairing").BrowserPairingError, never>;
|
|
8
|
+
export declare const ownerAuthLayer: Layer.Layer<OwnerAuth, never, OwnerSession.OwnerSession>;
|
|
9
|
+
//# sourceMappingURL=OwnerSession.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OwnerSession.d.ts","sourceRoot":"","sources":["../../src/server/OwnerSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAEtC,OAAO,EAAa,SAAS,EAAgB,MAAM,WAAW,CAAA;AAE9D,oGAAoG;AACpG,eAAO,MAAM,gBAAgB,SAAU,MAAM,6OAQzC,CAAA;AAEJ,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,YAAa,YAAY,CAAC,mBAAmB,uFAGvE,CAAA;AAEH,eAAO,MAAM,cAAc,0DAe1B,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
|
+
import { HttpServerRequest } from "effect/http";
|
|
4
|
+
import { Forbidden, OwnerAuth, Unauthorized } from "../api.js";
|
|
5
|
+
/** Fresh process session. Codes are minted only after the authenticated HTTP listener has built. */
|
|
6
|
+
export const makeOwnerSession = (port) => OwnerSession.make({
|
|
7
|
+
product: "alchemy-console",
|
|
8
|
+
cookieName: `alchemy_console_owner_${port}`,
|
|
9
|
+
writes: "none",
|
|
10
|
+
freshUrlHint: "run `alchemy-console login` for a fresh URL",
|
|
11
|
+
authorityOrigin: OwnerSession.loopbackOrigin("127.0.0.1", port),
|
|
12
|
+
browserOrigin: OwnerSession.loopbackOrigin("127.0.0.1", port)
|
|
13
|
+
});
|
|
14
|
+
/** Credential-bearing URL. Stdout and browser launch are its only announcement boundaries. */
|
|
15
|
+
export const mintBootstrapUrl = (session) => session.mintBootstrapCode.pipe(Effect.map((code) => OwnerSession.bootstrapUrl(session.browserOrigin, code)));
|
|
16
|
+
export const ownerAuthLayer = Layer.effect(OwnerAuth, Effect.gen(function* () {
|
|
17
|
+
const session = yield* OwnerSession.OwnerSession;
|
|
18
|
+
return OwnerAuth.of(Effect.fn("AlchemyConsole.authorizeOwner")(function* (httpEffect) {
|
|
19
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
20
|
+
yield* session.authorizeHttp(request.cookies[session.cookieName] ?? "").pipe(Effect.catchTags({
|
|
21
|
+
OwnerSessionUnauthorizedError: () => Effect.fail(new Unauthorized({})),
|
|
22
|
+
OwnerSessionForbiddenError: () => Effect.fail(new Forbidden({}))
|
|
23
|
+
}));
|
|
24
|
+
return yield* httpEffect;
|
|
25
|
+
}));
|
|
26
|
+
}));
|
|
27
|
+
//# sourceMappingURL=OwnerSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OwnerSession.js","sourceRoot":"","sources":["../../src/server/OwnerSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE9D,oGAAoG;AACpG,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAE,EAAE,CAC/C,YAAY,CAAC,IAAI,CAAC;IAChB,OAAO,EAAE,iBAAiB;IAC1B,UAAU,EAAE,yBAAyB,IAAI,EAAE;IAC3C,MAAM,EAAE,MAAM;IACd,YAAY,EAAE,6CAA6C;IAC3D,eAAe,EAAE,YAAY,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC;IAC/D,aAAa,EAAE,YAAY,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC;CAC9D,CAAC,CAAA;AAEJ,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAyC,EAAE,EAAE,CAC5E,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAC7E,CAAA;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CACxC,SAAS,EACT,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,YAAY,CAAA;IAChD,OAAO,SAAS,CAAC,EAAE,CACjB,MAAM,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC,QAAQ,CAAC,EAAC,UAAU;QAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC,iBAAiB,CAAA;QAC1D,KAAK,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;YAC5F,6BAA6B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC;YACtE,0BAA0B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC;SACjE,CAAC,CAAC,CAAA;QACH,OAAO,KAAK,CAAC,CAAC,UAAU,CAAA;IAC1B,CAAC,CAAC,CACH,CAAA;AACH,CAAC,CAAC,CACH,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
|
|
2
|
+
import { Deferred, Layer } from "effect";
|
|
3
|
+
import { type ControlOptions } from "./ControlSocket.js";
|
|
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>;
|
|
6
|
+
//# sourceMappingURL=Server.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { NodeHttpServer } from "@effect/platform-node";
|
|
2
|
+
import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
|
|
3
|
+
import { Deferred, Effect, Layer } from "effect";
|
|
4
|
+
import { Etag, HttpPlatform, HttpRouter, HttpServer } from "effect/http";
|
|
5
|
+
import { createServer } from "node:http";
|
|
6
|
+
import { controlSocket } from "./ControlSocket.js";
|
|
7
|
+
import { application } from "./HttpApplication.js";
|
|
8
|
+
import { makeOwnerSession, mintBootstrapUrl } from "./OwnerSession.js";
|
|
9
|
+
/** Catalog and platform services are supplied by the executable. This layer owns both listeners. */
|
|
10
|
+
export const makeServer = (options, ready) => Layer.unwrap(Effect.gen(function* () {
|
|
11
|
+
const server = yield* HttpServer.HttpServer;
|
|
12
|
+
if (server.address._tag === "UnixPathAddress")
|
|
13
|
+
return yield* Effect.die("Alchemy console requires a TCP listener");
|
|
14
|
+
const port = server.address.port;
|
|
15
|
+
const session = yield* makeOwnerSession(port);
|
|
16
|
+
const listening = yield* Deferred.make();
|
|
17
|
+
const control = Layer.effectDiscard(controlSocket({ ...options, port }, session, Deferred.await(listening)));
|
|
18
|
+
return HttpRouter.serve(application, { disableLogger: true, disableListenLog: true }).pipe(Layer.provide(Layer.succeed(OwnerSession.OwnerSession, session)), Layer.provide(HttpPlatform.layer), Layer.provide(Etag.layer),
|
|
19
|
+
// The control socket can mint only after the HTTP routes are built and listening.
|
|
20
|
+
Layer.tap(() => Layer.build(control)), Layer.tap(() => mintBootstrapUrl(session).pipe(Effect.tap(() => Deferred.succeed(listening, undefined)), Effect.flatMap((url) => Deferred.succeed(ready, url)))));
|
|
21
|
+
})).pipe(Layer.provide(NodeHttpServer.layerServer(createServer, { host: "127.0.0.1", port: options.port })));
|
|
22
|
+
//# sourceMappingURL=Server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Server.js","sourceRoot":"","sources":["../../src/server/Server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAuB,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEtE,oGAAoG;AACpG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAuB,EAAE,KAAgC,EAAE,EAAE,CACtF,KAAK,CAAC,MAAM,CACV,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAA;IAC3C,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,iBAAiB;QAAE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAA;IAClH,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAA;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAQ,CAAA;IAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;IAC5G,OAAO,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACxF,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,EAChE,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EACjC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;IACzB,kFAAkF;IAClF,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EACrC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CACb,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,CAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,EACxD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CACtD,CACF,CACF,CAAA;AACH,CAAC,CAAC,CACH,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FileSystem, Layer, Path } from "effect";
|
|
2
|
+
import { HttpClient } from "effect/http";
|
|
3
|
+
import { SsoCredentials } from "../sso-credentials.js";
|
|
4
|
+
import { StackSource } from "./Catalog.js";
|
|
5
|
+
/** Runtime configuration contains server-private paths. It is never an API response. */
|
|
6
|
+
export interface SourceOptions {
|
|
7
|
+
readonly root: string;
|
|
8
|
+
readonly awsConfigFile: string;
|
|
9
|
+
}
|
|
10
|
+
/** Reads only when the authenticated catalog is requested; each project/profile/bucket reports its own failure. */
|
|
11
|
+
export declare const sourceLayer: (options: SourceOptions) => Layer.Layer<StackSource, never, FileSystem.FileSystem | HttpClient.HttpClient | Path.Path | SsoCredentials>;
|
|
12
|
+
//# sourceMappingURL=Sources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sources.d.ts","sourceRoot":"","sources":["../../src/server/Sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAQxC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAwB,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhE,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAED,mHAAmH;AACnH,eAAO,MAAM,WAAW,YAAa,aAAa,gHAgH/C,CAAA"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Effect, FileSystem, Layer, Path } from "effect";
|
|
2
|
+
import { HttpClient } from "effect/http";
|
|
3
|
+
import { CatalogReadError } from "../api.js";
|
|
4
|
+
import { discoverProjects } from "../discovery.js";
|
|
5
|
+
import { readLocalState } from "../local-state.js";
|
|
6
|
+
import { discoverSsoProfiles } from "../profiles.js";
|
|
7
|
+
import { makeS3Reader } from "../s3-live.js";
|
|
8
|
+
import { S3Reader } from "../s3-reader.js";
|
|
9
|
+
import { discoverS3Buckets, discoverS3Stacks, readS3State } from "../s3-state.js";
|
|
10
|
+
import { SsoCredentials } from "../sso-credentials.js";
|
|
11
|
+
import { readAlchemyVersion } from "../version.js";
|
|
12
|
+
import { StackSource } from "./Catalog.js";
|
|
13
|
+
/** Reads only when the authenticated catalog is requested; each project/profile/bucket reports its own failure. */
|
|
14
|
+
export const sourceLayer = (options) => Layer.effect(StackSource, Effect.gen(function* () {
|
|
15
|
+
const fs = yield* FileSystem.FileSystem;
|
|
16
|
+
const path = yield* Path.Path;
|
|
17
|
+
// Capture the source adapter's services once; read closures need no transport/request context.
|
|
18
|
+
const client = yield* HttpClient.HttpClient;
|
|
19
|
+
const sso = yield* SsoCredentials;
|
|
20
|
+
const local = (effect) => effect.pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path));
|
|
21
|
+
const discover = Effect.fn("AlchemyConsole.discoverSources")(function* () {
|
|
22
|
+
const stacks = [];
|
|
23
|
+
const issues = [];
|
|
24
|
+
const projects = yield* Effect.result(local(discoverProjects(options.root)));
|
|
25
|
+
if (projects._tag === "Failure")
|
|
26
|
+
issues.push({ source: "local", reason: projects.failure.reason });
|
|
27
|
+
else {
|
|
28
|
+
if (projects.success.length > 1000)
|
|
29
|
+
return yield* new CatalogReadError({ reason: "limit" });
|
|
30
|
+
for (const project of projects.success) {
|
|
31
|
+
const found = yield* Effect.result(local(Effect.gen(function* () {
|
|
32
|
+
const version = yield* readAlchemyVersion(project.directory, options.root);
|
|
33
|
+
// A source with no resource state need not use one of the supported local encodings.
|
|
34
|
+
if (!(yield* fs.exists(path.join(project.directory, ".alchemy", "state"))))
|
|
35
|
+
return [];
|
|
36
|
+
const state = yield* readLocalState(project.directory, version);
|
|
37
|
+
return state.stacks.map((stack) => ({
|
|
38
|
+
key: JSON.stringify(["local", project.directory, stack.app, stack.stage]),
|
|
39
|
+
summary: {
|
|
40
|
+
app: stack.app,
|
|
41
|
+
stage: stack.stage,
|
|
42
|
+
backend: "local",
|
|
43
|
+
alchemyVersion: version,
|
|
44
|
+
lastDeploy: stack.lastDeploy,
|
|
45
|
+
resourceCount: stack.resources.length
|
|
46
|
+
},
|
|
47
|
+
read: local(readLocalState(project.directory, version)).pipe(Effect.mapError((error) => new CatalogReadError({ reason: error.reason })), Effect.flatMap((fresh) => {
|
|
48
|
+
const current = fresh.stacks.find((item) => item.app === stack.app && item.stage === stack.stage);
|
|
49
|
+
return current === undefined ?
|
|
50
|
+
Effect.fail(new CatalogReadError({ reason: "not-found" })) :
|
|
51
|
+
Effect.succeed(current);
|
|
52
|
+
}))
|
|
53
|
+
}));
|
|
54
|
+
})).pipe(Effect.mapError((error) => new CatalogReadError({
|
|
55
|
+
reason: error._tag === "PlatformError" ? "io" : error.reason
|
|
56
|
+
}))));
|
|
57
|
+
if (found._tag === "Failure")
|
|
58
|
+
issues.push({ source: "local", reason: found.failure.reason });
|
|
59
|
+
else
|
|
60
|
+
for (const stack of found.success)
|
|
61
|
+
stacks.push(stack);
|
|
62
|
+
if (stacks.length > 1000 || issues.length > 1000)
|
|
63
|
+
return yield* new CatalogReadError({ reason: "limit" });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const profiles = yield* Effect.result(local(discoverSsoProfiles(options.awsConfigFile)));
|
|
67
|
+
if (profiles._tag === "Failure")
|
|
68
|
+
issues.push({ source: "s3", reason: profiles.failure.reason });
|
|
69
|
+
else {
|
|
70
|
+
if (profiles.success.length > 100)
|
|
71
|
+
return yield* new CatalogReadError({ reason: "limit" });
|
|
72
|
+
for (const profile of profiles.success) {
|
|
73
|
+
const reader = yield* makeS3Reader(profile).pipe(Effect.provideService(HttpClient.HttpClient, client), Effect.provideService(SsoCredentials, sso));
|
|
74
|
+
const buckets = yield* Effect.result(discoverS3Buckets().pipe(Effect.provideService(S3Reader, reader)));
|
|
75
|
+
if (buckets._tag === "Failure") {
|
|
76
|
+
issues.push({ source: "s3", reason: buckets.failure.reason });
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (buckets.success.length > 1000)
|
|
80
|
+
return yield* new CatalogReadError({ reason: "limit" });
|
|
81
|
+
for (const bucket of buckets.success) {
|
|
82
|
+
const found = yield* Effect.result(discoverS3Stacks(bucket).pipe(Effect.provideService(S3Reader, reader)));
|
|
83
|
+
if (found._tag === "Failure")
|
|
84
|
+
issues.push({ source: "s3", reason: found.failure.reason });
|
|
85
|
+
else {
|
|
86
|
+
for (const stack of found.success) {
|
|
87
|
+
stacks.push({
|
|
88
|
+
key: JSON.stringify([
|
|
89
|
+
"s3",
|
|
90
|
+
profile.name,
|
|
91
|
+
profile.account,
|
|
92
|
+
bucket.name,
|
|
93
|
+
stack.prefix,
|
|
94
|
+
stack.app,
|
|
95
|
+
stack.stage
|
|
96
|
+
]),
|
|
97
|
+
summary: {
|
|
98
|
+
app: stack.app,
|
|
99
|
+
stage: stack.stage,
|
|
100
|
+
backend: "s3",
|
|
101
|
+
alchemyVersion: null,
|
|
102
|
+
lastDeploy: null,
|
|
103
|
+
resourceCount: null
|
|
104
|
+
},
|
|
105
|
+
read: readS3State(stack).pipe(Effect.provideService(S3Reader, reader), Effect.mapError((error) => new CatalogReadError({ reason: error.reason })))
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (stacks.length > 1000 || issues.length > 1000)
|
|
110
|
+
return yield* new CatalogReadError({ reason: "limit" });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { stacks, issues };
|
|
115
|
+
});
|
|
116
|
+
return StackSource.of({ discover: discover() });
|
|
117
|
+
}));
|
|
118
|
+
//# sourceMappingURL=Sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sources.js","sourceRoot":"","sources":["../../src/server/Sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAuB,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAwB,WAAW,EAAE,MAAM,cAAc,CAAA;AAQhE,mHAAmH;AACnH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAsB,EAAE,EAAE,CACpD,KAAK,CAAC,MAAM,CACV,WAAW,EACX,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAA;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;IAC7B,+FAA+F;IAC/F,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAA;IAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,cAAc,CAAA;IACjC,MAAM,KAAK,GAAG,CAAO,MAA8D,EAAE,EAAE,CACrF,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,UAAU,EAAE,EAAE,CAAC,EAChD,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CACvC,CAAA;IACH,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC,gCAAgC,CAAC,CAAC,QAAQ,CAAC;QACpE,MAAM,MAAM,GAA2B,EAAE,CAAA;QACzC,MAAM,MAAM,GAA0B,EAAE,CAAA;QACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC5E,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;aAC7F,CAAC;YACJ,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI;gBAAE,OAAO,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;YAC3F,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACvC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAChC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;oBACxB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;oBAC1E,qFAAqF;oBACrF,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;wBAAE,OAAO,EAAE,CAAA;oBACrF,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;oBAC/D,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,CAAC;wBACnD,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;wBACzE,OAAO,EAAE;4BACP,GAAG,EAAE,KAAK,CAAC,GAAG;4BACd,KAAK,EAAE,KAAK,CAAC,KAAK;4BAClB,OAAO,EAAE,OAAO;4BAChB,cAAc,EAAE,OAAO;4BACvB,UAAU,EAAE,KAAK,CAAC,UAAU;4BAC5B,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM;yBACtC;wBACD,IAAI,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAC1D,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,EAC1E,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;4BACvB,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,CAAA;4BACjG,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC;gCAC5B,MAAM,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;gCAC5D,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;wBAC3B,CAAC,CAAC,CACH;qBACF,CAAC,CAAC,CAAA;gBACL,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACjC,IAAI,gBAAgB,CAAC;oBACnB,MAAM,EAAE,KAAK,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM;iBAC7D,CAAC,CACH,CAAC,CACH,CAAA;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;oBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;;oBACvF,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO;wBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBAC1D,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI;oBAAE,OAAO,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;YAC3G,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAA;QACxF,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;aAC1F,CAAC;YACJ,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG;gBAAE,OAAO,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;YAC1F,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACvC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,CAC9C,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,EACpD,MAAM,CAAC,cAAc,CAAC,cAAc,EAAE,GAAG,CAAC,CAC3C,CAAA;gBACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;gBACvG,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC/B,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;oBAC7D,SAAQ;gBACV,CAAC;gBACD,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI;oBAAE,OAAO,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;gBAC1F,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACrC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;oBAC1G,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;wBAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;yBACpF,CAAC;wBACJ,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;4BAClC,MAAM,CAAC,IAAI,CAAC;gCACV,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC;oCAClB,IAAI;oCACJ,OAAO,CAAC,IAAI;oCACZ,OAAO,CAAC,OAAO;oCACf,MAAM,CAAC,IAAI;oCACX,KAAK,CAAC,MAAM;oCACZ,KAAK,CAAC,GAAG;oCACT,KAAK,CAAC,KAAK;iCACZ,CAAC;gCACF,OAAO,EAAE;oCACP,GAAG,EAAE,KAAK,CAAC,GAAG;oCACd,KAAK,EAAE,KAAK,CAAC,KAAK;oCAClB,OAAO,EAAE,IAAI;oCACb,cAAc,EAAE,IAAI;oCACpB,UAAU,EAAE,IAAI;oCAChB,aAAa,EAAE,IAAI;iCACpB;gCACD,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,CAC3B,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,EACvC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAC3E;6BACF,CAAC,CAAA;wBACJ,CAAC;oBACH,CAAC;oBACD,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI;wBAAE,OAAO,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;gBAC3G,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;IAC3B,CAAC,CAAC,CAAA;IACF,OAAO,WAAW,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAA;AACjD,CAAC,CAAC,CACH,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fromSSO } from "@aws-sdk/credential-provider-sso";
|
|
2
|
+
import type { ResolvedCredentials } from "@distilled.cloud/aws/Credentials";
|
|
3
|
+
import { Context, Effect, Layer } from "effect";
|
|
4
|
+
import type { SsoProfile } from "./profiles.js";
|
|
5
|
+
import { S3ReadError } from "./s3-reader.js";
|
|
6
|
+
declare const SsoCredentials_base: Context.ServiceClass<SsoCredentials, "@knpkv/alchemy-console/SsoCredentials", {
|
|
7
|
+
readonly resolve: (profile: SsoProfile) => Effect.Effect<ResolvedCredentials, S3ReadError>;
|
|
8
|
+
}>;
|
|
9
|
+
/** Credential-bearing service; results go only to server-side signing and never to persistence, HTTP or logs. */
|
|
10
|
+
export declare class SsoCredentials extends SsoCredentials_base {
|
|
11
|
+
}
|
|
12
|
+
/** SDK factory returns credential-bearing material; inject it at this signing boundary for tests. */
|
|
13
|
+
export type SsoCredentialProviderFactory = typeof fromSSO;
|
|
14
|
+
/** Use only an explicit SSO provider; never a default credential chain. */
|
|
15
|
+
export declare const ssoCredentialsLayer: (provider?: SsoCredentialProviderFactory) => Layer.Layer<SsoCredentials, never, never>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=sso-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sso-credentials.d.ts","sourceRoot":"","sources":["../src/sso-credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAmD,MAAM,kCAAkC,CAAA;AAC3G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAoB,MAAM,QAAQ,CAAA;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;;sBAUxB,CAAC,OAAO,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,WAAW,CAAC;;AAF5F,iHAAiH;AACjH,qBAAa,cAAe,SAAQ,mBAES;CAAG;AAEhD,qGAAqG;AACrG,MAAM,MAAM,4BAA4B,GAAG,OAAO,OAAO,CAAA;AAEzD,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,cAAc,4BAA4B,8CA+BtE,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { fromSSO } from "@aws-sdk/credential-provider-sso";
|
|
2
|
+
import { Context, Effect, Layer, Redacted, Schema } from "effect";
|
|
3
|
+
import { S3ReadError } from "./s3-reader.js";
|
|
4
|
+
const CredentialIdentity = Schema.Struct({
|
|
5
|
+
accessKeyId: Schema.NonEmptyString,
|
|
6
|
+
secretAccessKey: Schema.NonEmptyString,
|
|
7
|
+
sessionToken: Schema.NonEmptyString
|
|
8
|
+
});
|
|
9
|
+
/** Credential-bearing service; results go only to server-side signing and never to persistence, HTTP or logs. */
|
|
10
|
+
export class SsoCredentials extends Context.Service()("@knpkv/alchemy-console/SsoCredentials") {
|
|
11
|
+
}
|
|
12
|
+
/** Use only an explicit SSO provider; never a default credential chain. */
|
|
13
|
+
export const ssoCredentialsLayer = (provider = fromSSO) => Layer.succeed(SsoCredentials, {
|
|
14
|
+
resolve: Effect.fn("AlchemyConsole.resolveSsoCredentials")(function* (profile) {
|
|
15
|
+
const request = {
|
|
16
|
+
profile: profile.name,
|
|
17
|
+
configFilepath: profile.configFile,
|
|
18
|
+
ssoAccountId: profile.account,
|
|
19
|
+
ssoRoleName: profile.role,
|
|
20
|
+
ssoRegion: profile.ssoRegion,
|
|
21
|
+
ssoStartUrl: profile.startUrl,
|
|
22
|
+
clientConfig: { region: profile.ssoRegion },
|
|
23
|
+
ignoreCache: true
|
|
24
|
+
};
|
|
25
|
+
if (profile.session !== null)
|
|
26
|
+
request.ssoSession = profile.session;
|
|
27
|
+
const raw = yield* Effect.tryPromise({
|
|
28
|
+
try: () => provider(request)(),
|
|
29
|
+
catch: () => new S3ReadError({ reason: "authentication" })
|
|
30
|
+
}).pipe(Effect.timeoutOrElse({
|
|
31
|
+
duration: "15 seconds",
|
|
32
|
+
orElse: () => Effect.fail(new S3ReadError({ reason: "authentication" }))
|
|
33
|
+
}));
|
|
34
|
+
const identity = yield* Schema.decodeUnknownEffect(CredentialIdentity)(raw).pipe(Effect.mapError(() => new S3ReadError({ reason: "authentication" })));
|
|
35
|
+
return {
|
|
36
|
+
accessKeyId: Redacted.make(identity.accessKeyId),
|
|
37
|
+
secretAccessKey: Redacted.make(identity.secretAccessKey),
|
|
38
|
+
sessionToken: Redacted.make(identity.sessionToken),
|
|
39
|
+
region: profile.region
|
|
40
|
+
};
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=sso-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sso-credentials.js","sourceRoot":"","sources":["../src/sso-credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAmD,MAAM,kCAAkC,CAAA;AAE3G,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE5C,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC,cAAc;IAClC,eAAe,EAAE,MAAM,CAAC,cAAc;IACtC,YAAY,EAAE,MAAM,CAAC,cAAc;CACpC,CAAC,CAAA;AAEF,iHAAiH;AACjH,MAAM,OAAO,cAAe,SAAQ,OAAO,CAAC,OAAO,EAE/C,CAAC,uCAAuC,CAAC;CAAG;AAKhD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,QAAQ,GAAiC,OAAO,EAAE,EAAE,CACtF,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE;IAC5B,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,sCAAsC,CAAC,CAAC,QAAQ,CAAC,EAAC,OAAmB;QACtF,MAAM,OAAO,GAA2C;YACtD,OAAO,EAAE,OAAO,CAAC,IAAI;YACrB,cAAc,EAAE,OAAO,CAAC,UAAU;YAClC,YAAY,EAAE,OAAO,CAAC,OAAO;YAC7B,WAAW,EAAE,OAAO,CAAC,IAAI;YACzB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,OAAO,CAAC,QAAQ;YAC7B,YAAY,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE;YAC3C,WAAW,EAAE,IAAI;SAClB,CAAA;QACD,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,OAAO,CAAA;QAClE,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;YACnC,GAAG,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;YAC9B,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;SAC3D,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;YAC3B,QAAQ,EAAE,YAAY;YACtB,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;SACzE,CAAC,CAAC,CAAA;QACH,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAC9E,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC,CACrE,CAAA;QACD,OAAO;YACL,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;YAChD,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;YACxD,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;YAClD,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAA;IACH,CAAC,CAAC;CACH,CAAC,CAAA"}
|
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { StateReadError } from "./schema.js";
|
|
3
|
+
/** Decode persisted beta.74/beta.77 JSON into metadata; action rows are validated and excluded. */
|
|
4
|
+
export declare const decodeResource: (text: string) => Effect.Effect<{
|
|
5
|
+
readonly fqn: string;
|
|
6
|
+
readonly logicalId: string;
|
|
7
|
+
readonly resourceType: string;
|
|
8
|
+
readonly status: "created" | "creating" | "deleting" | "replaced" | "replacing" | "updated" | "updating";
|
|
9
|
+
readonly parent: string | null;
|
|
10
|
+
readonly providerId: string | null;
|
|
11
|
+
readonly account: string | null;
|
|
12
|
+
readonly region: string | null;
|
|
13
|
+
readonly consoleUrl: string | null;
|
|
14
|
+
} | null, StateReadError, never>;
|
|
15
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,QAAQ,CAAA;AAClD,OAAO,EAAmB,cAAc,EAAE,MAAM,aAAa,CAAA;AA6B7D,mGAAmG;AACnG,eAAO,MAAM,cAAc;;;;;;;;;;gCAoBzB,CAAA"}
|