@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/src/codex-transport.js
CHANGED
|
@@ -13,6 +13,7 @@ function createCodexTransport({
|
|
|
13
13
|
endpoint = "",
|
|
14
14
|
env = process.env,
|
|
15
15
|
appPath = "",
|
|
16
|
+
platform = process.platform,
|
|
16
17
|
spawnImpl = spawn,
|
|
17
18
|
WebSocketImpl = WebSocket,
|
|
18
19
|
} = {}) {
|
|
@@ -20,11 +21,11 @@ function createCodexTransport({
|
|
|
20
21
|
return createWebSocketTransport({ endpoint, WebSocketImpl });
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
return createSpawnTransport({ env, appPath, spawnImpl });
|
|
24
|
+
return createSpawnTransport({ env, appPath, platform, spawnImpl });
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
function createSpawnTransport({ env, appPath, spawnImpl = spawn }) {
|
|
27
|
-
const launchPlans = createCodexLaunchPlans({ env, appPath });
|
|
27
|
+
function createSpawnTransport({ env, appPath, platform, spawnImpl = spawn }) {
|
|
28
|
+
const launchPlans = createCodexLaunchPlans({ env, appPath, platform });
|
|
28
29
|
let launchIndex = -1;
|
|
29
30
|
let activeLaunch = null;
|
|
30
31
|
let codex = null;
|
|
@@ -154,13 +155,12 @@ function createSpawnTransport({ env, appPath, spawnImpl = spawn }) {
|
|
|
154
155
|
return;
|
|
155
156
|
}
|
|
156
157
|
stdoutBuffer += chunk.toString("utf8");
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
listeners.emitMessage(trimmedLine);
|
|
158
|
+
let newlineIndex;
|
|
159
|
+
while ((newlineIndex = stdoutBuffer.indexOf("\n")) !== -1) {
|
|
160
|
+
const line = stdoutBuffer.substring(0, newlineIndex).trim();
|
|
161
|
+
stdoutBuffer = stdoutBuffer.substring(newlineIndex + 1);
|
|
162
|
+
if (line) {
|
|
163
|
+
listeners.emitMessage(line);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
});
|
package/src/desktop-handler.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// FILE: desktop-handler.js
|
|
2
|
-
// Purpose: Handles explicit desktop handoff, display wake, and
|
|
2
|
+
// Purpose: Handles explicit desktop handoff, display wake, bridge update, and preference RPCs for Codex.app.
|
|
3
3
|
// Layer: Bridge handler
|
|
4
4
|
// Exports: handleDesktopRequest
|
|
5
5
|
// Depends on: child_process, fs, os, path, ./rollout-watch
|
|
@@ -126,6 +126,8 @@ async function handleDesktopMethod(method, params, options = {}) {
|
|
|
126
126
|
return readBridgePreferences(options);
|
|
127
127
|
case "desktop/preferences/update":
|
|
128
128
|
return updateBridgePreferences(params, options);
|
|
129
|
+
case "desktop/bridge/updateAndRestart":
|
|
130
|
+
return updateBridgePackageAndRestart(options);
|
|
129
131
|
default:
|
|
130
132
|
throw desktopError("unknown_method", `Unknown desktop method: ${method}`);
|
|
131
133
|
}
|
|
@@ -360,6 +362,17 @@ async function updateBridgePreferences(params, options = {}) {
|
|
|
360
362
|
});
|
|
361
363
|
}
|
|
362
364
|
|
|
365
|
+
async function updateBridgePackageAndRestart(options = {}) {
|
|
366
|
+
if (typeof options.updateBridgePackageAndRestart !== "function") {
|
|
367
|
+
throw desktopError(
|
|
368
|
+
"unsupported_bridge_update",
|
|
369
|
+
"This bridge does not support iPhone-triggered bridge updates yet."
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return options.updateBridgePackageAndRestart();
|
|
374
|
+
}
|
|
375
|
+
|
|
363
376
|
function resolveThreadId(params) {
|
|
364
377
|
if (!params || typeof params !== "object") {
|
|
365
378
|
return "";
|
|
@@ -11,6 +11,7 @@ const path = require("path");
|
|
|
11
11
|
const FRAME_HEADER_BYTES = 4;
|
|
12
12
|
const MAX_FRAME_BYTES = 256 * 1024 * 1024;
|
|
13
13
|
const REQUEST_TIMEOUT_MS = 10_000;
|
|
14
|
+
const DESKTOP_IPC_ACTION_SOURCE = "desktop-ipc-action-follower";
|
|
14
15
|
const DESKTOP_RESUME_METHODS = new Set(["thread/read", "thread/resume"]);
|
|
15
16
|
const ACTION_METHODS = new Set([
|
|
16
17
|
"item/commandExecution/requestApproval",
|
|
@@ -54,6 +55,7 @@ function createDesktopIpcActionFollower({
|
|
|
54
55
|
onDisconnect,
|
|
55
56
|
});
|
|
56
57
|
const rawStatesByThreadId = new Map();
|
|
58
|
+
const assistantMessageTextsByThreadId = new Map();
|
|
57
59
|
const pendingRoutesByRequestId = new Map();
|
|
58
60
|
const activeThreadIds = new Set();
|
|
59
61
|
const recoveringThreadIds = new Set();
|
|
@@ -84,6 +86,7 @@ function createDesktopIpcActionFollower({
|
|
|
84
86
|
|
|
85
87
|
function stopAll() {
|
|
86
88
|
rawStatesByThreadId.clear();
|
|
89
|
+
assistantMessageTextsByThreadId.clear();
|
|
87
90
|
pendingRoutesByRequestId.clear();
|
|
88
91
|
activeThreadIds.clear();
|
|
89
92
|
recoveringThreadIds.clear();
|
|
@@ -132,11 +135,13 @@ function createDesktopIpcActionFollower({
|
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
rawStatesByThreadId.set(threadId, nextState);
|
|
138
|
+
syncProjectedAssistantDeltas(threadId, previousState, nextState);
|
|
135
139
|
syncProjectedActions(threadId, projectPendingDesktopActions(threadId, nextState));
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
function onDisconnect() {
|
|
139
143
|
rawStatesByThreadId.clear();
|
|
144
|
+
assistantMessageTextsByThreadId.clear();
|
|
140
145
|
pendingRoutesByRequestId.clear();
|
|
141
146
|
recoveringThreadIds.clear();
|
|
142
147
|
queuedChangesByThreadId.clear();
|
|
@@ -273,9 +278,34 @@ function createDesktopIpcActionFollower({
|
|
|
273
278
|
}
|
|
274
279
|
|
|
275
280
|
rawStatesByThreadId.set(threadId, nextState);
|
|
281
|
+
syncProjectedAssistantDeltas(threadId, baselineState, nextState);
|
|
276
282
|
syncProjectedActions(threadId, projectPendingDesktopActions(threadId, nextState));
|
|
277
283
|
}
|
|
278
284
|
|
|
285
|
+
function syncProjectedAssistantDeltas(threadId, previousState, nextState) {
|
|
286
|
+
const previousTexts = assistantMessageTextsByThreadId.get(threadId);
|
|
287
|
+
if (!previousTexts && !previousState) {
|
|
288
|
+
assistantMessageTextsByThreadId.set(threadId, snapshotAssistantMessageTexts(nextState));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const notifications = projectDesktopAssistantDeltaNotifications(
|
|
293
|
+
threadId,
|
|
294
|
+
previousState,
|
|
295
|
+
nextState,
|
|
296
|
+
previousTexts || snapshotAssistantMessageTexts(previousState)
|
|
297
|
+
);
|
|
298
|
+
if (notifications.length === 0) {
|
|
299
|
+
assistantMessageTextsByThreadId.set(threadId, snapshotAssistantMessageTexts(nextState));
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
for (const notification of notifications) {
|
|
304
|
+
sendApplicationResponse(JSON.stringify(notification));
|
|
305
|
+
}
|
|
306
|
+
assistantMessageTextsByThreadId.set(threadId, snapshotAssistantMessageTexts(nextState));
|
|
307
|
+
}
|
|
308
|
+
|
|
279
309
|
return {
|
|
280
310
|
observeInbound,
|
|
281
311
|
stopAll,
|
|
@@ -550,6 +580,93 @@ function projectPendingDesktopActions(threadId, conversationState) {
|
|
|
550
580
|
.filter(Boolean);
|
|
551
581
|
}
|
|
552
582
|
|
|
583
|
+
// Desktop IPC exposes full conversation snapshots/patches, not app-server assistant delta events.
|
|
584
|
+
// Mirror only suffix growth for assistant rows so phones can render the same live text progression.
|
|
585
|
+
function projectDesktopAssistantDeltaNotifications(
|
|
586
|
+
threadId,
|
|
587
|
+
previousState,
|
|
588
|
+
nextState,
|
|
589
|
+
previousTexts = snapshotAssistantMessageTexts(previousState)
|
|
590
|
+
) {
|
|
591
|
+
const nextMessages = collectAssistantMessages(nextState);
|
|
592
|
+
const notifications = [];
|
|
593
|
+
|
|
594
|
+
for (const message of nextMessages) {
|
|
595
|
+
const previousText = previousTexts.get(message.key) || "";
|
|
596
|
+
if (!message.text || !message.text.startsWith(previousText) || message.text.length <= previousText.length) {
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const delta = message.text.slice(previousText.length);
|
|
601
|
+
notifications.push({
|
|
602
|
+
method: "item/agentMessage/delta",
|
|
603
|
+
params: {
|
|
604
|
+
threadId,
|
|
605
|
+
turnId: message.turnId,
|
|
606
|
+
itemId: message.itemId,
|
|
607
|
+
delta,
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return notifications;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function snapshotAssistantMessageTexts(conversationState) {
|
|
616
|
+
return new Map(collectAssistantMessages(conversationState).map((message) => [message.key, message.text]));
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function collectAssistantMessages(conversationState) {
|
|
620
|
+
const turns = Array.isArray(conversationState?.turns) ? conversationState.turns : [];
|
|
621
|
+
const messages = [];
|
|
622
|
+
for (const turn of turns) {
|
|
623
|
+
const turnId = readString(turn?.id) || readString(turn?.turnId) || readString(turn?.turn_id);
|
|
624
|
+
const items = Array.isArray(turn?.items) ? turn.items : [];
|
|
625
|
+
for (const item of items) {
|
|
626
|
+
if (!isAssistantMessageItem(item)) {
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const itemId = readString(item?.id) || readString(item?.itemId) || readString(item?.item_id);
|
|
631
|
+
const text = assistantMessageText(item);
|
|
632
|
+
if (!turnId || !itemId) {
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
messages.push({
|
|
637
|
+
key: `${turnId}:${itemId}`,
|
|
638
|
+
turnId,
|
|
639
|
+
itemId,
|
|
640
|
+
text,
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return messages;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function isAssistantMessageItem(item) {
|
|
648
|
+
const type = normalizeToken(item?.type);
|
|
649
|
+
if (type === "agentmessage" || type === "assistantmessage") {
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
return type === "message" && normalizeToken(item?.role) === "assistant";
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function assistantMessageText(item) {
|
|
656
|
+
const directText = readString(item?.text) || readString(item?.message);
|
|
657
|
+
if (directText) {
|
|
658
|
+
return directText;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const content = Array.isArray(item?.content) ? item.content : [];
|
|
662
|
+
return content
|
|
663
|
+
.map((entry) => entry && typeof entry === "object" ? entry : null)
|
|
664
|
+
.filter(Boolean)
|
|
665
|
+
.map((entry) => readString(entry.text) || readString(entry?.data?.text))
|
|
666
|
+
.filter(Boolean)
|
|
667
|
+
.join("");
|
|
668
|
+
}
|
|
669
|
+
|
|
553
670
|
function projectPendingDesktopAction(threadId, request) {
|
|
554
671
|
const requestId = requestIdKey(request.id);
|
|
555
672
|
const method = readString(request.method);
|
|
@@ -572,6 +689,7 @@ function projectPendingDesktopAction(threadId, request) {
|
|
|
572
689
|
method,
|
|
573
690
|
params: {
|
|
574
691
|
...params,
|
|
692
|
+
remodexActionSource: DESKTOP_IPC_ACTION_SOURCE,
|
|
575
693
|
threadId: readString(params.threadId) || readString(params.thread_id) || threadId,
|
|
576
694
|
},
|
|
577
695
|
};
|
|
@@ -674,6 +792,10 @@ function writeFrame(socket, payload, callback) {
|
|
|
674
792
|
}
|
|
675
793
|
|
|
676
794
|
function resolveDefaultIpcSocketPath() {
|
|
795
|
+
if (process.platform === "win32") {
|
|
796
|
+
return "\\\\.\\pipe\\codex-ipc";
|
|
797
|
+
}
|
|
798
|
+
|
|
677
799
|
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
678
800
|
return path.join(os.tmpdir(), "codex-ipc", `ipc-${uid}.sock`);
|
|
679
801
|
}
|
|
@@ -699,6 +821,12 @@ function readString(value) {
|
|
|
699
821
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
700
822
|
}
|
|
701
823
|
|
|
824
|
+
function normalizeToken(value) {
|
|
825
|
+
return typeof value === "string"
|
|
826
|
+
? value.toLowerCase().replace(/[_-\s]+/g, "")
|
|
827
|
+
: "";
|
|
828
|
+
}
|
|
829
|
+
|
|
702
830
|
function cloneJSON(value) {
|
|
703
831
|
return JSON.parse(JSON.stringify(value));
|
|
704
832
|
}
|
|
@@ -715,6 +843,7 @@ module.exports = {
|
|
|
715
843
|
applyConversationStateChange,
|
|
716
844
|
createDesktopIpcActionFollower,
|
|
717
845
|
desktopFollowerPayloadForResponse,
|
|
846
|
+
projectDesktopAssistantDeltaNotifications,
|
|
718
847
|
projectPendingDesktopActions,
|
|
719
848
|
resolveDefaultIpcSocketPath,
|
|
720
849
|
seedConversationStateFromThreadRead,
|
package/src/git-handler.js
CHANGED
|
@@ -20,9 +20,11 @@ const GITHUB_CLI_TIMEOUT_MS = 120_000;
|
|
|
20
20
|
const GIT_DRAFT_PATCH_MAX_BYTES = 80_000;
|
|
21
21
|
const EMPTY_TREE_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
22
22
|
const DEFAULT_GIT_WRITER_MODEL = "gpt-5.4-mini";
|
|
23
|
+
const STATUS_UPSTREAM_FETCH_TTL_MS = 15_000;
|
|
23
24
|
|
|
24
25
|
let runStructuredCodexJsonImpl = runStructuredCodexJson;
|
|
25
26
|
let runGitHubCliImpl = runGitHubCli;
|
|
27
|
+
const statusUpstreamFetchCache = new Map();
|
|
26
28
|
|
|
27
29
|
function resolveGitWriterModel(rawModel) {
|
|
28
30
|
const trimmed = typeof rawModel === "string" ? rawModel.trim() : "";
|
|
@@ -172,9 +174,43 @@ async function gitStatus(cwd) {
|
|
|
172
174
|
return nonRepositoryStatus(cwd);
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
const
|
|
177
|
+
const snapshot = await readGitStatusSnapshot(cwd);
|
|
178
|
+
const { ahead, behind } = await freshBranchInfoForStatus(cwd, snapshot);
|
|
179
|
+
const dirty = snapshot.files.length > 0;
|
|
180
|
+
const noUpstream = snapshot.tracking === null && !snapshot.detached;
|
|
181
|
+
const hasHeadCommit = await refExists(cwd, "HEAD").catch(() => false);
|
|
182
|
+
const hasPushRemote = await pushRemoteAvailable(cwd, snapshot.tracking).catch(() => false);
|
|
183
|
+
const publishedToRemote = !snapshot.detached && !!snapshot.branch && await remoteBranchExists(cwd, snapshot.branch).catch(() => false);
|
|
184
|
+
const localOnlyCommitCount = await countLocalOnlyCommits(cwd, { detached: snapshot.detached }).catch(() => 0);
|
|
185
|
+
const state = computeState(dirty, ahead, behind, snapshot.detached, noUpstream);
|
|
186
|
+
const canPush = hasPushRemote && hasHeadCommit && (ahead > 0 || noUpstream) && !snapshot.detached;
|
|
187
|
+
const diff = await repoDiffTotals(cwd, {
|
|
188
|
+
tracking: snapshot.tracking,
|
|
189
|
+
fileLines: snapshot.fileLines,
|
|
190
|
+
}).catch(() => ({ additions: 0, deletions: 0, binaryFiles: 0 }));
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
isRepo: true,
|
|
194
|
+
repoRoot: snapshot.repoRoot,
|
|
195
|
+
branch: snapshot.branch,
|
|
196
|
+
tracking: snapshot.tracking,
|
|
197
|
+
dirty,
|
|
198
|
+
hasHeadCommit,
|
|
199
|
+
hasPushRemote,
|
|
200
|
+
ahead,
|
|
201
|
+
behind,
|
|
202
|
+
localOnlyCommitCount,
|
|
203
|
+
state,
|
|
204
|
+
canPush,
|
|
205
|
+
publishedToRemote,
|
|
206
|
+
files: snapshot.files,
|
|
207
|
+
diff,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function readGitStatusSnapshot(cwd) {
|
|
212
|
+
const [porcelain, repoRoot] = await Promise.all([
|
|
176
213
|
git(cwd, "status", "--porcelain=v1", "-b"),
|
|
177
|
-
revListCounts(cwd).catch(() => ({ ahead: 0, behind: 0 })),
|
|
178
214
|
resolveRepoRoot(cwd).catch(() => null),
|
|
179
215
|
]);
|
|
180
216
|
|
|
@@ -184,45 +220,27 @@ async function gitStatus(cwd) {
|
|
|
184
220
|
|
|
185
221
|
const branch = parseBranchFromStatus(branchLine);
|
|
186
222
|
const tracking = parseTrackingFromStatus(branchLine);
|
|
223
|
+
const detached = branchLine.includes("HEAD detached") || branchLine.includes("no branch");
|
|
187
224
|
const files = fileLines.map((line) => ({
|
|
188
225
|
path: line.substring(3).trim(),
|
|
189
226
|
status: line.substring(0, 2).trim(),
|
|
190
227
|
}));
|
|
191
228
|
|
|
192
|
-
const dirty = files.length > 0;
|
|
193
|
-
const { ahead, behind } = branchInfo;
|
|
194
|
-
const detached = branchLine.includes("HEAD detached") || branchLine.includes("no branch");
|
|
195
|
-
const noUpstream = tracking === null && !detached;
|
|
196
|
-
const hasHeadCommit = await refExists(cwd, "HEAD").catch(() => false);
|
|
197
|
-
const hasPushRemote = await pushRemoteAvailable(cwd, tracking).catch(() => false);
|
|
198
|
-
const publishedToRemote = !detached && !!branch && await remoteBranchExists(cwd, branch).catch(() => false);
|
|
199
|
-
const localOnlyCommitCount = await countLocalOnlyCommits(cwd, { detached }).catch(() => 0);
|
|
200
|
-
const state = computeState(dirty, ahead, behind, detached, noUpstream);
|
|
201
|
-
const canPush = hasPushRemote && hasHeadCommit && (ahead > 0 || noUpstream) && !detached;
|
|
202
|
-
const diff = await repoDiffTotals(cwd, {
|
|
203
|
-
tracking,
|
|
204
|
-
fileLines,
|
|
205
|
-
}).catch(() => ({ additions: 0, deletions: 0, binaryFiles: 0 }));
|
|
206
|
-
|
|
207
229
|
return {
|
|
208
|
-
isRepo: true,
|
|
209
230
|
repoRoot,
|
|
210
231
|
branch,
|
|
211
232
|
tracking,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
hasPushRemote,
|
|
215
|
-
ahead,
|
|
216
|
-
behind,
|
|
217
|
-
localOnlyCommitCount,
|
|
218
|
-
state,
|
|
219
|
-
canPush,
|
|
220
|
-
publishedToRemote,
|
|
233
|
+
detached,
|
|
234
|
+
fileLines,
|
|
221
235
|
files,
|
|
222
|
-
diff,
|
|
223
236
|
};
|
|
224
237
|
}
|
|
225
238
|
|
|
239
|
+
async function freshBranchInfoForStatus(cwd, snapshot) {
|
|
240
|
+
await refreshStatusUpstreamIfNeeded(cwd, snapshot.tracking, snapshot.repoRoot).catch(() => false);
|
|
241
|
+
return await revListCounts(cwd).catch(() => ({ ahead: 0, behind: 0 }));
|
|
242
|
+
}
|
|
243
|
+
|
|
226
244
|
async function gitInit(cwd) {
|
|
227
245
|
if (await isInsideGitWorkTree(cwd)) {
|
|
228
246
|
throw gitError("already_git_repository", "This folder is already inside a Git repository.");
|
|
@@ -2506,6 +2524,52 @@ async function revListCounts(cwd) {
|
|
|
2506
2524
|
};
|
|
2507
2525
|
}
|
|
2508
2526
|
|
|
2527
|
+
// Keeps Update eligibility based on the current upstream ref, not stale local fetch data.
|
|
2528
|
+
async function refreshStatusUpstreamIfNeeded(cwd, tracking, repoRoot) {
|
|
2529
|
+
const parsedTracking = parseTrackingRef(tracking);
|
|
2530
|
+
if (!parsedTracking) {
|
|
2531
|
+
return false;
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
const cacheKey = `${repoRoot || cwd}\0${parsedTracking.remote}\0${parsedTracking.branch}`;
|
|
2535
|
+
const now = Date.now();
|
|
2536
|
+
const lastFetchAt = statusUpstreamFetchCache.get(cacheKey) || 0;
|
|
2537
|
+
if (now - lastFetchAt < STATUS_UPSTREAM_FETCH_TTL_MS) {
|
|
2538
|
+
return false;
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
statusUpstreamFetchCache.set(cacheKey, now);
|
|
2542
|
+
if (statusUpstreamFetchCache.size > 200) {
|
|
2543
|
+
statusUpstreamFetchCache.clear();
|
|
2544
|
+
statusUpstreamFetchCache.set(cacheKey, now);
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
await git(
|
|
2548
|
+
cwd,
|
|
2549
|
+
"fetch",
|
|
2550
|
+
"--quiet",
|
|
2551
|
+
parsedTracking.remote,
|
|
2552
|
+
`+refs/heads/${parsedTracking.branch}:refs/remotes/${parsedTracking.remote}/${parsedTracking.branch}`
|
|
2553
|
+
);
|
|
2554
|
+
return true;
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
function parseTrackingRef(tracking) {
|
|
2558
|
+
if (typeof tracking !== "string") {
|
|
2559
|
+
return null;
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
const separatorIndex = tracking.indexOf("/");
|
|
2563
|
+
if (separatorIndex <= 0 || separatorIndex === tracking.length - 1) {
|
|
2564
|
+
return null;
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
return {
|
|
2568
|
+
remote: tracking.slice(0, separatorIndex),
|
|
2569
|
+
branch: tracking.slice(separatorIndex + 1),
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2509
2573
|
function parseBranchFromStatus(line) {
|
|
2510
2574
|
// "## main...origin/main" or "## main" or "## HEAD (no branch)"
|
|
2511
2575
|
const match = line.match(/^## (.+?)(?:\.{3}|$)/);
|
package/src/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Depends on: ./bridge, ./secure-device-state, ./session-state, ./rollout-watch, ./macos-launch-agent
|
|
6
6
|
|
|
7
7
|
const { startBridge } = require("./bridge");
|
|
8
|
-
const { readBridgeDeviceState,
|
|
8
|
+
const { readBridgeDeviceState, resetBridgeTrustState } = require("./secure-device-state");
|
|
9
9
|
const { openLastActiveThread } = require("./session-state");
|
|
10
10
|
const { watchThreadRollout } = require("./rollout-watch");
|
|
11
11
|
const { readBridgeConfig } = require("./codex-desktop-refresher");
|
|
@@ -14,6 +14,7 @@ const {
|
|
|
14
14
|
printMacOSBridgePairingQr,
|
|
15
15
|
printMacOSBridgeServiceStatus,
|
|
16
16
|
resetMacOSBridgePairing,
|
|
17
|
+
restartMacOSBridgeService,
|
|
17
18
|
runMacOSBridgeService,
|
|
18
19
|
startMacOSBridgeService,
|
|
19
20
|
stopMacOSBridgeService,
|
|
@@ -26,11 +27,12 @@ module.exports = {
|
|
|
26
27
|
readBridgeConfig,
|
|
27
28
|
readBridgeDeviceState,
|
|
28
29
|
resetMacOSBridgePairing,
|
|
30
|
+
restartMacOSBridgeService,
|
|
29
31
|
startBridge,
|
|
30
32
|
runMacOSBridgeService,
|
|
31
33
|
startMacOSBridgeService,
|
|
32
34
|
stopMacOSBridgeService,
|
|
33
|
-
resetBridgePairing:
|
|
35
|
+
resetBridgePairing: resetBridgeTrustState,
|
|
34
36
|
openLastActiveThread,
|
|
35
37
|
watchThreadRollout,
|
|
36
38
|
};
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
// Exports: version comparison + bridge/iPhone compatibility helpers
|
|
5
5
|
// Depends on: none
|
|
6
6
|
|
|
7
|
-
const MINIMUM_SUPPORTED_IOS_APP_VERSION = "
|
|
8
|
-
const IOS_APP_COMPATIBILITY_GATE_BRIDGE_VERSION = "
|
|
9
|
-
const
|
|
10
|
-
const LEGACY_BRIDGE_DOWNGRADE_COMMAND = `npm install -g remodex@${
|
|
7
|
+
const MINIMUM_SUPPORTED_IOS_APP_VERSION = "2.0";
|
|
8
|
+
const IOS_APP_COMPATIBILITY_GATE_BRIDGE_VERSION = "2.0.0";
|
|
9
|
+
const LEGACY_BRIDGE_VERSION_FOR_IOS_1_X = "1.5.1";
|
|
10
|
+
const LEGACY_BRIDGE_DOWNGRADE_COMMAND = `npm install -g @makerbi/remodex@${LEGACY_BRIDGE_VERSION_FOR_IOS_1_X}`;
|
|
11
11
|
const NOTICE_BOX_WIDTH = 74;
|
|
12
12
|
|
|
13
13
|
function buildIOSAppCompatibilitySnapshot({
|
|
@@ -80,7 +80,7 @@ function buildSnapshot({
|
|
|
80
80
|
isCompatible,
|
|
81
81
|
requiresAppUpdate,
|
|
82
82
|
minimumSupportedIOSAppVersion: MINIMUM_SUPPORTED_IOS_APP_VERSION,
|
|
83
|
-
legacyBridgeVersion:
|
|
83
|
+
legacyBridgeVersion: LEGACY_BRIDGE_VERSION_FOR_IOS_1_X,
|
|
84
84
|
downgradeCommand: LEGACY_BRIDGE_DOWNGRADE_COMMAND,
|
|
85
85
|
message,
|
|
86
86
|
};
|
|
@@ -108,7 +108,7 @@ function buildLegacyIOSAppCompatibilityMessage({
|
|
|
108
108
|
return `Remodex bridge ${normalizedBridgeVersion} requires Remodex iPhone `
|
|
109
109
|
+ `${MINIMUM_SUPPORTED_IOS_APP_VERSION} or later. `
|
|
110
110
|
+ `Update the iPhone app from the App Store first, or install Remodex bridge `
|
|
111
|
-
+ `${
|
|
111
|
+
+ `${LEGACY_BRIDGE_VERSION_FOR_IOS_1_X} to keep using iPhone ${normalizedIOSAppVersion}.`;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
function buildCachedIOSAppCompatibilityWarning({
|
|
@@ -232,7 +232,7 @@ function splitVersionParts(value) {
|
|
|
232
232
|
|
|
233
233
|
module.exports = {
|
|
234
234
|
LEGACY_BRIDGE_DOWNGRADE_COMMAND,
|
|
235
|
-
|
|
235
|
+
LEGACY_BRIDGE_VERSION_FOR_IOS_1_X,
|
|
236
236
|
IOS_APP_COMPATIBILITY_GATE_BRIDGE_VERSION,
|
|
237
237
|
MINIMUM_SUPPORTED_IOS_APP_VERSION,
|
|
238
238
|
buildCachedIOSAppCompatibilityWarning,
|