@openacp/cli 2026.331.1 → 2026.331.2
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 +2 -1
- package/dist/cli.js +24932 -270
- package/dist/cli.js.map +1 -1
- package/dist/data/registry-snapshot.json +1 -1
- package/dist/index.js +17626 -409
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/adapter-ELG3VRZ3.js +0 -14
- package/dist/adapter-ELG3VRZ3.js.map +0 -1
- package/dist/agent-catalog-UYD26QDK.js +0 -10
- package/dist/agent-catalog-UYD26QDK.js.map +0 -1
- package/dist/agent-dependencies-ED2ZTUHG.js +0 -23
- package/dist/agent-dependencies-ED2ZTUHG.js.map +0 -1
- package/dist/agent-registry-YOGP656W.js +0 -8
- package/dist/agent-registry-YOGP656W.js.map +0 -1
- package/dist/agent-store-5UHZH2XI.js +0 -8
- package/dist/agent-store-5UHZH2XI.js.map +0 -1
- package/dist/api-client-PEMHYL5U.js +0 -13
- package/dist/api-client-PEMHYL5U.js.map +0 -1
- package/dist/api-server-DATG2KBR.js +0 -10
- package/dist/api-server-DATG2KBR.js.map +0 -1
- package/dist/api-server-L5Z7XACW.js +0 -7
- package/dist/api-server-L5Z7XACW.js.map +0 -1
- package/dist/autostart-CUPZMKKC.js +0 -22
- package/dist/autostart-CUPZMKKC.js.map +0 -1
- package/dist/chunk-23SRIVG4.js +0 -50
- package/dist/chunk-23SRIVG4.js.map +0 -1
- package/dist/chunk-2KT6TROD.js +0 -129
- package/dist/chunk-2KT6TROD.js.map +0 -1
- package/dist/chunk-2R5XM3ES.js +0 -154
- package/dist/chunk-2R5XM3ES.js.map +0 -1
- package/dist/chunk-3EWTPOF7.js +0 -51
- package/dist/chunk-3EWTPOF7.js.map +0 -1
- package/dist/chunk-566W6INH.js +0 -83
- package/dist/chunk-566W6INH.js.map +0 -1
- package/dist/chunk-5WGVYX3C.js +0 -55
- package/dist/chunk-5WGVYX3C.js.map +0 -1
- package/dist/chunk-7GXEMMEV.js +0 -44
- package/dist/chunk-7GXEMMEV.js.map +0 -1
- package/dist/chunk-7U6IZIJP.js +0 -186
- package/dist/chunk-7U6IZIJP.js.map +0 -1
- package/dist/chunk-7YIKTRSM.js +0 -105
- package/dist/chunk-7YIKTRSM.js.map +0 -1
- package/dist/chunk-7ZCQF6QM.js +0 -27
- package/dist/chunk-7ZCQF6QM.js.map +0 -1
- package/dist/chunk-AFKX424Q.js +0 -92
- package/dist/chunk-AFKX424Q.js.map +0 -1
- package/dist/chunk-BYCJQPMN.js +0 -543
- package/dist/chunk-BYCJQPMN.js.map +0 -1
- package/dist/chunk-CDAUYTVP.js +0 -41
- package/dist/chunk-CDAUYTVP.js.map +0 -1
- package/dist/chunk-EWVXSTQK.js +0 -6544
- package/dist/chunk-EWVXSTQK.js.map +0 -1
- package/dist/chunk-FNRSWA2K.js +0 -1
- package/dist/chunk-FNRSWA2K.js.map +0 -1
- package/dist/chunk-FPKQYCQS.js +0 -776
- package/dist/chunk-FPKQYCQS.js.map +0 -1
- package/dist/chunk-IZ5UEZF7.js +0 -138
- package/dist/chunk-IZ5UEZF7.js.map +0 -1
- package/dist/chunk-K6UY5M75.js +0 -653
- package/dist/chunk-K6UY5M75.js.map +0 -1
- package/dist/chunk-KGAQW6F4.js +0 -106
- package/dist/chunk-KGAQW6F4.js.map +0 -1
- package/dist/chunk-LGFWH3AE.js +0 -26
- package/dist/chunk-LGFWH3AE.js.map +0 -1
- package/dist/chunk-LRV56K2M.js +0 -4106
- package/dist/chunk-LRV56K2M.js.map +0 -1
- package/dist/chunk-MDJHCCFS.js +0 -485
- package/dist/chunk-MDJHCCFS.js.map +0 -1
- package/dist/chunk-MLF4W5R6.js +0 -101
- package/dist/chunk-MLF4W5R6.js.map +0 -1
- package/dist/chunk-NHD5XDD2.js +0 -686
- package/dist/chunk-NHD5XDD2.js.map +0 -1
- package/dist/chunk-NJX75BLK.js +0 -259
- package/dist/chunk-NJX75BLK.js.map +0 -1
- package/dist/chunk-NOEAJNTK.js +0 -156
- package/dist/chunk-NOEAJNTK.js.map +0 -1
- package/dist/chunk-ON7HB5O7.js +0 -58
- package/dist/chunk-ON7HB5O7.js.map +0 -1
- package/dist/chunk-OSBZXY2W.js +0 -126
- package/dist/chunk-OSBZXY2W.js.map +0 -1
- package/dist/chunk-OYSAN7UX.js +0 -15
- package/dist/chunk-OYSAN7UX.js.map +0 -1
- package/dist/chunk-P3HHJANC.js +0 -209
- package/dist/chunk-P3HHJANC.js.map +0 -1
- package/dist/chunk-R2YLDQLI.js +0 -1115
- package/dist/chunk-R2YLDQLI.js.map +0 -1
- package/dist/chunk-R6KZYF7D.js +0 -231
- package/dist/chunk-R6KZYF7D.js.map +0 -1
- package/dist/chunk-S64CB6J3.js +0 -98
- package/dist/chunk-S64CB6J3.js.map +0 -1
- package/dist/chunk-SSLVNCEA.js +0 -236
- package/dist/chunk-SSLVNCEA.js.map +0 -1
- package/dist/chunk-TGP34LQN.js +0 -681
- package/dist/chunk-TGP34LQN.js.map +0 -1
- package/dist/chunk-VUSCVRJL.js +0 -229
- package/dist/chunk-VUSCVRJL.js.map +0 -1
- package/dist/chunk-W26AUH5B.js +0 -61
- package/dist/chunk-W26AUH5B.js.map +0 -1
- package/dist/chunk-WQCJTU2C.js +0 -84
- package/dist/chunk-WQCJTU2C.js.map +0 -1
- package/dist/chunk-XRJUS6FE.js +0 -53
- package/dist/chunk-XRJUS6FE.js.map +0 -1
- package/dist/chunk-YZCKSNRN.js +0 -453
- package/dist/chunk-YZCKSNRN.js.map +0 -1
- package/dist/chunk-ZIRH6QWW.js +0 -69
- package/dist/chunk-ZIRH6QWW.js.map +0 -1
- package/dist/chunk-ZSLHHQPQ.js +0 -282
- package/dist/chunk-ZSLHHQPQ.js.map +0 -1
- package/dist/config-X4UP7H6R.js +0 -13
- package/dist/config-X4UP7H6R.js.map +0 -1
- package/dist/config-editor-7BENRVG5.js +0 -11
- package/dist/config-editor-7BENRVG5.js.map +0 -1
- package/dist/config-registry-M3FFWEVM.js +0 -18
- package/dist/config-registry-M3FFWEVM.js.map +0 -1
- package/dist/context-FVGCU5TI.js +0 -9
- package/dist/context-FVGCU5TI.js.map +0 -1
- package/dist/core-plugins-JSY2I44L.js +0 -25
- package/dist/core-plugins-JSY2I44L.js.map +0 -1
- package/dist/daemon-UOSRDEXW.js +0 -34
- package/dist/daemon-UOSRDEXW.js.map +0 -1
- package/dist/dev-loader-7P3HZCIA.js +0 -37
- package/dist/dev-loader-7P3HZCIA.js.map +0 -1
- package/dist/doctor-6DLACBR4.js +0 -10
- package/dist/doctor-6DLACBR4.js.map +0 -1
- package/dist/file-service-FQQYME7M.js +0 -8
- package/dist/file-service-FQQYME7M.js.map +0 -1
- package/dist/install-cloudflared-LNS5L5FR.js +0 -33
- package/dist/install-cloudflared-LNS5L5FR.js.map +0 -1
- package/dist/install-context-KZO5FR4D.js +0 -78
- package/dist/install-context-KZO5FR4D.js.map +0 -1
- package/dist/install-jq-SN4IA5K4.js +0 -31
- package/dist/install-jq-SN4IA5K4.js.map +0 -1
- package/dist/instance-context-FLCE7VZ4.js +0 -13
- package/dist/instance-context-FLCE7VZ4.js.map +0 -1
- package/dist/instance-registry-SW5FWKHO.js +0 -7
- package/dist/instance-registry-SW5FWKHO.js.map +0 -1
- package/dist/integrate-JIEZYDOR.js +0 -371
- package/dist/integrate-JIEZYDOR.js.map +0 -1
- package/dist/log-YZ243M5G.js +0 -25
- package/dist/log-YZ243M5G.js.map +0 -1
- package/dist/main-D7M2AKRM.js +0 -697
- package/dist/main-D7M2AKRM.js.map +0 -1
- package/dist/menu-ALFN37IR.js +0 -15
- package/dist/menu-ALFN37IR.js.map +0 -1
- package/dist/notifications-MO23S7S3.js +0 -8
- package/dist/notifications-MO23S7S3.js.map +0 -1
- package/dist/plugin-create-HFKS23JY.js +0 -968
- package/dist/plugin-create-HFKS23JY.js.map +0 -1
- package/dist/plugin-installer-VSTYZSXC.js +0 -9
- package/dist/plugin-installer-VSTYZSXC.js.map +0 -1
- package/dist/plugin-registry-6J3YSFHF.js +0 -7
- package/dist/plugin-registry-6J3YSFHF.js.map +0 -1
- package/dist/plugin-search-MGKAL5JM.js +0 -39
- package/dist/plugin-search-MGKAL5JM.js.map +0 -1
- package/dist/post-upgrade-F4YPMTUT.js +0 -79
- package/dist/post-upgrade-F4YPMTUT.js.map +0 -1
- package/dist/read-text-file-DJBTITIB.js +0 -7
- package/dist/read-text-file-DJBTITIB.js.map +0 -1
- package/dist/registry-client-GTBWLXYU.js +0 -7
- package/dist/registry-client-GTBWLXYU.js.map +0 -1
- package/dist/security-O4XGN2CM.js +0 -8
- package/dist/security-O4XGN2CM.js.map +0 -1
- package/dist/settings-manager-B4UN2LAC.js +0 -7
- package/dist/settings-manager-B4UN2LAC.js.map +0 -1
- package/dist/setup-44WLBIOT.js +0 -989
- package/dist/setup-44WLBIOT.js.map +0 -1
- package/dist/speech-GHTSWDAN.js +0 -9
- package/dist/speech-GHTSWDAN.js.map +0 -1
- package/dist/suggest-RST5VOHB.js +0 -36
- package/dist/suggest-RST5VOHB.js.map +0 -1
- package/dist/telegram-D7ASLVEB.js +0 -7
- package/dist/telegram-D7ASLVEB.js.map +0 -1
- package/dist/tunnel-ALJDPFDQ.js +0 -10
- package/dist/tunnel-ALJDPFDQ.js.map +0 -1
- package/dist/tunnel-service-TBAHDXMF.js +0 -755
- package/dist/tunnel-service-TBAHDXMF.js.map +0 -1
- package/dist/validators-GITLOFXC.js +0 -11
- package/dist/validators-GITLOFXC.js.map +0 -1
- package/dist/version-AXXV6IV2.js +0 -15
- package/dist/version-AXXV6IV2.js.map +0 -1
|
@@ -1,755 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TunnelRegistry
|
|
3
|
-
} from "./chunk-NHD5XDD2.js";
|
|
4
|
-
import {
|
|
5
|
-
createChildLogger
|
|
6
|
-
} from "./chunk-R6KZYF7D.js";
|
|
7
|
-
import "./chunk-ZSLHHQPQ.js";
|
|
8
|
-
|
|
9
|
-
// src/plugins/tunnel/tunnel-service.ts
|
|
10
|
-
import { serve } from "@hono/node-server";
|
|
11
|
-
|
|
12
|
-
// src/plugins/tunnel/viewer-store.ts
|
|
13
|
-
import * as fs from "fs";
|
|
14
|
-
import * as path from "path";
|
|
15
|
-
import { nanoid } from "nanoid";
|
|
16
|
-
var log = createChildLogger({ module: "viewer-store" });
|
|
17
|
-
var MAX_CONTENT_SIZE = 1e6;
|
|
18
|
-
var EXTENSION_LANGUAGE = {
|
|
19
|
-
".ts": "typescript",
|
|
20
|
-
".tsx": "typescript",
|
|
21
|
-
".js": "javascript",
|
|
22
|
-
".jsx": "javascript",
|
|
23
|
-
".py": "python",
|
|
24
|
-
".rs": "rust",
|
|
25
|
-
".go": "go",
|
|
26
|
-
".java": "java",
|
|
27
|
-
".kt": "kotlin",
|
|
28
|
-
".rb": "ruby",
|
|
29
|
-
".php": "php",
|
|
30
|
-
".c": "c",
|
|
31
|
-
".cpp": "cpp",
|
|
32
|
-
".h": "c",
|
|
33
|
-
".hpp": "cpp",
|
|
34
|
-
".cs": "csharp",
|
|
35
|
-
".swift": "swift",
|
|
36
|
-
".sh": "bash",
|
|
37
|
-
".zsh": "bash",
|
|
38
|
-
".bash": "bash",
|
|
39
|
-
".json": "json",
|
|
40
|
-
".yaml": "yaml",
|
|
41
|
-
".yml": "yaml",
|
|
42
|
-
".toml": "toml",
|
|
43
|
-
".xml": "xml",
|
|
44
|
-
".html": "html",
|
|
45
|
-
".css": "css",
|
|
46
|
-
".scss": "scss",
|
|
47
|
-
".sql": "sql",
|
|
48
|
-
".md": "markdown",
|
|
49
|
-
".dockerfile": "dockerfile",
|
|
50
|
-
".tf": "hcl",
|
|
51
|
-
".vue": "xml",
|
|
52
|
-
".svelte": "xml"
|
|
53
|
-
};
|
|
54
|
-
var ViewerStore = class {
|
|
55
|
-
entries = /* @__PURE__ */ new Map();
|
|
56
|
-
cleanupTimer;
|
|
57
|
-
ttlMs;
|
|
58
|
-
constructor(ttlMinutes = 60) {
|
|
59
|
-
this.ttlMs = ttlMinutes * 60 * 1e3;
|
|
60
|
-
this.cleanupTimer = setInterval(() => this.cleanup(), 5 * 60 * 1e3);
|
|
61
|
-
}
|
|
62
|
-
storeFile(sessionId, filePath, content, workingDirectory) {
|
|
63
|
-
if (!this.isPathAllowed(filePath, workingDirectory)) {
|
|
64
|
-
log.warn({ filePath, workingDirectory }, "Path outside workspace, rejecting");
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
if (content.length > MAX_CONTENT_SIZE) {
|
|
68
|
-
log.debug({ filePath, size: content.length }, "File too large for viewer");
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
const id = nanoid(12);
|
|
72
|
-
const now = Date.now();
|
|
73
|
-
this.entries.set(id, {
|
|
74
|
-
id,
|
|
75
|
-
type: "file",
|
|
76
|
-
filePath,
|
|
77
|
-
content,
|
|
78
|
-
language: this.detectLanguage(filePath),
|
|
79
|
-
sessionId,
|
|
80
|
-
workingDirectory,
|
|
81
|
-
createdAt: now,
|
|
82
|
-
expiresAt: now + this.ttlMs
|
|
83
|
-
});
|
|
84
|
-
log.debug({ id, filePath }, "Stored file for viewing");
|
|
85
|
-
return id;
|
|
86
|
-
}
|
|
87
|
-
storeDiff(sessionId, filePath, oldContent, newContent, workingDirectory) {
|
|
88
|
-
if (!this.isPathAllowed(filePath, workingDirectory)) {
|
|
89
|
-
log.warn({ filePath, workingDirectory }, "Path outside workspace, rejecting");
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
const combined = oldContent.length + newContent.length;
|
|
93
|
-
if (combined > MAX_CONTENT_SIZE) {
|
|
94
|
-
log.debug({ filePath, size: combined }, "Diff content too large for viewer");
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
const id = nanoid(12);
|
|
98
|
-
const now = Date.now();
|
|
99
|
-
this.entries.set(id, {
|
|
100
|
-
id,
|
|
101
|
-
type: "diff",
|
|
102
|
-
filePath,
|
|
103
|
-
content: newContent,
|
|
104
|
-
oldContent,
|
|
105
|
-
language: this.detectLanguage(filePath),
|
|
106
|
-
sessionId,
|
|
107
|
-
workingDirectory,
|
|
108
|
-
createdAt: now,
|
|
109
|
-
expiresAt: now + this.ttlMs
|
|
110
|
-
});
|
|
111
|
-
log.debug({ id, filePath }, "Stored diff for viewing");
|
|
112
|
-
return id;
|
|
113
|
-
}
|
|
114
|
-
storeOutput(sessionId, label, output) {
|
|
115
|
-
if (output.length > MAX_CONTENT_SIZE) {
|
|
116
|
-
log.debug({ label, size: output.length }, "Output too large for viewer");
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
const id = nanoid(12);
|
|
120
|
-
const now = Date.now();
|
|
121
|
-
this.entries.set(id, {
|
|
122
|
-
id,
|
|
123
|
-
type: "output",
|
|
124
|
-
filePath: label,
|
|
125
|
-
content: output,
|
|
126
|
-
language: "text",
|
|
127
|
-
sessionId,
|
|
128
|
-
workingDirectory: "",
|
|
129
|
-
createdAt: now,
|
|
130
|
-
expiresAt: now + this.ttlMs
|
|
131
|
-
});
|
|
132
|
-
log.debug({ id, label }, "Stored output for viewing");
|
|
133
|
-
return id;
|
|
134
|
-
}
|
|
135
|
-
get(id) {
|
|
136
|
-
const entry = this.entries.get(id);
|
|
137
|
-
if (!entry) return void 0;
|
|
138
|
-
if (Date.now() > entry.expiresAt) {
|
|
139
|
-
this.entries.delete(id);
|
|
140
|
-
return void 0;
|
|
141
|
-
}
|
|
142
|
-
return entry;
|
|
143
|
-
}
|
|
144
|
-
cleanup() {
|
|
145
|
-
const now = Date.now();
|
|
146
|
-
let removed = 0;
|
|
147
|
-
for (const [id, entry] of this.entries) {
|
|
148
|
-
if (now > entry.expiresAt) {
|
|
149
|
-
this.entries.delete(id);
|
|
150
|
-
removed++;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (removed > 0) {
|
|
154
|
-
log.debug({ removed, remaining: this.entries.size }, "Cleaned up expired viewer entries");
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
isPathAllowed(filePath, workingDirectory) {
|
|
158
|
-
const caseInsensitive = process.platform === "darwin" || process.platform === "win32";
|
|
159
|
-
let resolved;
|
|
160
|
-
let workspace;
|
|
161
|
-
try {
|
|
162
|
-
resolved = fs.realpathSync(path.resolve(workingDirectory, filePath));
|
|
163
|
-
} catch {
|
|
164
|
-
resolved = path.resolve(workingDirectory, filePath);
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
workspace = fs.realpathSync(path.resolve(workingDirectory));
|
|
168
|
-
} catch {
|
|
169
|
-
workspace = path.resolve(workingDirectory);
|
|
170
|
-
}
|
|
171
|
-
if (caseInsensitive) {
|
|
172
|
-
const rLower = resolved.toLowerCase();
|
|
173
|
-
const wLower = workspace.toLowerCase();
|
|
174
|
-
return rLower.startsWith(wLower + path.sep) || rLower === wLower;
|
|
175
|
-
}
|
|
176
|
-
return resolved.startsWith(workspace + path.sep) || resolved === workspace;
|
|
177
|
-
}
|
|
178
|
-
detectLanguage(filePath) {
|
|
179
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
180
|
-
return EXTENSION_LANGUAGE[ext];
|
|
181
|
-
}
|
|
182
|
-
destroy() {
|
|
183
|
-
clearInterval(this.cleanupTimer);
|
|
184
|
-
this.entries.clear();
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// src/plugins/tunnel/server.ts
|
|
189
|
-
import { Hono } from "hono";
|
|
190
|
-
|
|
191
|
-
// src/plugins/tunnel/templates/file-viewer.ts
|
|
192
|
-
function escapeHtml(text) {
|
|
193
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
194
|
-
}
|
|
195
|
-
var MONACO_LANGUAGE = {
|
|
196
|
-
typescript: "typescript",
|
|
197
|
-
javascript: "javascript",
|
|
198
|
-
python: "python",
|
|
199
|
-
rust: "rust",
|
|
200
|
-
go: "go",
|
|
201
|
-
java: "java",
|
|
202
|
-
kotlin: "kotlin",
|
|
203
|
-
ruby: "ruby",
|
|
204
|
-
php: "php",
|
|
205
|
-
c: "c",
|
|
206
|
-
cpp: "cpp",
|
|
207
|
-
csharp: "csharp",
|
|
208
|
-
swift: "swift",
|
|
209
|
-
bash: "shell",
|
|
210
|
-
json: "json",
|
|
211
|
-
yaml: "yaml",
|
|
212
|
-
toml: "ini",
|
|
213
|
-
xml: "xml",
|
|
214
|
-
html: "html",
|
|
215
|
-
css: "css",
|
|
216
|
-
scss: "scss",
|
|
217
|
-
sql: "sql",
|
|
218
|
-
markdown: "markdown",
|
|
219
|
-
dockerfile: "dockerfile",
|
|
220
|
-
hcl: "hcl",
|
|
221
|
-
plaintext: "plaintext"
|
|
222
|
-
};
|
|
223
|
-
function getMonacoLang(lang) {
|
|
224
|
-
if (!lang) return "plaintext";
|
|
225
|
-
return MONACO_LANGUAGE[lang] || "plaintext";
|
|
226
|
-
}
|
|
227
|
-
function renderFileViewer(entry) {
|
|
228
|
-
const fileName = entry.filePath || "untitled";
|
|
229
|
-
const lang = getMonacoLang(entry.language);
|
|
230
|
-
const safeContent = JSON.stringify(entry.content).replace(/<\//g, "<\\/");
|
|
231
|
-
return `<!DOCTYPE html>
|
|
232
|
-
<html lang="en">
|
|
233
|
-
<head>
|
|
234
|
-
<meta charset="UTF-8">
|
|
235
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
236
|
-
<title>${escapeHtml(fileName)} - OpenACP</title>
|
|
237
|
-
<style>
|
|
238
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
239
|
-
body { background: #1e1e1e; color: #d4d4d4; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; display: flex; flex-direction: column; height: 100vh; }
|
|
240
|
-
.header { background: #252526; border-bottom: 1px solid #3c3c3c; padding: 8px 16px; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; z-index: 10; }
|
|
241
|
-
.file-info { display: flex; align-items: center; gap: 8px; font-size: 13px; min-width: 0; }
|
|
242
|
-
.file-icon { font-size: 14px; flex-shrink: 0; }
|
|
243
|
-
.file-path { color: #969696; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
244
|
-
.file-name { color: #e0e0e0; font-weight: 500; flex-shrink: 0; }
|
|
245
|
-
.actions { display: flex; gap: 6px; flex-shrink: 0; }
|
|
246
|
-
.btn { background: #3c3c3c; color: #d4d4d4; border: 1px solid #505050; padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; transition: background 0.15s; }
|
|
247
|
-
.btn:hover { background: #505050; }
|
|
248
|
-
.btn.active { background: #0e639c; border-color: #1177bb; }
|
|
249
|
-
#editor-container { flex: 1; overflow: hidden; }
|
|
250
|
-
.status-bar { background: #007acc; color: #fff; padding: 2px 16px; font-size: 12px; display: flex; justify-content: space-between; flex-shrink: 0; }
|
|
251
|
-
</style>
|
|
252
|
-
</head>
|
|
253
|
-
<body>
|
|
254
|
-
<div class="header">
|
|
255
|
-
<div class="file-info">
|
|
256
|
-
<span class="file-icon">\u{1F4C4}</span>
|
|
257
|
-
${formatBreadcrumb(fileName)}
|
|
258
|
-
</div>
|
|
259
|
-
<div class="actions">
|
|
260
|
-
${lang === "markdown" ? '<button class="btn" onclick="togglePreview()" id="btn-preview">Preview</button>' : ""}
|
|
261
|
-
<button class="btn" onclick="toggleWordWrap()" id="btn-wrap">Wrap</button>
|
|
262
|
-
<button class="btn" onclick="toggleMinimap()" id="btn-minimap">Minimap</button>
|
|
263
|
-
<button class="btn" onclick="toggleTheme()" id="btn-theme">Light</button>
|
|
264
|
-
<button class="btn" onclick="copyCode()">Copy</button>
|
|
265
|
-
</div>
|
|
266
|
-
</div>
|
|
267
|
-
<div id="editor-container"></div>
|
|
268
|
-
<div id="preview-wrapper" style="display:none; flex:1; overflow-y:auto;">
|
|
269
|
-
<div id="preview-container" style="padding:24px 48px; max-width:900px; margin:0 auto; width:100%;"></div>
|
|
270
|
-
</div>
|
|
271
|
-
<div class="status-bar">
|
|
272
|
-
<span>${escapeHtml(entry.language || "plaintext")} | ${entry.content.split("\n").length} lines</span>
|
|
273
|
-
<span>OpenACP Viewer (read-only)</span>
|
|
274
|
-
</div>
|
|
275
|
-
|
|
276
|
-
${lang === "markdown" ? '<script src="https://cdn.jsdelivr.net/npm/marked@15.0.0/marked.min.js"></script>' : ""}
|
|
277
|
-
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/loader.js"></script>
|
|
278
|
-
<script>
|
|
279
|
-
const content = ${safeContent};
|
|
280
|
-
const lang = ${JSON.stringify(lang)};
|
|
281
|
-
let editor;
|
|
282
|
-
let isDark = true;
|
|
283
|
-
let wordWrap = false;
|
|
284
|
-
let minimap = true;
|
|
285
|
-
|
|
286
|
-
require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs' } });
|
|
287
|
-
require(['vs/editor/editor.main'], function () {
|
|
288
|
-
editor = monaco.editor.create(document.getElementById('editor-container'), {
|
|
289
|
-
value: content,
|
|
290
|
-
language: lang,
|
|
291
|
-
theme: 'vs-dark',
|
|
292
|
-
readOnly: true,
|
|
293
|
-
automaticLayout: true,
|
|
294
|
-
minimap: { enabled: true },
|
|
295
|
-
scrollBeyondLastLine: false,
|
|
296
|
-
fontSize: 13,
|
|
297
|
-
fontFamily: "'SF Mono', SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace",
|
|
298
|
-
lineNumbers: 'on',
|
|
299
|
-
renderLineHighlight: 'all',
|
|
300
|
-
wordWrap: 'off',
|
|
301
|
-
padding: { top: 8 },
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
// Handle line range from URL hash: #L42 or #L42-L55
|
|
305
|
-
function highlightFromHash() {
|
|
306
|
-
const hash = location.hash.slice(1);
|
|
307
|
-
const match = hash.match(/^L(\\d+)(?:-L?(\\d+))?$/);
|
|
308
|
-
if (!match) return;
|
|
309
|
-
const startLine = parseInt(match[1], 10);
|
|
310
|
-
const endLine = match[2] ? parseInt(match[2], 10) : startLine;
|
|
311
|
-
editor.revealLineInCenter(startLine);
|
|
312
|
-
editor.setSelection(new monaco.Selection(startLine, 1, endLine + 1, 1));
|
|
313
|
-
}
|
|
314
|
-
highlightFromHash();
|
|
315
|
-
window.addEventListener('hashchange', highlightFromHash);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
function toggleTheme() {
|
|
319
|
-
isDark = !isDark;
|
|
320
|
-
monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs');
|
|
321
|
-
document.body.style.background = isDark ? '#1e1e1e' : '#ffffff';
|
|
322
|
-
document.querySelector('.header').style.background = isDark ? '#252526' : '#f3f3f3';
|
|
323
|
-
document.querySelector('.header').style.borderColor = isDark ? '#3c3c3c' : '#e0e0e0';
|
|
324
|
-
document.getElementById('btn-theme').textContent = isDark ? 'Light' : 'Dark';
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function toggleWordWrap() {
|
|
328
|
-
wordWrap = !wordWrap;
|
|
329
|
-
editor.updateOptions({ wordWrap: wordWrap ? 'on' : 'off' });
|
|
330
|
-
document.getElementById('btn-wrap').classList.toggle('active', wordWrap);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function toggleMinimap() {
|
|
334
|
-
minimap = !minimap;
|
|
335
|
-
editor.updateOptions({ minimap: { enabled: minimap } });
|
|
336
|
-
document.getElementById('btn-minimap').classList.toggle('active', !minimap);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function copyCode() {
|
|
340
|
-
navigator.clipboard.writeText(content).then(() => {
|
|
341
|
-
const btn = event.target;
|
|
342
|
-
btn.textContent = 'Copied!';
|
|
343
|
-
setTimeout(() => btn.textContent = 'Copy', 2000);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
let previewMode = false;
|
|
348
|
-
function togglePreview() {
|
|
349
|
-
previewMode = !previewMode;
|
|
350
|
-
const editorEl = document.getElementById('editor-container');
|
|
351
|
-
const wrapperEl = document.getElementById('preview-wrapper');
|
|
352
|
-
const previewEl = document.getElementById('preview-container');
|
|
353
|
-
const btn = document.getElementById('btn-preview');
|
|
354
|
-
if (previewMode) {
|
|
355
|
-
editorEl.style.display = 'none';
|
|
356
|
-
wrapperEl.style.display = 'block';
|
|
357
|
-
previewEl.innerHTML = typeof marked !== 'undefined' ? marked.parse(content) : content.replace(/\\n/g, '<br>');
|
|
358
|
-
previewEl.style.color = isDark ? '#d4d4d4' : '#1e1e1e';
|
|
359
|
-
wrapperEl.style.background = isDark ? '#1e1e1e' : '#ffffff';
|
|
360
|
-
btn.classList.add('active');
|
|
361
|
-
btn.textContent = 'Editor';
|
|
362
|
-
} else {
|
|
363
|
-
editorEl.style.display = 'block';
|
|
364
|
-
wrapperEl.style.display = 'none';
|
|
365
|
-
btn.classList.remove('active');
|
|
366
|
-
btn.textContent = 'Preview';
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
</script>
|
|
370
|
-
<style>
|
|
371
|
-
#preview-container { font-size: 15px; line-height: 1.7; }
|
|
372
|
-
#preview-container h1 { font-size: 2em; margin: 0.5em 0 0.3em; border-bottom: 1px solid #3c3c3c; padding-bottom: 0.3em; }
|
|
373
|
-
#preview-container h2 { font-size: 1.5em; margin: 0.5em 0 0.3em; border-bottom: 1px solid #3c3c3c; padding-bottom: 0.2em; }
|
|
374
|
-
#preview-container h3 { font-size: 1.25em; margin: 0.4em 0 0.2em; }
|
|
375
|
-
#preview-container p { margin: 0.5em 0; }
|
|
376
|
-
#preview-container code { background: rgba(128,128,128,0.2); padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }
|
|
377
|
-
#preview-container pre { background: rgba(0,0,0,0.3); padding: 16px; border-radius: 6px; overflow-x: auto; margin: 0.5em 0; }
|
|
378
|
-
#preview-container pre code { background: none; padding: 0; }
|
|
379
|
-
#preview-container blockquote { border-left: 3px solid #505050; padding-left: 16px; margin: 0.5em 0; color: #969696; }
|
|
380
|
-
#preview-container ul, #preview-container ol { padding-left: 24px; margin: 0.5em 0; }
|
|
381
|
-
#preview-container table { border-collapse: collapse; margin: 0.5em 0; width: 100%; }
|
|
382
|
-
#preview-container th, #preview-container td { border: 1px solid #3c3c3c; padding: 6px 12px; text-align: left; }
|
|
383
|
-
#preview-container th { background: rgba(128,128,128,0.15); }
|
|
384
|
-
#preview-container a { color: #3794ff; }
|
|
385
|
-
#preview-container img { max-width: 100%; }
|
|
386
|
-
</style>
|
|
387
|
-
</body>
|
|
388
|
-
</html>`;
|
|
389
|
-
}
|
|
390
|
-
function formatBreadcrumb(filePath) {
|
|
391
|
-
const parts = filePath.split("/");
|
|
392
|
-
if (parts.length <= 1) return `<span class="file-name">${escapeHtml(filePath)}</span>`;
|
|
393
|
-
const dir = parts.slice(0, -1).join(" / ");
|
|
394
|
-
const name = parts[parts.length - 1];
|
|
395
|
-
return `<span class="file-path">${escapeHtml(dir)} /</span> <span class="file-name">${escapeHtml(name)}</span>`;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// src/plugins/tunnel/templates/diff-viewer.ts
|
|
399
|
-
import { createPatch } from "diff";
|
|
400
|
-
function escapeHtml2(text) {
|
|
401
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
402
|
-
}
|
|
403
|
-
var MONACO_LANGUAGE2 = {
|
|
404
|
-
typescript: "typescript",
|
|
405
|
-
javascript: "javascript",
|
|
406
|
-
python: "python",
|
|
407
|
-
rust: "rust",
|
|
408
|
-
go: "go",
|
|
409
|
-
java: "java",
|
|
410
|
-
kotlin: "kotlin",
|
|
411
|
-
ruby: "ruby",
|
|
412
|
-
php: "php",
|
|
413
|
-
c: "c",
|
|
414
|
-
cpp: "cpp",
|
|
415
|
-
csharp: "csharp",
|
|
416
|
-
swift: "swift",
|
|
417
|
-
bash: "shell",
|
|
418
|
-
json: "json",
|
|
419
|
-
yaml: "yaml",
|
|
420
|
-
toml: "ini",
|
|
421
|
-
xml: "xml",
|
|
422
|
-
html: "html",
|
|
423
|
-
css: "css",
|
|
424
|
-
scss: "scss",
|
|
425
|
-
sql: "sql",
|
|
426
|
-
markdown: "markdown",
|
|
427
|
-
dockerfile: "dockerfile",
|
|
428
|
-
hcl: "hcl",
|
|
429
|
-
plaintext: "plaintext"
|
|
430
|
-
};
|
|
431
|
-
function getMonacoLang2(lang) {
|
|
432
|
-
if (!lang) return "plaintext";
|
|
433
|
-
return MONACO_LANGUAGE2[lang] || "plaintext";
|
|
434
|
-
}
|
|
435
|
-
function renderDiffViewer(entry) {
|
|
436
|
-
const fileName = entry.filePath || "untitled";
|
|
437
|
-
const lang = getMonacoLang2(entry.language);
|
|
438
|
-
const oldContent = entry.oldContent || "";
|
|
439
|
-
const newContent = entry.content;
|
|
440
|
-
const patch = createPatch(fileName, oldContent, newContent, "before", "after");
|
|
441
|
-
const adds = (patch.match(/^\+[^+]/gm) || []).length;
|
|
442
|
-
const dels = (patch.match(/^-[^-]/gm) || []).length;
|
|
443
|
-
const safeOld = JSON.stringify(oldContent).replace(/<\//g, "<\\/");
|
|
444
|
-
const safeNew = JSON.stringify(newContent).replace(/<\//g, "<\\/");
|
|
445
|
-
return `<!DOCTYPE html>
|
|
446
|
-
<html lang="en">
|
|
447
|
-
<head>
|
|
448
|
-
<meta charset="UTF-8">
|
|
449
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
450
|
-
<title>${escapeHtml2(fileName)} (diff) - OpenACP</title>
|
|
451
|
-
<style>
|
|
452
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
453
|
-
body { background: #1e1e1e; color: #d4d4d4; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; display: flex; flex-direction: column; height: 100vh; }
|
|
454
|
-
.header { background: #252526; border-bottom: 1px solid #3c3c3c; padding: 8px 16px; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; z-index: 10; }
|
|
455
|
-
.file-info { display: flex; align-items: center; gap: 8px; font-size: 13px; }
|
|
456
|
-
.file-icon { font-size: 14px; }
|
|
457
|
-
.file-name { color: #e0e0e0; font-weight: 500; }
|
|
458
|
-
.stats { font-size: 12px; margin-left: 12px; }
|
|
459
|
-
.stats .add { color: #4ec9b0; }
|
|
460
|
-
.stats .del { color: #f14c4c; }
|
|
461
|
-
.actions { display: flex; gap: 6px; }
|
|
462
|
-
.btn { background: #3c3c3c; color: #d4d4d4; border: 1px solid #505050; padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; transition: background 0.15s; }
|
|
463
|
-
.btn:hover { background: #505050; }
|
|
464
|
-
.btn.active { background: #0e639c; border-color: #1177bb; }
|
|
465
|
-
#editor-container { flex: 1; overflow: hidden; }
|
|
466
|
-
.status-bar { background: #007acc; color: #fff; padding: 2px 16px; font-size: 12px; display: flex; justify-content: space-between; flex-shrink: 0; }
|
|
467
|
-
</style>
|
|
468
|
-
</head>
|
|
469
|
-
<body>
|
|
470
|
-
<div class="header">
|
|
471
|
-
<div class="file-info">
|
|
472
|
-
<span class="file-icon">\u{1F4DD}</span>
|
|
473
|
-
<span class="file-name">${escapeHtml2(fileName)}</span>
|
|
474
|
-
<span class="stats"><span class="add">+${adds}</span> / <span class="del">-${dels}</span></span>
|
|
475
|
-
</div>
|
|
476
|
-
<div class="actions">
|
|
477
|
-
<button class="btn active" id="btn-side" onclick="setView('side')">Side by Side</button>
|
|
478
|
-
<button class="btn" id="btn-inline" onclick="setView('inline')">Inline</button>
|
|
479
|
-
<button class="btn" onclick="toggleTheme()" id="btn-theme">Light</button>
|
|
480
|
-
</div>
|
|
481
|
-
</div>
|
|
482
|
-
<div id="editor-container"></div>
|
|
483
|
-
<div class="status-bar">
|
|
484
|
-
<span>${escapeHtml2(entry.language || "plaintext")} | <span class="add">+${adds}</span> <span class="del">-${dels}</span></span>
|
|
485
|
-
<span>OpenACP Diff Viewer</span>
|
|
486
|
-
</div>
|
|
487
|
-
|
|
488
|
-
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/loader.js"></script>
|
|
489
|
-
<script>
|
|
490
|
-
const oldContent = ${safeOld};
|
|
491
|
-
const newContent = ${safeNew};
|
|
492
|
-
const lang = ${JSON.stringify(lang)};
|
|
493
|
-
let diffEditor;
|
|
494
|
-
let isDark = true;
|
|
495
|
-
let renderSideBySide = true;
|
|
496
|
-
|
|
497
|
-
require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs' } });
|
|
498
|
-
require(['vs/editor/editor.main'], function () {
|
|
499
|
-
const originalModel = monaco.editor.createModel(oldContent, lang);
|
|
500
|
-
const modifiedModel = monaco.editor.createModel(newContent, lang);
|
|
501
|
-
|
|
502
|
-
diffEditor = monaco.editor.createDiffEditor(document.getElementById('editor-container'), {
|
|
503
|
-
theme: 'vs-dark',
|
|
504
|
-
readOnly: true,
|
|
505
|
-
automaticLayout: true,
|
|
506
|
-
renderSideBySide: true,
|
|
507
|
-
scrollBeyondLastLine: false,
|
|
508
|
-
fontSize: 13,
|
|
509
|
-
fontFamily: "'SF Mono', SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace",
|
|
510
|
-
padding: { top: 8 },
|
|
511
|
-
enableSplitViewResizing: true,
|
|
512
|
-
renderOverviewRuler: true,
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
diffEditor.setModel({ original: originalModel, modified: modifiedModel });
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
function setView(mode) {
|
|
519
|
-
renderSideBySide = mode === 'side';
|
|
520
|
-
diffEditor.updateOptions({ renderSideBySide });
|
|
521
|
-
document.getElementById('btn-side').classList.toggle('active', renderSideBySide);
|
|
522
|
-
document.getElementById('btn-inline').classList.toggle('active', !renderSideBySide);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
function toggleTheme() {
|
|
526
|
-
isDark = !isDark;
|
|
527
|
-
monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs');
|
|
528
|
-
document.body.style.background = isDark ? '#1e1e1e' : '#ffffff';
|
|
529
|
-
document.querySelector('.header').style.background = isDark ? '#252526' : '#f3f3f3';
|
|
530
|
-
document.querySelector('.header').style.borderColor = isDark ? '#3c3c3c' : '#e0e0e0';
|
|
531
|
-
document.getElementById('btn-theme').textContent = isDark ? 'Light' : 'Dark';
|
|
532
|
-
}
|
|
533
|
-
</script>
|
|
534
|
-
</body>
|
|
535
|
-
</html>`;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// src/plugins/tunnel/templates/output-viewer.ts
|
|
539
|
-
function renderOutputViewer(entry) {
|
|
540
|
-
const label = entry.filePath ?? "Output";
|
|
541
|
-
const lines = entry.content.split("\n");
|
|
542
|
-
const lineNumbers = lines.map((line, i) => {
|
|
543
|
-
const num = String(i + 1).padStart(String(lines.length).length, " ");
|
|
544
|
-
return `<span class="line-num">${num}</span><span class="line-content">${escapeHtml3(line)}</span>`;
|
|
545
|
-
}).join("\n");
|
|
546
|
-
return `<!DOCTYPE html>
|
|
547
|
-
<html lang="en">
|
|
548
|
-
<head>
|
|
549
|
-
<meta charset="UTF-8">
|
|
550
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
551
|
-
<title>${escapeHtml3(label)} \u2014 OpenACP</title>
|
|
552
|
-
<style>
|
|
553
|
-
body { background: #0d1117; color: #c9d1d9; font-family: 'JetBrains Mono', 'Fira Code', monospace; font-size: 13px; margin: 0; padding: 0; }
|
|
554
|
-
header { background: #161b22; border-bottom: 1px solid #30363d; padding: 12px 20px; position: sticky; top: 0; z-index: 10; }
|
|
555
|
-
header h1 { margin: 0; font-size: 14px; color: #e6edf3; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
556
|
-
.content { padding: 16px 20px; }
|
|
557
|
-
pre { margin: 0; line-height: 1.6; white-space: pre; overflow-x: auto; }
|
|
558
|
-
.line-num { color: #484f58; user-select: none; margin-right: 16px; display: inline-block; min-width: 3ch; text-align: right; }
|
|
559
|
-
.line-content { color: #c9d1d9; }
|
|
560
|
-
</style>
|
|
561
|
-
</head>
|
|
562
|
-
<body>
|
|
563
|
-
<header><h1>\u{1F4CB} ${escapeHtml3(label)}</h1></header>
|
|
564
|
-
<div class="content"><pre>${lineNumbers}</pre></div>
|
|
565
|
-
</body>
|
|
566
|
-
</html>`;
|
|
567
|
-
}
|
|
568
|
-
function escapeHtml3(text) {
|
|
569
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// src/plugins/tunnel/server.ts
|
|
573
|
-
function notFoundPage() {
|
|
574
|
-
return `<!DOCTYPE html>
|
|
575
|
-
<html><head><meta charset="UTF-8"><title>Not Found - OpenACP</title>
|
|
576
|
-
<style>body{background:#0d1117;color:#c9d1d9;font-family:sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0}
|
|
577
|
-
.box{text-align:center;padding:40px}.code{font-size:72px;font-weight:bold;color:#484f58}p{margin-top:16px;color:#8b949e}</style>
|
|
578
|
-
</head><body><div class="box"><div class="code">404</div><p>This viewer link has expired or does not exist.</p></div></body></html>`;
|
|
579
|
-
}
|
|
580
|
-
function createTunnelServer(store, authToken) {
|
|
581
|
-
const app = new Hono();
|
|
582
|
-
if (authToken) {
|
|
583
|
-
app.use("*", async (c, next) => {
|
|
584
|
-
if (c.req.path === "/health") return next();
|
|
585
|
-
const bearer = c.req.header("Authorization")?.replace("Bearer ", "");
|
|
586
|
-
const query = c.req.query("token");
|
|
587
|
-
if (bearer !== authToken && query !== authToken) {
|
|
588
|
-
return c.text("Unauthorized", 401);
|
|
589
|
-
}
|
|
590
|
-
return next();
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
594
|
-
app.get("/view/:id", (c) => {
|
|
595
|
-
const entry = store.get(c.req.param("id"));
|
|
596
|
-
if (!entry || entry.type !== "file") {
|
|
597
|
-
return c.html(notFoundPage(), 404);
|
|
598
|
-
}
|
|
599
|
-
return c.html(renderFileViewer(entry));
|
|
600
|
-
});
|
|
601
|
-
app.get("/diff/:id", (c) => {
|
|
602
|
-
const entry = store.get(c.req.param("id"));
|
|
603
|
-
if (!entry || entry.type !== "diff") {
|
|
604
|
-
return c.html(notFoundPage(), 404);
|
|
605
|
-
}
|
|
606
|
-
return c.html(renderDiffViewer(entry));
|
|
607
|
-
});
|
|
608
|
-
app.get("/output/:id", (c) => {
|
|
609
|
-
const entry = store.get(c.req.param("id"));
|
|
610
|
-
if (!entry || entry.type !== "output") {
|
|
611
|
-
return c.html(notFoundPage(), 404);
|
|
612
|
-
}
|
|
613
|
-
return c.html(renderOutputViewer(entry));
|
|
614
|
-
});
|
|
615
|
-
app.get("/api/file/:id", (c) => {
|
|
616
|
-
const entry = store.get(c.req.param("id"));
|
|
617
|
-
if (!entry || entry.type !== "file") {
|
|
618
|
-
return c.json({ error: "not found" }, 404);
|
|
619
|
-
}
|
|
620
|
-
return c.json({ filePath: entry.filePath, content: entry.content, language: entry.language });
|
|
621
|
-
});
|
|
622
|
-
app.get("/api/diff/:id", (c) => {
|
|
623
|
-
const entry = store.get(c.req.param("id"));
|
|
624
|
-
if (!entry || entry.type !== "diff") {
|
|
625
|
-
return c.json({ error: "not found" }, 404);
|
|
626
|
-
}
|
|
627
|
-
return c.json({ filePath: entry.filePath, oldContent: entry.oldContent, newContent: entry.content, language: entry.language });
|
|
628
|
-
});
|
|
629
|
-
return app;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// src/plugins/tunnel/tunnel-service.ts
|
|
633
|
-
var log2 = createChildLogger({ module: "tunnel" });
|
|
634
|
-
var TunnelService = class {
|
|
635
|
-
registry;
|
|
636
|
-
store;
|
|
637
|
-
server = null;
|
|
638
|
-
config;
|
|
639
|
-
systemPort = 0;
|
|
640
|
-
startError;
|
|
641
|
-
constructor(config, registryPath) {
|
|
642
|
-
this.config = config;
|
|
643
|
-
this.store = new ViewerStore(config.storeTtlMinutes);
|
|
644
|
-
this.registry = new TunnelRegistry({
|
|
645
|
-
maxUserTunnels: config.maxUserTunnels ?? 5,
|
|
646
|
-
providerOptions: config.options,
|
|
647
|
-
registryPath
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
async start() {
|
|
651
|
-
const authToken = this.config.auth.enabled ? this.config.auth.token : void 0;
|
|
652
|
-
const app = createTunnelServer(this.store, authToken);
|
|
653
|
-
let actualPort = this.config.port;
|
|
654
|
-
const maxRetries = 10;
|
|
655
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
656
|
-
const port = this.config.port + i;
|
|
657
|
-
const server = serve({ fetch: app.fetch, port });
|
|
658
|
-
const result = await new Promise((resolve2) => {
|
|
659
|
-
server.on("listening", () => resolve2({ ok: true }));
|
|
660
|
-
server.on("error", (err) => resolve2({ ok: false, code: err.code }));
|
|
661
|
-
});
|
|
662
|
-
if (result.ok) {
|
|
663
|
-
this.server = server;
|
|
664
|
-
actualPort = port;
|
|
665
|
-
if (i > 0) {
|
|
666
|
-
log2.info({ configuredPort: this.config.port, actualPort }, "Configured port in use, using next available");
|
|
667
|
-
}
|
|
668
|
-
log2.info({ port: actualPort }, "Tunnel HTTP server started");
|
|
669
|
-
break;
|
|
670
|
-
}
|
|
671
|
-
server.close();
|
|
672
|
-
if (result.code === "EACCES") {
|
|
673
|
-
log2.error({ port }, "Permission denied binding to port (try a port > 1024)");
|
|
674
|
-
break;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
if (!this.server) {
|
|
678
|
-
log2.error({ port: this.config.port }, "Failed to start tunnel HTTP server \u2014 no available port");
|
|
679
|
-
this.startError = `HTTP server failed to bind (tried ports ${this.config.port}-${this.config.port + maxRetries - 1})`;
|
|
680
|
-
return `http://localhost:${this.config.port}`;
|
|
681
|
-
}
|
|
682
|
-
this.systemPort = actualPort;
|
|
683
|
-
try {
|
|
684
|
-
await this.registry.add(actualPort, {
|
|
685
|
-
type: "system",
|
|
686
|
-
provider: this.config.provider,
|
|
687
|
-
label: "File Viewer"
|
|
688
|
-
});
|
|
689
|
-
} catch (err) {
|
|
690
|
-
this.startError = err.message;
|
|
691
|
-
log2.warn({ err: this.startError }, "System tunnel failed, running on localhost");
|
|
692
|
-
}
|
|
693
|
-
await this.registry.restore();
|
|
694
|
-
const systemEntry = this.registry.getSystemEntry();
|
|
695
|
-
return systemEntry?.publicUrl || `http://localhost:${actualPort}`;
|
|
696
|
-
}
|
|
697
|
-
async stop() {
|
|
698
|
-
await this.registry.shutdown();
|
|
699
|
-
this.registry.flush();
|
|
700
|
-
if (this.server) {
|
|
701
|
-
this.server.close();
|
|
702
|
-
this.server = null;
|
|
703
|
-
}
|
|
704
|
-
this.store.destroy();
|
|
705
|
-
log2.info("Tunnel service stopped");
|
|
706
|
-
}
|
|
707
|
-
// --- User tunnel management ---
|
|
708
|
-
async addTunnel(port, opts) {
|
|
709
|
-
return this.registry.add(port, {
|
|
710
|
-
type: "user",
|
|
711
|
-
provider: this.config.provider,
|
|
712
|
-
label: opts?.label,
|
|
713
|
-
sessionId: opts?.sessionId
|
|
714
|
-
});
|
|
715
|
-
}
|
|
716
|
-
async stopTunnel(port) {
|
|
717
|
-
return this.registry.stop(port);
|
|
718
|
-
}
|
|
719
|
-
async stopAllUser() {
|
|
720
|
-
return this.registry.stopAllUser();
|
|
721
|
-
}
|
|
722
|
-
async stopBySession(sessionId) {
|
|
723
|
-
return this.registry.stopBySession(sessionId);
|
|
724
|
-
}
|
|
725
|
-
listTunnels() {
|
|
726
|
-
return this.registry.list(false);
|
|
727
|
-
}
|
|
728
|
-
getTunnel(port) {
|
|
729
|
-
return this.registry.get(port);
|
|
730
|
-
}
|
|
731
|
-
// --- Viewer (system tunnel) ---
|
|
732
|
-
getPublicUrl() {
|
|
733
|
-
const system = this.registry.getSystemEntry();
|
|
734
|
-
return system?.publicUrl || `http://localhost:${this.systemPort || this.config.port}`;
|
|
735
|
-
}
|
|
736
|
-
getStartError() {
|
|
737
|
-
return this.startError;
|
|
738
|
-
}
|
|
739
|
-
getStore() {
|
|
740
|
-
return this.store;
|
|
741
|
-
}
|
|
742
|
-
fileUrl(entryId) {
|
|
743
|
-
return `${this.getPublicUrl()}/view/${entryId}`;
|
|
744
|
-
}
|
|
745
|
-
diffUrl(entryId) {
|
|
746
|
-
return `${this.getPublicUrl()}/diff/${entryId}`;
|
|
747
|
-
}
|
|
748
|
-
outputUrl(entryId) {
|
|
749
|
-
return `${this.getPublicUrl()}/output/${entryId}`;
|
|
750
|
-
}
|
|
751
|
-
};
|
|
752
|
-
export {
|
|
753
|
-
TunnelService
|
|
754
|
-
};
|
|
755
|
-
//# sourceMappingURL=tunnel-service-TBAHDXMF.js.map
|