codex-copilot-dx 0.6.21 → 0.7.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.
@@ -1,7 +1,5 @@
1
1
  import fs from "node:fs";
2
2
  import os from "node:os";
3
- import path from "node:path";
4
- import { atomicWriteFilePairSync, atomicWriteFileSync } from "./atomic-file.mjs";
5
3
  import {
6
4
  githubTokenLockPath,
7
5
  githubTokenMetadataPath,
@@ -11,172 +9,62 @@ import {
11
9
  githubTokenFingerprint,
12
10
  normalizeGithubIdentity,
13
11
  } from "./github-identity.mjs";
14
- import { withFileLock } from "./lock.mjs";
15
- import { parseSafePositiveInteger, RUNTIME_DEFAULTS } from "./runtime-config.mjs";
16
12
 
17
13
  export const AUTH_PROFILE_CODEX = "codex";
18
- export const AUTH_PROFILE_CLAUDE = "claude";
19
-
20
- const AUTH_PROFILES = new Set([AUTH_PROFILE_CODEX, AUTH_PROFILE_CLAUDE]);
21
14
 
22
15
  function checkedProfile(profile) {
23
16
  const value = String(profile || "").trim().toLowerCase();
24
- if (!AUTH_PROFILES.has(value)) throw new Error(`Unsupported authentication profile: ${profile}`);
17
+ if (value !== AUTH_PROFILE_CODEX) {
18
+ throw new Error(`Unsupported authentication profile: ${profile}`);
19
+ }
25
20
  return value;
26
21
  }
27
22
 
28
23
  export function authProfilePaths(profile, { home = os.homedir() } = {}) {
29
24
  const name = checkedProfile(profile);
30
- if (name === AUTH_PROFILE_CODEX) {
31
- return {
32
- profile: name,
33
- tokenPath: githubTokenPath(home),
34
- metadataPath: githubTokenMetadataPath(home),
35
- lockPath: githubTokenLockPath(home),
36
- };
37
- }
38
-
39
- const directory = path.join(home, ".local", "share", "copilot-api", "profiles", AUTH_PROFILE_CLAUDE);
40
- const tokenPath = path.join(directory, "github_token");
41
25
  return {
42
26
  profile: name,
43
- tokenPath,
44
- metadataPath: `${tokenPath}.account.json`,
45
- lockPath: `${tokenPath}.lock`,
27
+ tokenPath: githubTokenPath(home),
28
+ metadataPath: githubTokenMetadataPath(home),
29
+ lockPath: githubTokenLockPath(home),
46
30
  };
47
31
  }
48
32
 
49
33
  function readOptionalFile(filePath) {
50
34
  try {
51
- return { exists: true, data: fs.readFileSync(filePath), mode: fs.statSync(filePath).mode & 0o777 };
35
+ return { exists: true, data: fs.readFileSync(filePath) };
52
36
  } catch (error) {
53
- if (error?.code === "ENOENT") return { exists: false, data: null, mode: 0o600 };
37
+ if (error?.code === "ENOENT") return { exists: false, data: null };
54
38
  throw error;
55
39
  }
56
40
  }
57
41
 
58
42
  function parsedMetadata(snapshot) {
59
- if (!snapshot.exists) return { value: null, malformed: false };
43
+ if (!snapshot.exists) return null;
60
44
  try {
61
- return { value: JSON.parse(snapshot.data.toString("utf8")), malformed: false };
45
+ return JSON.parse(snapshot.data.toString("utf8"));
62
46
  } catch {
63
- return { value: null, malformed: true };
47
+ return null;
64
48
  }
65
49
  }
66
50
 
67
51
  export function readAuthProfileCredentials(profile, { home = os.homedir() } = {}) {
68
52
  const paths = authProfilePaths(profile, { home });
69
53
  const tokenSnapshot = readOptionalFile(paths.tokenPath);
70
- const metadataSnapshot = readOptionalFile(paths.metadataPath);
71
54
  const token = tokenSnapshot.exists ? tokenSnapshot.data.toString("utf8").trim() : "";
72
- const metadataResult = parsedMetadata(metadataSnapshot);
73
- const metadataFingerprint = metadataResult.value?.token_fingerprint;
55
+ const metadata = parsedMetadata(readOptionalFile(paths.metadataPath));
74
56
  const metadataMatchesToken = Boolean(token)
75
- && Boolean(metadataFingerprint)
76
- && metadataFingerprint === githubTokenFingerprint(token);
77
- const metadataIdentity = normalizeGithubIdentity(metadataResult.value);
78
- const identity = metadataMatchesToken ? metadataIdentity : null;
79
-
80
- if (paths.profile === AUTH_PROFILE_CODEX) {
81
- const configured = tokenSnapshot.exists;
82
- return {
83
- profile: paths.profile,
84
- configured,
85
- valid: configured && Boolean(token),
86
- reason: !configured ? "unconfigured" : token ? "" : "empty_token",
87
- token,
88
- identity,
89
- metadata: metadataResult.value,
90
- paths,
91
- };
92
- }
93
-
94
- // Metadata is written last, but any credential artifact reserves the Claude
95
- // profile. A partial write must fail closed instead of borrowing Codex.
96
- const configured = metadataSnapshot.exists || tokenSnapshot.exists;
97
- let reason = "";
98
- if (!configured) reason = "unconfigured";
99
- else if (!metadataSnapshot.exists) reason = "missing_metadata";
100
- else if (metadataResult.malformed) reason = "metadata_malformed";
101
- else if (!tokenSnapshot.exists) reason = "missing_token";
102
- else if (!token) reason = "empty_token";
103
- else if (!metadataIdentity) reason = "metadata_identity_missing";
104
- else if (!metadataFingerprint || metadataFingerprint !== githubTokenFingerprint(token)) reason = "token_metadata_mismatch";
57
+ && Boolean(metadata?.token_fingerprint)
58
+ && metadata.token_fingerprint === githubTokenFingerprint(token);
105
59
 
106
60
  return {
107
61
  profile: paths.profile,
108
- configured,
109
- valid: configured && !reason,
110
- reason,
62
+ configured: tokenSnapshot.exists,
63
+ valid: tokenSnapshot.exists && Boolean(token),
64
+ reason: !tokenSnapshot.exists ? "unconfigured" : token ? "" : "empty_token",
111
65
  token,
112
- identity,
113
- metadata: metadataResult.value,
114
- paths,
115
- };
116
- }
117
-
118
- function lockOptions(env = process.env) {
119
- return {
120
- timeoutMs: parseSafePositiveInteger(env.CCDX_TOKEN_LOCK_TIMEOUT_MS, RUNTIME_DEFAULTS.tokenLockTimeoutMs),
121
- staleMs: parseSafePositiveInteger(env.CCDX_TOKEN_LOCK_STALE_MS, RUNTIME_DEFAULTS.tokenLockStaleMs),
122
- };
123
- }
124
-
125
- export function withAuthProfileLock(profile, fn, {
126
- home = os.homedir(),
127
- env = process.env,
128
- ...options
129
- } = {}) {
130
- const { lockPath } = authProfilePaths(profile, { home });
131
- return withFileLock(lockPath, fn, { ...lockOptions(env), ...options });
132
- }
133
-
134
- export function writeClaudeAuthProfile(token, identity, {
135
- home = os.homedir(),
136
- now = () => new Date(),
137
- writeFile = atomicWriteFileSync,
138
- unlinkFile = fs.unlinkSync,
139
- } = {}) {
140
- const cleanToken = String(token || "").trim();
141
- const normalizedIdentity = normalizeGithubIdentity(identity);
142
- if (!cleanToken) throw new Error("Claude GitHub token is empty");
143
- if (!normalizedIdentity) throw new Error("Claude GitHub account identity is missing");
144
-
145
- const paths = authProfilePaths(AUTH_PROFILE_CLAUDE, { home });
146
- const directory = path.dirname(paths.tokenPath);
147
- fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
148
- fs.chmodSync(directory, 0o700);
149
-
150
- const timestamp = now();
151
- const updatedAt = timestamp instanceof Date ? timestamp.toISOString() : new Date(timestamp).toISOString();
152
- const metadata = `${JSON.stringify({
153
- profile: AUTH_PROFILE_CLAUDE,
154
- ...normalizedIdentity,
155
- token_fingerprint: githubTokenFingerprint(cleanToken),
156
- updated_at: updatedAt,
157
- }, null, 2)}\n`;
158
-
159
- // Commit the activation marker last. Readers never observe a configured
160
- // Claude profile unless the token and its fingerprint agree.
161
- atomicWriteFilePairSync(
162
- paths.tokenPath,
163
- cleanToken,
164
- paths.metadataPath,
66
+ identity: metadataMatchesToken ? normalizeGithubIdentity(metadata) : null,
165
67
  metadata,
166
- { mode: 0o600, writeFile, unlinkFile },
167
- );
168
-
169
- return {
170
- profile: AUTH_PROFILE_CLAUDE,
171
- identity: normalizedIdentity,
172
68
  paths,
173
69
  };
174
70
  }
175
-
176
- export function profileReauthMessage(profile, { home = os.homedir() } = {}) {
177
- const name = checkedProfile(profile);
178
- if (name === AUTH_PROFILE_CLAUDE) {
179
- return "Claude authentication is unavailable. Run `ccdx auth login claude --reauth`, then restart ccdx.";
180
- }
181
- return `Codex authentication is unavailable at ${authProfilePaths(name, { home }).tokenPath}. Run ccdx again to log in.`;
182
- }