@jskit-ai/assistant-core 0.2.7 → 0.2.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/examples/conversation/package.json +5 -5
- package/package.json +18 -7
- package/src/client/browser.js +21 -0
- package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
- package/src/client/conversation/AssistantComposerActions.vue +1 -1
- package/src/client/conversation/AssistantConversationElement.vue +14 -52
- package/src/client/conversation/AssistantGoalControl.vue +13 -10
- package/src/client/conversation/AssistantPromptInput.vue +6 -91
- package/src/client/conversation/AssistantTranscript.css +505 -0
- package/src/client/conversation/AssistantTranscript.vue +24 -1197
- package/src/client/conversation/attachmentFiles.js +158 -0
- package/src/client/conversation/attachmentQueueItems.js +121 -0
- package/src/client/conversation/attachmentUploadRows.js +265 -0
- package/src/client/conversation/conversationDisplay.js +54 -0
- package/src/client/conversation/messageDelivery.js +13 -9
- package/src/client/conversation/retainedConversation.js +14 -3
- package/src/client/conversation/submitText.js +13 -2
- package/src/client/conversation/transcriptEntries.js +225 -0
- package/src/client/conversation/useAssistantAttachments.js +86 -388
- package/src/client/conversation/useAssistantQuestions.js +16 -3
- package/src/client/conversation/useAssistantSuggestions.js +16 -2
- package/src/client/conversation/useScrollToBottom.js +17 -19
- package/src/client/conversation/useTextareaAutoGrow.js +115 -0
- package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
- package/src/client/lib/assistantApi.js +192 -161
- package/src/client/types.js +110 -0
- package/src/server/conversation/attachments.js +21 -6
- package/src/server/conversation/boundedToolLoop.js +1 -0
- package/src/server/conversation/claudeClient.js +71 -20
- package/src/server/conversation/claudeCommandRun.js +191 -0
- package/src/server/conversation/claudeHistory.js +7 -0
- package/src/server/conversation/claudeNativeTurn.js +708 -0
- package/src/server/conversation/claudeProcess.js +186 -112
- package/src/server/conversation/claudeProcessStart.js +214 -0
- package/src/server/conversation/claudePromptDelivery.js +183 -0
- package/src/server/conversation/claudeSessionCleanup.js +248 -0
- package/src/server/conversation/claudeTools.js +15 -3
- package/src/server/conversation/claudeTurn.js +212 -1388
- package/src/server/conversation/codexAccountReader.js +168 -0
- package/src/server/conversation/codexAuthPreflight.js +5 -4
- package/src/server/conversation/codexClient.js +18 -6
- package/src/server/conversation/codexConfiguration.js +8 -844
- package/src/server/conversation/codexConversationStops.js +377 -0
- package/src/server/conversation/codexConversationStore.js +119 -85
- package/src/server/conversation/codexConversationTurns.js +378 -0
- package/src/server/conversation/codexConversations.js +134 -866
- package/src/server/conversation/codexDetachedConversation.js +203 -0
- package/src/server/conversation/codexDetachedTurn.js +145 -104
- package/src/server/conversation/codexEvents.js +5 -200
- package/src/server/conversation/codexGoalCommands.js +14 -4
- package/src/server/conversation/codexHelperExecution.js +57 -57
- package/src/server/conversation/codexHelperThreadLedger.js +30 -288
- package/src/server/conversation/codexHelperThreadRecord.js +304 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
- package/src/server/conversation/codexHelperThreads.js +94 -93
- package/src/server/conversation/codexHistoryAdapter.js +13 -8
- package/src/server/conversation/codexIsolation.js +672 -0
- package/src/server/conversation/codexLiveItems.js +379 -0
- package/src/server/conversation/codexMessageCommands.js +68 -286
- package/src/server/conversation/codexMessageDelivery.js +46 -37
- package/src/server/conversation/codexModelCatalog.js +86 -7
- package/src/server/conversation/codexNotificationObserver.js +81 -88
- package/src/server/conversation/codexNotificationQueue.js +21 -10
- package/src/server/conversation/codexObservationControl.js +105 -89
- package/src/server/conversation/codexOutput.js +84 -419
- package/src/server/conversation/codexProcess.js +29 -1053
- package/src/server/conversation/codexProcessLeader.js +1 -0
- package/src/server/conversation/codexPromptDispatch.js +241 -0
- package/src/server/conversation/codexProvider.js +147 -1807
- package/src/server/conversation/codexProviderAuth.js +348 -0
- package/src/server/conversation/codexProviderInvalidation.js +371 -0
- package/src/server/conversation/codexProviderInventories.js +437 -0
- package/src/server/conversation/codexProviderOwner.js +149 -957
- package/src/server/conversation/codexProviderRelease.js +465 -0
- package/src/server/conversation/codexProviderSelection.js +26 -15
- package/src/server/conversation/codexProviderThreads.js +698 -0
- package/src/server/conversation/codexProviderTurns.js +453 -0
- package/src/server/conversation/codexReasoning.js +32 -23
- package/src/server/conversation/codexRenewalCommands.js +18 -6
- package/src/server/conversation/codexRenewalThreads.js +7 -22
- package/src/server/conversation/codexResultSettlement.js +70 -88
- package/src/server/conversation/codexRunJournal.js +91 -97
- package/src/server/conversation/codexRuntime.js +426 -0
- package/src/server/conversation/codexRuntimeInventory.js +113 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
- package/src/server/conversation/codexRuntimeStart.js +431 -0
- package/src/server/conversation/codexRuntimeStop.js +324 -0
- package/src/server/conversation/codexSessionCleanup.js +25 -10
- package/src/server/conversation/codexSessionReconciliation.js +21 -7
- package/src/server/conversation/codexThreadReadiness.js +49 -52
- package/src/server/conversation/codexThreadSnapshot.js +210 -0
- package/src/server/conversation/codexThreadStatus.js +48 -62
- package/src/server/conversation/codexTurn.js +171 -55
- package/src/server/conversation/codexTurnActivity.js +210 -0
- package/src/server/conversation/codexTurnControl.js +44 -48
- package/src/server/conversation/codexTurnEndings.js +610 -0
- package/src/server/conversation/codexTurnRecovery.js +97 -848
- package/src/server/conversation/codexTurnState.js +138 -111
- package/src/server/conversation/completedResponses.js +227 -0
- package/src/server/conversation/configuration.js +2 -2
- package/src/server/conversation/continuity.js +43 -6
- package/src/server/conversation/conversationDelivery.js +394 -0
- package/src/server/conversation/conversationEntries.js +365 -0
- package/src/server/conversation/conversationEvents.js +192 -0
- package/src/server/conversation/conversationGoals.js +187 -0
- package/src/server/conversation/conversationHandles.js +237 -0
- package/src/server/conversation/conversationReads.js +206 -0
- package/src/server/conversation/conversationRefusals.js +58 -0
- package/src/server/conversation/conversationReplacement.js +254 -0
- package/src/server/conversation/conversationSending.js +273 -0
- package/src/server/conversation/conversationStopping.js +121 -0
- package/src/server/conversation/conversationTurnDelivery.js +413 -0
- package/src/server/conversation/conversationTurns.js +511 -0
- package/src/server/conversation/deliveryTiming.js +25 -3
- package/src/server/conversation/fileStorage.js +11 -6
- package/src/server/conversation/hookBridge.js +14 -3
- package/src/server/conversation/index.js +1 -1
- package/src/server/conversation/localExecution.js +82 -625
- package/src/server/conversation/nativeCommandCallbacks.js +10 -0
- package/src/server/conversation/nativeConversationLifecycle.js +379 -0
- package/src/server/conversation/nativeHistoryExport.js +27 -18
- package/src/server/conversation/normalize.js +9 -0
- package/src/server/conversation/openCodeClient.js +295 -366
- package/src/server/conversation/openCodeNativeHistory.js +141 -0
- package/src/server/conversation/openCodeNativeSession.js +79 -0
- package/src/server/conversation/openCodeProcess.js +109 -53
- package/src/server/conversation/openCodeProcessOwner.js +565 -0
- package/src/server/conversation/openCodeRuntime.js +111 -1814
- package/src/server/conversation/openCodeSessionTurns.js +547 -0
- package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
- package/src/server/conversation/openCodeTools.js +28 -14
- package/src/server/conversation/openCodeTurn.js +80 -74
- package/src/server/conversation/openCodeTurnMonitor.js +388 -0
- package/src/server/conversation/processIdentity.js +582 -0
- package/src/server/conversation/provider.js +21 -6
- package/src/server/conversation/providers/api.js +268 -107
- package/src/server/conversation/providers/claude.js +29 -6
- package/src/server/conversation/providers/claudeDriver.js +325 -250
- package/src/server/conversation/providers/codex.js +69 -33
- package/src/server/conversation/providers/codexDriver.js +503 -1081
- package/src/server/conversation/providers/codexDriverConnection.js +331 -0
- package/src/server/conversation/providers/codexDriverTurns.js +464 -0
- package/src/server/conversation/providers/opencode.js +30 -4
- package/src/server/conversation/providers/opencodeDriver.js +321 -659
- package/src/server/conversation/providers/opencodeStandalone.js +475 -0
- package/src/server/conversation/providers/scoped.js +64 -19
- package/src/server/conversation/runtime.js +171 -2863
- package/src/server/conversation/storage.js +91 -50
- package/src/server/conversation/streams.js +15 -2
- package/src/server/conversation/structuredOutput.js +7 -11
- package/src/server/conversation/systemPrompt.js +13 -1
- package/src/server/conversation/tools.js +47 -15
- package/src/server/conversation/transcript.js +49 -20
- package/src/server/hosts/claudeTurn.js +2 -4
- package/src/server/hosts/codexConfiguration.js +6 -8
- package/src/server/hosts/codexEvents.js +4 -2
- package/src/server/hosts/codexProcess.js +1 -1
- package/src/server/hosts/codexProvider.js +11 -9
- package/src/server/hosts/codexTurn.js +3 -3
- package/src/server/hosts/openCodeClient.js +4 -2
- package/src/server/index.js +0 -2
- package/src/server/lib/aiConnectionClient.js +35 -25
- package/src/server/lib/ndjson.js +1 -0
- package/src/server/lib/providers/anthropicClient.js +15 -6
- package/src/server/lib/providers/common.js +10 -6
- package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
- package/src/server/lib/serviceToolCatalog.js +29 -283
- package/src/server/lib/serviceToolEntries.js +270 -0
- package/src/server/types.js +78 -0
- package/src/shared/assistantResource.js +0 -23
- package/src/shared/conversation/answerChoiceSugar.js +2 -14
- package/src/shared/conversation/messageInputSugar.js +18 -0
- package/src/shared/conversation/numberedQuestionSugar.js +6 -16
- package/src/shared/conversation/pagination.js +1 -1
- package/src/shared/conversation/questions.js +1 -0
- package/src/shared/conversation/streaming.js +1 -0
- package/src/shared/index.js +0 -1
- package/src/testing/conversationStorageContract.js +20 -0
- package/test/aiConnectionClient.test.js +42 -1
- package/test/apiConversationDriver.test.js +217 -38
- package/test/assistantBoundedToolLoop.test.js +1 -1
- package/test/claudeConversationSession.test.js +15 -11
- package/test/claudeInstructionResume.test.js +1 -1
- package/test/claudeProcess.test.js +9 -9
- package/test/claudeTurn.test.js +21 -8
- package/test/codexAccountChange.test.js +5 -3
- package/test/codexConversationSession.test.js +22 -21
- package/test/codexEvents.test.js +3 -1
- package/test/codexHelperDeletion.test.js +3 -7
- package/test/codexIsolation.test.js +6 -6
- package/test/codexModelCatalog.test.js +6 -7
- package/test/codexProcess.test.js +3 -3
- package/test/codexThreadSelection.test.js +4 -1
- package/test/codexTurn.test.js +29 -20
- package/test/conversationApiFacilities.test.js +11 -9
- package/test/conversationAttachments.test.js +3 -3
- package/test/conversationConfiguration.test.js +4 -4
- package/test/conversationNativeStorage.test.js +11 -10
- package/test/conversationReplacement.test.js +13 -13
- package/test/conversationRuntime.test.js +9 -5
- package/test/conversationSession.test.js +78 -22
- package/test/conversationTools.test.js +47 -26
- package/test/fileConversationStorage.test.js +10 -3
- package/test/fixtures/codexHelperThreadLedger.js +3 -3
- package/test/frameworkActions.test.js +33 -18
- package/test/localConversationExecution.test.js +4 -4
- package/test/localExecution.test.js +1 -1
- package/test/openCodeConnectionChange.test.js +3 -2
- package/test/openCodeRuntime.test.js +8 -7
- package/test/openCodeTools.test.js +1 -1
- package/test/opencodeConversationSession.test.js +4 -3
- package/test/serviceToolCatalog.test.js +3 -8
- package/src/server/lib/assistantCompletion.js +0 -190
- package/src/server/lib/assistantToolLoop.js +0 -259
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { open } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
|
|
5
|
+
import { normalizeNullableText } from "./normalize.js";
|
|
6
|
+
|
|
7
|
+
const CODEX_APP_SERVER_SELECTED_AUTH_MAX_BYTES = 1024 * 1024;
|
|
8
|
+
const CODEX_APP_SERVER_CHATGPT_ACCESS_TOKEN_MAX_LENGTH = 256 * 1024;
|
|
9
|
+
export const CODEX_APP_SERVER_ACCOUNT_ID_MAX_LENGTH = 512;
|
|
10
|
+
const CODEX_APP_SERVER_PLAN_TYPE_MAX_LENGTH = 128;
|
|
11
|
+
const CODEX_APP_SERVER_API_KEY_MAX_LENGTH = 16 * 1024;
|
|
12
|
+
|
|
13
|
+
/** Native account records and secret-change detection. Hosts select the credential home. */
|
|
14
|
+
export function createCodexAccountReader({
|
|
15
|
+
errorPrefix = "",
|
|
16
|
+
identityNamespace = "codex-account-v1",
|
|
17
|
+
secretNamespace = "codex-auth-secret-v1"
|
|
18
|
+
} = {}) {
|
|
19
|
+
function codexAppServerHelperAuthError(code = "", message = "") {
|
|
20
|
+
const error = new Error(
|
|
21
|
+
normalizeNullableText(message) || "Codex helper authentication is unavailable. Reconnect Codex and retry."
|
|
22
|
+
);
|
|
23
|
+
error.code = normalizeNullableText(code) || `${errorPrefix}codex_helper_auth_unavailable`;
|
|
24
|
+
return error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function boundedCodexAuthText(value, {
|
|
28
|
+
label = "Codex authentication value",
|
|
29
|
+
maxLength = 0
|
|
30
|
+
} = {}) {
|
|
31
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
|
|
32
|
+
throw codexAppServerHelperAuthError(
|
|
33
|
+
`${errorPrefix}codex_helper_auth_invalid`,
|
|
34
|
+
`${label} is missing or invalid. Reconnect Codex and retry.`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function codexAccountIdentitySignature(authMode = "", identity = "") {
|
|
41
|
+
return `sha256:${createHash("sha256")
|
|
42
|
+
.update(`${identityNamespace}\0`, "utf8")
|
|
43
|
+
.update(normalizeNullableText(authMode), "utf8")
|
|
44
|
+
.update("\0", "utf8")
|
|
45
|
+
.update(String(identity || ""), "utf8")
|
|
46
|
+
.digest("hex")}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function codexAuthSecretSignature(secret = "") {
|
|
50
|
+
return createHash("sha256")
|
|
51
|
+
.update(`${secretNamespace}\0`, "utf8")
|
|
52
|
+
.update(String(secret || ""), "utf8")
|
|
53
|
+
.digest("hex");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function readBoundedCodexAuthJson(filePath = "") {
|
|
57
|
+
let handle = null;
|
|
58
|
+
try {
|
|
59
|
+
handle = await open(filePath, "r");
|
|
60
|
+
const fileStat = await handle.stat();
|
|
61
|
+
if (!fileStat.isFile() || fileStat.size > CODEX_APP_SERVER_SELECTED_AUTH_MAX_BYTES) {
|
|
62
|
+
throw codexAppServerHelperAuthError(
|
|
63
|
+
`${errorPrefix}codex_helper_auth_invalid`,
|
|
64
|
+
"The selected Codex authentication record is invalid. Reconnect Codex and retry."
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const buffer = Buffer.alloc(CODEX_APP_SERVER_SELECTED_AUTH_MAX_BYTES + 1);
|
|
68
|
+
let offset = 0;
|
|
69
|
+
while (offset < buffer.length) {
|
|
70
|
+
const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, null);
|
|
71
|
+
if (bytesRead === 0) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
offset += bytesRead;
|
|
75
|
+
}
|
|
76
|
+
if (offset > CODEX_APP_SERVER_SELECTED_AUTH_MAX_BYTES) {
|
|
77
|
+
throw codexAppServerHelperAuthError(
|
|
78
|
+
`${errorPrefix}codex_helper_auth_invalid`,
|
|
79
|
+
"The selected Codex authentication record is too large. Reconnect Codex and retry."
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const parsed = JSON.parse(buffer.subarray(0, offset).toString("utf8"));
|
|
83
|
+
if (!isRecord(parsed)) {
|
|
84
|
+
throw new Error("invalid authentication record");
|
|
85
|
+
}
|
|
86
|
+
return parsed;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// readable-check: allow optional-call - data: a caught value whose code may be absent or not a string
|
|
89
|
+
if (error?.code?.startsWith?.(`${errorPrefix}codex_helper_auth_`)) {
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
throw codexAppServerHelperAuthError(
|
|
93
|
+
`${errorPrefix}codex_helper_auth_unavailable`,
|
|
94
|
+
"The selected Codex authentication record could not be read. Reconnect Codex and retry."
|
|
95
|
+
);
|
|
96
|
+
} finally {
|
|
97
|
+
await handle?.close().catch(() => null);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function readCodexSelectedAccountAuth(options = {}) {
|
|
102
|
+
const toolHomeSource = normalizeNullableText(options.toolHomeSource);
|
|
103
|
+
if (!toolHomeSource || !path.isAbsolute(toolHomeSource)) {
|
|
104
|
+
throw codexAppServerHelperAuthError(
|
|
105
|
+
`${errorPrefix}codex_helper_auth_unavailable`,
|
|
106
|
+
"The selected Codex account is unavailable. Reconnect Codex and retry."
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const auth = await readBoundedCodexAuthJson(path.join(toolHomeSource, ".codex", "auth.json"));
|
|
110
|
+
const storedMode = normalizeNullableText(auth.auth_mode).toLowerCase();
|
|
111
|
+
if (storedMode === "chatgpt") {
|
|
112
|
+
const tokens = isRecord(auth.tokens) ? auth.tokens : {};
|
|
113
|
+
const accountId = boundedCodexAuthText(tokens.account_id, {
|
|
114
|
+
label: "Codex account identity",
|
|
115
|
+
maxLength: CODEX_APP_SERVER_ACCOUNT_ID_MAX_LENGTH
|
|
116
|
+
});
|
|
117
|
+
const accessToken = boundedCodexAuthText(tokens.access_token, {
|
|
118
|
+
label: "Codex access token",
|
|
119
|
+
maxLength: CODEX_APP_SERVER_CHATGPT_ACCESS_TOKEN_MAX_LENGTH
|
|
120
|
+
});
|
|
121
|
+
const planTypeValue = tokens.chatgpt_plan_type ?? tokens.plan_type;
|
|
122
|
+
const planType = planTypeValue === undefined || planTypeValue === null || planTypeValue === ""
|
|
123
|
+
? ""
|
|
124
|
+
: boundedCodexAuthText(planTypeValue, {
|
|
125
|
+
label: "Codex plan type",
|
|
126
|
+
maxLength: CODEX_APP_SERVER_PLAN_TYPE_MAX_LENGTH
|
|
127
|
+
});
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
accessToken,
|
|
130
|
+
accountId,
|
|
131
|
+
authMode: "chatgpt",
|
|
132
|
+
identitySignature: codexAccountIdentitySignature("chatgpt", accountId),
|
|
133
|
+
planType,
|
|
134
|
+
secretSignature: codexAuthSecretSignature(accessToken)
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (["apikey", "api_key"].includes(storedMode)) {
|
|
138
|
+
const apiKey = boundedCodexAuthText(auth.OPENAI_API_KEY, {
|
|
139
|
+
label: "Codex API key",
|
|
140
|
+
maxLength: CODEX_APP_SERVER_API_KEY_MAX_LENGTH
|
|
141
|
+
});
|
|
142
|
+
return Object.freeze({
|
|
143
|
+
apiKey,
|
|
144
|
+
authMode: "apiKey",
|
|
145
|
+
identitySignature: codexAccountIdentitySignature("apiKey", apiKey),
|
|
146
|
+
secretSignature: codexAuthSecretSignature(apiKey)
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
throw codexAppServerHelperAuthError(
|
|
150
|
+
`${errorPrefix}codex_helper_auth_invalid`,
|
|
151
|
+
"The selected Codex authentication mode is unsupported. Reconnect Codex and retry."
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return Object.freeze({
|
|
156
|
+
error: codexAppServerHelperAuthError,
|
|
157
|
+
identitySignature: codexAccountIdentitySignature,
|
|
158
|
+
readRecord: readBoundedCodexAuthJson,
|
|
159
|
+
readSelected: readCodexSelectedAccountAuth
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const CODEX_AUTH_INVALIDATED_PATTERN =
|
|
164
|
+
/\b(?:token_invalidated|refresh_token_invalidated)\b|authentication token has been invalidated|HTTP error:\s*401 Unauthorized|401 Unauthorized/iu;
|
|
165
|
+
|
|
166
|
+
export function codexAuthOutputRequiresReconnect(output = "") {
|
|
167
|
+
return CODEX_AUTH_INVALIDATED_PATTERN.test(String(output || ""));
|
|
168
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { codexAuthOutputRequiresReconnect } from "./
|
|
2
|
+
import { codexAuthOutputRequiresReconnect } from "./codexAccountReader.js";
|
|
3
3
|
import {
|
|
4
4
|
assertExistingDirectory, codexAppServerCommandBaseEnv, codexAppServerCredentialHome,
|
|
5
5
|
codexAppServerProcessCwd, ensureWritablePrivateDirectory
|
|
@@ -30,7 +30,7 @@ function codexAuthPreflightArgs() {
|
|
|
30
30
|
|
|
31
31
|
async function runCodexAuthPreflight({
|
|
32
32
|
codexCommand = "codex",
|
|
33
|
-
env
|
|
33
|
+
env,
|
|
34
34
|
executionRoot = "",
|
|
35
35
|
runtimeDir = "",
|
|
36
36
|
terminalEnv = {},
|
|
@@ -86,9 +86,10 @@ async function runCodexAuthPreflight({
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export async function assertCodexAuthPreflightReady(options
|
|
89
|
+
export async function assertCodexAuthPreflightReady(options, {
|
|
90
90
|
reason = "codex-auth-preflight", execution, credentials
|
|
91
|
-
}
|
|
91
|
+
}) {
|
|
92
|
+
if (!options?.env) throw new TypeError("assertCodexAuthPreflightReady requires the application's environment, such as process.env.");
|
|
92
93
|
const result = await runCodexAuthPreflight(options, execution);
|
|
93
94
|
if (result.ok && !codexAuthOutputRequiresReconnect(result.output)) {
|
|
94
95
|
return result;
|
|
@@ -27,18 +27,23 @@ function codexAppServerRequestAbortedError(method = "") {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function addSocketListener(socket, eventName, handler) {
|
|
30
|
+
// readable-check: allow method-probe - platform object: a WebSocket is an EventTarget in some implementations and an EventEmitter in others
|
|
30
31
|
if (typeof socket.addEventListener === "function") {
|
|
31
32
|
socket.addEventListener(eventName, handler);
|
|
33
|
+
// readable-check: allow optional-call - platform object: a WebSocket is an EventTarget in some implementations and an EventEmitter in others
|
|
32
34
|
return () => socket.removeEventListener?.(eventName, handler);
|
|
33
35
|
}
|
|
36
|
+
// readable-check: allow method-probe - platform object: a WebSocket is an EventTarget in some implementations and an EventEmitter in others
|
|
34
37
|
if (typeof socket.on === "function") {
|
|
35
38
|
socket.on(eventName, handler);
|
|
39
|
+
// readable-check: allow optional-call - platform object: a WebSocket is an EventTarget in some implementations and an EventEmitter in others
|
|
36
40
|
return () => socket.off?.(eventName, handler) || socket.removeListener?.(eventName, handler);
|
|
37
41
|
}
|
|
38
42
|
throw new Error("Unsupported WebSocket implementation.");
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
function socketMessageText(event, maxBytes = Number.POSITIVE_INFINITY) {
|
|
46
|
+
// readable-check: allow no-dual-shape - platform object: an EventTarget socket delivers a message event, an EventEmitter socket the data itself
|
|
42
47
|
const data = event?.data ?? event;
|
|
43
48
|
if (typeof data === "string") {
|
|
44
49
|
return Buffer.byteLength(data, "utf8") <= maxBytes ? data : null;
|
|
@@ -50,11 +55,14 @@ function socketMessageText(event, maxBytes = Number.POSITIVE_INFINITY) {
|
|
|
50
55
|
return Buffer.byteLength(text, "utf8") <= maxBytes ? text : null;
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
/** A client without a disconnect listener reports nothing. */
|
|
59
|
+
function ignoreDisconnect() {}
|
|
60
|
+
|
|
53
61
|
class CodexAppServerJsonRpcClient {
|
|
54
62
|
constructor({
|
|
55
63
|
endpoint = "",
|
|
56
64
|
maxMessageBytes = Number.POSITIVE_INFINITY,
|
|
57
|
-
onDisconnect =
|
|
65
|
+
onDisconnect = ignoreDisconnect,
|
|
58
66
|
requestTimeoutMs = CODEX_APP_SERVER_REQUEST_TIMEOUT_MS,
|
|
59
67
|
WebSocketImpl = WebSocket
|
|
60
68
|
} = {}) {
|
|
@@ -79,6 +87,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
79
87
|
if (!this.endpoint) {
|
|
80
88
|
throw new Error("Codex app-server endpoint is required.");
|
|
81
89
|
}
|
|
90
|
+
// readable-check: allow method-probe - input validation: an application may supply its own WebSocket implementation
|
|
82
91
|
if (typeof this.WebSocketImpl !== "function") {
|
|
83
92
|
throw new Error("A WebSocket implementation is required for Codex app-server.");
|
|
84
93
|
}
|
|
@@ -123,6 +132,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
123
132
|
this.connected = false;
|
|
124
133
|
this.socket = null;
|
|
125
134
|
}
|
|
135
|
+
// readable-check: allow no-dual-shape - platform object: an EventTarget socket reports an ErrorEvent wrapping the error, an EventEmitter socket the error itself
|
|
126
136
|
settle(reject, error?.error || error);
|
|
127
137
|
}));
|
|
128
138
|
});
|
|
@@ -189,7 +199,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
189
199
|
return;
|
|
190
200
|
}
|
|
191
201
|
const removeAbortListener = () => {
|
|
192
|
-
signal?.removeEventListener
|
|
202
|
+
signal?.removeEventListener("abort", abort);
|
|
193
203
|
};
|
|
194
204
|
const abort = () => {
|
|
195
205
|
const pending = this.pendingRequests.get(id);
|
|
@@ -213,7 +223,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
213
223
|
resolve,
|
|
214
224
|
timeout
|
|
215
225
|
});
|
|
216
|
-
signal?.addEventListener
|
|
226
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
217
227
|
try {
|
|
218
228
|
this.send({
|
|
219
229
|
id,
|
|
@@ -230,6 +240,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
230
240
|
}
|
|
231
241
|
|
|
232
242
|
send(payload) {
|
|
243
|
+
// readable-check: allow method-probe - platform object: a WebSocket implementation's socket
|
|
233
244
|
if (!this.isOpen() || typeof this.socket.send !== "function") {
|
|
234
245
|
throw new Error("Codex app-server connection is not open.");
|
|
235
246
|
}
|
|
@@ -264,7 +275,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
264
275
|
return;
|
|
265
276
|
}
|
|
266
277
|
clearTimeout(pending.timeout);
|
|
267
|
-
pending.removeAbortListener
|
|
278
|
+
pending.removeAbortListener();
|
|
268
279
|
this.pendingRequests.delete(message.id);
|
|
269
280
|
if (message.error) {
|
|
270
281
|
const error = new Error(message.error.message || `Codex app-server request failed: ${pending.method}`);
|
|
@@ -316,7 +327,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
316
327
|
rejectPendingRequests(error) {
|
|
317
328
|
for (const pending of this.pendingRequests.values()) {
|
|
318
329
|
clearTimeout(pending.timeout);
|
|
319
|
-
pending.removeAbortListener
|
|
330
|
+
pending.removeAbortListener();
|
|
320
331
|
pending.reject(error);
|
|
321
332
|
}
|
|
322
333
|
this.pendingRequests.clear();
|
|
@@ -325,7 +336,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
325
336
|
disconnect(error) {
|
|
326
337
|
this.rejectPendingRequests(error);
|
|
327
338
|
this.close();
|
|
328
|
-
this.onDisconnect
|
|
339
|
+
this.onDisconnect(error);
|
|
329
340
|
}
|
|
330
341
|
|
|
331
342
|
close() {
|
|
@@ -333,6 +344,7 @@ class CodexAppServerJsonRpcClient {
|
|
|
333
344
|
const socket = this.socket;
|
|
334
345
|
this.connected = false;
|
|
335
346
|
this.socket = null;
|
|
347
|
+
// readable-check: allow optional-call - platform object: a WebSocket implementation's socket
|
|
336
348
|
socket?.close?.();
|
|
337
349
|
}
|
|
338
350
|
}
|