@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.
Files changed (51) hide show
  1. package/README.md +71 -4
  2. package/dist/api.d.ts +126 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +62 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/client/bootstrap.js +2834 -0
  7. package/dist/main.d.ts +3 -0
  8. package/dist/main.d.ts.map +1 -0
  9. package/dist/main.js +39 -0
  10. package/dist/main.js.map +1 -0
  11. package/dist/s3-live.d.ts +7 -2
  12. package/dist/s3-live.d.ts.map +1 -1
  13. package/dist/s3-live.js +3 -2
  14. package/dist/s3-live.js.map +1 -1
  15. package/dist/server/Bootstrap.d.ts +4 -0
  16. package/dist/server/Bootstrap.d.ts.map +1 -0
  17. package/dist/server/Bootstrap.js +12 -0
  18. package/dist/server/Bootstrap.js.map +1 -0
  19. package/dist/server/Catalog.d.ts +27 -0
  20. package/dist/server/Catalog.d.ts.map +1 -0
  21. package/dist/server/Catalog.js +83 -0
  22. package/dist/server/Catalog.js.map +1 -0
  23. package/dist/server/Config.d.ts +17 -0
  24. package/dist/server/Config.d.ts.map +1 -0
  25. package/dist/server/Config.js +23 -0
  26. package/dist/server/Config.js.map +1 -0
  27. package/dist/server/ControlSocket.d.ts +19 -0
  28. package/dist/server/ControlSocket.d.ts.map +1 -0
  29. package/dist/server/ControlSocket.js +147 -0
  30. package/dist/server/ControlSocket.js.map +1 -0
  31. package/dist/server/HttpApplication.d.ts +7 -0
  32. package/dist/server/HttpApplication.d.ts.map +1 -0
  33. package/dist/server/HttpApplication.js +46 -0
  34. package/dist/server/HttpApplication.js.map +1 -0
  35. package/dist/server/Launch.d.ts +7 -0
  36. package/dist/server/Launch.d.ts.map +1 -0
  37. package/dist/server/Launch.js +17 -0
  38. package/dist/server/Launch.js.map +1 -0
  39. package/dist/server/OwnerSession.d.ts +9 -0
  40. package/dist/server/OwnerSession.d.ts.map +1 -0
  41. package/dist/server/OwnerSession.js +27 -0
  42. package/dist/server/OwnerSession.js.map +1 -0
  43. package/dist/server/Server.d.ts +6 -0
  44. package/dist/server/Server.d.ts.map +1 -0
  45. package/dist/server/Server.js +22 -0
  46. package/dist/server/Server.js.map +1 -0
  47. package/dist/server/Sources.d.ts +12 -0
  48. package/dist/server/Sources.d.ts.map +1 -0
  49. package/dist/server/Sources.js +118 -0
  50. package/dist/server/Sources.js.map +1 -0
  51. package/package.json +17 -6
package/README.md CHANGED
@@ -1,9 +1,76 @@
1
1
  # @knpkv/alchemy-console
2
2
 
