@clinebot/core 0.0.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.
Files changed (200) hide show
  1. package/README.md +88 -0
  2. package/dist/account/cline-account-service.d.ts +34 -0
  3. package/dist/account/index.d.ts +3 -0
  4. package/dist/account/rpc.d.ts +38 -0
  5. package/dist/account/types.d.ts +74 -0
  6. package/dist/agents/agent-config-loader.d.ts +18 -0
  7. package/dist/agents/agent-config-parser.d.ts +25 -0
  8. package/dist/agents/hooks-config-loader.d.ts +23 -0
  9. package/dist/agents/index.d.ts +11 -0
  10. package/dist/agents/plugin-config-loader.d.ts +22 -0
  11. package/dist/agents/plugin-loader.d.ts +9 -0
  12. package/dist/agents/plugin-sandbox.d.ts +12 -0
  13. package/dist/agents/unified-config-file-watcher.d.ts +77 -0
  14. package/dist/agents/user-instruction-config-loader.d.ts +63 -0
  15. package/dist/auth/client.d.ts +11 -0
  16. package/dist/auth/cline.d.ts +41 -0
  17. package/dist/auth/codex.d.ts +39 -0
  18. package/dist/auth/oca.d.ts +22 -0
  19. package/dist/auth/server.d.ts +22 -0
  20. package/dist/auth/types.d.ts +72 -0
  21. package/dist/auth/utils.d.ts +32 -0
  22. package/dist/chat/chat-schema.d.ts +145 -0
  23. package/dist/default-tools/constants.d.ts +23 -0
  24. package/dist/default-tools/definitions.d.ts +96 -0
  25. package/dist/default-tools/executors/apply-patch-parser.d.ts +68 -0
  26. package/dist/default-tools/executors/apply-patch.d.ts +26 -0
  27. package/dist/default-tools/executors/bash.d.ts +49 -0
  28. package/dist/default-tools/executors/editor.d.ts +31 -0
  29. package/dist/default-tools/executors/file-read.d.ts +40 -0
  30. package/dist/default-tools/executors/index.d.ts +44 -0
  31. package/dist/default-tools/executors/search.d.ts +50 -0
  32. package/dist/default-tools/executors/web-fetch.d.ts +58 -0
  33. package/dist/default-tools/index.d.ts +57 -0
  34. package/dist/default-tools/presets.d.ts +124 -0
  35. package/dist/default-tools/schemas.d.ts +121 -0
  36. package/dist/default-tools/types.d.ts +237 -0
  37. package/dist/index.d.ts +23 -0
  38. package/dist/index.js +220 -0
  39. package/dist/input/file-indexer.d.ts +5 -0
  40. package/dist/input/index.d.ts +4 -0
  41. package/dist/input/mention-enricher.d.ts +12 -0
  42. package/dist/mcp/config-loader.d.ts +15 -0
  43. package/dist/mcp/index.d.ts +4 -0
  44. package/dist/mcp/manager.d.ts +24 -0
  45. package/dist/mcp/types.d.ts +66 -0
  46. package/dist/runtime/hook-file-hooks.d.ts +18 -0
  47. package/dist/runtime/rules.d.ts +5 -0
  48. package/dist/runtime/runtime-builder.d.ts +5 -0
  49. package/dist/runtime/sandbox/subprocess-sandbox.d.ts +19 -0
  50. package/dist/runtime/session-runtime.d.ts +36 -0
  51. package/dist/runtime/tool-approval.d.ts +9 -0
  52. package/dist/runtime/workflows.d.ts +13 -0
  53. package/dist/server/index.d.ts +47 -0
  54. package/dist/server/index.js +641 -0
  55. package/dist/session/default-session-manager.d.ts +77 -0
  56. package/dist/session/rpc-session-service.d.ts +12 -0
  57. package/dist/session/runtime-oauth-token-manager.d.ts +28 -0
  58. package/dist/session/session-artifacts.d.ts +19 -0
  59. package/dist/session/session-graph.d.ts +15 -0
  60. package/dist/session/session-host.d.ts +21 -0
  61. package/dist/session/session-manager.d.ts +50 -0
  62. package/dist/session/session-manifest.d.ts +30 -0
  63. package/dist/session/session-service.d.ts +113 -0
  64. package/dist/session/sqlite-rpc-session-backend.d.ts +30 -0
  65. package/dist/session/unified-session-persistence-service.d.ts +93 -0
  66. package/dist/session/workspace-manager.d.ts +28 -0
  67. package/dist/session/workspace-manifest.d.ts +25 -0
  68. package/dist/storage/provider-settings-legacy-migration.d.ts +13 -0
  69. package/dist/storage/provider-settings-manager.d.ts +20 -0
  70. package/dist/storage/sqlite-session-store.d.ts +29 -0
  71. package/dist/storage/sqlite-team-store.d.ts +31 -0
  72. package/dist/storage/team-store.d.ts +2 -0
  73. package/dist/team/index.d.ts +1 -0
  74. package/dist/team/projections.d.ts +8 -0
  75. package/dist/types/common.d.ts +10 -0
  76. package/dist/types/config.d.ts +37 -0
  77. package/dist/types/events.d.ts +54 -0
  78. package/dist/types/provider-settings.d.ts +20 -0
  79. package/dist/types/sessions.d.ts +9 -0
  80. package/dist/types/storage.d.ts +37 -0
  81. package/dist/types/workspace.d.ts +7 -0
  82. package/dist/types.d.ts +26 -0
  83. package/package.json +63 -0
  84. package/src/account/cline-account-service.test.ts +101 -0
  85. package/src/account/cline-account-service.ts +267 -0
  86. package/src/account/index.ts +20 -0
  87. package/src/account/rpc.test.ts +62 -0
  88. package/src/account/rpc.ts +172 -0
  89. package/src/account/types.ts +80 -0
  90. package/src/agents/agent-config-loader.test.ts +234 -0
  91. package/src/agents/agent-config-loader.ts +107 -0
  92. package/src/agents/agent-config-parser.ts +191 -0
  93. package/src/agents/hooks-config-loader.ts +97 -0
  94. package/src/agents/index.ts +84 -0
  95. package/src/agents/plugin-config-loader.test.ts +91 -0
  96. package/src/agents/plugin-config-loader.ts +160 -0
  97. package/src/agents/plugin-loader.test.ts +102 -0
  98. package/src/agents/plugin-loader.ts +105 -0
  99. package/src/agents/plugin-sandbox.test.ts +120 -0
  100. package/src/agents/plugin-sandbox.ts +471 -0
  101. package/src/agents/unified-config-file-watcher.test.ts +196 -0
  102. package/src/agents/unified-config-file-watcher.ts +483 -0
  103. package/src/agents/user-instruction-config-loader.test.ts +158 -0
  104. package/src/agents/user-instruction-config-loader.ts +438 -0
  105. package/src/auth/client.test.ts +40 -0
  106. package/src/auth/client.ts +25 -0
  107. package/src/auth/cline.test.ts +130 -0
  108. package/src/auth/cline.ts +414 -0
  109. package/src/auth/codex.test.ts +170 -0
  110. package/src/auth/codex.ts +466 -0
  111. package/src/auth/oca.test.ts +215 -0
  112. package/src/auth/oca.ts +546 -0
  113. package/src/auth/server.ts +216 -0
  114. package/src/auth/types.ts +78 -0
  115. package/src/auth/utils.test.ts +128 -0
  116. package/src/auth/utils.ts +247 -0
  117. package/src/chat/chat-schema.ts +82 -0
  118. package/src/default-tools/constants.ts +35 -0
  119. package/src/default-tools/definitions.test.ts +233 -0
  120. package/src/default-tools/definitions.ts +632 -0
  121. package/src/default-tools/executors/apply-patch-parser.ts +520 -0
  122. package/src/default-tools/executors/apply-patch.ts +359 -0
  123. package/src/default-tools/executors/bash.ts +205 -0
  124. package/src/default-tools/executors/editor.ts +231 -0
  125. package/src/default-tools/executors/file-read.test.ts +25 -0
  126. package/src/default-tools/executors/file-read.ts +94 -0
  127. package/src/default-tools/executors/index.ts +75 -0
  128. package/src/default-tools/executors/search.ts +278 -0
  129. package/src/default-tools/executors/web-fetch.ts +259 -0
  130. package/src/default-tools/index.ts +161 -0
  131. package/src/default-tools/presets.test.ts +63 -0
  132. package/src/default-tools/presets.ts +168 -0
  133. package/src/default-tools/schemas.ts +228 -0
  134. package/src/default-tools/types.ts +324 -0
  135. package/src/index.ts +119 -0
  136. package/src/input/file-indexer.d.ts +11 -0
  137. package/src/input/file-indexer.test.ts +87 -0
  138. package/src/input/file-indexer.ts +280 -0
  139. package/src/input/index.ts +7 -0
  140. package/src/input/mention-enricher.test.ts +82 -0
  141. package/src/input/mention-enricher.ts +119 -0
  142. package/src/mcp/config-loader.test.ts +238 -0
  143. package/src/mcp/config-loader.ts +219 -0
  144. package/src/mcp/index.ts +26 -0
  145. package/src/mcp/manager.test.ts +106 -0
  146. package/src/mcp/manager.ts +262 -0
  147. package/src/mcp/types.ts +88 -0
  148. package/src/runtime/hook-file-hooks.test.ts +106 -0
  149. package/src/runtime/hook-file-hooks.ts +736 -0
  150. package/src/runtime/index.ts +27 -0
  151. package/src/runtime/rules.ts +34 -0
  152. package/src/runtime/runtime-builder.team-persistence.test.ts +203 -0
  153. package/src/runtime/runtime-builder.test.ts +215 -0
  154. package/src/runtime/runtime-builder.ts +515 -0
  155. package/src/runtime/runtime-parity.test.ts +132 -0
  156. package/src/runtime/sandbox/subprocess-sandbox.ts +207 -0
  157. package/src/runtime/session-runtime.ts +44 -0
  158. package/src/runtime/tool-approval.ts +104 -0
  159. package/src/runtime/workflows.test.ts +119 -0
  160. package/src/runtime/workflows.ts +54 -0
  161. package/src/server/index.ts +282 -0
  162. package/src/session/default-session-manager.e2e.test.ts +354 -0
  163. package/src/session/default-session-manager.test.ts +816 -0
  164. package/src/session/default-session-manager.ts +1286 -0
  165. package/src/session/index.ts +37 -0
  166. package/src/session/rpc-session-service.ts +189 -0
  167. package/src/session/runtime-oauth-token-manager.test.ts +137 -0
  168. package/src/session/runtime-oauth-token-manager.ts +265 -0
  169. package/src/session/session-artifacts.ts +106 -0
  170. package/src/session/session-graph.ts +90 -0
  171. package/src/session/session-host.ts +190 -0
  172. package/src/session/session-manager.ts +56 -0
  173. package/src/session/session-manifest.ts +29 -0
  174. package/src/session/session-service.team-persistence.test.ts +48 -0
  175. package/src/session/session-service.ts +610 -0
  176. package/src/session/sqlite-rpc-session-backend.ts +303 -0
  177. package/src/session/unified-session-persistence-service.ts +781 -0
  178. package/src/session/workspace-manager.ts +98 -0
  179. package/src/session/workspace-manifest.ts +100 -0
  180. package/src/storage/artifact-store.ts +1 -0
  181. package/src/storage/index.ts +11 -0
  182. package/src/storage/provider-settings-legacy-migration.test.ts +175 -0
  183. package/src/storage/provider-settings-legacy-migration.ts +637 -0
  184. package/src/storage/provider-settings-manager.test.ts +111 -0
  185. package/src/storage/provider-settings-manager.ts +129 -0
  186. package/src/storage/session-store.ts +1 -0
  187. package/src/storage/sqlite-session-store.ts +270 -0
  188. package/src/storage/sqlite-team-store.ts +443 -0
  189. package/src/storage/team-store.ts +5 -0
  190. package/src/team/index.ts +4 -0
  191. package/src/team/projections.ts +285 -0
  192. package/src/types/common.ts +14 -0
  193. package/src/types/config.ts +64 -0
  194. package/src/types/events.ts +46 -0
  195. package/src/types/index.ts +24 -0
  196. package/src/types/provider-settings.ts +43 -0
  197. package/src/types/sessions.ts +16 -0
  198. package/src/types/storage.ts +64 -0
  199. package/src/types/workspace.ts +7 -0
  200. package/src/types.ts +127 -0
