opencode-mobile 1.0.11 → 1.2.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.
Files changed (99) hide show
  1. package/bin/audit +31 -0
  2. package/bin/qr +13 -0
  3. package/dist/index.d.ts +11 -3
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +545 -3
  6. package/dist/index.js.map +1 -1
  7. package/dist/src/cli/endpoint-tester.d.ts +19 -0
  8. package/dist/src/cli/endpoint-tester.d.ts.map +1 -0
  9. package/dist/src/cli/endpoint-tester.js +233 -0
  10. package/dist/src/cli/endpoint-tester.js.map +1 -0
  11. package/dist/src/cli/index.d.ts +20 -0
  12. package/dist/src/cli/index.d.ts.map +1 -0
  13. package/dist/src/cli/index.js +166 -0
  14. package/dist/src/cli/index.js.map +1 -0
  15. package/dist/src/cli/push-tester.d.ts +16 -0
  16. package/dist/src/cli/push-tester.d.ts.map +1 -0
  17. package/dist/src/cli/push-tester.js +293 -0
  18. package/dist/src/cli/push-tester.js.map +1 -0
  19. package/dist/src/cli/qr.d.ts +5 -0
  20. package/dist/src/cli/qr.d.ts.map +1 -0
  21. package/dist/src/cli/qr.js +127 -0
  22. package/dist/src/cli/qr.js.map +1 -0
  23. package/dist/src/cli/report.d.ts +25 -0
  24. package/dist/src/cli/report.d.ts.map +1 -0
  25. package/dist/src/cli/report.js +215 -0
  26. package/dist/src/cli/report.js.map +1 -0
  27. package/dist/src/cli/server-manager.d.ts +28 -0
  28. package/dist/src/cli/server-manager.d.ts.map +1 -0
  29. package/dist/src/cli/server-manager.js +340 -0
  30. package/dist/src/cli/server-manager.js.map +1 -0
  31. package/dist/src/cli/test-runner.d.ts +16 -0
  32. package/dist/src/cli/test-runner.d.ts.map +1 -0
  33. package/dist/src/cli/test-runner.js +201 -0
  34. package/dist/src/cli/test-runner.js.map +1 -0
  35. package/dist/src/cli/tunnel-qr.d.ts +8 -0
  36. package/dist/src/cli/tunnel-qr.d.ts.map +1 -0
  37. package/dist/src/cli/tunnel-qr.js +163 -0
  38. package/dist/src/cli/tunnel-qr.js.map +1 -0
  39. package/dist/src/cli/tunnel-tester.d.ts +22 -0
  40. package/dist/src/cli/tunnel-tester.d.ts.map +1 -0
  41. package/dist/src/cli/tunnel-tester.js +360 -0
  42. package/dist/src/cli/tunnel-tester.js.map +1 -0
  43. package/dist/src/cli/types.d.ts +112 -0
  44. package/dist/src/cli/types.d.ts.map +1 -0
  45. package/dist/src/cli/types.js +5 -0
  46. package/dist/src/cli/types.js.map +1 -0
  47. package/dist/src/push/index.d.ts +1 -0
  48. package/dist/src/push/index.d.ts.map +1 -1
  49. package/dist/src/push/index.js +1 -0
  50. package/dist/src/push/index.js.map +1 -1
  51. package/dist/src/push/notification-handler.d.ts +58 -0
  52. package/dist/src/push/notification-handler.d.ts.map +1 -0
  53. package/dist/src/push/notification-handler.js +110 -0
  54. package/dist/src/push/notification-handler.js.map +1 -0
  55. package/dist/src/tunnel/cloudflare.d.ts +38 -1
  56. package/dist/src/tunnel/cloudflare.d.ts.map +1 -1
  57. package/dist/src/tunnel/cloudflare.js +148 -51
  58. package/dist/src/tunnel/cloudflare.js.map +1 -1
  59. package/dist/src/tunnel/localtunnel.d.ts +32 -1
  60. package/dist/src/tunnel/localtunnel.d.ts.map +1 -1
  61. package/dist/src/tunnel/localtunnel.js +75 -16
  62. package/dist/src/tunnel/localtunnel.js.map +1 -1
  63. package/dist/src/tunnel/metadata.d.ts +29 -0
  64. package/dist/src/tunnel/metadata.d.ts.map +1 -0
  65. package/dist/src/tunnel/metadata.js +83 -0
  66. package/dist/src/tunnel/metadata.js.map +1 -0
  67. package/dist/src/tunnel/ngrok.d.ts +24 -1
  68. package/dist/src/tunnel/ngrok.d.ts.map +1 -1
  69. package/dist/src/tunnel/ngrok.js +381 -20
  70. package/dist/src/tunnel/ngrok.js.map +1 -1
  71. package/dist/src/tunnel/qrcode.d.ts +14 -2
  72. package/dist/src/tunnel/qrcode.d.ts.map +1 -1
  73. package/dist/src/tunnel/qrcode.js +96 -8
  74. package/dist/src/tunnel/qrcode.js.map +1 -1
  75. package/package.json +22 -4
  76. package/dist/push-notifications.d.ts +0 -4
  77. package/dist/push-notifications.d.ts.map +0 -1
  78. package/dist/push-notifications.js +0 -260
  79. package/dist/push-notifications.js.map +0 -1
  80. package/dist/reverse-proxy.d.ts +0 -9
  81. package/dist/reverse-proxy.d.ts.map +0 -1
  82. package/dist/reverse-proxy.js +0 -78
  83. package/dist/reverse-proxy.js.map +0 -1
  84. package/dist/sdk-logger.d.ts +0 -19
  85. package/dist/sdk-logger.d.ts.map +0 -1
  86. package/dist/sdk-logger.js +0 -103
  87. package/dist/sdk-logger.js.map +0 -1
  88. package/dist/src/tunnel/index.d.ts +0 -29
  89. package/dist/src/tunnel/index.d.ts.map +0 -1
  90. package/dist/src/tunnel/index.js +0 -125
  91. package/dist/src/tunnel/index.js.map +0 -1
  92. package/dist/src/utils/port.d.ts +0 -12
  93. package/dist/src/utils/port.d.ts.map +0 -1
  94. package/dist/src/utils/port.js +0 -41
  95. package/dist/src/utils/port.js.map +0 -1
  96. package/dist/tunnel-manager.d.ts +0 -30
  97. package/dist/tunnel-manager.d.ts.map +0 -1
  98. package/dist/tunnel-manager.js +0 -641
  99. package/dist/tunnel-manager.js.map +0 -1
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Session notification handler
3
+ *
4
+ * Fetches the last assistant message from a session to form push notifications.
5
+ *
6
+ * USAGE: Uncomment and integrate into main plugin when ready.
7
+ *
8
+ * Integration example:
9
+ *
10
+ * import { handleSessionNotification } from "./notification-handler";
11
+ *
12
+ * export const PushNotificationPlugin: Plugin = async (ctx) => {
13
+ * return {
14
+ * event: async ({ event }) => {
15
+ * if (event.type === "chat.message") {
16
+ * await handleSessionNotification(ctx, event);
17
+ * }
18
+ * },
19
+ * };
20
+ * };
21
+ */
22
+ /**
23
+ * Handle session notification - fetch last assistant message for context
24
+ *
25
+ * This enables push notifications that include the assistant's response content,
26
+ * allowing users to see what the AI said without opening the app.
27
+ */
28
+ export async function handleSessionNotification(ctx, event) {
29
+ // UNCOMMENT WHEN READY:
30
+ // const client = (ctx as any).client;
31
+ // if (!client) {
32
+ // logger.warn("No client available for session notification");
33
+ // return null;
34
+ // }
35
+ //
36
+ // try {
37
+ // const response = await client.Session.messages({
38
+ // path: { id: event.sessionID },
39
+ // query: { limit: 50 }
40
+ // });
41
+ //
42
+ // if (!response.data || response.data.length === 0) {
43
+ // logger.warn("No messages found in session");
44
+ // return null;
45
+ // }
46
+ //
47
+ // // Filter for assistant messages and get the last one
48
+ // const assistantMessages = response.data.filter(
49
+ // (msg: any) => msg.info.role === "assistant"
50
+ // );
51
+ //
52
+ // if (assistantMessages.length === 0) {
53
+ // logger.warn("No assistant messages found in session");
54
+ // return null;
55
+ // }
56
+ //
57
+ // const lastAssistant = assistantMessages[assistantMessages.length - 1];
58
+ // const info = lastAssistant.info as any;
59
+ //
60
+ // // Extract text content from parts
61
+ // const textContent = lastAssistant.parts
62
+ // .filter((part: any) => part.type === "text")
63
+ // .map((part: any) => part.text)
64
+ // .join("\n");
65
+ //
66
+ // const result: AssistantMessageInfo = {
67
+ // id: info.id,
68
+ // content: textContent.slice(0, 200) + (textContent.length > 200 ? "..." : ""),
69
+ // model: `${info.providerID}/${info.modelID}`,
70
+ // provider: info.providerID,
71
+ // created: new Date(info.time.created).toISOString(),
72
+ // tokens: info.tokens || { input: 0, output: 0, reasoning: 0 },
73
+ // cost: info.cost || 0,
74
+ // };
75
+ //
76
+ // logger.info("Retrieved last assistant message for notification", {
77
+ // messageID: result.id,
78
+ // model: result.model,
79
+ // contentPreview: result.content.slice(0, 50)
80
+ // });
81
+ //
82
+ // return result;
83
+ //
84
+ // } catch (error) {
85
+ // logger.error("Failed to fetch assistant message for notification", error);
86
+ // return null;
87
+ // }
88
+ return null;
89
+ }
90
+ /**
91
+ * Format assistant message for push notification
92
+ *
93
+ * Creates a notification payload from the assistant message info.
94
+ */
95
+ export function formatAssistantNotification(assistant, sessionName) {
96
+ const title = sessionName ? `Assistant (${sessionName})` : "Assistant Response";
97
+ const body = `Model: ${assistant.model}\n${assistant.content}`;
98
+ return {
99
+ title,
100
+ body: body.slice(0, 150) + (body.length > 150 ? "..." : ""),
101
+ data: {
102
+ type: "assistant_message",
103
+ messageID: assistant.id,
104
+ sessionID: assistant.id,
105
+ model: assistant.model,
106
+ },
107
+ };
108
+ }
109
+ export default handleSessionNotification;
110
+ //# sourceMappingURL=notification-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-handler.js","sourceRoot":"","sources":["../../../src/push/notification-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAqBH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,GAAgB,EAChB,KAA8D;IAE9D,wBAAwB;IACxB,sCAAsC;IACtC,iBAAiB;IACjB,iEAAiE;IACjE,iBAAiB;IACjB,IAAI;IACJ,GAAG;IACH,QAAQ;IACR,qDAAqD;IACrD,qCAAqC;IACrC,2BAA2B;IAC3B,QAAQ;IACR,GAAG;IACH,wDAAwD;IACxD,mDAAmD;IACnD,mBAAmB;IACnB,MAAM;IACN,GAAG;IACH,0DAA0D;IAC1D,oDAAoD;IACpD,kDAAkD;IAClD,OAAO;IACP,GAAG;IACH,0CAA0C;IAC1C,6DAA6D;IAC7D,mBAAmB;IACnB,MAAM;IACN,GAAG;IACH,2EAA2E;IAC3E,4CAA4C;IAC5C,GAAG;IACH,uCAAuC;IACvC,4CAA4C;IAC5C,mDAAmD;IACnD,qCAAqC;IACrC,mBAAmB;IACnB,GAAG;IACH,2CAA2C;IAC3C,mBAAmB;IACnB,oFAAoF;IACpF,mDAAmD;IACnD,iCAAiC;IACjC,0DAA0D;IAC1D,oEAAoE;IACpE,4BAA4B;IAC5B,OAAO;IACP,GAAG;IACH,uEAAuE;IACvE,4BAA4B;IAC5B,2BAA2B;IAC3B,kDAAkD;IAClD,QAAQ;IACR,GAAG;IACH,mBAAmB;IACnB,GAAG;IACH,oBAAoB;IACpB,+EAA+E;IAC/E,iBAAiB;IACjB,IAAI;IAEJ,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CACzC,SAA+B,EAC/B,WAAoB;IAEpB,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,cAAc,WAAW,GAAG,CAAC,CAAC,CAAC,oBAAoB,CAAC;IAChF,MAAM,IAAI,GAAG,UAAU,SAAS,CAAC,KAAK,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC;IAE/D,OAAO;QACL,KAAK;QACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,IAAI,EAAE;YACJ,IAAI,EAAE,mBAAmB;YACzB,SAAS,EAAE,SAAS,CAAC,EAAE;YACvB,SAAS,EAAE,SAAS,CAAC,EAAE;YACvB,KAAK,EAAE,SAAS,CAAC,KAAK;SACvB;KACF,CAAC;AACJ,CAAC;AAED,eAAe,yBAAyB,CAAC"}
@@ -1,9 +1,26 @@
1
1
  /**
2
2
  * Cloudflare tunnel provider implementation
3
+ *
4
+ * Refactored for testability:
5
+ * - Optional spawn function for dependency injection
6
+ * - Optional fs.existsSync for testing
7
+ * - Port validation
8
+ * - Clean separation of concerns
3
9
  */
