@mattstack/rt-client 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +62 -1
- package/dist/commands.d.ts +149 -1
- package/dist/health.d.ts +11 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +118 -15
- package/dist/relay.d.ts +15 -0
- package/dist/settings/identity-codec.d.ts +28 -0
- package/dist/settings/identity-codec.js +53 -0
- package/dist/settings/identity.d.ts +3 -20
- package/dist/transport.d.ts +9 -0
- package/package.json +8 -2
- package/src/client.ts +110 -17
- package/src/commands.ts +85 -5
- package/src/health.ts +15 -0
- package/src/index.ts +14 -1
- package/src/relay.ts +31 -0
- package/src/settings/identity-codec.ts +82 -0
- package/src/settings/identity.ts +3 -72
- package/src/settings/registry-defs.ts +7 -0
- package/src/transport.ts +9 -0
package/src/settings/identity.ts
CHANGED
|
@@ -32,79 +32,10 @@ import { runCapture } from "./exec.ts";
|
|
|
32
32
|
import { machineSettingsPath } from "./paths.ts";
|
|
33
33
|
import { readStore } from "./stores.ts";
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
const URL_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/(?:[^@/]+@)?([^/]+)\/(.+)$/;
|
|
35
|
+
import { normalizeRemote, type RepoIdentity } from "./identity-codec.ts";
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// so a Windows-drive-letter-free local remote never falsely matches.
|
|
41
|
-
const SCP_RE = /^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/;
|
|
42
|
-
|
|
43
|
-
export type RepoIdentity =
|
|
44
|
-
| { kind: "remote"; id: string }
|
|
45
|
-
| { kind: "path"; id: string };
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* The wire form crosses the daemon socket, sits in board config, and lands in
|
|
49
|
-
* console's `/runs/:repo/...` URL — all of which need one slash-free segment.
|
|
50
|
-
* `encodeURIComponent` guarantees that and is exactly reversible.
|
|
51
|
-
*/
|
|
52
|
-
export function serializeIdentity(id: RepoIdentity): string {
|
|
53
|
-
return `${id.kind}:${encodeURIComponent(id.id)}`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function parseIdentity(wire: string): RepoIdentity | null {
|
|
57
|
-
const colon = wire.indexOf(":");
|
|
58
|
-
if (colon === -1) return null;
|
|
59
|
-
const kind = wire.slice(0, colon);
|
|
60
|
-
if (kind !== "remote" && kind !== "path") return null;
|
|
61
|
-
const encoded = wire.slice(colon + 1);
|
|
62
|
-
let id: string;
|
|
63
|
-
try {
|
|
64
|
-
id = decodeURIComponent(encoded);
|
|
65
|
-
} catch {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
// Canonical wires only: the id segment must be byte-for-byte what
|
|
69
|
-
// serializeIdentity emits. Guard sites validate with parseIdentity and then
|
|
70
|
-
// use the WIRE as a single path component (repoDataDir et al.) — a
|
|
71
|
-
// hand-built wire with a literal "/" ("path:../..") would otherwise parse
|
|
72
|
-
// and escape the state directory.
|
|
73
|
-
if (encodeURIComponent(id) !== encoded) return null;
|
|
74
|
-
return { kind, id };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Pure normalization: `remote` → `host/path` (lowercase host, `.git` and
|
|
79
|
-
* embedded credentials stripped) or null when the remote doesn't match a
|
|
80
|
-
* recognized host form (local paths, garbage input).
|
|
81
|
-
*/
|
|
82
|
-
export function normalizeRemote(remote: string): string | null {
|
|
83
|
-
const trimmed = remote.trim();
|
|
84
|
-
if (!trimmed) return null;
|
|
85
|
-
|
|
86
|
-
let host: string | undefined;
|
|
87
|
-
let path: string | undefined;
|
|
88
|
-
|
|
89
|
-
const urlMatch = URL_RE.exec(trimmed);
|
|
90
|
-
if (urlMatch) {
|
|
91
|
-
host = urlMatch[1];
|
|
92
|
-
path = urlMatch[2];
|
|
93
|
-
} else if (!trimmed.startsWith("/") && !trimmed.startsWith("~")) {
|
|
94
|
-
const scpMatch = SCP_RE.exec(trimmed);
|
|
95
|
-
if (scpMatch) {
|
|
96
|
-
host = scpMatch[1];
|
|
97
|
-
path = scpMatch[2];
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!host || !path) return null;
|
|
102
|
-
|
|
103
|
-
const normalizedPath = path.replace(/\.git$/, "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
104
|
-
if (!normalizedPath) return null;
|
|
105
|
-
|
|
106
|
-
return `${host.toLowerCase()}/${normalizedPath}`;
|
|
107
|
-
}
|
|
37
|
+
export { serializeIdentity, parseIdentity, normalizeRemote } from "./identity-codec.ts";
|
|
38
|
+
export type { RepoIdentity } from "./identity-codec.ts";
|
|
108
39
|
|
|
109
40
|
/**
|
|
110
41
|
* The sync helper every non-derivation call site uses: machine-store
|
|
@@ -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
|