@mattstack/rt-client 0.10.1 → 0.11.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 +21 -1
- package/dist/commands.d.ts +574 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +203 -16
- package/dist/settings/exec.d.ts +2 -0
- package/dist/settings/resolve.d.ts +5 -0
- package/dist/smart-pane.d.ts +48 -0
- package/dist/transport.d.ts +7 -0
- package/package.json +4 -1
- package/src/client.ts +24 -3
- package/src/commands.ts +246 -2
- package/src/index.ts +8 -1
- package/src/settings/exec.ts +47 -13
- package/src/settings/registry-defs.ts +43 -0
- package/src/settings/resolve.ts +24 -3
- package/src/smart-pane.ts +94 -0
- package/src/transport.ts +5 -1
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ async function rtCommand(cmd, payload, opts = {}) {
|
|
|
14
14
|
const res = await fetch(`http://localhost/${cmd}`, {
|
|
15
15
|
unix: sockPath,
|
|
16
16
|
method: "POST",
|
|
17
|
-
headers: { "Content-Type": "application/json" },
|
|
17
|
+
headers: { "Content-Type": "application/json", "X-RT-Client": `rt-client/${process.pid}` },
|
|
18
18
|
body: JSON.stringify(payload),
|
|
19
19
|
signal: AbortSignal.timeout(opts.timeoutMs ?? 15000)
|
|
20
20
|
});
|
|
@@ -82,8 +82,19 @@ function chatPost(a, o = {}) {
|
|
|
82
82
|
const payload = { room: a.room, handle: a.handle, body: a.body };
|
|
83
83
|
if (a.mentions !== undefined)
|
|
84
84
|
payload.mentions = a.mentions;
|
|
85
|
+
if (a.quiet)
|
|
86
|
+
payload.quiet = true;
|
|
85
87
|
return rtCommand("chat:post", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
86
88
|
}
|
|
89
|
+
function chatAck(a, o = {}) {
|
|
90
|
+
return rtCommand("chat:ack", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
91
|
+
}
|
|
92
|
+
function chatClaim(a, o = {}) {
|
|
93
|
+
return rtCommand("chat:claim", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
94
|
+
}
|
|
95
|
+
function chatRelease(a, o = {}) {
|
|
96
|
+
return rtCommand("chat:release", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
|
|
97
|
+
}
|
|
87
98
|
function chatRead(a, o = {}) {
|
|
88
99
|
const payload = { handle: a.handle };
|
|
89
100
|
if (a.room !== undefined)
|
|
@@ -259,6 +270,9 @@ var COMMAND_NAMES = [
|
|
|
259
270
|
"runs:list",
|
|
260
271
|
"runs:get",
|
|
261
272
|
"runs:abandon",
|
|
273
|
+
"chat:ack",
|
|
274
|
+
"chat:claim",
|
|
275
|
+
"chat:release",
|
|
262
276
|
"chat:join",
|
|
263
277
|
"chat:leave",
|
|
264
278
|
"chat:post",
|
|
@@ -286,7 +300,45 @@ var COMMAND_NAMES = [
|
|
|
286
300
|
"pane:directories",
|
|
287
301
|
"pane:spawn",
|
|
288
302
|
"pane:send",
|
|
289
|
-
"pane:focus"
|
|
303
|
+
"pane:focus",
|
|
304
|
+
"cache:read",
|
|
305
|
+
"branch:enrich",
|
|
306
|
+
"cache:refresh",
|
|
307
|
+
"daemon:log-level",
|
|
308
|
+
"ping",
|
|
309
|
+
"status",
|
|
310
|
+
"tray:status",
|
|
311
|
+
"tcc:check",
|
|
312
|
+
"repos",
|
|
313
|
+
"ports",
|
|
314
|
+
"notifications",
|
|
315
|
+
"discussions:refresh",
|
|
316
|
+
"discussions:resolve",
|
|
317
|
+
"discussions:reply",
|
|
318
|
+
"discussions:diffs",
|
|
319
|
+
"mr:action",
|
|
320
|
+
"mr:fetch-job-detail",
|
|
321
|
+
"mr:fetch-job-trace",
|
|
322
|
+
"endpoint:claim",
|
|
323
|
+
"endpoint:lookup",
|
|
324
|
+
"endpoint:release",
|
|
325
|
+
"endpoint:status",
|
|
326
|
+
"repos:locate",
|
|
327
|
+
"freshness:reconcile",
|
|
328
|
+
"hooks:repair",
|
|
329
|
+
"hooks:watch",
|
|
330
|
+
"sdm:catalog",
|
|
331
|
+
"sdm:snapshot",
|
|
332
|
+
"sdm:recents",
|
|
333
|
+
"sdm:reconnect",
|
|
334
|
+
"system-processes",
|
|
335
|
+
"worktree:provision",
|
|
336
|
+
"worktree:create",
|
|
337
|
+
"worktree:dispose",
|
|
338
|
+
"worktree:list",
|
|
339
|
+
"worktree:restore",
|
|
340
|
+
"worktree:freshen",
|
|
341
|
+
"worktree:adopt"
|
|
290
342
|
];
|
|
291
343
|
// src/relay.ts
|
|
292
344
|
var DEFAULT_WS_URL = "ws://127.0.0.1:9401/ws";
|
|
@@ -413,6 +465,56 @@ function repoNameForPath(repoPath, reposJsonPath) {
|
|
|
413
465
|
return fromDb;
|
|
414
466
|
return repoNameFromJson(repoPath, jsonPath);
|
|
415
467
|
}
|
|
468
|
+
// src/smart-pane.ts
|
|
469
|
+
var DEFAULT_MIN_COLS = 50;
|
|
470
|
+
var DEFAULT_MIN_ROWS = 14;
|
|
471
|
+
function decidePlacement(rect, opts = {}) {
|
|
472
|
+
const minCols = opts.minCols ?? DEFAULT_MIN_COLS;
|
|
473
|
+
const minRows = opts.minRows ?? DEFAULT_MIN_ROWS;
|
|
474
|
+
if (!rect || rect.width <= 0 || rect.height <= 0)
|
|
475
|
+
return { kind: "split", direction: "right" };
|
|
476
|
+
const canRight = rect.width >= 2 * minCols;
|
|
477
|
+
const canDown = rect.height >= 2 * minRows;
|
|
478
|
+
if (!canRight && !canDown)
|
|
479
|
+
return { kind: "tab" };
|
|
480
|
+
if (canRight && canDown) {
|
|
481
|
+
return rect.width > 2 * rect.height ? { kind: "split", direction: "right" } : { kind: "split", direction: "down" };
|
|
482
|
+
}
|
|
483
|
+
return canRight ? { kind: "split", direction: "right" } : { kind: "split", direction: "down" };
|
|
484
|
+
}
|
|
485
|
+
async function anchorRect(herdr, anchorPaneId) {
|
|
486
|
+
const r = await herdr("pane.layout", { pane_id: anchorPaneId });
|
|
487
|
+
if (!r.ok)
|
|
488
|
+
return null;
|
|
489
|
+
const pane = (r.result?.layout?.panes ?? []).find((p) => p.pane_id === anchorPaneId);
|
|
490
|
+
return pane?.rect ? { width: pane.rect.width, height: pane.rect.height } : null;
|
|
491
|
+
}
|
|
492
|
+
async function openSmartPane(herdr, anchorPaneId, opts = {}) {
|
|
493
|
+
const focus = opts.focus ?? true;
|
|
494
|
+
const placement = decidePlacement(await anchorRect(herdr, anchorPaneId), opts);
|
|
495
|
+
let paneId;
|
|
496
|
+
if (placement.kind === "split") {
|
|
497
|
+
const s = await herdr("pane.split", { pane_id: anchorPaneId, direction: placement.direction, focus });
|
|
498
|
+
if (!s.ok)
|
|
499
|
+
throw new Error(`pane.split failed: ${s.message}`);
|
|
500
|
+
paneId = s.result?.pane?.pane_id;
|
|
501
|
+
if (!paneId)
|
|
502
|
+
throw new Error("pane.split returned no pane_id");
|
|
503
|
+
} else {
|
|
504
|
+
const workspaceId = anchorPaneId.split(":")[0];
|
|
505
|
+
const t = await herdr("tab.create", { workspace_id: workspaceId, focus });
|
|
506
|
+
if (!t.ok)
|
|
507
|
+
throw new Error(`tab.create failed: ${t.message}`);
|
|
508
|
+
paneId = t.result?.root_pane?.pane_id;
|
|
509
|
+
if (!paneId)
|
|
510
|
+
throw new Error("tab.create returned no pane_id");
|
|
511
|
+
}
|
|
512
|
+
if (opts.command) {
|
|
513
|
+
await herdr("pane.send_text", { pane_id: paneId, text: opts.command });
|
|
514
|
+
await herdr("pane.send_keys", { pane_id: paneId, keys: ["enter"] });
|
|
515
|
+
}
|
|
516
|
+
return { paneId, placement };
|
|
517
|
+
}
|
|
416
518
|
// src/settings/resolve.ts
|
|
417
519
|
import { homedir as homedir4 } from "os";
|
|
418
520
|
import { join as join5 } from "path";
|
|
@@ -482,6 +584,15 @@ var REGISTRY = [
|
|
|
482
584
|
migrated: true,
|
|
483
585
|
description: "Per-repo worktree pool config (onDeck size, ready steps, name pool); root/branchFormat/ready computed-or-empty in the reader."
|
|
484
586
|
},
|
|
587
|
+
{
|
|
588
|
+
key: "rt.worktreeReadyApproval",
|
|
589
|
+
type: "string",
|
|
590
|
+
scopes: ALL_SCOPES,
|
|
591
|
+
merge: "replace",
|
|
592
|
+
repoScoped: true,
|
|
593
|
+
migrated: true,
|
|
594
|
+
description: "Per-repo user approval of a team-authored `ready` shell ladder, as its content hash (RT-89). The reader trusts only user/machine scopes so a team store can never approve its own shell; a hash mismatch after a team edit re-holds the ladder until `rt worktree ready-approve` records the new one."
|
|
595
|
+
},
|
|
485
596
|
{
|
|
486
597
|
key: "rt.repoIdentityOverrides",
|
|
487
598
|
type: "object",
|
|
@@ -627,6 +738,24 @@ var REGISTRY = [
|
|
|
627
738
|
migrated: true,
|
|
628
739
|
description: "Age floor in days for the log janitor pruning every surface's rotated log files under ~/.mattstack/rt/logs (default 14). A fresh key, not an ownership-latch port, so a default is fine here."
|
|
629
740
|
},
|
|
741
|
+
{
|
|
742
|
+
key: "rt.logLevel",
|
|
743
|
+
type: "string",
|
|
744
|
+
scopes: ["machine", "user"],
|
|
745
|
+
default: "info",
|
|
746
|
+
merge: "replace",
|
|
747
|
+
migrated: true,
|
|
748
|
+
description: "Daemon log level (trace|debug|info|warn|error). RT_LOG_LEVEL env wins, then this setting, then info (lib/daemon-logger.ts resolveDaemonLogLevel). A fresh key, not an ownership-latch port, so a default is fine here."
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
key: "rt.apiPort",
|
|
752
|
+
type: "number",
|
|
753
|
+
scopes: ["machine", "user"],
|
|
754
|
+
default: 9401,
|
|
755
|
+
merge: "replace",
|
|
756
|
+
migrated: true,
|
|
757
|
+
description: "TCP port for the daemon's local HTTP/WS API. Escape hatch when 9401 is held: RT_API_PORT env wins, then this setting, then 9401 (lib/daemon-config.ts resolveApiPort(), read at bind time by lib/daemon/api-server.ts)."
|
|
758
|
+
},
|
|
630
759
|
{
|
|
631
760
|
key: "rt.hooks",
|
|
632
761
|
type: "object",
|
|
@@ -636,6 +765,21 @@ var REGISTRY = [
|
|
|
636
765
|
migrated: true,
|
|
637
766
|
description: "Per-repo git hook enable/disable state ({enabled, hooks: {<hookName>: boolean}}); ownership-latch port of repos/<repo>/hooks.json, store wins per field once it owns the key — including per-hook-name entries inside the nested hooks map, each defaulting to enabled when absent. The installed git-hook shim still greps repos/<repo>/hooks.json with zero process spawns (a hook fires on every git operation); that file is now a DERIVED CACHE this key writes through, kept current by commands/hooks.ts's regenerateHooksCache at every write seam."
|
|
638
767
|
},
|
|
768
|
+
{
|
|
769
|
+
key: "rt.daemonPath",
|
|
770
|
+
type: "string",
|
|
771
|
+
scopes: ["machine"],
|
|
772
|
+
merge: "replace",
|
|
773
|
+
description: "Absolute colon-separated PATH the daemon uses for every child it spawns, instead of probing your login shell. Set this when the daemon can't find node/git/bun/pnpm (e.g. a fish shell, a blocking .zshrc, or PATH exports that live only in .zshrc). Machine-scoped: it never travels to another machine."
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
key: "rt.trustedBrowserOrigins",
|
|
777
|
+
type: "array",
|
|
778
|
+
scopes: ["user", "machine"],
|
|
779
|
+
default: [],
|
|
780
|
+
merge: "replace",
|
|
781
|
+
description: "Browser Origins (scheme://host:port, exact string match) trusted to read the :9401 daemon API and subscribe to /ws without presenting the local api-token -- e.g. a locally-hosted console or chat-viewer dev server. Empty by default: every current mattstack consumer (the CLI, the Swift tray, rt-client from Bun/Node processes, the VS Code extension) is a non-browser client (sends no Origin header at all) and is unaffected either way."
|
|
782
|
+
},
|
|
639
783
|
{
|
|
640
784
|
key: "mattstack.integrations",
|
|
641
785
|
type: "object",
|
|
@@ -1296,8 +1440,24 @@ function expandCtxFrom(opts) {
|
|
|
1296
1440
|
teamsDir: teamsDir()
|
|
1297
1441
|
};
|
|
1298
1442
|
}
|
|
1443
|
+
var warnSink = null;
|
|
1444
|
+
var warnedOnce = new Set;
|
|
1445
|
+
function setSettingsWarnSink(sink) {
|
|
1446
|
+
warnSink = sink;
|
|
1447
|
+
warnedOnce.clear();
|
|
1448
|
+
}
|
|
1449
|
+
function emitSettingsWarning(msg) {
|
|
1450
|
+
if (warnSink) {
|
|
1451
|
+
if (warnedOnce.has(msg))
|
|
1452
|
+
return;
|
|
1453
|
+
warnedOnce.add(msg);
|
|
1454
|
+
warnSink(msg);
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
console.warn(msg);
|
|
1458
|
+
}
|
|
1299
1459
|
function warnInvalid(key, entry) {
|
|
1300
|
-
|
|
1460
|
+
emitSettingsWarning(`rt: ignoring "${key}" from the ${entry.scope} scope (${entry.file ?? "no file"}): ${entry.reason}`);
|
|
1301
1461
|
}
|
|
1302
1462
|
function getSetting(key, opts = {}) {
|
|
1303
1463
|
const def = getDef(key);
|
|
@@ -1332,7 +1492,7 @@ function listSettings(opts = {}) {
|
|
|
1332
1492
|
listed.value = expandVariables(resolution.value, ctx);
|
|
1333
1493
|
} catch (err) {
|
|
1334
1494
|
listed.expandError = err.message;
|
|
1335
|
-
|
|
1495
|
+
emitSettingsWarning(`rt: showing "${def.key}" unexpanded — ${listed.expandError}`);
|
|
1336
1496
|
}
|
|
1337
1497
|
}
|
|
1338
1498
|
out.push(listed);
|
|
@@ -1360,7 +1520,7 @@ function listUnregistered(stores, opts) {
|
|
|
1360
1520
|
scan("machine", stores.machine.file, stores.machine.global);
|
|
1361
1521
|
scan("machine.repo", stores.machine.file, repoSection(stores.machine));
|
|
1362
1522
|
return [...found.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, hit]) => {
|
|
1363
|
-
|
|
1523
|
+
emitSettingsWarning(`rt: unregistered setting "${key}" in ${hit.file} — ignoring it (this rt may be older than the store)`);
|
|
1364
1524
|
return {
|
|
1365
1525
|
key,
|
|
1366
1526
|
value: hit.value,
|
|
@@ -1585,21 +1745,42 @@ async function runCapture(argv, opts = {}) {
|
|
|
1585
1745
|
} catch {
|
|
1586
1746
|
return { stdout: "", stderr: "", exitCode: -1 };
|
|
1587
1747
|
}
|
|
1588
|
-
const
|
|
1748
|
+
const timeoutMs = opts.timeoutMs ?? 1e4;
|
|
1749
|
+
let killTimer;
|
|
1750
|
+
const term = setTimeout(() => {
|
|
1589
1751
|
try {
|
|
1590
|
-
proc.kill();
|
|
1752
|
+
proc.kill("SIGTERM");
|
|
1591
1753
|
} catch {}
|
|
1592
|
-
|
|
1754
|
+
killTimer = setTimeout(() => {
|
|
1755
|
+
try {
|
|
1756
|
+
proc.kill("SIGKILL");
|
|
1757
|
+
} catch {}
|
|
1758
|
+
}, 2000);
|
|
1759
|
+
killTimer.unref?.();
|
|
1760
|
+
}, timeoutMs);
|
|
1761
|
+
const captured = (async () => {
|
|
1762
|
+
try {
|
|
1763
|
+
const stdoutPromise = new Response(proc.stdout).text();
|
|
1764
|
+
const stderrPromise = captureStderr ? new Response(proc.stderr).text() : Promise.resolve("");
|
|
1765
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
1766
|
+
stdoutPromise,
|
|
1767
|
+
stderrPromise,
|
|
1768
|
+
proc.exited
|
|
1769
|
+
]);
|
|
1770
|
+
return { stdout, stderr, exitCode };
|
|
1771
|
+
} catch {
|
|
1772
|
+
return { stdout: "", stderr: "", exitCode: -1 };
|
|
1773
|
+
}
|
|
1774
|
+
})();
|
|
1775
|
+
let deadlineTimer;
|
|
1776
|
+
const deadline = new Promise((resolve) => {
|
|
1777
|
+
deadlineTimer = setTimeout(() => resolve({ stdout: "", stderr: "", exitCode: -1, timedOut: true }), timeoutMs);
|
|
1778
|
+
});
|
|
1593
1779
|
try {
|
|
1594
|
-
|
|
1595
|
-
const stderrPromise = captureStderr ? new Response(proc.stderr).text() : Promise.resolve("");
|
|
1596
|
-
const [stdout, stderr] = await Promise.all([stdoutPromise, stderrPromise]);
|
|
1597
|
-
const exitCode = await proc.exited;
|
|
1598
|
-
return { stdout, stderr, exitCode };
|
|
1599
|
-
} catch {
|
|
1600
|
-
return { stdout: "", stderr: "", exitCode: -1 };
|
|
1780
|
+
return await Promise.race([captured, deadline]);
|
|
1601
1781
|
} finally {
|
|
1602
|
-
clearTimeout(
|
|
1782
|
+
clearTimeout(term);
|
|
1783
|
+
clearTimeout(deadlineTimer);
|
|
1603
1784
|
}
|
|
1604
1785
|
}
|
|
1605
1786
|
|
|
@@ -1722,6 +1903,7 @@ export {
|
|
|
1722
1903
|
validateValue,
|
|
1723
1904
|
unsetSetting,
|
|
1724
1905
|
subscribe,
|
|
1906
|
+
setSettingsWarnSink,
|
|
1725
1907
|
setSetting,
|
|
1726
1908
|
serializeIdentity,
|
|
1727
1909
|
rtCommand,
|
|
@@ -1741,6 +1923,7 @@ export {
|
|
|
1741
1923
|
paneFocus,
|
|
1742
1924
|
paneDirectories,
|
|
1743
1925
|
paneAccounts,
|
|
1926
|
+
openSmartPane,
|
|
1744
1927
|
normalizeRemote,
|
|
1745
1928
|
listTeams,
|
|
1746
1929
|
listSettings,
|
|
@@ -1754,6 +1937,7 @@ export {
|
|
|
1754
1937
|
expandVariables,
|
|
1755
1938
|
eventsHead,
|
|
1756
1939
|
deriveRepoIdentity,
|
|
1940
|
+
decidePlacement,
|
|
1757
1941
|
daemonHealth,
|
|
1758
1942
|
createRelay,
|
|
1759
1943
|
clearIdentityMemo,
|
|
@@ -1761,6 +1945,7 @@ export {
|
|
|
1761
1945
|
chatSignOut,
|
|
1762
1946
|
chatSignIn,
|
|
1763
1947
|
chatRooms,
|
|
1948
|
+
chatRelease,
|
|
1764
1949
|
chatRead,
|
|
1765
1950
|
chatPost,
|
|
1766
1951
|
chatMessages,
|
|
@@ -1770,10 +1955,12 @@ export {
|
|
|
1770
1955
|
chatInvite,
|
|
1771
1956
|
chatDmOpen,
|
|
1772
1957
|
chatDm,
|
|
1958
|
+
chatClaim,
|
|
1773
1959
|
chatBuddies,
|
|
1774
1960
|
chatBack,
|
|
1775
1961
|
chatAway,
|
|
1776
1962
|
chatArchive,
|
|
1963
|
+
chatAck,
|
|
1777
1964
|
allDefs,
|
|
1778
1965
|
agentStart,
|
|
1779
1966
|
agentResume,
|
package/dist/settings/exec.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface RunResult {
|
|
|
11
11
|
stdout: string;
|
|
12
12
|
stderr: string;
|
|
13
13
|
exitCode: number;
|
|
14
|
+
/** Set true only when the deadline fired before the child settled. */
|
|
15
|
+
timedOut?: boolean;
|
|
14
16
|
}
|
|
15
17
|
/**
|
|
16
18
|
* Run argv and capture stdout. Never throws: spawn failures and timeouts
|
|
@@ -120,6 +120,11 @@ export interface ExpandCtx {
|
|
|
120
120
|
* mutates its input.
|
|
121
121
|
*/
|
|
122
122
|
export declare function expandVariables(value: unknown, ctx: ExpandCtx): unknown;
|
|
123
|
+
/** The daemon binds a deduped log.warn here so a hot-path getSetting on a
|
|
124
|
+
* disallowed-scope key warns once, not every tick. Default: console.warn
|
|
125
|
+
* (CLI/test behavior unchanged). null restores the default. */
|
|
126
|
+
export declare function setSettingsWarnSink(sink: ((msg: string) => void) | null): void;
|
|
127
|
+
export declare function emitSettingsWarning(msg: string): void;
|
|
123
128
|
/**
|
|
124
129
|
* Resolves one key across the whole ladder. Throws for an unregistered key —
|
|
125
130
|
* an explicit get of something rt has never heard of is a caller bug, not a
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout-smart herdr pane placement. `decidePlacement` is pure (geometry ->
|
|
3
|
+
* where a new pane should go); `openSmartPane` reads an anchor pane's real
|
|
4
|
+
* rect, decides, and performs the herdr call through an injected caller so it
|
|
5
|
+
* works over any transport (socket API or a CLI adapter).
|
|
6
|
+
*/
|
|
7
|
+
export type Placement = {
|
|
8
|
+
kind: "split";
|
|
9
|
+
direction: "right" | "down";
|
|
10
|
+
} | {
|
|
11
|
+
kind: "tab";
|
|
12
|
+
};
|
|
13
|
+
export interface PlacementOpts {
|
|
14
|
+
minCols?: number;
|
|
15
|
+
minRows?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface HerdrCall {
|
|
18
|
+
(method: string, params: Record<string, unknown>): Promise<{
|
|
19
|
+
ok: true;
|
|
20
|
+
result: any;
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Where to put a new pane relative to an anchor whose rect (in cells) is given.
|
|
29
|
+
* Cells are ~2:1 (height:width in px), so a pane is visually wider than tall
|
|
30
|
+
* when width > 2*height; split the longer visual axis so both halves stay
|
|
31
|
+
* usable. A null/degenerate rect falls back to a right split (old behavior).
|
|
32
|
+
*/
|
|
33
|
+
export declare function decidePlacement(rect: {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
} | null, opts?: PlacementOpts): Placement;
|
|
37
|
+
/**
|
|
38
|
+
* Open a herdr pane placed intelligently next to `anchorPaneId` (split right or
|
|
39
|
+
* down, or a new tab when crowded). Optionally run `command` in it. Returns the
|
|
40
|
+
* new pane id and the placement chosen. Throws on a herdr failure.
|
|
41
|
+
*/
|
|
42
|
+
export declare function openSmartPane(herdr: HerdrCall, anchorPaneId: string, opts?: {
|
|
43
|
+
command?: string;
|
|
44
|
+
focus?: boolean;
|
|
45
|
+
} & PlacementOpts): Promise<{
|
|
46
|
+
paneId: string;
|
|
47
|
+
placement: Placement;
|
|
48
|
+
}>;
|
package/dist/transport.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ export interface RtResponse<T = unknown> {
|
|
|
2
2
|
ok: boolean;
|
|
3
3
|
data?: T;
|
|
4
4
|
error?: string;
|
|
5
|
+
/** Structured form of `error` on a handler throw (R035): `code` defaults
|
|
6
|
+
* to "handler-threw" when the thrown error carries none. Additive; older
|
|
7
|
+
* daemons and the reject path never set this. */
|
|
8
|
+
failure?: {
|
|
9
|
+
code: string;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
5
12
|
}
|
|
6
13
|
export interface RtClientOptions {
|
|
7
14
|
sockPath?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mattstack/rt-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -51,5 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"typescript": "^5.9.2"
|
|
54
57
|
}
|
|
55
58
|
}
|
package/src/client.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
WakeMode,
|
|
19
19
|
ChatMember,
|
|
20
20
|
ChatMessage,
|
|
21
|
+
ChatClaimOutcome,
|
|
21
22
|
RoomSummary,
|
|
22
23
|
BuddyStatus,
|
|
23
24
|
PresenceRow,
|
|
@@ -166,12 +167,32 @@ export function chatLeave(
|
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
export function chatPost(
|
|
169
|
-
a: { room: string; handle: string; body: string; mentions?: string[] },
|
|
170
|
+
a: { room: string; handle: string; body: string; mentions?: string[]; quiet?: boolean },
|
|
170
171
|
o: RtClientOptions = {},
|
|
171
|
-
): Promise<RtResponse<{ id: number; recipients: string[] }>> {
|
|
172
|
+
): Promise<RtResponse<{ id: number; recipients: string[]; others: number }>> {
|
|
172
173
|
const payload: Record<string, unknown> = { room: a.room, handle: a.handle, body: a.body };
|
|
173
174
|
if (a.mentions !== undefined) payload.mentions = a.mentions;
|
|
174
|
-
|
|
175
|
+
if (a.quiet) payload.quiet = true;
|
|
176
|
+
return rtCommand<{ id: number; recipients: string[]; others: number }>("chat:post", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function chatAck(
|
|
180
|
+
a: { id: number; handle: string },
|
|
181
|
+
o: RtClientOptions = {},
|
|
182
|
+
): Promise<RtResponse<{ author: string; room: string; already: boolean }>> {
|
|
183
|
+
return rtCommand<{ author: string; room: string; already: boolean }>(
|
|
184
|
+
"chat:ack",
|
|
185
|
+
{ id: a.id, handle: a.handle },
|
|
186
|
+
{ sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 },
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function chatClaim(a: { id: number; handle: string }, o: RtClientOptions = {}): Promise<RtResponse<ChatClaimOutcome>> {
|
|
191
|
+
return rtCommand<ChatClaimOutcome>("chat:claim", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function chatRelease(a: { id: number; handle: string }, o: RtClientOptions = {}): Promise<RtResponse<{ holder: string }>> {
|
|
195
|
+
return rtCommand<{ holder: string }>("chat:release", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
|
|
175
196
|
}
|
|
176
197
|
|
|
177
198
|
export function chatRead(
|