@@ -0,0 +1,98 @@
1
+ import type { WorkspaceInfo } from "../types/workspace";
2
+ import {
3
+ generateWorkspaceInfo,
4
+ normalizeWorkspacePath,
5
+ upsertWorkspaceInfo,
6
+ type WorkspaceManifest,
7
+ WorkspaceManifestSchema,
8
+ } from "./workspace-manifest";
9
+
10
+ export interface WorkspaceManagerEvent {
11
+ type: "workspace_added" | "workspace_switched";
12
+ workspace: WorkspaceInfo;
13
+ }
14
+
15
+ export interface WorkspaceManager {
16
+ addWorkspacePath(workspacePath: string): Promise<WorkspaceInfo>;
17
+ switchWorkspace(workspacePath: string): Promise<WorkspaceInfo>;
18
+ subscribe(listener: (event: WorkspaceManagerEvent) => void): () => void;
19
+ getCurrentWorkspace(): WorkspaceInfo | undefined;
20
+ getWorkspace(workspacePath: string): WorkspaceInfo | undefined;
21
+ listWorkspaces(): WorkspaceInfo[];
22
+ getManifest(): WorkspaceManifest;
23
+ }
24
+
25
+ export class InMemoryWorkspaceManager implements WorkspaceManager {
26
+ private manifest: WorkspaceManifest;
27
+ private readonly listeners = new Set<
28
+ (event: WorkspaceManagerEvent) => void
29
+ >();
30
+
31
+ constructor(manifest?: WorkspaceManifest) {
32
+ this.manifest = WorkspaceManifestSchema.parse(
33
+ manifest ?? { workspaces: {} },
34
+ );
35
+ }
36
+
37
+ async addWorkspacePath(workspacePath: string): Promise<WorkspaceInfo> {
38
+ const info = await generateWorkspaceInfo(workspacePath);
39
+ this.manifest = upsertWorkspaceInfo(this.manifest, info);
40
+ this.emit({ type: "workspace_added", workspace: info });
41
+ return info;
42
+ }
43
+
44
+ async switchWorkspace(workspacePath: string): Promise<WorkspaceInfo> {
45
+ const normalizedPath = normalizeWorkspacePath(workspacePath);
46
+ const existing = this.manifest.workspaces[normalizedPath];
47
+ if (existing) {
48
+ this.manifest = WorkspaceManifestSchema.parse({
49
+ ...this.manifest,
50
+ currentWorkspacePath: normalizedPath,
51
+ });
52
+ this.emit({ type: "workspace_switched", workspace: existing });
53
+ return existing;
54
+ }
55
+
56
+ const added = await this.addWorkspacePath(normalizedPath);
57
+ this.manifest = WorkspaceManifestSchema.parse({
58
+ ...this.manifest,
59
+ currentWorkspacePath: added.rootPath,
60
+ });
61
+ this.emit({ type: "workspace_switched", workspace: added });
62
+ return added;
63
+ }
64
+
65
+ subscribe(listener: (event: WorkspaceManagerEvent) => void): () => void {
66
+ this.listeners.add(listener);
67
+ return () => {
68
+ this.listeners.delete(listener);
69
+ };
70
+ }
71
+
72
+ getCurrentWorkspace(): WorkspaceInfo | undefined {
73
+ const currentPath = this.manifest.currentWorkspacePath;
74
+ if (!currentPath) {
75
+ return undefined;
76
+ }
77
+ return this.manifest.workspaces[currentPath];
78
+ }
79
+
80
+ getWorkspace(workspacePath: string): WorkspaceInfo | undefined {
81
+ const normalizedPath = normalizeWorkspacePath(workspacePath);
82
+ return this.manifest.workspaces[normalizedPath];
83
+ }
84
+
85
+ listWorkspaces(): WorkspaceInfo[] {
86
+ return Object.values(this.manifest.workspaces);
87
+ }
88
+
89
+ getManifest(): WorkspaceManifest {
90
+ return this.manifest;
91
+ }
92
+
93
+ private emit(event: WorkspaceManagerEvent): void {
94
+ for (const listener of this.listeners) {
95
+ listener(event);
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,100 @@
1
+ import { basename, resolve } from "node:path";
2
+ import simpleGit from "simple-git";
3
+ import { z } from "zod";
4
+ import type { WorkspaceInfo } from "../types/workspace";
5
+
6
+ export const WorkspaceInfoSchema = z.object({
7
+ rootPath: z.string().min(1),
8
+ hint: z.string().min(1).optional(),
9
+ associatedRemoteUrls: z.array(z.string().min(1)).optional(),
10
+ latestGitCommitHash: z.string().min(1).optional(),
11
+ latestGitBranchName: z.string().min(1).optional(),
12
+ });
13
+
14
+ export const WorkspaceManifestSchema = z.object({
15
+ currentWorkspacePath: z.string().min(1).optional(),
16
+ workspaces: z.record(z.string().min(1), WorkspaceInfoSchema),
17
+ });
18
+
19
+ export type WorkspaceManifest = z.infer<typeof WorkspaceManifestSchema>;
20
+
21
+ export function emptyWorkspaceManifest(): WorkspaceManifest {
22
+ return { workspaces: {} };
23
+ }
24
+
25
+ export function normalizeWorkspacePath(workspacePath: string): string {
26
+ return resolve(workspacePath);
27
+ }
28
+
29
+ export async function generateWorkspaceInfo(
30
+ workspacePath: string,
31
+ ): Promise<WorkspaceInfo> {
32
+ const rootPath = normalizeWorkspacePath(workspacePath);
33
+ const info: WorkspaceInfo = {
34
+ rootPath,
35
+ hint: basename(rootPath),
36
+ };
37
+
38
+ try {
39
+ const git = simpleGit({ baseDir: rootPath });
40
+ const isRepo = await git.checkIsRepo();
41
+ if (!isRepo) {
42
+ return info;
43
+ }
44
+
45
+ const remotes = await git.getRemotes(true);
46
+ if (remotes.length > 0) {
47
+ const associatedRemoteUrls = remotes.map((remote) => {
48
+ const remoteUrl = remote.refs.fetch || remote.refs.push;
49
+ return `${remote.name}: ${remoteUrl}`;
50
+ });
51
+ info.associatedRemoteUrls = associatedRemoteUrls;
52
+ }
53
+
54
+ const latestGitCommitHash = (await git.revparse(["HEAD"])).trim();
55
+ if (latestGitCommitHash.length > 0) {
56
+ info.latestGitCommitHash = latestGitCommitHash;
57
+ }
58
+
59
+ const latestGitBranchName = (await git.branch()).current.trim();
60
+ if (latestGitBranchName.length > 0) {
61
+ info.latestGitBranchName = latestGitBranchName;
62
+ }
63
+ } catch {
64
+ // Non-git workspaces keep only path + hint.
65
+ }
66
+
67
+ return info;
68
+ }
69
+
70
+ export function upsertWorkspaceInfo(
71
+ manifest: WorkspaceManifest,
72
+ info: WorkspaceInfo,
73
+ ): WorkspaceManifest {
74
+ const nextManifest: WorkspaceManifest = {
75
+ ...manifest,
76
+ workspaces: {
77
+ ...manifest.workspaces,
78
+ [info.rootPath]: info,
79
+ },
80
+ };
81
+ if (!nextManifest.currentWorkspacePath) {
82
+ nextManifest.currentWorkspacePath = info.rootPath;
83
+ }
84
+ return WorkspaceManifestSchema.parse(nextManifest);
85
+ }
86
+
87
+ export async function buildWorkspaceMetadata(cwd: string): Promise<string> {
88
+ const workspaceInfo = await generateWorkspaceInfo(cwd);
89
+ const workspaceConfig = {
90
+ workspaces: {
91
+ [workspaceInfo.rootPath]: {
92
+ hint: workspaceInfo.hint,
93
+ associatedRemoteUrls: workspaceInfo.associatedRemoteUrls,
94
+ latestGitCommitHash: workspaceInfo.latestGitCommitHash,
95
+ latestGitBranchName: workspaceInfo.latestGitBranchName,
96
+ },
97
+ },
98
+ };
99
+ return `# Workspace Configuration\n${JSON.stringify(workspaceConfig, null, 2)}`;
100
+ }
@@ -0,0 +1 @@
1
+ export type { ArtifactStore } from "../types/storage";
@@ -0,0 +1,11 @@
1
+ export type { ArtifactStore } from "./artifact-store";
2
+ export {
3
+ type MigrateLegacyProviderSettingsOptions,
4
+ type MigrateLegacyProviderSettingsResult,
5
+ migrateLegacyProviderSettings,
6
+ } from "./provider-settings-legacy-migration";
7
+ export { ProviderSettingsManager } from "./provider-settings-manager";
8
+ export type { SessionStore } from "./session-store";
9
+ export { SqliteSessionStore } from "./sqlite-session-store";
10
+ export type { TeamStore } from "./team-store";
11
+ export { SqliteTeamStore, type SqliteTeamStoreOptions } from "./team-store";
@@ -0,0 +1,175 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { migrateLegacyProviderSettings } from "./provider-settings-legacy-migration";
6
+ import { ProviderSettingsManager } from "./provider-settings-manager";
7
+
8
+ describe("migrateLegacyProviderSettings", () => {
9
+ const tempDirs: string[] = [];
10
+
11
+ afterEach(() => {
12
+ for (const dir of tempDirs.splice(0)) {
13
+ rmSync(dir, { recursive: true, force: true });
14
+ }
15
+ });
16
+
17
+ it("migrates legacy provider state into providers.json when target is empty", () => {
18
+ const tempDir = mkdtempSync(
19
+ path.join(os.tmpdir(), "core-legacy-provider-"),
20
+ );
21
+ tempDirs.push(tempDir);
22
+ const providersPath = path.join(tempDir, "settings", "providers.json");
23
+ const manager = new ProviderSettingsManager({ filePath: providersPath });
24
+
25
+ writeFileSync(
26
+ path.join(tempDir, "globalState.json"),
27
+ JSON.stringify(
28
+ {
29
+ mode: "act",
30
+ actModeApiProvider: "anthropic",
31
+ actModeApiModelId: "claude-sonnet-4-6",
32
+ anthropicBaseUrl: "https://example.invalid/anthropic",
33
+ actModeReasoningEffort: "high",
34
+ actModeThinkingBudgetTokens: 2048,
35
+ requestTimeoutMs: 90000,
36
+ },
37
+ null,
38
+ 2,
39
+ ),
40
+ );
41
+ writeFileSync(
42
+ path.join(tempDir, "secrets.json"),
43
+ JSON.stringify({ apiKey: "legacy-anthropic-key" }, null, 2),
44
+ );
45
+
46
+ const result = migrateLegacyProviderSettings({
47
+ providerSettingsManager: manager,
48
+ dataDir: tempDir,
49
+ });
50
+
51
+ expect(result).toMatchObject({
52
+ migrated: true,
53
+ providerCount: 1,
54
+ lastUsedProvider: "anthropic",
55
+ });
56
+ expect(manager.getProviderSettings("anthropic")).toEqual({
57
+ provider: "anthropic",
58
+ model: "claude-sonnet-4-6",
59
+ apiKey: "legacy-anthropic-key",
60
+ baseUrl: "https://example.invalid/anthropic",
61
+ timeout: 90000,
62
+ reasoning: {
63
+ effort: "high",
64
+ budgetTokens: 2048,
65
+ },
66
+ });
67
+ expect(manager.read().providers.anthropic?.tokenSource).toBe("migration");
68
+ });
69
+
70
+ it("migrates missing providers without overwriting existing providers", () => {
71
+ const tempDir = mkdtempSync(
72
+ path.join(os.tmpdir(), "core-legacy-provider-"),
73
+ );
74
+ tempDirs.push(tempDir);
75
+ const providersPath = path.join(tempDir, "settings", "providers.json");
76
+ const manager = new ProviderSettingsManager({ filePath: providersPath });
77
+
78
+ manager.saveProviderSettings({
79
+ provider: "openai",
80
+ model: "gpt-5",
81
+ apiKey: "already-migrated",
82
+ });
83
+ writeFileSync(
84
+ path.join(tempDir, "globalState.json"),
85
+ JSON.stringify(
86
+ {
87
+ mode: "act",
88
+ actModeApiProvider: "anthropic",
89
+ },
90
+ null,
91
+ 2,
92
+ ),
93
+ );
94
+ writeFileSync(
95
+ path.join(tempDir, "secrets.json"),
96
+ JSON.stringify({ apiKey: "legacy-key" }, null, 2),
97
+ );
98
+
99
+ const result = migrateLegacyProviderSettings({
100
+ providerSettingsManager: manager,
101
+ dataDir: tempDir,
102
+ });
103
+
104
+ expect(result.migrated).toBe(true);
105
+ expect(manager.getProviderSettings("openai")?.apiKey).toBe(
106
+ "already-migrated",
107
+ );
108
+ expect(manager.getProviderSettings("anthropic")).toEqual({
109
+ provider: "anthropic",
110
+ model: "claude-sonnet-4-6",
111
+ apiKey: "legacy-key",
112
+ });
113
+ expect(manager.read().providers.openai?.tokenSource).toBe("manual");
114
+ expect(manager.read().providers.anthropic?.tokenSource).toBe("migration");
115
+ });
116
+
117
+ it("migrates legacy OpenAI Codex OAuth credentials", () => {
118
+ const tempDir = mkdtempSync(
119
+ path.join(os.tmpdir(), "core-legacy-provider-"),
120
+ );
121
+ tempDirs.push(tempDir);
122
+ const providersPath = path.join(tempDir, "settings", "providers.json");
123
+ const manager = new ProviderSettingsManager({ filePath: providersPath });
124
+
125
+ writeFileSync(
126
+ path.join(tempDir, "globalState.json"),
127
+ JSON.stringify(
128
+ {
129
+ mode: "act",
130
+ actModeApiProvider: "openai-codex",
131
+ },
132
+ null,
133
+ 2,
134
+ ),
135
+ );
136
+ writeFileSync(
137
+ path.join(tempDir, "secrets.json"),
138
+ JSON.stringify(
139
+ {
140
+ "openai-codex-oauth-credentials": JSON.stringify({
141
+ type: "openai-codex",
142
+ access_token: "legacy-access",
143
+ refresh_token: "legacy-refresh",
144
+ expires: Date.now() + 60_000,
145
+ accountId: "acct_123",
146
+ }),
147
+ },
148
+ null,
149
+ 2,
150
+ ),
151
+ );
152
+
153
+ const result = migrateLegacyProviderSettings({
154
+ providerSettingsManager: manager,
155
+ dataDir: tempDir,
156
+ });
157
+
158
+ expect(result).toMatchObject({
159
+ migrated: true,
160
+ lastUsedProvider: "openai-codex",
161
+ });
162
+ expect(manager.getProviderSettings("openai-codex")).toEqual({
163
+ provider: "openai-codex",
164
+ apiKey: "legacy-access",
165
+ auth: {
166
+ accessToken: "legacy-access",
167
+ refreshToken: "legacy-refresh",
168
+ accountId: "acct_123",
169
+ },
170
+ });
171
+ expect(manager.read().providers["openai-codex"]?.tokenSource).toBe(
172
+ "migration",
173
+ );
174
+ });
175
+ });