1import * as Schema from "effect/Schema";
2import { AuthEnvironmentScopes } from "./auth.ts";
3import { EnvironmentId, ThreadId } from "./baseSchemas.ts";
4import { ExecutionEnvironmentDescriptor } from "./environment.ts";
5import { OrchestrationV2Command, OrchestrationV2ShellSnapshot } from "./orchestrationV2.ts";
6
7export const HUB_THREAD_ORGANIZATION_FIELDS = [
8 "settledOverride",
9 "settledAt",
10 "unsettledAt",
11 "snoozedUntil",
12 "snoozedAt",
13 "pinnedAt",
14 "pinOrderKey",
15 "activeOrderKey",
16 "autoSettleDisabledAt",
17] as const;
18
19const organizationCommandTypes = [
20 "thread.settle",
21 "thread.unsettle",
22 "thread.snooze",
23 "thread.unsnooze",
24 "thread.pin",
25 "thread.unpin",
26 "thread.pin.reorder",
27 "thread.active.reorder",
28 "thread.auto-settle.set",
29] as const;
30
31export const HubOrganizationCommand = OrchestrationV2Command.pipe(
32 Schema.refine(
33 (
34 command,
35 ): command is Extract<
36 OrchestrationV2Command,
37 { readonly type: (typeof organizationCommandTypes)[number] }
38 > => organizationCommandTypes.some((type) => type === command.type),
39 ),
40);
41export type HubOrganizationCommand = typeof HubOrganizationCommand.Type;
42
43export const HubInboxCommand = Schema.Struct({
44 environmentId: EnvironmentId,
45 command: HubOrganizationCommand,
46});
47export type HubInboxCommand = typeof HubInboxCommand.Type;
48
49export const HubInboxEnvironment = Schema.Struct({
50 environmentId: EnvironmentId,
51 snapshot: OrchestrationV2ShellSnapshot,
52 pendingThreadIds: Schema.Array(ThreadId),
53});
54export type HubInboxEnvironment = typeof HubInboxEnvironment.Type;
55
56export const HubInbox = Schema.Struct({ environments: Schema.Array(HubInboxEnvironment) });
57export type HubInbox = typeof HubInbox.Type;
58
59export const HubDirectory = Schema.Struct({
60 hubEnvironmentId: EnvironmentId,
61 environments: Schema.Array(
62 Schema.Struct({
63 environmentId: EnvironmentId,
64 label: Schema.NonEmptyString,
65 httpBaseUrl: Schema.NonEmptyString,
66 scopes: AuthEnvironmentScopes,
67 descriptor: ExecutionEnvironmentDescriptor,
68 }),
69 ),
70});
71export type HubDirectory = typeof HubDirectory.Type;
72
73export const HubManagementRequest = Schema.Union([
74 Schema.Struct({ type: Schema.Literal("hub.configure"), publicUrl: Schema.NonEmptyString }),
75 Schema.Struct({ type: Schema.Literal("hub.status") }),
76 Schema.Struct({
77 type: Schema.Literal("hub.enroll"),
78 url: Schema.NonEmptyString,
79 environmentId: EnvironmentId,
80 tokenFile: Schema.NonEmptyString,
81 }),
82 Schema.Struct({ type: Schema.Literal("hub.remove"), environmentId: EnvironmentId }),
83]);
84export type HubManagementRequest = typeof HubManagementRequest.Type;
85
86export class HubError extends Schema.TaggedError<HubError>()("HubError", {
87 reason: Schema.Literals([
88 "configuration",
89 "address",
90 "identity",
91 "credential",
92 "not-found",
93 "upstream",
94 "storage",
95 ]),
96 cause: Schema.optionalKey(Schema.Redacted(Schema.Defect())),
97}) {
98 override get message(): string {
99 switch (this.reason) {
100 case "configuration":
101 return "Configure the server's public URL before enrolling machines.";
102 case "address":
103 return "Use an HTTP or HTTPS address without a password, query, or fragment.";
104 case "identity":
105 return "The machine identity changed. Check its address before enrolling it.";
106 case "credential":
107 return "The machine access grant is unavailable. Enroll it with a new grant.";
108 case "not-found":
109 return "This machine was removed. Refresh the server's machine list.";
110 case "upstream":
111 return "The machine is unreachable. Check its private network connection.";
112 case "storage":
113 return "The server could not save its machines. Check its storage permissions.";
114 }
115 }
116}