@mattstack/rt-client 0.5.0 → 0.6.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/dist/client.d.ts +56 -1
- package/dist/commands.d.ts +109 -0
- package/dist/health.d.ts +11 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +111 -14
- package/dist/relay.d.ts +15 -0
- package/dist/transport.d.ts +9 -0
- package/package.json +1 -1
- package/src/client.ts +93 -17
- package/src/commands.ts +59 -5
- package/src/health.ts +15 -0
- package/src/index.ts +12 -1
- package/src/relay.ts +31 -0
- package/src/settings/registry-defs.ts +7 -0
- package/src/transport.ts +9 -0
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, BranchEnrichment, 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
|
|
@@ -58,6 +58,7 @@ export declare function chatPost(a: {
|
|
|
58
58
|
room: string;
|
|
59
59
|
handle: string;
|
|
60
60
|
body: string;
|
|
61
|
+
mentions?: string[];
|
|
61
62
|
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
62
63
|
id: number;
|
|
63
64
|
recipients: string[];
|
|
@@ -97,12 +98,15 @@ export declare function chatMessages(a: {
|
|
|
97
98
|
export declare function chatArm(a: {
|
|
98
99
|
handle: string;
|
|
99
100
|
room?: string;
|
|
101
|
+
sessionId?: string;
|
|
100
102
|
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
101
103
|
export declare function chatTouch(a: {
|
|
102
104
|
handle: string;
|
|
105
|
+
sessionId?: string;
|
|
103
106
|
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
104
107
|
export declare function chatDisarm(a: {
|
|
105
108
|
handle: string;
|
|
109
|
+
sessionId?: string;
|
|
106
110
|
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
107
111
|
export declare function chatUnreadWaking(a: {
|
|
108
112
|
handle: string;
|
|
@@ -115,6 +119,57 @@ export declare function chatUnreadWaking(a: {
|
|
|
115
119
|
maxId: number;
|
|
116
120
|
}[];
|
|
117
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
|
+
}>>;
|
|
118
173
|
export declare function eventsHead(o?: RtClientOptions): Promise<RtResponse<{
|
|
119
174
|
cursor: number;
|
|
120
175
|
}>>;
|
package/dist/commands.d.ts
CHANGED
|
@@ -103,6 +103,8 @@ export interface ChatMember {
|
|
|
103
103
|
armedAt?: number;
|
|
104
104
|
cwd?: string;
|
|
105
105
|
pane?: string;
|
|
106
|
+
/** Presence-joined by chat:who's handler — the only place this type is ever returned, and it always attaches one. */
|
|
107
|
+
status: BuddyStatus;
|
|
106
108
|
}
|
|
107
109
|
export interface ChatMessage {
|
|
108
110
|
id: number;
|
|
@@ -119,6 +121,35 @@ export interface RoomSummary {
|
|
|
119
121
|
unread: number;
|
|
120
122
|
mentions: number;
|
|
121
123
|
lastPostedAt?: number;
|
|
124
|
+
/** Set only by chat:rooms's left join against chat_dms. */
|
|
125
|
+
kind?: "dm";
|
|
126
|
+
participants?: {
|
|
127
|
+
a: string;
|
|
128
|
+
b: string;
|
|
129
|
+
};
|
|
130
|
+
/** Set only by chat:rooms's left join against chat_room_defaults; undefined for a room never stamped a default (every DM room included). */
|
|
131
|
+
defaultWake?: WakeMode;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Duplicated shape on purpose, same reasoning as ChatMember/ChatMessage
|
|
135
|
+
* above: mirrors lib/state/presence-store.ts's types, which rt-client
|
|
136
|
+
* cannot import.
|
|
137
|
+
*/
|
|
138
|
+
export type BuddyStatus = "live" | "idle" | "deaf" | "offline";
|
|
139
|
+
export interface PresenceRow {
|
|
140
|
+
sessionId: string;
|
|
141
|
+
handle: string;
|
|
142
|
+
baseHandle: string;
|
|
143
|
+
cwd?: string;
|
|
144
|
+
repo?: string;
|
|
145
|
+
branch?: string;
|
|
146
|
+
pane?: string;
|
|
147
|
+
statusText?: string;
|
|
148
|
+
signedInAt: number;
|
|
149
|
+
lastSeenAt: number;
|
|
150
|
+
tailSeenAt?: number;
|
|
151
|
+
armedAt?: number;
|
|
152
|
+
signedOutAt?: number;
|
|
122
153
|
}
|
|
123
154
|
export type Attention = {
|
|
124
155
|
needs: boolean;
|
|
@@ -156,6 +187,7 @@ export interface RunSummary {
|
|
|
156
187
|
stages?: {
|
|
157
188
|
name: string;
|
|
158
189
|
status: string;
|
|
190
|
+
started_at: number | null;
|
|
159
191
|
}[];
|
|
160
192
|
}
|
|
161
193
|
export interface RunAgent {
|
|
@@ -359,6 +391,7 @@ export interface Commands {
|
|
|
359
391
|
room: string;
|
|
360
392
|
handle: string;
|
|
361
393
|
body: string;
|
|
394
|
+
mentions?: string[];
|
|
362
395
|
};
|
|
363
396
|
data: {
|
|
364
397
|
id: number;
|
|
@@ -416,18 +449,21 @@ export interface Commands {
|
|
|
416
449
|
payload: {
|
|
417
450
|
handle: string;
|
|
418
451
|
room?: string;
|
|
452
|
+
sessionId?: string;
|
|
419
453
|
};
|
|
420
454
|
data: Record<string, never>;
|
|
421
455
|
};
|
|
422
456
|
"chat:touch": {
|
|
423
457
|
payload: {
|
|
424
458
|
handle: string;
|
|
459
|
+
sessionId?: string;
|
|
425
460
|
};
|
|
426
461
|
data: Record<string, never>;
|
|
427
462
|
};
|
|
428
463
|
"chat:disarm": {
|
|
429
464
|
payload: {
|
|
430
465
|
handle: string;
|
|
466
|
+
sessionId?: string;
|
|
431
467
|
};
|
|
432
468
|
data: Record<string, never>;
|
|
433
469
|
};
|
|
@@ -445,6 +481,79 @@ export interface Commands {
|
|
|
445
481
|
}[];
|
|
446
482
|
};
|
|
447
483
|
};
|
|
484
|
+
"chat:sign-in": {
|
|
485
|
+
payload: {
|
|
486
|
+
sessionId: string;
|
|
487
|
+
baseHandle: string;
|
|
488
|
+
cwd?: string;
|
|
489
|
+
repo?: string;
|
|
490
|
+
branch?: string;
|
|
491
|
+
pane?: string;
|
|
492
|
+
statusText?: string;
|
|
493
|
+
};
|
|
494
|
+
data: {
|
|
495
|
+
handle: string;
|
|
496
|
+
reclaimed: boolean;
|
|
497
|
+
};
|
|
498
|
+
};
|
|
499
|
+
"chat:sign-out": {
|
|
500
|
+
payload: {
|
|
501
|
+
sessionId: string;
|
|
502
|
+
};
|
|
503
|
+
data: Record<string, never>;
|
|
504
|
+
};
|
|
505
|
+
"chat:away": {
|
|
506
|
+
payload: {
|
|
507
|
+
sessionId: string;
|
|
508
|
+
text: string;
|
|
509
|
+
};
|
|
510
|
+
data: Record<string, never>;
|
|
511
|
+
};
|
|
512
|
+
"chat:back": {
|
|
513
|
+
payload: {
|
|
514
|
+
sessionId: string;
|
|
515
|
+
};
|
|
516
|
+
data: Record<string, never>;
|
|
517
|
+
};
|
|
518
|
+
"chat:buddies": {
|
|
519
|
+
payload: Record<string, never>;
|
|
520
|
+
data: {
|
|
521
|
+
buddies: Array<PresenceRow & {
|
|
522
|
+
status: BuddyStatus;
|
|
523
|
+
}>;
|
|
524
|
+
};
|
|
525
|
+
};
|
|
526
|
+
/** `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). */
|
|
527
|
+
"chat:pulse": {
|
|
528
|
+
payload: {
|
|
529
|
+
sessionId: string;
|
|
530
|
+
cwd?: string;
|
|
531
|
+
repo?: string;
|
|
532
|
+
branch?: string;
|
|
533
|
+
pane?: string;
|
|
534
|
+
};
|
|
535
|
+
data: {
|
|
536
|
+
unread: {
|
|
537
|
+
dms: number;
|
|
538
|
+
mentions: number;
|
|
539
|
+
rooms: number;
|
|
540
|
+
};
|
|
541
|
+
status: BuddyStatus;
|
|
542
|
+
};
|
|
543
|
+
};
|
|
544
|
+
"chat:dm": {
|
|
545
|
+
payload: {
|
|
546
|
+
from: string;
|
|
547
|
+
to: string;
|
|
548
|
+
body: string;
|
|
549
|
+
sessionId?: string;
|
|
550
|
+
};
|
|
551
|
+
data: {
|
|
552
|
+
room: string;
|
|
553
|
+
id: number;
|
|
554
|
+
recipients: string[];
|
|
555
|
+
};
|
|
556
|
+
};
|
|
448
557
|
}
|
|
449
558
|
export type CommandName = keyof Commands;
|
|
450
559
|
export declare const COMMAND_NAMES: readonly CommandName[];
|
package/dist/health.d.ts
ADDED
|
@@ -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, readBranchCache, 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, BranchEnrichment, 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
|
@@ -73,13 +73,16 @@ function chatJoin(a, o = {}) {
|
|
|
73
73
|
payload.cwd = a.cwd;
|
|
74
74
|
if (a.pane !== undefined)
|
|
75
75
|
payload.pane = a.pane;
|
|
76
|
-
return rtCommand("chat:join", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
76
|
+
return rtCommand("chat:join", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
77
77
|
}
|
|
78
78
|
function chatLeave(a, o = {}) {
|
|
79
|
-
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 });
|
|
80
80
|
}
|
|
81
81
|
function chatPost(a, o = {}) {
|
|
82
|
-
|
|
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 });
|
|
83
86
|
}
|
|
84
87
|
function chatRead(a, o = {}) {
|
|
85
88
|
const payload = { handle: a.handle };
|
|
@@ -89,19 +92,19 @@ function chatRead(a, o = {}) {
|
|
|
89
92
|
payload.limit = a.limit;
|
|
90
93
|
if (a.sinceMs !== undefined)
|
|
91
94
|
payload.sinceMs = a.sinceMs;
|
|
92
|
-
return rtCommand("chat:read", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
95
|
+
return rtCommand("chat:read", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
93
96
|
}
|
|
94
97
|
function chatRooms(a, o = {}) {
|
|
95
|
-
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 });
|
|
96
99
|
}
|
|
97
100
|
function chatWho(a, o = {}) {
|
|
98
|
-
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 });
|
|
99
102
|
}
|
|
100
103
|
function chatMark(a, o = {}) {
|
|
101
104
|
const payload = { handle: a.handle };
|
|
102
105
|
if (a.room !== undefined)
|
|
103
106
|
payload.room = a.room;
|
|
104
|
-
return rtCommand("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
107
|
+
return rtCommand("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
105
108
|
}
|
|
106
109
|
function chatMessages(a, o = {}) {
|
|
107
110
|
const payload = { room: a.room };
|
|
@@ -109,28 +112,80 @@ function chatMessages(a, o = {}) {
|
|
|
109
112
|
payload.before = a.before;
|
|
110
113
|
if (a.limit !== undefined)
|
|
111
114
|
payload.limit = a.limit;
|
|
112
|
-
return rtCommand("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
115
|
+
return rtCommand("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
113
116
|
}
|
|
114
117
|
function chatArm(a, o = {}) {
|
|
115
118
|
const payload = { handle: a.handle };
|
|
116
119
|
if (a.room !== undefined)
|
|
117
120
|
payload.room = a.room;
|
|
118
|
-
|
|
121
|
+
if (a.sessionId !== undefined)
|
|
122
|
+
payload.sessionId = a.sessionId;
|
|
123
|
+
return rtCommand("chat:arm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
119
124
|
}
|
|
120
125
|
function chatTouch(a, o = {}) {
|
|
121
|
-
|
|
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 });
|
|
122
130
|
}
|
|
123
131
|
function chatDisarm(a, o = {}) {
|
|
124
|
-
|
|
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 });
|
|
125
136
|
}
|
|
126
137
|
function chatUnreadWaking(a, o = {}) {
|
|
127
138
|
const payload = { handle: a.handle };
|
|
128
139
|
if (a.room !== undefined)
|
|
129
140
|
payload.room = a.room;
|
|
130
|
-
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 });
|
|
131
186
|
}
|
|
132
187
|
function eventsHead(o = {}) {
|
|
133
|
-
return rtCommand("events:head", {}, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
188
|
+
return rtCommand("events:head", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
134
189
|
}
|
|
135
190
|
// src/commands.ts
|
|
136
191
|
var COMMAND_NAMES = [
|
|
@@ -157,7 +212,14 @@ var COMMAND_NAMES = [
|
|
|
157
212
|
"chat:arm",
|
|
158
213
|
"chat:touch",
|
|
159
214
|
"chat:disarm",
|
|
160
|
-
"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"
|
|
161
223
|
];
|
|
162
224
|
// src/relay.ts
|
|
163
225
|
var DEFAULT_WS_URL = "ws://127.0.0.1:9401/ws";
|
|
@@ -202,6 +264,25 @@ function subscribe(onEvent, opts = {}) {
|
|
|
202
264
|
} catch {}
|
|
203
265
|
};
|
|
204
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
|
+
}
|
|
205
286
|
// src/repos.ts
|
|
206
287
|
import { existsSync, readFileSync } from "fs";
|
|
207
288
|
import { homedir as homedir2 } from "os";
|
|
@@ -509,6 +590,13 @@ var REGISTRY = [
|
|
|
509
590
|
merge: "replace",
|
|
510
591
|
description: "Absolute path to the installed mattstack.app bundle, written by the app at launch so rt stops hardcoding ~/Applications."
|
|
511
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
|
+
},
|
|
512
600
|
{
|
|
513
601
|
key: "rt.integrations",
|
|
514
602
|
type: "object",
|
|
@@ -1549,18 +1637,27 @@ export {
|
|
|
1549
1637
|
expandVariables,
|
|
1550
1638
|
eventsHead,
|
|
1551
1639
|
deriveRepoIdentity,
|
|
1640
|
+
daemonHealth,
|
|
1641
|
+
createRelay,
|
|
1552
1642
|
clearIdentityMemo,
|
|
1553
1643
|
chatWho,
|
|
1554
1644
|
chatUnreadWaking,
|
|
1555
1645
|
chatTouch,
|
|
1646
|
+
chatSignOut,
|
|
1647
|
+
chatSignIn,
|
|
1556
1648
|
chatRooms,
|
|
1557
1649
|
chatRead,
|
|
1650
|
+
chatPulse,
|
|
1558
1651
|
chatPost,
|
|
1559
1652
|
chatMessages,
|
|
1560
1653
|
chatMark,
|
|
1561
1654
|
chatLeave,
|
|
1562
1655
|
chatJoin,
|
|
1656
|
+
chatDm,
|
|
1563
1657
|
chatDisarm,
|
|
1658
|
+
chatBuddies,
|
|
1659
|
+
chatBack,
|
|
1660
|
+
chatAway,
|
|
1564
1661
|
chatArm,
|
|
1565
1662
|
allDefs,
|
|
1566
1663
|
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;
|
package/dist/transport.d.ts
CHANGED
|
@@ -6,6 +6,15 @@ export interface RtResponse<T = unknown> {
|
|
|
6
6
|
export interface RtClientOptions {
|
|
7
7
|
sockPath?: string;
|
|
8
8
|
wsUrl?: string;
|
|
9
|
+
/** Per-call override of rtCommand's own default (15s); chat's pulse wrapper needs an 800ms hook budget. */
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Test seam for createRelay (relay.ts): swaps the daemon subscription for
|
|
13
|
+
* a fake without a live WebSocket server. Typed structurally against
|
|
14
|
+
* relay.ts's `subscribe` rather than importing its RelayEventType, which
|
|
15
|
+
* would make this module depend on the one that already depends on it.
|
|
16
|
+
*/
|
|
17
|
+
subscribeImpl?: (onEvent: (type: string, data: unknown) => void, opts?: RtClientOptions) => () => void;
|
|
9
18
|
}
|
|
10
19
|
/**
|
|
11
20
|
* Display-only: a module-load snapshot for callers that just want to show
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -19,6 +19,8 @@ import type {
|
|
|
19
19
|
ChatMember,
|
|
20
20
|
ChatMessage,
|
|
21
21
|
RoomSummary,
|
|
22
|
+
BuddyStatus,
|
|
23
|
+
PresenceRow,
|
|
22
24
|
} from "./commands.ts";
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -151,21 +153,23 @@ export function chatJoin(
|
|
|
151
153
|
if (a.wakeOn !== undefined) payload.wakeOn = a.wakeOn;
|
|
152
154
|
if (a.cwd !== undefined) payload.cwd = a.cwd;
|
|
153
155
|
if (a.pane !== undefined) payload.pane = a.pane;
|
|
154
|
-
return rtCommand<{ handle: string; memberCount: number; unread: number }>("chat:join", payload, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
156
|
+
return rtCommand<{ handle: string; memberCount: number; unread: number }>("chat:join", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
export function chatLeave(
|
|
158
160
|
a: { room: string; handle: string },
|
|
159
161
|
o: RtClientOptions = {},
|
|
160
162
|
): Promise<RtResponse<Record<string, never>>> {
|
|
161
|
-
return rtCommand<Record<string, never>>("chat:leave", { room: a.room, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
163
|
+
return rtCommand<Record<string, never>>("chat:leave", { room: a.room, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
export function chatPost(
|
|
165
|
-
a: { room: string; handle: string; body: string },
|
|
167
|
+
a: { room: string; handle: string; body: string; mentions?: string[] },
|
|
166
168
|
o: RtClientOptions = {},
|
|
167
169
|
): Promise<RtResponse<{ id: number; recipients: string[] }>> {
|
|
168
|
-
|
|
170
|
+
const payload: Record<string, unknown> = { room: a.room, handle: a.handle, body: a.body };
|
|
171
|
+
if (a.mentions !== undefined) payload.mentions = a.mentions;
|
|
172
|
+
return rtCommand<{ id: number; recipients: string[] }>("chat:post", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
169
173
|
}
|
|
170
174
|
|
|
171
175
|
export function chatRead(
|
|
@@ -176,21 +180,21 @@ export function chatRead(
|
|
|
176
180
|
if (a.room !== undefined) payload.room = a.room;
|
|
177
181
|
if (a.limit !== undefined) payload.limit = a.limit;
|
|
178
182
|
if (a.sinceMs !== undefined) payload.sinceMs = a.sinceMs;
|
|
179
|
-
return rtCommand<{ rooms: { room: string; messages: ChatMessage[] }[] }>("chat:read", payload, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
183
|
+
return rtCommand<{ rooms: { room: string; messages: ChatMessage[] }[] }>("chat:read", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
export function chatRooms(
|
|
183
187
|
a: { handle: string },
|
|
184
188
|
o: RtClientOptions = {},
|
|
185
189
|
): Promise<RtResponse<{ rooms: RoomSummary[] }>> {
|
|
186
|
-
return rtCommand<{ rooms: RoomSummary[] }>("chat:rooms", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
190
|
+
return rtCommand<{ rooms: RoomSummary[] }>("chat:rooms", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
export function chatWho(
|
|
190
194
|
a: { room: string },
|
|
191
195
|
o: RtClientOptions = {},
|
|
192
196
|
): Promise<RtResponse<{ members: ChatMember[] }>> {
|
|
193
|
-
return rtCommand<{ members: ChatMember[] }>("chat:who", { room: a.room }, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
197
|
+
return rtCommand<{ members: ChatMember[] }>("chat:who", { room: a.room }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
export function chatMark(
|
|
@@ -199,7 +203,7 @@ export function chatMark(
|
|
|
199
203
|
): Promise<RtResponse<Record<string, never>>> {
|
|
200
204
|
const payload: Record<string, unknown> = { handle: a.handle };
|
|
201
205
|
if (a.room !== undefined) payload.room = a.room;
|
|
202
|
-
return rtCommand<Record<string, never>>("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
206
|
+
return rtCommand<Record<string, never>>("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
203
207
|
}
|
|
204
208
|
|
|
205
209
|
export function chatMessages(
|
|
@@ -209,30 +213,35 @@ export function chatMessages(
|
|
|
209
213
|
const payload: Record<string, unknown> = { room: a.room };
|
|
210
214
|
if (a.before !== undefined) payload.before = a.before;
|
|
211
215
|
if (a.limit !== undefined) payload.limit = a.limit;
|
|
212
|
-
return rtCommand<{ messages: ChatMessage[] }>("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
216
|
+
return rtCommand<{ messages: ChatMessage[] }>("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
213
217
|
}
|
|
214
218
|
|
|
215
219
|
export function chatArm(
|
|
216
|
-
a: { handle: string; room?: string },
|
|
220
|
+
a: { handle: string; room?: string; sessionId?: string },
|
|
217
221
|
o: RtClientOptions = {},
|
|
218
222
|
): Promise<RtResponse<Record<string, never>>> {
|
|
219
223
|
const payload: Record<string, unknown> = { handle: a.handle };
|
|
220
224
|
if (a.room !== undefined) payload.room = a.room;
|
|
221
|
-
|
|
225
|
+
if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
|
|
226
|
+
return rtCommand<Record<string, never>>("chat:arm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
export function chatTouch(
|
|
225
|
-
a: { handle: string },
|
|
230
|
+
a: { handle: string; sessionId?: string },
|
|
226
231
|
o: RtClientOptions = {},
|
|
227
232
|
): Promise<RtResponse<Record<string, never>>> {
|
|
228
|
-
|
|
233
|
+
const payload: Record<string, unknown> = { handle: a.handle };
|
|
234
|
+
if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
|
|
235
|
+
return rtCommand<Record<string, never>>("chat:touch", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
229
236
|
}
|
|
230
237
|
|
|
231
238
|
export function chatDisarm(
|
|
232
|
-
a: { handle: string },
|
|
239
|
+
a: { handle: string; sessionId?: string },
|
|
233
240
|
o: RtClientOptions = {},
|
|
234
241
|
): Promise<RtResponse<Record<string, never>>> {
|
|
235
|
-
|
|
242
|
+
const payload: Record<string, unknown> = { handle: a.handle };
|
|
243
|
+
if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
|
|
244
|
+
return rtCommand<Record<string, never>>("chat:disarm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
236
245
|
}
|
|
237
246
|
|
|
238
247
|
export function chatUnreadWaking(
|
|
@@ -241,9 +250,76 @@ export function chatUnreadWaking(
|
|
|
241
250
|
): Promise<RtResponse<{ rooms: { room: string; count: number; mentions: number; maxId: number }[] }>> {
|
|
242
251
|
const payload: Record<string, unknown> = { handle: a.handle };
|
|
243
252
|
if (a.room !== undefined) payload.room = a.room;
|
|
244
|
-
return rtCommand<{ rooms: { room: string; count: number; mentions: number; maxId: number }[] }>("chat:unread-waking", payload, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
253
|
+
return rtCommand<{ rooms: { room: string; count: number; mentions: number; maxId: number }[] }>("chat:unread-waking", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ─── Presence ──────────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
export function chatSignIn(
|
|
259
|
+
a: { sessionId: string; baseHandle: string; cwd?: string; repo?: string; branch?: string; pane?: string; statusText?: string },
|
|
260
|
+
o: RtClientOptions = {},
|
|
261
|
+
): Promise<RtResponse<{ handle: string; reclaimed: boolean }>> {
|
|
262
|
+
const payload: Record<string, unknown> = { sessionId: a.sessionId, baseHandle: a.baseHandle };
|
|
263
|
+
if (a.cwd !== undefined) payload.cwd = a.cwd;
|
|
264
|
+
if (a.repo !== undefined) payload.repo = a.repo;
|
|
265
|
+
if (a.branch !== undefined) payload.branch = a.branch;
|
|
266
|
+
if (a.pane !== undefined) payload.pane = a.pane;
|
|
267
|
+
if (a.statusText !== undefined) payload.statusText = a.statusText;
|
|
268
|
+
return rtCommand<{ handle: string; reclaimed: boolean }>("chat:sign-in", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function chatSignOut(
|
|
272
|
+
a: { sessionId: string },
|
|
273
|
+
o: RtClientOptions = {},
|
|
274
|
+
): Promise<RtResponse<Record<string, never>>> {
|
|
275
|
+
return rtCommand<Record<string, never>>("chat:sign-out", { sessionId: a.sessionId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function chatAway(
|
|
279
|
+
a: { sessionId: string; text: string },
|
|
280
|
+
o: RtClientOptions = {},
|
|
281
|
+
): Promise<RtResponse<Record<string, never>>> {
|
|
282
|
+
return rtCommand<Record<string, never>>("chat:away", { sessionId: a.sessionId, text: a.text }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function chatBack(
|
|
286
|
+
a: { sessionId: string },
|
|
287
|
+
o: RtClientOptions = {},
|
|
288
|
+
): Promise<RtResponse<Record<string, never>>> {
|
|
289
|
+
return rtCommand<Record<string, never>>("chat:back", { sessionId: a.sessionId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function chatBuddies(
|
|
293
|
+
o: RtClientOptions = {},
|
|
294
|
+
): Promise<RtResponse<{ buddies: Array<PresenceRow & { status: BuddyStatus }> }>> {
|
|
295
|
+
return rtCommand<{ buddies: Array<PresenceRow & { status: BuddyStatus }> }>("chat:buddies", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function chatPulse(
|
|
299
|
+
a: { sessionId: string; cwd?: string; repo?: string; branch?: string; pane?: string },
|
|
300
|
+
o: RtClientOptions = {},
|
|
301
|
+
): Promise<RtResponse<{ unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus }>> {
|
|
302
|
+
const payload: Record<string, unknown> = { sessionId: a.sessionId };
|
|
303
|
+
if (a.cwd !== undefined) payload.cwd = a.cwd;
|
|
304
|
+
if (a.repo !== undefined) payload.repo = a.repo;
|
|
305
|
+
if (a.branch !== undefined) payload.branch = a.branch;
|
|
306
|
+
if (a.pane !== undefined) payload.pane = a.pane;
|
|
307
|
+
return rtCommand<{ unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus }>(
|
|
308
|
+
"chat:pulse",
|
|
309
|
+
payload,
|
|
310
|
+
{ sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 },
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function chatDm(
|
|
315
|
+
a: { from: string; to: string; body: string; sessionId?: string },
|
|
316
|
+
o: RtClientOptions = {},
|
|
317
|
+
): Promise<RtResponse<{ room: string; id: number; recipients: string[] }>> {
|
|
318
|
+
const payload: Record<string, unknown> = { from: a.from, to: a.to, body: a.body };
|
|
319
|
+
if (a.sessionId !== undefined) payload.sessionId = a.sessionId;
|
|
320
|
+
return rtCommand<{ room: string; id: number; recipients: string[] }>("chat:dm", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
245
321
|
}
|
|
246
322
|
|
|
247
323
|
export function eventsHead(o: RtClientOptions = {}): Promise<RtResponse<{ cursor: number }>> {
|
|
248
|
-
return rtCommand<{ cursor: number }>("events:head", {}, { sockPath: o.sockPath, timeoutMs: 10_000 });
|
|
324
|
+
return rtCommand<{ cursor: number }>("events:head", {}, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
249
325
|
}
|
package/src/commands.ts
CHANGED
|
@@ -96,6 +96,8 @@ export interface ChatMember {
|
|
|
96
96
|
armedAt?: number;
|
|
97
97
|
cwd?: string;
|
|
98
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;
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
export interface ChatMessage {
|
|
@@ -114,6 +116,34 @@ export interface RoomSummary {
|
|
|
114
116
|
unread: number;
|
|
115
117
|
mentions: number;
|
|
116
118
|
lastPostedAt?: number;
|
|
119
|
+
/** Set only by chat:rooms's left join against chat_dms. */
|
|
120
|
+
kind?: "dm";
|
|
121
|
+
participants?: { a: string; b: string };
|
|
122
|
+
/** Set only by chat:rooms's left join against chat_room_defaults; undefined for a room never stamped a default (every DM room included). */
|
|
123
|
+
defaultWake?: WakeMode;
|
|
124
|
+
}
|
|
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
|
+
|
|
133
|
+
export interface PresenceRow {
|
|
134
|
+
sessionId: string;
|
|
135
|
+
handle: string;
|
|
136
|
+
baseHandle: string;
|
|
137
|
+
cwd?: string;
|
|
138
|
+
repo?: string;
|
|
139
|
+
branch?: string;
|
|
140
|
+
pane?: string;
|
|
141
|
+
statusText?: string;
|
|
142
|
+
signedInAt: number;
|
|
143
|
+
lastSeenAt: number;
|
|
144
|
+
tailSeenAt?: number;
|
|
145
|
+
armedAt?: number;
|
|
146
|
+
signedOutAt?: number;
|
|
117
147
|
}
|
|
118
148
|
|
|
119
149
|
// SKILLS-53: one judgment, computed once in rt, so the console and the tray
|
|
@@ -147,7 +177,7 @@ export interface RunSummary {
|
|
|
147
177
|
pre-mirror daemons. */
|
|
148
178
|
agent?: RunAgent | null;
|
|
149
179
|
/** Executed stages only, in run order — the pipeline may define more that have not started. */
|
|
150
|
-
stages?: { name: string; status: string }[];
|
|
180
|
+
stages?: { name: string; status: string; started_at: number | null }[];
|
|
151
181
|
}
|
|
152
182
|
export interface RunAgent {
|
|
153
183
|
status: "working" | "idle" | "blocked" | "done" | "unknown";
|
|
@@ -220,16 +250,33 @@ export interface Commands {
|
|
|
220
250
|
"runs:abandon": { payload: { runId: string; repo?: string; reason?: string }; data: { ok: boolean } };
|
|
221
251
|
"chat:join": { payload: { room: string; handle: string; wakeOn?: WakeMode; cwd?: string; pane?: string }; data: { handle: string; memberCount: number; unread: number } };
|
|
222
252
|
"chat:leave": { payload: { room: string; handle: string }; data: Record<string, never> };
|
|
223
|
-
"chat:post": { payload: { room: string; handle: string; body: string }; data: { id: number; recipients: string[] } };
|
|
253
|
+
"chat:post": { payload: { room: string; handle: string; body: string; mentions?: string[] }; data: { id: number; recipients: string[] } };
|
|
224
254
|
"chat:read": { payload: { handle: string; room?: string; limit?: number; sinceMs?: number }; data: { rooms: { room: string; messages: ChatMessage[] }[] } };
|
|
225
255
|
"chat:rooms": { payload: { handle: string }; data: { rooms: RoomSummary[] } };
|
|
226
256
|
"chat:who": { payload: { room: string }; data: { members: ChatMember[] } };
|
|
227
257
|
"chat:mark": { payload: { handle: string; room?: string }; data: Record<string, never> };
|
|
228
258
|
"chat:messages": { payload: { room: string; before?: number; limit?: number }; data: { messages: ChatMessage[] } };
|
|
229
|
-
"chat:arm": { payload: { handle: string; room?: string }; data: Record<string, never> };
|
|
230
|
-
"chat:touch": { payload: { handle: string }; data: Record<string, never> };
|
|
231
|
-
"chat:disarm": { payload: { handle: string }; data: Record<string, never> };
|
|
259
|
+
"chat:arm": { payload: { handle: string; room?: string; sessionId?: string }; data: Record<string, never> };
|
|
260
|
+
"chat:touch": { payload: { handle: string; sessionId?: string }; data: Record<string, never> };
|
|
261
|
+
"chat:disarm": { payload: { handle: string; sessionId?: string }; data: Record<string, never> };
|
|
232
262
|
"chat:unread-waking": { payload: { handle: string; room?: string }; data: { rooms: { room: string; count: number; mentions: number; maxId: number }[] } };
|
|
263
|
+
|
|
264
|
+
// A session id keys these to one signed-in handle, not a room-membership
|
|
265
|
+
// handle string.
|
|
266
|
+
"chat:sign-in": {
|
|
267
|
+
payload: { sessionId: string; baseHandle: string; cwd?: string; repo?: string; branch?: string; pane?: string; statusText?: string };
|
|
268
|
+
data: { handle: string; reclaimed: boolean };
|
|
269
|
+
};
|
|
270
|
+
"chat:sign-out": { payload: { sessionId: string }; data: Record<string, never> };
|
|
271
|
+
"chat:away": { payload: { sessionId: string; text: string }; data: Record<string, never> };
|
|
272
|
+
"chat:back": { payload: { sessionId: string }; data: Record<string, never> };
|
|
273
|
+
"chat:buddies": { payload: Record<string, never>; data: { buddies: Array<PresenceRow & { status: BuddyStatus }> } };
|
|
274
|
+
/** `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). */
|
|
275
|
+
"chat:pulse": {
|
|
276
|
+
payload: { sessionId: string; cwd?: string; repo?: string; branch?: string; pane?: string };
|
|
277
|
+
data: { unread: { dms: number; mentions: number; rooms: number }; status: BuddyStatus };
|
|
278
|
+
};
|
|
279
|
+
"chat:dm": { payload: { from: string; to: string; body: string; sessionId?: string }; data: { room: string; id: number; recipients: string[] } };
|
|
233
280
|
}
|
|
234
281
|
|
|
235
282
|
export type CommandName = keyof Commands;
|
|
@@ -259,4 +306,11 @@ export const COMMAND_NAMES: readonly CommandName[] = [
|
|
|
259
306
|
"chat:touch",
|
|
260
307
|
"chat:disarm",
|
|
261
308
|
"chat:unread-waking",
|
|
309
|
+
"chat:sign-in",
|
|
310
|
+
"chat:sign-out",
|
|
311
|
+
"chat:away",
|
|
312
|
+
"chat:back",
|
|
313
|
+
"chat:buddies",
|
|
314
|
+
"chat:pulse",
|
|
315
|
+
"chat:dm",
|
|
262
316
|
];
|
package/src/health.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { eventsHead } from "./client.ts";
|
|
2
|
+
import type { RtClientOptions } from "./transport.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A daemon-down result is a successful probe, not a failure of this call —
|
|
6
|
+
* eventsHead already never throws (transport.ts degrades every fetch to
|
|
7
|
+
* `{ ok: false, error }`), so this only reshapes that envelope for callers
|
|
8
|
+
* who want a boolean, not `{ ok, data, error }`.
|
|
9
|
+
*/
|
|
10
|
+
export async function daemonHealth(
|
|
11
|
+
opts: RtClientOptions = {},
|
|
12
|
+
): Promise<{ reachable: boolean; error?: string }> {
|
|
13
|
+
const res = await eventsHead(opts);
|
|
14
|
+
return { reachable: res.ok, error: res.ok ? undefined : res.error };
|
|
15
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,13 @@ export {
|
|
|
22
22
|
chatTouch,
|
|
23
23
|
chatDisarm,
|
|
24
24
|
chatUnreadWaking,
|
|
25
|
+
chatSignIn,
|
|
26
|
+
chatSignOut,
|
|
27
|
+
chatAway,
|
|
28
|
+
chatBack,
|
|
29
|
+
chatBuddies,
|
|
30
|
+
chatPulse,
|
|
31
|
+
chatDm,
|
|
25
32
|
eventsHead,
|
|
26
33
|
} from "./client.ts";
|
|
27
34
|
|
|
@@ -49,11 +56,15 @@ export type {
|
|
|
49
56
|
ChatMember,
|
|
50
57
|
ChatMessage,
|
|
51
58
|
RoomSummary,
|
|
59
|
+
BuddyStatus,
|
|
60
|
+
PresenceRow,
|
|
52
61
|
} from "./commands.ts";
|
|
53
62
|
|
|
54
|
-
export { subscribe, DEFAULT_WS_URL } from "./relay.ts";
|
|
63
|
+
export { subscribe, createRelay, DEFAULT_WS_URL } from "./relay.ts";
|
|
55
64
|
export type { RelayEventType } from "./relay.ts";
|
|
56
65
|
|
|
66
|
+
export { daemonHealth } from "./health.ts";
|
|
67
|
+
|
|
57
68
|
export { repoNameForPath } from "./repos.ts";
|
|
58
69
|
|
|
59
70
|
// ─── Settings (RT-50) ────────────────────────────────────────────────────────
|
package/src/relay.ts
CHANGED
|
@@ -59,3 +59,34 @@ export function subscribe(
|
|
|
59
59
|
try { ws?.close(); } catch { /* already closed */ }
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* One daemon subscription for the whole process, republished onto a
|
|
65
|
+
* caller-chosen pub/sub topic. Every subscriber to that topic then shares
|
|
66
|
+
* one relay connection instead of each opening its own — filtering here
|
|
67
|
+
* (rather than at each subscriber) is what keeps an unrelated event from
|
|
68
|
+
* making every subscriber re-render.
|
|
69
|
+
*
|
|
70
|
+
* Ported from console's `startRelay` (src/server/ws.ts) with the match
|
|
71
|
+
* predicate and target topic lifted to arguments.
|
|
72
|
+
*/
|
|
73
|
+
export function createRelay(
|
|
74
|
+
cfg: { match: (topic: string) => boolean; topic: string; publish: (topic: string, data: string) => void },
|
|
75
|
+
opts: RtClientOptions = {},
|
|
76
|
+
): () => void {
|
|
77
|
+
const doSubscribe = opts.subscribeImpl ?? subscribe;
|
|
78
|
+
return doSubscribe((type, data) => {
|
|
79
|
+
if (type !== "event") return;
|
|
80
|
+
const frame = data as { topic?: unknown };
|
|
81
|
+
if (typeof frame?.topic !== "string" || !cfg.match(frame.topic)) return;
|
|
82
|
+
// Serializing outside the catch keeps the suppression narrow: only a
|
|
83
|
+
// publish that rejects its own frame is expected here, and one
|
|
84
|
+
// subscriber's broken publish must not tear down the shared relay.
|
|
85
|
+
const payload = JSON.stringify(data);
|
|
86
|
+
try {
|
|
87
|
+
cfg.publish(cfg.topic, payload);
|
|
88
|
+
} catch {
|
|
89
|
+
/* the subscriber went away */
|
|
90
|
+
}
|
|
91
|
+
}, opts);
|
|
92
|
+
}
|
|
@@ -228,6 +228,13 @@ export const REGISTRY: readonly SettingDef[] = [
|
|
|
228
228
|
merge: "replace",
|
|
229
229
|
description: "Absolute path to the installed mattstack.app bundle, written by the app at launch so rt stops hardcoding ~/Applications.",
|
|
230
230
|
},
|
|
231
|
+
{
|
|
232
|
+
key: "mattstack.mode",
|
|
233
|
+
type: "string",
|
|
234
|
+
scopes: ["machine"],
|
|
235
|
+
merge: "replace",
|
|
236
|
+
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.",
|
|
237
|
+
},
|
|
231
238
|
{
|
|
232
239
|
key: "rt.integrations",
|
|
233
240
|
type: "object",
|
package/src/transport.ts
CHANGED
|
@@ -19,6 +19,15 @@ export interface RtResponse<T = unknown> {
|
|
|
19
19
|
export interface RtClientOptions {
|
|
20
20
|
sockPath?: string;
|
|
21
21
|
wsUrl?: string;
|
|
22
|
+
/** Per-call override of rtCommand's own default (15s); chat's pulse wrapper needs an 800ms hook budget. */
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Test seam for createRelay (relay.ts): swaps the daemon subscription for
|
|
26
|
+
* a fake without a live WebSocket server. Typed structurally against
|
|
27
|
+
* relay.ts's `subscribe` rather than importing its RelayEventType, which
|
|
28
|
+
* would make this module depend on the one that already depends on it.
|
|
29
|
+
*/
|
|
30
|
+
subscribeImpl?: (onEvent: (type: string, data: unknown) => void, opts?: RtClientOptions) => () => void;
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
// Duplicates the ~/.mattstack/rt layout: rt-client has no dependency on rt's
|