@timlaing/opencode-tunnel-switch 1.4.3 → 1.6.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/README.md +7 -5
- package/package.json +3 -3
- package/src/tui.ts +22 -9
package/README.md
CHANGED
|
@@ -7,9 +7,11 @@ The external [ssh-tunnel-switch](https://github.com/timlaing/ssh-tunnel-switch)
|
|
|
7
7
|
After a successful tunnel switch, the plugin requests that OpenCode retry the current model request after one second, continuing the existing work automatically. Status display runs independently so a slow or failed notification cannot delay continuation. If a switch returns 429 (for example, another switch is already in progress), only the current work request is retried after `Retry-After`; the switch is not retried in that retry sequence. A local cooldown similarly delays the work request until it expires. Other switch failures stop the retry, and OpenCode's retry limit still applies.
|
|
8
8
|
|
|
9
9
|
The OpenCode session sidebar also includes a **Tunnel** section with connection
|
|
10
|
-
state, server,
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
state, server, start time, uptime, proxy address and API availability. While
|
|
11
|
+
collapsed, the header still names the tunnel and its reachability, for example
|
|
12
|
+
`▶ Tunnel (fra1 · reachable)`. It refreshes every five seconds;
|
|
13
|
+
`/tunnel-refresh` refreshes immediately. Failed checks mark previous details
|
|
14
|
+
stale. Reload OpenCode after updating the plugin.
|
|
13
15
|
The TUI uses the same API options from the terminal machine, so remote-server
|
|
14
16
|
setups need an API address reachable from that machine.
|
|
15
17
|
|
|
@@ -69,7 +71,7 @@ Keep registry credentials in your npm user configuration, not the repository. Co
|
|
|
69
71
|
"$schema": "https://opencode.ai/config.json",
|
|
70
72
|
"plugin": [
|
|
71
73
|
[
|
|
72
|
-
"@timlaing/opencode-tunnel-switch@1.
|
|
74
|
+
"@timlaing/opencode-tunnel-switch@1.5.0",
|
|
73
75
|
{
|
|
74
76
|
"apiIp": "127.0.0.1",
|
|
75
77
|
"apiPort": 8765,
|
|
@@ -85,7 +87,7 @@ The REST token is separate from GitHub registry credentials. Proxy IP/port are d
|
|
|
85
87
|
## Serializer dependency
|
|
86
88
|
|
|
87
89
|
OpenTUI currently requires a Solid version that constrains Seroval to a vulnerable
|
|
88
|
-
release range. This checkout overrides Seroval to
|
|
90
|
+
release range. This checkout overrides Seroval to `~1.6.11` and audits that resolved
|
|
89
91
|
tree. npm applies overrides only from the installing root project: applications
|
|
90
92
|
installing this package must also enforce a patched Seroval version in their root
|
|
91
93
|
dependency configuration. This package's override does not patch OpenCode's own
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timlaing/opencode-tunnel-switch",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"verify:published": "node scripts/verify-published-package.mjs"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@opencode/plugin": "2.
|
|
19
|
+
"@opencode/plugin": "2.1.4",
|
|
20
20
|
"zod": "^4.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
@@ -49,6 +49,6 @@
|
|
|
49
49
|
"solid-js": "^1.9.12"
|
|
50
50
|
},
|
|
51
51
|
"overrides": {
|
|
52
|
-
"seroval": "
|
|
52
|
+
"seroval": "~1.6.11"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/tui.ts
CHANGED
|
@@ -100,11 +100,13 @@ export default Plugin.define({
|
|
|
100
100
|
const positive = themed(["text", "feedback", "success", "base"], "#a6e3a1");
|
|
101
101
|
const warning = themed(["text", "feedback", "warning", "base"], "#f9e2af");
|
|
102
102
|
const negative = themed(["text", "feedback", "error", "base"], "#f38ba8");
|
|
103
|
-
// A row:
|
|
104
|
-
//
|
|
103
|
+
// A row: bold title in the theme's base colour, two-space indented and
|
|
104
|
+
// without a colon; value flush right (muted or state-coloured), matching
|
|
105
|
+
// the sibling sidebars' label/value layout.
|
|
105
106
|
const line = (label: string, value: string | (() => string), colour?: string, suffix?: string) =>
|
|
106
107
|
jsx("box", { flexDirection: "row", gap: 1, minWidth: 0, get children() { return [
|
|
107
|
-
jsx("text", { fg: base, wrapMode: "none", truncate: true, flexGrow: 1, flexShrink: 1, minWidth: 0,
|
|
108
|
+
jsx("text", { fg: base, wrapMode: "none", truncate: true, flexGrow: 1, flexShrink: 1, minWidth: 0,
|
|
109
|
+
get children() { return jsx("b", { children: ` ${label}` }); } }),
|
|
108
110
|
jsx("text", { wrapMode: "none", truncate: true, flexShrink: 0, get children() {
|
|
109
111
|
const children = [jsx("span", {
|
|
110
112
|
style: { fg: colour ?? muted },
|
|
@@ -114,10 +116,20 @@ export default Plugin.define({
|
|
|
114
116
|
return children;
|
|
115
117
|
} }),
|
|
116
118
|
]; } });
|
|
117
|
-
// Muted summary shown beside "Tunnel" while the sidebar is collapsed
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
// Muted summary shown beside "Tunnel" while the sidebar is collapsed,
|
|
120
|
+
// naming the tunnel (its configured server) once one has been reported.
|
|
121
|
+
// The name is capped so a long hostname cannot push the reachability
|
|
122
|
+
// state out of the collapsed header.
|
|
123
|
+
/** Keep a server name within 20 columns, elided with an ellipsis. */
|
|
124
|
+
const clipName = (name: string): string => (name.length <= 20 ? name : `${name.slice(0, 19)}…`);
|
|
125
|
+
const collapsedSummary = (name: string | null | undefined, checked: number | undefined,
|
|
126
|
+
available: boolean): string => {
|
|
127
|
+
let reach: string;
|
|
128
|
+
if (!checked) reach = "checking…";
|
|
129
|
+
else if (available) reach = "reachable";
|
|
130
|
+
else reach = "unavailable";
|
|
131
|
+
if (!name) return ` (${reach})`;
|
|
132
|
+
return ` (${clipName(name)} · ${reach})`;
|
|
121
133
|
};
|
|
122
134
|
// Feedback colour for a tunnel state.
|
|
123
135
|
const stateColour = (state: TunnelStatus["state"]): string => {
|
|
@@ -153,7 +165,7 @@ export default Plugin.define({
|
|
|
153
165
|
jsx("text", { fg: base, get children() {
|
|
154
166
|
const children = [jsx("b", { children: "Tunnel" })];
|
|
155
167
|
if (!open) children.push(jsx("span", { style: { fg: muted },
|
|
156
|
-
children: collapsedSummary(checked, available) }));
|
|
168
|
+
children: collapsedSummary(status?.server, checked, available) }));
|
|
157
169
|
return children;
|
|
158
170
|
} }),
|
|
159
171
|
]; } });
|
|
@@ -172,7 +184,8 @@ export default Plugin.define({
|
|
|
172
184
|
line("Proxy", status.proxy),
|
|
173
185
|
);
|
|
174
186
|
}
|
|
175
|
-
|
|
187
|
+
// The relative age stays muted (the default value colour) between polls.
|
|
188
|
+
lines.push(line("Checked", () => formatAge(checked, clock()), muted));
|
|
176
189
|
return [header, ...lines];
|
|
177
190
|
} });
|
|
178
191
|
} catch {
|