10
+ import { spawn, ChildProcess } from "child_process";
4
11
  import type { TunnelConfig, TunnelInfo } from "./types";
12
+ export type { TunnelConfig, TunnelInfo };
5
13
  /**
6
- * Start a Cloudflare tunnel
14
+ * Find cloudflared binary (extracted for testability)
15
+ */
16
+ export declare function findCloudflared(paths?: string[], existsSync?: (path: string) => boolean): string | null;
17
+ /**
18
+ * Create a cloudflare tunnel instance
19
+ * This function is testable - accepts external spawn and existsSync
20
+ */
21
+ export declare function createCloudflareTunnel(config: TunnelConfig, spawnFn?: typeof spawn, existsSyncFn?: (path: string) => boolean, onUrl?: (url: string) => void): Promise<TunnelInfo>;
22
+ /**
23
+ * Start a Cloudflare tunnel (legacy function - uses module state)
7
24
  */
8
25
  export declare function startCloudflareTunnel(config: TunnelConfig): Promise<TunnelInfo>;
9
26
  /**
@@ -14,4 +31,24 @@ export declare function stopCloudflareTunnel(): Promise<void>;
14
31
  * Check if cloudflared is installed
15
32
  */
16
33
  export declare function isCloudflareInstalled(): Promise<boolean>;
