@ystemsrx/cfshare 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -6
- package/README.zh.md +33 -6
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +368 -0
- package/dist/src/manager.d.ts +142 -0
- package/dist/src/manager.d.ts.map +1 -0
- package/dist/src/manager.js +2251 -0
- package/dist/src/policy.d.ts +15 -0
- package/dist/src/policy.d.ts.map +1 -0
- package/dist/src/policy.js +130 -0
- package/dist/src/schemas.d.ts +97 -0
- package/dist/src/schemas.d.ts.map +1 -0
- package/dist/src/schemas.js +149 -0
- package/dist/src/templates/fileExplorerTemplate.d.ts +12 -0
- package/dist/src/templates/fileExplorerTemplate.d.ts.map +1 -0
- package/dist/src/templates/fileExplorerTemplate.js +1340 -0
- package/dist/src/templates/markdownPreviewTemplate.d.ts +5 -0
- package/dist/src/templates/markdownPreviewTemplate.d.ts.map +1 -0
- package/dist/src/templates/markdownPreviewTemplate.js +243 -0
- package/dist/src/tools.d.ts +3 -0
- package/dist/src/tools.d.ts.map +1 -0
- package/dist/src/tools.js +138 -0
- package/dist/src/types.d.ts +100 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +1 -0
- package/package.json +10 -1
- package/skills/cfshare/SKILL.md +1 -1
- package/src/cli.ts +484 -0
- package/src/manager.ts +45 -9
- package/src/shims.d.ts +55 -0
- package/src/tools.ts +11 -2
package/README.md
CHANGED
|
@@ -163,12 +163,39 @@ openclaw plugins enable cfshare
|
|
|
163
163
|
openclaw gateway restart
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
> [!NOTE]
|
|
167
|
-
> If you're running in daemon mode (`--install-daemon`), you can also restart via `openclaw restart`.
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
##
|
|
166
|
+
> [!NOTE]
|
|
167
|
+
> If you're running in daemon mode (`--install-daemon`), you can also restart via `openclaw restart`.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 🖥️ Direct CLI Usage (No OpenClaw Required)
|
|
172
|
+
|
|
173
|
+
You can now call CFShare directly from the terminal after npm installation:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npm install -g @ystemsrx/cfshare
|
|
177
|
+
cfshare env_check
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Common examples:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Expose an existing local service (keeps running until Ctrl+C by default)
|
|
184
|
+
cfshare expose_port '{"port":3000,"opts":{"access":"token"}}'
|
|
185
|
+
|
|
186
|
+
# Share files/directories
|
|
187
|
+
cfshare expose_files '{"paths":["./dist"],"opts":{"access":"none"}}'
|
|
188
|
+
|
|
189
|
+
# Stop an exposure
|
|
190
|
+
cfshare exposure_stop '{"id":"all"}'
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`expose_port` and `expose_files` keep the process alive by default so the tunnel stays active.
|
|
194
|
+
Use `--no-keep-alive` if you only want to print the result and exit.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## ⚙️ Configuration (Optional)
|
|
172
199
|
|
|
173
200
|
CFShare works out of the box. The defaults below suit most use cases. To customize, edit `~/.openclaw/openclaw.json`:
|
|
174
201
|
|
package/README.zh.md
CHANGED
|
@@ -163,12 +163,39 @@ openclaw plugins enable cfshare
|
|
|
163
163
|
openclaw gateway restart
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
> [!NOTE]
|
|
167
|
-
> 如果你使用守护进程模式(`--install-daemon`),也可以通过 `openclaw restart` 重启。
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
##
|
|
166
|
+
> [!NOTE]
|
|
167
|
+
> 如果你使用守护进程模式(`--install-daemon`),也可以通过 `openclaw restart` 重启。
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 🖥️ 直接命令行调用(无需 OpenClaw)
|
|
172
|
+
|
|
173
|
+
现在你也可以在安装后直接从终端调用 CFShare:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npm install -g @ystemsrx/cfshare
|
|
177
|
+
cfshare env_check
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
常用示例:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# 暴露已有本地服务(默认会持续运行,直到 Ctrl+C)
|
|
184
|
+
cfshare expose_port '{"port":3000,"opts":{"access":"token"}}'
|
|
185
|
+
|
|
186
|
+
# 分享文件/目录
|
|
187
|
+
cfshare expose_files '{"paths":["./dist"],"opts":{"access":"none"}}'
|
|
188
|
+
|
|
189
|
+
# 停止分享
|
|
190
|
+
cfshare exposure_stop '{"id":"all"}'
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`expose_port` 与 `expose_files` 默认会保持进程运行以维持隧道。
|
|
194
|
+
如果你只想输出结果后立即退出,可加 `--no-keep-alive`。
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## ⚙️ 配置(可选)
|
|
172
199
|
|
|
173
200
|
CFShare 开箱即用,以下默认配置适合绝大多数场景。如需调整,编辑 `~/.openclaw/openclaw.json`:
|
|
174
201
|
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
declare const plugin: {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
configSchema: Record<string, unknown>;
|
|
7
|
+
register(api: OpenClawPluginApi): void;
|
|
8
|
+
};
|
|
9
|
+
export default plugin;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,QAAA,MAAM,MAAM;;;;;kBAKI,iBAAiB;CAGhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
2
|
+
import { registerCfshareTools } from "./src/tools.js";
|
|
3
|
+
const plugin = {
|
|
4
|
+
id: "cfshare",
|
|
5
|
+
name: "CFShare",
|
|
6
|
+
description: "Cloudflare Quick Tunnel wrapper for secure temporary sharing",
|
|
7
|
+
configSchema: emptyPluginConfigSchema(),
|
|
8
|
+
register(api) {
|
|
9
|
+
registerCfshareTools(api);
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
export default plugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { CfshareManager } from "./manager.js";
|
|
7
|
+
const TOOL_NAMES = new Set([
|
|
8
|
+
"env_check",
|
|
9
|
+
"expose_port",
|
|
10
|
+
"expose_files",
|
|
11
|
+
"exposure_list",
|
|
12
|
+
"exposure_get",
|
|
13
|
+
"exposure_stop",
|
|
14
|
+
"exposure_logs",
|
|
15
|
+
"maintenance",
|
|
16
|
+
"audit_query",
|
|
17
|
+
"audit_export",
|
|
18
|
+
]);
|
|
19
|
+
function normalizeCommand(input) {
|
|
20
|
+
return input.trim().toLowerCase().replace(/-/g, "_");
|
|
21
|
+
}
|
|
22
|
+
function expandHome(input) {
|
|
23
|
+
if (input === "~") {
|
|
24
|
+
return os.homedir();
|
|
25
|
+
}
|
|
26
|
+
if (input.startsWith("~/")) {
|
|
27
|
+
return path.join(os.homedir(), input.slice(2));
|
|
28
|
+
}
|
|
29
|
+
return input;
|
|
30
|
+
}
|
|
31
|
+
function resolvePathFromCwd(input) {
|
|
32
|
+
const expanded = expandHome(input);
|
|
33
|
+
return path.isAbsolute(expanded) ? path.normalize(expanded) : path.resolve(process.cwd(), expanded);
|
|
34
|
+
}
|
|
35
|
+
function printHelp() {
|
|
36
|
+
const lines = [
|
|
37
|
+
"CFShare CLI",
|
|
38
|
+
"",
|
|
39
|
+
"Usage:",
|
|
40
|
+
" cfshare <tool> [params-json] [options]",
|
|
41
|
+
"",
|
|
42
|
+
"Tools:",
|
|
43
|
+
" env_check",
|
|
44
|
+
" expose_port",
|
|
45
|
+
" expose_files",
|
|
46
|
+
" exposure_list",
|
|
47
|
+
" exposure_get",
|
|
48
|
+
" exposure_stop",
|
|
49
|
+
" exposure_logs",
|
|
50
|
+
" maintenance",
|
|
51
|
+
" audit_query",
|
|
52
|
+
" audit_export",
|
|
53
|
+
"",
|
|
54
|
+
"Options:",
|
|
55
|
+
" --params <json> Tool parameters as JSON",
|
|
56
|
+
" --params-file <path> Read tool parameters from JSON file",
|
|
57
|
+
" --config <json> Runtime config JSON (same as plugin config)",
|
|
58
|
+
" --config-file <path> Read runtime config from JSON file",
|
|
59
|
+
" --workspace-dir <dir> Workspace dir for expose_files context",
|
|
60
|
+
" --keep-alive Keep process running after expose_*",
|
|
61
|
+
" --no-keep-alive Exit immediately after expose_* result",
|
|
62
|
+
" --compact Compact JSON output",
|
|
63
|
+
" -h, --help Show help",
|
|
64
|
+
" -v, --version Show version",
|
|
65
|
+
"",
|
|
66
|
+
"Examples:",
|
|
67
|
+
" cfshare env_check",
|
|
68
|
+
" cfshare expose_port '{\"port\":3000,\"opts\":{\"access\":\"token\"}}'",
|
|
69
|
+
" cfshare expose_files --params '{\"paths\":[\"./build\"],\"opts\":{\"access\":\"none\"}}'",
|
|
70
|
+
" cfshare exposure_stop --params '{\"id\":\"all\"}'",
|
|
71
|
+
];
|
|
72
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
73
|
+
}
|
|
74
|
+
function assertValue(args, index, flag) {
|
|
75
|
+
const value = args[index];
|
|
76
|
+
if (!value || value.startsWith("-")) {
|
|
77
|
+
throw new Error(`missing value for ${flag}`);
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
function parseArgs(argv) {
|
|
82
|
+
const opts = {};
|
|
83
|
+
const positionals = [];
|
|
84
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
85
|
+
const token = argv[i];
|
|
86
|
+
if (!token) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (token === "-h" || token === "--help") {
|
|
90
|
+
opts.help = true;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (token === "-v" || token === "--version") {
|
|
94
|
+
opts.version = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (token === "--params") {
|
|
98
|
+
opts.paramsJson = assertValue(argv, i + 1, token);
|
|
99
|
+
i += 1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (token === "--params-file") {
|
|
103
|
+
opts.paramsFile = assertValue(argv, i + 1, token);
|
|
104
|
+
i += 1;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (token === "--config") {
|
|
108
|
+
opts.configJson = assertValue(argv, i + 1, token);
|
|
109
|
+
i += 1;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (token === "--config-file") {
|
|
113
|
+
opts.configFile = assertValue(argv, i + 1, token);
|
|
114
|
+
i += 1;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (token === "--workspace-dir") {
|
|
118
|
+
opts.workspaceDir = assertValue(argv, i + 1, token);
|
|
119
|
+
i += 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (token === "--keep-alive") {
|
|
123
|
+
opts.keepAlive = true;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (token === "--no-keep-alive") {
|
|
127
|
+
opts.keepAlive = false;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (token === "--compact") {
|
|
131
|
+
opts.compact = true;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (token.startsWith("-")) {
|
|
135
|
+
throw new Error(`unknown option: ${token}`);
|
|
136
|
+
}
|
|
137
|
+
positionals.push(token);
|
|
138
|
+
}
|
|
139
|
+
if (positionals.length > 0) {
|
|
140
|
+
opts.command = positionals[0];
|
|
141
|
+
}
|
|
142
|
+
if (positionals.length > 1) {
|
|
143
|
+
if (opts.paramsJson || opts.paramsFile) {
|
|
144
|
+
throw new Error("params-json conflicts with --params/--params-file");
|
|
145
|
+
}
|
|
146
|
+
opts.paramsJson = positionals[1];
|
|
147
|
+
}
|
|
148
|
+
if (positionals.length > 2) {
|
|
149
|
+
throw new Error("too many positional arguments");
|
|
150
|
+
}
|
|
151
|
+
return opts;
|
|
152
|
+
}
|
|
153
|
+
async function parseJsonInput(source, label) {
|
|
154
|
+
try {
|
|
155
|
+
return JSON.parse(source);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
throw new Error(`failed to parse ${label}: ${String(error)}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function parseJsonFile(filePath, label) {
|
|
162
|
+
const resolved = resolvePathFromCwd(filePath);
|
|
163
|
+
const content = await fs.readFile(resolved, "utf8");
|
|
164
|
+
return await parseJsonInput(content, `${label} (${resolved})`);
|
|
165
|
+
}
|
|
166
|
+
function asObject(input, label) {
|
|
167
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
168
|
+
throw new Error(`${label} must be a JSON object`);
|
|
169
|
+
}
|
|
170
|
+
return input;
|
|
171
|
+
}
|
|
172
|
+
function createRuntimeApi(config) {
|
|
173
|
+
const stringifyArgs = (args) => args
|
|
174
|
+
.map((value) => {
|
|
175
|
+
if (typeof value === "string") {
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
return JSON.stringify(value);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return String(value);
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
.join(" ");
|
|
186
|
+
const logger = {
|
|
187
|
+
info: (...args) => {
|
|
188
|
+
if (process.env.CFSHARE_LOG_LEVEL === "info" || process.env.CFSHARE_LOG_LEVEL === "debug") {
|
|
189
|
+
process.stderr.write(`[cfshare] ${stringifyArgs(args)}\n`);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
warn: (...args) => {
|
|
193
|
+
process.stderr.write(`[cfshare][warn] ${stringifyArgs(args)}\n`);
|
|
194
|
+
},
|
|
195
|
+
error: (...args) => {
|
|
196
|
+
process.stderr.write(`[cfshare][error] ${stringifyArgs(args)}\n`);
|
|
197
|
+
},
|
|
198
|
+
debug: (...args) => {
|
|
199
|
+
if (process.env.CFSHARE_LOG_LEVEL === "debug") {
|
|
200
|
+
process.stderr.write(`[cfshare][debug] ${stringifyArgs(args)}\n`);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
return {
|
|
205
|
+
logger,
|
|
206
|
+
resolvePath: resolvePathFromCwd,
|
|
207
|
+
pluginConfig: config,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function shouldKeepAlive(command, keepAliveFlag) {
|
|
211
|
+
if (typeof keepAliveFlag === "boolean") {
|
|
212
|
+
return keepAliveFlag;
|
|
213
|
+
}
|
|
214
|
+
return command === "expose_port" || command === "expose_files";
|
|
215
|
+
}
|
|
216
|
+
async function waitUntilExposureStops(manager, id) {
|
|
217
|
+
await new Promise((resolve, reject) => {
|
|
218
|
+
let stopping = false;
|
|
219
|
+
let interval;
|
|
220
|
+
const shutdown = async (reason) => {
|
|
221
|
+
if (stopping) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
stopping = true;
|
|
225
|
+
if (interval) {
|
|
226
|
+
clearInterval(interval);
|
|
227
|
+
}
|
|
228
|
+
process.removeListener("SIGINT", onSigint);
|
|
229
|
+
process.removeListener("SIGTERM", onSigterm);
|
|
230
|
+
try {
|
|
231
|
+
await manager.stopExposure(id, { reason });
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// best effort cleanup on signal
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
resolve();
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const onSigint = () => {
|
|
241
|
+
void shutdown("cli interrupted");
|
|
242
|
+
};
|
|
243
|
+
const onSigterm = () => {
|
|
244
|
+
void shutdown("cli terminated");
|
|
245
|
+
};
|
|
246
|
+
process.once("SIGINT", onSigint);
|
|
247
|
+
process.once("SIGTERM", onSigterm);
|
|
248
|
+
interval = setInterval(async () => {
|
|
249
|
+
try {
|
|
250
|
+
const detail = (await manager.exposureGet({ id }));
|
|
251
|
+
const statusValue = detail.status;
|
|
252
|
+
const state = typeof statusValue === "string"
|
|
253
|
+
? statusValue
|
|
254
|
+
: typeof statusValue === "object" && statusValue && "state" in statusValue
|
|
255
|
+
? String(statusValue.state ?? "")
|
|
256
|
+
: "";
|
|
257
|
+
if (state === "stopped" || state === "expired" || state === "error" || state === "not_found") {
|
|
258
|
+
clearInterval(interval);
|
|
259
|
+
process.removeListener("SIGINT", onSigint);
|
|
260
|
+
process.removeListener("SIGTERM", onSigterm);
|
|
261
|
+
resolve();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
clearInterval(interval);
|
|
266
|
+
process.removeListener("SIGINT", onSigint);
|
|
267
|
+
process.removeListener("SIGTERM", onSigterm);
|
|
268
|
+
reject(error);
|
|
269
|
+
}
|
|
270
|
+
}, 1000);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
async function runTool(manager, command, params, opts) {
|
|
274
|
+
if (command === "env_check") {
|
|
275
|
+
return await manager.envCheck();
|
|
276
|
+
}
|
|
277
|
+
if (command === "expose_port") {
|
|
278
|
+
return await manager.exposePort(params);
|
|
279
|
+
}
|
|
280
|
+
if (command === "expose_files") {
|
|
281
|
+
const ctx = opts.workspaceDir ? { workspaceDir: opts.workspaceDir } : undefined;
|
|
282
|
+
return await manager.exposeFiles(params, ctx);
|
|
283
|
+
}
|
|
284
|
+
if (command === "exposure_list") {
|
|
285
|
+
return manager.exposureList();
|
|
286
|
+
}
|
|
287
|
+
if (command === "exposure_get") {
|
|
288
|
+
return await manager.exposureGet(params);
|
|
289
|
+
}
|
|
290
|
+
if (command === "exposure_stop") {
|
|
291
|
+
const stopParams = params;
|
|
292
|
+
const target = stopParams.ids ?? stopParams.id;
|
|
293
|
+
if (!target) {
|
|
294
|
+
throw new Error("exposure_stop requires id or ids");
|
|
295
|
+
}
|
|
296
|
+
return await manager.stopExposure(target, stopParams.opts);
|
|
297
|
+
}
|
|
298
|
+
if (command === "exposure_logs") {
|
|
299
|
+
const logParams = params;
|
|
300
|
+
const target = logParams.ids ?? logParams.id;
|
|
301
|
+
if (!target) {
|
|
302
|
+
throw new Error("exposure_logs requires id or ids");
|
|
303
|
+
}
|
|
304
|
+
return manager.exposureLogs(target, logParams.opts);
|
|
305
|
+
}
|
|
306
|
+
if (command === "maintenance") {
|
|
307
|
+
const maintenanceParams = params;
|
|
308
|
+
return await manager.maintenance(maintenanceParams.action, maintenanceParams.opts);
|
|
309
|
+
}
|
|
310
|
+
if (command === "audit_query") {
|
|
311
|
+
const queryParams = params;
|
|
312
|
+
return await manager.auditQuery(queryParams.filters);
|
|
313
|
+
}
|
|
314
|
+
if (command === "audit_export") {
|
|
315
|
+
const exportParams = params;
|
|
316
|
+
return await manager.auditExport(exportParams.range);
|
|
317
|
+
}
|
|
318
|
+
throw new Error(`unsupported command: ${command}`);
|
|
319
|
+
}
|
|
320
|
+
async function readVersion() {
|
|
321
|
+
const packagePath = new URL("../../package.json", import.meta.url);
|
|
322
|
+
const content = await fs.readFile(packagePath, "utf8");
|
|
323
|
+
const parsed = JSON.parse(content);
|
|
324
|
+
return parsed.version ?? "unknown";
|
|
325
|
+
}
|
|
326
|
+
async function main() {
|
|
327
|
+
const options = parseArgs(process.argv.slice(2));
|
|
328
|
+
if (options.version) {
|
|
329
|
+
process.stdout.write(`${await readVersion()}\n`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (options.help || !options.command) {
|
|
333
|
+
printHelp();
|
|
334
|
+
process.exit(options.help ? 0 : 1);
|
|
335
|
+
}
|
|
336
|
+
const command = normalizeCommand(options.command);
|
|
337
|
+
if (!TOOL_NAMES.has(command)) {
|
|
338
|
+
throw new Error(`unknown tool: ${options.command}`);
|
|
339
|
+
}
|
|
340
|
+
const paramsInput = options.paramsJson !== undefined
|
|
341
|
+
? await parseJsonInput(options.paramsJson, "--params")
|
|
342
|
+
: options.paramsFile
|
|
343
|
+
? await parseJsonFile(options.paramsFile, "--params-file")
|
|
344
|
+
: {};
|
|
345
|
+
const configInput = options.configJson !== undefined
|
|
346
|
+
? await parseJsonInput(options.configJson, "--config")
|
|
347
|
+
: options.configFile
|
|
348
|
+
? await parseJsonFile(options.configFile, "--config-file")
|
|
349
|
+
: {};
|
|
350
|
+
const params = asObject(paramsInput, "params");
|
|
351
|
+
const config = asObject(configInput, "config");
|
|
352
|
+
const manager = new CfshareManager(createRuntimeApi(config));
|
|
353
|
+
const result = await runTool(manager, command, params, options);
|
|
354
|
+
process.stdout.write(`${JSON.stringify(result, null, options.compact ? undefined : 2)}\n`);
|
|
355
|
+
if (shouldKeepAlive(command, options.keepAlive)) {
|
|
356
|
+
const exposureId = typeof result === "object" && result ? result.id : undefined;
|
|
357
|
+
if (typeof exposureId !== "string" || !exposureId) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
process.stderr.write(`cfshare: exposure ${exposureId} is running. Press Ctrl+C to stop or use --no-keep-alive.\n`);
|
|
361
|
+
await waitUntilExposureStops(manager, exposureId);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
void main().catch((error) => {
|
|
365
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
366
|
+
process.stderr.write(`cfshare error: ${message}\n`);
|
|
367
|
+
process.exit(1);
|
|
368
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { AccessMode, AuditEvent, ExposureRecord, ExposureStatus, ExposureType, FilePresentationMode } from "./types.js";
|
|
3
|
+
export type CfshareRuntimeApi = Pick<OpenClawPluginApi, "logger" | "resolvePath" | "pluginConfig">;
|
|
4
|
+
type EnvCheckResult = {
|
|
5
|
+
cloudflared: {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
path?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
};
|
|
10
|
+
defaults: Record<string, unknown>;
|
|
11
|
+
warnings: string[];
|
|
12
|
+
};
|
|
13
|
+
type ToolContext = {
|
|
14
|
+
workspaceDir?: string;
|
|
15
|
+
};
|
|
16
|
+
type ExposureFilter = {
|
|
17
|
+
status?: ExposureStatus;
|
|
18
|
+
type?: ExposureType;
|
|
19
|
+
};
|
|
20
|
+
type ExposureGetField = "id" | "type" | "status" | "port" | "public_url" | "expires_at" | "local_url" | "stats" | "file_sharing" | "last_error" | "manifest" | "created_at";
|
|
21
|
+
type ExposureGetParams = {
|
|
22
|
+
id?: string;
|
|
23
|
+
ids?: string[];
|
|
24
|
+
filter?: ExposureFilter;
|
|
25
|
+
fields?: ExposureGetField[];
|
|
26
|
+
opts?: {
|
|
27
|
+
probe_public?: boolean;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
type ExposureLogsOpts = {
|
|
31
|
+
lines?: number;
|
|
32
|
+
since_seconds?: number;
|
|
33
|
+
component?: "tunnel" | "origin" | "all";
|
|
34
|
+
};
|
|
35
|
+
export declare class CfshareManager {
|
|
36
|
+
private readonly logger;
|
|
37
|
+
private readonly resolvePath;
|
|
38
|
+
private readonly pluginConfig;
|
|
39
|
+
private readonly cloudflaredPathInput;
|
|
40
|
+
private readonly stateDir;
|
|
41
|
+
private readonly policyFile;
|
|
42
|
+
private readonly ignoreFile;
|
|
43
|
+
private readonly workspaceRoot;
|
|
44
|
+
private readonly auditFile;
|
|
45
|
+
private readonly sessionsFile;
|
|
46
|
+
private readonly exportsDir;
|
|
47
|
+
private initialized;
|
|
48
|
+
private initializing?;
|
|
49
|
+
private policy;
|
|
50
|
+
private policyWarnings;
|
|
51
|
+
private ignoreMatcher;
|
|
52
|
+
private cloudflaredResolvedPath?;
|
|
53
|
+
private guardTimer?;
|
|
54
|
+
private readonly sessions;
|
|
55
|
+
constructor(api: CfshareRuntimeApi);
|
|
56
|
+
private ensureInitialized;
|
|
57
|
+
private initialize;
|
|
58
|
+
private reloadPolicy;
|
|
59
|
+
private appendLog;
|
|
60
|
+
private writeAudit;
|
|
61
|
+
private persistSessionsSnapshot;
|
|
62
|
+
private makeAccessState;
|
|
63
|
+
private makeResponsePublicUrl;
|
|
64
|
+
private buildRateLimiter;
|
|
65
|
+
private isAuthorized;
|
|
66
|
+
private startReverseProxy;
|
|
67
|
+
private sendFileResponse;
|
|
68
|
+
private createZipArchive;
|
|
69
|
+
private createFolderZipArchive;
|
|
70
|
+
private startFileServer;
|
|
71
|
+
private detectWorkspaceRootBehavior;
|
|
72
|
+
private copyInputsToWorkspace;
|
|
73
|
+
private makeExposureRecord;
|
|
74
|
+
private summarizeExposeInputs;
|
|
75
|
+
private buildExposeFilesResponseManifest;
|
|
76
|
+
private makeManifestResponse;
|
|
77
|
+
private startGuard;
|
|
78
|
+
private reapExpired;
|
|
79
|
+
private startTunnel;
|
|
80
|
+
private startTunnelWithRetry;
|
|
81
|
+
private terminateProcess;
|
|
82
|
+
envCheck(): Promise<EnvCheckResult>;
|
|
83
|
+
exposePort(params: {
|
|
84
|
+
port: number;
|
|
85
|
+
opts?: {
|
|
86
|
+
ttl_seconds?: number;
|
|
87
|
+
access?: AccessMode;
|
|
88
|
+
protect_origin?: boolean;
|
|
89
|
+
allowlist_paths?: string[];
|
|
90
|
+
};
|
|
91
|
+
}): Promise<Record<string, unknown>>;
|
|
92
|
+
exposeFiles(params: {
|
|
93
|
+
paths: string[];
|
|
94
|
+
opts?: {
|
|
95
|
+
mode?: "normal" | "zip";
|
|
96
|
+
presentation?: FilePresentationMode;
|
|
97
|
+
ttl_seconds?: number;
|
|
98
|
+
access?: AccessMode;
|
|
99
|
+
max_downloads?: number;
|
|
100
|
+
};
|
|
101
|
+
}, ctx?: ToolContext): Promise<Record<string, unknown>>;
|
|
102
|
+
exposureList(): ExposureRecord[];
|
|
103
|
+
private normalizeRequestedIds;
|
|
104
|
+
private matchesExposureFilter;
|
|
105
|
+
private resolveExposureSelection;
|
|
106
|
+
private buildExposureDetail;
|
|
107
|
+
private projectExposureDetail;
|
|
108
|
+
private makeExposureGetNotFound;
|
|
109
|
+
exposureGet(params: ExposureGetParams): Promise<Record<string, unknown>>;
|
|
110
|
+
stopExposure(idOrIds: string | string[], opts?: {
|
|
111
|
+
reason?: string;
|
|
112
|
+
expired?: boolean;
|
|
113
|
+
keepAudit?: boolean;
|
|
114
|
+
}): Promise<Record<string, unknown>>;
|
|
115
|
+
private exposureLogsOne;
|
|
116
|
+
exposureLogs(idOrIds: string | string[], opts?: ExposureLogsOpts): Record<string, unknown>;
|
|
117
|
+
maintenance(action: "start_guard" | "run_gc" | "set_policy", opts?: {
|
|
118
|
+
policy?: unknown;
|
|
119
|
+
ignore_patterns?: string[];
|
|
120
|
+
}): Promise<Record<string, unknown>>;
|
|
121
|
+
private deepMerge;
|
|
122
|
+
private readPolicyJson;
|
|
123
|
+
private runGc;
|
|
124
|
+
auditQuery(filters?: {
|
|
125
|
+
id?: string;
|
|
126
|
+
event?: string;
|
|
127
|
+
type?: "port" | "files";
|
|
128
|
+
from_ts?: string;
|
|
129
|
+
to_ts?: string;
|
|
130
|
+
limit?: number;
|
|
131
|
+
}): Promise<AuditEvent[]>;
|
|
132
|
+
auditExport(range?: {
|
|
133
|
+
from_ts?: string;
|
|
134
|
+
to_ts?: string;
|
|
135
|
+
id?: string;
|
|
136
|
+
event?: string;
|
|
137
|
+
type?: "port" | "files";
|
|
138
|
+
output_path?: string;
|
|
139
|
+
}): Promise<Record<string, unknown>>;
|
|
140
|
+
}
|
|
141
|
+
export {};
|
|
142
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"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"}
|