jeopi-cli 16.2.19 → 16.2.21

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.
@@ -16,6 +16,10 @@ export declare const NPM_PACKAGE = "jeopi-cli";
16
16
  * See #1686.
17
17
  */
18
18
  export declare const NPM_REGISTRY = "https://registry.npmjs.org/";
19
+ interface ReleaseInfo {
20
+ tag: string;
21
+ version: string;
22
+ }
19
23
  /** Result from running the installed binary and parsing its reported version. */
20
24
  export interface InstalledVersionVerification {
21
25
  ok: boolean;
@@ -45,6 +49,14 @@ interface UpdateMethodResolutionOptions {
45
49
  miseDataDir?: string;
46
50
  }
47
51
  export declare function resolveUpdateMethodForTest(jeopiPath: string, bunBinDir: string | undefined, options?: UpdateMethodResolutionOptions): UpdateMethod;
52
+ /**
53
+ * Get the latest release info from the npm registry.
54
+ * Uses npm instead of GitHub API to avoid unauthenticated rate limiting.
55
+ *
56
+ * Exported for tests: the error contract below is what a user sees when the
57
+ * update check dies, and it must stay diagnosable.
58
+ */
59
+ export declare function getLatestRelease(): Promise<ReleaseInfo>;
48
60
  /**
49
61
  * Resolve the latest published {@link NPM_PACKAGE} version when it is newer
50
62
  * than `currentVersion`, else `undefined`. Backs the startup update notice
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "jeopi-cli",
4
- "version": "16.2.19",
4
+ "version": "16.2.21",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/akillness/jeopi",
7
7
  "author": "Can Boluk",
@@ -56,17 +56,17 @@
56
56
  "@agentclientprotocol/sdk": "0.25.0",
57
57
  "@babel/parser": "^7.29.7",
58
58
  "@mozilla/readability": "^0.6.0",
59
- "jeopi-hashline": "16.2.19",
60
- "jeopi-stats": "16.2.19",
61
- "jeopi-agent-core": "16.2.19",
62
- "jeopi-ai": "16.2.19",
63
- "jeopi-catalog": "16.2.19",
64
- "jeopi-mnemopi": "16.2.19",
65
- "jeopi-natives": "16.2.19",
66
- "jeopi-tui": "16.2.19",
67
- "jeopi-utils": "16.2.19",
68
- "jeopi-wire": "16.2.19",
69
- "jeopi-snapcompact": "16.2.19",
59
+ "jeopi-hashline": "16.2.21",
60
+ "jeopi-stats": "16.2.21",
61
+ "jeopi-agent-core": "16.2.21",
62
+ "jeopi-ai": "16.2.21",
63
+ "jeopi-catalog": "16.2.21",
64
+ "jeopi-mnemopi": "16.2.21",
65
+ "jeopi-natives": "16.2.21",
66
+ "jeopi-tui": "16.2.21",
67
+ "jeopi-utils": "16.2.21",
68
+ "jeopi-wire": "16.2.21",
69
+ "jeopi-snapcompact": "16.2.21",
70
70
  "@opentelemetry/api": "^1.9.1",
71
71
  "@opentelemetry/context-async-hooks": "^2.7.1",
72
72
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
@@ -244,11 +244,29 @@ async function resolveUpdateTarget(): Promise<UpdateTarget> {
244
244
  /**
245
245
  * Get the latest release info from the npm registry.
246
246
  * Uses npm instead of GitHub API to avoid unauthenticated rate limiting.
247
+ *
248
+ * Exported for tests: the error contract below is what a user sees when the
249
+ * update check dies, and it must stay diagnosable.
247
250
  */
248
- async function getLatestRelease(): Promise<ReleaseInfo> {
249
- const response = await fetch(`${NPM_REGISTRY}${NPM_PACKAGE}/latest`);
251
+ export async function getLatestRelease(): Promise<ReleaseInfo> {
252
+ const url = `${NPM_REGISTRY}${NPM_PACKAGE}/latest`;
253
+ const response = await fetch(url);
254
+ if (response.status === 404) {
255
+ // A 404 here means the package this build was compiled to track does not
256
+ // exist on the registry (e.g. the CLI was renamed after this build
257
+ // shipped — `jeopi` → `jeopi-cli`). Self-update can never recover from
258
+ // this, so name the phantom package and the manual escape hatch instead
259
+ // of surfacing a bare "Not Found".
260
+ throw new Error(
261
+ `Package "${NPM_PACKAGE}" was not found on ${NPM_REGISTRY} (HTTP 404). ` +
262
+ `This build's update check targets a package that is not published; ` +
263
+ `reinstall manually: bun install -g ${NPM_PACKAGE}`,
264
+ );
265
+ }
250
266
  if (!response.ok) {
251
- throw new Error(`Failed to fetch release info: ${response.statusText}`);
267
+ throw new Error(
268
+ `Failed to fetch release info for ${NPM_PACKAGE} from ${NPM_REGISTRY}: HTTP ${response.status} ${response.statusText}`,
269
+ );
252
270
  }
253
271
 
254
272
  const data = (await response.json()) as { version: string };
@@ -1227,7 +1227,12 @@ export class EventController {
1227
1227
  this.ctx.statusContainer.clear();
1228
1228
  }
1229
1229
  if (!event.success) {
1230
- this.ctx.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
1230
+ const finalError = event.finalError || "Unknown error";
1231
+ this.ctx.showError(
1232
+ finalError.startsWith("Quota exhausted.")
1233
+ ? finalError
1234
+ : `Retry failed after ${event.attempt} attempts: ${finalError}`,
1235
+ );
1231
1236
  }
1232
1237
  this.#ensureWorkingLoaderWhileStreaming();
1233
1238
  this.ctx.ui.requestRender();
@@ -12998,6 +12998,12 @@ export class AgentSession {
12998
12998
  // Smart Fallback if no exact headers found
12999
12999
  return undefined;
13000
13000
  }
13001
+ #formatRetryDelayCapError(errorMessage: string, delayMs: number, maxDelayMs: number, usageLimit: boolean): string {
13002
+ if (usageLimit) {
13003
+ return `Quota exhausted. Provider requested ${delayMs}ms wait (${formatDuration(delayMs)}), exceeds retry.maxDelayMs (${maxDelayMs}ms), so jeopi did not sleep. Original error: ${errorMessage}`;
13004
+ }
13005
+ return `Provider requested ${delayMs}ms wait, exceeds retry.maxDelayMs (${maxDelayMs}ms). Original error: ${errorMessage}`;
13006
+ }
13001
13007
 
13002
13008
  /**
13003
13009
  * Handle retryable errors with exponential backoff.
@@ -13174,13 +13180,14 @@ export class AgentSession {
13174
13180
  const maxDelayMs = retrySettings.maxDelayMs;
13175
13181
  if (maxDelayMs > 0 && delayMs > maxDelayMs && !switchedCredential && !switchedModel && !classifierRefusal) {
13176
13182
  const attempt = this.#retryAttempt;
13183
+ const usageLimit = AIError.is(id, AIError.Flag.UsageLimit);
13177
13184
  this.#retryAttempt = 0;
13178
13185
  this.#refusalBackoff = undefined;
13179
13186
  await this.#emitSessionEvent({
13180
13187
  type: "auto_retry_end",
13181
13188
  success: false,
13182
13189
  attempt,
13183
- finalError: `Provider requested ${delayMs}ms wait, exceeds retry.maxDelayMs (${maxDelayMs}ms). Original error: ${errorMessage}`,
13190
+ finalError: this.#formatRetryDelayCapError(errorMessage, delayMs, maxDelayMs, usageLimit),
13184
13191
  });
13185
13192
  this.#resolveRetry();
13186
13193
  return false;