@aipanel/provider-opencode 1.2.0-beta.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/es/system.js ADDED
@@ -0,0 +1,175 @@
1
+ import { spawn } from "child_process";
2
+ import { createLogger } from "@aipanel/core/node";
3
+ const log = createLogger("OpenCodeSystem");
4
+ async function checkOpenCodeInstalled() {
5
+ const timer = log.timer("checkOpenCodeInstalled");
6
+ return new Promise((resolve) => {
7
+ log.debug("Checking if OpenCode is installed...");
8
+ const proc = spawn("opencode", ["--version"], { stdio: "ignore", shell: true });
9
+ proc.on("close", (code) => {
10
+ const installed = code === 0;
11
+ timer.end(installed ? "\u2713 OpenCode is installed" : "\u274C OpenCode not found");
12
+ resolve(installed);
13
+ });
14
+ proc.on("error", (err) => {
15
+ log.debug("Failed to check OpenCode installation", { error: err.message });
16
+ timer.end("\u274C Check failed");
17
+ resolve(false);
18
+ });
19
+ });
20
+ }
21
+ function getOpenCodeVersion() {
22
+ return new Promise((resolve) => {
23
+ const proc = spawn("opencode", ["--version"], { stdio: "pipe", shell: true });
24
+ let output = "";
25
+ proc.stdout?.on("data", (data) => {
26
+ output += data.toString();
27
+ });
28
+ proc.on("close", (code) => {
29
+ if (code === 0 && output.trim()) {
30
+ resolve(output.trim());
31
+ } else {
32
+ resolve(null);
33
+ }
34
+ });
35
+ proc.on("error", () => {
36
+ resolve(null);
37
+ });
38
+ });
39
+ }
40
+ const KILL_ORPHAN_TIMEOUT = 5e3;
41
+ async function killOrphanOpenCodeProcesses() {
42
+ const timer = log.timer("killOrphanOpenCodeProcesses");
43
+ log.debug("Looking for orphan OpenCode processes (PPID=1)");
44
+ return new Promise((resolve) => {
45
+ let settled = false;
46
+ const done = (count) => {
47
+ if (settled) return;
48
+ settled = true;
49
+ resolve(count);
50
+ };
51
+ const timeout = setTimeout(() => {
52
+ log.warn("Kill orphan processes timed out, skipping");
53
+ timer.end("\u26A0 Timeout, skipped");
54
+ done(0);
55
+ }, KILL_ORPHAN_TIMEOUT);
56
+ const wrappedResolve = (count) => {
57
+ clearTimeout(timeout);
58
+ done(count);
59
+ };
60
+ if (process.platform === "win32") {
61
+ killOrphanProcessesOnWindows(wrappedResolve, timer);
62
+ } else {
63
+ killOrphanProcessesOnUnix(wrappedResolve, timer);
64
+ }
65
+ });
66
+ }
67
+ function killOrphanProcessesOnWindows(resolve, timer) {
68
+ log.debug("Using Windows method to find orphan processes");
69
+ const proc = spawn(
70
+ "wmic",
71
+ ["process", "where", 'name="opencode.exe"', "get", "processid,parentprocessid"],
72
+ { stdio: "pipe" }
73
+ );
74
+ let output = "";
75
+ proc.stdout?.on("data", (data) => {
76
+ output += data.toString();
77
+ });
78
+ proc.on("close", () => {
79
+ const lines = output.split("\n").filter((line) => line.trim());
80
+ const pidsToKill = [];
81
+ lines.forEach((line) => {
82
+ const parts = line.trim().split(/\s+/);
83
+ if (parts.length >= 2) {
84
+ const ppid = parts[0];
85
+ const pid = parts[1];
86
+ if (ppid === "1" && pid && !isNaN(Number(pid))) {
87
+ pidsToKill.push(pid);
88
+ }
89
+ }
90
+ });
91
+ if (pidsToKill.length > 0) {
92
+ log.debug(`Found ${pidsToKill.length} orphan processes`, { pids: pidsToKill });
93
+ let killedCount = 0;
94
+ let completedCount = 0;
95
+ pidsToKill.forEach((pid) => {
96
+ const killProc = spawn("taskkill", ["/F", "/PID", pid], { stdio: "ignore" });
97
+ killProc.on("close", (code) => {
98
+ completedCount++;
99
+ if (code === 0) {
100
+ killedCount++;
101
+ log.debug(`Killed orphan process ${pid}`);
102
+ }
103
+ if (completedCount === pidsToKill.length) {
104
+ timer.end(`\u2713 Killed ${killedCount} orphan processes`);
105
+ resolve(killedCount);
106
+ }
107
+ });
108
+ });
109
+ } else {
110
+ log.debug("No orphan processes found");
111
+ timer.end("No orphan processes found");
112
+ resolve(0);
113
+ }
114
+ });
115
+ proc.on("error", (err) => {
116
+ log.debug("Failed to find orphan processes", { error: err.message });
117
+ timer.end("\u274C Failed to find orphan processes");
118
+ resolve(0);
119
+ });
120
+ }
121
+ function killOrphanProcessesOnUnix(resolve, timer) {
122
+ log.debug("Using Unix method to find orphan processes");
123
+ const proc = spawn("ps", ["-e", "-o", "pid,ppid,comm"], { stdio: "pipe" });
124
+ let output = "";
125
+ proc.stdout?.on("data", (data) => {
126
+ output += data.toString();
127
+ });
128
+ proc.on("close", () => {
129
+ const lines = output.split("\n");
130
+ const pidsToKill = [];
131
+ lines.forEach((line) => {
132
+ const trimmed = line.trim();
133
+ if (trimmed.includes("opencode")) {
134
+ const parts = trimmed.split(/\s+/);
135
+ if (parts.length >= 3) {
136
+ const pid = parts[0];
137
+ const ppid = parts[1];
138
+ const comm = parts.slice(2).join(" ");
139
+ if (ppid === "1" && comm.includes("opencode")) {
140
+ pidsToKill.push(pid);
141
+ }
142
+ }
143
+ }
144
+ });
145
+ if (pidsToKill.length > 0) {
146
+ log.debug(`Found ${pidsToKill.length} orphan processes`, { pids: pidsToKill });
147
+ const killProc = spawn("kill", ["-9", ...pidsToKill], { stdio: "ignore" });
148
+ killProc.on("close", (code) => {
149
+ const killedCount = code === 0 ? pidsToKill.length : 0;
150
+ timer.end(
151
+ killedCount > 0 ? `\u2713 Killed ${killedCount} orphan processes` : "\u274C Failed to kill processes"
152
+ );
153
+ resolve(killedCount);
154
+ });
155
+ killProc.on("error", () => {
156
+ timer.end("\u274C Failed to kill processes");
157
+ resolve(0);
158
+ });
159
+ } else {
160
+ log.debug("No orphan processes found");
161
+ timer.end("No orphan processes found");
162
+ resolve(0);
163
+ }
164
+ });
165
+ proc.on("error", (err) => {
166
+ log.debug("Failed to find orphan processes", { error: err.message });
167
+ timer.end("\u274C Failed to find orphan processes");
168
+ resolve(0);
169
+ });
170
+ }
171
+ export {
172
+ checkOpenCodeInstalled,
173
+ getOpenCodeVersion,
174
+ killOrphanOpenCodeProcesses
175
+ };
package/es/types.d.ts ADDED
@@ -0,0 +1,160 @@
1
+ /**
2
+ * OpenCode Provider 专属类型
3
+ * 所有与 OpenCode Web 绑定的类型自包含于此,核心层不感知。
4
+ */
5
+ import type { LogFileConfig } from "@aipanel/core";
6
+ /**
7
+ * OpenCode 界面语言选项
8
+ */
9
+ export type OpenCodeLanguage = "en" | "zh" | "zht" | "ko" | "ja" | "de" | "es" | "fr" | "da" | "pl" | "ru" | "bs" | "ar" | "no" | "br" | "th" | "tr";
10
+ /**
11
+ * OpenCode 内部设置(与 localStorage settings.v3 对应)
12
+ * 用于配置 OpenCode Web 内部行为
13
+ */
14
+ export interface OpenCodeSettings {
15
+ /** 通用设置 */
16
+ general?: {
17
+ /** 自动保存 */
18
+ autoSave?: boolean;
19
+ /** 显示更新说明 */
20
+ releaseNotes?: boolean;
21
+ /** 后续动作模式 */
22
+ followup?: "steer" | "suggest" | "none";
23
+ /** 显示推理摘要 */
24
+ showReasoningSummaries?: boolean;
25
+ /** 默认展开 shell 工具部分 */
26
+ shellToolPartsExpanded?: boolean;
27
+ /** 默认展开编辑工具部分 */
28
+ editToolPartsExpanded?: boolean;
29
+ };
30
+ /** 外观设置 */
31
+ appearance?: {
32
+ /** 界面字体大小 */
33
+ fontSize?: number;
34
+ /** 代码字体 */
35
+ mono?: string;
36
+ /** 界面字体 */
37
+ sans?: string;
38
+ };
39
+ /** 权限设置 */
40
+ permissions?: {
41
+ /** 自动批准权限请求 */
42
+ autoApprove?: boolean;
43
+ };
44
+ /** 通知设置 */
45
+ notifications?: {
46
+ /** 智能体完成时通知 */
47
+ agent?: boolean;
48
+ /** 权限请求时通知 */
49
+ permissions?: boolean;
50
+ /** 错误时通知 */
51
+ errors?: boolean;
52
+ };
53
+ /** 音效设置 */
54
+ sounds?: {
55
+ /** 启用智能体音效 */
56
+ agentEnabled?: boolean;
57
+ /** 智能体音效 */
58
+ agent?: string;
59
+ /** 启用权限音效 */
60
+ permissionsEnabled?: boolean;
61
+ /** 权限音效 */
62
+ permissions?: string;
63
+ /** 启用错误音效 */
64
+ errorsEnabled?: boolean;
65
+ /** 错误音效 */
66
+ errors?: string;
67
+ };
68
+ }
69
+ /**
70
+ * OpenCode Provider 专属配置(对应插件配置的 providerOptions 段)
71
+ * 保留字符串索引签名,以赋给 PluginOptions 的 Record<string, unknown> 泛型约束。
72
+ */
73
+ export type OpenCodeProviderOptions = {
74
+ /** OpenCode 界面语言,默认跟随浏览器语言 */
75
+ language?: OpenCodeLanguage;
76
+ /** OpenCode 内部设置,直接映射到 localStorage settings.v3 */
77
+ settings?: OpenCodeSettings;
78
+ /** 自定义日志文件配置 */
79
+ logFiles?: LogFileConfig[];
80
+ /** 启用 LSP 诊断(TypeScript + ESLint),agent 编辑文件后自动返回错误信息,默认 false */
81
+ enableLsp?: boolean;
82
+ /** 启用 LSP 错误硬阻止:编辑后有错误则回滚文件并拒绝修改,默认 false */
83
+ enableBlockOnError?: boolean;
84
+ /** 启用代码格式化功能(prettier),默认 true */
85
+ enablePrettier?: boolean;
86
+ /** 允许 Provider 自定义扩展字段(schema 由具体 Provider 定义) */
87
+ [key: string]: unknown;
88
+ };
89
+ /**
90
+ * OpenCode Web 服务启动选项(进程管理内部类型)
91
+ */
92
+ export interface WebOptions {
93
+ /** 服务端口 */
94
+ port: number;
95
+ /** 服务主机名 */
96
+ hostname: string;
97
+ /** 服务器 URL */
98
+ serverUrl: string;
99
+ /** 工作目录 */
100
+ cwd: string;
101
+ /** 配置目录路径 */
102
+ configDir?: string;
103
+ /** CORS 允许的源 */
104
+ corsOrigins?: string[];
105
+ /** 上下文 API URL */
106
+ contextApiUrl?: string;
107
+ /** 进程日志 API URL */
108
+ logsApiUrl?: string;
109
+ /** 日志文件配置(JSON 字符串) */
110
+ logFilesJson?: string;
111
+ /** 启用 LSP 错误硬阻止(环境变量透传给 OpenCode 插件) */
112
+ enableBlockOnError?: boolean;
113
+ /** 启用 verbose 模式(环境变量透传,调试日志输出) */
114
+ verbose?: boolean;
115
+ /** 启用 LSP / 质量门禁(环境变量透传,控制 block-on-error 插件运行) */
116
+ enableLsp?: boolean;
117
+ /** 启用代码格式化功能(prettier) */
118
+ enablePrettier?: boolean;
119
+ /** Vue DevTools API 地址(环境变量透传给 OpenCode 插件) */
120
+ vueDevtoolsApiUrl?: string;
121
+ }
122
+ /**
123
+ * OpenCode 会话信息(REST API 原始返回)
124
+ */
125
+ export interface SessionInfo {
126
+ /** 会话 ID */
127
+ id: string;
128
+ /** 会话标识符 */
129
+ slug: string;
130
+ /** 项目 ID */
131
+ projectID: string;
132
+ /** 项目目录 */
133
+ directory: string;
134
+ /** 会话标题 */
135
+ title: string;
136
+ /** 版本号 */
137
+ version: string;
138
+ /** 会话 URL */
139
+ url?: string;
140
+ /** 父会话 ID(subagent 会话才有) */
141
+ parentID?: string;
142
+ /** 代码变更统计 */
143
+ summary: {
144
+ /** 新增行数 */
145
+ additions: number;
146
+ /** 删除行数 */
147
+ deletions: number;
148
+ /** 修改文件数 */
149
+ files: number;
150
+ };
151
+ /** 时间信息 */
152
+ time: {
153
+ /** 创建时间戳 */
154
+ created: number;
155
+ /** 更新时间戳 */
156
+ updated: number;
157
+ /** 归档时间戳(已归档会话才有) */
158
+ archived?: number;
159
+ };
160
+ }
package/es/types.js ADDED
File without changes
package/lib/api.cjs ADDED
@@ -0,0 +1,251 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
30
+ var api_exports = {};
31
+ __export(api_exports, {
32
+ OpenCodeAPI: () => OpenCodeAPI
33
+ });
34
+ module.exports = __toCommonJS(api_exports);
35
+ var import_http = __toESM(require("http"));
36
+ var import_core = require("@aipanel/core");
37
+ var import_node = require("@aipanel/core/node");
38
+ const log = (0, import_node.createLogger)("API");
39
+ class OpenCodeAPI {
40
+ constructor(hostname, getPort, getProxyPort, chromeDevtoolsPort = import_core.CHROME_DEVTOOLS_PORT) {
41
+ __publicField(this, "hostname", hostname);
42
+ __publicField(this, "getPort", getPort);
43
+ __publicField(this, "getProxyPort", getProxyPort);
44
+ __publicField(this, "chromeDevtoolsPort", chromeDevtoolsPort);
45
+ }
46
+ /** 构建代理 iframe URL(旧版格式:/{base64(projectDir)}/session/{id}) */
47
+ buildSessionProxyUrl(projectDir, sessionId) {
48
+ return `http://${this.hostname}:${this.getProxyPort()}/${(0, import_core.base64Encode)(projectDir)}/session/${sessionId}`;
49
+ }
50
+ createHttpRequest(options, body, timeout) {
51
+ const timer = new import_node.PerformanceTimer("HTTP Request", {
52
+ operation: `${options.method || "GET"} ${options.path}`
53
+ });
54
+ return new Promise((resolve, reject) => {
55
+ const req = import_http.default.request(options, (res) => {
56
+ let data = "";
57
+ res.on("data", (chunk) => data += chunk);
58
+ res.on("end", () => {
59
+ try {
60
+ const result = JSON.parse(data);
61
+ timer.end(`\u2713 Status: ${res.statusCode}`);
62
+ resolve(result);
63
+ } catch {
64
+ timer.end("\u274C JSON parse error");
65
+ reject(new Error(`JSON parse error: ${data.substring(0, 100)}`));
66
+ }
67
+ });
68
+ });
69
+ req.on("error", (e) => {
70
+ timer.end("\u274C Request failed");
71
+ reject(e);
72
+ });
73
+ if (timeout) {
74
+ req.setTimeout(timeout, () => {
75
+ timer.end("\u274C Request timeout");
76
+ req.destroy();
77
+ reject(new Error(`Request timeout after ${timeout}ms`));
78
+ });
79
+ }
80
+ if (body) req.write(body);
81
+ req.end();
82
+ });
83
+ }
84
+ async getSessions(projectDir, retries = import_core.DEFAULT_RETRIES) {
85
+ const timer = log.timer("getSessions", { retries, projectDir });
86
+ let lastError = null;
87
+ for (let i = 0; i < retries; i++) {
88
+ try {
89
+ log.debug(`Attempt ${i + 1}/${retries}`, { operation: "getSessions", projectDir });
90
+ const sessions = await this.createHttpRequest({
91
+ hostname: this.hostname,
92
+ port: this.getPort(),
93
+ path: `/session?directory=${encodeURIComponent(projectDir)}`
94
+ });
95
+ const sessionsWithUrl = sessions.map((s) => ({
96
+ ...s,
97
+ url: s.directory && s.id ? this.buildSessionProxyUrl(s.directory, s.id) : ""
98
+ }));
99
+ timer.end(`Found ${sessions.length} sessions`);
100
+ return sessionsWithUrl;
101
+ } catch (e) {
102
+ lastError = e instanceof Error ? e : new Error(String(e));
103
+ log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
104
+ operation: "getSessions"
105
+ });
106
+ if (i < retries - 1) {
107
+ log.debug(`Retrying in ${import_core.RETRY_DELAY}ms...`, {
108
+ operation: "getSessions"
109
+ });
110
+ await (0, import_core.sleep)(import_core.RETRY_DELAY);
111
+ }
112
+ }
113
+ }
114
+ timer.end("\u274C All retries exhausted");
115
+ throw lastError;
116
+ }
117
+ async createSession(projectDir, retries = import_core.DEFAULT_RETRIES, title) {
118
+ const timer = log.timer("createSession", { retries, title, projectDir });
119
+ let lastError = null;
120
+ for (let i = 0; i < retries; i++) {
121
+ try {
122
+ log.debug(`Attempt ${i + 1}/${retries}`, {
123
+ operation: "createSession",
124
+ title,
125
+ projectDir
126
+ });
127
+ const requestBody = title ? JSON.stringify({ title }) : void 0;
128
+ const session = await this.createHttpRequest(
129
+ {
130
+ hostname: this.hostname,
131
+ port: this.getPort(),
132
+ path: "/session",
133
+ method: "POST",
134
+ headers: {
135
+ ...requestBody ? { "Content-Type": "application/json" } : {}
136
+ }
137
+ },
138
+ requestBody
139
+ );
140
+ const sessionWithUrl = {
141
+ ...session,
142
+ url: this.buildSessionProxyUrl(projectDir, session.id)
143
+ };
144
+ timer.end(`Created session: ${session.id}`);
145
+ return sessionWithUrl;
146
+ } catch (e) {
147
+ lastError = e instanceof Error ? e : new Error(String(e));
148
+ log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
149
+ operation: "createSession"
150
+ });
151
+ if (i < retries - 1) {
152
+ log.debug(`Retrying in ${import_core.RETRY_DELAY}ms...`, {
153
+ operation: "createSession"
154
+ });
155
+ await (0, import_core.sleep)(import_core.RETRY_DELAY);
156
+ }
157
+ }
158
+ }
159
+ timer.end("\u274C All retries exhausted");
160
+ throw lastError;
161
+ }
162
+ async deleteSession(sessionId, retries = import_core.DEFAULT_RETRIES) {
163
+ const timer = log.timer("deleteSession", { sessionId, retries });
164
+ let lastError = null;
165
+ for (let i = 0; i < retries; i++) {
166
+ try {
167
+ log.debug(`Attempt ${i + 1}/${retries}`, {
168
+ operation: "deleteSession",
169
+ sessionId
170
+ });
171
+ await this.createHttpRequest({
172
+ hostname: this.hostname,
173
+ port: this.getPort(),
174
+ path: `/session/${sessionId}`,
175
+ method: "DELETE"
176
+ });
177
+ timer.end(`Deleted session: ${sessionId}`);
178
+ return;
179
+ } catch (e) {
180
+ lastError = e instanceof Error ? e : new Error(String(e));
181
+ log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
182
+ operation: "deleteSession",
183
+ sessionId
184
+ });
185
+ if (i < retries - 1) {
186
+ log.debug(`Retrying in ${import_core.RETRY_DELAY}ms...`, {
187
+ operation: "deleteSession",
188
+ sessionId
189
+ });
190
+ await (0, import_core.sleep)(import_core.RETRY_DELAY);
191
+ }
192
+ }
193
+ }
194
+ timer.end("\u274C All retries exhausted");
195
+ throw lastError;
196
+ }
197
+ async getToolIds(retries = import_core.DEFAULT_RETRIES) {
198
+ const timer = log.timer("getToolIds", { retries });
199
+ let lastError = null;
200
+ for (let i = 0; i < retries; i++) {
201
+ try {
202
+ log.debug(`Attempt ${i + 1}/${retries}`, {
203
+ operation: "getToolIds"
204
+ });
205
+ const toolIds = await this.createHttpRequest({
206
+ hostname: this.hostname,
207
+ port: this.getPort(),
208
+ path: "/experimental/tool/ids"
209
+ });
210
+ timer.end(`Found ${toolIds.length} tools`);
211
+ return toolIds;
212
+ } catch (e) {
213
+ lastError = e instanceof Error ? e : new Error(String(e));
214
+ log.debug(`Attempt ${i + 1} failed: ${lastError.message}`, {
215
+ operation: "getToolIds"
216
+ });
217
+ if (i < retries - 1) {
218
+ log.debug(`Retrying in ${import_core.RETRY_DELAY}ms...`, {
219
+ operation: "getToolIds"
220
+ });
221
+ await (0, import_core.sleep)(import_core.RETRY_DELAY);
222
+ }
223
+ }
224
+ }
225
+ timer.end("\u274C All retries exhausted");
226
+ throw lastError;
227
+ }
228
+ async getOrCreateSession(projectDir) {
229
+ const timer = log.timer("getOrCreateSession", { projectDir });
230
+ log.debug("Getting sessions...", { projectDir });
231
+ const sessions = await this.getSessions(projectDir);
232
+ log.debug(`Found ${sessions.length} sessions`, {
233
+ sessions: sessions.map((s) => ({ id: s.id, directory: s.directory }))
234
+ });
235
+ const matchingSession = sessions.find((s) => s.directory === projectDir);
236
+ if (matchingSession) {
237
+ const url2 = this.buildSessionProxyUrl(projectDir, matchingSession.id);
238
+ timer.end(`Using existing session: ${matchingSession.id}`);
239
+ return url2;
240
+ }
241
+ log.debug("Creating new session...", { projectDir });
242
+ const newSession = await this.createSession(projectDir);
243
+ const url = this.buildSessionProxyUrl(projectDir, newSession.id);
244
+ timer.end(`Created new session: ${newSession.id}`);
245
+ return url;
246
+ }
247
+ }
248
+ // Annotate the CommonJS export names for ESM import in node:
249
+ 0 && (module.exports = {
250
+ OpenCodeAPI
251
+ });
package/lib/api.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { SessionInfo } from "./types";
2
+ export declare class OpenCodeAPI {
3
+ private hostname;
4
+ private getPort;
5
+ private getProxyPort;
6
+ private chromeDevtoolsPort;
7
+ constructor(hostname: string, getPort: () => number, getProxyPort: () => number, chromeDevtoolsPort?: number);
8
+ /** 构建代理 iframe URL(旧版格式:/{base64(projectDir)}/session/{id}) */
9
+ buildSessionProxyUrl(projectDir: string, sessionId: string): string;
10
+ private createHttpRequest;
11
+ getSessions(projectDir: string, retries?: number): Promise<SessionInfo[]>;
12
+ createSession(projectDir: string, retries?: number, title?: string): Promise<SessionInfo>;
13
+ deleteSession(sessionId: string, retries?: number): Promise<void>;
14
+ getToolIds(retries?: number): Promise<string[]>;
15
+ getOrCreateSession(projectDir: string): Promise<string>;
16
+ }