@mattstack/rt-client 0.14.0 → 0.15.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 +13 -4
- package/dist/commands.d.ts +168 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +81 -0
- package/package.json +2 -2
- package/src/client.ts +80 -3
- package/src/commands.ts +68 -1
- package/src/index.ts +15 -1
- package/src/settings/registry-defs.ts +17 -0
package/dist/client.d.ts
CHANGED
|
@@ -105,10 +105,7 @@ export declare function chatWho(a: {
|
|
|
105
105
|
}, o?: RtClientOptions): Promise<RtResponse<{
|
|
106
106
|
members: ChatMember[];
|
|
107
107
|
}>>;
|
|
108
|
-
export declare function chatMark(a:
|
|
109
|
-
handle: string;
|
|
110
|
-
room?: string;
|
|
111
|
-
}, o?: RtClientOptions): Promise<RtResponse<Record<string, never>>>;
|
|
108
|
+
export declare function chatMark(a: Commands["chat:mark"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["chat:mark"]["data"]>>;
|
|
112
109
|
export declare function chatMessages(a: {
|
|
113
110
|
room: string;
|
|
114
111
|
before?: number;
|
|
@@ -186,3 +183,15 @@ export declare function paneSend(a: Commands["pane:send"]["payload"], o?: RtClie
|
|
|
186
183
|
/** Brings a herdr pane to the front. The daemon routes this to the tray, which
|
|
187
184
|
owns the herdr focus and the native terminal-window raise. */
|
|
188
185
|
export declare function paneFocus(a: Commands["pane:focus"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["pane:focus"]["data"]>>;
|
|
186
|
+
export declare function gateOpen(a: Commands["gate:open"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:open"]["data"]>>;
|
|
187
|
+
export declare function gateAnswer(a: Commands["gate:answer"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:answer"]["data"]>>;
|
|
188
|
+
/** Daemon clamps its own wait to 240s (gates-store.ts); the client abort
|
|
189
|
+
must outlive that cap, same +10s buffer as commands/events.ts's
|
|
190
|
+
IPC_TIMEOUT_MS over DAEMON_WAIT_MS. */
|
|
191
|
+
export declare function gateWait(a: Commands["gate:wait"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:wait"]["data"]>>;
|
|
192
|
+
export declare function gateList(a: Commands["gate:list"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:list"]["data"]>>;
|
|
193
|
+
export declare function gatePark(a: Commands["gate:park"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:park"]["data"]>>;
|
|
194
|
+
export declare function gateClose(a: Commands["gate:close"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:close"]["data"]>>;
|
|
195
|
+
export declare function gateSubscribe(a: Commands["gate:subscribe"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:subscribe"]["data"]>>;
|
|
196
|
+
export declare function gateUnsubscribe(a: Commands["gate:unsubscribe"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:unsubscribe"]["data"]>>;
|
|
197
|
+
export declare function gateSubscriptions(a: Commands["gate:subscriptions"]["payload"], o?: RtClientOptions): Promise<RtResponse<Commands["gate:subscriptions"]["data"]>>;
|
package/dist/commands.d.ts
CHANGED
|
@@ -91,6 +91,65 @@ export interface EventsBusEvent {
|
|
|
91
91
|
payload: unknown;
|
|
92
92
|
emittedAt: number;
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Duplicated shape on purpose, same reasoning as EventsBusEvent above:
|
|
96
|
+
* these mirror lib/daemon/gates-store.ts's types, which rt-client cannot
|
|
97
|
+
* import. gates-store.ts imports them back FROM this package (Commands
|
|
98
|
+
* already flows daemon -> rt-client, e.g. handlers/events.ts), so this is
|
|
99
|
+
* the single source of truth for the wire shape.
|
|
100
|
+
*/
|
|
101
|
+
/** The `by` value a pane spells when it answers its own gate: the only
|
|
102
|
+
value gates-store.ts's release tracking (CAS winner or loser) reacts to. */
|
|
103
|
+
export declare const GATE_BY_PANE = "pane";
|
|
104
|
+
export type GateStatus = "open" | "answered" | "parked" | "closed";
|
|
105
|
+
export interface GateQuestion {
|
|
106
|
+
id: string;
|
|
107
|
+
label: string;
|
|
108
|
+
multi: boolean;
|
|
109
|
+
options: string[];
|
|
110
|
+
}
|
|
111
|
+
export interface GateAnswer {
|
|
112
|
+
answers: Record<string, string | string[] | {
|
|
113
|
+
value: string | string[];
|
|
114
|
+
note?: string;
|
|
115
|
+
}>;
|
|
116
|
+
by: string;
|
|
117
|
+
answeredAt: number;
|
|
118
|
+
}
|
|
119
|
+
export interface GateRow {
|
|
120
|
+
id: string;
|
|
121
|
+
subject: string;
|
|
122
|
+
kind: string;
|
|
123
|
+
questions: GateQuestion[];
|
|
124
|
+
meta: Record<string, unknown> | null;
|
|
125
|
+
status: GateStatus;
|
|
126
|
+
answer: GateAnswer | null;
|
|
127
|
+
openedAt: number;
|
|
128
|
+
parkedAt: number | null;
|
|
129
|
+
closedAt: number | null;
|
|
130
|
+
closedReason: "abandoned" | "superseded" | "pruned" | null;
|
|
131
|
+
agent: string | null;
|
|
132
|
+
pane: string | null;
|
|
133
|
+
nudge: {
|
|
134
|
+
session: string;
|
|
135
|
+
} | null;
|
|
136
|
+
delivery: {
|
|
137
|
+
outcome: "delivered" | "dead-pane";
|
|
138
|
+
at: number;
|
|
139
|
+
} | null;
|
|
140
|
+
released: boolean;
|
|
141
|
+
}
|
|
142
|
+
export interface GateSubscription {
|
|
143
|
+
id: string;
|
|
144
|
+
subjectPrefix: string;
|
|
145
|
+
session: string;
|
|
146
|
+
createdAt: number;
|
|
147
|
+
lastDelivery: {
|
|
148
|
+
outcome: "delivered" | "failed";
|
|
149
|
+
at: number;
|
|
150
|
+
} | null;
|
|
151
|
+
dead: boolean;
|
|
152
|
+
}
|
|
94
153
|
/**
|
|
95
154
|
* Duplicated shape on purpose, same reasoning as EventsBusEvent above:
|
|
96
155
|
* these mirror lib/state/chat-store.ts's types, which rt-client cannot
|
|
@@ -810,6 +869,7 @@ export interface Commands {
|
|
|
810
869
|
payload: {
|
|
811
870
|
handle: string;
|
|
812
871
|
room?: string;
|
|
872
|
+
upto?: number;
|
|
813
873
|
};
|
|
814
874
|
data: Record<string, never>;
|
|
815
875
|
};
|
|
@@ -1205,6 +1265,114 @@ export interface Commands {
|
|
|
1205
1265
|
payload: Record<string, never>;
|
|
1206
1266
|
data: unknown;
|
|
1207
1267
|
};
|
|
1268
|
+
"gate:open": {
|
|
1269
|
+
payload: {
|
|
1270
|
+
subject: string;
|
|
1271
|
+
kind: string;
|
|
1272
|
+
questions: GateQuestion[];
|
|
1273
|
+
meta?: Record<string, unknown>;
|
|
1274
|
+
agent?: string;
|
|
1275
|
+
pane?: string;
|
|
1276
|
+
nudge?: {
|
|
1277
|
+
session: string;
|
|
1278
|
+
};
|
|
1279
|
+
};
|
|
1280
|
+
data: {
|
|
1281
|
+
id: string;
|
|
1282
|
+
supersededId: string | null;
|
|
1283
|
+
};
|
|
1284
|
+
};
|
|
1285
|
+
/**
|
|
1286
|
+
* A CAS loss is a DEFINED OUTCOME, not an error: `ok:true` with
|
|
1287
|
+
* `conflict:true` and the WINNING row, so every consumer gets the winner
|
|
1288
|
+
* typed with no envelope hacks. `ok:false` is reserved for
|
|
1289
|
+
* not-found/closed/validation failures.
|
|
1290
|
+
*/
|
|
1291
|
+
"gate:answer": {
|
|
1292
|
+
payload: {
|
|
1293
|
+
id: string;
|
|
1294
|
+
answers: GateAnswer["answers"];
|
|
1295
|
+
by: string;
|
|
1296
|
+
};
|
|
1297
|
+
data: {
|
|
1298
|
+
row: GateRow;
|
|
1299
|
+
conflict?: true;
|
|
1300
|
+
};
|
|
1301
|
+
};
|
|
1302
|
+
/** `ok:false "not-found"` on an unknown id is terminal; the CLI loop must not re-enter on it.
|
|
1303
|
+
* `timeout` carries no row (nothing settled); `answered`/`closed` always carry the settled row. */
|
|
1304
|
+
"gate:wait": {
|
|
1305
|
+
payload: {
|
|
1306
|
+
id: string;
|
|
1307
|
+
waitMs?: number;
|
|
1308
|
+
};
|
|
1309
|
+
data: {
|
|
1310
|
+
status: "timeout";
|
|
1311
|
+
} | {
|
|
1312
|
+
status: "answered" | "closed";
|
|
1313
|
+
row: GateRow;
|
|
1314
|
+
};
|
|
1315
|
+
};
|
|
1316
|
+
/** Paged like events:list: an omitted `limit` clamps daemon-side rather than
|
|
1317
|
+
* forcing a full-table read; `cursor` is the paging rowid to resume from. */
|
|
1318
|
+
"gate:list": {
|
|
1319
|
+
payload: {
|
|
1320
|
+
open?: boolean;
|
|
1321
|
+
subjectPrefix?: string;
|
|
1322
|
+
kind?: string;
|
|
1323
|
+
limit?: number;
|
|
1324
|
+
cursor?: number;
|
|
1325
|
+
};
|
|
1326
|
+
data: {
|
|
1327
|
+
gates: GateRow[];
|
|
1328
|
+
cursor: number;
|
|
1329
|
+
};
|
|
1330
|
+
};
|
|
1331
|
+
"gate:park": {
|
|
1332
|
+
payload: {
|
|
1333
|
+
id: string;
|
|
1334
|
+
};
|
|
1335
|
+
data: {
|
|
1336
|
+
ok: true;
|
|
1337
|
+
};
|
|
1338
|
+
};
|
|
1339
|
+
"gate:close": {
|
|
1340
|
+
payload: {
|
|
1341
|
+
id: string;
|
|
1342
|
+
reason: "abandoned" | "superseded" | "pruned";
|
|
1343
|
+
};
|
|
1344
|
+
data: {
|
|
1345
|
+
ok: true;
|
|
1346
|
+
};
|
|
1347
|
+
};
|
|
1348
|
+
"gate:subscribe": {
|
|
1349
|
+
payload: {
|
|
1350
|
+
subjectPrefix: string;
|
|
1351
|
+
session: string;
|
|
1352
|
+
};
|
|
1353
|
+
data: {
|
|
1354
|
+
id: string;
|
|
1355
|
+
};
|
|
1356
|
+
};
|
|
1357
|
+
"gate:unsubscribe": {
|
|
1358
|
+
payload: {
|
|
1359
|
+
id: string;
|
|
1360
|
+
};
|
|
1361
|
+
data: {
|
|
1362
|
+
removed: boolean;
|
|
1363
|
+
};
|
|
1364
|
+
};
|
|
1365
|
+
/** The shepherd's gap-recovery liveness check and the observability window
|
|
1366
|
+
* onto delivery outcomes (dead marks included). */
|
|
1367
|
+
"gate:subscriptions": {
|
|
1368
|
+
payload: {
|
|
1369
|
+
session?: string;
|
|
1370
|
+
live?: boolean;
|
|
1371
|
+
};
|
|
1372
|
+
data: {
|
|
1373
|
+
subscriptions: GateSubscription[];
|
|
1374
|
+
};
|
|
1375
|
+
};
|
|
1208
1376
|
/** Wire reply on success is always `{ok:true, repaired}` (no `data`
|
|
1209
1377
|
* wrapper) — `data` here documents the extra field the same way PingData
|
|
1210
1378
|
* does for `ping`, not the literal wire nesting (R3). */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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, chatAck, chatClaim, chatRelease, chatPost, chatRead, chatRooms, chatWho, chatMark, chatMessages, chatSignIn, chatSignOut, chatAway, chatBack, chatBuddies, chatDm, chatArchive, chatDmOpen, eventsHead, eventsEmit, eventsWait, eventsList, agentStart, agentResume, agentGet, agentList, paneList, panePeek, paneSpawn, paneAccounts, paneDirectories, chatInvite, paneSend, paneFocus, } from "./client.ts";
|
|
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, ChatClaimOutcome, RoomSummary, BuddyStatus, PresenceRow, AgentRecord, AgentSurface, AgentStatus, ChatPane, PaneAccount, PaneDirectory, InviteResult, PaneDelivery, PaneSendResult, PaneFocusResult, } from "./commands.ts";
|
|
3
|
+
export { readProjectMRs, readDiscussions, readMrsByBranch, readBranchCache, resolveForgeToken, listRuns, getRun, abandonRun, chatJoin, chatLeave, chatAck, chatClaim, chatRelease, chatPost, chatRead, chatRooms, chatWho, chatMark, chatMessages, chatSignIn, chatSignOut, chatAway, chatBack, chatBuddies, chatDm, chatArchive, chatDmOpen, eventsHead, eventsEmit, eventsWait, eventsList, agentStart, agentResume, agentGet, agentList, paneList, panePeek, paneSpawn, paneAccounts, paneDirectories, chatInvite, paneSend, paneFocus, gateOpen, gateAnswer, gateWait, gateList, gatePark, gateClose, gateSubscribe, gateUnsubscribe, gateSubscriptions, } from "./client.ts";
|
|
4
|
+
export { COMMAND_NAMES, GATE_BY_PANE } 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, ChatClaimOutcome, RoomSummary, BuddyStatus, PresenceRow, AgentRecord, AgentSurface, AgentStatus, ChatPane, PaneAccount, PaneDirectory, InviteResult, PaneDelivery, PaneSendResult, PaneFocusResult, GateStatus, GateQuestion, GateAnswer, GateRow, GateSubscription, } from "./commands.ts";
|
|
6
6
|
export { subscribe, createRelay, DEFAULT_WS_URL } from "./relay.ts";
|
|
7
7
|
export type { RelayEventType } from "./relay.ts";
|
|
8
8
|
export { daemonHealth } from "./health.ts";
|
package/dist/index.js
CHANGED
|
@@ -118,6 +118,8 @@ function chatMark(a, o = {}) {
|
|
|
118
118
|
const payload = { handle: a.handle };
|
|
119
119
|
if (a.room !== undefined)
|
|
120
120
|
payload.room = a.room;
|
|
121
|
+
if (a.upto !== undefined)
|
|
122
|
+
payload.upto = a.upto;
|
|
121
123
|
return rtCommand("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
122
124
|
}
|
|
123
125
|
function chatMessages(a, o = {}) {
|
|
@@ -268,7 +270,50 @@ function paneSend(a, o = {}) {
|
|
|
268
270
|
function paneFocus(a, o = {}) {
|
|
269
271
|
return rtCommand("pane:focus", { paneId: a.paneId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
270
272
|
}
|
|
273
|
+
function gateOpen(a, o = {}) {
|
|
274
|
+
const payload = { subject: a.subject, kind: a.kind, questions: a.questions };
|
|
275
|
+
for (const k of ["meta", "agent", "pane", "nudge"])
|
|
276
|
+
if (a[k] !== undefined)
|
|
277
|
+
payload[k] = a[k];
|
|
278
|
+
return rtCommand("gate:open", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
279
|
+
}
|
|
280
|
+
function gateAnswer(a, o = {}) {
|
|
281
|
+
return rtCommand("gate:answer", { id: a.id, answers: a.answers, by: a.by }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
282
|
+
}
|
|
283
|
+
function gateWait(a, o = {}) {
|
|
284
|
+
const payload = { id: a.id };
|
|
285
|
+
if (a.waitMs !== undefined)
|
|
286
|
+
payload.waitMs = a.waitMs;
|
|
287
|
+
return rtCommand("gate:wait", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 250000 });
|
|
288
|
+
}
|
|
289
|
+
function gateList(a, o = {}) {
|
|
290
|
+
const payload = {};
|
|
291
|
+
for (const k of ["open", "subjectPrefix", "kind", "limit", "cursor"])
|
|
292
|
+
if (a[k] !== undefined)
|
|
293
|
+
payload[k] = a[k];
|
|
294
|
+
return rtCommand("gate:list", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
295
|
+
}
|
|
296
|
+
function gatePark(a, o = {}) {
|
|
297
|
+
return rtCommand("gate:park", { id: a.id }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
298
|
+
}
|
|
299
|
+
function gateClose(a, o = {}) {
|
|
300
|
+
return rtCommand("gate:close", { id: a.id, reason: a.reason }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
301
|
+
}
|
|
302
|
+
function gateSubscribe(a, o = {}) {
|
|
303
|
+
return rtCommand("gate:subscribe", { subjectPrefix: a.subjectPrefix, session: a.session }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
304
|
+
}
|
|
305
|
+
function gateUnsubscribe(a, o = {}) {
|
|
306
|
+
return rtCommand("gate:unsubscribe", { id: a.id }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
307
|
+
}
|
|
308
|
+
function gateSubscriptions(a, o = {}) {
|
|
309
|
+
const payload = {};
|
|
310
|
+
for (const k of ["session", "live"])
|
|
311
|
+
if (a[k] !== undefined)
|
|
312
|
+
payload[k] = a[k];
|
|
313
|
+
return rtCommand("gate:subscriptions", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
314
|
+
}
|
|
271
315
|
// src/commands.ts
|
|
316
|
+
var GATE_BY_PANE = "pane";
|
|
272
317
|
var COMMAND_NAMES = [
|
|
273
318
|
"project-mrs:read",
|
|
274
319
|
"discussions:read",
|
|
@@ -337,6 +382,15 @@ var COMMAND_NAMES = [
|
|
|
337
382
|
"endpoint:status",
|
|
338
383
|
"repos:locate",
|
|
339
384
|
"freshness:reconcile",
|
|
385
|
+
"gate:open",
|
|
386
|
+
"gate:answer",
|
|
387
|
+
"gate:wait",
|
|
388
|
+
"gate:list",
|
|
389
|
+
"gate:park",
|
|
390
|
+
"gate:close",
|
|
391
|
+
"gate:subscribe",
|
|
392
|
+
"gate:unsubscribe",
|
|
393
|
+
"gate:subscriptions",
|
|
340
394
|
"hooks:repair",
|
|
341
395
|
"hooks:watch",
|
|
342
396
|
"sdm:catalog",
|
|
@@ -688,6 +742,15 @@ var REGISTRY = [
|
|
|
688
742
|
migrated: true,
|
|
689
743
|
description: "Home-repo snapshot daemon config: enabled, debounce/push delays, and the janitor threshold/interval for zones left dirty too long."
|
|
690
744
|
},
|
|
745
|
+
{
|
|
746
|
+
key: "rt.teamSnapshot",
|
|
747
|
+
type: "object",
|
|
748
|
+
scopes: ["machine"],
|
|
749
|
+
default: { enabled: true, debounceSec: 20, pushDelaySec: 60, janitorThresholdHours: 6, janitorIntervalMin: 30, pullIntervalSec: 300 },
|
|
750
|
+
merge: "deep",
|
|
751
|
+
migrated: true,
|
|
752
|
+
description: "Team-clone snapshot daemon config: the home snapshot's fields plus pullIntervalSec, the fast-forward/rebase pull cadence for every clone under ~/.mattstack/teams."
|
|
753
|
+
},
|
|
691
754
|
{
|
|
692
755
|
key: "rt.sync",
|
|
693
756
|
type: "object",
|
|
@@ -990,6 +1053,14 @@ var REGISTRY = [
|
|
|
990
1053
|
merge: "deep",
|
|
991
1054
|
description: "This developer's triage user-intent flags (which triage sweeps run automatically); a sibling flat key of board.triage.doctorSkill, not its container — the board reader assembles the two independently."
|
|
992
1055
|
},
|
|
1056
|
+
{
|
|
1057
|
+
key: "board.reReview",
|
|
1058
|
+
type: "object",
|
|
1059
|
+
scopes: ["user", "team"],
|
|
1060
|
+
merge: "deep",
|
|
1061
|
+
default: { enabled: true },
|
|
1062
|
+
description: "Gate for the board's automatic re-review sweep ({enabled}); rt's cron.triage step installs the board-triage trigger only while this is on. A fresh key, not an ownership-latch port, so a default is fine here."
|
|
1063
|
+
},
|
|
993
1064
|
{
|
|
994
1065
|
key: "board.agent.account",
|
|
995
1066
|
type: "string",
|
|
@@ -2031,6 +2102,15 @@ export {
|
|
|
2031
2102
|
getSetting,
|
|
2032
2103
|
getRun,
|
|
2033
2104
|
getDef,
|
|
2105
|
+
gateWait,
|
|
2106
|
+
gateUnsubscribe,
|
|
2107
|
+
gateSubscriptions,
|
|
2108
|
+
gateSubscribe,
|
|
2109
|
+
gatePark,
|
|
2110
|
+
gateOpen,
|
|
2111
|
+
gateList,
|
|
2112
|
+
gateClose,
|
|
2113
|
+
gateAnswer,
|
|
2034
2114
|
explainSetting,
|
|
2035
2115
|
expandVariables,
|
|
2036
2116
|
eventsWait,
|
|
@@ -2070,6 +2150,7 @@ export {
|
|
|
2070
2150
|
abandonRun,
|
|
2071
2151
|
SCOPE_ORDER,
|
|
2072
2152
|
REGISTRY,
|
|
2153
|
+
GATE_BY_PANE,
|
|
2073
2154
|
DEFAULT_WS_URL,
|
|
2074
2155
|
DEFAULT_SOCK,
|
|
2075
2156
|
COMMAND_NAMES
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mattstack/rt-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"./test/fake-daemon.ts": "./test/fake-daemon.ts"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
|
-
"@mattstack/glance": ">=0.
|
|
21
|
+
"@mattstack/glance": ">=0.24.0"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"jsonc-parser": "^3.3.1"
|
package/src/client.ts
CHANGED
|
@@ -223,12 +223,13 @@ export function chatWho(
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
export function chatMark(
|
|
226
|
-
a:
|
|
226
|
+
a: Commands["chat:mark"]["payload"],
|
|
227
227
|
o: RtClientOptions = {},
|
|
228
|
-
): Promise<RtResponse<
|
|
228
|
+
): Promise<RtResponse<Commands["chat:mark"]["data"]>> {
|
|
229
229
|
const payload: Record<string, unknown> = { handle: a.handle };
|
|
230
230
|
if (a.room !== undefined) payload.room = a.room;
|
|
231
|
-
|
|
231
|
+
if (a.upto !== undefined) payload.upto = a.upto;
|
|
232
|
+
return rtCommand<Commands["chat:mark"]["data"]>("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
232
233
|
}
|
|
233
234
|
|
|
234
235
|
export function chatMessages(
|
|
@@ -442,3 +443,79 @@ export function paneFocus(
|
|
|
442
443
|
): Promise<RtResponse<Commands["pane:focus"]["data"]>> {
|
|
443
444
|
return rtCommand<Commands["pane:focus"]["data"]>("pane:focus", { paneId: a.paneId }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
444
445
|
}
|
|
446
|
+
|
|
447
|
+
// ─── Gates (BOARD-20/21 gate facility) ─────────────────────────────────────
|
|
448
|
+
|
|
449
|
+
export function gateOpen(
|
|
450
|
+
a: Commands["gate:open"]["payload"],
|
|
451
|
+
o: RtClientOptions = {},
|
|
452
|
+
): Promise<RtResponse<Commands["gate:open"]["data"]>> {
|
|
453
|
+
const payload: Record<string, unknown> = { subject: a.subject, kind: a.kind, questions: a.questions };
|
|
454
|
+
for (const k of ["meta", "agent", "pane", "nudge"] as const) if (a[k] !== undefined) payload[k] = a[k];
|
|
455
|
+
return rtCommand<Commands["gate:open"]["data"]>("gate:open", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export function gateAnswer(
|
|
459
|
+
a: Commands["gate:answer"]["payload"],
|
|
460
|
+
o: RtClientOptions = {},
|
|
461
|
+
): Promise<RtResponse<Commands["gate:answer"]["data"]>> {
|
|
462
|
+
return rtCommand<Commands["gate:answer"]["data"]>("gate:answer", { id: a.id, answers: a.answers, by: a.by }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Daemon clamps its own wait to 240s (gates-store.ts); the client abort
|
|
466
|
+
must outlive that cap, same +10s buffer as commands/events.ts's
|
|
467
|
+
IPC_TIMEOUT_MS over DAEMON_WAIT_MS. */
|
|
468
|
+
export function gateWait(
|
|
469
|
+
a: Commands["gate:wait"]["payload"],
|
|
470
|
+
o: RtClientOptions = {},
|
|
471
|
+
): Promise<RtResponse<Commands["gate:wait"]["data"]>> {
|
|
472
|
+
const payload: Record<string, unknown> = { id: a.id };
|
|
473
|
+
if (a.waitMs !== undefined) payload.waitMs = a.waitMs;
|
|
474
|
+
return rtCommand<Commands["gate:wait"]["data"]>("gate:wait", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 250_000 });
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export function gateList(
|
|
478
|
+
a: Commands["gate:list"]["payload"],
|
|
479
|
+
o: RtClientOptions = {},
|
|
480
|
+
): Promise<RtResponse<Commands["gate:list"]["data"]>> {
|
|
481
|
+
const payload: Record<string, unknown> = {};
|
|
482
|
+
for (const k of ["open", "subjectPrefix", "kind", "limit", "cursor"] as const) if (a[k] !== undefined) payload[k] = a[k];
|
|
483
|
+
return rtCommand<Commands["gate:list"]["data"]>("gate:list", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export function gatePark(
|
|
487
|
+
a: Commands["gate:park"]["payload"],
|
|
488
|
+
o: RtClientOptions = {},
|
|
489
|
+
): Promise<RtResponse<Commands["gate:park"]["data"]>> {
|
|
490
|
+
return rtCommand<Commands["gate:park"]["data"]>("gate:park", { id: a.id }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export function gateClose(
|
|
494
|
+
a: Commands["gate:close"]["payload"],
|
|
495
|
+
o: RtClientOptions = {},
|
|
496
|
+
): Promise<RtResponse<Commands["gate:close"]["data"]>> {
|
|
497
|
+
return rtCommand<Commands["gate:close"]["data"]>("gate:close", { id: a.id, reason: a.reason }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export function gateSubscribe(
|
|
501
|
+
a: Commands["gate:subscribe"]["payload"],
|
|
502
|
+
o: RtClientOptions = {},
|
|
503
|
+
): Promise<RtResponse<Commands["gate:subscribe"]["data"]>> {
|
|
504
|
+
return rtCommand<Commands["gate:subscribe"]["data"]>("gate:subscribe", { subjectPrefix: a.subjectPrefix, session: a.session }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export function gateUnsubscribe(
|
|
508
|
+
a: Commands["gate:unsubscribe"]["payload"],
|
|
509
|
+
o: RtClientOptions = {},
|
|
510
|
+
): Promise<RtResponse<Commands["gate:unsubscribe"]["data"]>> {
|
|
511
|
+
return rtCommand<Commands["gate:unsubscribe"]["data"]>("gate:unsubscribe", { id: a.id }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export function gateSubscriptions(
|
|
515
|
+
a: Commands["gate:subscriptions"]["payload"],
|
|
516
|
+
o: RtClientOptions = {},
|
|
517
|
+
): Promise<RtResponse<Commands["gate:subscriptions"]["data"]>> {
|
|
518
|
+
const payload: Record<string, unknown> = {};
|
|
519
|
+
for (const k of ["session", "live"] as const) if (a[k] !== undefined) payload[k] = a[k];
|
|
520
|
+
return rtCommand<Commands["gate:subscriptions"]["data"]>("gate:subscriptions", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
521
|
+
}
|
package/src/commands.ts
CHANGED
|
@@ -83,6 +83,41 @@ export interface ForgeTokenData {
|
|
|
83
83
|
*/
|
|
84
84
|
export interface EventsBusEvent { id: number; topic: string; payload: unknown; emittedAt: number }
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Duplicated shape on purpose, same reasoning as EventsBusEvent above:
|
|
88
|
+
* these mirror lib/daemon/gates-store.ts's types, which rt-client cannot
|
|
89
|
+
* import. gates-store.ts imports them back FROM this package (Commands
|
|
90
|
+
* already flows daemon -> rt-client, e.g. handlers/events.ts), so this is
|
|
91
|
+
* the single source of truth for the wire shape.
|
|
92
|
+
*/
|
|
93
|
+
/** The `by` value a pane spells when it answers its own gate: the only
|
|
94
|
+
value gates-store.ts's release tracking (CAS winner or loser) reacts to. */
|
|
95
|
+
export const GATE_BY_PANE = "pane";
|
|
96
|
+
|
|
97
|
+
export type GateStatus = "open" | "answered" | "parked" | "closed";
|
|
98
|
+
export interface GateQuestion { id: string; label: string; multi: boolean; options: string[] }
|
|
99
|
+
export interface GateAnswer { answers: Record<string, string | string[] | { value: string | string[]; note?: string }>; by: string; answeredAt: number }
|
|
100
|
+
export interface GateRow {
|
|
101
|
+
id: string; subject: string; kind: string;
|
|
102
|
+
questions: GateQuestion[]; meta: Record<string, unknown> | null;
|
|
103
|
+
status: GateStatus; answer: GateAnswer | null;
|
|
104
|
+
openedAt: number; parkedAt: number | null; closedAt: number | null;
|
|
105
|
+
closedReason: "abandoned" | "superseded" | "pruned" | null;
|
|
106
|
+
agent: string | null; pane: string | null;
|
|
107
|
+
nudge: { session: string } | null;
|
|
108
|
+
delivery: { outcome: "delivered" | "dead-pane"; at: number } | null;
|
|
109
|
+
released: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface GateSubscription {
|
|
113
|
+
id: string;
|
|
114
|
+
subjectPrefix: string;
|
|
115
|
+
session: string;
|
|
116
|
+
createdAt: number;
|
|
117
|
+
lastDelivery: { outcome: "delivered" | "failed"; at: number } | null;
|
|
118
|
+
dead: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
86
121
|
/**
|
|
87
122
|
* Duplicated shape on purpose, same reasoning as EventsBusEvent above:
|
|
88
123
|
* these mirror lib/state/chat-store.ts's types, which rt-client cannot
|
|
@@ -437,7 +472,7 @@ export interface Commands {
|
|
|
437
472
|
"chat:read": { payload: { handle: string; room?: string; limit?: number; sinceMs?: number }; data: { rooms: { room: string; messages: ChatMessage[] }[] } };
|
|
438
473
|
"chat:rooms": { payload: { handle: string; includeArchived?: boolean }; data: { rooms: RoomSummary[] } };
|
|
439
474
|
"chat:who": { payload: { room: string }; data: { members: ChatMember[] } };
|
|
440
|
-
"chat:mark": { payload: { handle: string; room?: string }; data: Record<string, never> };
|
|
475
|
+
"chat:mark": { payload: { handle: string; room?: string; upto?: number }; data: Record<string, never> };
|
|
441
476
|
"chat:messages": { payload: { room: string; before?: number; limit?: number }; data: { messages: ChatMessage[] } };
|
|
442
477
|
|
|
443
478
|
// A session id keys these to one signed-in handle, not a room-membership
|
|
@@ -538,6 +573,29 @@ export interface Commands {
|
|
|
538
573
|
"repos:locate": { payload: { newPath: string; repo?: string; dryRun?: boolean }; data: unknown };
|
|
539
574
|
"freshness:reconcile": { payload: Record<string, never>; data: unknown };
|
|
540
575
|
|
|
576
|
+
// ─── Gate facility (BOARD-20/21) ─────────────────────────────────────────
|
|
577
|
+
"gate:open": { payload: { subject: string; kind: string; questions: GateQuestion[]; meta?: Record<string, unknown>; agent?: string; pane?: string; nudge?: { session: string } }; data: { id: string; supersededId: string | null } };
|
|
578
|
+
/**
|
|
579
|
+
* A CAS loss is a DEFINED OUTCOME, not an error: `ok:true` with
|
|
580
|
+
* `conflict:true` and the WINNING row, so every consumer gets the winner
|
|
581
|
+
* typed with no envelope hacks. `ok:false` is reserved for
|
|
582
|
+
* not-found/closed/validation failures.
|
|
583
|
+
*/
|
|
584
|
+
"gate:answer": { payload: { id: string; answers: GateAnswer["answers"]; by: string }; data: { row: GateRow; conflict?: true } };
|
|
585
|
+
/** `ok:false "not-found"` on an unknown id is terminal; the CLI loop must not re-enter on it.
|
|
586
|
+
* `timeout` carries no row (nothing settled); `answered`/`closed` always carry the settled row. */
|
|
587
|
+
"gate:wait": { payload: { id: string; waitMs?: number }; data: { status: "timeout" } | { status: "answered" | "closed"; row: GateRow } };
|
|
588
|
+
/** Paged like events:list: an omitted `limit` clamps daemon-side rather than
|
|
589
|
+
* forcing a full-table read; `cursor` is the paging rowid to resume from. */
|
|
590
|
+
"gate:list": { payload: { open?: boolean; subjectPrefix?: string; kind?: string; limit?: number; cursor?: number }; data: { gates: GateRow[]; cursor: number } };
|
|
591
|
+
"gate:park": { payload: { id: string }; data: { ok: true } };
|
|
592
|
+
"gate:close": { payload: { id: string; reason: "abandoned" | "superseded" | "pruned" }; data: { ok: true } };
|
|
593
|
+
"gate:subscribe": { payload: { subjectPrefix: string; session: string }; data: { id: string } };
|
|
594
|
+
"gate:unsubscribe": { payload: { id: string }; data: { removed: boolean } };
|
|
595
|
+
/** The shepherd's gap-recovery liveness check and the observability window
|
|
596
|
+
* onto delivery outcomes (dead marks included). */
|
|
597
|
+
"gate:subscriptions": { payload: { session?: string; live?: boolean }; data: { subscriptions: GateSubscription[] } };
|
|
598
|
+
|
|
541
599
|
/** Wire reply on success is always `{ok:true, repaired}` (no `data`
|
|
542
600
|
* wrapper) — `data` here documents the extra field the same way PingData
|
|
543
601
|
* does for `ping`, not the literal wire nesting (R3). */
|
|
@@ -632,6 +690,15 @@ export const COMMAND_NAMES: readonly CommandName[] = [
|
|
|
632
690
|
"endpoint:status",
|
|
633
691
|
"repos:locate",
|
|
634
692
|
"freshness:reconcile",
|
|
693
|
+
"gate:open",
|
|
694
|
+
"gate:answer",
|
|
695
|
+
"gate:wait",
|
|
696
|
+
"gate:list",
|
|
697
|
+
"gate:park",
|
|
698
|
+
"gate:close",
|
|
699
|
+
"gate:subscribe",
|
|
700
|
+
"gate:unsubscribe",
|
|
701
|
+
"gate:subscriptions",
|
|
635
702
|
"hooks:repair",
|
|
636
703
|
"hooks:watch",
|
|
637
704
|
"sdm:catalog",
|
package/src/index.ts
CHANGED
|
@@ -45,9 +45,18 @@ export {
|
|
|
45
45
|
chatInvite,
|
|
46
46
|
paneSend,
|
|
47
47
|
paneFocus,
|
|
48
|
+
gateOpen,
|
|
49
|
+
gateAnswer,
|
|
50
|
+
gateWait,
|
|
51
|
+
gateList,
|
|
52
|
+
gatePark,
|
|
53
|
+
gateClose,
|
|
54
|
+
gateSubscribe,
|
|
55
|
+
gateUnsubscribe,
|
|
56
|
+
gateSubscriptions,
|
|
48
57
|
} from "./client.ts";
|
|
49
58
|
|
|
50
|
-
export { COMMAND_NAMES } from "./commands.ts";
|
|
59
|
+
export { COMMAND_NAMES, GATE_BY_PANE } from "./commands.ts";
|
|
51
60
|
export type {
|
|
52
61
|
Discussion,
|
|
53
62
|
DemandDecl,
|
|
@@ -84,6 +93,11 @@ export type {
|
|
|
84
93
|
PaneDelivery,
|
|
85
94
|
PaneSendResult,
|
|
86
95
|
PaneFocusResult,
|
|
96
|
+
GateStatus,
|
|
97
|
+
GateQuestion,
|
|
98
|
+
GateAnswer,
|
|
99
|
+
GateRow,
|
|
100
|
+
GateSubscription,
|
|
87
101
|
} from "./commands.ts";
|
|
88
102
|
|
|
89
103
|
export { subscribe, createRelay, DEFAULT_WS_URL } from "./relay.ts";
|
|
@@ -137,6 +137,15 @@ export const REGISTRY: readonly SettingDef[] = [
|
|
|
137
137
|
migrated: true,
|
|
138
138
|
description: "Home-repo snapshot daemon config: enabled, debounce/push delays, and the janitor threshold/interval for zones left dirty too long.",
|
|
139
139
|
},
|
|
140
|
+
{
|
|
141
|
+
key: "rt.teamSnapshot",
|
|
142
|
+
type: "object",
|
|
143
|
+
scopes: ["machine"],
|
|
144
|
+
default: { enabled: true, debounceSec: 20, pushDelaySec: 60, janitorThresholdHours: 6, janitorIntervalMin: 30, pullIntervalSec: 300 },
|
|
145
|
+
merge: "deep",
|
|
146
|
+
migrated: true,
|
|
147
|
+
description: "Team-clone snapshot daemon config: the home snapshot's fields plus pullIntervalSec, the fast-forward/rebase pull cadence for every clone under ~/.mattstack/teams.",
|
|
148
|
+
},
|
|
140
149
|
{
|
|
141
150
|
key: "rt.sync",
|
|
142
151
|
type: "object",
|
|
@@ -464,6 +473,14 @@ export const REGISTRY: readonly SettingDef[] = [
|
|
|
464
473
|
merge: "deep",
|
|
465
474
|
description: "This developer's triage user-intent flags (which triage sweeps run automatically); a sibling flat key of board.triage.doctorSkill, not its container — the board reader assembles the two independently.",
|
|
466
475
|
},
|
|
476
|
+
{
|
|
477
|
+
key: "board.reReview",
|
|
478
|
+
type: "object",
|
|
479
|
+
scopes: ["user", "team"],
|
|
480
|
+
merge: "deep",
|
|
481
|
+
default: { enabled: true },
|
|
482
|
+
description: "Gate for the board's automatic re-review sweep ({enabled}); rt's cron.triage step installs the board-triage trigger only while this is on. A fresh key, not an ownership-latch port, so a default is fine here.",
|
|
483
|
+
},
|
|
467
484
|
|
|
468
485
|
// --- board (machine) ---------------------------------------------------
|
|
469
486
|
{
|