@mattstack/rt-client 0.3.0 → 0.4.1
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/README.md +48 -0
- package/dist/client.d.ts +88 -1
- package/dist/commands.d.ts +180 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +338 -21
- package/dist/repos.d.ts +9 -3
- package/dist/settings/identity.d.ts +43 -19
- package/dist/settings/write.d.ts +12 -0
- package/package.json +1 -1
- package/src/client.ts +141 -1
- package/src/commands.ts +91 -1
- package/src/index.ts +38 -3
- package/src/repos.ts +85 -14
- package/src/settings/identity.ts +121 -28
- package/src/settings/registry-defs.ts +43 -6
- package/src/settings/write.ts +91 -0
package/README.md
CHANGED
|
@@ -26,6 +26,54 @@ Bun-only: the settings exec path (`src/settings/exec.ts`) shells out via
|
|
|
26
26
|
`@mattstack/glance` is a peer dependency: rt-client returns glance's forge types
|
|
27
27
|
so merge request shapes stay identical across rt, gitq, and mr-board.
|
|
28
28
|
|
|
29
|
+
## Repo identity
|
|
30
|
+
|
|
31
|
+
Every per-repo key in the rt estate is a stable serialized identity, not a
|
|
32
|
+
repo name. Whatever you store per-repo, send to the daemon, or put in a REST
|
|
33
|
+
path is keyed by the wire form this package emits. The one exception:
|
|
34
|
+
settings-store sections (`repos.<identity>`) key on the RAW `host/path` form
|
|
35
|
+
(`RepoIdentity.id` for a remote-kind identity) — the settings resolver never
|
|
36
|
+
sees the wire form, and a serialized key there misses silently.
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
remote:gitlab.com%2Facme%2Facme-dev path:%2FUsers%2Fdev%2Fscratch
|
|
40
|
+
└─┬──┘ └──────────┬─────────────┘
|
|
41
|
+
kind the id, encodeURIComponent'd — slash-free, fits one URL segment
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`kind` is `remote` (the repo has an origin: id is normalized `host/path`) or
|
|
45
|
+
`path` (no usable remote: id is the main worktree's realpath). The `:` is a
|
|
46
|
+
literal delimiter.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import {
|
|
50
|
+
deriveRepoIdentity, // (repoPath: string) => Promise<RepoIdentity> — never null
|
|
51
|
+
serializeIdentity, // (id: RepoIdentity) => string — the wire form above
|
|
52
|
+
parseIdentity, // (wire: string) => RepoIdentity | null — THE validity check
|
|
53
|
+
identityFromRemote, // (remoteUrl: string) => RepoIdentity | null — sync
|
|
54
|
+
type RepoIdentity, // { kind: "remote" | "path"; id: string }
|
|
55
|
+
} from '@mattstack/rt-client';
|
|
56
|
+
|
|
57
|
+
const identity = serializeIdentity(await deriveRepoIdentity(repoPath));
|
|
58
|
+
|
|
59
|
+
await rtCommand(['worktree', 'list', '--repo', identity]); // daemon key
|
|
60
|
+
const url = `/api/runs/${encodeURIComponent(identity)}/${runId}`; // URL segment
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Do | Don't |
|
|
64
|
+
|---|---|
|
|
65
|
+
| Get identities from these functions, once, at the boundary | Re-derive with your own git calls (`git remote get-url` diverges under `insteadOf`) |
|
|
66
|
+
| Key stores and daemon payloads on the serialized form | Key anything on a folder basename or a remote's last segment |
|
|
67
|
+
| Key settings sections (`repos.<identity>`) on the raw `host/path` id | Put the serialized form in a settings lookup, or the raw form in a daemon payload |
|
|
68
|
+
| `encodeURIComponent(identity)` in URL path segments | Ship the wire form raw in a URL — its `%` signs decode into slashes |
|
|
69
|
+
| Decode for display: `parseIdentity(wire)`, then the id's last path segment (remote) or basename (path) — the returned `id` is already decoded | `decodeURIComponent` the id again, show the wire form to a human, or build a chat handle from it |
|
|
70
|
+
| Treat the `repo` field from `runs:list` as an opaque key, passed back verbatim | Validate or re-derive `runs:*` repo keys — pre-cutover runs keep their original keys |
|
|
71
|
+
|
|
72
|
+
Repo-keyed daemon verbs accept serialized identities only; a bare repo name
|
|
73
|
+
doesn't error, it resolves empty. `parseIdentity` is strict — only strings
|
|
74
|
+
`serializeIdentity` emitted parse — so validate payloads with it, and never
|
|
75
|
+
hand-assemble or string-split a wire.
|
|
76
|
+
|
|
29
77
|
## License
|
|
30
78
|
|
|
31
79
|
MIT
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RtResponse, RtClientOptions } from "./transport.ts";
|
|
2
|
-
import type { DemandDecl, ProjectMRsData, DiscussionsData, MrByBranchData, ForgeSlug, ForgeTokenData, RunSummary, RunDetail } from "./commands.ts";
|
|
2
|
+
import type { DemandDecl, ProjectMRsData, DiscussionsData, MrByBranchData, ForgeSlug, ForgeTokenData, RunSummary, RunDetail, WakeMode, ChatMember, ChatMessage, RoomSummary } from "./commands.ts";
|
|
3
3
|
/**
|
|
4
4
|
* One repo's project open-MR store. A cold repo forces a full paginated sync
|
|
5
5
|
* on the daemon side when maxAgeMs demands it, which can run tens of seconds
|
|
@@ -25,3 +25,90 @@ export declare function listRuns(repo?: string, opts?: RtClientOptions): Promise
|
|
|
25
25
|
runs: RunSummary[];
|
|
26
26
|
}>>;
|
|
27
27
|
export declare function getRun(runId: string, repo?: string, opts?: RtClientOptions): Promise<RtResponse<RunDetail>>;
|
|
28
|
+
/**
|
|
29
|
+
* SKILLS-54. rt's only write path into run state, so a wedged run can be
|
|
30
|
+
* resolved by a person instead of lying in the data forever. The write happens
|
|
31
|
+
* in the daemon and is attributed there; consumers never touch the run DB.
|
|
32
|
+
*/
|
|
33
|
+
export declare function abandonRun(runId: string, repo?: string, reason?: string, opts?: RtClientOptions): Promise<RtResponse<{
|
|
34
|
+
ok: boolean;
|
|
35
|
+
}>>;
|
|
36
|
+
export declare function chatJoin(a: {
|
|
37
|
+
room: string;
|
|
38
|
+
handle: string;
|
|
39
|
+
wakeOn?: WakeMode;
|
|
40
|
+
cwd?: string;
|
|
41
|
+
pane?: string;
|
|
42
|
+
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
43
|
+
handle: string;
|
|
44
|
+
memberCount: number;
|
|
45
|
+
unread: number;
|
|
46
|
+
}>>;
|
|
47
|
+
export declare function chatLeave(a: {
|
|
48
|
+
room: string;
|
|
49
|
+
handle: string;
|
|
50
|
+
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
51
|
+
export declare function chatPost(a: {
|
|
52
|
+
room: string;
|
|
53
|
+
handle: string;
|
|
54
|
+
body: string;
|
|
55
|
+
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
56
|
+
id: number;
|
|
57
|
+
recipients: string[];
|
|
58
|
+
}>>;
|
|
59
|
+
export declare function chatRead(a: {
|
|
60
|
+
handle: string;
|
|
61
|
+
room?: string;
|
|
62
|
+
limit?: number;
|
|
63
|
+
sinceMs?: number;
|
|
64
|
+
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
65
|
+
rooms: {
|
|
66
|
+
room: string;
|
|
67
|
+
messages: ChatMessage[];
|
|
68
|
+
}[];
|
|
69
|
+
}>>;
|
|
70
|
+
export declare function chatRooms(a: {
|
|
71
|
+
handle: string;
|
|
72
|
+
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
73
|
+
rooms: RoomSummary[];
|
|
74
|
+
}>>;
|
|
75
|
+
export declare function chatWho(a: {
|
|
76
|
+
room: string;
|
|
77
|
+
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
78
|
+
members: ChatMember[];
|
|
79
|
+
}>>;
|
|
80
|
+
export declare function chatMark(a: {
|
|
81
|
+
handle: string;
|
|
82
|
+
room?: string;
|
|
83
|
+
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
84
|
+
export declare function chatMessages(a: {
|
|
85
|
+
room: string;
|
|
86
|
+
before?: number;
|
|
87
|
+
limit?: number;
|
|
88
|
+
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
89
|
+
messages: ChatMessage[];
|
|
90
|
+
}>>;
|
|
91
|
+
export declare function chatArm(a: {
|
|
92
|
+
handle: string;
|
|
93
|
+
room?: string;
|
|
94
|
+
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
95
|
+
export declare function chatTouch(a: {
|
|
96
|
+
handle: string;
|
|
97
|
+
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
98
|
+
export declare function chatDisarm(a: {
|
|
99
|
+
handle: string;
|
|
100
|
+
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
101
|
+
export declare function chatUnreadWaking(a: {
|
|
102
|
+
handle: string;
|
|
103
|
+
room?: string;
|
|
104
|
+
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
105
|
+
rooms: {
|
|
106
|
+
room: string;
|
|
107
|
+
count: number;
|
|
108
|
+
mentions: number;
|
|
109
|
+
maxId: number;
|
|
110
|
+
}[];
|
|
111
|
+
}>>;
|
|
112
|
+
export declare function eventsHead(o?: RtClientOptions): Promise<RtResponse<{
|
|
113
|
+
cursor: number;
|
|
114
|
+
}>>;
|
package/dist/commands.d.ts
CHANGED
|
@@ -55,6 +55,44 @@ export interface EventsBusEvent {
|
|
|
55
55
|
payload: unknown;
|
|
56
56
|
emittedAt: number;
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Duplicated shape on purpose, same reasoning as EventsBusEvent above:
|
|
60
|
+
* these mirror lib/state/chat-store.ts's types, which rt-client cannot
|
|
61
|
+
* import (it's outside lib/state/ and outside this package entirely).
|
|
62
|
+
*/
|
|
63
|
+
export type WakeMode = "mention" | "all" | "none";
|
|
64
|
+
export interface ChatMember {
|
|
65
|
+
room: string;
|
|
66
|
+
handle: string;
|
|
67
|
+
joinedAt: number;
|
|
68
|
+
lastReadId: number;
|
|
69
|
+
wakeOn: WakeMode;
|
|
70
|
+
lastSeenAt?: number;
|
|
71
|
+
armedAt?: number;
|
|
72
|
+
cwd?: string;
|
|
73
|
+
pane?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface ChatMessage {
|
|
76
|
+
id: number;
|
|
77
|
+
room: string;
|
|
78
|
+
handle: string;
|
|
79
|
+
body: string;
|
|
80
|
+
mentions: string[];
|
|
81
|
+
replyTo?: number;
|
|
82
|
+
postedAt: number;
|
|
83
|
+
}
|
|
84
|
+
export interface RoomSummary {
|
|
85
|
+
room: string;
|
|
86
|
+
memberCount: number;
|
|
87
|
+
unread: number;
|
|
88
|
+
mentions: number;
|
|
89
|
+
lastPostedAt?: number;
|
|
90
|
+
}
|
|
91
|
+
export type Attention = {
|
|
92
|
+
needs: boolean;
|
|
93
|
+
reason: "failed" | "stale" | "stranded" | null;
|
|
94
|
+
evidence: string;
|
|
95
|
+
};
|
|
58
96
|
export interface RunSummary {
|
|
59
97
|
id: string;
|
|
60
98
|
repo: string;
|
|
@@ -65,6 +103,18 @@ export interface RunSummary {
|
|
|
65
103
|
spawned_by: string | null;
|
|
66
104
|
started_at: number;
|
|
67
105
|
ended_at: number | null;
|
|
106
|
+
pack_commits: string | null;
|
|
107
|
+
pack_dirty: number;
|
|
108
|
+
attention: Attention;
|
|
109
|
+
/** Max over stage, field, and decision timestamps; falls back to
|
|
110
|
+
`started_at` when the run has produced no events yet. The board orders
|
|
111
|
+
by silence, so this — not `started_at` — is its sort key. */
|
|
112
|
+
last_event_at: number;
|
|
113
|
+
/** Denormalized from the run's `ticket` / `branch` fields so the LIST view
|
|
114
|
+
can render and search them without a detail fetch per row. Null when
|
|
115
|
+
the run has not produced that field yet. */
|
|
116
|
+
ticket: string | null;
|
|
117
|
+
branch: string | null;
|
|
68
118
|
}
|
|
69
119
|
export interface RunStageRow {
|
|
70
120
|
name: string;
|
|
@@ -72,6 +122,8 @@ export interface RunStageRow {
|
|
|
72
122
|
attempt: number;
|
|
73
123
|
started_at: number | null;
|
|
74
124
|
ended_at: number | null;
|
|
125
|
+
reason: string | null;
|
|
126
|
+
detail_path: string | null;
|
|
75
127
|
}
|
|
76
128
|
export interface RunFieldRow {
|
|
77
129
|
key: string;
|
|
@@ -204,6 +256,12 @@ export interface Commands {
|
|
|
204
256
|
cursor: number;
|
|
205
257
|
};
|
|
206
258
|
};
|
|
259
|
+
"events:head": {
|
|
260
|
+
payload: Record<string, never>;
|
|
261
|
+
data: {
|
|
262
|
+
cursor: number;
|
|
263
|
+
};
|
|
264
|
+
};
|
|
207
265
|
"runs:list": {
|
|
208
266
|
payload: {
|
|
209
267
|
repo?: string;
|
|
@@ -219,6 +277,128 @@ export interface Commands {
|
|
|
219
277
|
};
|
|
220
278
|
data: RunDetail;
|
|
221
279
|
};
|
|
280
|
+
"runs:abandon": {
|
|
281
|
+
payload: {
|
|
282
|
+
runId: string;
|
|
283
|
+
repo?: string;
|
|
284
|
+
reason?: string;
|
|
285
|
+
};
|
|
286
|
+
data: {
|
|
287
|
+
ok: boolean;
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
"chat:join": {
|
|
291
|
+
payload: {
|
|
292
|
+
room: string;
|
|
293
|
+
handle: string;
|
|
294
|
+
wakeOn?: WakeMode;
|
|
295
|
+
cwd?: string;
|
|
296
|
+
pane?: string;
|
|
297
|
+
};
|
|
298
|
+
data: {
|
|
299
|
+
handle: string;
|
|
300
|
+
memberCount: number;
|
|
301
|
+
unread: number;
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
"chat:leave": {
|
|
305
|
+
payload: {
|
|
306
|
+
room: string;
|
|
307
|
+
handle: string;
|
|
308
|
+
};
|
|
309
|
+
data: Record<string, never>;
|
|
310
|
+
};
|
|
311
|
+
"chat:post": {
|
|
312
|
+
payload: {
|
|
313
|
+
room: string;
|
|
314
|
+
handle: string;
|
|
315
|
+
body: string;
|
|
316
|
+
};
|
|
317
|
+
data: {
|
|
318
|
+
id: number;
|
|
319
|
+
recipients: string[];
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
"chat:read": {
|
|
323
|
+
payload: {
|
|
324
|
+
handle: string;
|
|
325
|
+
room?: string;
|
|
326
|
+
limit?: number;
|
|
327
|
+
sinceMs?: number;
|
|
328
|
+
};
|
|
329
|
+
data: {
|
|
330
|
+
rooms: {
|
|
331
|
+
room: string;
|
|
332
|
+
messages: ChatMessage[];
|
|
333
|
+
}[];
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
"chat:rooms": {
|
|
337
|
+
payload: {
|
|
338
|
+
handle: string;
|
|
339
|
+
};
|
|
340
|
+
data: {
|
|
341
|
+
rooms: RoomSummary[];
|
|
342
|
+
};
|
|
343
|
+
};
|
|
344
|
+
"chat:who": {
|
|
345
|
+
payload: {
|
|
346
|
+
room: string;
|
|
347
|
+
};
|
|
348
|
+
data: {
|
|
349
|
+
members: ChatMember[];
|
|
350
|
+
};
|
|
351
|
+
};
|
|
352
|
+
"chat:mark": {
|
|
353
|
+
payload: {
|
|
354
|
+
handle: string;
|
|
355
|
+
room?: string;
|
|
356
|
+
};
|
|
357
|
+
data: Record<string, never>;
|
|
358
|
+
};
|
|
359
|
+
"chat:messages": {
|
|
360
|
+
payload: {
|
|
361
|
+
room: string;
|
|
362
|
+
before?: number;
|
|
363
|
+
limit?: number;
|
|
364
|
+
};
|
|
365
|
+
data: {
|
|
366
|
+
messages: ChatMessage[];
|
|
367
|
+
};
|
|
368
|
+
};
|
|
369
|
+
"chat:arm": {
|
|
370
|
+
payload: {
|
|
371
|
+
handle: string;
|
|
372
|
+
room?: string;
|
|
373
|
+
};
|
|
374
|
+
data: Record<string, never>;
|
|
375
|
+
};
|
|
376
|
+
"chat:touch": {
|
|
377
|
+
payload: {
|
|
378
|
+
handle: string;
|
|
379
|
+
};
|
|
380
|
+
data: Record<string, never>;
|
|
381
|
+
};
|
|
382
|
+
"chat:disarm": {
|
|
383
|
+
payload: {
|
|
384
|
+
handle: string;
|
|
385
|
+
};
|
|
386
|
+
data: Record<string, never>;
|
|
387
|
+
};
|
|
388
|
+
"chat:unread-waking": {
|
|
389
|
+
payload: {
|
|
390
|
+
handle: string;
|
|
391
|
+
room?: string;
|
|
392
|
+
};
|
|
393
|
+
data: {
|
|
394
|
+
rooms: {
|
|
395
|
+
room: string;
|
|
396
|
+
count: number;
|
|
397
|
+
mentions: number;
|
|
398
|
+
maxId: number;
|
|
399
|
+
}[];
|
|
400
|
+
};
|
|
401
|
+
};
|
|
222
402
|
}
|
|
223
403
|
export type CommandName = keyof Commands;
|
|
224
404
|
export declare const COMMAND_NAMES: readonly CommandName[];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
export { rtCommand, DEFAULT_SOCK } from "./transport.ts";
|
|
2
2
|
export type { RtResponse, RtClientOptions } from "./transport.ts";
|
|
3
|
-
export { readProjectMRs, readDiscussions, readMrsByBranch, resolveForgeToken } from "./client.ts";
|
|
3
|
+
export { readProjectMRs, readDiscussions, readMrsByBranch, resolveForgeToken, listRuns, getRun, abandonRun, chatJoin, chatLeave, chatPost, chatRead, chatRooms, chatWho, chatMark, chatMessages, chatArm, chatTouch, chatDisarm, chatUnreadWaking, eventsHead, } from "./client.ts";
|
|
4
4
|
export { COMMAND_NAMES } from "./commands.ts";
|
|
5
|
-
export type { Discussion, DemandDecl, ProjectMRsScope, ProjectMRsData, DiscussionsData, MrByBranchEntry, MrByBranchData, Commands, CommandName, ForgeSlug, ForgeTokenData, } from "./commands.ts";
|
|
5
|
+
export type { Discussion, DemandDecl, ProjectMRsScope, ProjectMRsData, DiscussionsData, MrByBranchEntry, MrByBranchData, Commands, CommandName, ForgeSlug, ForgeTokenData, Attention, RunSummary, RunStageRow, RunFieldRow, RunDecisionRow, RunDetail, WakeMode, ChatMember, ChatMessage, RoomSummary, } from "./commands.ts";
|
|
6
6
|
export { subscribe, DEFAULT_WS_URL } from "./relay.ts";
|
|
7
7
|
export type { RelayEventType } from "./relay.ts";
|
|
8
8
|
export { repoNameForPath } from "./repos.ts";
|
|
9
9
|
export { getSetting, listSettings, explainSetting, expandVariables, SCOPE_ORDER } from "./settings/resolve.ts";
|
|
10
10
|
export type { Scope, Provenance, ResolveOpts, Resolved, InvalidScope, ListedSetting, ExplainRow, ExpandCtx, } from "./settings/resolve.ts";
|
|
11
|
-
export { setSetting } from "./settings/write.ts";
|
|
11
|
+
export { setSetting, unsetSetting } from "./settings/write.ts";
|
|
12
12
|
export type { SetSettingOpts } from "./settings/write.ts";
|
|
13
13
|
export { getDef, allDefs, validateValue, isMigrated } from "./settings/registry-machinery.ts";
|
|
14
14
|
export type { SettingDef, SettingScope } from "./settings/registry-machinery.ts";
|
|
15
15
|
export { REGISTRY } from "./settings/registry-defs.ts";
|
|
16
16
|
export { readStore, listTeams } from "./settings/stores.ts";
|
|
17
17
|
export type { StoreFile } from "./settings/stores.ts";
|
|
18
|
-
export { normalizeRemote, identityFromRemote, deriveRepoIdentity, clearIdentityMemo } from "./settings/identity.ts";
|
|
18
|
+
export { normalizeRemote, identityFromRemote, deriveRepoIdentity, clearIdentityMemo, serializeIdentity, parseIdentity, resolveNameToIdentity, type RepoIdentity, } from "./settings/identity.ts";
|