@doist/doistbot-cli 1.0.7 → 1.0.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.
@@ -70,13 +70,16 @@ export async function runAuthSetup(dependencies = {}) {
70
70
  const status = await deps.getAuthStatus();
71
71
  deps.writeLine('1. OpenAI Codex');
72
72
  deps.writeLine();
73
- if (status.openai.configured) {
73
+ if (status.openai.configured && !status.openai.refreshFailed) {
74
74
  deps.writeLine('✓ OpenAI already configured (pi-auth).');
75
75
  }
76
76
  else {
77
77
  if (!deps.isInteractive) {
78
78
  throw new Error('Cannot configure OpenAI Codex auth in non-interactive mode.');
79
79
  }
80
+ if (status.openai.refreshFailed) {
81
+ deps.writeLine('OpenAI session could not be refreshed (expired, revoked, or offline). Reconnecting.');
82
+ }
80
83
  await deps.loginOpenAICodex();
81
84
  deps.writeLine();
82
85
  deps.writeLine('✓ OpenAI configured.');
@@ -149,10 +152,19 @@ export async function runAuthSetup(dependencies = {}) {
149
152
  }
150
153
  export async function runAuthStatus() {
151
154
  const status = await getAuthStatus();
152
- writeLine(`OpenAI: ${status.openai.configured ? `configured (${status.openai.source ?? 'unknown'})` : 'not configured'}`);
155
+ writeLine(`OpenAI: ${formatOpenAIStatus(status.openai)}`);
153
156
  writeLine(`Gemini ${formatOptionalLabel()}: ${status.gemini.configured ? `configured (${status.gemini.source ?? 'unknown'})` : 'not configured'}`);
154
157
  writeLine(`OpenRouter ${formatOptionalLabel()}: ${status.openrouter.configured ? `configured (${status.openrouter.source ?? 'unknown'})` : 'not configured'}`);
155
158
  }
159
+ function formatOpenAIStatus(status) {
160
+ if (!status.configured) {
161
+ return 'not configured';
162
+ }
163
+ const base = `configured (${status.source ?? 'unknown'})`;
164
+ return status.refreshFailed
165
+ ? `${base} — session could not be refreshed; check your connection or run \`doistbot auth login\``
166
+ : base;
167
+ }
156
168
  export async function runAuthLogout(target, dependencies = {}) {
157
169
  const deps = {
158
170
  deleteGeminiApiKey,
@@ -4,7 +4,9 @@ import { checkGit, checkPiDependency, checkRepository, checkSandboxBuild } from
4
4
  import { renderDiagnosticCheck } from '../terminal.js';
5
5
  export function getConfiguredProviders({ status, }) {
6
6
  return [
7
- status.openai.configured ? `OpenAI (${status.openai.source ?? 'configured'})` : undefined,
7
+ status.openai.configured && !status.openai.refreshFailed
8
+ ? `OpenAI (${status.openai.source ?? 'configured'})`
9
+ : undefined,
8
10
  status.gemini.configured
9
11
  ? `Gemini optional (${status.gemini.source ?? 'configured'})`
10
12
  : undefined,
@@ -21,10 +21,14 @@ function getReadableProviderFailure(failure) {
21
21
  if (failure.model === 'openai' &&
22
22
  (normalized.includes('no api key found for openai') ||
23
23
  normalized.includes('no api key found for openai-codex') ||
24
+ normalized.includes('no api key for provider') ||
24
25
  normalized.includes('use /login') ||
25
26
  normalized.includes('invalid api key') ||
26
- normalized.includes('incorrect api key'))) {
27
- return 'OpenAI Codex is not authenticated. Run `doistbot auth` to reconnect OpenAI.';
27
+ normalized.includes('incorrect api key') ||
28
+ normalized.includes('refresh_token_invalidated') ||
29
+ normalized.includes('unauthorized') ||
30
+ /\b401\b/.test(normalized))) {
31
+ return 'OpenAI Codex is not authenticated. Run `doistbot auth login` to reconnect OpenAI.';
28
32
  }
29
33
  if (failure.model === 'gemini' &&
30
34
  (normalized.includes('429') ||
@@ -40,7 +44,8 @@ function getReadableProviderFailure(failure) {
40
44
  normalized.includes('invalid_argument') ||
41
45
  normalized.includes('invalid api key') ||
42
46
  normalized.includes('no api key found for google') ||
43
- normalized.includes('no api key found for gemini'))) {
47
+ normalized.includes('no api key found for gemini') ||
48
+ normalized.includes('no api key for provider'))) {
44
49
  return 'Gemini API key is invalid or missing. Run `doistbot auth` and paste a valid Gemini key.';
45
50
  }
46
51
  const firstLine = error
@@ -205,7 +210,7 @@ function getMissingAuthProviderFailures(missingProviders) {
205
210
  if (missingProviders.includes('OpenAI')) {
206
211
  failures.push({
207
212
  model: 'openai',
208
- message: 'OpenAI not configured; run `doistbot auth`',
213
+ message: 'OpenAI authentication unavailable; run `doistbot auth login`',
209
214
  });
210
215
  }
211
216
  return failures;
package/dist/auth.js CHANGED
@@ -28,9 +28,22 @@ export function getBrowserOpenCommand(platform, url) {
28
28
  }
29
29
  return { command: 'xdg-open', args: [url] };
30
30
  }
31
- export async function hasOpenAICodexAuth(storage) {
31
+ /**
32
+ * Check that the stored OAuth session is usable, not just present.
33
+ * getApiKey returns the cached access token when unexpired; when expired it
34
+ * attempts one cross-process-locked refresh, persists the result, and returns
35
+ * undefined when the refresh fails (revoked session, no network) while
36
+ * preserving the stored credentials. Unexpected storage errors propagate.
37
+ */
38
+ export async function getOpenAICodexAuthState(storage) {
32
39
  const resolvedStorage = storage ?? (await createPiAuthStorage());
33
- return resolvedStorage.get(OPENAI_CODEX_PROVIDER_ID)?.type === 'oauth';
40
+ if (resolvedStorage.get(OPENAI_CODEX_PROVIDER_ID)?.type !== 'oauth') {
41
+ return 'missing';
42
+ }
43
+ const apiKey = await resolvedStorage.getApiKey(OPENAI_CODEX_PROVIDER_ID, {
44
+ includeFallback: false,
45
+ });
46
+ return apiKey ? 'ready' : 'refresh-failed';
34
47
  }
35
48
  export async function loginOpenAICodex(storage) {
36
49
  const resolvedStorage = storage ?? (await createPiAuthStorage());
@@ -105,11 +118,15 @@ export async function getAuthStatus({ env = process.env, storage, geminiStore =
105
118
  const openRouterStored = envApiKeys.openrouter
106
119
  ? { value: undefined }
107
120
  : await readStoredOpenRouterApiKey(openRouterStore).catch(() => ({ value: undefined }));
108
- const openAICodexAuthConfigured = await hasOpenAICodexAuth(storage);
121
+ const openAICodexAuthState = await getOpenAICodexAuthState(storage);
109
122
  return {
110
- openai: openAICodexAuthConfigured
111
- ? { configured: true, source: 'pi-auth' }
112
- : { configured: false, source: undefined },
123
+ openai: openAICodexAuthState === 'missing'
124
+ ? { configured: false, source: undefined }
125
+ : {
126
+ configured: true,
127
+ source: 'pi-auth',
128
+ refreshFailed: openAICodexAuthState === 'refresh-failed',
129
+ },
113
130
  gemini: envApiKeys.gemini
114
131
  ? { configured: true, source: 'env' }
115
132
  : {
package/dist/config.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getStoredGeminiApiKey, getStoredOpenRouterApiKey, hasOpenAICodexAuth, PI_AUTH_API_KEY, } from './auth.js';
1
+ import { getOpenAICodexAuthState, getStoredGeminiApiKey, getStoredOpenRouterApiKey, PI_AUTH_API_KEY, } from './auth.js';
2
2
  import { getEnvApiKeys } from './env.js';
3
3
  const REQUIRED_CLI_AUTH_PROVIDERS = [{ provider: 'openai', label: 'OpenAI' }];
4
4
  const ALL_REVIEW_FOCUSES = [
@@ -29,7 +29,7 @@ function createCliGeminiSummaryModel(credential) {
29
29
  export async function getApiKeys(env = process.env, auth = {
30
30
  getStoredGeminiApiKey,
31
31
  getStoredOpenRouterApiKey,
32
- hasOpenAICodexAuth,
32
+ getOpenAICodexAuthState,
33
33
  }) {
34
34
  const envApiKeys = getEnvApiKeys(env);
35
35
  const storedGeminiApiKey = envApiKeys.gemini
@@ -38,7 +38,7 @@ export async function getApiKeys(env = process.env, auth = {
38
38
  const storedOpenRouterApiKey = envApiKeys.openrouter
39
39
  ? undefined
40
40
  : await auth.getStoredOpenRouterApiKey().catch(() => undefined);
41
- const openAICodexAuthConfigured = await auth.hasOpenAICodexAuth();
41
+ const openAICodexAuthState = await auth.getOpenAICodexAuthState();
42
42
  const openRouterApiKey = envApiKeys.openrouter || storedOpenRouterApiKey;
43
43
  const apiKeys = {};
44
44
  const geminiApiKey = envApiKeys.gemini || storedGeminiApiKey;
@@ -49,13 +49,16 @@ export async function getApiKeys(env = process.env, auth = {
49
49
  apiKeys.deepseek = { apiKey: openRouterApiKey, piProviderId: 'openrouter' };
50
50
  apiKeys.zai = { apiKey: openRouterApiKey, piProviderId: 'openrouter' };
51
51
  }
52
- if (openAICodexAuthConfigured) {
52
+ if (openAICodexAuthState === 'ready') {
53
53
  apiKeys.openai = PI_AUTH_API_KEY;
54
54
  }
55
55
  return apiKeys;
56
56
  }
57
57
  export function getMissingRequiredCliAuthProviders(status) {
58
- return REQUIRED_CLI_AUTH_PROVIDERS.filter(({ provider }) => !status[provider].configured).map(({ label }) => label);
58
+ return REQUIRED_CLI_AUTH_PROVIDERS.filter(({ provider }) => {
59
+ const providerStatus = status[provider];
60
+ return !providerStatus.configured || providerStatus.refreshFailed === true;
61
+ }).map(({ label }) => label);
59
62
  }
60
63
  export function getMissingRequiredCliReviewAuthProviders(apiKeys) {
61
64
  return REQUIRED_CLI_AUTH_PROVIDERS.filter(({ provider }) => {
@@ -67,7 +70,7 @@ export function getMissingRequiredCliReviewAuthProviders(apiKeys) {
67
70
  }).map(({ label }) => label);
68
71
  }
69
72
  export function formatMissingRequiredCliAuthProviders(missingProviders) {
70
- return `${missingProviders.join(' and ')} not configured; run \`doistbot auth\``;
73
+ return `${missingProviders.join(' and ')} authentication unavailable (not configured, or the session could not be refreshed); run \`doistbot auth login\``;
71
74
  }
72
75
  export function getCliReviewRouting(apiKeys) {
73
76
  const hasGemini = Boolean(apiKeys.gemini);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/doistbot-cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Local self-review CLI for Doistbot",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -42,8 +42,8 @@
42
42
  "dependencies": {
43
43
  "@inquirer/checkbox": "5.2.1",
44
44
  "@inquirer/password": "5.1.1",
45
- "@earendil-works/pi-ai": "0.79.6",
46
- "@earendil-works/pi-coding-agent": "0.79.6",
45
+ "@earendil-works/pi-ai": "0.80.6",
46
+ "@earendil-works/pi-coding-agent": "0.80.6",
47
47
  "@napi-rs/keyring": "1.3.0",
48
48
  "ajv": "8.20.0",
49
49
  "chalk": "4.1.2",
@@ -13,7 +13,7 @@ export const DEFAULT_PI_MODELS = {
13
13
  deepseek: 'deepseek-v4-pro',
14
14
  gemini: 'gemini-3.1-pro-preview',
15
15
  zai: 'glm5.2',
16
- openai: 'gpt-5.4',
16
+ openai: 'gpt-5.6-terra',
17
17
  };
18
18
  export const DEFAULT_OPENROUTER_MODELS = {
19
19
  deepseek: 'deepseek/deepseek-v4-pro',
@@ -11,8 +11,9 @@ import { withPhase } from '../../core/tracing.js';
11
11
  import { buildPrompt } from './prompt.js';
12
12
  const CHAT_PROMPT_FILE = 'chat-prompt.md';
13
13
  const CHAT_TIMEOUT_MS = 5 * 60 * 1000;
14
- const CHAT_PROVIDER = 'zai';
15
- const CHAT_MODEL = 'z-ai/glm-5.2';
14
+ const CHAT_PROVIDER = 'openai';
15
+ const CHAT_MODEL = 'gpt-5.6-luna';
16
+ const CHAT_THINKING_LEVEL = 'high';
16
17
  function parseContext() {
17
18
  const base = parseBaseContext();
18
19
  const reactionIdRaw = process.env.REACTION_ID;
@@ -125,12 +126,12 @@ async function gatherIssueCommentContext(octokit, owner, repo, prNumber, comment
125
126
  };
126
127
  }
127
128
  export async function invokeChatModel(prompt) {
128
- const apiKey = process.env.OPENROUTER_API_KEY?.trim();
129
+ const apiKey = process.env.OPENAI_KEY?.trim();
129
130
  if (!apiKey) {
130
131
  // Config error: no model call attempted, so no model span. The reason is
131
132
  // returned so the caller's root span reports the real cause.
132
133
  logger.warn('Chat model API key missing');
133
- return { ok: false, reason: 'OPENROUTER_API_KEY missing' };
134
+ return { ok: false, reason: 'OPENAI_KEY missing' };
134
135
  }
135
136
  return withPhase('chat.model', {
136
137
  'gen_ai.operation.name': 'chat',
@@ -140,11 +141,11 @@ export async function invokeChatModel(prompt) {
140
141
  const result = await invokePiPrompt({
141
142
  provider: CHAT_PROVIDER,
142
143
  apiKey,
143
- piProviderId: 'openrouter',
144
144
  prompt,
145
145
  cwd: WORKSPACE_DIR,
146
146
  timeoutMs: CHAT_TIMEOUT_MS,
147
147
  capabilityPreset: 'workspace-inspection',
148
+ thinkingLevel: CHAT_THINKING_LEVEL,
148
149
  // Keep the executed model in sync with the recorded span attr.
149
150
  modelName: CHAT_MODEL,
150
151
  });
@@ -371,7 +372,7 @@ async function main() {
371
372
  await recordTaskMetrics({
372
373
  type: 'chat',
373
374
  repository: `${ctx.owner}/${ctx.repo}`,
374
- model: 'zai',
375
+ model: 'openai',
375
376
  outcome: metricOutcome,
376
377
  durationMs: performance.now() - startedAt,
377
378
  });
@@ -383,6 +383,7 @@ export async function runFixCore(input) {
383
383
  prompt,
384
384
  apiKey: ctx.openaiApiKey,
385
385
  cwd: targetRepo.localPath,
386
+ thinkingLevel: 'xhigh',
386
387
  timeoutMs,
387
388
  extraEnv: {
388
389
  GH_TOKEN: input.ghToken,