34
+ /**
35
+ * Get the current Cloudflare tunnel URL
36
+ */
37
+ export declare function getCloudflareUrl(): string | null;
38
+ /**
39
+ * Get current process (for testing)
40
+ */
41
+ export declare function getProcess(): ChildProcess | null;
42
+ /**
43
+ * Set process (for testing)
44
+ */
45
+ export declare function setProcess(process: ChildProcess | null): void;
46
+ /**
47
+ * Set URL (for testing)
48
+ */
49
+ export declare function setUrl(url: string | null): void;
50
+ /**
51
+ * Clear state (for testing)
52
+ */
53
+ export declare function clearState(): void;
17
54
  //# sourceMappingURL=cloudflare.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/tunnel/cloudflare.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAKxD;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CA2CrF;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAM1D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS9D"}
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/tunnel/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGxD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAczC;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,EAChB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GACrC,MAAM,GAAG,IAAI,CAUf;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,OAAO,KAAK,EACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,EACxC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAC5B,OAAO,CAAC,UAAU,CAAC,CAgFrB;AAgBD;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAErF;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAM1D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAEhD;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,YAAY,GAAG,IAAI,CAEhD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAE7D;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAGjC"}
@@ -1,74 +1,171 @@
1
1
  /**
2
2
  * Cloudflare tunnel provider implementation
3
+ *
4
+ * Refactored for testability:
5
+ * - Optional spawn function for dependency injection
6
+ * - Optional fs.existsSync for testing
7
+ * - Port validation
8
+ * - Clean separation of concerns
3
9
  */
