@bitkyc08/opencodex 2.7.41 → 2.7.42
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/README.md +4 -0
- package/gui/dist/assets/index-Bl_VBGoI.js +65 -0
- package/gui/dist/assets/index-DfVGuN88.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/base.ts +6 -0
- package/src/adapters/kiro-constants.ts +6 -2
- package/src/adapters/kiro-retry.ts +175 -10
- package/src/adapters/kiro.ts +172 -85
- package/src/adapters/mimo-free.ts +1 -0
- package/src/adapters/openai-chat.ts +30 -4
- package/src/adapters/openai-responses.ts +90 -12
- package/src/bridge.ts +91 -43
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +29 -2
- package/src/cli/access.ts +108 -0
- package/src/cli/account-auth.ts +223 -0
- package/src/cli/account.ts +9 -1
- package/src/cli/agent.ts +184 -0
- package/src/cli/combo.ts +119 -0
- package/src/cli/config-command.ts +145 -0
- package/src/cli/debug.ts +20 -8
- package/src/cli/doctor.ts +45 -8
- package/src/cli/help.ts +65 -13
- package/src/cli/index.ts +108 -7
- package/src/cli/integrations.ts +142 -0
- package/src/cli/models-runtime.ts +212 -0
- package/src/cli/models.ts +9 -10
- package/src/cli/observe.ts +117 -0
- package/src/cli/provider-runtime.ts +152 -0
- package/src/cli/provider.ts +23 -1
- package/src/cli/runtime-api.ts +325 -0
- package/src/cli/star-prompt.ts +3 -3
- package/src/cli/status.ts +17 -0
- package/src/cli/system-command.ts +112 -0
- package/src/codex/auth-api.ts +3 -2
- package/src/codex/catalog/aggregation.ts +113 -18
- package/src/codex/catalog/provider-fetch.ts +24 -13
- package/src/codex/catalog/sync.ts +20 -8
- package/src/codex/catalog.ts +2 -1
- package/src/codex/refresh.ts +10 -3
- package/src/codex/routing.ts +21 -32
- package/src/codex/sync.ts +17 -0
- package/src/config.ts +48 -0
- package/src/generated/jawcode-model-metadata.ts +2 -1
- package/src/grok/inject.ts +184 -4
- package/src/grok/status.ts +33 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/windows-elevation.ts +627 -0
- package/src/providers/openai-sidecar.ts +46 -2
- package/src/providers/registry.ts +52 -0
- package/src/server/auth-cors.ts +6 -0
- package/src/server/chat-completions.ts +6 -1
- package/src/server/claude-messages.ts +20 -1
- package/src/server/images.ts +14 -7
- package/src/server/management/agent-settings-routes.ts +10 -4
- package/src/server/management/combo-routes.ts +0 -1
- package/src/server/management/config-routes.ts +0 -1
- package/src/server/management/logs-usage-routes.ts +94 -0
- package/src/server/management/model-routes.ts +0 -1
- package/src/server/management/oauth-account-routes.ts +0 -1
- package/src/server/management/provider-routes.ts +0 -1
- package/src/server/management/shared.ts +0 -1
- package/src/server/management/system-routes.ts +27 -15
- package/src/server/management-api.ts +0 -1
- package/src/server/memory-watchdog.ts +54 -10
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +122 -2
- package/src/server/responses/core.ts +76 -13
- package/src/server/responses/passthrough-error.ts +38 -13
- package/src/server/startup-action-control.ts +266 -15
- package/src/service.ts +512 -3
- package/src/storage/cleanup.ts +1538 -0
- package/src/storage/scanner.ts +4 -1
- package/src/types.ts +16 -0
- package/src/update/job.ts +229 -25
- package/src/usage/log.ts +39 -0
- package/src/web-search/loop.ts +8 -1
- package/gui/dist/assets/index-B2J4t3te.css +0 -1
- package/gui/dist/assets/index-BmvM6wRb.js +0 -65
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { formatErrorResponse } from "../../bridge";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (!trimmed || trimmed.length > 128) return undefined;
|
|
7
|
-
return parseRetryAfterMs(trimmed, now) !== undefined ? trimmed : undefined;
|
|
8
|
-
}
|
|
2
|
+
import {
|
|
3
|
+
resolveClientRetryAfter,
|
|
4
|
+
validateClientRetryAfterHeader,
|
|
5
|
+
} from "../../lib/retry-after";
|
|
9
6
|
|
|
10
7
|
/**
|
|
11
8
|
* Passthrough adapters historically relayed upstream non-2xx bodies verbatim.
|
|
@@ -16,9 +13,11 @@ function sanitizedRetryAfter(value: string | null | undefined, now: number): str
|
|
|
16
13
|
* HTML/text errors) must keep their original bytes and headers so pool-retry
|
|
17
14
|
* activation and client diagnostics stay honest.
|
|
18
15
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* /
|
|
16
|
+
* Retry-After is validated independently of the body path:
|
|
17
|
+
* - valid upstream values are preserved
|
|
18
|
+
* - missing/malformed values are replaced when resolveClientRetryAfter yields a value
|
|
19
|
+
* - malformed/expired values are removed when the resolver returns undefined
|
|
20
|
+
* (e.g. quota-exhausted 429s must not keep junk headers or get the synthetic "2")
|
|
22
21
|
*/
|
|
23
22
|
export function formatPassthroughUpstreamError(
|
|
24
23
|
status: number,
|
|
@@ -31,13 +30,38 @@ export function formatPassthroughUpstreamError(
|
|
|
31
30
|
): Response {
|
|
32
31
|
const trimmed = bodyText.trim();
|
|
33
32
|
const now = options?.now ?? Date.now();
|
|
34
|
-
const
|
|
33
|
+
const upstreamRetryAfter = options?.headers?.get("retry-after")?.trim() || undefined;
|
|
34
|
+
const originalValid = validateClientRetryAfterHeader(upstreamRetryAfter, now);
|
|
35
|
+
const resolved = resolveClientRetryAfter({
|
|
36
|
+
status,
|
|
37
|
+
message: trimmed || `Provider error ${status}: (empty body)`,
|
|
38
|
+
upstreamRetryAfter,
|
|
39
|
+
now,
|
|
40
|
+
});
|
|
35
41
|
|
|
36
42
|
if (trimmed) {
|
|
43
|
+
const needsSet = resolved !== undefined && upstreamRetryAfter !== resolved;
|
|
44
|
+
const needsDelete = resolved === undefined
|
|
45
|
+
&& upstreamRetryAfter !== undefined
|
|
46
|
+
&& originalValid === undefined;
|
|
47
|
+
|
|
48
|
+
if (!needsSet && !needsDelete) {
|
|
49
|
+
return new Response(bodyText, {
|
|
50
|
+
status,
|
|
51
|
+
...(options?.statusText ? { statusText: options.statusText } : {}),
|
|
52
|
+
...(options?.headers ? { headers: options.headers } : { headers: { "Content-Type": "application/json" } }),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const headers = options?.headers
|
|
57
|
+
? new Headers(options.headers)
|
|
58
|
+
: new Headers({ "Content-Type": "application/json" });
|
|
59
|
+
if (needsSet) headers.set("Retry-After", resolved!);
|
|
60
|
+
else headers.delete("Retry-After");
|
|
37
61
|
return new Response(bodyText, {
|
|
38
62
|
status,
|
|
39
63
|
...(options?.statusText ? { statusText: options.statusText } : {}),
|
|
40
|
-
|
|
64
|
+
headers,
|
|
41
65
|
});
|
|
42
66
|
}
|
|
43
67
|
|
|
@@ -45,9 +69,10 @@ export function formatPassthroughUpstreamError(
|
|
|
45
69
|
status,
|
|
46
70
|
"upstream_error",
|
|
47
71
|
`Provider error ${status}: (empty body)`,
|
|
72
|
+
resolved !== undefined ? { retryAfter: resolved } : undefined,
|
|
48
73
|
);
|
|
49
74
|
const headers = new Headers(response.headers);
|
|
50
75
|
headers.set("Content-Type", "application/json");
|
|
51
|
-
if (
|
|
76
|
+
if (resolved !== undefined) headers.set("Retry-After", resolved);
|
|
52
77
|
return new Response(response.body, { status: response.status, headers });
|
|
53
78
|
}
|
|
@@ -1,9 +1,110 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { execFile } from "node:child_process";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { durableBunPath } from "../lib/bun-runtime";
|
|
5
|
+
import {
|
|
6
|
+
WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED_MARKER,
|
|
7
|
+
isWindowsSchtasksCreateAccessDenied,
|
|
8
|
+
} from "../lib/windows-elevation";
|
|
9
|
+
import {
|
|
10
|
+
finalizeWindowsSchedulerServiceRegistration,
|
|
11
|
+
type ElevatedReconciliationOutcome,
|
|
12
|
+
type FinalizeWindowsSchedulerOptions,
|
|
13
|
+
type FinalizeWindowsSchedulerResult,
|
|
14
|
+
type WindowsSchedulerTaskProbe,
|
|
15
|
+
} from "../service";
|
|
4
16
|
|
|
5
17
|
export type StartupInstallAction = "install-service" | "install-shim";
|
|
6
|
-
|
|
18
|
+
|
|
19
|
+
export type StartupInstallState =
|
|
20
|
+
| { status: "idle" }
|
|
21
|
+
| {
|
|
22
|
+
status: "running";
|
|
23
|
+
action: StartupInstallAction;
|
|
24
|
+
attemptId: string;
|
|
25
|
+
startedAt: number;
|
|
26
|
+
}
|
|
27
|
+
| {
|
|
28
|
+
status: "indeterminate";
|
|
29
|
+
action: "install-service";
|
|
30
|
+
attemptId: string;
|
|
31
|
+
startedAt: number;
|
|
32
|
+
timedOutAt: number;
|
|
33
|
+
reconciliation: Promise<ElevatedReconciliationOutcome>;
|
|
34
|
+
}
|
|
35
|
+
| {
|
|
36
|
+
status: "blocked";
|
|
37
|
+
reason: "partial-elevated-install";
|
|
38
|
+
action: "install-service";
|
|
39
|
+
attemptId: string;
|
|
40
|
+
detail: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const INSTALL_DETAIL_LIMIT = 2_000;
|
|
44
|
+
const INDETERMINATE_MESSAGE =
|
|
45
|
+
"Windows elevation timed out, but the elevated Task Scheduler transaction may still be running. New service installation attempts are temporarily blocked while OpenCodex reconciles its final state.";
|
|
46
|
+
const RECONCILING_MESSAGE =
|
|
47
|
+
"A previous elevated Windows service installation is still being reconciled. Wait for it to finish or inspect the Task Scheduler state before retrying.";
|
|
48
|
+
const BLOCKED_PARTIAL_MESSAGE =
|
|
49
|
+
"A previous elevated Windows service installation left a partial Task Scheduler state. Remove the OpenCodex scheduler task (or confirm it is absent), then clear the install block before retrying.";
|
|
50
|
+
|
|
51
|
+
let installState: StartupInstallState = { status: "idle" };
|
|
52
|
+
|
|
53
|
+
type FinalizeFn = (
|
|
54
|
+
script?: string,
|
|
55
|
+
options?: FinalizeWindowsSchedulerOptions,
|
|
56
|
+
) => Promise<FinalizeWindowsSchedulerResult>;
|
|
57
|
+
let finalizeImpl: FinalizeFn = finalizeWindowsSchedulerServiceRegistration;
|
|
58
|
+
|
|
59
|
+
/** Test-only seam so elevation retry tests do not mock.module the service package. */
|
|
60
|
+
export function setStartupInstallFinalizeForTests(next: FinalizeFn | null): void {
|
|
61
|
+
finalizeImpl = next ?? finalizeWindowsSchedulerServiceRegistration;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Snapshot of the install lock for diagnostics and tests. */
|
|
65
|
+
export function getStartupInstallState(): StartupInstallState {
|
|
66
|
+
return installState;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** True when this attempt still owns the active/indeterminate/blocked lock. */
|
|
70
|
+
export function ownsStartupInstallAttempt(attemptId: string): boolean {
|
|
71
|
+
return (installState.status === "running"
|
|
72
|
+
|| installState.status === "indeterminate"
|
|
73
|
+
|| installState.status === "blocked")
|
|
74
|
+
&& installState.attemptId === attemptId;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Clear a partial-install block after the operator has cleaned up Task Scheduler.
|
|
79
|
+
* Requires an observed probe and clears only when the task is proven absent.
|
|
80
|
+
* Does not itself delete the task.
|
|
81
|
+
*/
|
|
82
|
+
export function clearStartupInstallPartialBlock(options: {
|
|
83
|
+
probe: WindowsSchedulerTaskProbe;
|
|
84
|
+
}): { cleared: boolean; detail: string } {
|
|
85
|
+
if (installState.status !== "blocked") {
|
|
86
|
+
return { cleared: false, detail: `Install lock is ${installState.status}, not blocked.` };
|
|
87
|
+
}
|
|
88
|
+
if (options.probe.status === "present") {
|
|
89
|
+
return {
|
|
90
|
+
cleared: false,
|
|
91
|
+
detail: "OpenCodex Task Scheduler task is still present; remove it before clearing the block.",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (options.probe.status === "unknown") {
|
|
95
|
+
return {
|
|
96
|
+
cleared: false,
|
|
97
|
+
detail: `Could not verify Task Scheduler absence (${options.probe.detail}); not clearing the block.`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
installState = { status: "idle" };
|
|
101
|
+
return { cleared: true, detail: "Partial-install block cleared." };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Test-only reset of the install lock. */
|
|
105
|
+
export function resetStartupInstallStateForTests(): void {
|
|
106
|
+
installState = { status: "idle" };
|
|
107
|
+
}
|
|
7
108
|
|
|
8
109
|
export function startupInstallArgv(action: StartupInstallAction): string[] {
|
|
9
110
|
return action === "install-service"
|
|
@@ -11,14 +112,52 @@ export function startupInstallArgv(action: StartupInstallAction): string[] {
|
|
|
11
112
|
: ["codex-shim", "install"];
|
|
12
113
|
}
|
|
13
114
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
115
|
+
export interface CliInstallFailure {
|
|
116
|
+
/** Machine marker such as OCX_ERROR_CODE=..., when present in any stream. */
|
|
117
|
+
code: string | null;
|
|
118
|
+
stdout: string;
|
|
119
|
+
stderr: string;
|
|
120
|
+
message: string;
|
|
121
|
+
/** Bounded user-facing diagnostic (may append the marker if truncated away). */
|
|
122
|
+
detail: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function extractOcxErrorCode(text: string): string | null {
|
|
126
|
+
const match = text.match(/OCX_ERROR_CODE=[A-Z0-9_]+/);
|
|
127
|
+
return match ? match[0] : null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Classify a CLI install failure, keeping the machine marker separate from the
|
|
132
|
+
* truncated user-facing diagnostic.
|
|
133
|
+
*/
|
|
134
|
+
export function classifyCliInstallFailure(stdout: string, stderr: string, error: Error): CliInstallFailure {
|
|
135
|
+
const stdoutText = stdout.trim();
|
|
136
|
+
const stderrText = stderr.trim();
|
|
137
|
+
const message = error.message.trim();
|
|
138
|
+
const combined = [stderrText, stdoutText, message].filter(Boolean).join("\n");
|
|
139
|
+
const code = extractOcxErrorCode(combined);
|
|
140
|
+
let detail = combined || message;
|
|
141
|
+
if (detail.length > INSTALL_DETAIL_LIMIT) {
|
|
142
|
+
const truncated = detail.slice(0, INSTALL_DETAIL_LIMIT);
|
|
143
|
+
detail = code && !truncated.includes(code)
|
|
144
|
+
? `${truncated}\n${code}… (truncated)`
|
|
145
|
+
: `${truncated}… (truncated)`;
|
|
146
|
+
}
|
|
147
|
+
return { code, stdout: stdoutText, stderr: stderrText, message, detail };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @deprecated Prefer classifyCliInstallFailure; kept for existing tests. */
|
|
151
|
+
export function installFailureDetail(stdout: string, stderr: string, error: Error): string {
|
|
152
|
+
return classifyCliInstallFailure(stdout, stderr, error).detail;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function runCliInstall(action: StartupInstallAction): Promise<{ stdout: string; stderr: string }> {
|
|
18
156
|
const bun = durableBunPath();
|
|
19
157
|
const cli = join(import.meta.dir, "..", "cli", "index.ts");
|
|
20
|
-
const
|
|
21
|
-
|
|
158
|
+
const argv = [cli, ...startupInstallArgv(action)];
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
execFile(bun, argv, {
|
|
22
161
|
encoding: "utf8",
|
|
23
162
|
env: process.env,
|
|
24
163
|
timeout: 60_000,
|
|
@@ -26,16 +165,128 @@ export function runStartupInstallAction(action: StartupInstallAction): Promise<{
|
|
|
26
165
|
maxBuffer: 256 * 1024,
|
|
27
166
|
}, (error, stdout, stderr) => {
|
|
28
167
|
if (error) {
|
|
29
|
-
const
|
|
30
|
-
reject(new Error(detail
|
|
168
|
+
const failure = classifyCliInstallFailure(stdout, stderr, error);
|
|
169
|
+
reject(Object.assign(new Error(failure.detail), { ocxInstallFailure: failure }));
|
|
31
170
|
return;
|
|
32
171
|
}
|
|
33
|
-
resolve({
|
|
34
|
-
message: action === "install-service"
|
|
35
|
-
? "Background service installed."
|
|
36
|
-
: "Codex launcher shim installed.",
|
|
37
|
-
});
|
|
172
|
+
resolve({ stdout, stderr });
|
|
38
173
|
});
|
|
39
174
|
});
|
|
40
|
-
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function installFailureCode(error: unknown): string | null {
|
|
178
|
+
if (error && typeof error === "object" && "ocxInstallFailure" in error) {
|
|
179
|
+
const failure = (error as { ocxInstallFailure?: CliInstallFailure }).ocxInstallFailure;
|
|
180
|
+
if (failure?.code) return failure.code;
|
|
181
|
+
}
|
|
182
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
183
|
+
return extractOcxErrorCode(detail);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function rejectIfBusy(_action: StartupInstallAction): Error | null {
|
|
187
|
+
if (installState.status === "running") {
|
|
188
|
+
return new Error(`Another startup installation is already running: ${installState.action}`);
|
|
189
|
+
}
|
|
190
|
+
if (installState.status === "indeterminate") {
|
|
191
|
+
return new Error(RECONCILING_MESSAGE);
|
|
192
|
+
}
|
|
193
|
+
if (installState.status === "blocked") {
|
|
194
|
+
return new Error(BLOCKED_PARTIAL_MESSAGE);
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function applyReconciliationOutcome(
|
|
200
|
+
attemptId: string,
|
|
201
|
+
outcome: ElevatedReconciliationOutcome,
|
|
202
|
+
): void {
|
|
203
|
+
if (installState.status !== "indeterminate" || installState.attemptId !== attemptId) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (outcome === "blocked-partial") {
|
|
207
|
+
installState = {
|
|
208
|
+
status: "blocked",
|
|
209
|
+
reason: "partial-elevated-install",
|
|
210
|
+
action: "install-service",
|
|
211
|
+
attemptId,
|
|
212
|
+
detail: BLOCKED_PARTIAL_MESSAGE,
|
|
213
|
+
};
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
installState = { status: "idle" };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Execute the existing fixed CLI installer outside the proxy event loop.
|
|
221
|
+
*
|
|
222
|
+
* After an elevation request timeout the lock becomes `indeterminate` until the
|
|
223
|
+
* original elevated transaction completes and is reconciled. A process restart
|
|
224
|
+
* clears this in-memory lock — callers must then inspect Task Scheduler reality
|
|
225
|
+
* (see evaluateSchedulerInstallRestartReconciliation) before installing again.
|
|
226
|
+
*/
|
|
227
|
+
export function runStartupInstallAction(action: StartupInstallAction): Promise<{ message: string }> {
|
|
228
|
+
const busy = rejectIfBusy(action);
|
|
229
|
+
if (busy) return Promise.reject(busy);
|
|
230
|
+
|
|
231
|
+
const attemptId = randomUUID();
|
|
232
|
+
const startedAt = Date.now();
|
|
233
|
+
installState = { status: "running", action, attemptId, startedAt };
|
|
234
|
+
|
|
235
|
+
const operation = (async () => {
|
|
236
|
+
try {
|
|
237
|
+
await runCliInstall(action);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
const code = installFailureCode(error);
|
|
240
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
241
|
+
// Elevate only for a structured Task Scheduler /create access denial — never for
|
|
242
|
+
// WinSW removal, asset writes, or generic permission errors.
|
|
243
|
+
if (
|
|
244
|
+
action === "install-service"
|
|
245
|
+
&& process.platform === "win32"
|
|
246
|
+
&& (code === WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED_MARKER
|
|
247
|
+
|| isWindowsSchtasksCreateAccessDenied(detail))
|
|
248
|
+
) {
|
|
249
|
+
const finalized = await finalizeImpl(undefined, {
|
|
250
|
+
attemptId,
|
|
251
|
+
stillOwnsAttempt: ownsStartupInstallAttempt,
|
|
252
|
+
});
|
|
253
|
+
if (finalized.kind === "indeterminate") {
|
|
254
|
+
const ownedAttemptId = finalized.attemptId;
|
|
255
|
+
installState = {
|
|
256
|
+
status: "indeterminate",
|
|
257
|
+
action: "install-service",
|
|
258
|
+
attemptId: ownedAttemptId,
|
|
259
|
+
startedAt,
|
|
260
|
+
timedOutAt: Date.now(),
|
|
261
|
+
reconciliation: finalized.reconciliation,
|
|
262
|
+
};
|
|
263
|
+
void finalized.reconciliation.then(
|
|
264
|
+
outcome => {
|
|
265
|
+
applyReconciliationOutcome(ownedAttemptId, outcome);
|
|
266
|
+
},
|
|
267
|
+
() => {
|
|
268
|
+
// Fail closed: a rejected reconciliation must not leave an unhandled rejection
|
|
269
|
+
// or silently idle the lock while Task Scheduler state may still be partial.
|
|
270
|
+
applyReconciliationOutcome(ownedAttemptId, "blocked-partial");
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
throw new Error(INDETERMINATE_MESSAGE);
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
message: action === "install-service"
|
|
281
|
+
? "Background service installed."
|
|
282
|
+
: "Codex launcher shim installed.",
|
|
283
|
+
};
|
|
284
|
+
})();
|
|
285
|
+
|
|
286
|
+
return operation.finally(() => {
|
|
287
|
+
// Do not clear an indeterminate or blocked lock from a timed-out elevation.
|
|
288
|
+
if (installState.status === "running" && installState.attemptId === attemptId) {
|
|
289
|
+
installState = { status: "idle" };
|
|
290
|
+
}
|
|
291
|
+
});
|
|
41
292
|
}
|