@tcb-sandbox/cli 0.3.8 → 0.4.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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  版本变更只记在此文件,不写进 README。
4
4
 
5
+ ## 0.3.9
6
+
7
+ - 升级内置依赖 **`@tcb-sandbox/sdk-js`** 至 `0.2.0`(API 版本对齐,含 E2B 兼容层根路径挂载改动)。
8
+
5
9
  ## 0.3.7
6
10
 
7
11
  - `serve` 增加别名 **`local`**。
package/README.md CHANGED
@@ -32,8 +32,8 @@ tcb-sandbox --session-id my-session read --path hello.txt
32
32
  ## 环境变量
33
33
 
34
34
  ```bash
35
- export TRW_ENDPOINT=https://your-gateway.com
36
- export TRW_SESSION_ID=your-session-id
35
+ export TCB_SANDBOX_ENDPOINT=https://your-gateway.com
36
+ export TCB_SANDBOX_SESSION_ID=your-session-id
37
37
 
38
38
  # 然后可直接使用
39
39
  tcb-sandbox health
@@ -60,6 +60,12 @@ tcb-sandbox bash --command "ls -la"
60
60
  | `pty` | PTY 终端 |
61
61
  | `snapshot` | 快照管理 |
62
62
 
63
+ ## 测试说明
64
+
65
+ - `pnpm test`:CLI 契约回归(mock HTTP)
66
+ - 不包含:`serve` 真实内嵌 TRW 行为验证
67
+ - 跨门面(HTTP/MCP/CLI/E2B)完整矩阵请使用 sibling `../trw-example` 的 `pnpm start` 或 `pnpm test:full`
68
+
63
69
  ## 本地模式详情
64
70
 
65
71
  ```bash
package/dist/cli.js CHANGED
@@ -5,9 +5,8 @@ import process from "node:process";
5
5
  import { TcbSandboxClient } from "@tcb-sandbox/sdk-js";
6
6
  import { Command } from "commander";
7
7
  import { BUNDLED_API_DOCS } from "./bundled-docs.js";
8
- import { defaultWorkspaceRoot, runServe } from "./serve.js";
8
+ import { defaultMultiSessionWorkspaceRoot, defaultWorkspaceRoot, runServe } from "./serve.js";
9
9
  const DEFAULT_TIMEOUT = 600_000;
