dsh-live-teams 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 +176 -0
- package/NOTICE +11 -0
- package/README.md +85 -0
- package/cordis.patch.yml +25 -0
- package/lib/binding.d.ts +18 -0
- package/lib/binding.js +42 -0
- package/lib/changed-paths.d.ts +26 -0
- package/lib/changed-paths.js +69 -0
- package/lib/client.js +6753 -0
- package/lib/command-queue.d.ts +60 -0
- package/lib/command-queue.js +185 -0
- package/lib/compatibility.js +109 -0
- package/lib/context-provider.d.ts +110 -0
- package/lib/context-provider.js +249 -0
- package/lib/dispatch.d.ts +174 -0
- package/lib/dispatch.js +624 -0
- package/lib/errors.d.ts +36 -0
- package/lib/errors.js +103 -0
- package/lib/git-artifacts.d.ts +50 -0
- package/lib/git-artifacts.js +242 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.js +14 -0
- package/lib/mailbox.d.ts +274 -0
- package/lib/mailbox.js +721 -0
- package/lib/member-tools.d.ts +57 -0
- package/lib/member-tools.js +1265 -0
- package/lib/migrations.d.ts +17 -0
- package/lib/migrations.js +47 -0
- package/lib/plugin.d.ts +106 -0
- package/lib/plugin.js +1003 -0
- package/lib/roles.d.ts +35 -0
- package/lib/roles.js +284 -0
- package/lib/routes.d.ts +586 -0
- package/lib/routes.js +2816 -0
- package/lib/scope.d.ts +62 -0
- package/lib/scope.js +133 -0
- package/lib/session-bridge.d.ts +76 -0
- package/lib/session-bridge.js +147 -0
- package/lib/session-title.js +35 -0
- package/lib/storage.d.ts +9 -0
- package/lib/storage.js +65 -0
- package/lib/task-store.d.ts +729 -0
- package/lib/task-store.js +2205 -0
- package/lib/team-store.d.ts +216 -0
- package/lib/team-store.js +765 -0
- package/lib/tree-snapshot.d.ts +28 -0
- package/lib/tree-snapshot.js +80 -0
- package/lib/types/client/TeamView.d.ts +26 -0
- package/lib/types/client/TeamView.dom.test.d.ts +1 -0
- package/lib/types/client/api.d.ts +522 -0
- package/lib/types/client/api.test.d.ts +1 -0
- package/lib/types/client/attention.d.ts +65 -0
- package/lib/types/client/attention.test.d.ts +1 -0
- package/lib/types/client/index.d.ts +31 -0
- package/lib/types/client/locales.d.ts +577 -0
- package/lib/types/client/member-name.d.ts +14 -0
- package/lib/types/client/member-name.test.d.ts +1 -0
- package/lib/types/client/roster.d.ts +26 -0
- package/lib/types/client/roster.test.d.ts +1 -0
- package/lib/types/client/styles.d.ts +3 -0
- package/package.json +104 -0
- package/roles/builder.md +40 -0
- package/roles/delegate.md +36 -0
- package/roles/lead.md +46 -0
- package/roles/oracle.md +36 -0
- package/roles/researcher.md +37 -0
- package/roles/reviewer.md +45 -0
- package/roles/scout.md +36 -0
- package/roles/verifier.md +36 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
//#region src/team-store.d.ts
|
|
2
|
+
export type TeamId = string;
|
|
3
|
+
export type MemberId = string;
|
|
4
|
+
export type SessionId = string;
|
|
5
|
+
export type BindingGeneration = number;
|
|
6
|
+
export type ContactTarget = string | 'human' | 'team';
|
|
7
|
+
export interface ContactGrant {
|
|
8
|
+
from: MemberId;
|
|
9
|
+
to: ContactTarget;
|
|
10
|
+
grantedBy: string;
|
|
11
|
+
at: number;
|
|
12
|
+
}
|
|
13
|
+
export interface MemberRecord {
|
|
14
|
+
memberId: MemberId;
|
|
15
|
+
displayName: string;
|
|
16
|
+
role: string;
|
|
17
|
+
roles?: readonly string[];
|
|
18
|
+
/** Short statement of this member's purpose in the team. */
|
|
19
|
+
note?: string;
|
|
20
|
+
availability: string;
|
|
21
|
+
sessionId?: SessionId;
|
|
22
|
+
bindingGeneration: BindingGeneration;
|
|
23
|
+
contractRevision: number;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Whether the dispatcher may start assigned work. `paused` stops starts and
|
|
28
|
+
* nothing else: an attempt already running keeps running, ownership does not
|
|
29
|
+
* move, and a delivery already queued for a member still reaches that member.
|
|
30
|
+
*/
|
|
31
|
+
export interface DispatchSwitch {
|
|
32
|
+
mode: 'auto' | 'paused';
|
|
33
|
+
/** Who flipped it: `human:desktop` or a lead member id. */
|
|
34
|
+
by: string;
|
|
35
|
+
reason?: string;
|
|
36
|
+
at: number;
|
|
37
|
+
}
|
|
38
|
+
export interface TeamState {
|
|
39
|
+
schemaVersion: number;
|
|
40
|
+
revision: number;
|
|
41
|
+
teamId: TeamId;
|
|
42
|
+
workspaceId: string;
|
|
43
|
+
workspacePath: string;
|
|
44
|
+
members: MemberRecord[];
|
|
45
|
+
archivedMemberIds: string[];
|
|
46
|
+
createdAt: number;
|
|
47
|
+
updatedAt: number;
|
|
48
|
+
/** Persisted human-readable team name (optional for pre-B10 state). */
|
|
49
|
+
name?: string;
|
|
50
|
+
/** Human-selected lead member; omitted for legacy state until resolved by role. */
|
|
51
|
+
leadMemberId?: MemberId;
|
|
52
|
+
/** Standing contact grants issued by the lead. */
|
|
53
|
+
contactGrants?: ContactGrant[];
|
|
54
|
+
/** Dispatch switch; absent means `auto` (the pre-B29 state). */
|
|
55
|
+
dispatch?: DispatchSwitch;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
}
|
|
58
|
+
type StoreDefaults = {
|
|
59
|
+
workspacePath?: string;
|
|
60
|
+
workspaceId?: string;
|
|
61
|
+
teamId?: string;
|
|
62
|
+
name?: string;
|
|
63
|
+
};
|
|
64
|
+
export type TeamCandidate = {
|
|
65
|
+
readonly teamId: string;
|
|
66
|
+
readonly name?: string;
|
|
67
|
+
readonly memberCount?: number;
|
|
68
|
+
readonly updatedAt?: number;
|
|
69
|
+
readonly usable?: boolean;
|
|
70
|
+
readonly reason?: string;
|
|
71
|
+
};
|
|
72
|
+
export type TeamAdoption = {
|
|
73
|
+
readonly state: TeamState;
|
|
74
|
+
readonly adoptedFrom: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Names the transport and the product already use for actors that are not members. A member with one
|
|
78
|
+
* of these ids would send mail *as* that actor — `human` is trusted by the mailbox as a human
|
|
79
|
+
* instruction — so the store refuses to persist such a member at all. The check lives here, not on
|
|
80
|
+
* the HTTP boundary, because a route parser protects one door while the store protects every caller.
|
|
81
|
+
*/
|
|
82
|
+
export declare const RESERVED_ACTOR_NAMES: readonly string[];
|
|
83
|
+
export declare function isReservedActorName(memberId: string): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Who leads this team: the recorded lead when it is still a member, otherwise the single member
|
|
86
|
+
* holding the lead role, otherwise nobody. One definition, because the roster a member is shown and
|
|
87
|
+
* the gate that admits its calls must agree — the expression used to be inlined in a dozen places,
|
|
88
|
+
* and the first change to the rule would have desynchronised them (external review, finding 7).
|
|
89
|
+
*/
|
|
90
|
+
export declare function resolveLeadMemberId(team: Pick<TeamState, 'leadMemberId' | 'members'>): string | undefined;
|
|
91
|
+
declare const TEAM_ROOT = ".dsh-live-teams";
|
|
92
|
+
export declare class TeamStore {
|
|
93
|
+
private readonly defaults;
|
|
94
|
+
private readonly locations;
|
|
95
|
+
constructor(defaults?: StoreDefaults);
|
|
96
|
+
open(request?: StoreDefaults): Promise<TeamState>;
|
|
97
|
+
isDisabledSync(teamId: string): boolean;
|
|
98
|
+
readExisting(request?: StoreDefaults): Promise<TeamState | undefined>;
|
|
99
|
+
isDisabled(teamId: string): Promise<boolean>;
|
|
100
|
+
/** Discover old team directories in this workspace without creating state. */
|
|
101
|
+
listAdoptionCandidates(request: {
|
|
102
|
+
workspacePath: string;
|
|
103
|
+
derivedTeamId: string;
|
|
104
|
+
}): Promise<TeamCandidate[]>;
|
|
105
|
+
/** Adopt one old team into the workspace-derived directory. */
|
|
106
|
+
adoptTeam(request: {
|
|
107
|
+
workspacePath: string;
|
|
108
|
+
workspaceId: string;
|
|
109
|
+
derivedTeamId: string;
|
|
110
|
+
candidateTeamId: string;
|
|
111
|
+
replaceEmptyDerived: boolean;
|
|
112
|
+
actor?: string;
|
|
113
|
+
}): Promise<TeamAdoption>;
|
|
114
|
+
/**
|
|
115
|
+
* Adopt the workspace's own team when it sits under another workspace id.
|
|
116
|
+
*
|
|
117
|
+
* The registry mints a new id whenever an entry is re-created, and the team directory is
|
|
118
|
+
* named by id — so after such an event the panel showed an empty team while the real one
|
|
119
|
+
* (members, tasks, journal) sat right next to it, and only a human clicking "adopt" could
|
|
120
|
+
* bring it back. The canonical path is the stable identity here; the id is a registry
|
|
121
|
+
* variable, so it is adopted rather than treated as a reason to refuse.
|
|
122
|
+
*
|
|
123
|
+
* Deliberately conservative: only one usable candidate, only if it has members, only if it
|
|
124
|
+
* declares this path, and never when the current directory already holds a live team. An
|
|
125
|
+
* ambiguous set stays a human decision — the panel already offers the choice.
|
|
126
|
+
*/
|
|
127
|
+
adoptByPath(request: {
|
|
128
|
+
workspacePath: string;
|
|
129
|
+
workspaceId: string;
|
|
130
|
+
teamId: string;
|
|
131
|
+
actor?: string;
|
|
132
|
+
}): Promise<{
|
|
133
|
+
adoptedFrom: string;
|
|
134
|
+
memberCount: number;
|
|
135
|
+
} | undefined>;
|
|
136
|
+
setDisabled(teamId: string, disabled: boolean): Promise<void>;
|
|
137
|
+
renameTeam(teamId: string, expectedRevision: number, name: string): Promise<{
|
|
138
|
+
state: TeamState;
|
|
139
|
+
from: string;
|
|
140
|
+
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Flip the dispatch switch. Authority belongs to the callers, exactly as it does
|
|
143
|
+
* for `renameTeam`: the desktop route admits the human, and the lead-only tool
|
|
144
|
+
* admits the lead. A request that changes nothing is refused rather than silently
|
|
145
|
+
* accepted, so a double click cannot look like a state change that never happened.
|
|
146
|
+
*/
|
|
147
|
+
setDispatchMode(teamId: string, mode: DispatchSwitch['mode'], request: {
|
|
148
|
+
actor: string;
|
|
149
|
+
reason?: string;
|
|
150
|
+
}, expectedRevision: number): Promise<{
|
|
151
|
+
state: TeamState;
|
|
152
|
+
from: DispatchSwitch['mode'];
|
|
153
|
+
to: DispatchSwitch['mode'];
|
|
154
|
+
}>;
|
|
155
|
+
commit<T>(teamId: string, request: {
|
|
156
|
+
expectedRevision: number;
|
|
157
|
+
mutate: (state: TeamState) => T | Promise<T>;
|
|
158
|
+
}): Promise<{
|
|
159
|
+
state: TeamState;
|
|
160
|
+
value: T;
|
|
161
|
+
}>;
|
|
162
|
+
memberBySession(teamId: string, sessionId: string): Promise<MemberRecord | undefined>;
|
|
163
|
+
bindSession(teamId: string, memberId: string, sessionId: string, expectedRevision: number): Promise<MemberRecord>;
|
|
164
|
+
unbindSession(teamId: string, memberId: string, expectedRevision: number): Promise<MemberRecord>;
|
|
165
|
+
setLead(teamId: string, memberId: string, expectedRevision: number): Promise<TeamState>;
|
|
166
|
+
updateContactGrant(teamId: string, from: MemberId, to: ContactTarget, action: 'add' | 'remove', grantedBy: string): Promise<{
|
|
167
|
+
state: TeamState;
|
|
168
|
+
grants: ContactGrant[];
|
|
169
|
+
changed: boolean;
|
|
170
|
+
}>;
|
|
171
|
+
setMemberNote(teamId: string, memberId: string, note: string, expectedRevision: number): Promise<{
|
|
172
|
+
state: TeamState;
|
|
173
|
+
member: MemberRecord;
|
|
174
|
+
}>;
|
|
175
|
+
removeMember(teamId: string, memberId: string, expectedRevision: number): Promise<{
|
|
176
|
+
state: TeamState;
|
|
177
|
+
removed: MemberRecord;
|
|
178
|
+
}>;
|
|
179
|
+
appendEvent(teamId: string, event: unknown): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* The audit, read from a byte offset when the caller has one.
|
|
182
|
+
*
|
|
183
|
+
* The journal grows forever and the panel re-reads it every few seconds; reading the whole file was
|
|
184
|
+
* the one part of polling that got worse with age (a ten-times-longer journal meant seventeen
|
|
185
|
+
* megabytes a minute). The file is append-only, so an offset is a safe cursor: the caller passes
|
|
186
|
+
* back the offset this returned and pays only for what is new. Without an offset the tail is read —
|
|
187
|
+
* never the whole history, which nobody reads in a panel anyway.
|
|
188
|
+
*/
|
|
189
|
+
readEvents(teamId: string, after?: number): Promise<{
|
|
190
|
+
events: unknown[];
|
|
191
|
+
torn: string | null;
|
|
192
|
+
nextOffset: number;
|
|
193
|
+
truncated: boolean;
|
|
194
|
+
}>;
|
|
195
|
+
private location;
|
|
196
|
+
private requireLocation;
|
|
197
|
+
private read;
|
|
198
|
+
memberBySessionSync(teamId: string, sessionId: string): MemberRecord | undefined;
|
|
199
|
+
}
|
|
200
|
+
export declare function createTeamStore(options?: StoreDefaults): TeamStore;
|
|
201
|
+
/**
|
|
202
|
+
* Read the dispatch switch strictly: an absent switch is `auto`, and anything
|
|
203
|
+
* malformed is corrupt state rather than a quiet fallback to automatic dispatch.
|
|
204
|
+
*/
|
|
205
|
+
export declare function readDispatchSwitch(state: TeamState): DispatchSwitch;
|
|
206
|
+
export declare function readTeamStateSync(request: {
|
|
207
|
+
workspacePath: string;
|
|
208
|
+
teamId: string;
|
|
209
|
+
}): TeamState | undefined;
|
|
210
|
+
export declare function readMemberBySessionSync(request: {
|
|
211
|
+
workspacePath: string;
|
|
212
|
+
teamId: string;
|
|
213
|
+
sessionId: string;
|
|
214
|
+
}): MemberRecord | undefined;
|
|
215
|
+
//#endregion
|
|
216
|
+
export { TEAM_ROOT };
|