@mattstack/rt-client 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +88 -1
- package/dist/commands.d.ts +180 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +278 -21
- package/dist/repos.d.ts +9 -3
- package/dist/settings/identity.d.ts +43 -19
- package/package.json +1 -1
- package/src/client.ts +141 -1
- package/src/commands.ts +91 -1
- package/src/index.ts +37 -2
- package/src/repos.ts +85 -14
- package/src/settings/identity.ts +121 -28
- package/src/settings/registry-defs.ts +43 -6
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
+
|
|
1
4
|
// src/transport.ts
|
|
2
5
|
import { homedir } from "os";
|
|
3
6
|
import { join } from "path";
|
|
@@ -39,6 +42,93 @@ function readMrsByBranch(repoName, branches, opts = {}) {
|
|
|
39
42
|
function resolveForgeToken(repoName, forge, opts = {}) {
|
|
40
43
|
return rtCommand("secrets:forge-token", { repoName, forge }, { sockPath: opts.sockPath, timeoutMs: 1e4 });
|
|
41
44
|
}
|
|
45
|
+
function listRuns(repo, opts = {}) {
|
|
46
|
+
const payload = {};
|
|
47
|
+
if (repo !== undefined)
|
|
48
|
+
payload.repo = repo;
|
|
49
|
+
return rtCommand("runs:list", payload, { sockPath: opts.sockPath, timeoutMs: 1e4 });
|
|
50
|
+
}
|
|
51
|
+
function getRun(runId, repo, opts = {}) {
|
|
52
|
+
const payload = { runId };
|
|
53
|
+
if (repo !== undefined)
|
|
54
|
+
payload.repo = repo;
|
|
55
|
+
return rtCommand("runs:get", payload, { sockPath: opts.sockPath, timeoutMs: 1e4 });
|
|
56
|
+
}
|
|
57
|
+
function abandonRun(runId, repo, reason, opts = {}) {
|
|
58
|
+
const payload = { runId };
|
|
59
|
+
if (repo !== undefined)
|
|
60
|
+
payload.repo = repo;
|
|
61
|
+
if (reason !== undefined)
|
|
62
|
+
payload.reason = reason;
|
|
63
|
+
return rtCommand("runs:abandon", payload, { sockPath: opts.sockPath, timeoutMs: 1e4 });
|
|
64
|
+
}
|
|
65
|
+
function chatJoin(a, o = {}) {
|
|
66
|
+
const payload = { room: a.room, handle: a.handle };
|
|
67
|
+
if (a.wakeOn !== undefined)
|
|
68
|
+
payload.wakeOn = a.wakeOn;
|
|
69
|
+
if (a.cwd !== undefined)
|
|
70
|
+
payload.cwd = a.cwd;
|
|
71
|
+
if (a.pane !== undefined)
|
|
72
|
+
payload.pane = a.pane;
|
|
73
|
+
return rtCommand("chat:join", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
74
|
+
}
|
|
75
|
+
function chatLeave(a, o = {}) {
|
|
76
|
+
return rtCommand("chat:leave", { room: a.room, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
77
|
+
}
|
|
78
|
+
function chatPost(a, o = {}) {
|
|
79
|
+
return rtCommand("chat:post", { room: a.room, handle: a.handle, body: a.body }, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
80
|
+
}
|
|
81
|
+
function chatRead(a, o = {}) {
|
|
82
|
+
const payload = { handle: a.handle };
|
|
83
|
+
if (a.room !== undefined)
|
|
84
|
+
payload.room = a.room;
|
|
85
|
+
if (a.limit !== undefined)
|
|
86
|
+
payload.limit = a.limit;
|
|
87
|
+
if (a.sinceMs !== undefined)
|
|
88
|
+
payload.sinceMs = a.sinceMs;
|
|
89
|
+
return rtCommand("chat:read", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
90
|
+
}
|
|
91
|
+
function chatRooms(a, o = {}) {
|
|
92
|
+
return rtCommand("chat:rooms", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
93
|
+
}
|
|
94
|
+
function chatWho(a, o = {}) {
|
|
95
|
+
return rtCommand("chat:who", { room: a.room }, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
96
|
+
}
|
|
97
|
+
function chatMark(a, o = {}) {
|
|
98
|
+
const payload = { handle: a.handle };
|
|
99
|
+
if (a.room !== undefined)
|
|
100
|
+
payload.room = a.room;
|
|
101
|
+
return rtCommand("chat:mark", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
102
|
+
}
|
|
103
|
+
function chatMessages(a, o = {}) {
|
|
104
|
+
const payload = { room: a.room };
|
|
105
|
+
if (a.before !== undefined)
|
|
106
|
+
payload.before = a.before;
|
|
107
|
+
if (a.limit !== undefined)
|
|
108
|
+
payload.limit = a.limit;
|
|
109
|
+
return rtCommand("chat:messages", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
110
|
+
}
|
|
111
|
+
function chatArm(a, o = {}) {
|
|
112
|
+
const payload = { handle: a.handle };
|
|
113
|
+
if (a.room !== undefined)
|
|
114
|
+
payload.room = a.room;
|
|
115
|
+
return rtCommand("chat:arm", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
116
|
+
}
|
|
117
|
+
function chatTouch(a, o = {}) {
|
|
118
|
+
return rtCommand("chat:touch", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
119
|
+
}
|
|
120
|
+
function chatDisarm(a, o = {}) {
|
|
121
|
+
return rtCommand("chat:disarm", { handle: a.handle }, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
122
|
+
}
|
|
123
|
+
function chatUnreadWaking(a, o = {}) {
|
|
124
|
+
const payload = { handle: a.handle };
|
|
125
|
+
if (a.room !== undefined)
|
|
126
|
+
payload.room = a.room;
|
|
127
|
+
return rtCommand("chat:unread-waking", payload, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
128
|
+
}
|
|
129
|
+
function eventsHead(o = {}) {
|
|
130
|
+
return rtCommand("events:head", {}, { sockPath: o.sockPath, timeoutMs: 1e4 });
|
|
131
|
+
}
|
|
42
132
|
// src/commands.ts
|
|
43
133
|
var COMMAND_NAMES = [
|
|
44
134
|
"project-mrs:read",
|
|
@@ -49,8 +139,22 @@ var COMMAND_NAMES = [
|
|
|
49
139
|
"events:emit",
|
|
50
140
|
"events:wait",
|
|
51
141
|
"events:list",
|
|
142
|
+
"events:head",
|
|
52
143
|
"runs:list",
|
|
53
|
-
"runs:get"
|
|
144
|
+
"runs:get",
|
|
145
|
+
"runs:abandon",
|
|
146
|
+
"chat:join",
|
|
147
|
+
"chat:leave",
|
|
148
|
+
"chat:post",
|
|
149
|
+
"chat:read",
|
|
150
|
+
"chat:rooms",
|
|
151
|
+
"chat:who",
|
|
152
|
+
"chat:mark",
|
|
153
|
+
"chat:messages",
|
|
154
|
+
"chat:arm",
|
|
155
|
+
"chat:touch",
|
|
156
|
+
"chat:disarm",
|
|
157
|
+
"chat:unread-waking"
|
|
54
158
|
];
|
|
55
159
|
// src/relay.ts
|
|
56
160
|
var DEFAULT_WS_URL = "ws://127.0.0.1:9401/ws";
|
|
@@ -98,16 +202,48 @@ function subscribe(onEvent, opts = {}) {
|
|
|
98
202
|
// src/repos.ts
|
|
99
203
|
import { existsSync, readFileSync } from "fs";
|
|
100
204
|
import { homedir as homedir2 } from "os";
|
|
101
|
-
import { join as join2 } from "path";
|
|
205
|
+
import { dirname, join as join2 } from "path";
|
|
102
206
|
function defaultReposJsonPath() {
|
|
103
207
|
return join2(homedir2(), ".mattstack", "rt", "repos.json");
|
|
104
208
|
}
|
|
105
|
-
function
|
|
106
|
-
const path = reposJsonPath ?? defaultReposJsonPath();
|
|
209
|
+
function loadBunSqliteDatabase() {
|
|
107
210
|
try {
|
|
108
|
-
|
|
211
|
+
return __require("bun:sqlite").Database;
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function repoNameFromStateDb(repoPath, dbPath) {
|
|
217
|
+
if (!existsSync(dbPath))
|
|
218
|
+
return null;
|
|
219
|
+
const DatabaseCtor = loadBunSqliteDatabase();
|
|
220
|
+
if (!DatabaseCtor)
|
|
221
|
+
return null;
|
|
222
|
+
try {
|
|
223
|
+
const db = new DatabaseCtor(dbPath, { readonly: true });
|
|
224
|
+
try {
|
|
225
|
+
const rows = db.query("SELECT k, v FROM kv WHERE ns = 'repo-index';").all();
|
|
226
|
+
for (const row of rows) {
|
|
227
|
+
try {
|
|
228
|
+
if (JSON.parse(row.v) === repoPath)
|
|
229
|
+
return row.k;
|
|
230
|
+
} catch {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
} finally {
|
|
236
|
+
db.close();
|
|
237
|
+
}
|
|
238
|
+
} catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function repoNameFromJson(repoPath, reposJsonPath) {
|
|
243
|
+
try {
|
|
244
|
+
if (!existsSync(reposJsonPath))
|
|
109
245
|
return null;
|
|
110
|
-
const raw = readFileSync(
|
|
246
|
+
const raw = readFileSync(reposJsonPath, "utf8");
|
|
111
247
|
const index = JSON.parse(raw);
|
|
112
248
|
for (const [repoName, value] of Object.entries(index)) {
|
|
113
249
|
if (value === repoPath)
|
|
@@ -118,6 +254,14 @@ function repoNameForPath(repoPath, reposJsonPath) {
|
|
|
118
254
|
return null;
|
|
119
255
|
}
|
|
120
256
|
}
|
|
257
|
+
function repoNameForPath(repoPath, reposJsonPath) {
|
|
258
|
+
const jsonPath = reposJsonPath ?? defaultReposJsonPath();
|
|
259
|
+
const dbPath = join2(dirname(jsonPath), "state.db");
|
|
260
|
+
const fromDb = repoNameFromStateDb(repoPath, dbPath);
|
|
261
|
+
if (fromDb !== null)
|
|
262
|
+
return fromDb;
|
|
263
|
+
return repoNameFromJson(repoPath, jsonPath);
|
|
264
|
+
}
|
|
121
265
|
// src/settings/resolve.ts
|
|
122
266
|
import { homedir as homedir4 } from "os";
|
|
123
267
|
import { join as join5 } from "path";
|
|
@@ -278,7 +422,7 @@ var REGISTRY = [
|
|
|
278
422
|
merge: "deep",
|
|
279
423
|
repoScoped: true,
|
|
280
424
|
migrated: true,
|
|
281
|
-
description: "Branch-naming templates.
|
|
425
|
+
description: "Branch-naming templates, repoScoped. Read by the VS Code extension (extensions/vscode/rt-context), which lazily imports the legacy repos/<repo>/branch-naming.json into this key on first read; rt itself has no CLI-side reader."
|
|
282
426
|
},
|
|
283
427
|
{
|
|
284
428
|
key: "rt.variations",
|
|
@@ -338,9 +482,8 @@ var REGISTRY = [
|
|
|
338
482
|
scopes: ALL_SCOPES,
|
|
339
483
|
merge: "deep",
|
|
340
484
|
repoScoped: true,
|
|
341
|
-
migrated:
|
|
342
|
-
|
|
343
|
-
description: "User-defined lifecycle hooks rt runs around commands (pre/post command scripts)."
|
|
485
|
+
migrated: true,
|
|
486
|
+
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."
|
|
344
487
|
},
|
|
345
488
|
{
|
|
346
489
|
key: "mattstack.integrations",
|
|
@@ -363,6 +506,14 @@ var REGISTRY = [
|
|
|
363
506
|
merge: "replace",
|
|
364
507
|
description: "Absolute path to the installed mattstack.app bundle, written by the app at launch so rt stops hardcoding ~/Applications."
|
|
365
508
|
},
|
|
509
|
+
{
|
|
510
|
+
key: "rt.integrations",
|
|
511
|
+
type: "object",
|
|
512
|
+
scopes: ["user"],
|
|
513
|
+
merge: "deep",
|
|
514
|
+
migrated: true,
|
|
515
|
+
description: "User-confirmed integration hosts (forgeHost, switchboardUrl), written only by an explicit `rt setup <id> connect --host` after that host validates a real credential. The one trusted source a credential is ever sent to — mattstack.integrations' team-declared host is shown to the user but never auto-used for a fetch."
|
|
516
|
+
},
|
|
366
517
|
{
|
|
367
518
|
key: "claude.marketplaces",
|
|
368
519
|
type: "array",
|
|
@@ -551,6 +702,35 @@ var REGISTRY = [
|
|
|
551
702
|
scopes: ["machine"],
|
|
552
703
|
merge: "deep",
|
|
553
704
|
description: "gitq checkout-board config: tracked repos, local port, and the herdr workspace it launches into."
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
key: "chat.handle",
|
|
708
|
+
type: "string",
|
|
709
|
+
scopes: ["user"],
|
|
710
|
+
merge: "replace",
|
|
711
|
+
description: "Explicit rt chat handle for this developer on this machine; overrides the derived <repo>-<dir> handle when set."
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
key: "chat.humanHandle",
|
|
715
|
+
type: "string",
|
|
716
|
+
scopes: ["user"],
|
|
717
|
+
default: "matt",
|
|
718
|
+
merge: "replace",
|
|
719
|
+
description: "The human's own chat handle, so agents can @-mention them by name."
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
key: "chat.push.provider",
|
|
723
|
+
type: "string",
|
|
724
|
+
scopes: ["user"],
|
|
725
|
+
merge: "replace",
|
|
726
|
+
description: "Push notification provider used to alert the human of chat mentions when away from a terminal."
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
key: "chat.push.target",
|
|
730
|
+
type: "string",
|
|
731
|
+
scopes: ["user"],
|
|
732
|
+
merge: "replace",
|
|
733
|
+
description: "Destination (topic/URL/token) the configured chat.push.provider sends to."
|
|
554
734
|
}
|
|
555
735
|
];
|
|
556
736
|
|
|
@@ -992,7 +1172,7 @@ function explainSetting(key, opts = {}) {
|
|
|
992
1172
|
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync4, renameSync, unlinkSync, writeFileSync } from "fs";
|
|
993
1173
|
import { applyEdits, modify, parseTree } from "jsonc-parser";
|
|
994
1174
|
import { randomBytes } from "crypto";
|
|
995
|
-
import { dirname } from "path";
|
|
1175
|
+
import { dirname as dirname2 } from "path";
|
|
996
1176
|
var FORMAT = { tabSize: 2, insertSpaces: true, eol: `
|
|
997
1177
|
` };
|
|
998
1178
|
function refuse(message) {
|
|
@@ -1101,7 +1281,7 @@ function writeIntoStore(storePath, jsonPath, value, createIfMissing) {
|
|
|
1101
1281
|
if (!createIfMissing) {
|
|
1102
1282
|
refuse(`store file ${storePath} does not exist`);
|
|
1103
1283
|
}
|
|
1104
|
-
mkdirSync(
|
|
1284
|
+
mkdirSync(dirname2(storePath), { recursive: true });
|
|
1105
1285
|
content = seedHeader();
|
|
1106
1286
|
}
|
|
1107
1287
|
const edits = modify(content, jsonPath, value, { formattingOptions: FORMAT });
|
|
@@ -1120,6 +1300,9 @@ function writeIntoStore(storePath, jsonPath, value, createIfMissing) {
|
|
|
1120
1300
|
throw err;
|
|
1121
1301
|
}
|
|
1122
1302
|
}
|
|
1303
|
+
// src/settings/identity.ts
|
|
1304
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync } from "fs";
|
|
1305
|
+
|
|
1123
1306
|
// src/settings/exec.ts
|
|
1124
1307
|
async function runCapture(argv, opts = {}) {
|
|
1125
1308
|
const captureStderr = opts.stderr === "pipe";
|
|
@@ -1156,6 +1339,27 @@ async function runCapture(argv, opts = {}) {
|
|
|
1156
1339
|
// src/settings/identity.ts
|
|
1157
1340
|
var URL_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/(?:[^@/]+@)?([^/]+)\/(.+)$/;
|
|
1158
1341
|
var SCP_RE = /^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/;
|
|
1342
|
+
function serializeIdentity(id) {
|
|
1343
|
+
return `${id.kind}:${encodeURIComponent(id.id)}`;
|
|
1344
|
+
}
|
|
1345
|
+
function parseIdentity(wire) {
|
|
1346
|
+
const colon = wire.indexOf(":");
|
|
1347
|
+
if (colon === -1)
|
|
1348
|
+
return null;
|
|
1349
|
+
const kind = wire.slice(0, colon);
|
|
1350
|
+
if (kind !== "remote" && kind !== "path")
|
|
1351
|
+
return null;
|
|
1352
|
+
const encoded = wire.slice(colon + 1);
|
|
1353
|
+
let id;
|
|
1354
|
+
try {
|
|
1355
|
+
id = decodeURIComponent(encoded);
|
|
1356
|
+
} catch {
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
if (encodeURIComponent(id) !== encoded)
|
|
1360
|
+
return null;
|
|
1361
|
+
return { kind, id };
|
|
1362
|
+
}
|
|
1159
1363
|
function normalizeRemote(remote) {
|
|
1160
1364
|
const trimmed = remote.trim();
|
|
1161
1365
|
if (!trimmed)
|
|
@@ -1186,9 +1390,10 @@ function identityFromRemote(remote) {
|
|
|
1186
1390
|
if (overrides !== null && typeof overrides === "object" && !Array.isArray(overrides)) {
|
|
1187
1391
|
const hit = overrides[remote];
|
|
1188
1392
|
if (typeof hit === "string")
|
|
1189
|
-
return hit;
|
|
1393
|
+
return { kind: "remote", id: hit };
|
|
1190
1394
|
}
|
|
1191
|
-
|
|
1395
|
+
const normalized = normalizeRemote(remote);
|
|
1396
|
+
return normalized === null ? null : { kind: "remote", id: normalized };
|
|
1192
1397
|
}
|
|
1193
1398
|
var memo = new Map;
|
|
1194
1399
|
async function deriveRepoIdentity(repoPath) {
|
|
@@ -1197,43 +1402,95 @@ async function deriveRepoIdentity(repoPath) {
|
|
|
1197
1402
|
return cached;
|
|
1198
1403
|
const result = await (async () => {
|
|
1199
1404
|
const spawned = await runCapture(["git", "-C", repoPath, "config", "--get", "remote.origin.url"]);
|
|
1200
|
-
if (spawned.exitCode
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1405
|
+
if (spawned.exitCode === 0) {
|
|
1406
|
+
const remote = spawned.stdout.trim();
|
|
1407
|
+
const fromRemote = remote ? identityFromRemote(remote) : null;
|
|
1408
|
+
if (fromRemote)
|
|
1409
|
+
return fromRemote;
|
|
1410
|
+
}
|
|
1411
|
+
const listed = await runCapture(["git", "-C", repoPath, "worktree", "list", "--porcelain"]);
|
|
1412
|
+
const first = listed.exitCode === 0 ? /^worktree (.+)$/m.exec(listed.stdout)?.[1]?.trim() : undefined;
|
|
1413
|
+
let base;
|
|
1414
|
+
if (first) {
|
|
1415
|
+
const top = await runCapture(["git", "-C", first, "rev-parse", "--show-toplevel"]);
|
|
1416
|
+
if (top.exitCode === 0 && top.stdout.trim())
|
|
1417
|
+
base = top.stdout.trim();
|
|
1418
|
+
}
|
|
1419
|
+
if (!base) {
|
|
1420
|
+
const own = await runCapture(["git", "-C", repoPath, "rev-parse", "--show-toplevel"]);
|
|
1421
|
+
base = own.exitCode === 0 && own.stdout.trim() ? own.stdout.trim() : repoPath;
|
|
1422
|
+
}
|
|
1423
|
+
return { kind: "path", id: safeRealpath(base) };
|
|
1206
1424
|
})();
|
|
1207
|
-
if (result
|
|
1425
|
+
if (result.kind === "remote")
|
|
1208
1426
|
memo.set(repoPath, Promise.resolve(result));
|
|
1209
1427
|
return result;
|
|
1210
1428
|
}
|
|
1211
1429
|
function clearIdentityMemo() {
|
|
1212
1430
|
memo.clear();
|
|
1213
1431
|
}
|
|
1432
|
+
function safeRealpath(p) {
|
|
1433
|
+
try {
|
|
1434
|
+
return realpathSync(p);
|
|
1435
|
+
} catch {
|
|
1436
|
+
return p;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
async function resolveNameToIdentity(name, reposJsonPath) {
|
|
1440
|
+
if (!existsSync4(reposJsonPath))
|
|
1441
|
+
return null;
|
|
1442
|
+
try {
|
|
1443
|
+
const index = JSON.parse(readFileSync5(reposJsonPath, "utf8"));
|
|
1444
|
+
const path = index[name];
|
|
1445
|
+
if (typeof path !== "string")
|
|
1446
|
+
return null;
|
|
1447
|
+
return await deriveRepoIdentity(path);
|
|
1448
|
+
} catch {
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1214
1452
|
export {
|
|
1215
1453
|
validateValue,
|
|
1216
1454
|
subscribe,
|
|
1217
1455
|
setSetting,
|
|
1456
|
+
serializeIdentity,
|
|
1218
1457
|
rtCommand,
|
|
1458
|
+
resolveNameToIdentity,
|
|
1219
1459
|
resolveForgeToken,
|
|
1220
1460
|
repoNameForPath,
|
|
1221
1461
|
readStore,
|
|
1222
1462
|
readProjectMRs,
|
|
1223
1463
|
readMrsByBranch,
|
|
1224
1464
|
readDiscussions,
|
|
1465
|
+
parseIdentity,
|
|
1225
1466
|
normalizeRemote,
|
|
1226
1467
|
listTeams,
|
|
1227
1468
|
listSettings,
|
|
1469
|
+
listRuns,
|
|
1228
1470
|
isMigrated,
|
|
1229
1471
|
identityFromRemote,
|
|
1230
1472
|
getSetting,
|
|
1473
|
+
getRun,
|
|
1231
1474
|
getDef,
|
|
1232
1475
|
explainSetting,
|
|
1233
1476
|
expandVariables,
|
|
1477
|
+
eventsHead,
|
|
1234
1478
|
deriveRepoIdentity,
|
|
1235
1479
|
clearIdentityMemo,
|
|
1480
|
+
chatWho,
|
|
1481
|
+
chatUnreadWaking,
|
|
1482
|
+
chatTouch,
|
|
1483
|
+
chatRooms,
|
|
1484
|
+
chatRead,
|
|
1485
|
+
chatPost,
|
|
1486
|
+
chatMessages,
|
|
1487
|
+
chatMark,
|
|
1488
|
+
chatLeave,
|
|
1489
|
+
chatJoin,
|
|
1490
|
+
chatDisarm,
|
|
1491
|
+
chatArm,
|
|
1236
1492
|
allDefs,
|
|
1493
|
+
abandonRun,
|
|
1237
1494
|
SCOPE_ORDER,
|
|
1238
1495
|
REGISTRY,
|
|
1239
1496
|
DEFAULT_WS_URL,
|
package/dist/repos.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Exact-match lookup: returns the repo name whose recorded path equals
|
|
3
|
-
* `repoPath`, or null if
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* `repoPath`, or null if no source has a match. Never throws -- a
|
|
4
|
+
* resolution failure just means the caller falls back to whatever it had
|
|
5
|
+
* before (an unqualified path, a prompt, etc).
|
|
6
|
+
*
|
|
7
|
+
* Prefers state.db (authoritative, kept live by every rt process); falls
|
|
8
|
+
* back to the repos.json compat mirror when state.db is unreachable — a
|
|
9
|
+
* pre-upgrade rt install, a non-Bun consumer, or a state.db this process
|
|
10
|
+
* can't open. `reposJsonPath`, when passed, also relocates the state.db
|
|
11
|
+
* lookup: both files live side by side under the same rt data directory.
|
|
6
12
|
*/
|
|
7
13
|
export declare function repoNameForPath(repoPath: string, reposJsonPath?: string): string | null;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Repo identity: the
|
|
3
|
-
*
|
|
2
|
+
* Repo identity: the tagged value that keys `repos.<identity>` sections in
|
|
3
|
+
* every settings store.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* `remote.origin.url` —
|
|
7
|
-
* independent
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* A `remote`-kind identity is `host/path` (lowercase host, path case
|
|
6
|
+
* preserved), derived from `remote.origin.url` — checkout-location
|
|
7
|
+
* independent, since every worktree of a repo shares the same remote. When
|
|
8
|
+
* no usable remote exists, identity falls back to `path`-kind: the realpath
|
|
9
|
+
* of the *main* worktree, which is still shared across that repo's linked
|
|
10
|
+
* worktrees (see `deriveRepoIdentity`) even though it is filesystem-bound.
|
|
11
|
+
* `deriveRepoIdentity` therefore never returns null — every repo has at
|
|
12
|
+
* least a path-kind identity.
|
|
12
13
|
*
|
|
13
14
|
* Three entry points:
|
|
14
15
|
* - `normalizeRemote` is the pure string transform, no I/O.
|
|
@@ -24,6 +25,20 @@
|
|
|
24
25
|
* through `identityFromRemote`, memoized per path so repeated callers in
|
|
25
26
|
* one process don't re-spawn git.
|
|
26
27
|
*/
|
|
28
|
+
export type RepoIdentity = {
|
|
29
|
+
kind: "remote";
|
|
30
|
+
id: string;
|
|
31
|
+
} | {
|
|
32
|
+
kind: "path";
|
|
33
|
+
id: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The wire form crosses the daemon socket, sits in board config, and lands in
|
|
37
|
+
* console's `/runs/:repo/...` URL — all of which need one slash-free segment.
|
|
38
|
+
* `encodeURIComponent` guarantees that and is exactly reversible.
|
|
39
|
+
*/
|
|
40
|
+
export declare function serializeIdentity(id: RepoIdentity): string;
|
|
41
|
+
export declare function parseIdentity(wire: string): RepoIdentity | null;
|
|
27
42
|
/**
|
|
28
43
|
* Pure normalization: `remote` → `host/path` (lowercase host, `.git` and
|
|
29
44
|
* embedded credentials stripped) or null when the remote doesn't match a
|
|
@@ -36,21 +51,30 @@ export declare function normalizeRemote(remote: string): string | null;
|
|
|
36
51
|
* Reads the machine store fresh each call (files are small; store reads are
|
|
37
52
|
* not memoized anywhere in the resolver design).
|
|
38
53
|
*/
|
|
39
|
-
export declare function identityFromRemote(remote: string):
|
|
54
|
+
export declare function identityFromRemote(remote: string): RepoIdentity | null;
|
|
40
55
|
/**
|
|
41
56
|
* Async derivation from a repo path: `git -C <repoPath> config --get
|
|
42
57
|
* remote.origin.url`, then identityFromRemote (so overrides apply to
|
|
43
58
|
* derivation too). Never a sync spawn — safe to call from daemon contexts.
|
|
59
|
+
* Never returns null: no usable remote falls back to a path-kind identity
|
|
60
|
+
* (the main worktree's realpath, via `git worktree list`, so every linked
|
|
61
|
+
* worktree of one repo still shares the same identity).
|
|
44
62
|
*
|
|
45
|
-
* Only a
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* and
|
|
51
|
-
*
|
|
52
|
-
*
|
|
63
|
+
* Only a `remote`-kind result is memoized, for the life of the process; a
|
|
64
|
+
* remote change after that first success is NOT picked up until
|
|
65
|
+
* clearIdentityMemo() — documented behavior, not a bug (see spec: derivation
|
|
66
|
+
* is a one-time capture per process, not a live poll). A `path`-kind result
|
|
67
|
+
* is never cached and is retried on every subsequent call — cheap to
|
|
68
|
+
* recompute, and a caller racing repo provisioning (mid-clone,
|
|
69
|
+
* daemon-startup, a remote added after the fact) must not permanently lose
|
|
70
|
+
* the chance to pick up a real remote just because it asked too early.
|
|
53
71
|
*/
|
|
54
|
-
export declare function deriveRepoIdentity(repoPath: string): Promise<
|
|
72
|
+
export declare function deriveRepoIdentity(repoPath: string): Promise<RepoIdentity>;
|
|
55
73
|
/** Test-only: clear the derivation memo so a test can force re-derivation. */
|
|
56
74
|
export declare function clearIdentityMemo(): void;
|
|
75
|
+
/**
|
|
76
|
+
* One-shot helper for rewriting board's existing name-valued config to
|
|
77
|
+
* host/path identities. NOT a runtime path — the daemon never calls it.
|
|
78
|
+
* Resolves a repo name to the identity of the path it points at in repos.json.
|
|
79
|
+
*/
|
|
80
|
+
export declare function resolveNameToIdentity(name: string, reposJsonPath: string): Promise<RepoIdentity | null>;
|