10
- const DEFAULT_ACCEPT_HEADER = "application/problem+json, application/json;q=0.9, text/markdown;q=0.8, */*;q=0.1";
11
10
  const LOG_PRIORITIES = {
12
11
  debug: 10,
13
12
  info: 20,
@@ -92,17 +91,6 @@ function resolveLogFormat(opts) {
92
91
  function shouldLog(ctx, level) {
93
92
  return LOG_PRIORITIES[level] >= LOG_PRIORITIES[ctx.logLevel];
94
93
  }
95
- function maskHeaderValue(key, value) {
96
- const lower = key.toLowerCase();
97
- if (lower.includes("authorization") ||
98
- lower.includes("api-key") ||
99
- lower.includes("token") ||
100
- lower.includes("cookie") ||
101
- lower.includes("session")) {
102
- return "***";
103
- }
104
- return value;
105
- }
106
94
  const SENSITIVE_JSON_KEY_RE = /(authorization|api[_-]?key|token|secret|password|cookie|session|value)/i;
107
95
  function redactJsonForDebug(input) {
108
96
  if (Array.isArray(input)) {
@@ -262,7 +250,7 @@ Quick Start
262
250
  tcb-sandbox serve # alias: local — embedded TRW on loopback
263
251
  tcb-sandbox --endpoint http://127.0.0.1:9000 health
264
252
  tcb-sandbox --endpoint http://127.0.0.1:9000 docs
265
- tcb-sandbox --endpoint http://127.0.0.1:9000 --session-id demo tools call read --param path=README.md
253
+ tcb-sandbox --endpoint http://127.0.0.1:9000 --session-id demo read README.md
266
254
 
267
255
  Error Handling
268
256
  HTTP failures are parsed as RFC 9457 problem details when available.
@@ -271,17 +259,23 @@ Error Handling
271
259
  program
272
260
  .command("serve")
273
261
  .alias("local")
274
- .description("Run embedded tcb-remote-workspace locally (multi-session; X-Cloudbase-Session-Id; no auth).")
262
+ .description("Run embedded tcb-remote-workspace locally. Default: single-workspace (no session header needed). Use --multi-session for session-based subdirectories.")
275
263
  .option("--port <n>", "Listen port", "9000")
276
264
  .option("--host <addr>", "Bind address (use 0.0.0.0 for all interfaces)", "127.0.0.1")
277
- .option("--workspace-root <path>", "Parent directory; each session is a subdirectory by session id", defaultWorkspaceRoot())
265
+ .option("--workspace-root <path>", "Workspace directory (single mode) or parent directory for session subdirectories (multi-session mode)")
266
+ .option("--multi-session", "Enable multi-session mode: each X-Cloudbase-Session-Id creates a subdirectory under workspace-root", false)
278
267
  .action(async function (opts) {
279
268
  const port = asInt(opts.port);
280
- const workspaceRoot = path.resolve(opts.workspaceRoot);
269
+ const resolvedRoot = opts.workspaceRoot
270
+ ? path.resolve(opts.workspaceRoot)
271
+ : opts.multiSession
272
+ ? defaultMultiSessionWorkspaceRoot()
273
+ : defaultWorkspaceRoot();
281
274
  const code = await runServe({
282
275
  host: opts.host,
283
276
  port,
284
- workspaceRoot,
277
+ workspaceRoot: resolvedRoot,
278
+ multiSession: opts.multiSession,
285
279
  });
286
280
  process.exit(code === null ? 0 : code);
287
281
  });
@@ -310,7 +304,7 @@ Error Handling
310
304
  const response = await client.system.apiDocs();
311
305
  docs = ("data" in response ? response.data : response);
312
306
  }
313
- catch (err) {
307
+ catch (_err) {
314
308
  logMessage(ctx, "warn", "GET /api/docs unavailable, using bundled fallback docs");
315
309
  docs = BUNDLED_API_DOCS;
316
310
  }
package/dist/serve.js CHANGED
@@ -123,6 +123,9 @@ function pythonVersion() {
123
123
  return m ? m[1] : line.replace(/^Python\s+/i, "") || "?";
124
124
  }
125
125
  export function defaultWorkspaceRoot() {
126
+ return path.join(os.homedir(), ".tcb-sandbox", "workspace");
127
+ }
128
+ export function defaultMultiSessionWorkspaceRoot() {
126
129
  return path.join(os.homedir(), ".tcb-sandbox", "workspaces");
127
130
  }
128
131
  const MOTD_ASCII = "\n ____ _ _ ____\n / ___| | ___ _ _ __| | __ ) __ _ ___ ___\n | | | |/ _ \\| | | |/ _` | _ \\ / _` / __|/ _ \\\n | |___| | (_) | |_| | (_| | |_) | (_| \\__ \\ __/\n \\____|_|\\___/ \\__,_|\\__,_|____/ \\__,_|___/\\___|\n Remote Workspace\n";
@@ -139,8 +142,14 @@ export function printServeBanner(opts, trwMain, sandboxStatus) {
139
142
  lines.push(MOTD_ASCII.replace(/^\n/, "").replace(/\n$/, ""));
140
143
  lines.push("");
141
144
  lines.push(` ${S.dim("Listen :")} ${base}`);
142
- lines.push(` ${S.dim("Session :")} ${S.bold("X-Cloudbase-Session-Id")} ${S.dim("(不传时默认 default;示例 test-03271546 → 子目录 test-03271546)")}`);
143
- lines.push(` ${S.dim("Workspace:")} ${wsRoot}${path.sep}${S.dim("<session-id>")}`);
145
+ if (opts.multiSession) {
146
+ lines.push(` ${S.dim("Session :")} ${S.bold("X-Cloudbase-Session-Id")} ${S.dim("(multi-session mode; each session → subdirectory)")}`);
147
+ lines.push(` ${S.dim("Workspace:")} ${wsRoot}${path.sep}${S.dim("<session-id>")}`);
148
+ }
149
+ else {
150
+ lines.push(` ${S.dim("Session :")} ${S.dim("(single-workspace mode; no session header needed)")}`);
151
+ lines.push(` ${S.dim("Workspace:")} ${wsRoot}`);
152
+ }
144
153
  lines.push(` ${S.dim("Disk :")} ${disk}`);
145
154
  if (sandboxStatus) {
146
155
  const sandboxColor = sandboxStatus.startsWith("active") ? S.green : S.yellow;
@@ -193,7 +202,7 @@ async function isPortInUse(host, port) {
193
202
  const SHUTDOWN_TIMEOUT_MS = 10_000;
194
203
  const MAX_RESTART_COUNT = 3;
195
204
  const MIN_UPTIME_FOR_HEALTHY_MS = 5_000;
196
- function ensureMcporterConfig(workspaceRoot) {
205
+ function ensureMcporterConfig(_workspaceRoot) {
197
206
  const mcporterDir = path.join(os.homedir(), ".mcporter");
198
207
  const configPath = path.join(mcporterDir, "mcporter.json");
199
208
  if (fs.existsSync(configPath))
@@ -239,6 +248,8 @@ export async function runServe(opts) {
239
248
  HOST: opts.host,
240
249
  PORT: String(opts.port),
241
250
  WORKSPACE_ROOT: opts.workspaceRoot,
251
+ WORKSPACE_MODE: opts.multiSession ? "multi" : "single",
252
+ LOCAL_MODE: "true",
242
253
  };
243
254
  let shutdownRequested = false;
244
255
  let restartCount = 0;