@makerbi/remodex 1.5.4 → 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.js +22 -9
- 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 +752 -71
- 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/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 +796 -15
- package/src/voice-handler.js +129 -62
- package/src/workspace-handler.js +327 -14
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
// Depends on: global fetch
|
|
6
6
|
|
|
7
7
|
const DEFAULT_PUSH_SERVICE_TIMEOUT_MS = 10_000;
|
|
8
|
+
const DEFAULT_PUSH_SERVICE_RETRY_LIMIT = 2;
|
|
9
|
+
const DEFAULT_PUSH_SERVICE_RETRY_BASE_DELAY_MS = 500;
|
|
8
10
|
|
|
9
11
|
function createPushNotificationServiceClient({
|
|
10
12
|
baseUrl = "",
|
|
@@ -13,8 +15,16 @@ function createPushNotificationServiceClient({
|
|
|
13
15
|
fetchImpl = globalThis.fetch,
|
|
14
16
|
logPrefix = "[remodex]",
|
|
15
17
|
requestTimeoutMs = DEFAULT_PUSH_SERVICE_TIMEOUT_MS,
|
|
18
|
+
retryLimit = DEFAULT_PUSH_SERVICE_RETRY_LIMIT,
|
|
19
|
+
retryBaseDelayMs = DEFAULT_PUSH_SERVICE_RETRY_BASE_DELAY_MS,
|
|
20
|
+
sleepImpl = sleep,
|
|
16
21
|
} = {}) {
|
|
17
22
|
const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
|
|
23
|
+
const safeRetryLimit = normalizeNonNegativeInteger(retryLimit, DEFAULT_PUSH_SERVICE_RETRY_LIMIT);
|
|
24
|
+
const safeRetryBaseDelayMs = normalizeNonNegativeInteger(
|
|
25
|
+
retryBaseDelayMs,
|
|
26
|
+
DEFAULT_PUSH_SERVICE_RETRY_BASE_DELAY_MS
|
|
27
|
+
);
|
|
18
28
|
|
|
19
29
|
async function registerDevice({
|
|
20
30
|
deviceToken,
|
|
@@ -55,48 +65,70 @@ function createPushNotificationServiceClient({
|
|
|
55
65
|
return { ok: false, skipped: true };
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
let response;
|
|
68
|
-
try {
|
|
69
|
-
response = await fetchImpl(`${normalizedBaseUrl}${pathname}`, {
|
|
70
|
-
method: "POST",
|
|
71
|
-
headers: {
|
|
72
|
-
"content-type": "application/json",
|
|
73
|
-
},
|
|
74
|
-
body: JSON.stringify(payload),
|
|
75
|
-
signal: controller?.signal,
|
|
76
|
-
});
|
|
77
|
-
} catch (error) {
|
|
78
|
-
if (isAbortError(error)) {
|
|
79
|
-
const timeoutError = new Error(`Push service request timed out after ${requestTimeoutMs}ms`);
|
|
80
|
-
timeoutError.code = "push_request_timeout";
|
|
81
|
-
throw timeoutError;
|
|
68
|
+
const bodyJSON = JSON.stringify(payload);
|
|
69
|
+
let lastError = null;
|
|
70
|
+
for (let attempt = 0; attempt <= safeRetryLimit; attempt += 1) {
|
|
71
|
+
if (attempt > 0) {
|
|
72
|
+
const delayMs = safeRetryBaseDelayMs * Math.pow(2, attempt - 1);
|
|
73
|
+
if (delayMs > 0) {
|
|
74
|
+
await sleepImpl(delayMs);
|
|
75
|
+
}
|
|
82
76
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
|
|
78
|
+
const controller = typeof AbortController === "function" && requestTimeoutMs > 0
|
|
79
|
+
? new AbortController()
|
|
80
|
+
: null;
|
|
81
|
+
const timeoutID = controller
|
|
82
|
+
? setTimeout(() => {
|
|
83
|
+
controller.abort(createTimeoutAbortError(requestTimeoutMs));
|
|
84
|
+
}, requestTimeoutMs)
|
|
85
|
+
: null;
|
|
86
|
+
|
|
87
|
+
let response;
|
|
88
|
+
try {
|
|
89
|
+
response = await fetchImpl(`${normalizedBaseUrl}${pathname}`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: {
|
|
92
|
+
"content-type": "application/json",
|
|
93
|
+
},
|
|
94
|
+
body: bodyJSON,
|
|
95
|
+
signal: controller?.signal,
|
|
96
|
+
});
|
|
97
|
+
} catch (error) {
|
|
98
|
+
lastError = error;
|
|
99
|
+
if (isAbortError(error)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (isRetryableNetworkError(error)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
} finally {
|
|
107
|
+
if (timeoutID) {
|
|
108
|
+
clearTimeout(timeoutID);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const responseText = await response.text();
|
|
113
|
+
const parsed = safeParseJSON(responseText);
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
const message = parsed?.error || parsed?.message || responseText || `HTTP ${response.status}`;
|
|
116
|
+
const error = new Error(message);
|
|
117
|
+
error.status = response.status;
|
|
118
|
+
if (response.status >= 500 && attempt < safeRetryLimit) {
|
|
119
|
+
lastError = error;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
throw error;
|
|
87
123
|
}
|
|
88
|
-
}
|
|
89
124
|
|
|
90
|
-
|
|
91
|
-
const parsed = safeParseJSON(responseText);
|
|
92
|
-
if (!response.ok) {
|
|
93
|
-
const message = parsed?.error || parsed?.message || responseText || `HTTP ${response.status}`;
|
|
94
|
-
const error = new Error(message);
|
|
95
|
-
error.status = response.status;
|
|
96
|
-
throw error;
|
|
125
|
+
return parsed ?? { ok: true };
|
|
97
126
|
}
|
|
98
127
|
|
|
99
|
-
|
|
128
|
+
if (lastError) {
|
|
129
|
+
throw lastError;
|
|
130
|
+
}
|
|
131
|
+
return { ok: false };
|
|
100
132
|
}
|
|
101
133
|
|
|
102
134
|
return {
|
|
@@ -127,6 +159,7 @@ function normalizeBaseUrl(value) {
|
|
|
127
159
|
function createTimeoutAbortError(timeoutMs) {
|
|
128
160
|
const error = new Error(`Push service request timed out after ${timeoutMs}ms`);
|
|
129
161
|
error.name = "AbortError";
|
|
162
|
+
error.code = "push_request_timeout";
|
|
130
163
|
return error;
|
|
131
164
|
}
|
|
132
165
|
|
|
@@ -134,6 +167,21 @@ function isAbortError(error) {
|
|
|
134
167
|
return error?.name === "AbortError" || error?.code === "ABORT_ERR";
|
|
135
168
|
}
|
|
136
169
|
|
|
170
|
+
function isRetryableNetworkError(error) {
|
|
171
|
+
const code = error?.code ?? "";
|
|
172
|
+
return code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ETIMEDOUT"
|
|
173
|
+
|| code === "ENETUNREACH" || code === "EHOSTUNREACH" || code === "ENOTFOUND"
|
|
174
|
+
|| error?.message?.includes("fetch failed");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function normalizeNonNegativeInteger(value, fallback) {
|
|
178
|
+
return Number.isInteger(value) && value >= 0 ? value : fallback;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function sleep(delayMs) {
|
|
182
|
+
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
183
|
+
}
|
|
184
|
+
|
|
137
185
|
function safeParseJSON(value) {
|
|
138
186
|
if (!value || typeof value !== "string") {
|
|
139
187
|
return null;
|
|
@@ -9,6 +9,9 @@ const {
|
|
|
9
9
|
} = require("./push-notification-completion-dedupe");
|
|
10
10
|
|
|
11
11
|
const DEFAULT_PREVIEW_MAX_CHARS = 160;
|
|
12
|
+
const MAX_THREAD_TITLE_ENTRIES = 200;
|
|
13
|
+
const MAX_TURN_STATE_ENTRIES = 500;
|
|
14
|
+
const MAX_THREAD_ID_BY_TURN_ENTRIES = 500;
|
|
12
15
|
|
|
13
16
|
function createPushNotificationTracker({
|
|
14
17
|
sessionId,
|
|
@@ -73,6 +76,10 @@ function createPushNotificationTracker({
|
|
|
73
76
|
// Remembers thread/turn linkage before the terminal event arrives on a different payload shape.
|
|
74
77
|
function rememberMessageContext({ threadId, turnId, params, eventObject }) {
|
|
75
78
|
if (threadId && turnId) {
|
|
79
|
+
if (!threadIdByTurnId.has(turnId) && threadIdByTurnId.size >= MAX_THREAD_ID_BY_TURN_ENTRIES) {
|
|
80
|
+
const oldest = threadIdByTurnId.keys().next().value;
|
|
81
|
+
threadIdByTurnId.delete(oldest);
|
|
82
|
+
}
|
|
76
83
|
threadIdByTurnId.set(turnId, threadId);
|
|
77
84
|
ensureTurnState(threadId, turnId);
|
|
78
85
|
}
|
|
@@ -83,6 +90,10 @@ function createPushNotificationTracker({
|
|
|
83
90
|
|
|
84
91
|
const nextTitle = extractThreadTitle(params, eventObject);
|
|
85
92
|
if (nextTitle) {
|
|
93
|
+
if (!threadTitleById.has(threadId) && threadTitleById.size >= MAX_THREAD_TITLE_ENTRIES) {
|
|
94
|
+
const oldest = threadTitleById.keys().next().value;
|
|
95
|
+
threadTitleById.delete(oldest);
|
|
96
|
+
}
|
|
86
97
|
threadTitleById.set(threadId, nextTitle);
|
|
87
98
|
}
|
|
88
99
|
}
|
|
@@ -225,6 +236,10 @@ function createPushNotificationTracker({
|
|
|
225
236
|
function ensureTurnState(threadId, turnId) {
|
|
226
237
|
const key = turnStateKey(threadId, turnId);
|
|
227
238
|
if (!turnStateByKey.has(key)) {
|
|
239
|
+
if (turnStateByKey.size >= MAX_TURN_STATE_ENTRIES) {
|
|
240
|
+
const oldest = turnStateByKey.keys().next().value;
|
|
241
|
+
turnStateByKey.delete(oldest);
|
|
242
|
+
}
|
|
228
243
|
turnStateByKey.set(key, {
|
|
229
244
|
latestAssistantPreview: "",
|
|
230
245
|
latestFailurePreview: "",
|