3
- Read-only data layer for a local Alchemy stack console. The package adds
4
- project discovery, lockfile version lookup, local state metadata, and SSO/S3
5
- readers. There is no executable or HTTP listener yet. Authenticated API and
6
- design-backed Rly screens follow in separate PRs.
3
+ Run `alchemy-console` to serve the read-only API on `127.0.0.1:3114` and open a
4
+ one-time login link. `alchemy-console login` behaves the same way: it asks an
5
+ existing server for a fresh link, or starts one in the foreground. `--no-open`
6
+ prints the link without opening a browser. CI, non-interactive stdout and
7
+ `BROWSER=none` also skip opening. `--port` selects a port from 1024 through 65535.
8
+ `alchemy-console serve` always prints only and runs until SIGINT or SIGTERM.
9
+ Closing a browser tab leaves the foreground server running.
10
+
11
+ The current browser page performs login, then navigates to JSON at `/api/stacks`.
12
+ Stack screens will follow the approved design references in a separate change.
13
+
14
+ Configuration is read at startup:
15
+
16
+ | Variable | Default | Boundary |
17
+ | ---------------------- | ---------------------------------------------- | ------------------------------------- |
18
+ | `ALCHEMY_CONSOLE_ROOT` | `Work/dev` under the user's home | Server-private project discovery path |
19
+ | `AWS_CONFIG_FILE` | `.aws/config` under the user's home | Server-private SSO configuration path |
20
+ | `ALCHEMY_CONSOLE_HOME` | `.cache/alchemy-console` under the user's home | Server-private control directory |
21
+
22
+ Login reuse is per port. A reused server keeps its startup configuration. The
23
+ control directory must be owned by the current user with mode `0700`; each
24
+ `serve-<port>.sock` is `0600`. Symlinks, foreign owners and pre-existing stale
25
+ sockets fail explicitly. For an abandoned socket, confirm its server has stopped
26
+ before removing that entry and restarting. The control channel currently requires
27
+ POSIX ownership and Unix sockets.
28
+
29
+ ## Authenticated API
30
+
31
+ The client-safe contracts are exported from `@knpkv/alchemy-console/api` and may be
32
+ used with Effect's `HttpApiClient`. Both endpoints require the process's owner
33
+ cookie, and all responses are private and uncacheable:
34
+
35
+ The cookie name includes the bound listener port, so sessions on different ports
36
+ can coexist in one browser. Both API boundaries select that listener's cookie.
37
+ Cookies still share the loopback host and travel across ports; the name separates
38
+ sessions and does not provide port confidentiality. The API middleware resolves
39
+ the name at runtime rather than declaring a fixed OpenAPI cookie scheme.
40
+
41
+ | Route | Result |
42
+ | --------------------- | ------------------------------------------------------ |
43
+ | `GET /api/stacks` | Stack summaries and explicit local/S3 discovery issues |
44
+ | `GET /api/stacks/:id` | Current normalized resource state for a listed stack |
45
+
46
+ Listing runs discovery on demand, without polling or schedules. IDs are random,
47
+ process-scoped identifiers with no encoded locator. They remain stable across
48
+ scans while a stack remains present, then expire when it disappears or the server
49
+ restarts. They are normalized, authenticated client-visible identifiers; the
50
+ server never persists them. Do not emit them or stack metadata through public
51
+ routes or telemetry. Browsers must not persist private provider coordinates.
52
+
53
+ Discovery reports sanitized reason codes for failed projects, SSO profiles and
54
+ buckets while continuing other sources. A discovered entrypoint with no state
55
+ cannot supply an app or stage and contributes no stack row. Remote resource
56
+ counts and Alchemy versions remain unknown until the corresponding state or
57
+ lockfile establishes them. Discovery is capped at 1000 projects/stacks, 100 SSO
58
+ profiles, 1000 candidate buckets per profile and two minutes per list request.
59
+
60
+ The one public authentication route, `POST /auth/bootstrap`, exchanges a code
61
+ from the URL fragment for an HttpOnly, SameSite=Strict owner cookie. Codes are
62
+ single-use and expire after 60 seconds; a fresh mint replaces an unspent code.
63
+ The page removes the fragment before exchanging it. Codes are minted and
64
+ announced only after the authenticated HTTP listener is ready. Credential URLs
65
+ go only to stdout and the browser opener, never logs or telemetry. Session tokens
66
+ remain in the server and HttpOnly cookie. Every unsafe API method is refused,
67
+ including apply and destroy. Reads reject foreign origins and cross-site browser
68
+ requests. Listener and request Host are restricted to the bound loopback address.
69
+
70
+ ## Data readers
71
+
72
+ The root export provides project discovery, lockfile version lookup, local
73
+ state metadata and SSO/S3 readers for server-side consumers.
7
74
 
8
75
  `discoverProjects(directory)` scans an explicit root for `alchemy.run.ts` without
9
76
  importing or executing it. It skips `.git`, `node_modules`, `.alchemy`, `.direnv`,
