gitterm 0.0.1 → 0.0.3

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.
Files changed (2) hide show
  1. package/dist/index.js +155 -119
  2. package/package.json +2 -3
package/dist/index.js CHANGED
@@ -10034,7 +10034,7 @@ async function runTunnel(args) {
10034
10034
  `);
10035
10035
  }
10036
10036
  if (!args.port) {
10037
- const portInput = await prompt("Enter the local port to expose (e.g. 4096): ");
10037
+ const portInput = await prompt("Enter the port your Opencode server is running on (e.g. 4096): ");
10038
10038
  const parsedPort = Number.parseInt(portInput, 10);
10039
10039
  if (!Number.isFinite(parsedPort) || parsedPort <= 0) {
10040
10040
  throw new Error("Invalid port number");
@@ -10065,25 +10065,122 @@ async function runTunnel(args) {
10065
10065
  primaryPort = args.port;
10066
10066
  }
10067
10067
  const exposedPorts = parseExposeArgs(args.expose);
10068
- const pendingRequestBodies = new Map;
10069
- const pendingRequestMeta = new Map;
10068
+ const pendingRequests = new Map;
10070
10069
  const activeRequests = new Map;
10071
- function mergeBody(id) {
10072
- const parts = pendingRequestBodies.get(id) ?? [];
10073
- pendingRequestBodies.delete(id);
10074
- if (parts.length === 0)
10075
- return new Uint8Array;
10076
- const total = parts.reduce((sum, p) => sum + p.byteLength, 0);
10077
- const merged = new Uint8Array(total);
10078
- let off = 0;
10079
- for (const p of parts) {
10080
- merged.set(p, off);
10081
- off += p.byteLength;
10082
- }
10083
- return merged;
10070
+ function createBodyStream(pending) {
10071
+ return new ReadableStream({
10072
+ start: (controller) => {
10073
+ pending.bodyController = controller;
10074
+ if (pending.bufferedChunks.length > 0) {
10075
+ for (const chunk of pending.bufferedChunks) {
10076
+ controller.enqueue(chunk);
10077
+ }
10078
+ pending.bufferedChunks = [];
10079
+ }
10080
+ if (pending.bodyClosed)
10081
+ controller.close();
10082
+ },
10083
+ cancel: () => {
10084
+ pending.abortController.abort();
10085
+ }
10086
+ });
10084
10087
  }
10085
10088
  const effectiveWsUrl = args.wsUrl ?? connectCfg?.wsUrl ?? DEFAULT_WS_URL;
10086
10089
  const ws = new WebSocket(effectiveWsUrl);
10090
+ async function startUpstreamRequest(id, pending) {
10091
+ if (pending.started)
10092
+ return;
10093
+ pending.started = true;
10094
+ const meta = pending.meta;
10095
+ const abortController = pending.abortController;
10096
+ activeRequests.set(id, abortController);
10097
+ try {
10098
+ const base = new URL(targetBase.replace(/\/$/, "") + "/");
10099
+ base.hostname = "localhost";
10100
+ base.port = String(meta.port ?? primaryPort);
10101
+ const url = new URL(meta.path.replace(/^\//, ""), base);
10102
+ const headers = new Headers(meta.headers);
10103
+ headers.delete("host");
10104
+ headers.delete("content-length");
10105
+ headers.delete("connection");
10106
+ headers.delete("keep-alive");
10107
+ headers.delete("proxy-authenticate");
10108
+ headers.delete("proxy-authorization");
10109
+ headers.delete("te");
10110
+ headers.delete("trailers");
10111
+ headers.delete("transfer-encoding");
10112
+ headers.delete("upgrade");
10113
+ const upstream = await fetch(url, {
10114
+ method: meta.method,
10115
+ headers,
10116
+ body: pending.ignoreBody ? undefined : pending.bodyStream ?? undefined,
10117
+ redirect: "manual",
10118
+ signal: abortController.signal
10119
+ });
10120
+ ws.send(JSON.stringify({
10121
+ type: "response",
10122
+ id,
10123
+ statusCode: upstream.status,
10124
+ headers: headersToRecord(upstream.headers),
10125
+ timestamp: Date.now()
10126
+ }));
10127
+ if (!upstream.body) {
10128
+ activeRequests.delete(id);
10129
+ ws.send(JSON.stringify({
10130
+ type: "data",
10131
+ id,
10132
+ final: true,
10133
+ timestamp: Date.now()
10134
+ }));
10135
+ return;
10136
+ }
10137
+ const reader = upstream.body.getReader();
10138
+ try {
10139
+ while (true) {
10140
+ const { done, value } = await reader.read();
10141
+ if (done)
10142
+ break;
10143
+ if (!value)
10144
+ continue;
10145
+ ws.send(JSON.stringify({
10146
+ type: "data",
10147
+ id,
10148
+ data: bytesToBase64(value),
10149
+ final: false,
10150
+ timestamp: Date.now()
10151
+ }));
10152
+ }
10153
+ } finally {
10154
+ reader.releaseLock();
10155
+ }
10156
+ activeRequests.delete(id);
10157
+ ws.send(JSON.stringify({
10158
+ type: "data",
10159
+ id,
10160
+ final: true,
10161
+ timestamp: Date.now()
10162
+ }));
10163
+ } catch (error) {
10164
+ activeRequests.delete(id);
10165
+ if (error instanceof Error && error.name === "AbortError") {
10166
+ return;
10167
+ }
10168
+ ws.send(JSON.stringify({
10169
+ type: "response",
10170
+ id,
10171
+ statusCode: 502,
10172
+ headers: { "content-type": "application/json" },
10173
+ timestamp: Date.now()
10174
+ }));
10175
+ ws.send(JSON.stringify({
10176
+ type: "data",
10177
+ id,
10178
+ data: bytesToBase64(new TextEncoder().encode(JSON.stringify({ error: "upstream_error" }))),
10179
+ final: true,
10180
+ timestamp: Date.now()
10181
+ }));
10182
+ }
10183
+ }
10087
10184
  ws.addEventListener("open", () => {
10088
10185
  console.log("Establishing secure tunnel for workspace...");
10089
10186
  ws.send(JSON.stringify({
@@ -10146,123 +10243,63 @@ Press Ctrl+C to disconnect.
10146
10243
  controller.abort();
10147
10244
  activeRequests.delete(frame.id);
10148
10245
  }
10149
- pendingRequestBodies.delete(frame.id);
10150
- pendingRequestMeta.delete(frame.id);
10246
+ const pending = pendingRequests.get(frame.id);
10247
+ if (pending?.bodyController) {
10248
+ try {
10249
+ pending.bodyController.close();
10250
+ } catch {}
10251
+ }
10252
+ pendingRequests.delete(frame.id);
10151
10253
  return;
10152
10254
  }
10153
10255
  if (frame.type === "request") {
10154
- pendingRequestBodies.set(frame.id, []);
10155
- pendingRequestMeta.set(frame.id, {
10256
+ const meta = {
10156
10257
  method: (frame.method ?? "GET").toUpperCase(),
10157
10258
  path: frame.path ?? "/",
10158
10259
  headers: frame.headers ?? {},
10159
10260
  port: frame.port
10160
- });
10261
+ };
10262
+ const ignoreBody = meta.method === "GET" || meta.method === "HEAD";
10263
+ const pending = {
10264
+ meta,
10265
+ bodyStream: null,
10266
+ bufferedChunks: [],
10267
+ bodyClosed: false,
10268
+ started: false,
10269
+ abortController: new AbortController,
10270
+ ignoreBody
10271
+ };
10272
+ pending.bodyStream = ignoreBody ? null : createBodyStream(pending);
10273
+ pendingRequests.set(frame.id, pending);
10274
+ startUpstreamRequest(frame.id, pending);
10161
10275
  return;
10162
10276
  }
10163
10277
  if (frame.type === "data") {
10164
- const chunks = pendingRequestBodies.get(frame.id);
10165
- if (!chunks) {
10278
+ const pending = pendingRequests.get(frame.id);
10279
+ if (!pending)
10280
+ return;
10281
+ if (pending.ignoreBody) {
10282
+ if (frame.final)
10283
+ pendingRequests.delete(frame.id);
10166
10284
  return;
10167
10285
  }
10168
- if (frame.data)
10169
- chunks.push(base64ToBytes(frame.data));
10286
+ if (frame.data) {
10287
+ const chunk = base64ToBytes(frame.data);
10288
+ if (pending.bodyController) {
10289
+ pending.bodyController.enqueue(chunk);
10290
+ } else {
10291
+ pending.bufferedChunks.push(chunk);
10292
+ }
10293
+ }
10170
10294
  if (!frame.final)
10171
10295
  return;
10172
- const abortController = new AbortController;
10173
- activeRequests.set(frame.id, abortController);
10174
- const meta = pendingRequestMeta.get(frame.id);
10175
- if (!meta)
10176
- return;
10177
- pendingRequestMeta.delete(frame.id);
10178
- try {
10179
- const reqBody = mergeBody(frame.id);
10180
- const base = new URL(targetBase.replace(/\/$/, "") + "/");
10181
- base.hostname = "localhost";
10182
- base.port = String(meta.port ?? primaryPort);
10183
- const url = new URL(meta.path.replace(/^\//, ""), base);
10184
- const headers = new Headers(meta.headers);
10185
- headers.delete("host");
10186
- headers.delete("content-length");
10187
- headers.delete("connection");
10188
- headers.delete("keep-alive");
10189
- headers.delete("proxy-authenticate");
10190
- headers.delete("proxy-authorization");
10191
- headers.delete("te");
10192
- headers.delete("trailers");
10193
- headers.delete("transfer-encoding");
10194
- headers.delete("upgrade");
10195
- const upstream = await fetch(url, {
10196
- method: meta.method,
10197
- headers,
10198
- body: reqBody.byteLength > 0 ? reqBody : undefined,
10199
- redirect: "manual",
10200
- signal: abortController.signal
10201
- });
10202
- ws.send(JSON.stringify({
10203
- type: "response",
10204
- id: frame.id,
10205
- statusCode: upstream.status,
10206
- headers: headersToRecord(upstream.headers),
10207
- timestamp: Date.now()
10208
- }));
10209
- if (!upstream.body) {
10210
- activeRequests.delete(frame.id);
10211
- ws.send(JSON.stringify({
10212
- type: "data",
10213
- id: frame.id,
10214
- final: true,
10215
- timestamp: Date.now()
10216
- }));
10217
- return;
10218
- }
10219
- const reader = upstream.body.getReader();
10296
+ pending.bodyClosed = true;
10297
+ if (pending.bodyController) {
10220
10298
  try {
10221
- while (true) {
10222
- const { done, value } = await reader.read();
10223
- if (done)
10224
- break;
10225
- if (!value)
10226
- continue;
10227
- ws.send(JSON.stringify({
10228
- type: "data",
10229
- id: frame.id,
10230
- data: bytesToBase64(value),
10231
- final: false,
10232
- timestamp: Date.now()
10233
- }));
10234
- }
10235
- } finally {
10236
- reader.releaseLock();
10237
- }
10238
- activeRequests.delete(frame.id);
10239
- ws.send(JSON.stringify({
10240
- type: "data",
10241
- id: frame.id,
10242
- final: true,
10243
- timestamp: Date.now()
10244
- }));
10245
- } catch (error) {
10246
- activeRequests.delete(frame.id);
10247
- pendingRequestBodies.delete(frame.id);
10248
- if (error instanceof Error && error.name === "AbortError") {
10249
- return;
10250
- }
10251
- ws.send(JSON.stringify({
10252
- type: "response",
10253
- id: frame.id,
10254
- statusCode: 502,
10255
- headers: { "content-type": "application/json" },
10256
- timestamp: Date.now()
10257
- }));
10258
- ws.send(JSON.stringify({
10259
- type: "data",
10260
- id: frame.id,
10261
- data: bytesToBase64(new TextEncoder().encode(JSON.stringify({ error: "upstream_error" }))),
10262
- final: true,
10263
- timestamp: Date.now()
10264
- }));
10299
+ pending.bodyController.close();
10300
+ } catch {}
10265
10301
  }
10302
+ pendingRequests.delete(frame.id);
10266
10303
  }
10267
10304
  });
10268
10305
  ws.addEventListener("close", () => {
@@ -10282,8 +10319,7 @@ Disconnecting...`);
10282
10319
  controller.abort();
10283
10320
  }
10284
10321
  activeRequests.clear();
10285
- pendingRequestBodies.clear();
10286
- pendingRequestMeta.clear();
10322
+ pendingRequests.clear();
10287
10323
  try {
10288
10324
  ws.close();
10289
10325
  } catch {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitterm",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Complete suit of gitterm tools in one CLI",
5
5
  "keywords": [
6
6
  "gitterm",
@@ -33,13 +33,12 @@
33
33
  "@opencode-ai/sdk": "^1.1.24",
34
34
  "@types/yargs": "^17.0.35",
35
35
  "chalk": "^5.6.2",
36
- "drizzle-orm": "catalog:",
36
+ "drizzle-orm": "^0.45.1",
37
37
  "xdg-basedir": "^5.1.0",
38
38
  "yargs": "^18.0.0",
39
39
  "zod": "^3.23.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@gitterm/config": "workspace:*",
43
42
  "@types/bun": "^1.2.6",
44
43
  "typescript": "^5.0.0"
45
44
  },