@leo000001/opencode-quota-sidebar 4.0.4 → 4.0.9

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/dist/cli.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
2
3
  import { type HistoryPeriod } from './period.js';
3
4
  type CliCommand = {
4
5
  period: HistoryPeriod;
@@ -10,9 +11,15 @@ type CliServerCommand = {
10
11
  args: string[];
11
12
  shell?: boolean;
12
13
  };
14
+ type SpawnedCliServerProcess = ReturnType<typeof spawn>;
13
15
  export declare function parseCliArgs(argv: string[]): CliCommand;
14
16
  export declare function cliBaseUrl(): string;
15
17
  export declare function cliServerCommandCandidates(platform?: NodeJS.Platform): CliServerCommand[];
18
+ export declare function closeCliServerProcess(proc: SpawnedCliServerProcess, platform?: NodeJS.Platform, killProcess?: typeof process.kill, spawnProcess?: typeof spawn): void;
19
+ export declare function tryStartCliOpencodeServer(candidate: CliServerCommand, spawnProcess?: typeof spawn, closeProcess?: typeof closeCliServerProcess): Promise<{
20
+ url: string;
21
+ close: () => void;
22
+ }>;
16
23
  export declare function runCli(argv: string[]): Promise<string>;
17
24
  export declare function cliExitCodeForError(message: string): 0 | 1;
18
25
  export declare function cliShouldRunMain(argv1?: string, modulePath?: string, resolvePath?: (filePath: string) => string): boolean;
package/dist/cli.js CHANGED
@@ -132,12 +132,48 @@ export function cliServerCommandCandidates(platform = process.platform) {
132
132
  }
133
133
  return [{ command: 'opencode', args: directArgs }];
134
134
  }
135
- async function tryStartCliOpencodeServer(candidate) {
135
+ function releaseCliServerPipes(proc, inspect, onError, onExit) {
136
+ if (inspect) {
137
+ proc.stdout?.removeListener('data', inspect);
138
+ proc.stderr?.removeListener('data', inspect);
139
+ }
140
+ if (onError)
141
+ proc.removeListener('error', onError);
142
+ if (onExit)
143
+ proc.removeListener('exit', onExit);
144
+ proc.stdout?.unpipe();
145
+ proc.stderr?.unpipe();
146
+ proc.stdout?.destroy();
147
+ proc.stderr?.destroy();
148
+ }
149
+ export function closeCliServerProcess(proc, platform = process.platform, killProcess = process.kill, spawnProcess = spawn) {
150
+ const pid = proc.pid;
151
+ if (typeof pid !== 'number' || pid <= 0)
152
+ return;
153
+ if (platform === 'win32') {
154
+ const killer = spawnProcess('taskkill', ['/PID', String(pid), '/T', '/F'], {
155
+ stdio: 'ignore',
156
+ windowsHide: true,
157
+ });
158
+ killer.unref();
159
+ return;
160
+ }
161
+ try {
162
+ killProcess(-pid, 'SIGTERM');
163
+ }
164
+ catch (error) {
165
+ const code = error.code;
166
+ if (code !== 'ESRCH')
167
+ throw error;
168
+ }
169
+ }
170
+ export async function tryStartCliOpencodeServer(candidate, spawnProcess = spawn, closeProcess = closeCliServerProcess) {
136
171
  let proc;
137
172
  try {
138
- proc = spawn(candidate.command, candidate.args, {
173
+ proc = spawnProcess(candidate.command, candidate.args, {
139
174
  env: process.env,
140
175
  shell: candidate.shell ?? false,
176
+ detached: true,
141
177
  windowsHide: true,
142
178
  });
143
179
  }
@@ -150,12 +186,20 @@ async function tryStartCliOpencodeServer(candidate) {
150
186
  };
151
187
  }
152
188
  const url = await new Promise((resolve, reject) => {
189
+ let inspect;
190
+ let onError;
191
+ let onExit;
153
192
  const id = setTimeout(() => {
193
+ if (settled)
194
+ return;
195
+ settled = true;
196
+ releaseCliServerPipes(proc, inspect, onError, onExit);
197
+ closeProcess(proc);
154
198
  reject(new Error(`Timeout waiting for OpenCode server to start after ${CLI_SERVER_TIMEOUT_MS}ms`));
155
199
  }, CLI_SERVER_TIMEOUT_MS);
156
200
  let output = '';
157
201
  let settled = false;
158
- const inspect = (chunk) => {
202
+ inspect = (chunk) => {
159
203
  output += chunk.toString();
160
204
  const lines = output.split('\n');
161
205
  for (const line of lines) {
@@ -166,36 +210,48 @@ async function tryStartCliOpencodeServer(candidate) {
166
210
  continue;
167
211
  clearTimeout(id);
168
212
  settled = true;
213
+ releaseCliServerPipes(proc, inspect, onError, onExit);
214
+ // The CLI only needs the startup line; after that the detached server
215
+ // must not keep the parent process alive.
216
+ proc.unref();
169
217
  resolve(match[1]);
170
218
  return;
171
219
  }
172
220
  };
173
221
  proc.stdout?.on('data', inspect);
174
222
  proc.stderr?.on('data', inspect);
175
- proc.on('error', (error) => {
223
+ onError = (error) => {
224
+ if (settled)
225
+ return;
226
+ settled = true;
176
227
  clearTimeout(id);
228
+ releaseCliServerPipes(proc, inspect, onError, onExit);
177
229
  const code = error.code;
178
230
  reject({
179
231
  error,
180
232
  output,
181
233
  recoverable: code === 'ENOENT' || code === 'EINVAL',
182
234
  });
183
- });
184
- proc.on('exit', (code) => {
235
+ };
236
+ onExit = (code) => {
185
237
  if (settled)
186
238
  return;
239
+ settled = true;
187
240
  clearTimeout(id);
241
+ releaseCliServerPipes(proc, inspect, onError, onExit);
188
242
  let message = `OpenCode server exited with code ${code}`;
189
243
  if (output.trim())
190
244
  message += `\n${output}`;
191
245
  const recoverable = /not recognized as an internal or external command/i.test(output) ||
192
246
  /command not found/i.test(output);
193
247
  reject({ error: new Error(message), output, recoverable });
194
- });
248
+ };
249
+ proc.on('error', onError);
250
+ proc.on('exit', onExit);
195
251
  });
196
252
  return {
197
253
  url,
198
- close: () => proc.kill(),
254
+ close: () => closeProcess(proc),
199
255
  };
200
256
  }
201
257
  async function startCliOpencodeServer() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leo000001/opencode-quota-sidebar",
3
- "version": "4.0.4",
3
+ "version": "4.0.9",
4
4
  "description": "OpenCode plugin that shows quota and token usage in TUI sidebar panels and compact session titles",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",