package/dist/api.d.ts ADDED
@@ -0,0 +1,126 @@
1
+ /** Client-safe contracts. IDs and metadata cross only the authenticated owner API, never public telemetry. */
2
+ import { Schema } from "effect";
3
+ import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware } from "effect/http-api";
4
+ /** Random, process-scoped identity; contains no encoded provider locator. Not persisted by the server. */
5
+ export declare const StackId: Schema.String;
6
+ export type StackId = typeof StackId.Type;
7
+ export declare const SourceFailureReason: Schema.Literals<readonly ["io", "invalid-state", "invalid-lockfile", "unsupported-version", "limit", "outside-root", "invalid-config", "authentication", "denied", "not-found", "provider", "invalid-response", "invalid-source"]>;
8
+ export declare const DiscoveryIssue: Schema.Struct<{
9
+ readonly source: Schema.Literals<readonly ["local", "s3"]>;
10
+ readonly reason: Schema.Literals<readonly ["io", "invalid-state", "invalid-lockfile", "unsupported-version", "limit", "outside-root", "invalid-config", "authentication", "denied", "not-found", "provider", "invalid-response", "invalid-source"]>;
11
+ }>;
12
+ export type DiscoveryIssue = typeof DiscoveryIssue.Type;
13
+ export declare const StackSummary: Schema.Struct<{
14
+ readonly id: Schema.String;
15
+ readonly app: Schema.NonEmptyString;
16
+ readonly stage: Schema.NonEmptyString;
17
+ readonly backend: Schema.Literals<readonly ["local", "s3"]>;
18
+ readonly alchemyVersion: Schema.NullOr<Schema.String>;
19
+ readonly lastDeploy: Schema.NullOr<Schema.String>;
20
+ readonly resourceCount: Schema.NullOr<Schema.Int>;
21
+ }>;
22
+ export type StackSummary = typeof StackSummary.Type;
23
+ export declare const StackList: Schema.Struct<{
24
+ readonly stacks: Schema.$Array<Schema.Struct<{
25
+ readonly id: Schema.String;
26
+ readonly app: Schema.NonEmptyString;
27
+ readonly stage: Schema.NonEmptyString;
28
+ readonly backend: Schema.Literals<readonly ["local", "s3"]>;
29
+ readonly alchemyVersion: Schema.NullOr<Schema.String>;
30
+ readonly lastDeploy: Schema.NullOr<Schema.String>;
31
+ readonly resourceCount: Schema.NullOr<Schema.Int>;
32
+ }>>;
33
+ readonly issues: Schema.$Array<Schema.Struct<{
34
+ readonly source: Schema.Literals<readonly ["local", "s3"]>;
35
+ readonly reason: Schema.Literals<readonly ["io", "invalid-state", "invalid-lockfile", "unsupported-version", "limit", "outside-root", "invalid-config", "authentication", "denied", "not-found", "provider", "invalid-response", "invalid-source"]>;
36
+ }>>;
37
+ }>;
38
+ export type StackList = typeof StackList.Type;
39
+ export declare const StackDetail: Schema.Struct<{
40
+ readonly id: Schema.String;
41
+ readonly state: Schema.Struct<{
42
+ readonly app: Schema.NonEmptyString;
43
+ readonly stage: Schema.NonEmptyString;
44
+ readonly backend: Schema.Literals<readonly ["local", "s3"]>;
45
+ readonly alchemyVersion: Schema.NullOr<Schema.String>;
46
+ readonly lastDeploy: Schema.NullOr<Schema.String>;
47
+ readonly resources: Schema.$Array<Schema.Struct<{
48
+ readonly fqn: Schema.NonEmptyString;
49
+ readonly logicalId: Schema.NonEmptyString;
50
+ readonly resourceType: Schema.NonEmptyString;
51
+ readonly status: Schema.Literals<readonly ["creating", "created", "updating", "updated", "deleting", "replacing", "replaced"]>;
52
+ readonly parent: Schema.NullOr<Schema.String>;
53
+ readonly providerId: Schema.NullOr<Schema.String>;
54
+ readonly account: Schema.NullOr<Schema.String>;
55
+ readonly region: Schema.NullOr<Schema.String>;
56
+ readonly consoleUrl: Schema.NullOr<Schema.String>;
57
+ }>>;
58
+ }>;
59
+ }>;
60
+ export type StackDetail = typeof StackDetail.Type;
61
+ declare const CatalogReadError_base: Schema.Class<CatalogReadError, Schema.TaggedStruct<"CatalogReadError", {
62
+ readonly reason: Schema.Literals<readonly ["io", "invalid-state", "invalid-lockfile", "unsupported-version", "limit", "outside-root", "invalid-config", "authentication", "denied", "not-found", "provider", "invalid-response", "invalid-source"]>;
63
+ }>, import("effect/Cause").YieldableError>;
64
+ export declare class CatalogReadError extends CatalogReadError_base {
65
+ }
66
+ declare const StackNotFound_base: Schema.Class<StackNotFound, Schema.TaggedStruct<"StackNotFound", {}>, import("effect/Cause").YieldableError>;
67
+ export declare class StackNotFound extends StackNotFound_base {
68
+ }
69
+ declare const Unauthorized_base: Schema.Class<Unauthorized, Schema.TaggedStruct<"Unauthorized", {}>, import("effect/Cause").YieldableError>;
70
+ export declare class Unauthorized extends Unauthorized_base {
71
+ }
72
+ declare const Forbidden_base: Schema.Class<Forbidden, Schema.TaggedStruct<"Forbidden", {}>, import("effect/Cause").YieldableError>;
73
+ export declare class Forbidden extends Forbidden_base {
74
+ }
75
+ declare const OwnerAuth_base: HttpApiMiddleware.ServiceClass<OwnerAuth, "@knpkv/alchemy-console/OwnerAuth", {
76
+ requires: never;
77
+ provides: never;
78
+ error: readonly [typeof Unauthorized, typeof Forbidden];
79
+ clientError: never;
80
+ requiredForClient: false;
81
+ security: never;
82
+ }, HttpApiMiddleware.HttpApiMiddleware<never, readonly [typeof Unauthorized, typeof Forbidden], never>>;
83
+ /** The server resolves its listener-specific cookie name at request time. */
84
+ export declare class OwnerAuth extends OwnerAuth_base {
85
+ }
86
+ declare const AlchemyConsoleApi_base: HttpApi.HttpApi<"AlchemyConsoleApi", HttpApiGroup.HttpApiGroup<"stacks", HttpApiEndpoint.HttpApiEndpoint<"list", "GET", "/api/stacks", never, never, never, never, Schema.toCodecJson<Schema.Struct<{
87
+ readonly stacks: Schema.$Array<Schema.Struct<{
88
+ readonly id: Schema.String;
89
+ readonly app: Schema.NonEmptyString;
90
+ readonly stage: Schema.NonEmptyString;
91
+ readonly backend: Schema.Literals<readonly ["local", "s3"]>;
92
+ readonly alchemyVersion: Schema.NullOr<Schema.String>;
93
+ readonly lastDeploy: Schema.NullOr<Schema.String>;
94
+ readonly resourceCount: Schema.NullOr<Schema.Int>;
95
+ }>>;
96
+ readonly issues: Schema.$Array<Schema.Struct<{
97
+ readonly source: Schema.Literals<readonly ["local", "s3"]>;
98
+ readonly reason: Schema.Literals<readonly ["io", "invalid-state", "invalid-lockfile", "unsupported-version", "limit", "outside-root", "invalid-config", "authentication", "denied", "not-found", "provider", "invalid-response", "invalid-source"]>;
99
+ }>>;
100
+ }>>, Schema.toCodecJson<typeof CatalogReadError>, OwnerAuth, never> | HttpApiEndpoint.HttpApiEndpoint<"get", "GET", "/api/stacks/:id", Schema.toCodecStringTree<Schema.Struct<{
101
+ readonly id: Schema.String;
102
+ }>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
103
+ readonly id: Schema.String;
104
+ readonly state: Schema.Struct<{
105
+ readonly app: Schema.NonEmptyString;
106
+ readonly stage: Schema.NonEmptyString;
107
+ readonly backend: Schema.Literals<readonly ["local", "s3"]>;
108
+ readonly alchemyVersion: Schema.NullOr<Schema.String>;
109
+ readonly lastDeploy: Schema.NullOr<Schema.String>;
110
+ readonly resources: Schema.$Array<Schema.Struct<{
111
+ readonly fqn: Schema.NonEmptyString;
112
+ readonly logicalId: Schema.NonEmptyString;
113
+ readonly resourceType: Schema.NonEmptyString;
114
+ readonly status: Schema.Literals<readonly ["creating", "created", "updating", "updated", "deleting", "replacing", "replaced"]>;
115
+ readonly parent: Schema.NullOr<Schema.String>;
116
+ readonly providerId: Schema.NullOr<Schema.String>;
117
+ readonly account: Schema.NullOr<Schema.String>;
118
+ readonly region: Schema.NullOr<Schema.String>;
119
+ readonly consoleUrl: Schema.NullOr<Schema.String>;
120
+ }>>;
121
+ }>;
122
+ }>>, Schema.toCodecJson<typeof CatalogReadError | typeof StackNotFound>, OwnerAuth, never>, false>>;
123
+ export declare class AlchemyConsoleApi extends AlchemyConsoleApi_base {
124
+ }
125
+ export {};
126
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,8GAA8G;AAC9G,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAG3F,0GAA0G;AAC1G,eAAO,MAAM,OAAO,eAAuC,CAAA;AAC3D,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAEzC,eAAO,MAAM,mBAAmB,oOAc9B,CAAA;AAEF,eAAO,MAAM,cAAc;;;EAGzB,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAA;AAEvD,eAAO,MAAM,YAAY;;;;;;;;EAQvB,CAAA;AACF,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA;AAEnD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;EAA8F,CAAA;AACpH,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAA;AAC7C,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;EAAoD,CAAA;AAC5E,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;;;;AAEjD,qBAAa,gBAAiB,SAAQ,qBAEZ;CAAG;;AAE7B,qBAAa,aAAc,SAAQ,kBAAgF;CAAG;;AAEtH,qBAAa,YAAa,SAAQ,iBAA8E;CAAG;;AAEnH,qBAAa,SAAU,SAAQ,cAAwE;CAAG;;;;;;;;;AAE1G,6EAA6E;AAC7E,qBAAa,SAAU,SAAQ,cAE7B;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWL,qBAAa,iBAAkB,SAAQ,sBAAmE;CAAG"}
package/dist/api.js ADDED
@@ -0,0 +1,62 @@
1
+ /** Client-safe contracts. IDs and metadata cross only the authenticated owner API, never public telemetry. */
2
+ import { Schema } from "effect";
3
+ import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware } from "effect/http-api";
4
+ import { StackState } from "./schema.js";
5
+ /** Random, process-scoped identity; contains no encoded provider locator. Not persisted by the server. */
6
+ export const StackId = Schema.String.check(Schema.isUUID());
7
+ export const SourceFailureReason = Schema.Literals([
8
+ "io",
9
+ "invalid-state",
10
+ "invalid-lockfile",
11
+ "unsupported-version",
12
+ "limit",
13
+ "outside-root",
14
+ "invalid-config",
15
+ "authentication",
16
+ "denied",
17
+ "not-found",
18
+ "provider",
19
+ "invalid-response",
20
+ "invalid-source"
21
+ ]);
22
+ export const DiscoveryIssue = Schema.Struct({
23
+ source: Schema.Literals(["local", "s3"]),
24
+ reason: SourceFailureReason
25
+ });
26
+ export const StackSummary = Schema.Struct({
27
+ id: StackId,
28
+ app: Schema.NonEmptyString,
29
+ stage: Schema.NonEmptyString,
30
+ backend: Schema.Literals(["local", "s3"]),
31
+ alchemyVersion: Schema.NullOr(Schema.String),
32
+ lastDeploy: Schema.NullOr(Schema.String),
33
+ resourceCount: Schema.NullOr(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)))
34
+ });
35
+ export const StackList = Schema.Struct({ stacks: Schema.Array(StackSummary), issues: Schema.Array(DiscoveryIssue) });
36
+ export const StackDetail = Schema.Struct({ id: StackId, state: StackState });
37
+ export class CatalogReadError extends Schema.TaggedError()("CatalogReadError", {
38
+ reason: SourceFailureReason
39
+ }, { httpApiStatus: 503 }) {
40
+ }
41
+ export class StackNotFound extends Schema.TaggedError()("StackNotFound", {}, { httpApiStatus: 404 }) {
42
+ }
43
+ export class Unauthorized extends Schema.TaggedError()("Unauthorized", {}, { httpApiStatus: 401 }) {
44
+ }
45
+ export class Forbidden extends Schema.TaggedError()("Forbidden", {}, { httpApiStatus: 403 }) {
46
+ }
47
+ /** The server resolves its listener-specific cookie name at request time. */
48
+ export class OwnerAuth extends HttpApiMiddleware.Service()("@knpkv/alchemy-console/OwnerAuth", {
49
+ error: [Unauthorized, Forbidden]
50
+ }) {
51
+ }
52
+ const stacks = HttpApiGroup.make("stacks")
53
+ .add(HttpApiEndpoint.get("list", "/stacks", { success: StackList, error: CatalogReadError }))
54
+ .add(HttpApiEndpoint.get("get", "/stacks/:id", {
55
+ params: Schema.Struct({ id: StackId }),
56
+ success: StackDetail,
57
+ error: [StackNotFound, CatalogReadError]
58
+ }))
59
+ .prefix("/api");
60
+ export class AlchemyConsoleApi extends HttpApi.make("AlchemyConsoleApi").add(stacks).middleware(OwnerAuth) {
61
+ }
62
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}