opencode-timeout-continuer 1.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.
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +66 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -0
- package/dist/retry-manager.d.ts +34 -0
- package/dist/retry-manager.d.ts.map +1 -0
- package/dist/retry-manager.js +108 -0
- package/dist/retry-manager.js.map +1 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 harshpreet931
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# opencode-timeout-continuer
|
|
2
|
+
|
|
3
|
+
An OpenCode CLI plugin that automatically retries operations when timeout or retryable errors occur, using exponential backoff.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Automatic retry** on timeout and retryable API errors
|
|
8
|
+
- **Exponential backoff** with configurable delays (1s → 2s → 4s → ...)
|
|
9
|
+
- **Per-session tracking** - retry counts reset when session completes
|
|
10
|
+
- **Silent operation** - no UI interruptions
|
|
11
|
+
- **Fully configurable** - customize retry count, delays, and prompts
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### From npm (when published)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g opencode-timeout-continuer
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### From source
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git clone <repo-url>
|
|
25
|
+
cd opencode-timeout-continuer
|
|
26
|
+
npm install
|
|
27
|
+
npm run build
|
|
28
|
+
npm pack
|
|
29
|
+
npm install -g opencode-timeout-continuer-*.tgz
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
Add the plugin to your `opencode.json`:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"plugin": ["opencode-timeout-continuer"],
|
|
39
|
+
"plugin_config": {
|
|
40
|
+
"opencode-timeout-continuer": {
|
|
41
|
+
"enabled": true,
|
|
42
|
+
"maxRetries": 3,
|
|
43
|
+
"baseDelayMs": 1000,
|
|
44
|
+
"maxDelayMs": 30000,
|
|
45
|
+
"prompt": "Continue"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Configuration Options
|
|
52
|
+
|
|
53
|
+
| Option | Type | Default | Description |
|
|
54
|
+
|--------|------|---------|-------------|
|
|
55
|
+
| `enabled` | boolean | `true` | Enable/disable auto-retry |
|
|
56
|
+
| `maxRetries` | number | `3` | Maximum retry attempts per session (1-10) |
|
|
57
|
+
| `baseDelayMs` | number | `1000` | Base delay for exponential backoff in ms (100-60000) |
|
|
58
|
+
| `maxDelayMs` | number | `30000` | Maximum delay cap in ms (1000-300000) |
|
|
59
|
+
| `prompt` | string | `"Continue"` | Prompt text sent on retry |
|
|
60
|
+
|
|
61
|
+
## How It Works
|
|
62
|
+
|
|
63
|
+
1. **Detect**: The plugin listens for `session.error` events from OpenCode
|
|
64
|
+
2. **Analyze**: Checks if the error is retryable:
|
|
65
|
+
- API errors with `isRetryable: true`
|
|
66
|
+
- HTTP status codes: 408, 429, 502, 503, 504
|
|
67
|
+
- Error names containing "timeout" or "ETIMEDOUT"
|
|
68
|
+
3. **Retry**: If retryable and under max retries:
|
|
69
|
+
- Calculates exponential backoff delay
|
|
70
|
+
- Sends a continuation prompt after the delay
|
|
71
|
+
4. **Reset**: When a session goes idle, retry counts are reset
|
|
72
|
+
|
|
73
|
+
### Exponential Backoff
|
|
74
|
+
|
|
75
|
+
The delay between retries grows exponentially:
|
|
76
|
+
|
|
77
|
+
| Attempt | Delay |
|
|
78
|
+
|---------|-------|
|
|
79
|
+
| 1 | 1s |
|
|
80
|
+
| 2 | 2s |
|
|
81
|
+
| 3 | 4s |
|
|
82
|
+
| 4 | 8s (capped at maxDelayMs) |
|
|
83
|
+
|
|
84
|
+
Formula: `delay = min(baseDelayMs * 2^attempt, maxDelayMs)`
|
|
85
|
+
|
|
86
|
+
## Manual Testing
|
|
87
|
+
|
|
88
|
+
To test the plugin:
|
|
89
|
+
|
|
90
|
+
1. Install the plugin globally
|
|
91
|
+
2. Add it to your `opencode.json`
|
|
92
|
+
3. Start OpenCode with a prompt that might timeout (e.g., a complex query)
|
|
93
|
+
4. Watch for log messages in OpenCode's output:
|
|
94
|
+
- `Plugin initialized with maxRetries=3, baseDelayMs=1000`
|
|
95
|
+
- `Retryable error detected for session xxx, scheduling retry 1/3`
|
|
96
|
+
|
|
97
|
+
## Error Types Handled
|
|
98
|
+
|
|
99
|
+
| Error Type | Retryable? |
|
|
100
|
+
|------------|------------|
|
|
101
|
+
| API Timeout | Yes |
|
|
102
|
+
| HTTP 408 (Request Timeout) | Yes |
|
|
103
|
+
| HTTP 429 (Too Many Requests) | Yes |
|
|
104
|
+
| HTTP 502 (Bad Gateway) | Yes |
|
|
105
|
+
| HTTP 503 (Service Unavailable) | Yes |
|
|
106
|
+
| HTTP 504 (Gateway Timeout) | Yes |
|
|
107
|
+
| ProviderAuthError | No |
|
|
108
|
+
| MessageOutputLengthError | No |
|
|
109
|
+
| MessageAbortedError | No |
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AutoContinueConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Default configuration values
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_CONFIG: AutoContinueConfig;
|
|
6
|
+
/**
|
|
7
|
+
* Gets the plugin configuration by merging user config with defaults
|
|
8
|
+
*
|
|
9
|
+
* @param pluginConfig - The plugin_config section from opencode.json
|
|
10
|
+
* @returns Merged and validated configuration
|
|
11
|
+
*/
|
|
12
|
+
export declare function getConfig(pluginConfig?: Record<string, unknown>): AutoContinueConfig;
|
|
13
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,kBAM5B,CAAC;AAsCF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,CAYpF"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_CONFIG = void 0;
|
|
4
|
+
exports.getConfig = getConfig;
|
|
5
|
+
/**
|
|
6
|
+
* Default configuration values
|
|
7
|
+
*/
|
|
8
|
+
exports.DEFAULT_CONFIG = {
|
|
9
|
+
enabled: true,
|
|
10
|
+
maxRetries: 3,
|
|
11
|
+
baseDelayMs: 1000,
|
|
12
|
+
maxDelayMs: 30000,
|
|
13
|
+
prompt: 'Continue',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Validates a single config value and returns default if invalid
|
|
17
|
+
*/
|
|
18
|
+
function validateNumber(value, defaultValue, min, max) {
|
|
19
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
20
|
+
return defaultValue;
|
|
21
|
+
}
|
|
22
|
+
if (min !== undefined && value < min) {
|
|
23
|
+
return defaultValue;
|
|
24
|
+
}
|
|
25
|
+
if (max !== undefined && value > max) {
|
|
26
|
+
return defaultValue;
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validates string config value
|
|
32
|
+
*/
|
|
33
|
+
function validateString(value, defaultValue) {
|
|
34
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
35
|
+
return defaultValue;
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validates boolean config value
|
|
41
|
+
*/
|
|
42
|
+
function validateBoolean(value, defaultValue) {
|
|
43
|
+
if (typeof value !== 'boolean') {
|
|
44
|
+
return defaultValue;
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Gets the plugin configuration by merging user config with defaults
|
|
50
|
+
*
|
|
51
|
+
* @param pluginConfig - The plugin_config section from opencode.json
|
|
52
|
+
* @returns Merged and validated configuration
|
|
53
|
+
*/
|
|
54
|
+
function getConfig(pluginConfig) {
|
|
55
|
+
if (!pluginConfig || typeof pluginConfig !== 'object') {
|
|
56
|
+
return { ...exports.DEFAULT_CONFIG };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
enabled: validateBoolean(pluginConfig.enabled, exports.DEFAULT_CONFIG.enabled),
|
|
60
|
+
maxRetries: validateNumber(pluginConfig.maxRetries, exports.DEFAULT_CONFIG.maxRetries, 1, 10),
|
|
61
|
+
baseDelayMs: validateNumber(pluginConfig.baseDelayMs, exports.DEFAULT_CONFIG.baseDelayMs, 100, 60000),
|
|
62
|
+
maxDelayMs: validateNumber(pluginConfig.maxDelayMs, exports.DEFAULT_CONFIG.maxDelayMs, 1000, 300000),
|
|
63
|
+
prompt: validateString(pluginConfig.prompt, exports.DEFAULT_CONFIG.prompt),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AAuDA,8BAYC;AAjED;;GAEG;AACU,QAAA,cAAc,GAAuB;IAChD,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,IAAI;IACjB,UAAU,EAAE,KAAK;IACjB,MAAM,EAAE,UAAU;CACnB,CAAC;AAEF;;GAEG;AACH,SAAS,cAAc,CAAC,KAAc,EAAE,YAAoB,EAAE,GAAY,EAAE,GAAY;IACtF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,IAAI,GAAG,KAAK,SAAS,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;QACrC,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,IAAI,GAAG,KAAK,SAAS,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;QACrC,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,KAAc,EAAE,YAAoB;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,KAAc,EAAE,YAAqB;IAC5D,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAgB,SAAS,CAAC,YAAsC;IAC9D,IAAI,CAAC,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,EAAE,GAAG,sBAAc,EAAE,CAAC;IAC/B,CAAC;IAED,OAAO;QACL,OAAO,EAAE,eAAe,CAAC,YAAY,CAAC,OAAO,EAAE,sBAAc,CAAC,OAAO,CAAC;QACtE,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,UAAU,EAAE,sBAAc,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QACrF,WAAW,EAAE,cAAc,CAAC,YAAY,CAAC,WAAW,EAAE,sBAAc,CAAC,WAAW,EAAE,GAAG,EAAE,KAAK,CAAC;QAC7F,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,UAAU,EAAE,sBAAc,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC;QAC5F,MAAM,EAAE,cAAc,CAAC,YAAY,CAAC,MAAM,EAAE,sBAAc,CAAC,MAAM,CAAC;KACnE,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OpenCodeEvent } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* OpenCode plugin for automatic retry on timeout errors
|
|
4
|
+
*
|
|
5
|
+
* This plugin automatically retries OpenCode operations when timeout
|
|
6
|
+
* or retryable errors occur, using exponential backoff.
|
|
7
|
+
*
|
|
8
|
+
* Configuration (in opencode.json):
|
|
9
|
+
* ```json
|
|
10
|
+
* {
|
|
11
|
+
* "plugin": ["opencode-timeout-continuer"],
|
|
12
|
+
* "plugin_config": {
|
|
13
|
+
* "opencode-timeout-continuer": {
|
|
14
|
+
* "enabled": true,
|
|
15
|
+
* "maxRetries": 3,
|
|
16
|
+
* "baseDelayMs": 1000,
|
|
17
|
+
* "maxDelayMs": 30000,
|
|
18
|
+
* "prompt": "Continue"
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
type Plugin = (input: {
|
|
25
|
+
client: {
|
|
26
|
+
session: {
|
|
27
|
+
prompt: (params: {
|
|
28
|
+
path: {
|
|
29
|
+
id: string;
|
|
30
|
+
};
|
|
31
|
+
body: {
|
|
32
|
+
parts: Array<{
|
|
33
|
+
type: string;
|
|
34
|
+
text: string;
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
}) => Promise<void>;
|
|
38
|
+
};
|
|
39
|
+
app: {
|
|
40
|
+
log: (params: {
|
|
41
|
+
body: {
|
|
42
|
+
service: string;
|
|
43
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
44
|
+
message: string;
|
|
45
|
+
};
|
|
46
|
+
}) => Promise<void>;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
config?: Record<string, unknown>;
|
|
50
|
+
}) => Promise<{
|
|
51
|
+
event?: (params: {
|
|
52
|
+
event: OpenCodeEvent;
|
|
53
|
+
}) => Promise<void>;
|
|
54
|
+
}>;
|
|
55
|
+
declare const plugin: Plugin;
|
|
56
|
+
export default plugin;
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAA+B,MAAM,YAAY,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,KAAK,MAAM,GAAG,CAAC,KAAK,EAAE;IACpB,MAAM,EAAE;QACN,OAAO,EAAE;YACP,MAAM,EAAE,CAAC,MAAM,EAAE;gBACf,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBACrB,IAAI,EAAE;oBAAE,KAAK,EAAE,KAAK,CAAC;wBAAE,IAAI,EAAE,MAAM,CAAC;wBAAC,IAAI,EAAE,MAAM,CAAA;qBAAE,CAAC,CAAA;iBAAE,CAAC;aACxD,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;SACrB,CAAC;QACF,GAAG,EAAE;YACH,GAAG,EAAE,CAAC,MAAM,EAAE;gBACZ,IAAI,EAAE;oBACJ,OAAO,EAAE,MAAM,CAAC;oBAChB,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;oBAC3C,OAAO,EAAE,MAAM,CAAC;iBACjB,CAAC;aACH,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;SACrB,CAAC;KACH,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,KAAK,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,aAAa,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D,CAAC,CAAC;AAEH,QAAA,MAAM,MAAM,EAAE,MA2Fb,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const retry_manager_js_1 = require("./retry-manager.js");
|
|
4
|
+
const config_js_1 = require("./config.js");
|
|
5
|
+
const plugin = async ({ client, config }) => {
|
|
6
|
+
// Get plugin configuration
|
|
7
|
+
const pluginConfig = config;
|
|
8
|
+
const retryConfig = (0, config_js_1.getConfig)(pluginConfig);
|
|
9
|
+
// Initialize retry manager
|
|
10
|
+
const retryManager = new retry_manager_js_1.RetryManager(retryConfig);
|
|
11
|
+
// Log plugin initialization
|
|
12
|
+
await client.app.log({
|
|
13
|
+
body: {
|
|
14
|
+
service: 'opencode-timeout-continuer',
|
|
15
|
+
level: 'info',
|
|
16
|
+
message: `Plugin initialized with maxRetries=${retryConfig.maxRetries}, baseDelayMs=${retryConfig.baseDelayMs}`,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
event: async ({ event }) => {
|
|
21
|
+
// Handle session.error
|
|
22
|
+
if (event.type === 'session.error') {
|
|
23
|
+
const { sessionID, error } = event.properties;
|
|
24
|
+
if (!sessionID) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Check if we should retry
|
|
28
|
+
if (retryManager.shouldRetry(sessionID, error)) {
|
|
29
|
+
const currentCount = retryManager.getCount(sessionID);
|
|
30
|
+
await client.app.log({
|
|
31
|
+
body: {
|
|
32
|
+
service: 'opencode-timeout-continuer',
|
|
33
|
+
level: 'info',
|
|
34
|
+
message: `Retryable error detected for session ${sessionID}, scheduling retry ${currentCount + 1}/${retryConfig.maxRetries}`,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
// Schedule retry with exponential backoff
|
|
38
|
+
retryManager.scheduleRetry(sessionID, async () => {
|
|
39
|
+
try {
|
|
40
|
+
await client.session.prompt({
|
|
41
|
+
path: { id: sessionID },
|
|
42
|
+
body: {
|
|
43
|
+
parts: [{ type: 'text', text: retryConfig.prompt }],
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
await client.app.log({
|
|
49
|
+
body: {
|
|
50
|
+
service: 'opencode-timeout-continuer',
|
|
51
|
+
level: 'error',
|
|
52
|
+
message: `Failed to send retry prompt: ${err instanceof Error ? err.message : String(err)}`,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}, currentCount);
|
|
57
|
+
}
|
|
58
|
+
else if (error) {
|
|
59
|
+
// Log non-retryable error for debugging
|
|
60
|
+
const errInfo = error;
|
|
61
|
+
await client.app.log({
|
|
62
|
+
body: {
|
|
63
|
+
service: 'opencode-timeout-continuer',
|
|
64
|
+
level: 'debug',
|
|
65
|
+
message: `Non-retryable error for session ${sessionID}: ${errInfo.name || 'unknown'} - ${errInfo.data?.message || 'no message'}`,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Handle session.idle - reset retry state
|
|
71
|
+
if (event.type === 'session.idle') {
|
|
72
|
+
const { sessionID } = event.properties;
|
|
73
|
+
if (sessionID) {
|
|
74
|
+
retryManager.reset(sessionID);
|
|
75
|
+
await client.app.log({
|
|
76
|
+
body: {
|
|
77
|
+
service: 'opencode-timeout-continuer',
|
|
78
|
+
level: 'debug',
|
|
79
|
+
message: `Session ${sessionID} went idle, reset retry state`,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
exports.default = plugin;
|
|
88
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AutoContinueConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Manages retry state and exponential backoff for session errors
|
|
4
|
+
*/
|
|
5
|
+
export declare class RetryManager {
|
|
6
|
+
private sessions;
|
|
7
|
+
private config;
|
|
8
|
+
constructor(config: AutoContinueConfig);
|
|
9
|
+
/**
|
|
10
|
+
* Check if an error is retryable
|
|
11
|
+
*/
|
|
12
|
+
private isRetryableError;
|
|
13
|
+
/**
|
|
14
|
+
* Check if we should retry for this session
|
|
15
|
+
*/
|
|
16
|
+
shouldRetry(sessionId: string, error: unknown): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Get current retry count for a session
|
|
19
|
+
*/
|
|
20
|
+
getCount(sessionId: string): number;
|
|
21
|
+
/**
|
|
22
|
+
* Schedule a retry with exponential backoff
|
|
23
|
+
*/
|
|
24
|
+
scheduleRetry(sessionId: string, callback: () => void | Promise<void>, attempt: number): void;
|
|
25
|
+
/**
|
|
26
|
+
* Reset retry state for a session (called on session.idle)
|
|
27
|
+
*/
|
|
28
|
+
reset(sessionId: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Clear all retry state (cleanup on plugin unload)
|
|
31
|
+
*/
|
|
32
|
+
clearAll(): void;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=retry-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-manager.d.ts","sourceRoot":"","sources":["../src/retry-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,SAAS,CAAC;AAErE;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,MAAM,CAAqB;gBAEvB,MAAM,EAAE,kBAAkB;IAItC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA2BxB;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO;IAmBvD;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAInC;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IA2B7F;;OAEG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAQ9B;;OAEG;IACH,QAAQ,IAAI,IAAI;CAQjB"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RetryManager = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Manages retry state and exponential backoff for session errors
|
|
6
|
+
*/
|
|
7
|
+
class RetryManager {
|
|
8
|
+
sessions = new Map();
|
|
9
|
+
config;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check if an error is retryable
|
|
15
|
+
*/
|
|
16
|
+
isRetryableError(error) {
|
|
17
|
+
if (!error || typeof error !== 'object') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const err = error;
|
|
21
|
+
// Check for ApiError with isRetryable flag
|
|
22
|
+
if (err.name === 'ApiError' && err.data?.isRetryable === true) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
// Check for timeout-related status codes
|
|
26
|
+
const statusCode = err.data?.statusCode;
|
|
27
|
+
if (statusCode && [408, 429, 502, 503, 504].includes(statusCode)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
// Check error name for timeout patterns
|
|
31
|
+
const errorName = err.name?.toLowerCase() || '';
|
|
32
|
+
if (errorName.includes('timeout') || errorName.includes('etimedout')) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if we should retry for this session
|
|
39
|
+
*/
|
|
40
|
+
shouldRetry(sessionId, error) {
|
|
41
|
+
// Check if plugin is enabled
|
|
42
|
+
if (!this.config.enabled) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// Check if error is retryable
|
|
46
|
+
if (!this.isRetryableError(error)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
// Get or create session state
|
|
50
|
+
const state = this.sessions.get(sessionId);
|
|
51
|
+
const currentCount = state?.count ?? 0;
|
|
52
|
+
// Check retry limit
|
|
53
|
+
return currentCount < this.config.maxRetries;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get current retry count for a session
|
|
57
|
+
*/
|
|
58
|
+
getCount(sessionId) {
|
|
59
|
+
return this.sessions.get(sessionId)?.count ?? 0;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Schedule a retry with exponential backoff
|
|
63
|
+
*/
|
|
64
|
+
scheduleRetry(sessionId, callback, attempt) {
|
|
65
|
+
// Calculate delay with exponential backoff
|
|
66
|
+
const delay = Math.min(this.config.baseDelayMs * Math.pow(2, attempt), this.config.maxDelayMs);
|
|
67
|
+
// Increment retry count
|
|
68
|
+
const currentState = this.sessions.get(sessionId);
|
|
69
|
+
const newState = {
|
|
70
|
+
count: (currentState?.count ?? 0) + 1,
|
|
71
|
+
lastError: new Date(),
|
|
72
|
+
};
|
|
73
|
+
// Set up timeout
|
|
74
|
+
const timeoutId = setTimeout(async () => {
|
|
75
|
+
try {
|
|
76
|
+
await callback();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Callback errors are handled by the event system
|
|
80
|
+
}
|
|
81
|
+
}, delay);
|
|
82
|
+
newState.timeoutId = timeoutId;
|
|
83
|
+
this.sessions.set(sessionId, newState);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Reset retry state for a session (called on session.idle)
|
|
87
|
+
*/
|
|
88
|
+
reset(sessionId) {
|
|
89
|
+
const state = this.sessions.get(sessionId);
|
|
90
|
+
if (state?.timeoutId) {
|
|
91
|
+
clearTimeout(state.timeoutId);
|
|
92
|
+
}
|
|
93
|
+
this.sessions.delete(sessionId);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Clear all retry state (cleanup on plugin unload)
|
|
97
|
+
*/
|
|
98
|
+
clearAll() {
|
|
99
|
+
for (const state of this.sessions.values()) {
|
|
100
|
+
if (state.timeoutId) {
|
|
101
|
+
clearTimeout(state.timeoutId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
this.sessions.clear();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.RetryManager = RetryManager;
|
|
108
|
+
//# sourceMappingURL=retry-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-manager.js","sourceRoot":"","sources":["../src/retry-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the auto-continue plugin
|
|
3
|
+
*/
|
|
4
|
+
export interface AutoContinueConfig {
|
|
5
|
+
/** Enable/disable auto-continue functionality */
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
/** Maximum number of retry attempts */
|
|
8
|
+
maxRetries: number;
|
|
9
|
+
/** Base delay in milliseconds for exponential backoff */
|
|
10
|
+
baseDelayMs: number;
|
|
11
|
+
/** Maximum delay cap in milliseconds */
|
|
12
|
+
maxDelayMs: number;
|
|
13
|
+
/** Prompt text to send when retrying */
|
|
14
|
+
prompt: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tracks retry state for a specific session
|
|
18
|
+
*/
|
|
19
|
+
export interface SessionRetryState {
|
|
20
|
+
/** Current retry count */
|
|
21
|
+
count: number;
|
|
22
|
+
/** Timestamp of last error */
|
|
23
|
+
lastError: Date;
|
|
24
|
+
/** Pending timeout ID for cleanup */
|
|
25
|
+
timeoutId?: ReturnType<typeof setTimeout>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* API error structure from OpenCode
|
|
29
|
+
*/
|
|
30
|
+
export interface ApiError {
|
|
31
|
+
name: 'ApiError';
|
|
32
|
+
data: {
|
|
33
|
+
message: string;
|
|
34
|
+
statusCode?: number;
|
|
35
|
+
isRetryable: boolean;
|
|
36
|
+
responseHeaders?: Record<string, string>;
|
|
37
|
+
responseBody?: string;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Other error types that should NOT be retried
|
|
42
|
+
*/
|
|
43
|
+
export type NonRetryableError = {
|
|
44
|
+
name: 'ProviderAuthError';
|
|
45
|
+
} | {
|
|
46
|
+
name: 'MessageOutputLengthError';
|
|
47
|
+
} | {
|
|
48
|
+
name: 'MessageAbortedError';
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Event properties for session.error
|
|
52
|
+
*/
|
|
53
|
+
export interface SessionErrorEventProperties {
|
|
54
|
+
sessionID?: string;
|
|
55
|
+
error?: ApiError | NonRetryableError | Error;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Event properties for session.idle
|
|
59
|
+
*/
|
|
60
|
+
export interface SessionIdleEventProperties {
|
|
61
|
+
sessionID: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* OpenCode event types that the plugin handles
|
|
65
|
+
*/
|
|
66
|
+
export type OpenCodeEvent = {
|
|
67
|
+
type: 'session.error';
|
|
68
|
+
properties: SessionErrorEventProperties;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'session.idle';
|
|
71
|
+
properties: SessionIdleEventProperties;
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,SAAS,EAAE,IAAI,CAAC;IAChB,qCAAqC;IACrC,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,OAAO,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzC,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,0BAA0B,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAEpC;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,QAAQ,GAAG,iBAAiB,GAAG,KAAK,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,2BAA2B,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,UAAU,EAAE,0BAA0B,CAAA;CAAE,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-timeout-continuer",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OpenCode CLI plugin for automatic retry on timeout errors with exponential backoff",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"opencode",
|
|
19
|
+
"plugin",
|
|
20
|
+
"timeout",
|
|
21
|
+
"retry",
|
|
22
|
+
"exponential-backoff"
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/harshpreet931/opencode-timeout-continuer.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/harshpreet931/opencode-timeout-continuer/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/harshpreet931/opencode-timeout-continuer#readme",
|
|
32
|
+
"author": "harshpreet931",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"typescript": "^5.3.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|