@h-rig/pi-rig 0.0.6-alpha.89 → 0.0.6-alpha.90
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/src/client.js +9 -41
- package/dist/src/commands.js +2 -11
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +52 -186
- package/dist/src/live-mirror.js +1 -1
- package/package.json +2 -2
package/dist/src/client.js
CHANGED
|
@@ -45,13 +45,6 @@ function resolveGlobalConnectionsPath(env) {
|
|
|
45
45
|
return resolve(stateDir, "connections.json");
|
|
46
46
|
return resolve(homedir(), ".rig", "connections.json");
|
|
47
47
|
}
|
|
48
|
-
function inferRemoteProjectRoot(repoConnection, authState) {
|
|
49
|
-
const repoSlug = cleanString(repoConnection?.project) ?? cleanString(authState?.selectedRepo);
|
|
50
|
-
const checkoutBaseDir = cleanString(authState?.checkoutBaseDir);
|
|
51
|
-
if (!repoSlug || !checkoutBaseDir || !repoSlug.includes("/"))
|
|
52
|
-
return null;
|
|
53
|
-
return resolve(checkoutBaseDir, repoSlug);
|
|
54
|
-
}
|
|
55
48
|
function discoverRigContext(env) {
|
|
56
49
|
const cwd = cleanString(env.PWD);
|
|
57
50
|
if (!cwd)
|
|
@@ -67,11 +60,11 @@ function discoverRigContext(env) {
|
|
|
67
60
|
const server = readJson(resolve(projectRoot, ".rig", "state", "rig-server.json"));
|
|
68
61
|
const host = cleanString(server?.host);
|
|
69
62
|
const port = typeof server?.port === "number" ? server.port : null;
|
|
70
|
-
const
|
|
63
|
+
const authToken = cleanString(server?.authToken);
|
|
71
64
|
return {
|
|
72
65
|
projectRoot,
|
|
73
66
|
...host && port ? { serverUrl: `http://${host}:${port}` } : {},
|
|
74
|
-
...
|
|
67
|
+
...authToken ? { authToken } : {}
|
|
75
68
|
};
|
|
76
69
|
}
|
|
77
70
|
const global = readJson(resolveGlobalConnectionsPath(env));
|
|
@@ -79,22 +72,15 @@ function discoverRigContext(env) {
|
|
|
79
72
|
const selectedConnection = connections[selected];
|
|
80
73
|
const record = selectedConnection && typeof selectedConnection === "object" && !Array.isArray(selectedConnection) ? selectedConnection : null;
|
|
81
74
|
const baseUrl = record?.kind === "remote" ? cleanString(record.baseUrl) : null;
|
|
82
|
-
|
|
83
|
-
const authToken = cleanString(authState?.apiSessionToken) ?? cleanString(authState?.sessionToken);
|
|
84
|
-
const serverProjectRoot = cleanString(repoConnection?.serverProjectRoot) ?? inferRemoteProjectRoot(repoConnection, authState);
|
|
85
|
-
return {
|
|
86
|
-
projectRoot: serverProjectRoot ?? projectRoot,
|
|
87
|
-
...baseUrl ? { serverUrl: baseUrl } : {},
|
|
88
|
-
...authToken ? { authToken } : {}
|
|
89
|
-
};
|
|
75
|
+
return { projectRoot, ...baseUrl ? { serverUrl: baseUrl } : {} };
|
|
90
76
|
}
|
|
91
77
|
function createRigContextFromEnv(env = process.env) {
|
|
92
78
|
const runId = env.RIG_RUN_ID ?? env.RIG_SERVER_RUN_ID;
|
|
93
79
|
const taskId = env.RIG_TASK_ID;
|
|
94
80
|
const discovered = discoverRigContext(env);
|
|
95
|
-
const serverUrl =
|
|
96
|
-
const projectRoot =
|
|
97
|
-
const authToken =
|
|
81
|
+
const serverUrl = env.RIG_SERVER_URL ?? env.RIG_SERVER_BASE_URL ?? discovered.serverUrl;
|
|
82
|
+
const projectRoot = env.RIG_PROJECT_ROOT ?? env.PROJECT_RIG_ROOT ?? discovered.projectRoot;
|
|
83
|
+
const authToken = env.RIG_AUTH_TOKEN ?? env.RIG_SERVER_AUTH_TOKEN ?? discovered.authToken;
|
|
98
84
|
const steeringPollMs = cleanNonNegativeInteger(env.RIG_STEERING_POLL_MS);
|
|
99
85
|
const operatorSession = env.RIG_PI_OPERATOR_SESSION === "1" || env.RIG_PI_OPERATOR_SESSION === "true";
|
|
100
86
|
const active = Boolean(runId || taskId || serverUrl || projectRoot);
|
|
@@ -135,16 +121,6 @@ function requireServerUrl(context) {
|
|
|
135
121
|
}
|
|
136
122
|
var BRIDGE_REQUEST_TIMEOUT_MS = 30000;
|
|
137
123
|
var PROTOCOL_CHECK_TIMEOUT_MS = 1e4;
|
|
138
|
-
function mergeCookie(existing, name, value) {
|
|
139
|
-
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
140
|
-
if (!existing?.trim())
|
|
141
|
-
return encoded;
|
|
142
|
-
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
143
|
-
return [...parts, encoded].join("; ");
|
|
144
|
-
}
|
|
145
|
-
function queryAuthFallbackEnabled(env = process.env) {
|
|
146
|
-
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
147
|
-
}
|
|
148
124
|
|
|
149
125
|
class RigBridgeClient {
|
|
150
126
|
context;
|
|
@@ -155,22 +131,14 @@ class RigBridgeClient {
|
|
|
155
131
|
}
|
|
156
132
|
async request(pathname, init, timeoutMs = BRIDGE_REQUEST_TIMEOUT_MS) {
|
|
157
133
|
const headers = new Headers(init?.headers);
|
|
158
|
-
if (this.context.authToken) {
|
|
159
|
-
|
|
160
|
-
if (!headers.has("authorization"))
|
|
161
|
-
headers.set("authorization", bearer);
|
|
162
|
-
if (!headers.has("x-auth"))
|
|
163
|
-
headers.set("x-auth", bearer);
|
|
164
|
-
headers.set("cookie", mergeCookie(headers.get("cookie"), "rig_auth", this.context.authToken));
|
|
134
|
+
if (this.context.authToken && !headers.has("authorization")) {
|
|
135
|
+
headers.set("authorization", `Bearer ${this.context.authToken}`);
|
|
165
136
|
}
|
|
166
137
|
if (this.context.projectRoot && !headers.has("x-rig-project-root")) {
|
|
167
138
|
headers.set("x-rig-project-root", this.context.projectRoot);
|
|
168
139
|
}
|
|
169
140
|
const signal = init?.signal ?? (timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined);
|
|
170
|
-
const
|
|
171
|
-
if (this.context.authToken && queryAuthFallbackEnabled())
|
|
172
|
-
requestUrl.searchParams.set("rt", this.context.authToken);
|
|
173
|
-
const response = await this.fetchImpl(requestUrl.toString(), { ...init, headers, signal });
|
|
141
|
+
const response = await this.fetchImpl(joinUrl(requireServerUrl(this.context), pathname), { ...init, headers, signal });
|
|
174
142
|
return readJsonResponse(response);
|
|
175
143
|
}
|
|
176
144
|
async status(timeoutMs) {
|
package/dist/src/commands.js
CHANGED
|
@@ -53,17 +53,8 @@ function createRigSlashCommands(input) {
|
|
|
53
53
|
notify("Usage: /rig steer <message>", "error");
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!accepted) {
|
|
59
|
-
const reason = typeof result.error === "string" && result.error.trim() ? `: ${result.error.trim()}` : "";
|
|
60
|
-
notify(`Rig did not accept the steering message${reason}.`, "error");
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const entry = result.message && typeof result.message === "object" && !Array.isArray(result.message) ? result.message : null;
|
|
64
|
-
const runId = String(entry?.runId ?? second ?? input.context.runId ?? "run");
|
|
65
|
-
const preview = message.length > 80 ? `${message.slice(0, 79)}\u2026` : message;
|
|
66
|
-
notify(`Steering queued for ${runId} \u2192 "${preview}" \u2014 the worker applies it at its next checkpoint.`, "info");
|
|
56
|
+
await input.client.steer(message);
|
|
57
|
+
notify("Rig steering message queued.", "info");
|
|
67
58
|
return;
|
|
68
59
|
}
|
|
69
60
|
if (first === "stop") {
|
package/dist/src/index.d.ts
CHANGED
|
@@ -46,11 +46,13 @@ export type PiRigBridgeGate = {
|
|
|
46
46
|
readonly status: RigProtocolCheck["status"];
|
|
47
47
|
};
|
|
48
48
|
export type PiRigBridgeGateCheck = (ctx: unknown) => Promise<PiRigBridgeGate>;
|
|
49
|
+
/** Live refresh control handed to the operator widget by the WS bridge:
|
|
50
|
+
* while the socket is up, pushes (rig.event / snapshotInvalidated) drive the
|
|
51
|
+
* widget instead of the 1s status poll. */
|
|
49
52
|
export type OperatorLiveRefresh = {
|
|
50
53
|
isConnected(): boolean;
|
|
51
54
|
/** Returns true (and resets) when a push arrived since the last check. */
|
|
52
55
|
consumePushTrigger(): boolean;
|
|
53
|
-
dispose(): void;
|
|
54
56
|
};
|
|
55
57
|
export default function createPiRigExtension(pi: MinimalPiApi, options?: {
|
|
56
58
|
state?: PiRigExtensionState;
|
package/dist/src/index.js
CHANGED
|
@@ -47,13 +47,6 @@ function resolveGlobalConnectionsPath(env) {
|
|
|
47
47
|
return resolve(stateDir, "connections.json");
|
|
48
48
|
return resolve(homedir(), ".rig", "connections.json");
|
|
49
49
|
}
|
|
50
|
-
function inferRemoteProjectRoot(repoConnection, authState) {
|
|
51
|
-
const repoSlug = cleanString(repoConnection?.project) ?? cleanString(authState?.selectedRepo);
|
|
52
|
-
const checkoutBaseDir = cleanString(authState?.checkoutBaseDir);
|
|
53
|
-
if (!repoSlug || !checkoutBaseDir || !repoSlug.includes("/"))
|
|
54
|
-
return null;
|
|
55
|
-
return resolve(checkoutBaseDir, repoSlug);
|
|
56
|
-
}
|
|
57
50
|
function discoverRigContext(env) {
|
|
58
51
|
const cwd = cleanString(env.PWD);
|
|
59
52
|
if (!cwd)
|
|
@@ -69,11 +62,11 @@ function discoverRigContext(env) {
|
|
|
69
62
|
const server = readJson(resolve(projectRoot, ".rig", "state", "rig-server.json"));
|
|
70
63
|
const host = cleanString(server?.host);
|
|
71
64
|
const port = typeof server?.port === "number" ? server.port : null;
|
|
72
|
-
const
|
|
65
|
+
const authToken = cleanString(server?.authToken);
|
|
73
66
|
return {
|
|
74
67
|
projectRoot,
|
|
75
68
|
...host && port ? { serverUrl: `http://${host}:${port}` } : {},
|
|
76
|
-
...
|
|
69
|
+
...authToken ? { authToken } : {}
|
|
77
70
|
};
|
|
78
71
|
}
|
|
79
72
|
const global = readJson(resolveGlobalConnectionsPath(env));
|
|
@@ -81,22 +74,15 @@ function discoverRigContext(env) {
|
|
|
81
74
|
const selectedConnection = connections[selected];
|
|
82
75
|
const record = selectedConnection && typeof selectedConnection === "object" && !Array.isArray(selectedConnection) ? selectedConnection : null;
|
|
83
76
|
const baseUrl = record?.kind === "remote" ? cleanString(record.baseUrl) : null;
|
|
84
|
-
|
|
85
|
-
const authToken = cleanString(authState?.apiSessionToken) ?? cleanString(authState?.sessionToken);
|
|
86
|
-
const serverProjectRoot = cleanString(repoConnection?.serverProjectRoot) ?? inferRemoteProjectRoot(repoConnection, authState);
|
|
87
|
-
return {
|
|
88
|
-
projectRoot: serverProjectRoot ?? projectRoot,
|
|
89
|
-
...baseUrl ? { serverUrl: baseUrl } : {},
|
|
90
|
-
...authToken ? { authToken } : {}
|
|
91
|
-
};
|
|
77
|
+
return { projectRoot, ...baseUrl ? { serverUrl: baseUrl } : {} };
|
|
92
78
|
}
|
|
93
79
|
function createRigContextFromEnv(env = process.env) {
|
|
94
80
|
const runId = env.RIG_RUN_ID ?? env.RIG_SERVER_RUN_ID;
|
|
95
81
|
const taskId = env.RIG_TASK_ID;
|
|
96
82
|
const discovered = discoverRigContext(env);
|
|
97
|
-
const serverUrl =
|
|
98
|
-
const projectRoot =
|
|
99
|
-
const authToken =
|
|
83
|
+
const serverUrl = env.RIG_SERVER_URL ?? env.RIG_SERVER_BASE_URL ?? discovered.serverUrl;
|
|
84
|
+
const projectRoot = env.RIG_PROJECT_ROOT ?? env.PROJECT_RIG_ROOT ?? discovered.projectRoot;
|
|
85
|
+
const authToken = env.RIG_AUTH_TOKEN ?? env.RIG_SERVER_AUTH_TOKEN ?? discovered.authToken;
|
|
100
86
|
const steeringPollMs = cleanNonNegativeInteger(env.RIG_STEERING_POLL_MS);
|
|
101
87
|
const operatorSession = env.RIG_PI_OPERATOR_SESSION === "1" || env.RIG_PI_OPERATOR_SESSION === "true";
|
|
102
88
|
const active = Boolean(runId || taskId || serverUrl || projectRoot);
|
|
@@ -137,16 +123,6 @@ function requireServerUrl(context) {
|
|
|
137
123
|
}
|
|
138
124
|
var BRIDGE_REQUEST_TIMEOUT_MS = 30000;
|
|
139
125
|
var PROTOCOL_CHECK_TIMEOUT_MS = 1e4;
|
|
140
|
-
function mergeCookie(existing, name, value) {
|
|
141
|
-
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
142
|
-
if (!existing?.trim())
|
|
143
|
-
return encoded;
|
|
144
|
-
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
145
|
-
return [...parts, encoded].join("; ");
|
|
146
|
-
}
|
|
147
|
-
function queryAuthFallbackEnabled(env = process.env) {
|
|
148
|
-
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
149
|
-
}
|
|
150
126
|
|
|
151
127
|
class RigBridgeClient {
|
|
152
128
|
context;
|
|
@@ -157,22 +133,14 @@ class RigBridgeClient {
|
|
|
157
133
|
}
|
|
158
134
|
async request(pathname, init, timeoutMs = BRIDGE_REQUEST_TIMEOUT_MS) {
|
|
159
135
|
const headers = new Headers(init?.headers);
|
|
160
|
-
if (this.context.authToken) {
|
|
161
|
-
|
|
162
|
-
if (!headers.has("authorization"))
|
|
163
|
-
headers.set("authorization", bearer);
|
|
164
|
-
if (!headers.has("x-auth"))
|
|
165
|
-
headers.set("x-auth", bearer);
|
|
166
|
-
headers.set("cookie", mergeCookie(headers.get("cookie"), "rig_auth", this.context.authToken));
|
|
136
|
+
if (this.context.authToken && !headers.has("authorization")) {
|
|
137
|
+
headers.set("authorization", `Bearer ${this.context.authToken}`);
|
|
167
138
|
}
|
|
168
139
|
if (this.context.projectRoot && !headers.has("x-rig-project-root")) {
|
|
169
140
|
headers.set("x-rig-project-root", this.context.projectRoot);
|
|
170
141
|
}
|
|
171
142
|
const signal = init?.signal ?? (timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined);
|
|
172
|
-
const
|
|
173
|
-
if (this.context.authToken && queryAuthFallbackEnabled())
|
|
174
|
-
requestUrl.searchParams.set("rt", this.context.authToken);
|
|
175
|
-
const response = await this.fetchImpl(requestUrl.toString(), { ...init, headers, signal });
|
|
143
|
+
const response = await this.fetchImpl(joinUrl(requireServerUrl(this.context), pathname), { ...init, headers, signal });
|
|
176
144
|
return readJsonResponse(response);
|
|
177
145
|
}
|
|
178
146
|
async status(timeoutMs) {
|
|
@@ -699,17 +667,8 @@ function createRigSlashCommands(input) {
|
|
|
699
667
|
notify("Usage: /rig steer <message>", "error");
|
|
700
668
|
return;
|
|
701
669
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
if (!accepted) {
|
|
705
|
-
const reason = typeof result.error === "string" && result.error.trim() ? `: ${result.error.trim()}` : "";
|
|
706
|
-
notify(`Rig did not accept the steering message${reason}.`, "error");
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
const entry = result.message && typeof result.message === "object" && !Array.isArray(result.message) ? result.message : null;
|
|
710
|
-
const runId = String(entry?.runId ?? second ?? input.context.runId ?? "run");
|
|
711
|
-
const preview = message.length > 80 ? `${message.slice(0, 79)}\u2026` : message;
|
|
712
|
-
notify(`Steering queued for ${runId} \u2192 "${preview}" \u2014 the worker applies it at its next checkpoint.`, "info");
|
|
670
|
+
await input.client.steer(message);
|
|
671
|
+
notify("Rig steering message queued.", "info");
|
|
713
672
|
return;
|
|
714
673
|
}
|
|
715
674
|
if (first === "stop") {
|
|
@@ -808,7 +767,7 @@ async function createLiveMirror(input) {
|
|
|
808
767
|
renderTimer = setTimeout(() => {
|
|
809
768
|
renderTimer = null;
|
|
810
769
|
tui?.requestRender?.();
|
|
811
|
-
},
|
|
770
|
+
}, 33);
|
|
812
771
|
renderTimer.unref?.();
|
|
813
772
|
};
|
|
814
773
|
pi.registerMessageRenderer?.(DRONE_MESSAGE_TYPE, (message) => {
|
|
@@ -1027,52 +986,35 @@ function createPiRigExtensionState(input = {}) {
|
|
|
1027
986
|
...input.webSocketFactory ? { webSocketFactory: input.webSocketFactory } : {}
|
|
1028
987
|
};
|
|
1029
988
|
}
|
|
1030
|
-
function isStalePiContextError(error) {
|
|
1031
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1032
|
-
return /ctx is stale|stale after session replacement|session replacement or reload/i.test(message);
|
|
1033
|
-
}
|
|
1034
|
-
function safeUiCall(action) {
|
|
1035
|
-
try {
|
|
1036
|
-
action();
|
|
1037
|
-
} catch (error) {
|
|
1038
|
-
if (!isStalePiContextError(error)) {
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
function uiOf(ctx) {
|
|
1044
|
-
try {
|
|
1045
|
-
const ui = ctx && typeof ctx === "object" ? ctx.ui : null;
|
|
1046
|
-
return ui && typeof ui === "object" ? ui : null;
|
|
1047
|
-
} catch {
|
|
1048
|
-
return null;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
989
|
function notify(ctx, message, level = "info") {
|
|
1052
|
-
const ui =
|
|
1053
|
-
const notifyFn = ui
|
|
990
|
+
const ui = ctx && typeof ctx === "object" ? ctx.ui : null;
|
|
991
|
+
const notifyFn = ui && typeof ui === "object" ? ui.notify : null;
|
|
1054
992
|
if (typeof notifyFn === "function") {
|
|
1055
|
-
|
|
993
|
+
notifyFn.call(ui, message, level);
|
|
1056
994
|
}
|
|
1057
995
|
}
|
|
1058
996
|
function canNotify(ctx) {
|
|
1059
|
-
const ui =
|
|
1060
|
-
return Boolean(ui && typeof ui.notify === "function");
|
|
997
|
+
const ui = ctx && typeof ctx === "object" ? ctx.ui : null;
|
|
998
|
+
return Boolean(ui && typeof ui === "object" && typeof ui.notify === "function");
|
|
1061
999
|
}
|
|
1062
1000
|
function setWidget(ctx, id, lines) {
|
|
1063
|
-
const ui =
|
|
1064
|
-
const setWidgetFn = ui
|
|
1001
|
+
const ui = ctx && typeof ctx === "object" ? ctx.ui : null;
|
|
1002
|
+
const setWidgetFn = ui && typeof ui === "object" ? ui.setWidget : null;
|
|
1065
1003
|
if (typeof setWidgetFn === "function") {
|
|
1066
|
-
|
|
1004
|
+
setWidgetFn.call(ui, id, lines);
|
|
1067
1005
|
}
|
|
1068
1006
|
}
|
|
1069
1007
|
function setStatus(ctx, id, text) {
|
|
1070
|
-
const ui =
|
|
1071
|
-
const setStatusFn = ui
|
|
1008
|
+
const ui = ctx && typeof ctx === "object" ? ctx.ui : null;
|
|
1009
|
+
const setStatusFn = ui && typeof ui === "object" ? ui.setStatus : null;
|
|
1072
1010
|
if (typeof setStatusFn === "function") {
|
|
1073
|
-
|
|
1011
|
+
setStatusFn.call(ui, id, text);
|
|
1074
1012
|
}
|
|
1075
1013
|
}
|
|
1014
|
+
function uiOf(ctx) {
|
|
1015
|
+
const ui = ctx && typeof ctx === "object" ? ctx.ui : null;
|
|
1016
|
+
return ui && typeof ui === "object" ? ui : null;
|
|
1017
|
+
}
|
|
1076
1018
|
function setTitle(ctx, title) {
|
|
1077
1019
|
const ui = uiOf(ctx);
|
|
1078
1020
|
const setTitleFn = ui?.setTitle;
|
|
@@ -1239,31 +1181,24 @@ function startOperatorRunStatusLine(state, ctx, live) {
|
|
|
1239
1181
|
return;
|
|
1240
1182
|
const shortId = state.runId.slice(0, 8);
|
|
1241
1183
|
let inFlight = false;
|
|
1242
|
-
let disposed = false;
|
|
1243
1184
|
let lastRefreshAt = 0;
|
|
1244
1185
|
const refresh = async () => {
|
|
1245
|
-
if (
|
|
1186
|
+
if (inFlight)
|
|
1246
1187
|
return;
|
|
1247
1188
|
inFlight = true;
|
|
1248
1189
|
lastRefreshAt = Date.now();
|
|
1249
1190
|
try {
|
|
1250
1191
|
const run = runPayload(await state.client.attach(state.runId));
|
|
1251
|
-
if (disposed)
|
|
1252
|
-
return;
|
|
1253
1192
|
const status = String(run.status ?? "unknown");
|
|
1254
1193
|
setStatus(ctx, "rig", `drone ${shortId} \xB7 ${status} \xB7 ${shortPath(runLocation(run))}`);
|
|
1255
1194
|
} catch (error) {
|
|
1256
|
-
|
|
1257
|
-
setStatus(ctx, "rig", `drone ${shortId} \xB7 server unreachable: ${error instanceof Error ? error.message : String(error)}`);
|
|
1258
|
-
}
|
|
1195
|
+
setStatus(ctx, "rig", `drone ${shortId} \xB7 server unreachable: ${error instanceof Error ? error.message : String(error)}`);
|
|
1259
1196
|
} finally {
|
|
1260
1197
|
inFlight = false;
|
|
1261
1198
|
}
|
|
1262
1199
|
};
|
|
1263
1200
|
refresh();
|
|
1264
1201
|
const timer = setInterval(() => {
|
|
1265
|
-
if (disposed)
|
|
1266
|
-
return;
|
|
1267
1202
|
const triggered = live?.consumePushTrigger() ?? false;
|
|
1268
1203
|
if ((live?.isConnected() ?? false) && !triggered && Date.now() - lastRefreshAt < OPERATOR_WIDGET_WS_FALLBACK_MS) {
|
|
1269
1204
|
return;
|
|
@@ -1271,10 +1206,6 @@ function startOperatorRunStatusLine(state, ctx, live) {
|
|
|
1271
1206
|
refresh();
|
|
1272
1207
|
}, 5000);
|
|
1273
1208
|
unrefTimer(timer);
|
|
1274
|
-
return () => {
|
|
1275
|
-
disposed = true;
|
|
1276
|
-
clearInterval(timer);
|
|
1277
|
-
};
|
|
1278
1209
|
}
|
|
1279
1210
|
function operatorInboxNotification(event) {
|
|
1280
1211
|
const type = typeof event.type === "string" ? event.type : null;
|
|
@@ -1327,8 +1258,7 @@ function startOperatorBridge(state, ctx) {
|
|
|
1327
1258
|
const triggered = pushTrigger;
|
|
1328
1259
|
pushTrigger = false;
|
|
1329
1260
|
return triggered;
|
|
1330
|
-
}
|
|
1331
|
-
dispose: () => socket.close()
|
|
1261
|
+
}
|
|
1332
1262
|
};
|
|
1333
1263
|
}
|
|
1334
1264
|
function workerStatusLine(status) {
|
|
@@ -1450,30 +1380,24 @@ function registerOperatorConsoleCommands(pi, state, tryRegister) {
|
|
|
1450
1380
|
function startWorkerSessionMirror(pi, state, ctx) {
|
|
1451
1381
|
if (!state.operatorSession || !state.active || !state.runId)
|
|
1452
1382
|
return;
|
|
1453
|
-
let disposed = false;
|
|
1454
1383
|
let mirror = null;
|
|
1455
1384
|
const pendingEvents = [];
|
|
1456
1385
|
createLiveMirror({ pi }).then((created) => {
|
|
1457
|
-
if (disposed)
|
|
1458
|
-
return;
|
|
1459
1386
|
mirror = created;
|
|
1460
1387
|
const ui = uiOf(ctx);
|
|
1461
1388
|
if (ui && typeof ui.setWidget === "function") {
|
|
1462
|
-
|
|
1389
|
+
ui.setWidget("rig-tui-capture", (tui) => created.captureTui(tui));
|
|
1463
1390
|
}
|
|
1464
1391
|
for (const event of pendingEvents.splice(0))
|
|
1465
1392
|
created.handleWorkerEvent(event);
|
|
1466
1393
|
}).catch((error) => {
|
|
1467
|
-
|
|
1468
|
-
notify(ctx, `Live drone transcript unavailable: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
1394
|
+
notify(ctx, `Live drone transcript unavailable: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
1469
1395
|
});
|
|
1470
1396
|
const socket = new RigWorkerEventsSocket({
|
|
1471
1397
|
context: state,
|
|
1472
1398
|
webSocketFactory: state.webSocketFactory,
|
|
1473
1399
|
handlers: {
|
|
1474
1400
|
onFrame: (frame) => {
|
|
1475
|
-
if (disposed)
|
|
1476
|
-
return;
|
|
1477
1401
|
if (frame.type === "status.update") {
|
|
1478
1402
|
const status = frame.status && typeof frame.status === "object" && !Array.isArray(frame.status) ? frame.status : null;
|
|
1479
1403
|
if (status)
|
|
@@ -1495,21 +1419,14 @@ function startWorkerSessionMirror(pi, state, ctx) {
|
|
|
1495
1419
|
pendingEvents.push(event);
|
|
1496
1420
|
},
|
|
1497
1421
|
onConnect: () => {
|
|
1498
|
-
|
|
1499
|
-
setStatus(ctx, "rig-worker", "drone link live");
|
|
1422
|
+
setStatus(ctx, "rig-worker", "drone link live");
|
|
1500
1423
|
},
|
|
1501
1424
|
onDisconnect: () => {
|
|
1502
|
-
|
|
1503
|
-
setStatus(ctx, "rig-worker", "drone link down (reconnecting\u2026)");
|
|
1425
|
+
setStatus(ctx, "rig-worker", "drone link down (reconnecting\u2026)");
|
|
1504
1426
|
}
|
|
1505
1427
|
}
|
|
1506
1428
|
});
|
|
1507
1429
|
socket.start();
|
|
1508
|
-
return () => {
|
|
1509
|
-
disposed = true;
|
|
1510
|
-
pendingEvents.splice(0);
|
|
1511
|
-
socket.close();
|
|
1512
|
-
};
|
|
1513
1430
|
}
|
|
1514
1431
|
async function forwardWorkerUiRequest(state, ctx, event) {
|
|
1515
1432
|
const request = event.request && typeof event.request === "object" && !Array.isArray(event.request) ? event.request : event;
|
|
@@ -1579,10 +1496,8 @@ function startWorkerCommandRegistration(pi, state, ctx) {
|
|
|
1579
1496
|
const registeredNames = new Set;
|
|
1580
1497
|
let attempts = 0;
|
|
1581
1498
|
let inFlight = false;
|
|
1582
|
-
let disposed = false;
|
|
1583
|
-
let timer = null;
|
|
1584
1499
|
const attempt = async () => {
|
|
1585
|
-
if (
|
|
1500
|
+
if (inFlight)
|
|
1586
1501
|
return false;
|
|
1587
1502
|
inFlight = true;
|
|
1588
1503
|
attempts += 1;
|
|
@@ -1593,84 +1508,64 @@ function startWorkerCommandRegistration(pi, state, ctx) {
|
|
|
1593
1508
|
}
|
|
1594
1509
|
};
|
|
1595
1510
|
attempt().then((ready) => {
|
|
1596
|
-
if (
|
|
1511
|
+
if (ready)
|
|
1597
1512
|
return;
|
|
1598
|
-
timer = setInterval(() => {
|
|
1599
|
-
if (
|
|
1600
|
-
|
|
1601
|
-
clearInterval(timer);
|
|
1602
|
-
timer = null;
|
|
1513
|
+
const timer = setInterval(() => {
|
|
1514
|
+
if (attempts >= 60) {
|
|
1515
|
+
clearInterval(timer);
|
|
1603
1516
|
return;
|
|
1604
1517
|
}
|
|
1605
1518
|
attempt().then((nextReady) => {
|
|
1606
|
-
if (nextReady
|
|
1519
|
+
if (nextReady)
|
|
1607
1520
|
clearInterval(timer);
|
|
1608
|
-
timer = null;
|
|
1609
|
-
}
|
|
1610
1521
|
});
|
|
1611
1522
|
}, 2000);
|
|
1612
1523
|
unrefTimer(timer);
|
|
1613
1524
|
});
|
|
1614
|
-
return () => {
|
|
1615
|
-
disposed = true;
|
|
1616
|
-
if (timer)
|
|
1617
|
-
clearInterval(timer);
|
|
1618
|
-
timer = null;
|
|
1619
|
-
};
|
|
1620
1525
|
}
|
|
1621
1526
|
function startSteeringBridge(pi, state, ctx, gate, deliveredIds) {
|
|
1622
1527
|
if (state.operatorSession || !state.active || !state.runId || typeof pi.sendUserMessage !== "function")
|
|
1623
1528
|
return;
|
|
1624
1529
|
const runId = state.runId;
|
|
1625
|
-
let disposed = false;
|
|
1626
1530
|
const socket = new RigBridgeSocket({
|
|
1627
1531
|
context: state,
|
|
1628
1532
|
webSocketFactory: state.webSocketFactory,
|
|
1629
1533
|
handlers: {
|
|
1630
1534
|
onSteeringMessage: (message) => {
|
|
1631
|
-
if (disposed)
|
|
1632
|
-
return;
|
|
1633
1535
|
(async () => {
|
|
1634
1536
|
try {
|
|
1635
|
-
if (
|
|
1537
|
+
if (!await deliverSteeringMessage(pi, deliveredIds, message))
|
|
1636
1538
|
return;
|
|
1637
1539
|
const id = typeof message.id === "string" && message.id.trim() ? message.id : null;
|
|
1638
1540
|
if (id)
|
|
1639
1541
|
socket.ackSteering(runId, [id]);
|
|
1640
1542
|
notify(ctx, "Delivered 1 Rig steering message.");
|
|
1641
1543
|
} catch (error) {
|
|
1642
|
-
|
|
1643
|
-
notify(ctx, `Rig steering sync failed: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
1544
|
+
notify(ctx, `Rig steering sync failed: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
1644
1545
|
}
|
|
1645
1546
|
})();
|
|
1646
1547
|
},
|
|
1647
1548
|
onConnect: () => {
|
|
1648
|
-
if (disposed)
|
|
1649
|
-
return;
|
|
1650
1549
|
consumeQueuedSteering(pi, state, ctx, gate, deliveredIds);
|
|
1651
1550
|
}
|
|
1652
1551
|
}
|
|
1653
1552
|
});
|
|
1654
1553
|
(async () => {
|
|
1655
1554
|
const gateResult = await gate(ctx);
|
|
1656
|
-
if (
|
|
1555
|
+
if (!gateResult.allowed)
|
|
1657
1556
|
return;
|
|
1658
1557
|
if (gateResult.status === "compatible") {
|
|
1659
1558
|
socket.start();
|
|
1660
1559
|
}
|
|
1661
1560
|
})();
|
|
1662
1561
|
const intervalMs = state.steeringPollMs ?? 1000;
|
|
1663
|
-
if (intervalMs <= 0)
|
|
1664
|
-
return
|
|
1665
|
-
disposed = true;
|
|
1666
|
-
socket.close();
|
|
1667
|
-
};
|
|
1668
|
-
}
|
|
1562
|
+
if (intervalMs <= 0)
|
|
1563
|
+
return;
|
|
1669
1564
|
const WS_CONNECTED_SWEEP_MS = 1e4;
|
|
1670
1565
|
let inFlight = false;
|
|
1671
1566
|
let lastSweepAt = 0;
|
|
1672
1567
|
const timer = setInterval(() => {
|
|
1673
|
-
if (
|
|
1568
|
+
if (inFlight)
|
|
1674
1569
|
return;
|
|
1675
1570
|
if (socket.connected && Date.now() - lastSweepAt < WS_CONNECTED_SWEEP_MS)
|
|
1676
1571
|
return;
|
|
@@ -1681,35 +1576,11 @@ function startSteeringBridge(pi, state, ctx, gate, deliveredIds) {
|
|
|
1681
1576
|
});
|
|
1682
1577
|
}, intervalMs);
|
|
1683
1578
|
unrefTimer(timer);
|
|
1684
|
-
return () => {
|
|
1685
|
-
disposed = true;
|
|
1686
|
-
clearInterval(timer);
|
|
1687
|
-
socket.close();
|
|
1688
|
-
};
|
|
1689
1579
|
}
|
|
1690
1580
|
function createPiRigExtension(pi, options = {}) {
|
|
1691
1581
|
const state = options.state ?? createPiRigExtensionState();
|
|
1692
1582
|
const gate = createBridgeGate(state);
|
|
1693
1583
|
const deliveredSteeringIds = new Set;
|
|
1694
|
-
const sessionDisposables = new Set;
|
|
1695
|
-
const runtimeDisposables = new Set;
|
|
1696
|
-
const addDisposable = (target, disposable) => {
|
|
1697
|
-
if (disposable)
|
|
1698
|
-
target.add(disposable);
|
|
1699
|
-
};
|
|
1700
|
-
const disposeSet = (target) => {
|
|
1701
|
-
for (const dispose of target) {
|
|
1702
|
-
try {
|
|
1703
|
-
dispose();
|
|
1704
|
-
} catch {}
|
|
1705
|
-
}
|
|
1706
|
-
target.clear();
|
|
1707
|
-
};
|
|
1708
|
-
const disposeSessionResources = () => disposeSet(sessionDisposables);
|
|
1709
|
-
const disposeRuntimeResources = () => {
|
|
1710
|
-
disposeSet(sessionDisposables);
|
|
1711
|
-
disposeSet(runtimeDisposables);
|
|
1712
|
-
};
|
|
1713
1584
|
const commands = createRigSlashCommands({
|
|
1714
1585
|
context: state,
|
|
1715
1586
|
client: state.client,
|
|
@@ -1759,14 +1630,10 @@ function createPiRigExtension(pi, options = {}) {
|
|
|
1759
1630
|
}
|
|
1760
1631
|
}));
|
|
1761
1632
|
}
|
|
1762
|
-
|
|
1633
|
+
startSteeringBridge(pi, state, globalThis, gate, deliveredSteeringIds);
|
|
1763
1634
|
}
|
|
1764
1635
|
pi.on?.("input", async (event, ctx) => handleOperatorInput(event, state, ctx, gate));
|
|
1765
|
-
pi.on?.("session_shutdown", () => {
|
|
1766
|
-
disposeRuntimeResources();
|
|
1767
|
-
});
|
|
1768
1636
|
pi.on?.("session_start", async (_event, ctx) => {
|
|
1769
|
-
disposeSessionResources();
|
|
1770
1637
|
if (!state.active || !state.runId)
|
|
1771
1638
|
return;
|
|
1772
1639
|
const shortId = state.runId.slice(0, 8);
|
|
@@ -1782,12 +1649,11 @@ function createPiRigExtension(pi, options = {}) {
|
|
|
1782
1649
|
return;
|
|
1783
1650
|
}
|
|
1784
1651
|
setStatus(ctx, "rig", `drone ${shortId} \xB7 connecting\u2026`);
|
|
1785
|
-
const live = gateResult.
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
addDisposable(sessionDisposables, startWorkerCommandRegistration(pi, state, ctx));
|
|
1652
|
+
const live = gateResult.status === "compatible" ? startOperatorBridge(state, ctx) : undefined;
|
|
1653
|
+
startOperatorRunStatusLine(state, ctx, live);
|
|
1654
|
+
if (state.operatorSession && gateResult.status === "compatible") {
|
|
1655
|
+
startWorkerSessionMirror(pi, state, ctx);
|
|
1656
|
+
startWorkerCommandRegistration(pi, state, ctx);
|
|
1791
1657
|
}
|
|
1792
1658
|
await consumeQueuedSteering(pi, state, ctx, gate, deliveredSteeringIds);
|
|
1793
1659
|
});
|
package/dist/src/live-mirror.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/pi-rig",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.90",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Rig package",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
]
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.
|
|
41
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.90"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@earendil-works/pi-coding-agent": ">=0.79.0",
|