@mattstack/rt-client 0.4.1 → 0.6.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/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, WakeMode, ChatMember, ChatMessage, RoomSummary } from "./commands.ts";
2
+ import type { DemandDecl, ProjectMRsData, DiscussionsData, MrByBranchData, BranchEnrichment, ForgeSlug, ForgeTokenData, RunSummary, RunDetail, WakeMode, ChatMember, ChatMessage, RoomSummary, BuddyStatus, PresenceRow } 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
@@ -13,6 +13,12 @@ import type { DemandDecl, ProjectMRsData, DiscussionsData, MrByBranchData, Forge
13
13
  export declare function readProjectMRs(repoName: string, maxAgeMs?: number, opts?: RtClientOptions, demand?: DemandDecl): Promise<RtResponse<ProjectMRsData>>;
14
14
  export declare function readDiscussions(repoName: string, iid: number, opts?: RtClientOptions): Promise<RtResponse<DiscussionsData>>;
15
15
  export declare function readMrsByBranch(repoName: string, branches: string[], opts?: RtClientOptions): Promise<RtResponse<MrByBranchData>>;
16
+ /**
17
+ * Cached ticket/MR enrichment for a set of branches, keyed by branch name
18
+ * (the cache's own primary key -- see lib/state/branch-cache.ts). Serves
19
+ * whatever the daemon already has; it does not trigger a fetch.
20
+ */
21
+ export declare function readBranchCache(branches: string[], opts?: RtClientOptions): Promise<RtResponse<Record<string, BranchEnrichment>>>;
16
22
  /**
17
23
  * The forge token for one tracked repo (MAT-33). Grant-gated on the daemon
18
24
  * side: an untracked repo comes back `ok: false` with the `rt daemon track`
@@ -52,6 +58,7 @@ export declare function chatPost(a: {
52
58
  room: string;
53
59
  handle: string;
54
60
  body: string;
61
+ mentions?: string[];
55
62
  }, o?: RtClientOptions): Promise<RtResponse<{
56
63
  id: number;
57
64
  recipients: string[];
@@ -91,12 +98,15 @@ export declare function chatMessages(a: {
91
98
  export declare function chatArm(a: {
92
99
  handle: string;
93
100
  room?: string;
101
+ sessionId?: string;
94
102
  }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
95
103
  export declare function chatTouch(a: {
96
104
  handle: string;
105
+ sessionId?: string;
97
106
  }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
98
107
  export declare function chatDisarm(a: {
99
108
  handle: string;
109
+ sessionId?: string;
100
110
  }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
101
111
  export declare function chatUnreadWaking(a: {
102
112
  handle: string;
@@ -109,6 +119,57 @@ export declare function chatUnreadWaking(a: {
109
119
  maxId: number;
110
120
  }[];
111
121
  }>>;
122
+ export declare function chatSignIn(a: {
123
+ sessionId: string;
124
+ baseHandle: string;
125
+ cwd?: string;
126
+ repo?: string;
127
+ branch?: string;
128
+ pane?: string;
129
+ statusText?: string;
130
+ }, o?: RtClientOptions): Promise<RtResponse<{
131
+ handle: string;
132
+ reclaimed: boolean;
133
+ }>>;
134
+ export declare function chatSignOut(a: {
135
+ sessionId: string;
136
+ }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
137
+ export declare function chatAway(a: {
138
+ sessionId: string;
139
+ text: string;
140
+ }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
141
+ export declare function chatBack(a: {
142
+ sessionId: string;
143
+ }, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
144
+ export declare function chatBuddies(o?: RtClientOptions): Promise<RtResponse<{
145
+ buddies: Array<PresenceRow & {
146
+ status: BuddyStatus;
147
+ }>;
148
+ }>>;
149
+ export declare function chatPulse(a: {
150
+ sessionId: string;
151
+ cwd?: string;
152
+ repo?: string;
153
+ branch?: string;
154
+ pane?: string;
155
+ }, o?: RtClientOptions): Promise<RtResponse<{
156
+ unread: {
157
+ dms: number;
158
+ mentions: number;
159
+ rooms: number;
160
+ };
161
+ status: BuddyStatus;
162
+ }>>;
163
+ export declare function chatDm(a: {
164
+ from: string;
165
+ to: string;
166
+ body: string;
167
+ sessionId?: string;
168
+ }, o?: RtClientOptions): Promise<RtResponse<{
169
+ room: string;
170
+ id: number;
171
+ recipients: string[];
172
+ }>>;
112
173
  export declare function eventsHead(o?: RtClientOptions): Promise<RtResponse<{
113
174
  cursor: number;
114
175
  }>>;
@@ -40,6 +40,31 @@ export interface MrByBranchData {
40
40
  byBranch: Record<string, MrByBranchEntry | null>;
41
41
  syncedAt: number;
42
42
  }
43
+ /**
44
+ * Trimmed, structural view of the daemon's `CacheEntry` (lib/state/branch-cache.ts) --
45
+ * rt-client cannot import daemon/lib internals, so this names only the fields
46
+ * console's run-view rows read, spelled exactly as they land on the wire
47
+ * (`mr` is `toMRInfo(pr)`, i.e. `getMRDashboardProps` -- camelCase `webUrl`,
48
+ * nested `pipeline.status`, no `ciStatus`). Extra wire fields (including the
49
+ * rest of `pipeline`) are fine; anything this shape doesn't name is simply
50
+ * not surfaced.
51
+ */
52
+ export interface BranchEnrichment {
53
+ ticket: {
54
+ identifier: string;
55
+ title: string;
56
+ url: string;
57
+ } | null;
58
+ mr: {
59
+ iid: number;
60
+ webUrl: string | null;
61
+ state: string;
62
+ pipeline: {
63
+ status: string;
64
+ } | null;
65
+ } | null;
66
+ fetchedAt: number;
67
+ }
43
68
  /** Forges the daemon can hold a token for. */
44
69
  export type ForgeSlug = "gitlab" | "github";
45
70
  export interface ForgeTokenData {
@@ -71,6 +96,8 @@ export interface ChatMember {
71
96
  armedAt?: number;
72
97
  cwd?: string;
73
98
  pane?: string;
99
+ /** Presence-joined by chat:who's handler — the only place this type is ever returned, and it always attaches one. */
100
+ status: BuddyStatus;
74
101
  }
75
102
  export interface ChatMessage {
76
103
  id: number;
@@ -87,10 +114,39 @@ export interface RoomSummary {
87
114
  unread: number;
88
115
  mentions: number;
89
116
  lastPostedAt?: number;
117
+ /** Set only by chat:rooms's left join against chat_dms. */
118
+ kind?: "dm";
119
+ participants?: {
120
+ a: string;
121
+ b: string;
122
+ };
123
+ /** Set only by chat:rooms's left join against chat_room_defaults; undefined for a room never stamped a default (every DM room included). */
124
+ defaultWake?: WakeMode;
125
+ }
126
+ /**
127
+ * Duplicated shape on purpose, same reasoning as ChatMember/ChatMessage
128
+ * above: mirrors lib/state/presence-store.ts's types, which rt-client
129
+ * cannot import.
130
+ */
131
+ export type BuddyStatus = "live" | "idle" | "deaf" | "offline";
132
+ export interface PresenceRow {
133
+ sessionId: string;
134
+ handle: string;
135
+ baseHandle: string;
136
+ cwd?: string;
137
+ repo?: string;
138
+ branch?: string;
139
+ pane?: string;
140
+ statusText?: string;
141
+ signedInAt: number;
142
+ lastSeenAt: number;
143
+ tailSeenAt?: number;
144
+ armedAt?: number;
145
+ signedOutAt?: number;
90
146
  }
91
147
  export type Attention = {
92
148
  needs: boolean;
93
- reason: "failed" | "stale" | "stranded" | null;
149
+ reason: "failed" | "stale" | "stranded" | "blocked" | null;
94
150
  evidence: string;
95
151
  };
96
152
  export interface RunSummary {
@@ -115,6 +171,21 @@ export interface RunSummary {
115
171
  the run has not produced that field yet. */
116
172
  ticket: string | null;
117
173
  branch: string | null;
174
+ /** The herdr agent attributed to this run (matched by recorded claude
175
+ session, else by worktree), mirrored live from `herdr agent list`.
176
+ Null when no agent matches or herdr is unavailable; absent on
177
+ pre-mirror daemons. */
178
+ agent?: RunAgent | null;
179
+ /** Executed stages only, in run order — the pipeline may define more that have not started. */
180
+ stages?: {
181
+ name: string;
182
+ status: string;
183
+ started_at: number | null;
184
+ }[];
185
+ }
186
+ export interface RunAgent {
187
+ status: "working" | "idle" | "blocked" | "done" | "unknown";
188
+ pane: string;
118
189
  }
119
190
  export interface RunStageRow {
120
191
  name: string;
@@ -313,6 +384,7 @@ export interface Commands {
313
384
  room: string;
314
385
  handle: string;
315
386
  body: string;
387
+ mentions?: string[];
316
388
  };
317
389
  data: {
318
390
  id: number;
@@ -370,18 +442,21 @@ export interface Commands {
370
442
  payload: {
371
443
  handle: string;
372
444
  room?: string;
445
+ sessionId?: string;
373
446
  };
374
447
  data: Record<string, never>;
375
448
  };
376
449
  "chat:touch": {
377
450
  payload: {
378
451
  handle: string;
452
+ sessionId?: string;
379
453
  };
380
454
  data: Record<string, never>;
381
455
  };
382
456
  "chat:disarm": {
383
457
  payload: {
384
458
  handle: string;
459
+ sessionId?: string;
385
460
  };
386
461
  data: Record<string, never>;
387
462
  };
@@ -399,6 +474,79 @@ export interface Commands {
399
474
  }[];
400
475
  };
401
476
  };
477
+ "chat:sign-in": {
478
+ payload: {
479
+ sessionId: string;
480
+ baseHandle: string;
481
+ cwd?: string;
482
+ repo?: string;
483
+ branch?: string;
484
+ pane?: string;
485
+ statusText?: string;
486
+ };
487
+ data: {
488
+ handle: string;
489
+ reclaimed: boolean;
490
+ };
491
+ };
492
+ "chat:sign-out": {
493
+ payload: {
494
+ sessionId: string;
495
+ };
496
+ data: Record<string, never>;
497
+ };
498
+ "chat:away": {
499
+ payload: {
500
+ sessionId: string;
501
+ text: string;
502
+ };
503
+ data: Record<string, never>;
504
+ };
505
+ "chat:back": {
506
+ payload: {
507
+ sessionId: string;
508
+ };
509
+ data: Record<string, never>;
510
+ };
511
+ "chat:buddies": {
512
+ payload: Record<string, never>;
513
+ data: {
514
+ buddies: Array<PresenceRow & {
515
+ status: BuddyStatus;
516
+ }>;
517
+ };
518
+ };
519
+ /** `unread`'s three fields are disjoint and sum to the true total: `dms` is DM-room waking count; `mentions` is non-DM waking mentions; `rooms` is non-DM waking count minus those mentions (never negative). */
520
+ "chat:pulse": {
521
+ payload: {
522
+ sessionId: string;
523
+ cwd?: string;
524
+ repo?: string;
525
+ branch?: string;
526
+ pane?: string;
527
+ };
528
+ data: {
529
+ unread: {
530
+ dms: number;
531
+ mentions: number;
532
+ rooms: number;
533
+ };
534
+ status: BuddyStatus;
535
+ };
536
+ };
537
+ "chat:dm": {
538
+ payload: {
539
+ from: string;
540
+ to: string;
541
+ body: string;
542
+ sessionId?: string;
543
+ };
544
+ data: {
545
+ room: string;
546
+ id: number;
547
+ recipients: string[];
548
+ };
549
+ };
402
550
  }
403
551
  export type CommandName = keyof Commands;
404
552
  export declare const COMMAND_NAMES: readonly CommandName[];
@@ -0,0 +1,11 @@
1
+ import type { RtClientOptions } from "./transport.ts";
2
+ /**
3
+ * A daemon-down result is a successful probe, not a failure of this call —
4
+ * eventsHead already never throws (transport.ts degrades every fetch to
5
+ * `{ ok: false, error }`), so this only reshapes that envelope for callers
6
+ * who want a boolean, not `{ ok, data, error }`.
7
+ */
8
+ export declare function daemonHealth(opts?: RtClientOptions): Promise<{
9
+ reachable: boolean;
10
+ error?: string;
11
+ }>;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  export { rtCommand, DEFAULT_SOCK } from "./transport.ts";
2
2
  export type { RtResponse, RtClientOptions } from "./transport.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";
3
+ export { readProjectMRs, readDiscussions, readMrsByBranch, readBranchCache, resolveForgeToken, listRuns, getRun, abandonRun, chatJoin, chatLeave, chatPost, chatRead, chatRooms, chatWho, chatMark, chatMessages, chatArm, chatTouch, chatDisarm, chatUnreadWaking, chatSignIn, chatSignOut, chatAway, chatBack, chatBuddies, chatPulse, chatDm, 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, Attention, RunSummary, RunStageRow, RunFieldRow, RunDecisionRow, RunDetail, WakeMode, ChatMember, ChatMessage, RoomSummary, } from "./commands.ts";
6
- export { subscribe, DEFAULT_WS_URL } from "./relay.ts";
5
+ export type { Discussion, DemandDecl, ProjectMRsScope, ProjectMRsData, DiscussionsData, MrByBranchEntry, MrByBranchData, BranchEnrichment, Commands, CommandName, ForgeSlug, ForgeTokenData, Attention, RunSummary, RunStageRow, RunFieldRow, RunDecisionRow, RunDetail, WakeMode, ChatMember, ChatMessage, RoomSummary, BuddyStatus, PresenceRow, } from "./commands.ts";
6
+ export { subscribe, createRelay, DEFAULT_WS_URL } from "./relay.ts";
7
7
  export type { RelayEventType } from "./relay.ts";
8
+ export { daemonHealth } from "./health.ts";
8
9
  export { repoNameForPath } from "./repos.ts";
9
10
  export { getSetting, listSettings, explainSetting, expandVariables, SCOPE_ORDER } from "./settings/resolve.ts";
10
11
  export type { Scope, Provenance, ResolveOpts, Resolved, InvalidScope, ListedSetting, ExplainRow, ExpandCtx, } from "./settings/resolve.ts";
package/dist/index.js CHANGED
@@ -39,6 +39,9 @@ function readDiscussions(repoName, iid, opts = {}) {
39
39
  function readMrsByBranch(repoName, branches, opts = {}) {
40
40
  return rtCommand("mr:by-branch", { repoName, branches }, { sockPath: opts.sockPath, timeoutMs: 60000 });
41
41
  }
42
+ function readBranchCache(branches, opts = {}) {
43
+ return rtCommand("cache:read", { branches }, { sockPath: opts.sockPath, timeoutMs: 1e4 });
44
+ }
42
45
  function resolveForgeToken(repoName, forge, opts = {}) {
43
46
  return rtCommand("secrets:forge-token", { repoName, forge }, { sockPath: opts.sockPath, timeoutMs: 1e4 });
44
47
  }
@@ -70,13 +73,16 @@ function chatJoin(a, o = {}) {
70
73
  payload.cwd = a.cwd;
71
74
  if (a.pane !== undefined)
72
75
  payload.pane = a.pane;
73
- return rtCommand("chat:join", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
76
+ return rtCommand("chat:join", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
74
77
  }
75
78
  function chatLeave(a, o = {}) {
76
- return rtCommand("chat:leave", { room: a.room, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 1e4 });
79
+ return rtCommand("chat:leave", { room: a.room, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
77
80
  }
78
81
  function chatPost(a, o = {}) {
79
- return rtCommand("chat:post", { room: a.room, handle: a.handle, body: a.body }, { sockPath: o.sockPath, timeoutMs: 1e4 });
82
+ const payload = { room: a.room, handle: a.handle, body: a.body };
83
+ if (a.mentions !== undefined)
84
+ payload.mentions = a.mentions;
85
+ return rtCommand("chat:post", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
80
86
  }
81
87
  function chatRead(a, o = {}) {
82
88
  const payload = { handle: a.handle };
@@ -86,19 +92,19 @@ function chatRead(a, o = {}) {
86
92
  payload.limit = a.limit;
87
93
  if (a.sinceMs !== undefined)
88
94
  payload.sinceMs = a.sinceMs;
89
- return rtCommand("chat:read", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
95
+ return rtCommand("chat:read", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
90
96
  }
91
97
  function chatRooms(a, o = {}) {
92
- return rtCommand("chat:rooms", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 1e4 });
98
+ return rtCommand("chat:rooms", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
93
99
  }
94
100
  function chatWho(a, o = {}) {
95
- return rtCommand("chat:who", { room: a.room }, { sockPath: o.sockPath, timeoutMs: 1e4 });
101
+ return rtCommand("chat:who", { room: a.room }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
96
102
  }
97
103
  function chatMark(a, o = {}) {
98
104
  const payload = { handle: a.handle };
99
105
  if (a.room !== undefined)
100
106
  payload.room = a.room;
101
- return rtCommand("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
107
+ return rtCommand("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
102
108
  }
103
109
  function chatMessages(a, o = {}) {
104
110
  const payload = { room: a.room };
@@ -106,28 +112,80 @@ function chatMessages(a, o = {}) {
106
112
  payload.before = a.before;
107
113
  if (a.limit !== undefined)
108
114
  payload.limit = a.limit;
109
- return rtCommand("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
115
+ return rtCommand("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
110
116
  }
111
117
  function chatArm(a, o = {}) {
112
118
  const payload = { handle: a.handle };
113
119
  if (a.room !== undefined)
114
120
  payload.room = a.room;
115
- return rtCommand("chat:arm", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
121
+ if (a.sessionId !== undefined)
122
+ payload.sessionId = a.sessionId;
123
+ return rtCommand("chat:arm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
116
124
  }
117
125
  function chatTouch(a, o = {}) {
118
- return rtCommand("chat:touch", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 1e4 });
126
+ const payload = { handle: a.handle };
127
+ if (a.sessionId !== undefined)
128
+ payload.sessionId = a.sessionId;
129
+ return rtCommand("chat:touch", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
119
130
  }
120
131
  function chatDisarm(a, o = {}) {
121
- return rtCommand("chat:disarm", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 1e4 });
132
+ const payload = { handle: a.handle };
133
+ if (a.sessionId !== undefined)
134
+ payload.sessionId = a.sessionId;
135
+ return rtCommand("chat:disarm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
122
136
  }
123
137
  function chatUnreadWaking(a, o = {}) {
124
138
  const payload = { handle: a.handle };
125
139
  if (a.room !== undefined)
126
140
  payload.room = a.room;
127
- return rtCommand("chat:unread-waking", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
141
+ return rtCommand("chat:unread-waking", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
142
+ }
143
+ function chatSignIn(a, o = {}) {
144
+ const payload = { sessionId: a.sessionId, baseHandle: a.baseHandle };
145
+ if (a.cwd !== undefined)
146
+ payload.cwd = a.cwd;
147
+ if (a.repo !== undefined)
148
+ payload.repo = a.repo;
149
+ if (a.branch !== undefined)
150
+ payload.branch = a.branch;
151
+ if (a.pane !== undefined)
152
+ payload.pane = a.pane;
153
+ if (a.statusText !== undefined)
154
+ payload.statusText = a.statusText;
155
+ return rtCommand("chat:sign-in", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
156
+ }
157
+ function chatSignOut(a, o = {}) {
158
+ return rtCommand("chat:sign-out", { sessionId: a.sessionId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
159
+ }
160
+ function chatAway(a, o = {}) {
161
+ return rtCommand("chat:away", { sessionId: a.sessionId, text: a.text }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
162
+ }
163
+ function chatBack(a, o = {}) {
164
+ return rtCommand("chat:back", { sessionId: a.sessionId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
165
+ }
166
+ function chatBuddies(o = {}) {
167
+ return rtCommand("chat:buddies", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
168
+ }
169
+ function chatPulse(a, o = {}) {
170
+ const payload = { sessionId: a.sessionId };
171
+ if (a.cwd !== undefined)
172
+ payload.cwd = a.cwd;
173
+ if (a.repo !== undefined)
174
+ payload.repo = a.repo;
175
+ if (a.branch !== undefined)
176
+ payload.branch = a.branch;
177
+ if (a.pane !== undefined)
178
+ payload.pane = a.pane;
179
+ return rtCommand("chat:pulse", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
180
+ }
181
+ function chatDm(a, o = {}) {
182
+ const payload = { from: a.from, to: a.to, body: a.body };
183
+ if (a.sessionId !== undefined)
184
+ payload.sessionId = a.sessionId;
185
+ return rtCommand("chat:dm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
128
186
  }
129
187
  function eventsHead(o = {}) {
130
- return rtCommand("events:head", {}, { sockPath: o.sockPath, timeoutMs: 1e4 });
188
+ return rtCommand("events:head", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
131
189
  }
132
190
  // src/commands.ts
133
191
  var COMMAND_NAMES = [
@@ -154,7 +212,14 @@ var COMMAND_NAMES = [
154
212
  "chat:arm",
155
213
  "chat:touch",
156
214
  "chat:disarm",
157
- "chat:unread-waking"
215
+ "chat:unread-waking",
216
+ "chat:sign-in",
217
+ "chat:sign-out",
218
+ "chat:away",
219
+ "chat:back",
220
+ "chat:buddies",
221
+ "chat:pulse",
222
+ "chat:dm"
158
223
  ];
159
224
  // src/relay.ts
160
225
  var DEFAULT_WS_URL = "ws://127.0.0.1:9401/ws";
@@ -199,6 +264,25 @@ function subscribe(onEvent, opts = {}) {
199
264
  } catch {}
200
265
  };
201
266
  }
267
+ function createRelay(cfg, opts = {}) {
268
+ const doSubscribe = opts.subscribeImpl ?? subscribe;
269
+ return doSubscribe((type, data) => {
270
+ if (type !== "event")
271
+ return;
272
+ const frame = data;
273
+ if (typeof frame?.topic !== "string" || !cfg.match(frame.topic))
274
+ return;
275
+ const payload = JSON.stringify(data);
276
+ try {
277
+ cfg.publish(cfg.topic, payload);
278
+ } catch {}
279
+ }, opts);
280
+ }
281
+ // src/health.ts
282
+ async function daemonHealth(opts = {}) {
283
+ const res = await eventsHead(opts);
284
+ return { reachable: res.ok, error: res.ok ? undefined : res.error };
285
+ }
202
286
  // src/repos.ts
203
287
  import { existsSync, readFileSync } from "fs";
204
288
  import { homedir as homedir2 } from "os";
@@ -506,6 +590,13 @@ var REGISTRY = [
506
590
  merge: "replace",
507
591
  description: "Absolute path to the installed mattstack.app bundle, written by the app at launch so rt stops hardcoding ~/Applications."
508
592
  },
593
+ {
594
+ key: "mattstack.mode",
595
+ type: "string",
596
+ scopes: ["machine"],
597
+ merge: "replace",
598
+ description: 'The machine\'s intended flavor, "dev" or "prod". Normally written by `rt settings dev-mode` after a successful handoff; a manual `rt settings set` is the blessed repair escape hatch — the daemon park loop converges on whatever this says. Unset ⇒ derived from the dev wrapper\'s presence.'
599
+ },
509
600
  {
510
601
  key: "rt.integrations",
511
602
  type: "object",
@@ -1395,7 +1486,7 @@ async function runCapture(argv, opts = {}) {
1395
1486
  }
1396
1487
  }
1397
1488
 
1398
- // src/settings/identity.ts
1489
+ // src/settings/identity-codec.ts
1399
1490
  var URL_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/(?:[^@/]+@)?([^/]+)\/(.+)$/;
1400
1491
  var SCP_RE = /^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/;
1401
1492
  function serializeIdentity(id) {
@@ -1443,6 +1534,8 @@ function normalizeRemote(remote) {
1443
1534
  return null;
1444
1535
  return `${host.toLowerCase()}/${normalizedPath}`;
1445
1536
  }
1537
+
1538
+ // src/settings/identity.ts
1446
1539
  function identityFromRemote(remote) {
1447
1540
  const store = readStore(machineSettingsPath());
1448
1541
  const overrides = store.global["rt.repoIdentityOverrides"];
@@ -1522,6 +1615,7 @@ export {
1522
1615
  readProjectMRs,
1523
1616
  readMrsByBranch,
1524
1617
  readDiscussions,
1618
+ readBranchCache,
1525
1619
  parseIdentity,
1526
1620
  normalizeRemote,
1527
1621
  listTeams,
@@ -1536,18 +1630,27 @@ export {
1536
1630
  expandVariables,
1537
1631
  eventsHead,
1538
1632
  deriveRepoIdentity,
1633
+ daemonHealth,
1634
+ createRelay,
1539
1635
  clearIdentityMemo,
1540
1636
  chatWho,
1541
1637
  chatUnreadWaking,
1542
1638
  chatTouch,
1639
+ chatSignOut,
1640
+ chatSignIn,
1543
1641
  chatRooms,
1544
1642
  chatRead,
1643
+ chatPulse,
1545
1644
  chatPost,
1546
1645
  chatMessages,
1547
1646
  chatMark,
1548
1647
  chatLeave,
1549
1648
  chatJoin,
1649
+ chatDm,
1550
1650
  chatDisarm,
1651
+ chatBuddies,
1652
+ chatBack,
1653
+ chatAway,
1551
1654
  chatArm,
1552
1655
  allDefs,
1553
1656
  abandonRun,
package/dist/relay.d.ts CHANGED
@@ -13,3 +13,18 @@ export declare const DEFAULT_WS_URL = "ws://127.0.0.1:9401/ws";
13
13
  * the daemon being down just means silence, never a crash.
14
14
  */
15
15
  export declare function subscribe(onEvent: (type: RelayEventType, data: unknown) => void, opts?: RtClientOptions): () => void;
16
+ /**
17
+ * One daemon subscription for the whole process, republished onto a
18
+ * caller-chosen pub/sub topic. Every subscriber to that topic then shares
19
+ * one relay connection instead of each opening its own — filtering here
20
+ * (rather than at each subscriber) is what keeps an unrelated event from
21
+ * making every subscriber re-render.
22
+ *
23
+ * Ported from console's `startRelay` (src/server/ws.ts) with the match
24
+ * predicate and target topic lifted to arguments.
25
+ */
26
+ export declare function createRelay(cfg: {
27
+ match: (topic: string) => boolean;
28
+ topic: string;
29
+ publish: (topic: string, data: string) => void;
30
+ }, opts?: RtClientOptions): () => void;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The pure half of the repo-identity contract: the wire codec and the
3
+ * remote-URL normalizer. Split from identity.ts so browser bundles can key
4
+ * and label repos without dragging in fs/child_process — this module must
5
+ * never import node builtins or anything that does (the `./identity`
6
+ * subpath export points here, and a browser consumer evaluates it at module
7
+ * scope). Override-aware and derivation entry points stay in identity.ts.
8
+ */
9
+ export type RepoIdentity = {
10
+ kind: "remote";
11
+ id: string;
12
+ } | {
13
+ kind: "path";
14
+ id: string;
15
+ };
16
+ /**
17
+ * The wire form crosses the daemon socket, sits in board config, and lands in
18
+ * console's `/runs/:repo/...` URL — all of which need one slash-free segment.
19
+ * `encodeURIComponent` guarantees that and is exactly reversible.
20
+ */
21
+ export declare function serializeIdentity(id: RepoIdentity): string;
22
+ export declare function parseIdentity(wire: string): RepoIdentity | null;
23
+ /**
24
+ * Pure normalization: `remote` → `host/path` (lowercase host, `.git` and
25
+ * embedded credentials stripped) or null when the remote doesn't match a
26
+ * recognized host form (local paths, garbage input).
27
+ */
28
+ export declare function normalizeRemote(remote: string): string | null;
@@ -0,0 +1,53 @@
1
+ // src/settings/identity-codec.ts
2
+ var URL_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/(?:[^@/]+@)?([^/]+)\/(.+)$/;
3
+ var SCP_RE = /^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/;
4
+ function serializeIdentity(id) {
5
+ return `${id.kind}:${encodeURIComponent(id.id)}`;
6
+ }
7
+ function parseIdentity(wire) {
8
+ const colon = wire.indexOf(":");
9
+ if (colon === -1)
10
+ return null;
11
+ const kind = wire.slice(0, colon);
12
+ if (kind !== "remote" && kind !== "path")
13
+ return null;
14
+ const encoded = wire.slice(colon + 1);
15
+ let id;
16
+ try {
17
+ id = decodeURIComponent(encoded);
18
+ } catch {
19
+ return null;
20
+ }
21
+ if (encodeURIComponent(id) !== encoded)
22
+ return null;
23
+ return { kind, id };
24
+ }
25
+ function normalizeRemote(remote) {
26
+ const trimmed = remote.trim();
27
+ if (!trimmed)
28
+ return null;
29
+ let host;
30
+ let path;
31
+ const urlMatch = URL_RE.exec(trimmed);
32
+ if (urlMatch) {
33
+ host = urlMatch[1];
34
+ path = urlMatch[2];
35
+ } else if (!trimmed.startsWith("/") && !trimmed.startsWith("~")) {
36
+ const scpMatch = SCP_RE.exec(trimmed);
37
+ if (scpMatch) {
38
+ host = scpMatch[1];
39
+ path = scpMatch[2];
40
+ }
41
+ }
42
+ if (!host || !path)
43
+ return null;
44
+ const normalizedPath = path.replace(/\.git$/, "").replace(/^\/+/, "").replace(/\/+$/, "");
45
+ if (!normalizedPath)
46
+ return null;
47
+ return `${host.toLowerCase()}/${normalizedPath}`;
48
+ }
49
+ export {
50
+ serializeIdentity,
51
+ parseIdentity,
52
+ normalizeRemote
53
+ };