@makerbi/remodex 1.5.3 → 1.5.8
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-jsonl-diagnose.js +0 -0
- package/bin/remodex.js +42 -10
- 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 +122 -0
- package/src/bridge.js +823 -157
- package/src/codex-transport.js +38 -11
- package/src/desktop-handler.js +14 -1
- package/src/desktop-ipc-action-follower.js +173 -1
- package/src/index.js +4 -2
- package/src/macos-launch-agent.js +87 -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/rollout-live-mirror.js +331 -20
- package/src/rollout-watch.js +5 -1
- package/src/secure-device-state.js +26 -1
- package/src/secure-transport.js +40 -12
- package/src/session-jsonl-history.js +860 -14
- package/src/voice-handler.js +129 -62
- package/src/workspace-handler.js +327 -14
- package/src/private-defaults.json +0 -4
|
File without changes
|
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,
|
|
@@ -37,11 +39,29 @@ const defaultDeps = {
|
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
if (require.main === module) {
|
|
40
|
-
void
|
|
42
|
+
void runCli();
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
// ─── ENTRY POINT ─────────────────────────────────────────────
|
|
44
46
|
|
|
47
|
+
// Runs the CLI process and turns expected configuration failures into readable terminal output.
|
|
48
|
+
async function runCli({
|
|
49
|
+
mainImpl = main,
|
|
50
|
+
consoleImpl = console,
|
|
51
|
+
exitImpl = process.exit,
|
|
52
|
+
} = {}) {
|
|
53
|
+
try {
|
|
54
|
+
await mainImpl();
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const rawMessage = error && typeof error.message === "string"
|
|
57
|
+
? error.message.trim()
|
|
58
|
+
: String(error || "Command failed");
|
|
59
|
+
const message = rawMessage || "Command failed";
|
|
60
|
+
consoleImpl.error(message.startsWith("[remodex]") ? message : `[remodex] ${message}`);
|
|
61
|
+
exitImpl(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
45
65
|
async function main({
|
|
46
66
|
argv = process.argv,
|
|
47
67
|
platform = process.platform,
|
|
@@ -94,10 +114,7 @@ async function main({
|
|
|
94
114
|
consoleImpl,
|
|
95
115
|
exitImpl,
|
|
96
116
|
});
|
|
97
|
-
deps.
|
|
98
|
-
const result = await deps.startMacOSBridgeService({
|
|
99
|
-
waitForPairing: false,
|
|
100
|
-
});
|
|
117
|
+
const result = await deps.startMacOSBridgeService();
|
|
101
118
|
emitResult({
|
|
102
119
|
payload: {
|
|
103
120
|
ok: true,
|
|
@@ -118,10 +135,7 @@ async function main({
|
|
|
118
135
|
consoleImpl,
|
|
119
136
|
exitImpl,
|
|
120
137
|
});
|
|
121
|
-
deps.
|
|
122
|
-
const result = await deps.startMacOSBridgeService({
|
|
123
|
-
waitForPairing: false,
|
|
124
|
-
});
|
|
138
|
+
const result = await deps.restartMacOSBridgeService();
|
|
125
139
|
emitResult({
|
|
126
140
|
payload: {
|
|
127
141
|
ok: true,
|
|
@@ -136,6 +150,22 @@ async function main({
|
|
|
136
150
|
return;
|
|
137
151
|
}
|
|
138
152
|
|
|
153
|
+
if (command === "qr" || command === "pair") {
|
|
154
|
+
assertMacOSCommand(command, {
|
|
155
|
+
platform,
|
|
156
|
+
consoleImpl,
|
|
157
|
+
exitImpl,
|
|
158
|
+
});
|
|
159
|
+
consoleImpl.log("[remodex] Refreshing bridge pairing QR...");
|
|
160
|
+
const result = await deps.startMacOSBridgeService({
|
|
161
|
+
waitForPairing: true,
|
|
162
|
+
});
|
|
163
|
+
deps.printMacOSBridgePairingQr({
|
|
164
|
+
pairingSession: result.pairingSession,
|
|
165
|
+
});
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
139
169
|
if (command === "stop") {
|
|
140
170
|
assertMacOSCommand(command, {
|
|
141
171
|
platform,
|
|
@@ -239,7 +269,8 @@ async function main({
|
|
|
239
269
|
|
|
240
270
|
consoleImpl.error(`Unknown command: ${command}`);
|
|
241
271
|
consoleImpl.error(
|
|
242
|
-
"Usage: remodex up | remodex run [--extra-device|--extra-devices=N] | remodex start | remodex restart |
|
|
272
|
+
"Usage: remodex up | remodex run [--extra-device|--extra-devices=N] | remodex start | remodex restart | "
|
|
273
|
+
+ "remodex qr | remodex pair | remodex stop | remodex status | "
|
|
243
274
|
+ "remodex reset-pairing | remodex resume | remodex watch [threadId] | remodex --version | "
|
|
244
275
|
+ "append --json to start/restart/stop/status/reset-pairing/resume for machine-readable output"
|
|
245
276
|
);
|
|
@@ -351,4 +382,5 @@ module.exports = {
|
|
|
351
382
|
isVersionCommand,
|
|
352
383
|
main,
|
|
353
384
|
parseCliArgs,
|
|
385
|
+
runCli,
|
|
354
386
|
};
|
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
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// FILE: bridge-status.js
|
|
2
|
+
// Purpose: Owns bridge status publishing and stale-relay heartbeat downgrades.
|
|
3
|
+
// Layer: CLI service helper
|
|
4
|
+
// Exports: createBridgeStatusPublisher, buildHeartbeatBridgeStatus, hasRelayConnectionGoneStale
|
|
5
|
+
// Depends on: timers
|
|
6
|
+
|
|
7
|
+
const BRIDGE_STATUS_HEARTBEAT_INTERVAL_MS = 5_000;
|
|
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;
|
|
11
|
+
const STALE_RELAY_STATUS_MESSAGE = "Relay heartbeat stalled; reconnect pending.";
|
|
12
|
+
|
|
13
|
+
// Wraps daemon status publication so bridge.js does not own heartbeat bookkeeping.
|
|
14
|
+
function createBridgeStatusPublisher({
|
|
15
|
+
onBridgeStatus = null,
|
|
16
|
+
getCodexLaunchState = () => undefined,
|
|
17
|
+
heartbeatIntervalMs = BRIDGE_STATUS_HEARTBEAT_INTERVAL_MS,
|
|
18
|
+
now = () => Date.now(),
|
|
19
|
+
} = {}) {
|
|
20
|
+
let lastPublishedBridgeStatus = null;
|
|
21
|
+
let heartbeatTimer = null;
|
|
22
|
+
|
|
23
|
+
function publish(status) {
|
|
24
|
+
const nextStatus = {
|
|
25
|
+
...status,
|
|
26
|
+
codexLaunchState: getCodexLaunchState(),
|
|
27
|
+
};
|
|
28
|
+
lastPublishedBridgeStatus = nextStatus;
|
|
29
|
+
onBridgeStatus?.(nextStatus);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function startHeartbeat({
|
|
33
|
+
shouldPublish = () => true,
|
|
34
|
+
getLastRelayActivityAt = () => 0,
|
|
35
|
+
} = {}) {
|
|
36
|
+
if (heartbeatTimer) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
heartbeatTimer = setInterval(() => {
|
|
41
|
+
if (!lastPublishedBridgeStatus || !shouldPublish()) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
onBridgeStatus?.(buildHeartbeatBridgeStatus(
|
|
46
|
+
lastPublishedBridgeStatus,
|
|
47
|
+
getLastRelayActivityAt(),
|
|
48
|
+
{ now: now() }
|
|
49
|
+
));
|
|
50
|
+
}, heartbeatIntervalMs);
|
|
51
|
+
heartbeatTimer.unref?.();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function stopHeartbeat() {
|
|
55
|
+
if (!heartbeatTimer) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
clearInterval(heartbeatTimer);
|
|
60
|
+
heartbeatTimer = null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
latest() {
|
|
65
|
+
return lastPublishedBridgeStatus;
|
|
66
|
+
},
|
|
67
|
+
publish,
|
|
68
|
+
startHeartbeat,
|
|
69
|
+
stopHeartbeat,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Treats silent relay sockets as stale so the daemon can self-heal after sleep/wake.
|
|
74
|
+
function hasRelayConnectionGoneStale(
|
|
75
|
+
lastActivityAt,
|
|
76
|
+
{
|
|
77
|
+
now = Date.now(),
|
|
78
|
+
staleAfterMs = RELAY_WATCHDOG_STALE_AFTER_MS,
|
|
79
|
+
} = {}
|
|
80
|
+
) {
|
|
81
|
+
return Number.isFinite(lastActivityAt)
|
|
82
|
+
&& Number.isFinite(now)
|
|
83
|
+
&& now - lastActivityAt >= staleAfterMs;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Keeps persisted daemon status honest by downgrading stale "connected" snapshots.
|
|
87
|
+
function buildHeartbeatBridgeStatus(
|
|
88
|
+
status,
|
|
89
|
+
lastActivityAt,
|
|
90
|
+
{
|
|
91
|
+
now = Date.now(),
|
|
92
|
+
staleAfterMs = RELAY_WATCHDOG_STALE_AFTER_MS,
|
|
93
|
+
staleMessage = STALE_RELAY_STATUS_MESSAGE,
|
|
94
|
+
} = {}
|
|
95
|
+
) {
|
|
96
|
+
if (!status || typeof status !== "object") {
|
|
97
|
+
return status;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (status.connectionStatus !== "connected") {
|
|
101
|
+
return status;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!hasRelayConnectionGoneStale(lastActivityAt, { now, staleAfterMs })) {
|
|
105
|
+
return status;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
...status,
|
|
110
|
+
connectionStatus: "disconnected",
|
|
111
|
+
lastError: staleMessage,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
BRIDGE_STATUS_HEARTBEAT_INTERVAL_MS,
|
|
117
|
+
RELAY_WATCHDOG_STALE_AFTER_MS,
|
|
118
|
+
STALE_RELAY_STATUS_MESSAGE,
|
|
119
|
+
buildHeartbeatBridgeStatus,
|
|
120
|
+
createBridgeStatusPublisher,
|
|
121
|
+
hasRelayConnectionGoneStale,
|
|
122
|
+
};
|