@usine/server 0.1.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 +15 -0
- package/dist/backend.d.ts +26 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +39 -0
- package/dist/backend.js.map +1 -0
- package/dist/cli.d.ts +58 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +117 -0
- package/dist/cli.js.map +1 -0
- package/dist/http.d.ts +7 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +205 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +21 -0
- package/dist/main.js.map +1 -0
- package/dist/up.d.ts +4 -0
- package/dist/up.d.ts.map +1 -0
- package/dist/up.js +68 -0
- package/dist/up.js.map +1 -0
- package/dist/web/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/web/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/web/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/web/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/web/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/web/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/web/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/web/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/web/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/web/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/web/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/web/assets/index-CjRDHHWF.css +2 -0
- package/dist/web/assets/index-DmN0SkGf.js +295 -0
- package/dist/web/index.html +14 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Philippe L'ATTENTION
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @usine/server
|
|
2
|
+
|
|
3
|
+
The foreground CLI for a local [usine](https://github.com/basaltbytes/usine) factory. It is a thin Node adapter over `@usine/orchestrator`: the library owns polling, schedule triggers, durable execution, worker composition, and operator acts; this package serves their JSON and web surfaces.
|
|
4
|
+
|
|
5
|
+
Run it from an instance folder:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
usine up
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The command prints `http://127.0.0.1:7331` and stays attached to the terminal. Pass `--port <port>` (or set `USINE_PORT`) to listen elsewhere. `usine --help` prints the complete command surface and `usine --version` prints the exact `@usine/server` version pinned by the instance. If the chosen port is occupied, the CLI names the conflict and suggests the override instead of exposing Node's bind failure.
|
|
12
|
+
|
|
13
|
+
Without GitHub App credentials, it opens the setup screen in the default browser, logs the typed credential error, and leaves both trigger streams idle instead of crashing. The browser command has a two-second timeout; a failed opener produces a warning and does not stop the server. Existing Runs, transcript detail, and database-backed Loop pause state remain observable, while interventions stay unavailable. Credentials may come from `USINE_GITHUB_APP_ID` plus `USINE_GITHUB_PRIVATE_KEY`, or from a mode-0600 `.usine/github-app.json` file with `app_id` and `private_key` fields. The running process reads credentials only at startup, so stop and rerun `usine up` after adding them; the setup screen states that restart requirement explicitly.
|
|
14
|
+
|
|
15
|
+
The local-only UI opens each project as a Board built from its authored or proposed lanes. Moving an Item between label-backed lanes updates its GitHub labels; normal polling then evaluates label-protocol loops. Board cards show the latest Run and open its existing detail drawer. The UI can also approve, retry, or cancel a Run; pause or unpause a Loop's database runtime state; page backward through older transcript rows; and tail parent and one-level child session activity.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LoadedInstance } from "@usine/config";
|
|
2
|
+
import { Loop, Run } from "@usine/core";
|
|
3
|
+
import type { FactoryOverview, BoardMove, Operator, OperatorObservation, OperatorError, RunDetailView, TranscriptRequest } from "@usine/orchestrator";
|
|
4
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
5
|
+
declare const BackendUnavailableError_base: Schema.Class<BackendUnavailableError, Schema.TaggedStruct<"BackendUnavailableError", {
|
|
6
|
+
readonly message: Schema.String;
|
|
7
|
+
}>, import("effect/Cause").YieldableError>;
|
|
8
|
+
export declare class BackendUnavailableError extends BackendUnavailableError_base {
|
|
9
|
+
}
|
|
10
|
+
export type BackendError = OperatorError | BackendUnavailableError;
|
|
11
|
+
declare const FactoryBackend_base: Context.ServiceClass<FactoryBackend, "@usine/server/FactoryBackend", {
|
|
12
|
+
readonly overview: Effect.Effect<FactoryOverview, BackendError>;
|
|
13
|
+
readonly detail: (runId: Run.RunId, request: TranscriptRequest) => Effect.Effect<RunDetailView, BackendError>;
|
|
14
|
+
readonly approve: (runId: Run.RunId) => Effect.Effect<void, BackendError>;
|
|
15
|
+
readonly retry: (runId: Run.RunId) => Effect.Effect<void, BackendError>;
|
|
16
|
+
readonly cancel: (runId: Run.RunId) => Effect.Effect<void, BackendError>;
|
|
17
|
+
readonly moveBoardItem: (move: BoardMove) => Effect.Effect<void, BackendError>;
|
|
18
|
+
readonly pause: (loop: Loop.LoopName) => Effect.Effect<void, BackendError>;
|
|
19
|
+
readonly unpause: (loop: Loop.LoopName) => Effect.Effect<void, BackendError>;
|
|
20
|
+
}>;
|
|
21
|
+
export declare class FactoryBackend extends FactoryBackend_base {
|
|
22
|
+
}
|
|
23
|
+
export declare const liveBackendLayer: (instance: LoadedInstance, operator: Operator["Service"]) => Layer.Layer<FactoryBackend>;
|
|
24
|
+
export declare const needsSetupBackendLayer: (instance: LoadedInstance, errorTag: string, observation: OperatorObservation["Service"]) => Layer.Layer<FactoryBackend>;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EACV,eAAe,EACf,SAAS,EACT,QAAQ,EACR,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;;;;AAExD,qBAAa,uBAAwB,SAAQ,4BAI3C;CAAG;AAEL,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,uBAAuB,CAAC;;uBAK5C,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,YAAY,CAAC;qBAC9C,CACf,KAAK,EAAE,GAAG,CAAC,KAAK,EAChB,OAAO,EAAE,iBAAiB,KACvB,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC;sBAC7B,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC;oBACzD,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC;qBACtD,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC;4BAChD,CAAC,IAAI,EAAE,SAAS,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC;oBAC9D,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC;sBACxD,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC;;AAbhF,qBAAa,cAAe,SAAQ,mBAeD;CAAG;AAEtC,eAAO,MAAM,gBAAgB,aACjB,cAAc,YACd,QAAQ,CAAC,SAAS,CAAC,KAC5B,KAAK,CAAC,KAAK,CAAC,cAAc,CAUzB,CAAC;AAEL,eAAO,MAAM,sBAAsB,aACvB,cAAc,YACd,MAAM,eACH,mBAAmB,CAAC,SAAS,CAAC,KAC1C,KAAK,CAAC,KAAK,CAAC,cAAc,CAuB5B,CAAC"}
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Loop, Run } from "@usine/core";
|
|
2
|
+
import { NeedsSetupOverview } from "@usine/orchestrator";
|
|
3
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
4
|
+
export class BackendUnavailableError extends Schema.TaggedError("@usine/server/BackendUnavailableError")("BackendUnavailableError", {
|
|
5
|
+
message: Schema.String,
|
|
6
|
+
}) {
|
|
7
|
+
}
|
|
8
|
+
export class FactoryBackend extends Context.Service()("@usine/server/FactoryBackend") {
|
|
9
|
+
}
|
|
10
|
+
export const liveBackendLayer = (instance, operator) => Layer.succeed(FactoryBackend)({
|
|
11
|
+
overview: operator.overview(instance),
|
|
12
|
+
detail: operator.detail,
|
|
13
|
+
approve: operator.approve,
|
|
14
|
+
retry: operator.retry,
|
|
15
|
+
cancel: operator.cancel,
|
|
16
|
+
moveBoardItem: (move) => operator.moveBoardItem(instance, move),
|
|
17
|
+
pause: (loop) => operator.pause(loop).pipe(Effect.asVoid),
|
|
18
|
+
unpause: (loop) => operator.unpause(loop).pipe(Effect.asVoid),
|
|
19
|
+
});
|
|
20
|
+
export const needsSetupBackendLayer = (instance, errorTag, observation) => {
|
|
21
|
+
const unavailable = new BackendUnavailableError({
|
|
22
|
+
message: "GitHub App setup is required before operator actions are available.",
|
|
23
|
+
});
|
|
24
|
+
const overview = observation
|
|
25
|
+
.overview(instance)
|
|
26
|
+
.pipe(Effect.map(({ projects, loops, needsYou }) => new NeedsSetupOverview({ errorTag, projects, loops, needsYou })));
|
|
27
|
+
const fail = () => Effect.fail(unavailable);
|
|
28
|
+
return Layer.succeed(FactoryBackend)({
|
|
29
|
+
overview,
|
|
30
|
+
detail: observation.detail,
|
|
31
|
+
approve: fail,
|
|
32
|
+
retry: fail,
|
|
33
|
+
cancel: fail,
|
|
34
|
+
moveBoardItem: fail,
|
|
35
|
+
pause: fail,
|
|
36
|
+
unpause: fail,
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAUxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExD,MAAM,OAAO,uBAAwB,SAAQ,MAAM,CAAC,WAAW,CAC7D,uCAAuC,CACxC,CAAC,yBAAyB,EAAE;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CAAC;CAAG;AAIL,MAAM,OAAO,cAAe,SAAQ,OAAO,CAAC,OAAO,EAehD,CAAC,8BAA8B,CAAC;CAAG;AAEtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,QAAwB,EACxB,QAA6B,EACA,EAAE,CAC/B,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACrC,MAAM,EAAE,QAAQ,CAAC,MAAM;IACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;IACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;IACvB,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC;IAC/D,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IACzD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;CAC9D,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,QAAwB,EACxB,QAAgB,EAChB,WAA2C,EACd,EAAE;IAC/B,MAAM,WAAW,GAAG,IAAI,uBAAuB,CAAC;QAC9C,OAAO,EAAE,qEAAqE;KAC/E,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,WAAW;SACzB,QAAQ,CAAC,QAAQ,CAAC;SAClB,IAAI,CACH,MAAM,CAAC,GAAG,CACR,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAChC,IAAI,kBAAkB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAClE,CACF,CAAC;IACJ,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACnC,QAAQ;QACR,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,IAAI;KACd,CAAC,CAAC;AACL,CAAC,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Duration, Effect, FileSystem, Option, Schema } from "effect";
|
|
2
|
+
import { ChildProcessSpawner } from "effect/unstable/process";
|
|
3
|
+
import type { ServeError } from "effect/unstable/http/HttpServerError";
|
|
4
|
+
export declare const DEFAULT_PORT = 7331;
|
|
5
|
+
export declare const BROWSER_OPEN_TIMEOUT = "2 seconds";
|
|
6
|
+
export declare const Port: Schema.brand<Schema.Int, "ServerPort">;
|
|
7
|
+
export type Port = typeof Port.Type;
|
|
8
|
+
export declare const CLI_USAGE = "Usage:\n usine up [--port <port>]\n usine --help\n usine --version\n\nOptions:\n -p, --port <port> Listen on this port (default: 7331)\n -h, --help Print this usage\n -v, --version Print the pinned @usine/server version\n\nEnvironment:\n USINE_PORT Listen on this port when --port is absent";
|
|
9
|
+
declare const CliUsageError_base: Schema.Class<CliUsageError, Schema.TaggedStruct<"CliUsageError", {
|
|
10
|
+
readonly message: Schema.String;
|
|
11
|
+
}>, import("effect/Cause").YieldableError>;
|
|
12
|
+
export declare class CliUsageError extends CliUsageError_base {
|
|
13
|
+
}
|
|
14
|
+
declare const CliVersionError_base: Schema.Class<CliVersionError, Schema.TaggedStruct<"CliVersionError", {
|
|
15
|
+
readonly cause: Schema.Defect;
|
|
16
|
+
}>, import("effect/Cause").YieldableError>;
|
|
17
|
+
export declare class CliVersionError extends CliVersionError_base {
|
|
18
|
+
}
|
|
19
|
+
declare const PortInUseError_base: Schema.Class<PortInUseError, Schema.TaggedStruct<"PortInUseError", {
|
|
20
|
+
readonly port: Schema.brand<Schema.Int, "ServerPort">;
|
|
21
|
+
}>, import("effect/Cause").YieldableError>;
|
|
22
|
+
export declare class PortInUseError extends PortInUseError_base {
|
|
23
|
+
}
|
|
24
|
+
declare const ServerStartError_base: Schema.Class<ServerStartError, Schema.TaggedStruct<"ServerStartError", {
|
|
25
|
+
readonly port: Schema.brand<Schema.Int, "ServerPort">;
|
|
26
|
+
readonly cause: Schema.Defect;
|
|
27
|
+
}>, import("effect/Cause").YieldableError>;
|
|
28
|
+
export declare class ServerStartError extends ServerStartError_base {
|
|
29
|
+
}
|
|
30
|
+
declare const BrowserOpenError_base: Schema.Class<BrowserOpenError, Schema.TaggedStruct<"BrowserOpenError", {
|
|
31
|
+
readonly command: Schema.String;
|
|
32
|
+
readonly cause: Schema.Defect;
|
|
33
|
+
}>, import("effect/Cause").YieldableError>;
|
|
34
|
+
export declare class BrowserOpenError extends BrowserOpenError_base {
|
|
35
|
+
}
|
|
36
|
+
declare const HelpCommand_base: Schema.Class<HelpCommand, Schema.TaggedStruct<"HelpCommand", {}>, {}>;
|
|
37
|
+
export declare class HelpCommand extends HelpCommand_base {
|
|
38
|
+
}
|
|
39
|
+
declare const VersionCommand_base: Schema.Class<VersionCommand, Schema.TaggedStruct<"VersionCommand", {}>, {}>;
|
|
40
|
+
export declare class VersionCommand extends VersionCommand_base {
|
|
41
|
+
}
|
|
42
|
+
declare const UpCommand_base: Schema.Class<UpCommand, Schema.TaggedStruct<"UpCommand", {
|
|
43
|
+
readonly port: Schema.brand<Schema.Int, "ServerPort">;
|
|
44
|
+
}>, {}>;
|
|
45
|
+
export declare class UpCommand extends UpCommand_base {
|
|
46
|
+
}
|
|
47
|
+
export declare const CliCommand: Schema.Union<readonly [typeof HelpCommand, typeof VersionCommand, typeof UpCommand]>;
|
|
48
|
+
export type CliCommand = typeof CliCommand.Type;
|
|
49
|
+
/** Parse process arguments and environment into one validated CLI command. */
|
|
50
|
+
export declare const parseCli: (args: readonly string[], environmentPort: Option.Option<string>) => Effect.Effect<HelpCommand | UpCommand | VersionCommand, CliUsageError, never>;
|
|
51
|
+
/** Read the package version npm pinned for this installed server. */
|
|
52
|
+
export declare const packageVersion: () => Effect.Effect<string, CliVersionError, FileSystem.FileSystem>;
|
|
53
|
+
/** Translate Effect's low-level bind failure into the CLI's startup vocabulary. */
|
|
54
|
+
export declare const serverStartError: (port: Port, error: ServeError) => PortInUseError | ServerStartError;
|
|
55
|
+
/** Open the setup page after the HTTP server has bound successfully. */
|
|
56
|
+
export declare const openBrowser: (url: string, timeout?: Duration.Input | undefined) => Effect.Effect<undefined, BrowserOpenError, ChildProcessSpawner.ChildProcessSpawner>;
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/E,OAAO,EAAgB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAEvE,eAAO,MAAM,YAAY,OAAQ,CAAC;AAClC,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAEhD,eAAO,MAAM,IAAI,wCAEiB,CAAC;AACnC,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAC;AAIpC,eAAO,MAAM,SAAS,oUAWyC,CAAC;;;;AAEhE,qBAAa,aAAc,SAAQ,kBAGlC;CAAG;;;;AAEJ,qBAAa,eAAgB,SAAQ,oBAEW;CAAG;;;;AAEnD,qBAAa,cAAe,SAAQ,mBAED;CAAG;;;;;AAEtC,qBAAa,gBAAiB,SAAQ,qBAEuB;CAAG;;;;;AAEhE,qBAAa,gBAAiB,SAAQ,qBAKpC;CAAG;;AAEL,qBAAa,WAAY,SAAQ,gBAGhC;CAAG;;AAEJ,qBAAa,cAAe,SAAQ,mBAEb;CAAG;;;;AAE1B,qBAAa,SAAU,SAAQ,cAG9B;CAAG;AAEJ,eAAO,MAAM,UAAU,sFAAyD,CAAC;AACjF,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAkChD,8EAA8E;AAC9E,eAAO,MAAM,QAAQ,oJAenB,CAAC;AAMH,qEAAqE;AACrE,eAAO,MAAM,cAAc,qEAUzB,CAAC;AAIH,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,SAAU,IAAI,SAAS,UAAU,sCAGN,CAAC;AAUzD,wEAAwE;AACxE,eAAO,MAAM,WAAW,4IAyBtB,CAAC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { Console, Duration, Effect, FileSystem, Option, Schema } from "effect";
|
|
3
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
4
|
+
export const DEFAULT_PORT = 7_331;
|
|
5
|
+
export const BROWSER_OPEN_TIMEOUT = "2 seconds";
|
|
6
|
+
export const Port = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 65_535 }, { message: "Expected a TCP port" })).pipe(Schema.brand("ServerPort"));
|
|
7
|
+
const PortFromString = Schema.NumberFromString.pipe(Schema.decodeTo(Port));
|
|
8
|
+
export const CLI_USAGE = `Usage:
|
|
9
|
+
usine up [--port <port>]
|
|
10
|
+
usine --help
|
|
11
|
+
usine --version
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
-p, --port <port> Listen on this port (default: 7331)
|
|
15
|
+
-h, --help Print this usage
|
|
16
|
+
-v, --version Print the pinned @usine/server version
|
|
17
|
+
|
|
18
|
+
Environment:
|
|
19
|
+
USINE_PORT Listen on this port when --port is absent`;
|
|
20
|
+
export class CliUsageError extends Schema.TaggedError("@usine/server/CliUsageError")("CliUsageError", { message: Schema.String }) {
|
|
21
|
+
}
|
|
22
|
+
export class CliVersionError extends Schema.TaggedError("@usine/server/CliVersionError")("CliVersionError", { cause: Schema.Defect() }) {
|
|
23
|
+
}
|
|
24
|
+
export class PortInUseError extends Schema.TaggedError("@usine/server/PortInUseError")("PortInUseError", { port: Port }) {
|
|
25
|
+
}
|
|
26
|
+
export class ServerStartError extends Schema.TaggedError("@usine/server/ServerStartError")("ServerStartError", { port: Port, cause: Schema.Defect() }) {
|
|
27
|
+
}
|
|
28
|
+
export class BrowserOpenError extends Schema.TaggedError("@usine/server/BrowserOpenError")("BrowserOpenError", {
|
|
29
|
+
command: Schema.String,
|
|
30
|
+
cause: Schema.Defect(),
|
|
31
|
+
}) {
|
|
32
|
+
}
|
|
33
|
+
export class HelpCommand extends Schema.TaggedClass("@usine/server/HelpCommand")("HelpCommand", {}) {
|
|
34
|
+
}
|
|
35
|
+
export class VersionCommand extends Schema.TaggedClass("@usine/server/VersionCommand")("VersionCommand", {}) {
|
|
36
|
+
}
|
|
37
|
+
export class UpCommand extends Schema.TaggedClass("@usine/server/UpCommand")("UpCommand", { port: Port }) {
|
|
38
|
+
}
|
|
39
|
+
export const CliCommand = Schema.Union([HelpCommand, VersionCommand, UpCommand]);
|
|
40
|
+
const usageError = (message = CLI_USAGE) => new CliUsageError({ message });
|
|
41
|
+
const decodePort = (source, value) => Schema.decodeUnknownEffect(PortFromString)(value).pipe(Effect.mapError(() => usageError(`${source} must be a whole number between 1 and 65535.\n\n${CLI_USAGE}`)));
|
|
42
|
+
const upPort = (args, environmentPort) => {
|
|
43
|
+
if (args.length === 0) {
|
|
44
|
+
return Option.match(environmentPort, {
|
|
45
|
+
onNone: () => Effect.succeed(Port.make(DEFAULT_PORT)),
|
|
46
|
+
onSome: (value) => decodePort("USINE_PORT", value),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (args.length === 2 && (args[0] === "--port" || args[0] === "-p")) {
|
|
50
|
+
const value = args[1];
|
|
51
|
+
return value === undefined ? Effect.fail(usageError()) : decodePort("--port", value);
|
|
52
|
+
}
|
|
53
|
+
if (args.length === 1 && args[0]?.startsWith("--port=")) {
|
|
54
|
+
return decodePort("--port", args[0].slice("--port=".length));
|
|
55
|
+
}
|
|
56
|
+
return Effect.fail(usageError());
|
|
57
|
+
};
|
|
58
|
+
/** Parse process arguments and environment into one validated CLI command. */
|
|
59
|
+
export const parseCli = Effect.fn("Cli.parse")((args, environmentPort) => {
|
|
60
|
+
if ((args.length === 1 && (args[0] === "--help" || args[0] === "-h")) ||
|
|
61
|
+
(args.length === 2 && args[0] === "up" && (args[1] === "--help" || args[1] === "-h"))) {
|
|
62
|
+
return Effect.succeed(new HelpCommand());
|
|
63
|
+
}
|
|
64
|
+
if (args.length === 1 && (args[0] === "--version" || args[0] === "-v")) {
|
|
65
|
+
return Effect.succeed(new VersionCommand());
|
|
66
|
+
}
|
|
67
|
+
if (args[0] !== "up")
|
|
68
|
+
return Effect.fail(usageError());
|
|
69
|
+
return upPort(args.slice(1), environmentPort).pipe(Effect.map((port) => new UpCommand({ port })));
|
|
70
|
+
});
|
|
71
|
+
const PackageMetadataFromJson = Schema.fromJsonString(Schema.Struct({ version: Schema.NonEmptyString }));
|
|
72
|
+
/** Read the package version npm pinned for this installed server. */
|
|
73
|
+
export const packageVersion = Effect.fn("Cli.packageVersion")(function* () {
|
|
74
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
75
|
+
const packagePath = fileURLToPath(new URL("../package.json", import.meta.url));
|
|
76
|
+
const source = yield* fileSystem
|
|
77
|
+
.readFileString(packagePath)
|
|
78
|
+
.pipe(Effect.mapError((cause) => new CliVersionError({ cause })));
|
|
79
|
+
const metadata = yield* Schema.decodeUnknownEffect(PackageMetadataFromJson)(source).pipe(Effect.mapError((cause) => new CliVersionError({ cause })));
|
|
80
|
+
return metadata.version;
|
|
81
|
+
});
|
|
82
|
+
const AddressInUse = Schema.Struct({ code: Schema.Literal("EADDRINUSE") });
|
|
83
|
+
/** Translate Effect's low-level bind failure into the CLI's startup vocabulary. */
|
|
84
|
+
export const serverStartError = (port, error) => Schema.is(AddressInUse)(error.cause)
|
|
85
|
+
? new PortInUseError({ port })
|
|
86
|
+
: new ServerStartError({ port, cause: error.cause });
|
|
87
|
+
const browserCommand = (url) => {
|
|
88
|
+
if (process.platform === "darwin")
|
|
89
|
+
return { command: "open", args: [url] };
|
|
90
|
+
if (process.platform === "win32") {
|
|
91
|
+
return { command: "cmd.exe", args: ["/c", "start", "", url] };
|
|
92
|
+
}
|
|
93
|
+
return { command: "xdg-open", args: [url] };
|
|
94
|
+
};
|
|
95
|
+
/** Open the setup page after the HTTP server has bound successfully. */
|
|
96
|
+
export const openBrowser = Effect.fn("Cli.openBrowser")(function* (url, timeout = BROWSER_OPEN_TIMEOUT) {
|
|
97
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
98
|
+
const { command, args } = browserCommand(url);
|
|
99
|
+
const exitCode = yield* spawner
|
|
100
|
+
.exitCode(ChildProcess.make(command, args, {
|
|
101
|
+
detached: false,
|
|
102
|
+
forceKillAfter: "1 second",
|
|
103
|
+
stdin: "ignore",
|
|
104
|
+
stdout: "ignore",
|
|
105
|
+
stderr: "ignore",
|
|
106
|
+
}))
|
|
107
|
+
.pipe(Effect.timeout(timeout))
|
|
108
|
+
.pipe(Effect.mapError((cause) => new BrowserOpenError({ command, cause })));
|
|
109
|
+
if (Number(exitCode) !== 0) {
|
|
110
|
+
return yield* new BrowserOpenError({
|
|
111
|
+
command,
|
|
112
|
+
cause: new Error(`${command} exited with code ${String(exitCode)}`),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
yield* Console.log(`Opened ${url}`);
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG5E,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAClC,MAAM,CAAC,MAAM,oBAAoB,GAAG,WAAW,CAAC;AAEhD,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAClC,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,qBAAqB,EAAE,CAAC,CACtF,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;AAGnC,MAAM,cAAc,GAAG,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAE3E,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;+DAWsC,CAAC;AAEhE,MAAM,OAAO,aAAc,SAAQ,MAAM,CAAC,WAAW,CAAgB,6BAA6B,CAAC,CACjG,eAAe,EACf,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAC3B;CAAG;AAEJ,MAAM,OAAO,eAAgB,SAAQ,MAAM,CAAC,WAAW,CACrD,+BAA+B,CAChC,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;CAAG;AAEnD,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,CACpD,8BAA8B,CAC/B,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;CAAG;AAEtC,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,WAAW,CACtD,gCAAgC,CACjC,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;CAAG;AAEhE,MAAM,OAAO,gBAAiB,SAAQ,MAAM,CAAC,WAAW,CACtD,gCAAgC,CACjC,CAAC,kBAAkB,EAAE;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;CACvB,CAAC;CAAG;AAEL,MAAM,OAAO,WAAY,SAAQ,MAAM,CAAC,WAAW,CAAc,2BAA2B,CAAC,CAC3F,aAAa,EACb,EAAE,CACH;CAAG;AAEJ,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,CACpD,8BAA8B,CAC/B,CAAC,gBAAgB,EAAE,EAAE,CAAC;CAAG;AAE1B,MAAM,OAAO,SAAU,SAAQ,MAAM,CAAC,WAAW,CAAY,yBAAyB,CAAC,CACrF,WAAW,EACX,EAAE,IAAI,EAAE,IAAI,EAAE,CACf;CAAG;AAEJ,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC;AAGjF,MAAM,UAAU,GAAG,CAAC,OAAO,GAAG,SAAS,EAAE,EAAE,CAAC,IAAI,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;AAE3E,MAAM,UAAU,GAAG,CACjB,MAA+B,EAC/B,KAAa,EACuB,EAAE,CACtC,MAAM,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CACpD,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CACnB,UAAU,CAAC,GAAG,MAAM,mDAAmD,SAAS,EAAE,CAAC,CACpF,CACF,CAAC;AAEJ,MAAM,MAAM,GAAG,CACb,IAA2B,EAC3B,eAAsC,EACF,EAAE;IACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC,KAAK,CAAC,eAAe,EAAE;YACnC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACrD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC;SACnD,CAAC,CAAC;IACL,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACxD,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF,8EAA8E;AAC9E,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAC7C,IAA2B,EAC3B,eAAsC,EACI,EAAE;IAC5C,IACE,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACjE,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,EACrF,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACvE,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IACvD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC,CAAC,CAAC;AAEH,MAAM,uBAAuB,GAAG,MAAM,CAAC,cAAc,CACnD,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAClD,CAAC;AAEF,qEAAqE;AACrE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC;IACrE,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IAChD,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/E,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU;SAC7B,cAAc,CAAC,WAAW,CAAC;SAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CACtF,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAC3D,CAAC;IACF,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AAE3E,mFAAmF;AACnF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAU,EAAE,KAAiB,EAAE,EAAE,CAChE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IAClC,CAAC,CAAC,IAAI,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC;IAC9B,CAAC,CAAC,IAAI,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;AAEzD,MAAM,cAAc,GAAG,CAAC,GAAW,EAAE,EAAE;IACrC,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3E,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;IAChE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9C,CAAC,CAAC;AAEF,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,QAAQ,CAAC,EAC/D,GAAW,EACX,OAAO,GAAmB,oBAAoB;IAE9C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,mBAAmB,CAAC;IAC/D,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,OAAO;SAC5B,QAAQ,CACP,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE;QAC/B,QAAQ,EAAE,KAAK;QACf,cAAc,EAAE,UAAU;QAC1B,KAAK,EAAE,QAAQ;QACf,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,QAAQ;KACjB,CAAC,CACH;SACA,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;SAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9E,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC;YACjC,OAAO;YACP,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,qBAAqB,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;SACpE,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { HttpRouter } from "effect/unstable/http";
|
|
3
|
+
import { FactoryBackend } from "./backend.ts";
|
|
4
|
+
export declare const apiRoutes: Layer.Layer<never, never, HttpRouter.HttpRouter | HttpRouter.Request<"Requires", FactoryBackend>>;
|
|
5
|
+
export declare const staticRoutes: (webRoot: string) => Layer.Layer<never, never, HttpRouter.HttpRouter | HttpRouter.Request<"Error", import("effect/PlatformError").PlatformError> | HttpRouter.Request<"Requires", import("effect/unstable/http/HttpPlatform").HttpPlatform>>;
|
|
6
|
+
export declare const serverLayer: (backend: Layer.Layer<FactoryBackend>, webRoot: string, port: number) => Layer.Layer<never, import("effect/unstable/http/HttpServerError").ServeError, never>;
|
|
7
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAeA,OAAO,EAAU,KAAK,EAAiB,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAY,UAAU,EAAyC,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,cAAc,EAAqB,MAAM,cAAc,CAAC;AA4MjE,eAAO,MAAM,SAAS,mGA0DpB,CAAC;AAEH,eAAO,MAAM,YAAY,YAAa,MAAM,4NAsB3C,CAAC;AAEF,eAAO,MAAM,WAAW,YACb,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,WAC3B,MAAM,QACT,MAAM,yFAQb,CAAC"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { resolve, sep } from "node:path";
|
|
3
|
+
import { NodeHttpServer } from "@effect/platform-node";
|
|
4
|
+
import { Attempt, Item, Loop, Repository, Run } from "@usine/core";
|
|
5
|
+
import { BoardLaneId, BoardMove, ColdTranscriptRequest, EarlierTranscriptRequest, FactoryOverview, RunDetailView, TailTranscriptRequest, TranscriptCursor, } from "@usine/orchestrator";
|
|
6
|
+
import { Effect, Layer, Match, Schema } from "effect";
|
|
7
|
+
import { HttpBody, HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
8
|
+
import { FactoryBackend } from "./backend.js";
|
|
9
|
+
const PathRun = Schema.Struct({ runId: Run.RunId });
|
|
10
|
+
const PathLoop = Schema.Struct({ loop: Loop.LoopName });
|
|
11
|
+
const RepositoryPart = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9_.-]+$/u, { message: "Expected a repository path segment" }));
|
|
12
|
+
const PathBoardItem = Schema.Struct({
|
|
13
|
+
owner: RepositoryPart,
|
|
14
|
+
repository: RepositoryPart,
|
|
15
|
+
item: Schema.FiniteFromString.pipe(Schema.decodeTo(Item.Number)),
|
|
16
|
+
});
|
|
17
|
+
const BoardMoveBody = Schema.Struct({ toLane: BoardLaneId });
|
|
18
|
+
const TranscriptCursorFromString = Schema.FiniteFromString.pipe(Schema.decodeTo(TranscriptCursor));
|
|
19
|
+
const RunDetailSearch = Schema.Union([
|
|
20
|
+
Schema.Struct({ _tag: Schema.Literal("ColdTranscriptRequest") }),
|
|
21
|
+
Schema.Struct({
|
|
22
|
+
_tag: Schema.Literal("TailTranscriptRequest"),
|
|
23
|
+
after: TranscriptCursorFromString,
|
|
24
|
+
}),
|
|
25
|
+
Schema.Struct({
|
|
26
|
+
_tag: Schema.Literal("EarlierTranscriptRequest"),
|
|
27
|
+
attemptId: Attempt.AttemptId,
|
|
28
|
+
before: TranscriptCursorFromString,
|
|
29
|
+
}),
|
|
30
|
+
]);
|
|
31
|
+
const ApiOk = Schema.Struct({ ok: Schema.Literal(true) });
|
|
32
|
+
const ApiError = Schema.Struct({ error: Schema.String, message: Schema.String });
|
|
33
|
+
const ok = HttpServerResponse.schemaJson(ApiOk);
|
|
34
|
+
class InvalidRequestError extends Schema.TaggedError("@usine/server/InvalidRequestError")("InvalidRequestError", { message: Schema.String }) {
|
|
35
|
+
}
|
|
36
|
+
const errorResponse = (error) => {
|
|
37
|
+
const response = (() => {
|
|
38
|
+
switch (error._tag) {
|
|
39
|
+
case "RunNotFoundError":
|
|
40
|
+
return { status: 404, message: `Run ${error.runId} was not found.` };
|
|
41
|
+
case "RunOperationRejectedError":
|
|
42
|
+
return {
|
|
43
|
+
status: 409,
|
|
44
|
+
message: `Cannot ${error.operation} Run ${error.runId} while it is ${error.state}.`,
|
|
45
|
+
};
|
|
46
|
+
case "TranscriptAttemptNotFoundError":
|
|
47
|
+
return {
|
|
48
|
+
status: 404,
|
|
49
|
+
message: `Attempt ${error.attemptId} does not belong to Run ${error.runId}.`,
|
|
50
|
+
};
|
|
51
|
+
case "BackendUnavailableError":
|
|
52
|
+
return { status: 503, message: error.message };
|
|
53
|
+
case "BoardProjectNotFoundError":
|
|
54
|
+
return { status: 404, message: `Project ${error.repository} was not found.` };
|
|
55
|
+
case "BoardItemNotFoundError":
|
|
56
|
+
return {
|
|
57
|
+
status: 404,
|
|
58
|
+
message: `Item ${error.repository}#${error.item} was not found on the Board.`,
|
|
59
|
+
};
|
|
60
|
+
case "BoardLaneNotFoundError":
|
|
61
|
+
return { status: 404, message: `Board Lane ${error.lane} was not found.` };
|
|
62
|
+
case "BoardLaneReadOnlyError":
|
|
63
|
+
return {
|
|
64
|
+
status: 409,
|
|
65
|
+
message: `Board Lane ${error.lane} is derived from Item state and cannot accept label moves.`,
|
|
66
|
+
};
|
|
67
|
+
case "InstallationDiscoveryError":
|
|
68
|
+
case "AppPermissionError":
|
|
69
|
+
return { status: 503, message: error.message };
|
|
70
|
+
case "GitHubApiError":
|
|
71
|
+
case "InvalidGitHubResponseError":
|
|
72
|
+
return { status: 502, message: error.message };
|
|
73
|
+
case "GitHubRateLimitError":
|
|
74
|
+
return { status: 503, message: error.message };
|
|
75
|
+
case "EnginePersistenceError":
|
|
76
|
+
return {
|
|
77
|
+
status: 500,
|
|
78
|
+
message: `Runtime persistence failed while trying to ${error.operation}.`,
|
|
79
|
+
};
|
|
80
|
+
case "AttemptCancellationError":
|
|
81
|
+
return {
|
|
82
|
+
status: 500,
|
|
83
|
+
message: `Attempt ${error.attemptId} could not be canceled.`,
|
|
84
|
+
};
|
|
85
|
+
case "SchemaError":
|
|
86
|
+
return { status: 400, message: `Invalid request: ${error.message}` };
|
|
87
|
+
case "InvalidRequestError":
|
|
88
|
+
return { status: 400, message: error.message };
|
|
89
|
+
case "HttpBodyError":
|
|
90
|
+
return { status: 500, message: "The server could not encode its response." };
|
|
91
|
+
}
|
|
92
|
+
})();
|
|
93
|
+
return HttpServerResponse.schemaJson(ApiError)({ error: error._tag, message: response.message }, { status: response.status }).pipe(Effect.orDie);
|
|
94
|
+
};
|
|
95
|
+
const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "[::1]", "::1"]);
|
|
96
|
+
/**
|
|
97
|
+
* Cross-site request forgery guard. Browsers attach `Origin` to every
|
|
98
|
+
* cross-origin request — including the "simple" POSTs this API accepts — so a
|
|
99
|
+
* present, non-loopback origin can only be another site driving the
|
|
100
|
+
* operator's browser, and its side effect must be refused before the handler
|
|
101
|
+
* runs. Requests without the header (curl, same-origin GETs) pass through.
|
|
102
|
+
*/
|
|
103
|
+
const crossSiteOrigin = (request) => {
|
|
104
|
+
const origin = request.headers.origin;
|
|
105
|
+
if (origin === undefined)
|
|
106
|
+
return false;
|
|
107
|
+
try {
|
|
108
|
+
return !LOOPBACK_HOSTS.has(new URL(origin).hostname);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const handled = (effect) => Effect.gen(function* () {
|
|
115
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
116
|
+
if (crossSiteOrigin(request)) {
|
|
117
|
+
yield* Effect.logWarning("Cross-site request to the operator API was refused", {
|
|
118
|
+
origin: request.headers.origin,
|
|
119
|
+
url: request.url,
|
|
120
|
+
});
|
|
121
|
+
return yield* HttpServerResponse.schemaJson(ApiError)({ error: "CrossSiteRequestError", message: "Cross-site requests are refused." }, { status: 403 }).pipe(Effect.orDie);
|
|
122
|
+
}
|
|
123
|
+
return yield* effect.pipe(Effect.catch(errorResponse));
|
|
124
|
+
});
|
|
125
|
+
const runAction = (action) => handled(Effect.gen(function* () {
|
|
126
|
+
const backend = yield* FactoryBackend;
|
|
127
|
+
const { runId } = yield* HttpRouter.schemaPathParams(PathRun);
|
|
128
|
+
yield* action(backend, runId);
|
|
129
|
+
return yield* ok({ ok: true });
|
|
130
|
+
}));
|
|
131
|
+
const loopAction = (action) => handled(Effect.gen(function* () {
|
|
132
|
+
const backend = yield* FactoryBackend;
|
|
133
|
+
const { loop } = yield* HttpRouter.schemaPathParams(PathLoop);
|
|
134
|
+
yield* action(backend, loop);
|
|
135
|
+
return yield* ok({ ok: true });
|
|
136
|
+
}));
|
|
137
|
+
const transcriptRequest = (request) => {
|
|
138
|
+
const params = new URL(request.url, "http://localhost").searchParams;
|
|
139
|
+
const after = params.get("after");
|
|
140
|
+
const before = params.get("before");
|
|
141
|
+
const attemptId = params.get("attempt");
|
|
142
|
+
const input = after === null && before === null && attemptId === null
|
|
143
|
+
? { _tag: "ColdTranscriptRequest" }
|
|
144
|
+
: after !== null && before === null && attemptId === null
|
|
145
|
+
? { _tag: "TailTranscriptRequest", after }
|
|
146
|
+
: after === null && before !== null && attemptId !== null
|
|
147
|
+
? { _tag: "EarlierTranscriptRequest", attemptId, before }
|
|
148
|
+
: { _tag: "InvalidTranscriptRequest" };
|
|
149
|
+
return Schema.decodeUnknownEffect(RunDetailSearch)(input, { onExcessProperty: "error" }).pipe(Effect.map((search) => Match.valueTags(search, {
|
|
150
|
+
ColdTranscriptRequest: () => new ColdTranscriptRequest(),
|
|
151
|
+
TailTranscriptRequest: ({ after }) => new TailTranscriptRequest({ after }),
|
|
152
|
+
EarlierTranscriptRequest: ({ attemptId, before }) => new EarlierTranscriptRequest({ attemptId, before }),
|
|
153
|
+
})));
|
|
154
|
+
};
|
|
155
|
+
const boardMove = Effect.fn("Http.boardMove")(function* () {
|
|
156
|
+
const { owner, repository: name, item } = yield* HttpRouter.schemaPathParams(PathBoardItem);
|
|
157
|
+
const repository = yield* Schema.decodeUnknownEffect(Repository.Name)(`${owner}/${name}`);
|
|
158
|
+
const { toLane } = yield* HttpServerRequest.schemaBodyJson(BoardMoveBody, {
|
|
159
|
+
onExcessProperty: "error",
|
|
160
|
+
}).pipe(Effect.mapError((error) => new InvalidRequestError({ message: `Invalid request: ${error.message}` })));
|
|
161
|
+
return new BoardMove({ repository, item, toLane });
|
|
162
|
+
});
|
|
163
|
+
export const apiRoutes = HttpRouter.addAll([
|
|
164
|
+
HttpRouter.route("GET", "/api/overview", handled(Effect.gen(function* () {
|
|
165
|
+
const backend = yield* FactoryBackend;
|
|
166
|
+
return yield* HttpServerResponse.schemaJson(FactoryOverview)(yield* backend.overview);
|
|
167
|
+
}))),
|
|
168
|
+
HttpRouter.route("GET", "/api/runs/:runId", (request) => handled(Effect.gen(function* () {
|
|
169
|
+
const backend = yield* FactoryBackend;
|
|
170
|
+
const { runId } = yield* HttpRouter.schemaPathParams(PathRun);
|
|
171
|
+
return yield* HttpServerResponse.schemaJson(RunDetailView)(yield* backend.detail(runId, yield* transcriptRequest(request)));
|
|
172
|
+
}))),
|
|
173
|
+
HttpRouter.route("POST", "/api/runs/:runId/approve", runAction((backend, id) => backend.approve(id))),
|
|
174
|
+
HttpRouter.route("POST", "/api/projects/:owner/:repository/items/:item/lane", handled(Effect.gen(function* () {
|
|
175
|
+
const backend = yield* FactoryBackend;
|
|
176
|
+
yield* backend.moveBoardItem(yield* boardMove());
|
|
177
|
+
return yield* ok({ ok: true });
|
|
178
|
+
}))),
|
|
179
|
+
HttpRouter.route("POST", "/api/runs/:runId/retry", runAction((backend, id) => backend.retry(id))),
|
|
180
|
+
HttpRouter.route("POST", "/api/runs/:runId/cancel", runAction((backend, id) => backend.cancel(id))),
|
|
181
|
+
HttpRouter.route("POST", "/api/loops/:loop/pause", loopAction((backend, loop) => backend.pause(loop))),
|
|
182
|
+
HttpRouter.route("POST", "/api/loops/:loop/unpause", loopAction((backend, loop) => backend.unpause(loop))),
|
|
183
|
+
]);
|
|
184
|
+
export const staticRoutes = (webRoot) => {
|
|
185
|
+
const assetsRoot = resolve(webRoot, "assets");
|
|
186
|
+
return HttpRouter.addAll([
|
|
187
|
+
HttpRouter.route("GET", "/", HttpServerResponse.file(resolve(webRoot, "index.html"))),
|
|
188
|
+
HttpRouter.route("GET", "/assets/*", (request) => Effect.gen(function* () {
|
|
189
|
+
const pathname = decodeURIComponent(new URL(request.url, "http://localhost").pathname);
|
|
190
|
+
const asset = resolve(webRoot, pathname.slice(1));
|
|
191
|
+
if (!asset.startsWith(`${assetsRoot}${sep}`)) {
|
|
192
|
+
return HttpServerResponse.text("Not found", { status: 404 });
|
|
193
|
+
}
|
|
194
|
+
return yield* HttpServerResponse.file(asset).pipe(Effect.catch((error) => Effect.logDebug("Static asset could not be served", {
|
|
195
|
+
asset,
|
|
196
|
+
message: String(error),
|
|
197
|
+
}).pipe(Effect.as(HttpServerResponse.text("Not found", { status: 404 })))));
|
|
198
|
+
})),
|
|
199
|
+
]);
|
|
200
|
+
};
|
|
201
|
+
export const serverLayer = (backend, webRoot, port) => {
|
|
202
|
+
const routes = Layer.mergeAll(apiRoutes, staticRoutes(webRoot)).pipe(HttpRouter.provideRequest(backend));
|
|
203
|
+
return HttpRouter.serve(routes).pipe(Layer.provide(NodeHttpServer.layer(createServer, { host: "127.0.0.1", port })));
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @usine/server — the usine orchestrator as a headless server: a thin CLI over
|
|
3
|
+
* @usine/orchestrator that serves the web UI.
|
|
4
|
+
*
|
|
5
|
+
* The published `usine` binary lives in main.ts. Application behavior stays
|
|
6
|
+
* in @usine/orchestrator; this package owns only Node and HTTP adaptation.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @usine/server — the usine orchestrator as a headless server: a thin CLI over
|
|
3
|
+
* @usine/orchestrator that serves the web UI.
|
|
4
|
+
*
|
|
5
|
+
* The published `usine` binary lives in main.ts. Application behavior stays
|
|
6
|
+
* in @usine/orchestrator; this package owns only Node and HTTP adaptation.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC"}
|
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,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
3
|
+
import { Console, Effect, Match, Option } from "effect";
|
|
4
|
+
import { CLI_USAGE, packageVersion, parseCli } from "./cli.js";
|
|
5
|
+
const main = Effect.gen(function* () {
|
|
6
|
+
const command = yield* parseCli(process.argv.slice(2), Option.fromNullishOr(process.env.USINE_PORT));
|
|
7
|
+
return yield* Match.valueTags(command, {
|
|
8
|
+
HelpCommand: () => Console.log(CLI_USAGE),
|
|
9
|
+
VersionCommand: () => Effect.flatMap(packageVersion(), Console.log),
|
|
10
|
+
UpCommand: ({ port }) => Effect.flatMap(Effect.promise(() => import("./up.js")), ({ up }) => up(port)),
|
|
11
|
+
});
|
|
12
|
+
}).pipe(Effect.provide(NodeServices.layer), Effect.catchTags({
|
|
13
|
+
CliUsageError: ({ message }) => Console.error(message).pipe(Effect.andThen(Effect.sync(() => {
|
|
14
|
+
process.exitCode = 1;
|
|
15
|
+
}))),
|
|
16
|
+
PortInUseError: ({ port }) => Console.error(`Port ${port} is already in use. Stop the process using it or run usine up --port <port>.`).pipe(Effect.andThen(Effect.sync(() => {
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
}))),
|
|
19
|
+
}));
|
|
20
|
+
NodeRuntime.runMain(main);
|
|
21
|
+
//# 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,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAE/D,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,QAAQ,CAC7B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EACrB,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAC7C,CAAC;IACF,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE;QACrC,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QACzC,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC;QACnE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACtB,MAAM,CAAC,OAAO,CACZ,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EACvC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CACrB;KACJ,CAAC,CAAC;AACL,CAAC,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EAClC,MAAM,CAAC,SAAS,CAAC;IACf,aAAa,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAC7B,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CACzB,MAAM,CAAC,OAAO,CACZ,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACf,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CACH,CACF;IACH,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAC3B,OAAO,CAAC,KAAK,CACX,QAAQ,IAAI,8EAA8E,CAC3F,CAAC,IAAI,CACJ,MAAM,CAAC,OAAO,CACZ,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACf,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CACH,CACF;CACJ,CAAC,CACH,CAAC;AAEF,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|