@makerbi/remodex 2.0.0 → 2.3.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 +1 -1
- package/package.json +2 -2
- package/src/account-status.js +5 -4
- package/src/bridge.js +1809 -689
- package/src/codex-desktop-refresher.js +35 -7
- package/src/cursor-acp-client.js +242 -0
- package/src/cursor-models.js +134 -0
- package/src/cursor-provider.js +1197 -0
- package/src/desktop-ipc-action-follower.js +2323 -123
- package/src/desktop-ipc-conversation-adapter.js +1132 -0
- package/src/desktop-ipc-conversation-projector.js +1169 -0
- package/src/desktop-ipc-live-owner.js +1790 -0
- package/src/desktop-ipc-owner-transport.js +750 -0
- package/src/desktop-ipc-shared.js +473 -0
- package/src/desktop-ipc-state-patches.js +218 -0
- package/src/opencode-models.js +108 -0
- package/src/opencode-provider.js +1151 -0
- package/src/project-handler.js +50 -7
- package/src/project-registry.js +466 -0
- package/src/push-notification-tracker.js +4 -4
- package/src/rollout-live-mirror.js +946 -78
- package/src/rollout-turn-semantics.js +20 -0
- package/src/runtime-provider-models.js +164 -0
- package/src/runtime-provider-router.js +365 -0
- package/src/scripts/codex-refresh.applescript +26 -15
- package/src/secure-transport.js +204 -9
- package/src/session-jsonl-history.js +429 -39
- package/src/thread-context-handler.js +8 -6
- package/src/thread-runtime-settings-store.js +247 -0
- package/src/voice-audio.js +344 -0
- package/src/voice-handler.js +363 -173
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// FILE: rollout-turn-semantics.js
|
|
2
|
+
// Purpose: Shared desktop rollout turn-lifecycle semantics for the live mirror
|
|
3
|
+
// and the JSONL history fallback, so both agree on what ends a run.
|
|
4
|
+
|
|
5
|
+
// Any of these event_msg types ends a desktop run; task_complete is not the only
|
|
6
|
+
// terminal outcome (Stop on desktop writes turn_aborted, fatal failures write error).
|
|
7
|
+
const TERMINAL_TASK_EVENT_TYPES = new Set(["task_complete", "turn_aborted", "error"]);
|
|
8
|
+
|
|
9
|
+
// Desktop interleaves parallel turns in one rollout file. A terminal event only
|
|
10
|
+
// closes the tracked turn when it targets that turn, carries no explicit id, or
|
|
11
|
+
// nothing is tracked; a sibling turn's terminal event must leave the tracked
|
|
12
|
+
// run open.
|
|
13
|
+
function terminalEventClosesTrackedTurn(eventTurnId, trackedTurnId) {
|
|
14
|
+
return !eventTurnId || !trackedTurnId || eventTurnId === trackedTurnId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
TERMINAL_TASK_EVENT_TYPES,
|
|
19
|
+
terminalEventClosesTrackedTurn,
|
|
20
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// FILE: runtime-provider-models.js
|
|
2
|
+
// Purpose: Normalizes provider-aware model/thread metadata shared by local runtime providers.
|
|
3
|
+
// Layer: Bridge runtime provider helper
|
|
4
|
+
// Exports: provider constants plus model/provider parsing helpers.
|
|
5
|
+
// Depends on: none
|
|
6
|
+
|
|
7
|
+
const CODEX_PROVIDER_ID = "codex";
|
|
8
|
+
const CURSOR_PROVIDER_ID = "cursor";
|
|
9
|
+
const OPENCODE_PROVIDER_ID = "opencode";
|
|
10
|
+
|
|
11
|
+
const PROVIDER_FIELD_KEYS = [
|
|
12
|
+
"modelProvider",
|
|
13
|
+
"model_provider",
|
|
14
|
+
"provider",
|
|
15
|
+
"runtimeProvider",
|
|
16
|
+
"runtime_provider",
|
|
17
|
+
"harness",
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function normalizeRuntimeProvider(value) {
|
|
21
|
+
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
22
|
+
switch (normalized) {
|
|
23
|
+
case "":
|
|
24
|
+
return CODEX_PROVIDER_ID;
|
|
25
|
+
case "cursor-agent":
|
|
26
|
+
case "cursor_cli":
|
|
27
|
+
case "cursor-cli":
|
|
28
|
+
return CURSOR_PROVIDER_ID;
|
|
29
|
+
case "open-code":
|
|
30
|
+
case "open_code":
|
|
31
|
+
return OPENCODE_PROVIDER_ID;
|
|
32
|
+
case "claude-code":
|
|
33
|
+
case "claudecode":
|
|
34
|
+
return "claude";
|
|
35
|
+
default:
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function readModelProvider(value = {}) {
|
|
41
|
+
if (!value || typeof value !== "object") {
|
|
42
|
+
return CODEX_PROVIDER_ID;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return normalizeRuntimeProvider(
|
|
46
|
+
value.modelProvider
|
|
47
|
+
|| value.model_provider
|
|
48
|
+
|| value.provider
|
|
49
|
+
|| value.runtimeProvider
|
|
50
|
+
|| value.runtime_provider
|
|
51
|
+
|| value.harness
|
|
52
|
+
|| value.collaborationMode?.settings?.modelProvider
|
|
53
|
+
|| value.collaborationMode?.settings?.model_provider
|
|
54
|
+
|| value.collaborationMode?.settings?.provider
|
|
55
|
+
|| value.collaboration_mode?.settings?.modelProvider
|
|
56
|
+
|| value.collaboration_mode?.settings?.model_provider
|
|
57
|
+
|| value.collaboration_mode?.settings?.provider
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function hasExplicitProviderField(value = {}) {
|
|
62
|
+
if (!value || typeof value !== "object") {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return hasOwnProviderField(value)
|
|
67
|
+
|| hasOwnProviderField(value.collaborationMode?.settings)
|
|
68
|
+
|| hasOwnProviderField(value.collaboration_mode?.settings);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isCodexProvider(value) {
|
|
72
|
+
return normalizeRuntimeProvider(value) === CODEX_PROVIDER_ID;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isCursorProvider(value) {
|
|
76
|
+
return normalizeRuntimeProvider(value) === CURSOR_PROVIDER_ID;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isOpenCodeProvider(value) {
|
|
80
|
+
return normalizeRuntimeProvider(value) === OPENCODE_PROVIDER_ID;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function buildRuntimeModelOption({
|
|
84
|
+
provider,
|
|
85
|
+
id,
|
|
86
|
+
model = id,
|
|
87
|
+
displayName,
|
|
88
|
+
description = "",
|
|
89
|
+
isDefault = false,
|
|
90
|
+
supportsFastMode = false,
|
|
91
|
+
supportedReasoningEfforts = [],
|
|
92
|
+
defaultReasoningEffort = null,
|
|
93
|
+
}) {
|
|
94
|
+
const normalizedProvider = normalizeRuntimeProvider(provider);
|
|
95
|
+
const normalizedId = readString(id || model);
|
|
96
|
+
const normalizedModel = readString(model || id);
|
|
97
|
+
if (!normalizedProvider || !normalizedId || !normalizedModel) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
id: normalizedId,
|
|
103
|
+
model: normalizedModel,
|
|
104
|
+
modelProvider: normalizedProvider,
|
|
105
|
+
provider: normalizedProvider,
|
|
106
|
+
displayName: readString(displayName) || normalizedModel,
|
|
107
|
+
description: readString(description),
|
|
108
|
+
isDefault,
|
|
109
|
+
supportsFastMode,
|
|
110
|
+
supportedReasoningEfforts,
|
|
111
|
+
defaultReasoningEffort,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function stripProviderFields(value) {
|
|
116
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const clone = { ...value };
|
|
121
|
+
for (const key of PROVIDER_FIELD_KEYS) {
|
|
122
|
+
delete clone[key];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (clone.collaborationMode?.settings) {
|
|
126
|
+
clone.collaborationMode = {
|
|
127
|
+
...clone.collaborationMode,
|
|
128
|
+
settings: stripProviderFields(clone.collaborationMode.settings),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (clone.collaboration_mode?.settings) {
|
|
132
|
+
clone.collaboration_mode = {
|
|
133
|
+
...clone.collaboration_mode,
|
|
134
|
+
settings: stripProviderFields(clone.collaboration_mode.settings),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return clone;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function readString(value) {
|
|
141
|
+
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function hasOwnProviderField(value = {}) {
|
|
145
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return PROVIDER_FIELD_KEYS.some((key) => Object.prototype.hasOwnProperty.call(value, key));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
CODEX_PROVIDER_ID,
|
|
153
|
+
CURSOR_PROVIDER_ID,
|
|
154
|
+
OPENCODE_PROVIDER_ID,
|
|
155
|
+
PROVIDER_FIELD_KEYS,
|
|
156
|
+
buildRuntimeModelOption,
|
|
157
|
+
hasExplicitProviderField,
|
|
158
|
+
isCodexProvider,
|
|
159
|
+
isCursorProvider,
|
|
160
|
+
isOpenCodeProvider,
|
|
161
|
+
normalizeRuntimeProvider,
|
|
162
|
+
readModelProvider,
|
|
163
|
+
stripProviderFields,
|
|
164
|
+
};
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
// FILE: runtime-provider-router.js
|
|
2
|
+
// Purpose: Routes provider-aware Remodex RPCs between Codex app-server and local runtime providers.
|
|
3
|
+
// Layer: Bridge runtime routing
|
|
4
|
+
// Exports: createRuntimeProviderRouter plus merge helpers used by tests.
|
|
5
|
+
// Depends on: ./cursor-provider, ./opencode-provider, ./runtime-provider-models
|
|
6
|
+
|
|
7
|
+
const { createCursorProvider } = require("./cursor-provider");
|
|
8
|
+
const { createOpenCodeProvider } = require("./opencode-provider");
|
|
9
|
+
const {
|
|
10
|
+
CODEX_PROVIDER_ID,
|
|
11
|
+
readModelProvider,
|
|
12
|
+
stripProviderFields,
|
|
13
|
+
} = require("./runtime-provider-models");
|
|
14
|
+
|
|
15
|
+
const ROUTABLE_THREAD_METHODS = new Set([
|
|
16
|
+
"thread/start",
|
|
17
|
+
"thread/resume",
|
|
18
|
+
"thread/read",
|
|
19
|
+
"thread/turns/list",
|
|
20
|
+
"thread/name/set",
|
|
21
|
+
"thread/archive",
|
|
22
|
+
"thread/unarchive",
|
|
23
|
+
"turn/start",
|
|
24
|
+
"turn/interrupt",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
function createRuntimeProviderRouter({
|
|
28
|
+
sendCodexRequest,
|
|
29
|
+
sendApplicationResponse,
|
|
30
|
+
sendRuntimeMessage,
|
|
31
|
+
providers = null,
|
|
32
|
+
projectRegistry = null,
|
|
33
|
+
logPrefix = "[remodex]",
|
|
34
|
+
} = {}) {
|
|
35
|
+
const runtimeProviders = providers || [
|
|
36
|
+
createCursorProvider({
|
|
37
|
+
sendApplicationMessage: (message) => sendRuntimeProviderMessage("cursor", message),
|
|
38
|
+
logPrefix,
|
|
39
|
+
}),
|
|
40
|
+
createOpenCodeProvider({
|
|
41
|
+
sendApplicationMessage: (message) => sendRuntimeProviderMessage("opencode", message),
|
|
42
|
+
projectRegistry,
|
|
43
|
+
logPrefix,
|
|
44
|
+
}),
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
function sendRuntimeProviderMessage(provider, message) {
|
|
48
|
+
if (sendRuntimeMessage) {
|
|
49
|
+
sendRuntimeMessage(provider, message);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
sendApplicationResponse(message);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleApplicationMessage(rawMessage, options = {}) {
|
|
56
|
+
const sendResponse = options.sendResponse || sendApplicationResponse;
|
|
57
|
+
const parsed = safeParseJSON(rawMessage);
|
|
58
|
+
if (!parsed) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const responseProvider = runtimeProviders.find((provider) => (
|
|
63
|
+
parsed.id != null
|
|
64
|
+
&& !parsed.method
|
|
65
|
+
&& typeof provider.handleApplicationResponse === "function"
|
|
66
|
+
&& provider.handleApplicationResponse(parsed)
|
|
67
|
+
));
|
|
68
|
+
if (responseProvider) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const method = readString(parsed.method);
|
|
73
|
+
if (!method) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (method === "model/list") {
|
|
78
|
+
respondAsync(parsed, sendResponse, async () => {
|
|
79
|
+
const codexResult = await sendCodexRequest("model/list", stripProviderFields(parsed.params || {}));
|
|
80
|
+
const providerModels = await listProviderModels(runtimeProviders);
|
|
81
|
+
return mergeModelListResult(codexResult, providerModels);
|
|
82
|
+
});
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (method === "thread/list") {
|
|
87
|
+
respondAsync(parsed, sendResponse, async () => {
|
|
88
|
+
const codexResult = await sendCodexRequest("thread/list", stripProviderFields(parsed.params || {}));
|
|
89
|
+
const providerThreads = hasCursor(parsed.params)
|
|
90
|
+
? []
|
|
91
|
+
: await listProviderThreads(runtimeProviders, parsed.params || {});
|
|
92
|
+
registerThreadProjects(projectRegistry, threadsFromListResult(codexResult), {
|
|
93
|
+
source: "codex-thread-list",
|
|
94
|
+
provider: CODEX_PROVIDER_ID,
|
|
95
|
+
});
|
|
96
|
+
registerThreadProjects(projectRegistry, providerThreads, {
|
|
97
|
+
source: "provider-thread-list",
|
|
98
|
+
});
|
|
99
|
+
return mergeThreadListResult(codexResult, providerThreads);
|
|
100
|
+
});
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!ROUTABLE_THREAD_METHODS.has(method)) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const provider = providerForRequest(parsed, runtimeProviders);
|
|
109
|
+
if (!provider) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
rememberProjectFromRequest(projectRegistry, parsed, {
|
|
114
|
+
source: "provider-request",
|
|
115
|
+
provider: provider.id,
|
|
116
|
+
});
|
|
117
|
+
respondAsync(parsed, sendResponse, () => provider.handleRequest(parsed));
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function respondAsync(request, sendResponse, resolveResult) {
|
|
122
|
+
Promise.resolve()
|
|
123
|
+
.then(resolveResult)
|
|
124
|
+
.then((result) => {
|
|
125
|
+
if (request.id != null) {
|
|
126
|
+
sendResponse(JSON.stringify({
|
|
127
|
+
id: request.id,
|
|
128
|
+
result,
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.catch((error) => {
|
|
133
|
+
if (request.id != null) {
|
|
134
|
+
sendResponse(createJsonRpcErrorResponse(
|
|
135
|
+
request.id,
|
|
136
|
+
error,
|
|
137
|
+
error?.errorCode || "runtime_provider_failed"
|
|
138
|
+
));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
handleApplicationMessage,
|
|
145
|
+
providers: runtimeProviders,
|
|
146
|
+
shutdown() {
|
|
147
|
+
for (const provider of runtimeProviders) {
|
|
148
|
+
provider.shutdown?.();
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function listProviderModels(providers) {
|
|
155
|
+
const settled = await Promise.allSettled(providers.map((provider) => provider.listModels()));
|
|
156
|
+
return settled.flatMap((result) => (result.status === "fulfilled" ? result.value : []));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function listProviderThreads(providers, params) {
|
|
160
|
+
const settled = await Promise.allSettled(providers.map((provider) => provider.listThreads(params)));
|
|
161
|
+
return settled.flatMap((result) => {
|
|
162
|
+
if (result.status !== "fulfilled") {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
const payload = result.value;
|
|
166
|
+
return Array.isArray(payload?.data) ? payload.data : [];
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function providerForRequest(request, providers) {
|
|
171
|
+
const params = request.params || {};
|
|
172
|
+
const providerFromRequest = readModelProvider(params);
|
|
173
|
+
if (providerFromRequest !== CODEX_PROVIDER_ID) {
|
|
174
|
+
return providers.find((provider) => provider.canHandleProvider?.(providerFromRequest)) || null;
|
|
175
|
+
}
|
|
176
|
+
if (hasExplicitProviderField(params)) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const threadId = readThreadId(params);
|
|
181
|
+
if (!threadId) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return providers.find((provider) => provider.ownsThread?.(threadId)) || null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function mergeModelListResult(codexResult, providerModels) {
|
|
189
|
+
const result = codexResult && typeof codexResult === "object" ? codexResult : {};
|
|
190
|
+
const key = firstArrayKey(result, ["items", "data", "models"]) || "items";
|
|
191
|
+
const codexModels = Array.isArray(result[key]) ? result[key] : [];
|
|
192
|
+
const normalizedCodexModels = codexModels.map((model) => ({
|
|
193
|
+
...model,
|
|
194
|
+
modelProvider: CODEX_PROVIDER_ID,
|
|
195
|
+
provider: CODEX_PROVIDER_ID,
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
...result,
|
|
200
|
+
[key]: [
|
|
201
|
+
...normalizedCodexModels,
|
|
202
|
+
...providerModels,
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function mergeThreadListResult(codexResult, providerThreads) {
|
|
208
|
+
const result = codexResult && typeof codexResult === "object" ? codexResult : {};
|
|
209
|
+
const key = firstArrayKey(result, ["data", "items", "threads"]) || "data";
|
|
210
|
+
const codexThreads = Array.isArray(result[key]) ? result[key] : [];
|
|
211
|
+
const merged = dedupeMergedThreads(codexThreads, providerThreads).sort(compareThreadsByUpdatedAt);
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
...result,
|
|
215
|
+
[key]: merged,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function dedupeMergedThreads(codexThreads, providerThreads) {
|
|
220
|
+
const mergedById = new Map();
|
|
221
|
+
for (const thread of codexThreads) {
|
|
222
|
+
const threadId = readThreadIdentifier(thread);
|
|
223
|
+
if (threadId) {
|
|
224
|
+
mergedById.set(threadId, thread);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
for (const thread of providerThreads) {
|
|
229
|
+
const threadId = readThreadIdentifier(thread);
|
|
230
|
+
if (!threadId) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (!mergedById.has(threadId) || readModelProvider(thread) !== CODEX_PROVIDER_ID) {
|
|
234
|
+
mergedById.set(threadId, thread);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return Array.from(mergedById.values());
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function threadsFromListResult(result) {
|
|
241
|
+
const key = firstArrayKey(result, ["data", "items", "threads"]);
|
|
242
|
+
return key && Array.isArray(result?.[key]) ? result[key] : [];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function registerThreadProjects(projectRegistry, threads, metadata = {}) {
|
|
246
|
+
if (!projectRegistry || !Array.isArray(threads) || !threads.length) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
projectRegistry.rememberProjectsFromThreads(threads, metadata);
|
|
252
|
+
} catch {
|
|
253
|
+
// Project history is a cache; provider routing should not fail when it cannot be persisted.
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function rememberProjectFromRequest(projectRegistry, request, metadata = {}) {
|
|
258
|
+
if (!projectRegistry) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const params = request?.params || {};
|
|
263
|
+
const cwd = readString(params.cwd || params.current_working_directory || params.working_directory);
|
|
264
|
+
if (!cwd) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
projectRegistry.rememberProjectPath(cwd, metadata);
|
|
270
|
+
} catch {
|
|
271
|
+
// Best-effort cache write; the runtime request remains authoritative.
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function stripRuntimeProviderFieldsForCodex(rawMessage) {
|
|
276
|
+
const parsed = safeParseJSON(rawMessage);
|
|
277
|
+
if (!parsed || !parsed.params || typeof parsed.params !== "object" || Array.isArray(parsed.params)) {
|
|
278
|
+
return rawMessage;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return JSON.stringify({
|
|
282
|
+
...parsed,
|
|
283
|
+
params: stripProviderFields(parsed.params),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function compareThreadsByUpdatedAt(lhs, rhs) {
|
|
288
|
+
const lhsTime = Date.parse(lhs?.updatedAt || lhs?.updated_at || lhs?.createdAt || lhs?.created_at || 0) || 0;
|
|
289
|
+
const rhsTime = Date.parse(rhs?.updatedAt || rhs?.updated_at || rhs?.createdAt || rhs?.created_at || 0) || 0;
|
|
290
|
+
return rhsTime - lhsTime;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function firstArrayKey(value, keys) {
|
|
294
|
+
return keys.find((key) => Array.isArray(value?.[key])) || "";
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function hasCursor(params = {}) {
|
|
298
|
+
const cursor = params?.cursor ?? params?.nextCursor ?? params?.next_cursor;
|
|
299
|
+
return cursor != null && cursor !== "" && cursor !== false;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function hasExplicitProviderField(params = {}) {
|
|
303
|
+
return readModelProvider(params) !== CODEX_PROVIDER_ID
|
|
304
|
+
|| providerFieldHasValue(params)
|
|
305
|
+
|| providerFieldHasValue(params.collaborationMode?.settings)
|
|
306
|
+
|| providerFieldHasValue(params.collaboration_mode?.settings);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function providerFieldHasValue(value = {}) {
|
|
310
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return [
|
|
315
|
+
"modelProvider",
|
|
316
|
+
"model_provider",
|
|
317
|
+
"provider",
|
|
318
|
+
"runtimeProvider",
|
|
319
|
+
"runtime_provider",
|
|
320
|
+
"harness",
|
|
321
|
+
].some((key) => readString(value[key]));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function readThreadId(params = {}) {
|
|
325
|
+
return readString(params.threadId || params.thread_id || params.id);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function readThreadIdentifier(thread = {}) {
|
|
329
|
+
return readString(thread.id || thread.threadId || thread.thread_id);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function readString(value) {
|
|
333
|
+
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function createJsonRpcErrorResponse(requestId, error, defaultErrorCode) {
|
|
337
|
+
return JSON.stringify({
|
|
338
|
+
id: requestId,
|
|
339
|
+
error: {
|
|
340
|
+
code: -32000,
|
|
341
|
+
message: error?.userMessage || error?.message || "Runtime provider request failed.",
|
|
342
|
+
data: {
|
|
343
|
+
errorCode: error?.errorCode || defaultErrorCode,
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function safeParseJSON(rawMessage) {
|
|
350
|
+
try {
|
|
351
|
+
return JSON.parse(rawMessage);
|
|
352
|
+
} catch {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
module.exports = {
|
|
358
|
+
createRuntimeProviderRouter,
|
|
359
|
+
mergeModelListResult,
|
|
360
|
+
mergeThreadListResult,
|
|
361
|
+
providerForRequest,
|
|
362
|
+
registerThreadProjects,
|
|
363
|
+
stripRuntimeProviderFieldsForCodex,
|
|
364
|
+
threadsFromListResult,
|
|
365
|
+
};
|
|
@@ -1,39 +1,50 @@
|
|
|
1
1
|
-- FILE: codex-refresh.applescript
|
|
2
|
-
-- Purpose:
|
|
2
|
+
-- Purpose: Opens the target Codex deep link so the desktop window lands on the phone-driven thread.
|
|
3
3
|
-- Layer: UI automation helper
|
|
4
|
-
-- Args: bundle id, app path fallback, optional target deep link
|
|
4
|
+
-- Args: bundle id, app path fallback, optional target deep link, optional launch-if-closed flag ("1" default)
|
|
5
|
+
-- Note: content updates stream over desktop IPC live sync, so no settings-route
|
|
6
|
+
-- bounce/remount is needed anymore; that bounce caused visible page flipping.
|
|
5
7
|
|
|
6
8
|
on run argv
|
|
7
9
|
set bundleId to item 1 of argv
|
|
8
10
|
set appPath to item 2 of argv
|
|
9
11
|
set targetUrl to ""
|
|
10
|
-
set
|
|
12
|
+
set launchIfClosed to "1"
|
|
11
13
|
|
|
12
14
|
if (count of argv) is greater than or equal to 3 then
|
|
13
15
|
set targetUrl to item 3 of argv
|
|
14
16
|
end if
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
delay 0.12
|
|
21
|
-
|
|
22
|
-
my openCodexUrl(bundleId, appPath, bounceUrl)
|
|
23
|
-
delay 0.18
|
|
18
|
+
if (count of argv) is greater than or equal to 4 then
|
|
19
|
+
set launchIfClosed to item 4 of argv
|
|
20
|
+
end if
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
-- Navigation is a courtesy for an already-open Codex; cold-starting the app
|
|
23
|
+
-- just to show the phone-driven thread is disruptive and never required for
|
|
24
|
+
-- content sync (that streams over desktop IPC).
|
|
25
|
+
if launchIfClosed is "0" and not my isCodexRunning(bundleId) then
|
|
26
|
+
return
|
|
29
27
|
end if
|
|
30
28
|
|
|
29
|
+
my openCodexUrl(bundleId, appPath, targetUrl)
|
|
30
|
+
|
|
31
31
|
delay 0.18
|
|
32
32
|
try
|
|
33
33
|
tell application id bundleId to activate
|
|
34
34
|
end try
|
|
35
35
|
end run
|
|
36
36
|
|
|
37
|
+
on isCodexRunning(bundleId)
|
|
38
|
+
try
|
|
39
|
+
-- Avoid System Events here: LaunchAgents may not have automation permission.
|
|
40
|
+
set matches to do shell script "/usr/bin/lsappinfo find bundleid=" & quoted form of bundleId
|
|
41
|
+
return matches is not ""
|
|
42
|
+
on error
|
|
43
|
+
-- Unknown running state must fail closed for navigation-only callers.
|
|
44
|
+
return false
|
|
45
|
+
end try
|
|
46
|
+
end isCodexRunning
|
|
47
|
+
|
|
37
48
|
on openCodexUrl(bundleId, appPath, targetUrl)
|
|
38
49
|
try
|
|
39
50
|
if targetUrl is not "" then
|