4
10
  import { spawn } from "child_process";
5
- let cloudflareProcess = null;
6
- let cloudflareUrl = null;
11
+ // Module-level state (for backward compatibility)
12
+ let _process = null;
13
+ let _url = null;
14
+ // Default paths for cloudflared
15
+ const CLOUDFLARED_PATHS = [
16
+ "/usr/local/bin/cloudflared",
17
+ "/usr/bin/cloudflared",
18
+ `${process.env.HOME || ""}/.cloudflared/cloudflared`,
19
+ "/opt/homebrew/bin/cloudflared",
20
+ ];
7
21
  /**
8
- * Start a Cloudflare tunnel
22
+ * Find cloudflared binary (extracted for testability)
9
23
  */
10
- export async function startCloudflareTunnel(config) {
11
- return new Promise((resolve, reject) => {
24
+ export function findCloudflared(paths, existsSync) {
25
+ const searchPaths = paths || CLOUDFLARED_PATHS;
26
+ const checkExists = existsSync || ((p) => require("fs").existsSync(p));
27
+ for (const p of searchPaths) {
12
28
  try {
13
- const cloudflared = spawn("cloudflared", [
14
- "tunnel",
15
- "--url",
16
- `http://127.0.0.1:${config.port}`,
17
- ]);
18
- cloudflareProcess = cloudflared;
19
- let output = "";
20
- cloudflared.stdout.on("data", (data) => {
21
- output += data.toString();
22
- const match = output.match(/https:\/\/[^\s]+\\.trycloudflare\\.com/);
23
- if (match) {
24
- cloudflareUrl = match[0];
25
- resolve({
26
- url: cloudflareUrl,
27
- tunnelId: "cloudflare",
28
- port: config.port,
29
- provider: "cloudflare",
30
- });
29
+ if (checkExists(p))
30
+ return p;
31
+ }
32
+ catch { }
33
+ }
34
+ return null;
35
+ }
36
+ /**
37
+ * Create a cloudflare tunnel instance
38
+ * This function is testable - accepts external spawn and existsSync
39
+ */
40
+ export function createCloudflareTunnel(config, spawnFn, existsSyncFn, onUrl) {
41
+ // Validate port
42
+ if (!config.port || typeof config.port !== "number") {
43
+ return Promise.reject(new Error("Invalid port: must be a number"));
44
+ }
45
+ // Find cloudflared
46
+ const cloudflaredPath = findCloudflareD(CLOUDFLARED_PATHS, existsSyncFn);
47
+ if (!cloudflaredPath) {
48
+ return Promise.reject(new Error("cloudflared not found. Install from https://github.com/cloudflare/cloudflared"));
49
+ }
50
+ const spawnModule = spawnFn || spawn;
51
+ return new Promise((resolve, reject) => {
52
+ const timeout = setTimeout(() => reject(new Error("Timeout waiting for cloudflared URL (60s)")), 60000);
53
+ const process = spawnModule(cloudflaredPath, [
54
+ "tunnel",
55
+ "--url",
56
+ `http://127.0.0.1:${config.port}`,
57
+ ], { stdio: ["ignore", "pipe", "pipe"] });
58
+ _process = process;
59
+ _url = null;
60
+ const onData = (data) => {
61
+ const line = data.toString().trim();
62
+ const urlMatch = line.match(/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/);
63
+ if (urlMatch) {
64
+ const url = urlMatch[0];
65
+ _url = url;
66
+ if (onUrl) {
67
+ onUrl(url);
31
68
  }
32
- });
33
- cloudflared.stderr.on("data", (data) => {
34
- console.error("[Tunnel] Cloudflare stderr:", data.toString());
35
- });
36
- cloudflared.on("error", (err) => {
37
- reject(new Error(`Cloudflare tunnel failed: ${err.message}`));
38
- });
39
- cloudflared.on("close", (code) => {
40
- if (code !== 0 && !cloudflareUrl) {
41
- reject(new Error(`Cloudflare tunnel exited with code ${code}`));
69
+ else {
70
+ console.log("[Cloudflared] URL:", url);
42
71
  }
43
- });
44
- }
45
- catch (error) {
46
- reject(new Error(`Cloudflare tunnel failed: ${error.message}`));
47
- }
72
+ clearTimeout(timeout);
73
+ resolve({
74
+ url: _url,
75
+ tunnelId: _url.split("://")[1].split(".")[0],
76
+ port: config.port,
77
+ provider: "cloudflare",
78
+ });
79
+ }
80
+ else if (line.includes("ERR") || line.includes("error")) {
81
+ console.log("[Cloudflared]", line);
82
+ }
83
+ };
84
+ process.stdout?.on("data", onData);
85
+ process.stderr?.on("data", onData);
86
+ process.on("error", (err) => {
87
+ clearTimeout(timeout);
88
+ _process = null;
89
+ reject(err);
90
+ });
91
+ process.on("exit", (code) => {
92
+ // If no URL was captured, treat as failure even on clean exit
93
+ if (!_url) {
94
+ clearTimeout(timeout);
95
+ _process = null;
96
+ reject(new Error("cloudflared exited without providing a tunnel URL"));
97
+ return;
98
+ }
99
+ if (code !== 0 && code !== null) {
100
+ clearTimeout(timeout);
101
+ _process = null;
102
+ reject(new Error(`cloudflared exited with code ${code}`));
103
+ }
104
+ });
48
105
  });
49
106
  }
107
+ function findCloudflareD(paths, existsSync) {
108
+ const checkExists = existsSync || ((p) => require("fs").existsSync(p));
109
+ for (const p of paths) {
110
+ try {
111
+ if (checkExists(p))
112
+ return p;
113
+ }
114
+ catch { }
115
+ }
116
+ return null;
117
+ }
118
+ /**
119
+ * Start a Cloudflare tunnel (legacy function - uses module state)
120
+ */
121
+ export async function startCloudflareTunnel(config) {
122
+ return createCloudflareTunnel(config);
123
+ }
50
124
  /**
51
125
  * Stop the Cloudflare tunnel
52
126
  */
53
127
  export async function stopCloudflareTunnel() {
54
- if (cloudflareProcess) {
55
- cloudflareProcess.kill();
56
- cloudflareProcess = null;
57
- cloudflareUrl = null;
128
+ if (_process) {
129
+ _process.kill("SIGTERM");
130
+ _process = null;
58
131
  }
132
+ _url = null;
59
133
  }
60
134
  /**
61
135
  * Check if cloudflared is installed
62
136
  */
63
137
  export async function isCloudflareInstalled() {
64
- try {
65
- const { promisify } = await import("util");
66
- const execAsync = promisify((await import("child_process")).exec);
67
- await execAsync("which cloudflared");
68
- return true;
69
- }
70
- catch {
71
- return false;
72
- }
138
+ return findCloudflared() !== null;
139
+ }
140
+ /**
141
+ * Get the current Cloudflare tunnel URL
142
+ */
143
+ export function getCloudflareUrl() {
144
+ return _url;
145
+ }
146
+ /**
147
+ * Get current process (for testing)
148
+ */
149
+ export function getProcess() {
150
+ return _process;
151
+ }
152
+ /**
153
+ * Set process (for testing)
154
+ */
155
+ export function setProcess(process) {
156
+ _process = process;
157
+ }
158
+ /**
159
+ * Set URL (for testing)
160
+ */
161
+ export function setUrl(url) {
162
+ _url = url;
163
+ }
164
+ /**
165
+ * Clear state (for testing)
166
+ */
167
+ export function clearState() {
168
+ _process = null;
169
+ _url = null;
73
170
  }
74
171
  //# sourceMappingURL=cloudflare.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../src/tunnel/cloudflare.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAGtC,IAAI,iBAAiB,GAAQ,IAAI,CAAC;AAClC,IAAI,aAAa,GAAkB,IAAI,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAoB;IAC9D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,KAAK,CAAC,aAAa,EAAE;gBACvC,QAAQ;gBACR,OAAO;gBACP,oBAAoB,MAAM,CAAC,IAAI,EAAE;aAClC,CAAC,CAAC;YAEH,iBAAiB,GAAG,WAAW,CAAC;YAEhC,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBAC7C,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBACrE,IAAI,KAAK,EAAE,CAAC;oBACV,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBACzB,OAAO,CAAC;wBACN,GAAG,EAAE,aAAa;wBAClB,QAAQ,EAAE,YAAY;wBACtB,IAAI,EAAE,MAAM,CAAC,IAAI;wBACjB,QAAQ,EAAE,YAAY;qBACvB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBAC7C,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAChE,CAAC,CAAC,CAAC;YAEH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;gBACrC,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAChE,CAAC,CAAC,CAAC;YAEH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE;gBACvC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;oBACjC,MAAM,CAAC,IAAI,KAAK,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,IAAI,iBAAiB,EAAE,CAAC;QACtB,iBAAiB,CAAC,IAAI,EAAE,CAAC;QACzB,iBAAiB,GAAG,IAAI,CAAC;QACzB,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC;QACH,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../src/tunnel/cloudflare.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,44 @@
1
1
  /**
2
2
  * Localtunnel tunnel provider implementation
3
+ *
4
+ * Refactored for testability:
5
+ * - Optional instance parameter for dependency injection
6
+ * - Port validation
7
+ * - Clean separation of concerns
3
8
  */
9
+ import localtunnel from "localtunnel";
4
10
  import type { TunnelConfig, TunnelInfo } from "./types";
11
+ export type { TunnelConfig, TunnelInfo };
5
12
  /**
6
- * Start a localtunnel
13
+ * Create a localtunnel instance
14
+ * This function is testable - accepts external localtunnel for mocking
15
+ */
16
+ export declare function createLocaltunnel(config: TunnelConfig, options?: {
17
+ localtunnelModule?: typeof localtunnel;
18
+ onUrl?: (url: string) => void;
19
+ }): Promise<TunnelInfo>;
20
+ /**
21
+ * Start a localtunnel (legacy function - uses module state)
7
22
  */
8
23
  export declare function startLocaltunnel(config: TunnelConfig): Promise<TunnelInfo>;
9
24
  /**
10
25
  * Stop the localtunnel
11
26
  */
12
27
  export declare function stopLocaltunnel(): Promise<void>;
28
+ /**
29
+ * Get the current localtunnel URL
30
+ */
31
+ export declare function getLocaltunnelUrl(): string | null;
32
+ /**
33
+ * Get current instance (for testing)
34
+ */
35
+ export declare function getInstance(): any;
36
+ /**
37
+ * Set instance (for testing)
38
+ */
39
+ export declare function setInstance(instance: any): void;
40
+ /**
41
+ * Clear instance (for testing)
42
+ */
43
+ export declare function clearInstance(): void;
13
44
  //# sourceMappingURL=localtunnel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"localtunnel.d.ts","sourceRoot":"","sources":["../../../src/tunnel/localtunnel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAIxD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAchF;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAKrD"}
1
+ {"version":3,"file":"localtunnel.d.ts","sourceRoot":"","sources":["../../../src/tunnel/localtunnel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,WAAW,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGxD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAMzC;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE;IACP,iBAAiB,CAAC,EAAE,OAAO,WAAW,CAAC;IACvC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1B,GACL,OAAO,CAAC,UAAU,CAAC,CA6CrB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAEhF;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAKrD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjD;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,GAAG,CAEjC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAEpC"}
@@ -1,31 +1,90 @@
1
1
  /**
2
2
  * Localtunnel tunnel provider implementation
3
+ *
4
+ * Refactored for testability:
5
+ * - Optional instance parameter for dependency injection
6
+ * - Port validation
7
+ * - Clean separation of concerns
3
8
  */
4
9
  import localtunnel from "localtunnel";
5
- let localtunnelInstance = null;
10
+ // Module-level state (for backward compatibility)
11
+ // Use the factory functions for testable code
12
+ let _instance = null;
6
13
  /**
7
- * Start a localtunnel
14
+ * Create a localtunnel instance
15
+ * This function is testable - accepts external localtunnel for mocking
8
16
  */
9
- export async function startLocaltunnel(config) {
10
- const tunnel = await localtunnel({
11
- port: config.port,
12
- subdomain: config.subdomain,
17
+ export function createLocaltunnel(config, options = {}) {
18
+ const { localtunnelModule = localtunnel, onUrl } = options;
19
+ // Validate port
20
+ if (!config.port || typeof config.port !== "number") {
21
+ return Promise.reject(new Error("Invalid port: must be a number"));
22
+ }
23
+ return new Promise((resolve, reject) => {
24
+ const timeout = setTimeout(() => reject(new Error("Timeout waiting for localtunnel URL (30s)")), 30000);
25
+ localtunnelModule({ port: config.port, subdomain: config.subdomain }, (err, tunnel) => {
26
+ if (err) {
27
+ clearTimeout(timeout);
28
+ reject(new Error(`Localtunnel failed: ${err.message}`));
29
+ return;
30
+ }
31
+ clearTimeout(timeout);
32
+ _instance = tunnel;
33
+ if (onUrl) {
34
+ onUrl(tunnel.url);
35
+ }
36
+ else {
37
+ console.log("[Tunnel] URL:", tunnel.url);
38
+ }
39
+ tunnel.on("close", () => {
40
+ _instance = null;
41
+ });
42
+ resolve({
43
+ url: tunnel.url,
44
+ tunnelId: tunnel.url.split("://")[1].split(".")[0],
45
+ port: config.port,
46
+ provider: "localtunnel",
47
+ });
48
+ });
13
49
  });
14
- localtunnelInstance = tunnel;
15
- return {
16
- url: tunnel.url,
17
- tunnelId: tunnel.name,
18
- port: config.port,
19
- provider: "localtunnel",
20
- };
50
+ }
51
+ /**
52
+ * Start a localtunnel (legacy function - uses module state)
53
+ */
54
+ export async function startLocaltunnel(config) {
55
+ return createLocaltunnel(config);
21
56
  }
22
57
  /**
23
58
  * Stop the localtunnel
24
59
  */
25
60
  export async function stopLocaltunnel() {
26
- if (localtunnelInstance) {
27
- await localtunnelInstance.close();
28
- localtunnelInstance = null;
61
+ if (_instance) {
62
+ _instance.close();
63
+ _instance = null;
29
64
  }
30
65
  }
66
+ /**
67
+ * Get the current localtunnel URL
68
+ */
69
+ export function getLocaltunnelUrl() {
70
+ return _instance?.url || null;
71
+ }
72
+ /**
73
+ * Get current instance (for testing)
74
+ */
75
+ export function getInstance() {
76
+ return _instance;
77
+ }
78
+ /**
79
+ * Set instance (for testing)
80
+ */
81
+ export function setInstance(instance) {
82
+ _instance = instance;
83
+ }
84
+ /**
85
+ * Clear instance (for testing)
86
+ */
87
+ export function clearInstance() {
88
+ _instance = null;
89
+ }
31
90
  //# sourceMappingURL=localtunnel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"localtunnel.js","sourceRoot":"","sources":["../../../src/tunnel/localtunnel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,WAAW,MAAM,aAAa,CAAC;AAGtC,IAAI,mBAAmB,GAAQ,IAAI,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAoB;IACzD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;QAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC,CAAC;IAEH,mBAAmB,GAAG,MAAM,CAAC;IAE7B,OAAO;QACL,GAAG,EAAE,MAAM,CAAC,GAAa;QACzB,QAAQ,EAAE,MAAM,CAAC,IAAc;QAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,QAAQ,EAAE,aAAa;KACxB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,mBAAmB,EAAE,CAAC;QACxB,MAAM,mBAAmB,CAAC,KAAK,EAAE,CAAC;QAClC,mBAAmB,GAAG,IAAI,CAAC;IAC7B,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"localtunnel.js","sourceRoot":"","sources":["../../../src/tunnel/localtunnel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,WAAW,MAAM,aAAa,CAAC;AAMtC,kDAAkD;AAClD,8CAA8C;AAC9C,IAAI,SAAS,GAAQ,IAAI,CAAC;AAE1B;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAoB,EACpB,UAGI,EAAE;IAEN,MAAM,EAAE,iBAAiB,GAAG,WAAW,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAE3D,gBAAgB;IAChB,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,OAAO,GAAG,UAAU,CACxB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC,EACpE,KAAK,CACN,CAAC;QAEF,iBAAiB,CACf,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAClD,CAAC,GAAQ,EAAE,MAAW,EAAE,EAAE;YACxB,IAAI,GAAG,EAAE,CAAC;gBACR,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACxD,OAAO;YACT,CAAC;YAED,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,SAAS,GAAG,MAAM,CAAC;YAEnB,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3C,CAAC;YAED,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACtB,SAAS,GAAG,IAAI,CAAC;YACnB,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC;gBACN,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAClD,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAoB;IACzD,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,SAAS,EAAE,CAAC;QACd,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,SAAS,EAAE,GAAG,IAAI,IAAI,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAa;IACvC,SAAS,GAAG,QAAQ,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Tunnel metadata storage for .config/opencode/tunnel.json
3
+ */
4
+ export interface TunnelMetadata {
5
+ url: string | null;
6
+ tunnelId: string | null;
7
+ provider: string | null;
8
+ port: number | null;
9
+ targetPort: number | null;
10
+ startedAt: string | null;
11
+ lastUpdated: string | null;
12
+ }
13
+ /**
14
+ * Load tunnel metadata from disk
15
+ */
16
+ export declare function loadTunnelMetadata(): TunnelMetadata;
17
+ /**
18
+ * Save tunnel metadata to disk
19
+ */
20
+ export declare function saveTunnelMetadata(metadata: TunnelMetadata): boolean;
21
+ /**
22
+ * Update tunnel metadata when a tunnel starts
23
+ */
24
+ export declare function updateTunnelMetadata(url: string, tunnelId: string, provider: string, port: number, targetPort: number): void;
25
+ /**
26
+ * Clear tunnel metadata when tunnel stops
27
+ */
28
+ export declare function clearTunnelMetadata(): void;
29
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../src/tunnel/metadata.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,cAAc,CAkBnD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAapE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,IAAI,CAaN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAa1C"}