@harness-mix/cli 0.1.4 → 0.1.6
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/CHANGELOG.md +8 -0
- package/README.md +26 -96
- package/docs/chatgpt-web-sidebar.md +7 -1
- package/docs/cross-platform.md +15 -0
- package/docs/harness-handoff-design.md +377 -0
- package/docs/harness-management.md +28 -1
- package/docs/multi-agent-collaboration.md +9 -9
- package/docs/native-codex.md +21 -10
- package/output/native-build/harness-mix-shim.exe +0 -0
- package/output/native-build/renderer-extension.js +2596 -135
- package/package.json +20 -10
- package/scripts/chatgpt-context-ui-smoke.cjs +16 -0
- package/scripts/codex-accounts-test.cjs +81 -0
- package/scripts/codex-accounts-ui-smoke.cjs +70 -0
- package/scripts/collaboration-test.cjs +8 -3
- package/scripts/collaboration-ui-smoke.cjs +40 -26
- package/scripts/e2e-integrations.cjs +52 -0
- package/scripts/e2e-native-host.cjs +39 -0
- package/scripts/handoff-checkpoint-test.cjs +83 -0
- package/scripts/install-native.cjs +4 -1
- package/scripts/integrations-test.cjs +123 -0
- package/scripts/integrations-ui-smoke.cjs +124 -0
- package/scripts/native-protocol-test.cjs +180 -5
- package/scripts/native-secret-test.cjs +2 -2
- package/scripts/native-shim-test.cjs +20 -4
- package/scripts/native-sidebar-test.cjs +7 -0
- package/scripts/native-skill-roots-test.cjs +90 -0
- package/scripts/native-ui-smoke.cjs +58 -2
- package/scripts/publish-native-package.cjs +8 -5
- package/scripts/support/integrations-mcp-fixture.cjs +15 -0
- package/scripts/switch-harness-test.cjs +27 -1
- package/scripts/test-live-mentions.cjs +152 -0
- package/src/main/adapters/acp.js +3 -3
- package/src/main/adapters/antigravity.js +17 -17
- package/src/main/adapters/claude.js +50 -10
- package/src/main/adapters/codebuddy.js +1 -0
- package/src/main/adapters/codex-app-server.js +20 -13
- package/src/main/adapters/codex.js +25 -5
- package/src/main/adapters/cursor.js +5 -1
- package/src/main/adapters/dsh.js +9 -1
- package/src/main/adapters/grok.js +7 -3
- package/src/main/adapters/hermes.js +8 -0
- package/src/main/adapters/kiro.js +2 -1
- package/src/main/adapters/managed-mcp.js +25 -0
- package/src/main/adapters/native-acp.js +4 -4
- package/src/main/adapters/omp.js +11 -0
- package/src/main/adapters/openclaw.js +6 -0
- package/src/main/adapters/opencode.js +13 -3
- package/src/main/adapters/pi.js +5 -0
- package/src/main/adapters/qoder.js +1 -0
- package/src/main/adapters/trae.js +4 -0
- package/src/main/adapters/zcode.js +3 -0
- package/src/main/harness-adapter/manifest.js +17 -2
- package/src/main/host/collaboration.js +57 -11
- package/src/main/host/handoff-access.js +53 -0
- package/src/main/host/handoff-checkpoints.js +206 -0
- package/src/main/host/handoff-mcp.cjs +38 -0
- package/src/main/host/handoff-tools.js +13 -0
- package/src/main/host/handoff.js +28 -6
- package/src/main/host/integrations.js +339 -0
- package/src/main/host/runtime.js +95 -15
- package/src/main/host/store.js +4 -4
- package/src/main/native/codex-accounts.js +260 -0
- package/src/main/native/host.js +1 -1
- package/src/main/native/launcher.js +2 -2
- package/src/main/native/protocol.js +176 -14
- package/src/main/native/redact.js +1 -1
- package/src/main/native/rs/crates/shim/src/main.rs +155 -12
- package/src/main/native/thread-list.js +3 -0
- package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/index.d.ts +4 -0
- package/src/native-ui/renderer-extension/dist/types/index.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-chatgpt-context.d.ts +36 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-chatgpt-context.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts +3 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-handoff.d.ts +53 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-handoff.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts +11 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-integrations-client.d.ts +105 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-integrations-client.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +7 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/integrations-page.d.ts +6 -0
- package/src/native-ui/renderer-extension/dist/types/settings/integrations-page.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts +5 -0
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +8 -4
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/src/harness-mix-settings.ts +4 -1
- package/src/native-ui/renderer-extension/src/index.ts +28 -0
- package/src/native-ui/renderer-extension/src/renderer-agent-icon.ts +4 -4
- package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +46 -6
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +115 -18
- package/src/native-ui/renderer-extension/src/renderer-chatgpt-context.ts +87 -0
- package/src/native-ui/renderer-extension/src/renderer-codex-account-state.ts +10 -0
- package/src/native-ui/renderer-extension/src/renderer-composer-dom.ts +10 -0
- package/src/native-ui/renderer-extension/src/renderer-harness-handoff.ts +330 -0
- package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +556 -123
- package/src/native-ui/renderer-extension/src/renderer-integrations-client.ts +59 -0
- package/src/native-ui/renderer-extension/src/renderer-model-client.ts +17 -1
- package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +2 -0
- package/src/native-ui/renderer-extension/src/settings/accounts-list.ts +16 -1
- package/src/native-ui/renderer-extension/src/settings/accounts-page.ts +32 -3
- package/src/native-ui/renderer-extension/src/settings/integrations-page.ts +1474 -0
- package/src/native-ui/renderer-extension/src/settings/localization.ts +32 -13
- package/src/native-ui/renderer-extension/src/settings/pages.ts +49 -18
- package/src/native-ui/renderer-extension/src/settings/shell.css +103 -0
- package/src/native-ui/renderer-extension/test/renderer-agent-picker.test.ts +44 -13
- package/src/native-ui/renderer-extension/test/renderer-chatgpt-context.test.ts +21 -0
- package/src/native-ui/renderer-extension/test/renderer-codex-account-state.test.ts +13 -1
- package/src/native-ui/renderer-extension/test/renderer-harness-handoff.test.ts +20 -0
- package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +9 -2
- package/src/native-ui/renderer-extension/test/settings/localization.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/settings/pages.test.ts +77 -8
- package/src/native-ui/renderer-extension/test/settings/shell.test.ts +6 -0
- package/src/native-ui/shared-contracts/dist/codex-accounts.d.ts +19 -4
- package/src/native-ui/shared-contracts/dist/codex-accounts.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/codex-accounts.js +2 -0
- package/src/native-ui/shared-contracts/dist/codex-accounts.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/index.d.ts +2 -2
- package/src/native-ui/shared-contracts/dist/index.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/index.js +1 -1
- package/src/native-ui/shared-contracts/dist/index.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts +29 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.js +13 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/shared-contracts/src/codex-accounts.ts +2 -0
- package/src/native-ui/shared-contracts/src/index.ts +8 -1
- package/src/native-ui/shared-contracts/src/thread-harness-switch.ts +17 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
const serverSchema = z.object({
|
|
4
|
+
id: z.string(),
|
|
5
|
+
name: z.string(),
|
|
6
|
+
transportType: z.enum(['stdio', 'streamable_http']).optional(),
|
|
7
|
+
command: z.string().optional().default(''),
|
|
8
|
+
args: z.array(z.string()).optional().default([]),
|
|
9
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
10
|
+
env_vars: z.array(z.string()).optional(),
|
|
11
|
+
cwd: z.string().nullable().optional(),
|
|
12
|
+
url: z.string().optional(),
|
|
13
|
+
bearer_token_env_var: z.string().optional(),
|
|
14
|
+
http_headers: z.record(z.string(), z.string()).optional(),
|
|
15
|
+
env_http_headers: z.record(z.string(), z.string()).optional(),
|
|
16
|
+
enabled: z.boolean(),
|
|
17
|
+
scope: z.enum(['global', 'project']),
|
|
18
|
+
editable: z.boolean(),
|
|
19
|
+
effective: z.boolean(),
|
|
20
|
+
appliedSessions: z.number(),
|
|
21
|
+
});
|
|
22
|
+
const skillSchema = z.object({ name: z.string(), path: z.string(), root: z.string(), enabled: z.boolean(), writable: z.boolean(), scope: z.enum(['global', 'project']), status: z.string() });
|
|
23
|
+
const snapshotSchema = z.object({ harnessId: z.string(), mcpSupported: z.boolean(), skillsSupported: z.boolean(), servers: z.array(serverSchema), native: z.array(z.object({ sessionId: z.string(), name: z.string(), status: z.string(), tools: z.array(z.string()) })), skills: z.array(skillSchema), note: z.string() });
|
|
24
|
+
const catalogSchema = z.object({ harnesses: z.array(z.object({ id: z.string(), name: z.string(), available: z.boolean(), mcp: z.boolean(), skills: z.boolean() })) });
|
|
25
|
+
export type IntegrationSnapshot = z.infer<typeof snapshotSchema>;
|
|
26
|
+
export interface IntegrationScope { harnessId: string; scope: 'global' | 'project'; cwd?: string }
|
|
27
|
+
export interface ManagedServer {
|
|
28
|
+
id?: string | undefined;
|
|
29
|
+
name: string;
|
|
30
|
+
transportType?: 'stdio' | 'streamable_http' | undefined;
|
|
31
|
+
command?: string | undefined;
|
|
32
|
+
args?: string[] | undefined;
|
|
33
|
+
env?: Record<string, string> | undefined;
|
|
34
|
+
env_vars?: string[] | undefined;
|
|
35
|
+
cwd?: string | null | undefined;
|
|
36
|
+
url?: string | undefined;
|
|
37
|
+
bearer_token_env_var?: string | undefined;
|
|
38
|
+
http_headers?: Record<string, string> | undefined;
|
|
39
|
+
env_http_headers?: Record<string, string> | undefined;
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface DroppedSkillFile { path: string; contentBase64: string }
|
|
43
|
+
export interface SkillChange { name: string; root?: string; source?: string; files?: DroppedSkillFile[]; action: 'install' | 'enable' | 'disable' }
|
|
44
|
+
export interface RendererIntegrationsClient {
|
|
45
|
+
integrationCatalog(): Promise<z.infer<typeof catalogSchema>>;
|
|
46
|
+
listIntegrations(input: IntegrationScope): Promise<IntegrationSnapshot>;
|
|
47
|
+
saveMcp(input: IntegrationScope & { server: ManagedServer }): Promise<unknown>;
|
|
48
|
+
removeMcp(input: IntegrationScope & { id: string }): Promise<unknown>;
|
|
49
|
+
changeSkill(input: IntegrationScope & SkillChange): Promise<unknown>;
|
|
50
|
+
}
|
|
51
|
+
export function createRendererIntegrationsClient(send: (method: string, params: unknown) => Promise<unknown>): RendererIntegrationsClient {
|
|
52
|
+
return {
|
|
53
|
+
integrationCatalog: async () => catalogSchema.parse(await send('codexhost/integrations/catalog', {})),
|
|
54
|
+
listIntegrations: async input => snapshotSchema.parse(await send('codexhost/integrations/list', input)),
|
|
55
|
+
saveMcp: input => send('codexhost/integrations/mcp/save', input),
|
|
56
|
+
removeMcp: input => send('codexhost/integrations/mcp/remove', input),
|
|
57
|
+
changeSkill: input => send('codexhost/integrations/skill/change', input),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRendererIntegrationsClient, type RendererIntegrationsClient } from './renderer-integrations-client.js';
|
|
1
2
|
import {
|
|
2
3
|
harnessAccountListResultSchema,
|
|
3
4
|
type HarnessAccountListResult,
|
|
@@ -127,6 +128,7 @@ export const THREAD_TOKEN_USAGE_UPDATED_METHOD = "thread/tokenUsage/updated";
|
|
|
127
128
|
export const UPDATE_CHECK_METHOD = "codexhost/update/check";
|
|
128
129
|
export const UPDATE_START_METHOD = "codexhost/update/start";
|
|
129
130
|
export const UPDATE_STATUS_METHOD = "codexhost/update/status";
|
|
131
|
+
export const RUNTIME_VERSION_METHOD = "harness-mix/runtime/version";
|
|
130
132
|
export const CODEX_ACCOUNT_LIST_METHOD = "codexhost/account/list";
|
|
131
133
|
export const CODEX_ACCOUNT_REFRESH_METHOD = "codexhost/account/refresh";
|
|
132
134
|
export const CODEX_ACCOUNT_CREATE_METHOD = "codexhost/account/create";
|
|
@@ -171,7 +173,7 @@ function notificationTarget(manager: RequestManagerCandidate): RequestManagerCan
|
|
|
171
173
|
return nested && typeof nested.addNotificationCallback === "function" ? nested : null;
|
|
172
174
|
}
|
|
173
175
|
|
|
174
|
-
export interface RendererModelClient extends Partial<RendererSessionImportClient> {
|
|
176
|
+
export interface RendererModelClient extends Partial<RendererSessionImportClient>, Partial<RendererIntegrationsClient> {
|
|
175
177
|
listCollaborationAgents?(): Promise<Array<{ id: string; name: string; available: boolean; lead: boolean }>>;
|
|
176
178
|
currentHostId?(): string | null;
|
|
177
179
|
listHarnessPlugins?(): Promise<HarnessPluginListResult>;
|
|
@@ -197,6 +199,7 @@ export interface RendererModelClient extends Partial<RendererSessionImportClient
|
|
|
197
199
|
checkUpdate(): Promise<UpdateCheckResult>;
|
|
198
200
|
startUpdate(): Promise<UpdateStartResult>;
|
|
199
201
|
readUpdateStatus(): Promise<UpdateStatusResult>;
|
|
202
|
+
readCurrentVersion?(): Promise<{ version: string }>;
|
|
200
203
|
inspectCodexAccountUsage?(input: CodexAccountUsageParams): Promise<CodexAccountUsageResult>;
|
|
201
204
|
consumeCodexAccountResetCredit?(
|
|
202
205
|
input: CodexAccountResetCreditConsumeParams,
|
|
@@ -213,6 +216,7 @@ export interface RendererModelClient extends Partial<RendererSessionImportClient
|
|
|
213
216
|
cancelCodexAccountLogin(
|
|
214
217
|
input: CodexAccountLoginCancelParams,
|
|
215
218
|
): Promise<CodexAccountLoginCancelResult>;
|
|
219
|
+
logoutCodexAccount?(): Promise<CodexAccountMutationResult>;
|
|
216
220
|
subscribeCodexAccountLogin(listener: (result: CodexAccountLoginCompleted) => void): () => void;
|
|
217
221
|
}
|
|
218
222
|
|
|
@@ -331,6 +335,7 @@ export function createRendererModelClient(
|
|
|
331
335
|
if (!Array.isArray(result) || !result.every(a => a && typeof a.id === 'string' && typeof a.name === 'string' && typeof a.available === 'boolean' && typeof a.lead === 'boolean')) throw new Error('Invalid collaboration catalog');
|
|
332
336
|
return result;
|
|
333
337
|
},
|
|
338
|
+
...createRendererIntegrationsClient((method, params) => manager.sendRequest(method, params)),
|
|
334
339
|
...createRendererSessionImportClient(async (method, params) =>
|
|
335
340
|
manager.sendRequest(method, params),
|
|
336
341
|
),
|
|
@@ -477,6 +482,13 @@ export function createRendererModelClient(
|
|
|
477
482
|
);
|
|
478
483
|
return updateStatusResultSchema.parse(result);
|
|
479
484
|
},
|
|
485
|
+
async readCurrentVersion(): Promise<{ version: string }> {
|
|
486
|
+
const result = await manager.sendRequest(RUNTIME_VERSION_METHOD, {});
|
|
487
|
+
if (!isRecord(result) || typeof result.version !== "string" || !result.version.trim()) {
|
|
488
|
+
throw new Error("Invalid Harness Mix version response");
|
|
489
|
+
}
|
|
490
|
+
return { version: result.version.trim() };
|
|
491
|
+
},
|
|
480
492
|
async inspectCodexAccountUsage(
|
|
481
493
|
input: CodexAccountUsageParams,
|
|
482
494
|
): Promise<CodexAccountUsageResult> {
|
|
@@ -549,6 +561,10 @@ export function createRendererModelClient(
|
|
|
549
561
|
);
|
|
550
562
|
return codexAccountLoginCancelResultSchema.parse(result);
|
|
551
563
|
},
|
|
564
|
+
async logoutCodexAccount(): Promise<CodexAccountMutationResult> {
|
|
565
|
+
const result = await manager.sendRequest("codexhost/account/logout", {});
|
|
566
|
+
return codexAccountMutationResultSchema.parse(result);
|
|
567
|
+
},
|
|
552
568
|
subscribeCodexAccountLogin(listener: (result: CodexAccountLoginCompleted) => void): () => void {
|
|
553
569
|
const notifications = notificationTarget(source);
|
|
554
570
|
if (!notifications?.addNotificationCallback) {
|
|
@@ -24,6 +24,7 @@ const UPDATE_CHECK_TIMEOUT_MS = 5_000;
|
|
|
24
24
|
const UPDATE_RETRY_DELAYS_MS = [1_000, 3_000, 10_000, 30_000] as const;
|
|
25
25
|
|
|
26
26
|
export interface RendererSettingsLifecycleOptions {
|
|
27
|
+
getIntegrationsClient?(): import('./renderer-integrations-client.js').RendererIntegrationsClient | null;
|
|
27
28
|
getUpdateClient?(): RendererUpdateClient | null;
|
|
28
29
|
getConnectionDiagnostics?(): RendererConnectionDiagnostics | null;
|
|
29
30
|
getAccountClient?(): RendererCodexAccountClient | null;
|
|
@@ -74,6 +75,7 @@ export function installRendererSettingsLifecycle(
|
|
|
74
75
|
await options.openImportedThread(threadId, signal);
|
|
75
76
|
if (!disposed && !signal.aborted) shell?.close();
|
|
76
77
|
},
|
|
78
|
+
options.getIntegrationsClient,
|
|
77
79
|
);
|
|
78
80
|
const nextShell = installRendererSettingsShell(definitions, messages, ownerWindow.document);
|
|
79
81
|
const nextTrigger = installRendererSettingsHeaderTrigger({
|
|
@@ -12,6 +12,10 @@ import type { RendererSettingsMessages } from "./localization.js";
|
|
|
12
12
|
|
|
13
13
|
let resetDetailsSequence = 0;
|
|
14
14
|
|
|
15
|
+
export function isCodexAccountAuthenticated(account: CodexAccountSummary): boolean {
|
|
16
|
+
return account.authenticated ?? Boolean(account.email);
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
function accountPlanLabel(planType: CodexAccountSummary["planType"]): string | null {
|
|
16
20
|
if (!planType || planType === "unknown") return null;
|
|
17
21
|
if (planType === "free") return "Free";
|
|
@@ -90,6 +94,7 @@ export function renderAccountRows(
|
|
|
90
94
|
resetExpanded: boolean;
|
|
91
95
|
onActivate: () => void;
|
|
92
96
|
onSignIn: () => void;
|
|
97
|
+
onSignOut?: () => void;
|
|
93
98
|
onDelete: () => void;
|
|
94
99
|
onRetry: () => void;
|
|
95
100
|
onUseReset?: () => void;
|
|
@@ -176,7 +181,7 @@ export function renderAccountRows(
|
|
|
176
181
|
activate.addEventListener("click", input.onActivate);
|
|
177
182
|
actions.append(activate);
|
|
178
183
|
}
|
|
179
|
-
if (!account
|
|
184
|
+
if (!isCodexAccountAuthenticated(account)) {
|
|
180
185
|
const signIn = document.createElement("button");
|
|
181
186
|
signIn.type = "button";
|
|
182
187
|
signIn.className = "settings-account-action";
|
|
@@ -186,6 +191,16 @@ export function renderAccountRows(
|
|
|
186
191
|
signIn.addEventListener("click", input.onSignIn);
|
|
187
192
|
actions.append(signIn);
|
|
188
193
|
}
|
|
194
|
+
if (account.management === "native" && isCodexAccountAuthenticated(account) && input.onSignOut) {
|
|
195
|
+
const signOut = document.createElement("button");
|
|
196
|
+
signOut.type = "button";
|
|
197
|
+
signOut.className = "settings-account-action";
|
|
198
|
+
signOut.textContent = messages.accountSignOut;
|
|
199
|
+
signOut.dataset.accountFocus = `${account.accountId}:logout`;
|
|
200
|
+
signOut.disabled = input.actionsDisabled;
|
|
201
|
+
signOut.addEventListener("click", input.onSignOut);
|
|
202
|
+
actions.append(signOut);
|
|
203
|
+
}
|
|
189
204
|
// isDefault protects the native Account home; active selects the Account for
|
|
190
205
|
// new tasks. Preserve these distinct Host semantics when exposing deletion.
|
|
191
206
|
if (!account.isDefault) {
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
import {
|
|
21
21
|
accountListFocusRestorer,
|
|
22
22
|
createAccountsTable,
|
|
23
|
+
isCodexAccountAuthenticated,
|
|
23
24
|
renderAccountRows,
|
|
24
25
|
} from "./accounts-list.js";
|
|
25
26
|
import { mountHarnessAccounts, type RendererHarnessAccountClient } from "./harness-accounts.js";
|
|
@@ -44,6 +45,7 @@ export interface RendererCodexAccountClient extends RendererHarnessAccountClient
|
|
|
44
45
|
cancelCodexAccountLogin(
|
|
45
46
|
input: CodexAccountLoginCancelParams,
|
|
46
47
|
): Promise<CodexAccountLoginCancelResult>;
|
|
48
|
+
logoutCodexAccount?(): Promise<CodexAccountMutationResult>;
|
|
47
49
|
subscribeCodexAccountLogin?(listener: (result: CodexAccountLoginCompleted) => void): () => void;
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -224,13 +226,13 @@ export function createAccountsSettingsPage(
|
|
|
224
226
|
const restoreFocus = accountListFocusRestorer(list, search);
|
|
225
227
|
body.replaceChildren();
|
|
226
228
|
status.textContent = loginMessage ?? "";
|
|
227
|
-
connectedCount.textContent = String(accounts.filter(
|
|
229
|
+
connectedCount.textContent = String(accounts.filter(isCodexAccountAuthenticated).length);
|
|
228
230
|
search.disabled = login !== null || loginStartingAccountId !== null;
|
|
229
231
|
for (const [display, button] of displayButtons) {
|
|
230
232
|
button.setAttribute("aria-pressed", String(display === usageDisplay));
|
|
231
233
|
}
|
|
232
234
|
refreshUsage.disabled =
|
|
233
|
-
((!getClient()?.inspectCodexAccountUsage || !accounts.some(
|
|
235
|
+
((!getClient()?.inspectCodexAccountUsage || !accounts.some(isCodexAccountAuthenticated)) &&
|
|
234
236
|
!getClient()?.listHarnessAccounts) ||
|
|
235
237
|
harnessAccounts?.refreshing === true ||
|
|
236
238
|
[...usageByAccountId.values()].some((usage) => usage.status === "loading") ||
|
|
@@ -249,6 +251,9 @@ export function createAccountsSettingsPage(
|
|
|
249
251
|
emptyRow.append(emptyCell);
|
|
250
252
|
body.append(emptyRow);
|
|
251
253
|
}
|
|
254
|
+
// The stock Codex account remains protected, while additional Accounts
|
|
255
|
+
// are native Codex profiles isolated by CODEX_HOME.
|
|
256
|
+
add.hidden = false;
|
|
252
257
|
add.disabled = accountBusy();
|
|
253
258
|
for (const account of visibleAccounts) {
|
|
254
259
|
body.append(
|
|
@@ -262,6 +267,9 @@ export function createAccountsSettingsPage(
|
|
|
262
267
|
onActivate: () =>
|
|
263
268
|
mutate(() => client().activateCodexAccount({ accountId: account.accountId })),
|
|
264
269
|
onSignIn: () => startLogin(account.accountId),
|
|
270
|
+
...(account.management === "native" && getClient()?.logoutCodexAccount
|
|
271
|
+
? { onSignOut: () => signOut() }
|
|
272
|
+
: {}),
|
|
265
273
|
onDelete: () => deleteAccount(account.accountId),
|
|
266
274
|
onRetry: () => {
|
|
267
275
|
usageByAccountId.delete(account.accountId);
|
|
@@ -341,7 +349,7 @@ export function createAccountsSettingsPage(
|
|
|
341
349
|
};
|
|
342
350
|
const loadUsage = (nextAccounts: readonly CodexAccountSummary[]): void => {
|
|
343
351
|
const inspect = getClient()?.inspectCodexAccountUsage;
|
|
344
|
-
const signedIn = nextAccounts.filter(
|
|
352
|
+
const signedIn = nextAccounts.filter(isCodexAccountAuthenticated);
|
|
345
353
|
const keep = new Set(signedIn.map((account) => account.accountId));
|
|
346
354
|
for (const accountId of [...usageByAccountId.keys()]) {
|
|
347
355
|
if (!keep.has(accountId)) usageByAccountId.delete(accountId);
|
|
@@ -455,6 +463,27 @@ export function createAccountsSettingsPage(
|
|
|
455
463
|
},
|
|
456
464
|
});
|
|
457
465
|
};
|
|
466
|
+
const signOut = (): void => {
|
|
467
|
+
const logout = getClient()?.logoutCodexAccount;
|
|
468
|
+
if (!logout || accountBusy()) return;
|
|
469
|
+
if (document.defaultView?.confirm?.(messages.accountSignOutConfirm) === false) return;
|
|
470
|
+
accountActivating = true;
|
|
471
|
+
loginMessage = messages.accountSigningOut;
|
|
472
|
+
render();
|
|
473
|
+
void context.runLatest(() => logout(), {
|
|
474
|
+
success(result) {
|
|
475
|
+
accountActivating = false;
|
|
476
|
+
loginMessage = messages.accountSignedOut;
|
|
477
|
+
usageByAccountId.clear();
|
|
478
|
+
setAccounts([result.account]);
|
|
479
|
+
},
|
|
480
|
+
failure(error) {
|
|
481
|
+
accountActivating = false;
|
|
482
|
+
loginMessage = errorMessage(error, messages.accountSignOutFailed);
|
|
483
|
+
render();
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
};
|
|
458
487
|
const createAndLogin = (): void => {
|
|
459
488
|
if (accountBusy()) return;
|
|
460
489
|
accountCreating = true;
|