@omg-dev/cloud 0.6.41
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/dist/auth.d.ts +78 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +206 -0
- package/dist/auth.js.map +1 -0
- package/dist/config.d.ts +46 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +39 -0
- package/dist/config.js.map +1 -0
- package/dist/control-plane.d.ts +116 -0
- package/dist/control-plane.d.ts.map +1 -0
- package/dist/control-plane.js +163 -0
- package/dist/control-plane.js.map +1 -0
- package/dist/grant.d.ts +35 -0
- package/dist/grant.d.ts.map +1 -0
- package/dist/grant.js +85 -0
- package/dist/grant.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/readiness.d.ts +74 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +82 -0
- package/dist/readiness.js.map +1 -0
- package/dist/shared-binding.d.ts +135 -0
- package/dist/shared-binding.d.ts.map +1 -0
- package/dist/shared-binding.js +202 -0
- package/dist/shared-binding.js.map +1 -0
- package/dist/transports.d.ts +44 -0
- package/dist/transports.d.ts.map +1 -0
- package/dist/transports.js +119 -0
- package/dist/transports.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The account's machines, as the control plane reports them.
|
|
3
|
+
*
|
|
4
|
+
* Every dashboard read goes through `POST <controlPlane>/api/computer/<name>`
|
|
5
|
+
* with the account JWT as a bearer. The names are the control plane's own
|
|
6
|
+
* function names, so a new one needs no client change beyond a call.
|
|
7
|
+
*/
|
|
8
|
+
import { CLOUD_BINDING_ID, resolveCloudEndpoints } from "./config";
|
|
9
|
+
import { sharedBindingId, sharedComputerMachineIdentity, sharedComputerOwnerLabel, } from "./shared-binding";
|
|
10
|
+
export function toSharedBinding(computer) {
|
|
11
|
+
const ownerName = computer.name?.trim();
|
|
12
|
+
return {
|
|
13
|
+
id: sharedBindingId(computer.ownerUserId, computer.bindingId),
|
|
14
|
+
online: computer.online ?? true,
|
|
15
|
+
lastSeenAt: null,
|
|
16
|
+
defaultFolder: computer.defaultFolder ?? computer.binding?.defaultFolder ?? null,
|
|
17
|
+
computerUrl: null,
|
|
18
|
+
shared: true,
|
|
19
|
+
ownerUserId: computer.ownerUserId,
|
|
20
|
+
ownerBindingId: computer.bindingId,
|
|
21
|
+
ownerLabel: sharedComputerOwnerLabel(computer),
|
|
22
|
+
ownerName: ownerName && !ownerName.includes("@") ? ownerName : undefined,
|
|
23
|
+
email: computer.email,
|
|
24
|
+
machineLabel: sharedComputerMachineIdentity(computer),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const BLOCKED_CLOUD_STATUSES = new Set(["upgrade_required", "recycled"]);
|
|
28
|
+
/** A cloud Computer that the session proxy would answer with a permanent 425. */
|
|
29
|
+
export function isCloudComputerBlocked(cloud) {
|
|
30
|
+
return BLOCKED_CLOUD_STATUSES.has(cloud?.status ?? "");
|
|
31
|
+
}
|
|
32
|
+
export function createControlPlaneClient(options) {
|
|
33
|
+
const endpoints = resolveCloudEndpoints(options.endpoints);
|
|
34
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
35
|
+
async function call(name, body = {}) {
|
|
36
|
+
const token = await options.getAuthToken();
|
|
37
|
+
if (!token)
|
|
38
|
+
throw new Error("Please sign in again.");
|
|
39
|
+
const response = await fetchImpl(`${endpoints.controlPlaneOrigin}/api/computer/${name}`, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
42
|
+
body: JSON.stringify(body),
|
|
43
|
+
});
|
|
44
|
+
const text = await response.text().catch(() => "");
|
|
45
|
+
let data = {};
|
|
46
|
+
try {
|
|
47
|
+
data = text ? JSON.parse(text) : {};
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
data = {};
|
|
51
|
+
}
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw new Error(data?.error ?? `${name} failed (${response.status})`);
|
|
54
|
+
}
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
57
|
+
const listBindings = async () => (await call("listComputerBindings")).bindings ?? [];
|
|
58
|
+
const listSharedComputers = async () => ((await call("listSharedComputers")).computers ?? []).map(toSharedBinding);
|
|
59
|
+
const getCloudComputer = async () => (await call("getCloudComputer")) ?? null;
|
|
60
|
+
return {
|
|
61
|
+
call,
|
|
62
|
+
listBindings,
|
|
63
|
+
listSharedComputers,
|
|
64
|
+
getCloudComputer,
|
|
65
|
+
getOrProvisionCloudComputer: async () => (await call("getOrProvisionCloudComputer")) ?? null,
|
|
66
|
+
setComputerPreference: async (value) => {
|
|
67
|
+
await call("setComputerPreference", { value });
|
|
68
|
+
},
|
|
69
|
+
async listMachines() {
|
|
70
|
+
const [bindings, cloud, shared] = await Promise.allSettled([
|
|
71
|
+
listBindings(),
|
|
72
|
+
getCloudComputer(),
|
|
73
|
+
listSharedComputers(),
|
|
74
|
+
]);
|
|
75
|
+
const error = bindings.status === "rejected" && cloud.status === "rejected"
|
|
76
|
+
? bindings.reason instanceof Error
|
|
77
|
+
? bindings.reason.message
|
|
78
|
+
: "Couldn't load your computers."
|
|
79
|
+
: null;
|
|
80
|
+
return {
|
|
81
|
+
bindings: bindings.status === "fulfilled" ? bindings.value : [],
|
|
82
|
+
cloud: cloud.status === "fulfilled" ? cloud.value : null,
|
|
83
|
+
sharedComputers: shared.status === "fulfilled" ? shared.value : [],
|
|
84
|
+
error,
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Auto-select when there is no real choice to make. An account with exactly
|
|
91
|
+
* one online machine should not be asked which one; an account whose cloud
|
|
92
|
+
* Computer is plan-blocked should not have it silently chosen either.
|
|
93
|
+
* Returns null when a person has to choose.
|
|
94
|
+
*/
|
|
95
|
+
export function autoSelectBinding(list) {
|
|
96
|
+
const online = list.bindings.find((b) => b.online);
|
|
97
|
+
if (online)
|
|
98
|
+
return online.id;
|
|
99
|
+
if (list.cloud && !isCloudComputerBlocked(list.cloud) && list.bindings.length === 0) {
|
|
100
|
+
return CLOUD_BINDING_ID;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A machine's own name: what to call the COMPUTER, not the work it is pointed
|
|
106
|
+
* at. The hostname is the machine's real identity, so it goes first. The
|
|
107
|
+
* folder stays as a fallback; a folder name beats a truncated uuid.
|
|
108
|
+
*/
|
|
109
|
+
export function bindingLabel(binding) {
|
|
110
|
+
if (binding.computerUrl) {
|
|
111
|
+
try {
|
|
112
|
+
const host = new URL(binding.computerUrl).hostname.split(".")[0];
|
|
113
|
+
if (host && host !== "localhost" && !/^\d+$/.test(host))
|
|
114
|
+
return host;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
/* fall through */
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const boxId = binding.boxId?.trim();
|
|
121
|
+
if (boxId && !UUID_PATTERN.test(boxId))
|
|
122
|
+
return boxId;
|
|
123
|
+
const folder = binding.defaultFolder?.split("/").filter(Boolean).pop();
|
|
124
|
+
if (folder)
|
|
125
|
+
return folder;
|
|
126
|
+
return `${binding.id.slice(0, 8)}…`;
|
|
127
|
+
}
|
|
128
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
129
|
+
/** Turn a cloud status code into something a person can act on. */
|
|
130
|
+
export function cloudStatusLabel(status, blockedReason) {
|
|
131
|
+
switch (status) {
|
|
132
|
+
case "upgrade_required":
|
|
133
|
+
return blockedReason === "plan_downgraded"
|
|
134
|
+
? "Your plan no longer covers this computer"
|
|
135
|
+
: "Included computer time is used up";
|
|
136
|
+
case "provisioning":
|
|
137
|
+
return "Setting up…";
|
|
138
|
+
case "paused":
|
|
139
|
+
return "Paused";
|
|
140
|
+
case "recycled":
|
|
141
|
+
return "Removed";
|
|
142
|
+
case "ready":
|
|
143
|
+
case "running":
|
|
144
|
+
case "live":
|
|
145
|
+
return "Ready";
|
|
146
|
+
default:
|
|
147
|
+
return status ? status.replace(/_/g, " ") : "Unknown";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** Machine spec line, e.g. "4 vCPU · 8 GB RAM · 64 GB disk". */
|
|
151
|
+
export function machineSpec(machine) {
|
|
152
|
+
if (!machine)
|
|
153
|
+
return null;
|
|
154
|
+
const parts = [];
|
|
155
|
+
if (machine.vcpus)
|
|
156
|
+
parts.push(`${machine.vcpus} vCPU`);
|
|
157
|
+
if (machine.memoryMib)
|
|
158
|
+
parts.push(`${Math.round(machine.memoryMib / 1024)} GB RAM`);
|
|
159
|
+
if (machine.diskGib)
|
|
160
|
+
parts.push(`${machine.diskGib} GB disk`);
|
|
161
|
+
return parts.length ? parts.join(" · ") : null;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=control-plane.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-plane.js","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"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"}
|
package/dist/grant.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trade the account JWT for a signed, short-lived grant scoped to one machine.
|
|
3
|
+
*
|
|
4
|
+
* The session origin hands back a `cookie` field AND a Set-Cookie header. A
|
|
5
|
+
* browser gets the header for free; every client here uses the field as a
|
|
6
|
+
* bearer token, which the proxy's readSessionGrant() accepts precisely so
|
|
7
|
+
* non-browser clients do not have to care about cookie policy.
|
|
8
|
+
*/
|
|
9
|
+
import type { OmgGrant } from "@omg-dev/client";
|
|
10
|
+
import { type CloudEndpoints, type FetchLike, type GetAuthToken } from "./config";
|
|
11
|
+
export type ComputerGrantErrorCode = "unauthorized" | "forbidden" | "upgrade_required" | "unreachable" | "unavailable";
|
|
12
|
+
export declare class ComputerGrantError extends Error {
|
|
13
|
+
readonly code: ComputerGrantErrorCode;
|
|
14
|
+
constructor(message: string, code?: ComputerGrantErrorCode);
|
|
15
|
+
/**
|
|
16
|
+
* A 403 is never transient: the mint's authorization check failed for this
|
|
17
|
+
* exact (owner, machine) pair, and retrying with the same account will 403
|
|
18
|
+
* forever. Readiness reads this to say "pick another machine" instead of
|
|
19
|
+
* "try again".
|
|
20
|
+
*/
|
|
21
|
+
get forbidden(): boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface GrantMinterOptions {
|
|
24
|
+
endpoints?: Partial<CloudEndpoints>;
|
|
25
|
+
getAuthToken: GetAuthToken;
|
|
26
|
+
fetch?: FetchLike;
|
|
27
|
+
now?: () => number;
|
|
28
|
+
}
|
|
29
|
+
export type MintSessionGrant = (bindingId: string) => Promise<OmgGrant>;
|
|
30
|
+
/**
|
|
31
|
+
* `bindingId` may be the `shared:<ownerUserId>:<bindingId>` spelling. The
|
|
32
|
+
* mint endpoint only understands the raw pair, so decoding is this call's job.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createGrantMinter(options: GrantMinterOptions): MintSessionGrant;
|
|
35
|
+
//# sourceMappingURL=grant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant.d.ts","sourceRoot":"","sources":["../src/grant.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,YAAY,EAClB,MAAM,UAAU,CAAC;AAGlB,MAAM,MAAM,sBAAsB,GAC9B,cAAc,GACd,WAAW,GACX,kBAAkB,GAClB,aAAa,GACb,aAAa,CAAC;AAElB,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,YAAY,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,sBAAsC,EAIxE;IACD;;;;;OAKG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACpC,YAAY,EAAE,YAAY,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAExE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,gBAAgB,CAyE/E"}
|
package/dist/grant.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trade the account JWT for a signed, short-lived grant scoped to one machine.
|
|
3
|
+
*
|
|
4
|
+
* The session origin hands back a `cookie` field AND a Set-Cookie header. A
|
|
5
|
+
* browser gets the header for free; every client here uses the field as a
|
|
6
|
+
* bearer token, which the proxy's readSessionGrant() accepts precisely so
|
|
7
|
+
* non-browser clients do not have to care about cookie policy.
|
|
8
|
+
*/
|
|
9
|
+
import { SESSION_AUTH_PATH, resolveCloudEndpoints, } from "./config";
|
|
10
|
+
import { SHARED_REVOKED_DETAIL, isSharedBindingId, mintTargetForBinding } from "./shared-binding";
|
|
11
|
+
export class ComputerGrantError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
constructor(message, code = "unavailable") {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "ComputerGrantError";
|
|
16
|
+
this.code = code;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A 403 is never transient: the mint's authorization check failed for this
|
|
20
|
+
* exact (owner, machine) pair, and retrying with the same account will 403
|
|
21
|
+
* forever. Readiness reads this to say "pick another machine" instead of
|
|
22
|
+
* "try again".
|
|
23
|
+
*/
|
|
24
|
+
get forbidden() {
|
|
25
|
+
return this.code === "forbidden";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `bindingId` may be the `shared:<ownerUserId>:<bindingId>` spelling. The
|
|
30
|
+
* mint endpoint only understands the raw pair, so decoding is this call's job.
|
|
31
|
+
*/
|
|
32
|
+
export function createGrantMinter(options) {
|
|
33
|
+
const endpoints = resolveCloudEndpoints(options.endpoints);
|
|
34
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
35
|
+
const now = options.now ?? Date.now;
|
|
36
|
+
return async function mintSessionGrant(bindingId) {
|
|
37
|
+
const authToken = await options.getAuthToken();
|
|
38
|
+
if (!authToken)
|
|
39
|
+
throw new ComputerGrantError("Please sign in again.", "unauthorized");
|
|
40
|
+
let response;
|
|
41
|
+
try {
|
|
42
|
+
response = await fetchImpl(`${endpoints.sessionOrigin}${SESSION_AUTH_PATH}`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
credentials: "include",
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: `Bearer ${authToken}`,
|
|
47
|
+
"Content-Type": "application/json",
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(mintTargetForBinding(bindingId)),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
throw new ComputerGrantError("Couldn't reach your Computer. Try again in a moment.", "unreachable");
|
|
54
|
+
}
|
|
55
|
+
if (response.status === 401) {
|
|
56
|
+
throw new ComputerGrantError("Please sign in again.", "unauthorized");
|
|
57
|
+
}
|
|
58
|
+
if (response.status === 403) {
|
|
59
|
+
throw new ComputerGrantError(isSharedBindingId(bindingId)
|
|
60
|
+
? SHARED_REVOKED_DETAIL
|
|
61
|
+
: "This computer isn't available to your account anymore.", "forbidden");
|
|
62
|
+
}
|
|
63
|
+
// Billing wall: stop mint retries. Re-minting only spams session-auth.
|
|
64
|
+
if (response.status === 402) {
|
|
65
|
+
throw new ComputerGrantError("Your included computer time is used up.", "upgrade_required");
|
|
66
|
+
}
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
throw new ComputerGrantError("Couldn't open this Computer. Try again in a moment.", "unavailable");
|
|
69
|
+
}
|
|
70
|
+
const body = (await response.json().catch(() => null));
|
|
71
|
+
if (!body?.cookie) {
|
|
72
|
+
throw new ComputerGrantError("Your Computer is updating. Try again in a moment.");
|
|
73
|
+
}
|
|
74
|
+
// Relative lifetime wins: client and server clocks differ, and an absolute
|
|
75
|
+
// `exp` against a skewed clock either expires a fresh grant instantly or
|
|
76
|
+
// trusts a dead one. `exp` stays as the fallback for a server mid-rollout.
|
|
77
|
+
const expiresAt = typeof body.expiresInMs === "number"
|
|
78
|
+
? now() + body.expiresInMs
|
|
79
|
+
: typeof body.exp === "number"
|
|
80
|
+
? body.exp
|
|
81
|
+
: now();
|
|
82
|
+
return { token: body.cookie, expiresAt };
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=grant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant.js","sourceRoot":"","sources":["../src/grant.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { AUTH_APP_ID, CLOUD_BINDING_ID, DEFAULT_CLOUD_ENDPOINTS, SESSION_AUTH_PATH, resolveCloudEndpoints, type CloudEndpoints, type FetchLike, type GetAuthToken, } from "./config";
|
|
2
|
+
export { OmgAuthError, SignOutFailedError, createCloudAuth, type CloudAuth, type CloudAuthOptions, type SignedInUser, type SocialProvider, } from "./auth";
|
|
3
|
+
export * from "./shared-binding";
|
|
4
|
+
export { ComputerGrantError, createGrantMinter, type ComputerGrantErrorCode, type GrantMinterOptions, type MintSessionGrant, } from "./grant";
|
|
5
|
+
export { createDirectTransport, createMachineTransports, type MachineTransports, type TransportCacheOptions, } from "./transports";
|
|
6
|
+
export { probeReadiness, waitForReady, type BootstrapRoster, type ComputerReadiness, } from "./readiness";
|
|
7
|
+
export { autoSelectBinding, bindingLabel, cloudStatusLabel, createControlPlaneClient, isCloudComputerBlocked, machineSpec, toSharedBinding, type CloudComputer, type CloudComputerStatus, type ComputerBinding, type ControlPlaneClient, type ControlPlaneOptions, type MachineList, type SharedComputerBinding, } from "./control-plane";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,QAAQ,CAAC;AAChB,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,EACX,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { AUTH_APP_ID, CLOUD_BINDING_ID, DEFAULT_CLOUD_ENDPOINTS, SESSION_AUTH_PATH, resolveCloudEndpoints, } from "./config";
|
|
2
|
+
export { OmgAuthError, SignOutFailedError, createCloudAuth, } from "./auth";
|
|
3
|
+
export * from "./shared-binding";
|
|
4
|
+
export { ComputerGrantError, createGrantMinter, } from "./grant";
|
|
5
|
+
export { createDirectTransport, createMachineTransports, } from "./transports";
|
|
6
|
+
export { probeReadiness, waitForReady, } from "./readiness";
|
|
7
|
+
export { autoSelectBinding, bindingLabel, cloudStatusLabel, createControlPlaneClient, isCloudComputerBlocked, machineSpec, toSharedBinding, } from "./control-plane";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,GAItB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,eAAe,GAKhB,MAAM,QAAQ,CAAC;AAChB,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,GAIlB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GAGxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,YAAY,GAGb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,EACX,eAAe,GAQhB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is the Computer actually ready to answer, and if not, why?
|
|
3
|
+
*
|
|
4
|
+
* `GET /api/bootstrap` is the readiness authority for the whole surface. The
|
|
5
|
+
* status codes are not interchangeable and collapsing them into "error" is
|
|
6
|
+
* what turns a machine that is merely cold into a machine that looks broken.
|
|
7
|
+
*
|
|
8
|
+
* 425 in particular is not a failure. A reaped sandbox hibernates and wakes on
|
|
9
|
+
* connect; the proxy answers 425 while that is in flight and the only correct
|
|
10
|
+
* client behaviour is to wait and ask again.
|
|
11
|
+
*/
|
|
12
|
+
import type { OmgTransport } from "@omg-dev/client";
|
|
13
|
+
export type BootstrapRoster = {
|
|
14
|
+
agents: {
|
|
15
|
+
key: string;
|
|
16
|
+
label: string;
|
|
17
|
+
visible?: boolean;
|
|
18
|
+
status?: {
|
|
19
|
+
configured?: boolean;
|
|
20
|
+
accountConnected?: boolean;
|
|
21
|
+
};
|
|
22
|
+
}[];
|
|
23
|
+
repos: {
|
|
24
|
+
name: string;
|
|
25
|
+
cwd: string;
|
|
26
|
+
}[];
|
|
27
|
+
};
|
|
28
|
+
export type ComputerReadiness =
|
|
29
|
+
/** The roster rides along: one fetch, one owner, no second source of truth. */
|
|
30
|
+
{
|
|
31
|
+
status: "ready";
|
|
32
|
+
version?: string;
|
|
33
|
+
sessions: unknown[];
|
|
34
|
+
roster: BootstrapRoster;
|
|
35
|
+
}
|
|
36
|
+
/** Asked and not yet heard. Not "waking": that is something the machine says. */
|
|
37
|
+
| {
|
|
38
|
+
status: "connecting";
|
|
39
|
+
}
|
|
40
|
+
/** Cold sandbox resuming, the proxy said 425. Retry, do not show an error. */
|
|
41
|
+
| {
|
|
42
|
+
status: "waking";
|
|
43
|
+
}
|
|
44
|
+
/** Too many live agents for this plan (429) or this box's local cap. */
|
|
45
|
+
| {
|
|
46
|
+
status: "agent-limit";
|
|
47
|
+
message: string;
|
|
48
|
+
}
|
|
49
|
+
/** The runtime behind the proxy is down (502/503/504). */
|
|
50
|
+
| {
|
|
51
|
+
status: "unavailable";
|
|
52
|
+
message: string;
|
|
53
|
+
}
|
|
54
|
+
/** The mint 403'd. Needs a different machine, not a retry loop. */
|
|
55
|
+
| {
|
|
56
|
+
status: "unauthorized";
|
|
57
|
+
message: string;
|
|
58
|
+
} | {
|
|
59
|
+
status: "error";
|
|
60
|
+
message: string;
|
|
61
|
+
};
|
|
62
|
+
export declare function probeReadiness(transport: OmgTransport): Promise<ComputerReadiness>;
|
|
63
|
+
/**
|
|
64
|
+
* Wait out a wake. Bounded on purpose: a cold resume is sub-second and a cold
|
|
65
|
+
* provision is seconds, so a minute of 425s means something is wrong.
|
|
66
|
+
*/
|
|
67
|
+
export declare function waitForReady(transport: OmgTransport, { timeoutMs, intervalMs, onWaking, sleep, now, }?: {
|
|
68
|
+
timeoutMs?: number;
|
|
69
|
+
intervalMs?: number;
|
|
70
|
+
onWaking?: (attempt: number) => void;
|
|
71
|
+
sleep?: (ms: number) => Promise<void>;
|
|
72
|
+
now?: () => number;
|
|
73
|
+
}): Promise<ComputerReadiness>;
|
|
74
|
+
//# sourceMappingURL=readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAIpD,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,MAAM,CAAC,EAAE;YAAE,UAAU,CAAC,EAAE,OAAO,CAAC;YAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;KAC/D,EAAE,CAAC;IACJ,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,iBAAiB;AAC3B,+EAA+E;AAC7E;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE;AACrF,iFAAiF;GAC/E;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE;AAC1B,8EAA8E;GAC5E;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE;AACtB,wEAAwE;GACtE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC5C,0DAA0D;GACxD;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC5C,mEAAmE;GACjE;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,wBAAsB,cAAc,CAAC,SAAS,EAAE,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAiDxF;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,YAAY,EACvB,EACE,SAAkB,EAClB,UAAkB,EAClB,QAAQ,EACR,KAA+E,EAC/E,GAAc,GACf,GAAE;IACD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACf,GACL,OAAO,CAAC,iBAAiB,CAAC,CAW5B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is the Computer actually ready to answer, and if not, why?
|
|
3
|
+
*
|
|
4
|
+
* `GET /api/bootstrap` is the readiness authority for the whole surface. The
|
|
5
|
+
* status codes are not interchangeable and collapsing them into "error" is
|
|
6
|
+
* what turns a machine that is merely cold into a machine that looks broken.
|
|
7
|
+
*
|
|
8
|
+
* 425 in particular is not a failure. A reaped sandbox hibernates and wakes on
|
|
9
|
+
* connect; the proxy answers 425 while that is in flight and the only correct
|
|
10
|
+
* client behaviour is to wait and ask again.
|
|
11
|
+
*/
|
|
12
|
+
import { ComputerGrantError } from "./grant";
|
|
13
|
+
export async function probeReadiness(transport) {
|
|
14
|
+
let response;
|
|
15
|
+
try {
|
|
16
|
+
response = await transport.fetch("/api/bootstrap");
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (error instanceof ComputerGrantError && error.forbidden) {
|
|
20
|
+
return { status: "unauthorized", message: error.message };
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
status: "unavailable",
|
|
24
|
+
message: error instanceof Error ? error.message : "Couldn't reach your Computer.",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const text = await response.text().catch(() => "");
|
|
28
|
+
let body = {};
|
|
29
|
+
try {
|
|
30
|
+
body = text ? JSON.parse(text) : {};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
body = {};
|
|
34
|
+
}
|
|
35
|
+
const errorText = typeof body.error === "string" ? body.error : undefined;
|
|
36
|
+
if (response.ok) {
|
|
37
|
+
return {
|
|
38
|
+
status: "ready",
|
|
39
|
+
version: typeof body.version === "string" ? body.version : undefined,
|
|
40
|
+
sessions: Array.isArray(body.sessions) ? body.sessions : [],
|
|
41
|
+
roster: {
|
|
42
|
+
// `codingAgents` is the launchable roster. `agents` on the same body
|
|
43
|
+
// is AUTO agents, a different feature with a similar name.
|
|
44
|
+
agents: Array.isArray(body.codingAgents)
|
|
45
|
+
? body.codingAgents
|
|
46
|
+
: [],
|
|
47
|
+
repos: Array.isArray(body.repos) ? body.repos : [],
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (response.status === 425 || errorText === "sandbox waking")
|
|
52
|
+
return { status: "waking" };
|
|
53
|
+
if (response.status === 429) {
|
|
54
|
+
return { status: "agent-limit", message: errorText ?? "Too many agents running." };
|
|
55
|
+
}
|
|
56
|
+
if (response.status === 502 || response.status === 503 || response.status === 504) {
|
|
57
|
+
return { status: "unavailable", message: errorText ?? "Your Computer isn't responding." };
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
status: "error",
|
|
61
|
+
message: errorText ?? `Couldn't open your Computer (${response.status})`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Wait out a wake. Bounded on purpose: a cold resume is sub-second and a cold
|
|
66
|
+
* provision is seconds, so a minute of 425s means something is wrong.
|
|
67
|
+
*/
|
|
68
|
+
export async function waitForReady(transport, { timeoutMs = 60_000, intervalMs = 1_500, onWaking, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), now = Date.now, } = {}) {
|
|
69
|
+
const deadline = now() + timeoutMs;
|
|
70
|
+
let attempt = 0;
|
|
71
|
+
for (;;) {
|
|
72
|
+
const readiness = await probeReadiness(transport);
|
|
73
|
+
if (readiness.status !== "waking")
|
|
74
|
+
return readiness;
|
|
75
|
+
attempt += 1;
|
|
76
|
+
onWaking?.(attempt);
|
|
77
|
+
if (now() + intervalMs >= deadline)
|
|
78
|
+
return readiness;
|
|
79
|
+
await sleep(intervalMs);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.js","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Addressing someone else's machine.
|
|
3
|
+
*
|
|
4
|
+
* A binding id identifies a machine everywhere: it keys the transport cache,
|
|
5
|
+
* the grant owner, and the persisted preference. A shared machine needs to be
|
|
6
|
+
* addressable by the same KIND of string, so it is spelled
|
|
7
|
+
* `shared:<ownerUserId>:<bindingId>`, exactly as the control plane expects it
|
|
8
|
+
* (`control-plane/lib/computer-access.ts` in BennyKok/vibes). Both halves are
|
|
9
|
+
* needed because sharing is PER MACHINE. This is a contract with the server,
|
|
10
|
+
* not a client convention. Do not change the format here alone.
|
|
11
|
+
*/
|
|
12
|
+
export declare const SHARED_BINDING_PREFIX = "shared:";
|
|
13
|
+
export interface SharedBindingTarget {
|
|
14
|
+
ownerUserId: string;
|
|
15
|
+
bindingId: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function sharedBindingId(ownerUserId: string, bindingId: string): string;
|
|
18
|
+
export declare function isSharedBindingId(bindingId: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The (owner, machine) pair behind a shared binding id, or null for an
|
|
21
|
+
* ordinary machine.
|
|
22
|
+
*
|
|
23
|
+
* Splits on the FIRST colon only: the owner is a uuid and never contains one,
|
|
24
|
+
* while the trailing binding id is passed through untouched so it survives
|
|
25
|
+
* whatever spelling relay chooses for it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseSharedBindingId(bindingId: string): SharedBindingTarget | null;
|
|
28
|
+
/**
|
|
29
|
+
* What the session-auth mint endpoint should be asked for, for any binding id
|
|
30
|
+
* this app might have selected. The mint route (control-plane
|
|
31
|
+
* `handleSessionAuthMint`, POST /__omg/session-auth) takes the RAW binding id
|
|
32
|
+
* plus an optional `ownerUserId` — it does not understand the `shared:`
|
|
33
|
+
* spelling itself, that decoding is entirely this app's job.
|
|
34
|
+
*/
|
|
35
|
+
export declare function mintTargetForBinding(bindingId: string): {
|
|
36
|
+
bindingId: string;
|
|
37
|
+
ownerUserId?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Detail for a revoked share — mint 403 or a `shared:` preference that is no
|
|
41
|
+
* longer in `listSharedComputers`. The empty-state TITLE is already
|
|
42
|
+
* "No longer available"; this line says why, instead of restating that title.
|
|
43
|
+
*/
|
|
44
|
+
export declare const SHARED_REVOKED_DETAIL = "This computer is no longer shared with you.";
|
|
45
|
+
/**
|
|
46
|
+
* Optional machine identity the share row may carry. A guest never gets the
|
|
47
|
+
* owner's live `computerUrl` for transport, but the list payload can still
|
|
48
|
+
* name the box so two shares from one person are not the same string.
|
|
49
|
+
*/
|
|
50
|
+
export type SharedComputerIdentity = {
|
|
51
|
+
hostname?: string;
|
|
52
|
+
computerName?: string;
|
|
53
|
+
machineName?: string;
|
|
54
|
+
machineLabel?: string;
|
|
55
|
+
defaultFolder?: string | null;
|
|
56
|
+
computerUrl?: string | null;
|
|
57
|
+
binding?: {
|
|
58
|
+
hostname?: string;
|
|
59
|
+
computerName?: string;
|
|
60
|
+
machineName?: string;
|
|
61
|
+
computerUrl?: string | null;
|
|
62
|
+
defaultFolder?: string | null;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
/** What `listSharedComputers` returns for one machine shared with the signed-in account. */
|
|
66
|
+
export type SharedComputerView = SharedComputerIdentity & {
|
|
67
|
+
ownerUserId: string;
|
|
68
|
+
bindingId: string;
|
|
69
|
+
email: string;
|
|
70
|
+
name?: string;
|
|
71
|
+
image?: string;
|
|
72
|
+
sharedAt: number;
|
|
73
|
+
/**
|
|
74
|
+
* Liveness of the OWNER's machine, resolved server-side. This app cannot
|
|
75
|
+
* ask relay itself — relay only answers "which machines are YOURS", and a
|
|
76
|
+
* shared one never is. Undefined means the server could not resolve it
|
|
77
|
+
* either; treat that as reachable rather than rendering a false "offline",
|
|
78
|
+
* same as the web dashboard does.
|
|
79
|
+
*/
|
|
80
|
+
online?: boolean;
|
|
81
|
+
};
|
|
82
|
+
/** Fields the label helpers read — a list row or the synthesized binding. */
|
|
83
|
+
export type SharedComputerLabelSource = SharedComputerIdentity & {
|
|
84
|
+
ownerUserId?: string;
|
|
85
|
+
bindingId?: string;
|
|
86
|
+
id?: string;
|
|
87
|
+
email?: string;
|
|
88
|
+
name?: string;
|
|
89
|
+
ownerName?: string;
|
|
90
|
+
ownerLabel?: string;
|
|
91
|
+
ownerBindingId?: string;
|
|
92
|
+
online?: boolean;
|
|
93
|
+
};
|
|
94
|
+
/** "Ada" / "ada@example.com" — whichever the share row actually carries. */
|
|
95
|
+
export declare function sharedComputerOwnerLabel(computer: Pick<SharedComputerView, "name" | "email">): string;
|
|
96
|
+
export declare function looksLikeEmail(value: string): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* First name from a real display name. Never an email — possessivizing
|
|
99
|
+
* `ada@example.com` produced "ada@example.com's computer", which is the
|
|
100
|
+
* string this helper exists to stop shipping.
|
|
101
|
+
*/
|
|
102
|
+
export declare function sharedComputerFirstName(computer: SharedComputerLabelSource): string | null;
|
|
103
|
+
/**
|
|
104
|
+
* Which machine this share is, when the server said. Hostname first, then
|
|
105
|
+
* an explicit name, then the same URL/folder fallbacks `bindingLabel` uses
|
|
106
|
+
* for a box you own. Missing is fine — the title falls back to "computer"
|
|
107
|
+
* and collisions get a short tail.
|
|
108
|
+
*/
|
|
109
|
+
export declare function sharedComputerMachineIdentity(computer: SharedComputerLabelSource): string | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* Title for a shared machine.
|
|
112
|
+
*
|
|
113
|
+
* - Owner has a name: first-name possessive + machine identity when we have
|
|
114
|
+
* one ("Ada's MacBook" / "Ada's studio"), else "Ada's computer".
|
|
115
|
+
* - Owner is email-only: "Shared computer". Never `"ada@example.com's computer"`.
|
|
116
|
+
* - Two rows that would otherwise match get a short unique tail.
|
|
117
|
+
*
|
|
118
|
+
* Distinct from `bindingLabel` (format.ts), which reads a machine YOU own.
|
|
119
|
+
* Never call that on a synthesized shared binding: a guest has no
|
|
120
|
+
* `computerUrl` of their own, and it would fall through to a truncated id.
|
|
121
|
+
*/
|
|
122
|
+
export declare function sharedBindingLabel(computer: SharedComputerLabelSource, siblings?: SharedComputerLabelSource[]): string;
|
|
123
|
+
export declare function sharedBindingBaseTitle(computer: SharedComputerLabelSource): string;
|
|
124
|
+
/**
|
|
125
|
+
* Second line on the manage screen.
|
|
126
|
+
*
|
|
127
|
+
* Attribution is the section + title. A named owner gets liveness
|
|
128
|
+
* (Online / Offline). Email-only puts the email here — that is the only
|
|
129
|
+
* place the address belongs, and possessivizing it as a title is the
|
|
130
|
+
* thing this policy forbids.
|
|
131
|
+
*/
|
|
132
|
+
export declare function sharedComputerSubtitle(computer: SharedComputerLabelSource): string;
|
|
133
|
+
/** Picker row: the title, plus a title-case "Offline" suffix when down. */
|
|
134
|
+
export declare function sharedComputerPickerLabel(computer: SharedComputerLabelSource, siblings?: SharedComputerLabelSource[]): string;
|
|
135
|
+
//# sourceMappingURL=shared-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-binding.d.ts","sourceRoot":"","sources":["../src/shared-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CASlF;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAGA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,gDAAgD,CAAC;AAEnF;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;CACH,CAAC;AAEF,4FAA4F;AAC5F,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,yBAAyB,GAAG,sBAAsB,GAAG;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,4EAA4E;AAC5E,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,GACnD,MAAM,CAER;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,GAAG,IAAI,CAK1F;AASD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,yBAAyB,GAClC,MAAM,GAAG,SAAS,CAoBpB;AAsCD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,yBAAyB,EACnC,QAAQ,GAAE,yBAAyB,EAAO,GACzC,MAAM,CAKR;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,CAKlF;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,CASlF;AAED,2EAA2E;AAC3E,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,yBAAyB,EACnC,QAAQ,GAAE,yBAAyB,EAAO,GACzC,MAAM,CAGR"}
|