@makerbi/remodex 1.5.4 → 2.0.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/bin/remodex.js +77 -13
- package/package.json +1 -1
- package/src/account-status.js +7 -1
- package/src/apply-patch-changes.js +185 -0
- package/src/bootstrap-codex-cli.js +1 -1
- package/src/bridge-status.js +3 -2
- package/src/bridge.js +837 -73
- package/src/codex-transport.js +10 -10
- package/src/desktop-handler.js +14 -1
- package/src/desktop-ipc-action-follower.js +129 -0
- package/src/git-handler.js +92 -28
- package/src/index.js +4 -2
- package/src/ios-app-compatibility.js +7 -7
- package/src/macos-launch-agent.js +132 -2
- package/src/project-handler.js +162 -1
- package/src/push-notification-service-client.js +85 -37
- package/src/push-notification-tracker.js +15 -0
- package/src/qr.js +2 -5
- package/src/rollout-live-mirror.js +331 -20
- package/src/rollout-watch.js +5 -1
- package/src/secure-device-state.js +66 -2
- package/src/secure-transport.js +47 -12
- package/src/session-jsonl-history.js +850 -16
- package/src/voice-handler.js +162 -65
- package/src/workspace-handler.js +327 -14
package/bin/remodex.js
CHANGED
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
printMacOSBridgeServiceStatus,
|
|
12
12
|
readBridgeConfig,
|
|
13
13
|
resetMacOSBridgePairing,
|
|
14
|
+
restartMacOSBridgeService,
|
|
14
15
|
runMacOSBridgeService,
|
|
15
16
|
startBridge,
|
|
16
17
|
startMacOSBridgeService,
|
|
@@ -27,6 +28,7 @@ const defaultDeps = {
|
|
|
27
28
|
printMacOSBridgeServiceStatus,
|
|
28
29
|
readBridgeConfig,
|
|
29
30
|
resetMacOSBridgePairing,
|
|
31
|
+
restartMacOSBridgeService,
|
|
30
32
|
runMacOSBridgeService,
|
|
31
33
|
startBridge,
|
|
32
34
|
startMacOSBridgeService,
|
|
@@ -112,16 +114,13 @@ async function main({
|
|
|
112
114
|
consoleImpl,
|
|
113
115
|
exitImpl,
|
|
114
116
|
});
|
|
115
|
-
deps.
|
|
116
|
-
const result = await deps.startMacOSBridgeService({
|
|
117
|
-
waitForPairing: false,
|
|
118
|
-
});
|
|
117
|
+
const result = await deps.startMacOSBridgeService();
|
|
119
118
|
emitResult({
|
|
120
119
|
payload: {
|
|
121
120
|
ok: true,
|
|
122
121
|
currentVersion: version,
|
|
123
122
|
plistPath: result?.plistPath,
|
|
124
|
-
pairingSession: result?.pairingSession,
|
|
123
|
+
pairingSession: sanitizePairingSessionForOutput(result?.pairingSession),
|
|
125
124
|
},
|
|
126
125
|
message: "[remodex] macOS bridge service is running.",
|
|
127
126
|
jsonOutput,
|
|
@@ -136,16 +135,13 @@ async function main({
|
|
|
136
135
|
consoleImpl,
|
|
137
136
|
exitImpl,
|
|
138
137
|
});
|
|
139
|
-
deps.
|
|
140
|
-
const result = await deps.startMacOSBridgeService({
|
|
141
|
-
waitForPairing: false,
|
|
142
|
-
});
|
|
138
|
+
const result = await deps.restartMacOSBridgeService();
|
|
143
139
|
emitResult({
|
|
144
140
|
payload: {
|
|
145
141
|
ok: true,
|
|
146
142
|
currentVersion: version,
|
|
147
143
|
plistPath: result?.plistPath,
|
|
148
|
-
pairingSession: result?.pairingSession,
|
|
144
|
+
pairingSession: sanitizePairingSessionForOutput(result?.pairingSession),
|
|
149
145
|
},
|
|
150
146
|
message: "[remodex] macOS bridge service restarted.",
|
|
151
147
|
jsonOutput,
|
|
@@ -154,6 +150,32 @@ async function main({
|
|
|
154
150
|
return;
|
|
155
151
|
}
|
|
156
152
|
|
|
153
|
+
if (command === "qr" || command === "pair") {
|
|
154
|
+
assertMacOSCommand(command, {
|
|
155
|
+
platform,
|
|
156
|
+
consoleImpl,
|
|
157
|
+
exitImpl,
|
|
158
|
+
});
|
|
159
|
+
const result = await deps.startMacOSBridgeService({
|
|
160
|
+
waitForPairing: true,
|
|
161
|
+
});
|
|
162
|
+
if (jsonOutput) {
|
|
163
|
+
emitJson({
|
|
164
|
+
ok: true,
|
|
165
|
+
currentVersion: version,
|
|
166
|
+
plistPath: result?.plistPath,
|
|
167
|
+
pairingSession: result?.pairingSession,
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
consoleImpl.log("[remodex] Refreshing bridge pairing QR...");
|
|
173
|
+
deps.printMacOSBridgePairingQr({
|
|
174
|
+
pairingSession: result.pairingSession,
|
|
175
|
+
});
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
157
179
|
if (command === "stop") {
|
|
158
180
|
assertMacOSCommand(command, {
|
|
159
181
|
platform,
|
|
@@ -181,7 +203,7 @@ async function main({
|
|
|
181
203
|
});
|
|
182
204
|
if (jsonOutput) {
|
|
183
205
|
emitJson({
|
|
184
|
-
...deps.getMacOSBridgeServiceStatus(),
|
|
206
|
+
...sanitizeBridgeServiceStatusForOutput(deps.getMacOSBridgeServiceStatus()),
|
|
185
207
|
currentVersion: version,
|
|
186
208
|
});
|
|
187
209
|
return;
|
|
@@ -257,9 +279,10 @@ async function main({
|
|
|
257
279
|
|
|
258
280
|
consoleImpl.error(`Unknown command: ${command}`);
|
|
259
281
|
consoleImpl.error(
|
|
260
|
-
"Usage: remodex up | remodex run [--extra-device|--extra-devices=N] | remodex start | remodex restart |
|
|
282
|
+
"Usage: remodex up | remodex run [--extra-device|--extra-devices=N] | remodex start | remodex restart | "
|
|
283
|
+
+ "remodex qr | remodex pair | remodex stop | remodex status | "
|
|
261
284
|
+ "remodex reset-pairing | remodex resume | remodex watch [threadId] | remodex --version | "
|
|
262
|
-
+ "append --json to start/restart/stop/status/reset-pairing/resume for machine-readable output"
|
|
285
|
+
+ "append --json to start/restart/qr/pair/stop/status/reset-pairing/resume for machine-readable output"
|
|
263
286
|
);
|
|
264
287
|
exitImpl(1);
|
|
265
288
|
}
|
|
@@ -348,6 +371,47 @@ function emitJson(payload) {
|
|
|
348
371
|
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
349
372
|
}
|
|
350
373
|
|
|
374
|
+
// Keeps machine-readable CLI output useful without exposing relay endpoints or live pairing payloads.
|
|
375
|
+
function sanitizeBridgeServiceStatusForOutput(status = {}) {
|
|
376
|
+
const sanitized = {
|
|
377
|
+
...status,
|
|
378
|
+
daemonConfig: sanitizeDaemonConfigForOutput(status.daemonConfig),
|
|
379
|
+
pairingSession: sanitizePairingSessionForOutput(status.pairingSession),
|
|
380
|
+
};
|
|
381
|
+
return sanitized;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function sanitizeDaemonConfigForOutput(config) {
|
|
385
|
+
if (!config || typeof config !== "object") {
|
|
386
|
+
return config || null;
|
|
387
|
+
}
|
|
388
|
+
const { relayUrl, pushServiceUrl, ...rest } = config;
|
|
389
|
+
return {
|
|
390
|
+
...rest,
|
|
391
|
+
relayConfigured: Boolean(relayUrl),
|
|
392
|
+
pushServiceConfigured: Boolean(pushServiceUrl),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function sanitizePairingSessionForOutput(pairingSession) {
|
|
397
|
+
if (!pairingSession || typeof pairingSession !== "object") {
|
|
398
|
+
return pairingSession || null;
|
|
399
|
+
}
|
|
400
|
+
const payload = pairingSession.pairingPayload || {};
|
|
401
|
+
return {
|
|
402
|
+
createdAt: pairingSession.createdAt,
|
|
403
|
+
pairingCode: pairingSession.pairingCode,
|
|
404
|
+
pairingPayload: {
|
|
405
|
+
v: payload.v,
|
|
406
|
+
expiresAt: payload.expiresAt,
|
|
407
|
+
hasRelay: Boolean(payload.relay),
|
|
408
|
+
hasSessionId: Boolean(payload.sessionId),
|
|
409
|
+
hasMacIdentityPublicKey: Boolean(payload.macIdentityPublicKey),
|
|
410
|
+
displayName: payload.displayName,
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
351
415
|
function assertMacOSCommand(name, {
|
|
352
416
|
platform = process.platform,
|
|
353
417
|
consoleImpl = console,
|
package/package.json
CHANGED
package/src/account-status.js
CHANGED
|
@@ -26,7 +26,8 @@ function composeAccountStatus({
|
|
|
26
26
|
const tokenReady = Boolean(authToken);
|
|
27
27
|
const requiresOpenaiAuth = Boolean(accountRead?.requiresOpenaiAuth || authStatus?.requiresOpenaiAuth);
|
|
28
28
|
const hasPriorLoginContext = hasAccountLogin || Boolean(authMethod);
|
|
29
|
-
const
|
|
29
|
+
const hasUsableChatGPTToken = tokenReady && isChatGPTAuthMethod(authMethod);
|
|
30
|
+
const needsReauth = !loginInFlight && requiresOpenaiAuth && hasPriorLoginContext && !hasUsableChatGPTToken;
|
|
30
31
|
const isAuthenticated = !needsReauth && (tokenReady || hasAccountLogin);
|
|
31
32
|
const status = isAuthenticated
|
|
32
33
|
? "authenticated"
|
|
@@ -53,6 +54,10 @@ function composeAccountStatus({
|
|
|
53
54
|
};
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
function isChatGPTAuthMethod(authMethod) {
|
|
58
|
+
return authMethod === "chatgpt" || authMethod === "chatgptAuthTokens";
|
|
59
|
+
}
|
|
60
|
+
|
|
56
61
|
// Removes any token-bearing fields before the bridge sends auth state to the phone.
|
|
57
62
|
function redactAuthStatus(authStatus = null, extras = {}) {
|
|
58
63
|
const composed = composeAccountStatus({
|
|
@@ -165,6 +170,7 @@ function deriveHostCapabilities(platform) {
|
|
|
165
170
|
displayWake: isMacOS,
|
|
166
171
|
keepAwake: isMacOS,
|
|
167
172
|
hostBrowserLogin: isMacOS,
|
|
173
|
+
bridgeUpdate: isMacOS,
|
|
168
174
|
};
|
|
169
175
|
}
|
|
170
176
|
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// FILE: apply-patch-changes.js
|
|
2
|
+
// Purpose: Converts Codex apply_patch transcript payloads into fileChange-compatible records.
|
|
3
|
+
// Layer: Bridge util
|
|
4
|
+
// Exports: parseApplyPatchChanges, buildApplyPatchFileChangeItem
|
|
5
|
+
// Depends on: path
|
|
6
|
+
|
|
7
|
+
const path = require("path");
|
|
8
|
+
|
|
9
|
+
function buildApplyPatchFileChangeItem({
|
|
10
|
+
callId = "",
|
|
11
|
+
patch = "",
|
|
12
|
+
status = "completed",
|
|
13
|
+
idFallback = "apply-patch-file-change",
|
|
14
|
+
cwd = "",
|
|
15
|
+
} = {}) {
|
|
16
|
+
const changes = parseApplyPatchChanges(patch, { cwd });
|
|
17
|
+
if (changes.length === 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
id: readString(callId) || idFallback,
|
|
23
|
+
type: "fileChange",
|
|
24
|
+
status: readString(status) || "completed",
|
|
25
|
+
changes,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Parses the custom apply_patch DSL saved in desktop rollouts into the same
|
|
30
|
+
// change-array shape that iOS already decodes from app-server fileChange items.
|
|
31
|
+
function parseApplyPatchChanges(patchText, { cwd = "" } = {}) {
|
|
32
|
+
const lines = String(patchText || "").split(/\r?\n/);
|
|
33
|
+
const changes = [];
|
|
34
|
+
let current = null;
|
|
35
|
+
|
|
36
|
+
function startChange(kind, rawPath) {
|
|
37
|
+
flushCurrentChange();
|
|
38
|
+
const filePath = normalizePatchPath(rawPath, cwd);
|
|
39
|
+
if (!filePath) {
|
|
40
|
+
current = null;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
current = {
|
|
45
|
+
path: filePath,
|
|
46
|
+
originalPath: filePath,
|
|
47
|
+
kind,
|
|
48
|
+
additions: 0,
|
|
49
|
+
deletions: 0,
|
|
50
|
+
bodyLines: [],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function flushCurrentChange() {
|
|
55
|
+
if (!current || !current.path) {
|
|
56
|
+
current = null;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
changes.push({
|
|
61
|
+
path: current.path,
|
|
62
|
+
kind: current.kind,
|
|
63
|
+
additions: current.additions,
|
|
64
|
+
deletions: current.deletions,
|
|
65
|
+
diff: buildUnifiedDiffForApplyPatchChange(current),
|
|
66
|
+
});
|
|
67
|
+
current = null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const line of lines) {
|
|
71
|
+
if (line.startsWith("*** Add File: ")) {
|
|
72
|
+
startChange("add", line.slice("*** Add File: ".length));
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (line.startsWith("*** Update File: ")) {
|
|
77
|
+
startChange("update", line.slice("*** Update File: ".length));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (line.startsWith("*** Delete File: ")) {
|
|
82
|
+
startChange("delete", line.slice("*** Delete File: ".length));
|
|
83
|
+
flushCurrentChange();
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (line.startsWith("*** Move to: ")) {
|
|
88
|
+
if (current) {
|
|
89
|
+
current.path = normalizePatchPath(line.slice("*** Move to: ".length), cwd) || current.path;
|
|
90
|
+
current.kind = "rename";
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (line === "*** End Patch") {
|
|
96
|
+
flushCurrentChange();
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (line.startsWith("***")) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!current) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
current.bodyLines.push(line);
|
|
109
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
110
|
+
current.additions += 1;
|
|
111
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
112
|
+
current.deletions += 1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
flushCurrentChange();
|
|
117
|
+
return changes.filter((change) => (
|
|
118
|
+
change.additions > 0
|
|
119
|
+
|| change.deletions > 0
|
|
120
|
+
|| change.kind === "rename"
|
|
121
|
+
|| change.kind === "delete"
|
|
122
|
+
));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildUnifiedDiffForApplyPatchChange(change) {
|
|
126
|
+
const oldPath = change.originalPath || change.path;
|
|
127
|
+
const newPath = change.path || oldPath;
|
|
128
|
+
const diffLines = [`diff --git ${gitPatchPath("a", oldPath)} ${gitPatchPath("b", newPath)}`];
|
|
129
|
+
|
|
130
|
+
if (change.kind === "add") {
|
|
131
|
+
diffLines.push("new file mode 100644", "--- /dev/null", `+++ ${gitPatchPath("b", newPath)}`);
|
|
132
|
+
} else if (change.kind === "delete") {
|
|
133
|
+
diffLines.push("deleted file mode 100644", `--- ${gitPatchPath("a", oldPath)}`, "+++ /dev/null");
|
|
134
|
+
} else if (change.kind === "rename") {
|
|
135
|
+
diffLines.push(`rename from ${oldPath}`, `rename to ${newPath}`);
|
|
136
|
+
if (change.bodyLines.length > 0) {
|
|
137
|
+
diffLines.push(`--- ${gitPatchPath("a", oldPath)}`, `+++ ${gitPatchPath("b", newPath)}`);
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
diffLines.push(`--- ${gitPatchPath("a", oldPath)}`, `+++ ${gitPatchPath("b", newPath)}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const bodyLines = change.bodyLines || [];
|
|
144
|
+
if (bodyLines.length > 0) {
|
|
145
|
+
if (!bodyLines.some((line) => line.startsWith("@@"))) {
|
|
146
|
+
diffLines.push("@@");
|
|
147
|
+
}
|
|
148
|
+
diffLines.push(...bodyLines);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return diffLines.join("\n").trim();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function gitPatchPath(prefix, filePath) {
|
|
155
|
+
const trimmed = readString(filePath).replace(/^\/+/, "");
|
|
156
|
+
return `${prefix}/${trimmed || filePath || "unknown"}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function normalizePatchPath(rawPath, cwd) {
|
|
160
|
+
const filePath = readString(rawPath);
|
|
161
|
+
if (!filePath) {
|
|
162
|
+
return "";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const root = readString(cwd);
|
|
166
|
+
if (!root || !path.isAbsolute(filePath)) {
|
|
167
|
+
return filePath;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const relativePath = path.relative(root, filePath);
|
|
171
|
+
if (!relativePath || relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
|
172
|
+
return filePath;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return relativePath;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function readString(value) {
|
|
179
|
+
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = {
|
|
183
|
+
buildApplyPatchFileChangeItem,
|
|
184
|
+
parseApplyPatchChanges,
|
|
185
|
+
};
|
package/src/bridge-status.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
// Depends on: timers
|
|
6
6
|
|
|
7
7
|
const BRIDGE_STATUS_HEARTBEAT_INTERVAL_MS = 5_000;
|
|
8
|
-
// Keep
|
|
9
|
-
|
|
8
|
+
// Keep this above the 25s iOS foreground keepalive cadence so normal jitter
|
|
9
|
+
// does not kill a healthy Mac relay socket, while still recovering quickly.
|
|
10
|
+
const RELAY_WATCHDOG_STALE_AFTER_MS = 45_000;
|
|
10
11
|
const STALE_RELAY_STATUS_MESSAGE = "Relay heartbeat stalled; reconnect pending.";
|
|
11
12
|
|
|
12
13
|
// Wraps daemon status publication so bridge.js does not own heartbeat bookkeeping.
|