@huynle/hub 0.7.3 → 0.8.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/CHANGELOG.md +229 -0
- package/Dockerfile +7 -2
- package/README.md +153 -24
- package/package.json +5 -3
- package/packages/claude-code/package.json +1 -1
- package/packages/claude-code/src/daemon.ts +8 -1
- package/packages/claude-code/src/launcher.ts +29 -9
- package/packages/claude-code/src/profile.ts +173 -0
- package/packages/claude-code/src/sessions.ts +22 -4
- package/packages/cli/dist/hub-cli.js +6474 -3470
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/artifact-jobs-mcp.ts +226 -0
- package/packages/cli/src/artifacts-mcp.ts +160 -0
- package/packages/cli/src/bootstrap/enrollment/enroll.ts +20 -8
- package/packages/cli/src/bootstrap/enrollment/policy.ts +125 -12
- package/packages/cli/src/bootstrap/enrollment/status.ts +3 -2
- package/packages/cli/src/cli.ts +95 -13
- package/packages/cli/src/format.ts +48 -10
- package/packages/cli/src/http.ts +1 -0
- package/packages/cli/src/intents.ts +23 -0
- package/packages/cli/src/login.ts +20 -6
- package/packages/cli/src/mcp.ts +380 -9
- package/packages/cli/src/messaging.ts +3 -1
- package/packages/cli/src/setup/commands.ts +3 -2
- package/packages/cli/src/setup/runtime.ts +3 -2
- package/packages/cli/src/team-intake-mcp.ts +158 -0
- package/packages/cli/src/teams-mcp.ts +403 -0
- package/packages/cli/src/watchlist.ts +135 -0
- package/packages/codex/package.json +1 -1
- package/packages/codex/src/profile.ts +163 -0
- package/packages/codex/src/sessions.ts +12 -0
- package/packages/codex/src/spoke.ts +19 -1
- package/packages/node/package.json +1 -1
- package/packages/node/src/config.ts +96 -2
- package/packages/node/src/opencode-warm.ts +325 -0
- package/packages/node/src/run.ts +27 -1
- package/packages/preset/package.json +1 -1
- package/packages/preset/src/profile.ts +34 -0
- package/packages/preset/src/spoke.ts +4 -0
- package/packages/protocol/package.json +1 -1
- package/packages/protocol/src/artifacts.ts +142 -0
- package/packages/protocol/src/concierge.ts +366 -0
- package/packages/protocol/src/contracts.ts +104 -0
- package/packages/protocol/src/graph.ts +70 -0
- package/packages/protocol/src/hub-workspace.ts +201 -0
- package/packages/protocol/src/index.ts +169 -6
- package/packages/protocol/src/launch.ts +22 -0
- package/packages/protocol/src/profiles.ts +301 -0
- package/packages/protocol/src/teams.ts +204 -0
- package/packages/runtime-openshell/package.json +1 -1
- package/packages/runtime-sdk/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/packages/sdk/src/address.ts +50 -2
- package/packages/sdk/src/artifact-jobs.ts +194 -0
- package/packages/sdk/src/artifacts.ts +187 -0
- package/packages/sdk/src/client.ts +226 -5
- package/packages/sdk/src/concierge.ts +88 -0
- package/packages/sdk/src/errors.ts +3 -1
- package/packages/sdk/src/index.ts +5 -0
- package/packages/sdk/src/team-intake.ts +122 -0
- package/packages/sdk/src/teams.ts +346 -0
- package/packages/sdk/src/types.ts +548 -1
- package/packages/spoke-sdk/package.json +1 -1
- package/packages/spoke-sdk/src/index.ts +1 -0
- package/packages/spoke-sdk/src/profile-apply.ts +245 -0
- package/src/central/actions.ts +7 -0
- package/src/central/agent/acting-routes.ts +2 -1
- package/src/central/agent/acting-switch.ts +2 -1
- package/src/central/agent/acting.ts +215 -10
- package/src/central/agent/app-tokens.ts +3 -2
- package/src/central/agent/audit.ts +17 -0
- package/src/central/agent/body.ts +163 -0
- package/src/central/agent/decisions.ts +69 -2
- package/src/central/agent/delivery.ts +95 -15
- package/src/central/agent/directory-routes.ts +37 -7
- package/src/central/agent/directory.ts +70 -14
- package/src/central/agent/errors.ts +5 -0
- package/src/central/agent/events-routes.ts +21 -4
- package/src/central/agent/events.ts +11 -3
- package/src/central/agent/form-check.ts +85 -0
- package/src/central/agent/grant-requests.ts +2 -1
- package/src/central/agent/grant-routes.ts +4 -3
- package/src/central/agent/grants.ts +29 -2
- package/src/central/agent/idempotency.ts +133 -0
- package/src/central/agent/intent-routes.ts +116 -0
- package/src/central/agent/intents.ts +505 -0
- package/src/central/agent/interaction-graph-routes.ts +212 -0
- package/src/central/agent/interaction-graph.ts +865 -0
- package/src/central/agent/launch-engine.ts +16 -2
- package/src/central/agent/launch-routes.ts +6 -1
- package/src/central/agent/launch-spec.ts +14 -1
- package/src/central/agent/message-routes.ts +2 -1
- package/src/central/agent/messages.ts +27 -2
- package/src/central/agent/names.ts +12 -2
- package/src/central/agent/read-routes.ts +86 -19
- package/src/central/agent/routes.ts +176 -5
- package/src/central/agent/webhook-routes.ts +2 -1
- package/src/central/agent-data.ts +4 -0
- package/src/central/artifacts/access.ts +44 -0
- package/src/central/artifacts/blob-store.ts +113 -0
- package/src/central/artifacts/blob-support.ts +98 -0
- package/src/central/artifacts/events.ts +27 -0
- package/src/central/artifacts/host-caps.ts +162 -0
- package/src/central/artifacts/host.ts +315 -0
- package/src/central/artifacts/idempotency.ts +109 -0
- package/src/central/artifacts/jobs-routes.ts +773 -0
- package/src/central/artifacts/jobs.ts +344 -0
- package/src/central/artifacts/maintenance.ts +103 -0
- package/src/central/artifacts/routes.ts +360 -0
- package/src/central/artifacts/store.ts +776 -0
- package/src/central/artifacts/workspaces.ts +31 -0
- package/src/central/backend/file-blobs.ts +377 -0
- package/src/central/backend/index.ts +12 -3
- package/src/central/backend/memory-blobs.ts +116 -0
- package/src/central/backend/postgres-blobs.ts +513 -0
- package/src/central/backend/postgres-migrate.ts +33 -0
- package/src/central/backend/postgres-state.ts +19 -0
- package/src/central/backend/types.ts +8 -0
- package/src/central/concierge/activity.ts +142 -0
- package/src/central/concierge/arm.ts +63 -0
- package/src/central/concierge/board-render.ts +190 -0
- package/src/central/concierge/board-results.ts +169 -0
- package/src/central/concierge/board-routes.ts +339 -0
- package/src/central/concierge/board.ts +249 -0
- package/src/central/concierge/context.ts +221 -0
- package/src/central/concierge/entitlements.ts +13 -0
- package/src/central/concierge/guard.ts +120 -0
- package/src/central/concierge/inbound.ts +89 -0
- package/src/central/concierge/index.ts +84 -0
- package/src/central/concierge/notes.ts +41 -0
- package/src/central/concierge/notifier.ts +986 -0
- package/src/central/concierge/prompt.ts +130 -0
- package/src/central/concierge/proposals.ts +262 -0
- package/src/central/concierge/push-remap.ts +49 -0
- package/src/central/concierge/routes.ts +62 -0
- package/src/central/concierge/schedule.ts +113 -0
- package/src/central/concierge/service.ts +243 -0
- package/src/central/concierge/session-routes.ts +69 -0
- package/src/central/concierge/settings.ts +217 -0
- package/src/central/concierge/supervisor.ts +740 -0
- package/src/central/concierge/tiers.ts +260 -0
- package/src/central/concierge/watchlist-routes.ts +277 -0
- package/src/central/concierge/watchlist.ts +273 -0
- package/src/central/config.ts +7 -1
- package/src/central/http.ts +633 -50
- package/src/central/mcp-http.ts +10 -21
- package/src/central/profiles/apply.ts +333 -0
- package/src/central/profiles/content.ts +174 -0
- package/src/central/profiles/events.ts +40 -0
- package/src/central/profiles/idempotency.ts +14 -0
- package/src/central/profiles/import.ts +139 -0
- package/src/central/profiles/routes.ts +356 -0
- package/src/central/profiles/store.ts +597 -0
- package/src/central/profiles/usage-ports.ts +120 -0
- package/src/central/profiles/usage.ts +364 -0
- package/src/central/push.ts +7 -0
- package/src/central/registry.ts +43 -6
- package/src/central/store.ts +4 -0
- package/src/central/teams/collections.ts +34 -0
- package/src/central/teams/discovery.ts +346 -0
- package/src/central/teams/draft-edits.ts +46 -0
- package/src/central/teams/events.ts +46 -0
- package/src/central/teams/intake-routes.ts +100 -0
- package/src/central/teams/intake.ts +772 -0
- package/src/central/teams/policy.ts +231 -0
- package/src/central/teams/routes.ts +516 -0
- package/src/central/teams/seats.ts +272 -0
- package/src/central/teams/staffing.ts +888 -0
- package/src/central/teams/store.ts +667 -0
- package/src/central/teams/validation.ts +256 -0
- package/src/central/workspaces/board.ts +408 -0
- package/src/central/workspaces/checkpoints.ts +316 -0
- package/src/central/workspaces/coordinator.ts +841 -0
- package/src/central/workspaces/events.ts +85 -0
- package/src/central/workspaces/lifecycle.ts +93 -0
- package/src/central/workspaces/master.ts +490 -0
- package/src/central/workspaces/members.ts +632 -0
- package/src/central/workspaces/routes.ts +354 -0
- package/src/central/workspaces/sidechats-routes.ts +410 -0
- package/src/central/workspaces/sidechats.ts +482 -0
- package/src/central/workspaces/store.ts +571 -0
- package/src/central/workspaces/supervision.ts +355 -0
- package/src/index.ts +97 -15
- package/src/spoke/concierge-agent.ts +33 -0
- package/src/spoke/deliver.ts +22 -4
- package/src/spoke/fork-revert.ts +142 -0
- package/src/spoke/hub-rpc.ts +41 -5
- package/src/spoke/hub-tool.ts +383 -18
- package/src/spoke/listing.ts +47 -4
- package/src/spoke/profile-apply.ts +298 -0
- package/src/spoke/session-context.ts +176 -0
- package/src/spoke/spoke.ts +319 -20
- package/src/spoke/timeline.ts +6 -1
- package/src/spoke/tracker.ts +23 -1
- package/src/spoke/who-teams.ts +81 -0
- package/src/tui-messaging.ts +32 -18
- package/web/dist/assets/ArtifactPage-BS0gVkfu.js +1 -0
- package/web/dist/assets/ArtifactPane-PLYHNs6s.js +1 -0
- package/web/dist/assets/ArtifactWorkbench-9eTj1CeW.css +1 -0
- package/web/dist/assets/ArtifactWorkbench-LkDyJ5Dv.js +6 -0
- package/web/dist/assets/Library-CtxRqTjo.js +2 -0
- package/web/dist/assets/Library-DYsFj_5c.css +1 -0
- package/web/dist/assets/ProfilesDestination-BYiHKLmc.js +1 -0
- package/web/dist/assets/TeamsPage-BwjsAAUC.js +1 -0
- package/web/dist/assets/Watchtower-C4swrWVs.js +2 -0
- package/web/dist/assets/Watchtower-CTgqHk5g.css +1 -0
- package/web/dist/assets/index-DaH5GUdV.js +31 -0
- package/web/dist/assets/index-WWuKfWxL.css +1 -0
- package/web/dist/assets/live-BUofsQpz.css +1 -0
- package/web/dist/assets/live-YyTjE47g.js +5 -0
- package/web/dist/assets/pane-layout-CKC4I00k.js +1 -0
- package/web/dist/assets/plural-Bspvoqfq.js +1 -0
- package/web/dist/assets/renderer2d-CX5j3NcD.js +1 -0
- package/web/dist/assets/renderer3d-BXIVOvfi.js +5393 -0
- package/web/dist/assets/safe-area-DfPsOdWR.js +1 -0
- package/web/dist/assets/source-CvUHTc3O.js +1 -0
- package/web/dist/assets/ui-C9Igxzjm.js +1 -0
- package/web/dist/index.html +5 -2
- package/web/dist/sw.js +22 -2
- package/web/dist/assets/index-Dm8OmkaO.css +0 -1
- package/web/dist/assets/index-FUJbj_Du.js +0 -20
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
// hub.teams (HWA-37) over /api/agent/teams: assemble and operate persistent teams — definitions (create, list with
|
|
2
|
+
// tag/project/metadata filters, show, draft edits incl. role and policy-edge editing, publish, history and diff,
|
|
3
|
+
// pause/resume, archive/restore), staffing (attach a session, launch a member, replace, unstaff) and status (roster
|
|
4
|
+
// plus intake counts). The intake verbs (submit, intake, intakes, setIntakeStatus) are HWA-36's (team-intake.ts)
|
|
5
|
+
// and part of the same object. `team` is the team's ID (team_…) or its name.
|
|
6
|
+
//
|
|
7
|
+
// Who: users; app tokens with teams:read (reads) or teams:manage (changes) — seats, roster, status and staffing also
|
|
8
|
+
// need sessions:read; staffing by an app or session still needs the user's grants (needs_user otherwise). Session
|
|
9
|
+
// tokens read only the teams they are seated in and never change definitions. Team roles and labels confer no
|
|
10
|
+
// authority. Every change of an existing record sends the revision you read (expectedRevision); creates take an
|
|
11
|
+
// Idempotency-Key. The wire types mirror the hub's records (the SDK imports nothing outside its package; the hub's
|
|
12
|
+
// tests keep them in step).
|
|
13
|
+
import { type TeamIntakeApi, type TeamIntakeTransport, teamIntakeApi } from "./team-intake.ts"
|
|
14
|
+
import type { LaunchSpec, LaunchView } from "./types.ts"
|
|
15
|
+
|
|
16
|
+
interface Signal {
|
|
17
|
+
readonly signal?: AbortSignal
|
|
18
|
+
}
|
|
19
|
+
interface Keyed extends Signal {
|
|
20
|
+
readonly idempotencyKey?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type TeamMode = "open" | "restricted"
|
|
24
|
+
export type TeamMessageKind = "msg" | "ask"
|
|
25
|
+
|
|
26
|
+
export interface TeamRole {
|
|
27
|
+
readonly name: string
|
|
28
|
+
readonly label?: string
|
|
29
|
+
readonly description?: string
|
|
30
|
+
/** Holders may contact sessions outside all their teams. */
|
|
31
|
+
readonly externalContact?: boolean
|
|
32
|
+
readonly responsibilities?: readonly string[]
|
|
33
|
+
/** The profile its members should run (staffing reports whether they do; it never pins it). */
|
|
34
|
+
readonly profile?: { readonly profileID: string; readonly version?: number }
|
|
35
|
+
/** Placement preferences for staffing launches (advisory). */
|
|
36
|
+
readonly prefs?: { readonly harness?: string; readonly model?: string; readonly machineID?: string }
|
|
37
|
+
readonly minMembers?: number
|
|
38
|
+
readonly maxMembers?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** A communication path: `from` may contact `to` with these kinds (two-way: also back). Restricted mode only. */
|
|
42
|
+
export interface TeamEdge {
|
|
43
|
+
readonly from: string
|
|
44
|
+
readonly to: string
|
|
45
|
+
readonly kinds: readonly TeamMessageKind[]
|
|
46
|
+
readonly direction: "one-way" | "two-way"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The publishable content of a team. */
|
|
50
|
+
export interface TeamSpec {
|
|
51
|
+
/** Slug used in addresses (`@@role#name`); never changes. */
|
|
52
|
+
readonly name: string
|
|
53
|
+
readonly title: string
|
|
54
|
+
readonly description?: string
|
|
55
|
+
readonly mode: TeamMode
|
|
56
|
+
readonly roles: readonly TeamRole[]
|
|
57
|
+
/** The intake role (e.g. the project manager): receives submitted work and supervises members. */
|
|
58
|
+
readonly entryRole: string
|
|
59
|
+
readonly edges: readonly TeamEdge[]
|
|
60
|
+
readonly tags?: readonly string[]
|
|
61
|
+
readonly metadata?: Readonly<Record<string, string>>
|
|
62
|
+
readonly projects?: readonly string[]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A team: the published spec plus its record fields. */
|
|
66
|
+
export interface Team extends TeamSpec {
|
|
67
|
+
readonly v: 1
|
|
68
|
+
readonly id: string
|
|
69
|
+
readonly state: "active" | "archived"
|
|
70
|
+
/** Present while paused: intake and new staffing stop; the policy still applies. */
|
|
71
|
+
readonly paused?: true
|
|
72
|
+
/** The published version (1 on create, +1 per publish). */
|
|
73
|
+
readonly version: number
|
|
74
|
+
readonly revision: number
|
|
75
|
+
/** A pending draft (edits are published explicitly). */
|
|
76
|
+
readonly draft?: { readonly baseVersion: number; readonly digest: string; readonly savedAt: number }
|
|
77
|
+
readonly createdAt: number
|
|
78
|
+
readonly publishedAt: number
|
|
79
|
+
readonly updatedAt: number
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface TeamDraft {
|
|
83
|
+
readonly baseVersion: number
|
|
84
|
+
readonly spec: TeamSpec
|
|
85
|
+
readonly digest: string
|
|
86
|
+
readonly savedAt: number
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface TeamVersionEntry {
|
|
90
|
+
readonly version: number
|
|
91
|
+
readonly digest: string
|
|
92
|
+
readonly publishedAt: number
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface TeamVersion extends TeamVersionEntry {
|
|
96
|
+
readonly v: 1
|
|
97
|
+
readonly teamID: string
|
|
98
|
+
readonly spec: TeamSpec
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** What changed (names and keys only). Edge keys are `from>to` as drawn. */
|
|
102
|
+
export interface TeamDiff {
|
|
103
|
+
readonly fields: readonly string[]
|
|
104
|
+
readonly roles: { readonly added: readonly string[]; readonly removed: readonly string[]; readonly changed: readonly string[] }
|
|
105
|
+
readonly edges: { readonly added: readonly string[]; readonly removed: readonly string[]; readonly changed: readonly string[] }
|
|
106
|
+
readonly tags: { readonly added: readonly string[]; readonly removed: readonly string[] }
|
|
107
|
+
readonly metadata: { readonly added: readonly string[]; readonly removed: readonly string[]; readonly changed: readonly string[] }
|
|
108
|
+
readonly projects: { readonly added: readonly string[]; readonly removed: readonly string[] }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** A session staffed in a role; a replacement is the slot's next generation. */
|
|
112
|
+
export interface TeamStaffing {
|
|
113
|
+
readonly v: 1
|
|
114
|
+
readonly id: string
|
|
115
|
+
readonly teamID: string
|
|
116
|
+
readonly role: string
|
|
117
|
+
readonly sessionID: string
|
|
118
|
+
readonly generation: number
|
|
119
|
+
readonly since: number
|
|
120
|
+
readonly until?: number
|
|
121
|
+
readonly revision: number
|
|
122
|
+
/** The published version it was staffed under. */
|
|
123
|
+
readonly teamVersion: number
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type TeamSessionPresence = "online" | "offline" | "archived" | "deleted" | "unknown"
|
|
127
|
+
|
|
128
|
+
export interface TeamStaffingRequest {
|
|
129
|
+
readonly id: string
|
|
130
|
+
readonly teamID: string
|
|
131
|
+
readonly role: string
|
|
132
|
+
readonly state: "launching" | "staffed" | "failed"
|
|
133
|
+
readonly by: string
|
|
134
|
+
readonly launchID?: string
|
|
135
|
+
readonly replaces?: { readonly staffingID: string; readonly generation: number }
|
|
136
|
+
readonly sessionID?: string
|
|
137
|
+
readonly machineID?: string
|
|
138
|
+
readonly staffingID?: string
|
|
139
|
+
readonly reason?: string
|
|
140
|
+
readonly createdAt: number
|
|
141
|
+
readonly updatedAt: number
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface TeamWorkspaceLink {
|
|
145
|
+
readonly id: string
|
|
146
|
+
readonly state: "active" | "archived" | "missing"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface TeamRosterSeat {
|
|
150
|
+
readonly staffing: TeamStaffing
|
|
151
|
+
readonly presence: TeamSessionPresence
|
|
152
|
+
readonly memberID?: string
|
|
153
|
+
readonly supervisorMemberID?: string
|
|
154
|
+
readonly profile?: {
|
|
155
|
+
readonly requested: { readonly profileID: string; readonly version?: number }
|
|
156
|
+
readonly assigned?: { readonly profileID: string; readonly version: number }
|
|
157
|
+
readonly status: string
|
|
158
|
+
readonly matches: boolean
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface TeamRosterRole {
|
|
163
|
+
readonly role: string
|
|
164
|
+
readonly entry: boolean
|
|
165
|
+
readonly minMembers: number
|
|
166
|
+
readonly maxMembers: number
|
|
167
|
+
readonly seats: readonly TeamRosterSeat[]
|
|
168
|
+
readonly launching: number
|
|
169
|
+
readonly online: number
|
|
170
|
+
readonly offline: number
|
|
171
|
+
readonly vacant: boolean
|
|
172
|
+
readonly understaffed: boolean
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface TeamRoster {
|
|
176
|
+
readonly teamID: string
|
|
177
|
+
readonly version: number
|
|
178
|
+
readonly state: "active" | "archived"
|
|
179
|
+
readonly paused: boolean
|
|
180
|
+
readonly workspace?: TeamWorkspaceLink
|
|
181
|
+
readonly roles: readonly TeamRosterRole[]
|
|
182
|
+
readonly requests: readonly TeamStaffingRequest[]
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Intake counts (no text): by status, and routed items by progress. Closed items count while retained. */
|
|
186
|
+
export interface TeamIntakeSummary {
|
|
187
|
+
readonly queued: number
|
|
188
|
+
readonly routed: number
|
|
189
|
+
readonly done: number
|
|
190
|
+
readonly cancelled: number
|
|
191
|
+
readonly failed: number
|
|
192
|
+
readonly open: number
|
|
193
|
+
readonly inProgress: number
|
|
194
|
+
readonly blocked: number
|
|
195
|
+
readonly oldestQueuedAt?: number
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface TeamStatus {
|
|
199
|
+
readonly team: Team
|
|
200
|
+
readonly roster: TeamRoster
|
|
201
|
+
readonly intake?: TeamIntakeSummary
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** The owner's team settings (users only; app and session tokens are refused). */
|
|
205
|
+
export interface TeamSettings {
|
|
206
|
+
/** On: team members see only the sessions of their teams (non-team sessions are hidden from them). */
|
|
207
|
+
readonly teamIsolation: boolean
|
|
208
|
+
/** 0 while never set; send it back as expectedRevision. */
|
|
209
|
+
readonly revision: number
|
|
210
|
+
readonly updatedAt?: number
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface TeamListFilter extends Signal {
|
|
214
|
+
/** active (default), archived or all. */
|
|
215
|
+
readonly state?: "active" | "archived" | "all"
|
|
216
|
+
/** Teams carrying every one of these tags. */
|
|
217
|
+
readonly tags?: readonly string[]
|
|
218
|
+
readonly project?: string
|
|
219
|
+
/** Teams whose metadata has every one of these pairs. */
|
|
220
|
+
readonly metadata?: Readonly<Record<string, string>>
|
|
221
|
+
readonly paused?: boolean
|
|
222
|
+
readonly limit?: number
|
|
223
|
+
readonly cursor?: string
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Spec fields a draft edit may change (the name never changes; null clears an optional field). */
|
|
227
|
+
export type TeamPatch = { readonly [K in Exclude<keyof TeamSpec, "name">]?: TeamSpec[K] | null }
|
|
228
|
+
|
|
229
|
+
export interface Page<T> {
|
|
230
|
+
readonly items: readonly T[]
|
|
231
|
+
readonly cursor?: string
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface Teams extends TeamIntakeApi {
|
|
235
|
+
create(spec: TeamSpec, options?: Keyed): Promise<{ team: Team; created: boolean }>
|
|
236
|
+
list(filter?: TeamListFilter): Promise<Page<Team>>
|
|
237
|
+
get(team: string, options?: Signal): Promise<{ team: Team; draft?: TeamDraft }>
|
|
238
|
+
/** Saves edits as the draft; with `publish: true` publishes it at once. */
|
|
239
|
+
update(team: string, patch: TeamPatch, expectedRevision: number, options?: Signal & { readonly publish?: boolean }): Promise<{ team: Team; published?: boolean }>
|
|
240
|
+
draft(team: string, options?: Signal): Promise<{ draft: TeamDraft }>
|
|
241
|
+
discardDraft(team: string, expectedRevision: number, options?: Signal): Promise<{ team: Team }>
|
|
242
|
+
/** Adds the role to the draft, or replaces the role of that name. */
|
|
243
|
+
setRole(team: string, role: TeamRole, expectedRevision: number, options?: Signal): Promise<{ team: Team; draft: TeamDraft }>
|
|
244
|
+
/** Removes a role (not the entry role) and its edges from the draft. */
|
|
245
|
+
removeRole(team: string, role: string, expectedRevision: number, options?: Signal): Promise<{ team: Team; draft: TeamDraft }>
|
|
246
|
+
/** Draws (or replaces) the edge from → to in the draft. */
|
|
247
|
+
setEdge(team: string, edge: TeamEdge, expectedRevision: number, options?: Signal): Promise<{ team: Team; draft: TeamDraft }>
|
|
248
|
+
removeEdge(team: string, from: string, to: string, expectedRevision: number, options?: Signal): Promise<{ team: Team; draft: TeamDraft }>
|
|
249
|
+
/** Publishes the draft as the next version (published false: it equalled the published spec). */
|
|
250
|
+
publish(team: string, expectedRevision: number, options?: Signal): Promise<{ team: Team; published: boolean }>
|
|
251
|
+
pause(team: string, expectedRevision: number, options?: Signal): Promise<{ team: Team }>
|
|
252
|
+
resume(team: string, expectedRevision: number, options?: Signal): Promise<{ team: Team }>
|
|
253
|
+
archive(team: string, expectedRevision: number, options?: Signal): Promise<{ team: Team }>
|
|
254
|
+
restore(team: string, expectedRevision: number, options?: Signal): Promise<{ team: Team }>
|
|
255
|
+
versions(team: string, options?: Signal & { readonly limit?: number; readonly cursor?: string }): Promise<Page<TeamVersionEntry>>
|
|
256
|
+
version(team: string, version: number, options?: Signal): Promise<{ version: TeamVersion }>
|
|
257
|
+
/** From a version (default: the published one) to another version or the draft (default). */
|
|
258
|
+
diff(team: string, options?: Signal & { readonly from?: number; readonly to?: number | "draft" }): Promise<{ from: number; to: number | "draft"; diff: TeamDiff }>
|
|
259
|
+
/** Current seats (with ended ones on `ended`). */
|
|
260
|
+
staffing(team: string, options?: Signal & { readonly ended?: boolean }): Promise<{ items: readonly TeamStaffing[] }>
|
|
261
|
+
/** Staffs an existing session in a role (the same role and session again: its seat, created false). */
|
|
262
|
+
staff(team: string, input: { readonly role: string; readonly sessionID: string }, options?: Keyed): Promise<{ staffing: TeamStaffing; workspace?: TeamWorkspaceLink; created: boolean }>
|
|
263
|
+
/** Launches a new member for a role through the launch API; the running session is staffed. */
|
|
264
|
+
launchMember(team: string, input: { readonly role: string; readonly launch: LaunchSpec }, options?: Keyed): Promise<{ request: TeamStaffingRequest; launch: LaunchView; created: boolean }>
|
|
265
|
+
/** Replaces a seat by an existing session or a launch; the old seat ends at once. */
|
|
266
|
+
replace(
|
|
267
|
+
team: string,
|
|
268
|
+
staffingID: string,
|
|
269
|
+
input: { readonly expectedRevision: number } & ({ readonly sessionID: string; readonly launch?: never } | { readonly launch: LaunchSpec; readonly sessionID?: never }),
|
|
270
|
+
options?: Signal,
|
|
271
|
+
): Promise<{ ended: TeamStaffing; staffing?: TeamStaffing; request?: TeamStaffingRequest; launch?: LaunchView }>
|
|
272
|
+
unstaff(team: string, staffingID: string, expectedRevision: number, options?: Signal): Promise<{ staffing: TeamStaffing }>
|
|
273
|
+
roster(team: string, options?: Signal): Promise<{ roster: TeamRoster }>
|
|
274
|
+
/** The team, its roster and intake counts. */
|
|
275
|
+
status(team: string, options?: Signal): Promise<TeamStatus>
|
|
276
|
+
/** The owner's team settings (GET /api/agent/team-settings; users only). */
|
|
277
|
+
settings(options?: Signal): Promise<{ settings: TeamSettings }>
|
|
278
|
+
/** Changes them under CAS (expectedRevision 0 when never set). */
|
|
279
|
+
setSettings(input: { readonly teamIsolation: boolean }, expectedRevision: number, options?: Signal): Promise<{ settings: TeamSettings }>
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
type Query = Readonly<Record<string, string | number | readonly string[] | undefined>>
|
|
283
|
+
|
|
284
|
+
/** What hub.teams needs from the client (HubClient's request and call); queries may repeat a key (arrays). */
|
|
285
|
+
export interface TeamsTransport extends TeamIntakeTransport {
|
|
286
|
+
request(method: string, pathname: string, options?: { readonly query?: Query; readonly body?: unknown; readonly headers?: Readonly<Record<string, string>>; readonly signal?: AbortSignal }): Promise<Response>
|
|
287
|
+
call<T>(method: string, pathname: string, options?: { readonly query?: Query; readonly body?: unknown; readonly signal?: AbortSignal }): Promise<T>
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const seg = (value: string) => encodeURIComponent(value)
|
|
291
|
+
const sig = (options: Signal = {}) => (options.signal ? { signal: options.signal } : {})
|
|
292
|
+
const key = (options: Keyed = {}) => (options.idempotencyKey ? { headers: { "idempotency-key": options.idempotencyKey } } : {})
|
|
293
|
+
const base = (team: string) => `/api/agent/teams/${seg(team)}`
|
|
294
|
+
|
|
295
|
+
export function teamsApi(hub: TeamsTransport): Teams {
|
|
296
|
+
const created = async <T>(method: string, pathname: string, body: unknown, options: Keyed = {}): Promise<T & { created: boolean }> => {
|
|
297
|
+
const response = await hub.request(method, pathname, { body, ...key(options), ...sig(options) })
|
|
298
|
+
return { ...((await response.json()) as T), created: response.status === 201 }
|
|
299
|
+
}
|
|
300
|
+
const revised = (verb: string) => (team: string, expectedRevision: number, options?: Signal) => hub.call<{ team: Team }>("POST", `${base(team)}/${verb}`, { body: { expectedRevision }, ...sig(options) })
|
|
301
|
+
return {
|
|
302
|
+
...teamIntakeApi(hub),
|
|
303
|
+
create: (spec, options) => created("POST", "/api/agent/teams", spec, options),
|
|
304
|
+
list: (filter = {}) =>
|
|
305
|
+
hub.call("GET", "/api/agent/teams", {
|
|
306
|
+
query: {
|
|
307
|
+
state: filter.state,
|
|
308
|
+
tag: filter.tags,
|
|
309
|
+
project: filter.project,
|
|
310
|
+
metadata: filter.metadata ? Object.entries(filter.metadata).map(([k, v]) => `${k}=${v}`) : undefined,
|
|
311
|
+
paused: filter.paused === undefined ? undefined : String(filter.paused),
|
|
312
|
+
limit: filter.limit,
|
|
313
|
+
cursor: filter.cursor,
|
|
314
|
+
},
|
|
315
|
+
...sig(filter),
|
|
316
|
+
}),
|
|
317
|
+
get: (team, options) => hub.call("GET", base(team), sig(options)),
|
|
318
|
+
update: (team, patch, expectedRevision, options = {}) => hub.call("PATCH", base(team), { body: { ...patch, expectedRevision, ...(options.publish ? { publish: true } : {}) }, ...sig(options) }),
|
|
319
|
+
draft: (team, options) => hub.call("GET", `${base(team)}/draft`, sig(options)),
|
|
320
|
+
discardDraft: (team, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft`, { query: { expectedRevision }, ...sig(options) }),
|
|
321
|
+
setRole: (team, role, expectedRevision, options) => {
|
|
322
|
+
const { name, ...fields } = role
|
|
323
|
+
return hub.call("PUT", `${base(team)}/draft/roles/${seg(name)}`, { body: { ...fields, expectedRevision }, ...sig(options) })
|
|
324
|
+
},
|
|
325
|
+
removeRole: (team, role, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft/roles/${seg(role)}`, { query: { expectedRevision }, ...sig(options) }),
|
|
326
|
+
setEdge: (team, edge, expectedRevision, options) => hub.call("PUT", `${base(team)}/draft/edges`, { body: { ...edge, expectedRevision }, ...sig(options) }),
|
|
327
|
+
removeEdge: (team, from, to, expectedRevision, options) => hub.call("DELETE", `${base(team)}/draft/edges`, { query: { from, to, expectedRevision }, ...sig(options) }),
|
|
328
|
+
publish: (team, expectedRevision, options) => hub.call("POST", `${base(team)}/publish`, { body: { expectedRevision }, ...sig(options) }),
|
|
329
|
+
pause: revised("pause"),
|
|
330
|
+
resume: revised("resume"),
|
|
331
|
+
archive: revised("archive"),
|
|
332
|
+
restore: revised("restore"),
|
|
333
|
+
versions: (team, options = {}) => hub.call("GET", `${base(team)}/versions`, { query: { limit: options.limit, cursor: options.cursor }, ...sig(options) }),
|
|
334
|
+
version: (team, version, options) => hub.call("GET", `${base(team)}/versions/${version}`, sig(options)),
|
|
335
|
+
diff: (team, options = {}) => hub.call("GET", `${base(team)}/diff`, { query: { from: options.from, to: options.to }, ...sig(options) }),
|
|
336
|
+
staffing: (team, options = {}) => hub.call("GET", `${base(team)}/staffing`, { query: { ended: options.ended ? 1 : undefined }, ...sig(options) }),
|
|
337
|
+
staff: (team, input, options) => created("PUT", `${base(team)}/staffing`, input, options),
|
|
338
|
+
launchMember: (team, input, options) => created("POST", `${base(team)}/staffing/launch`, input, options),
|
|
339
|
+
replace: (team, staffingID, input, options) => hub.call("POST", `${base(team)}/staffing/${seg(staffingID)}/replace`, { body: input, ...sig(options) }),
|
|
340
|
+
unstaff: (team, staffingID, expectedRevision, options) => hub.call("DELETE", `${base(team)}/staffing/${seg(staffingID)}`, { query: { expectedRevision }, ...sig(options) }),
|
|
341
|
+
roster: (team, options) => hub.call("GET", `${base(team)}/roster`, sig(options)),
|
|
342
|
+
status: (team, options) => hub.call("GET", `${base(team)}/status`, sig(options)),
|
|
343
|
+
settings: (options) => hub.call("GET", "/api/agent/team-settings", sig(options)),
|
|
344
|
+
setSettings: (input, expectedRevision, options) => hub.call("PUT", "/api/agent/team-settings", { body: { teamIsolation: input.teamIsolation, expectedRevision }, ...sig(options) }),
|
|
345
|
+
}
|
|
346
|
+
}
|