@usecontextlayer/ctxs 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +157 -30
  2. package/package.json +3 -3
package/dist/cli.mjs CHANGED
@@ -10670,8 +10670,133 @@ function resolveGoldensDir() {
10670
10670
  }
10671
10671
  }
10672
10672
 
10673
+ //#endregion
10674
+ //#region ../../node_modules/.pnpm/js-base64@3.7.8/node_modules/js-base64/base64.mjs
10675
+ const _hasBuffer = typeof Buffer === "function";
10676
+ const _TD = typeof TextDecoder === "function" ? new TextDecoder() : void 0;
10677
+ const _TE = typeof TextEncoder === "function" ? new TextEncoder() : void 0;
10678
+ const b64chs = Array.prototype.slice.call("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
10679
+ const b64tab = ((a) => {
10680
+ let tab = {};
10681
+ a.forEach((c, i) => tab[c] = i);
10682
+ return tab;
10683
+ })(b64chs);
10684
+ const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
10685
+ const _fromCC = String.fromCharCode.bind(String);
10686
+ const _U8Afrom = typeof Uint8Array.from === "function" ? Uint8Array.from.bind(Uint8Array) : (it) => new Uint8Array(Array.prototype.slice.call(it, 0));
10687
+ const _mkUriSafe = (src) => src.replace(/=/g, "").replace(/[+\/]/g, (m0) => m0 == "+" ? "-" : "_");
10688
+ const _tidyB64 = (s) => s.replace(/[^A-Za-z0-9\+\/]/g, "");
10689
+ /**
10690
+ * polyfill version of `btoa`
10691
+ */
10692
+ const btoaPolyfill = (bin) => {
10693
+ let u32, c0, c1, c2, asc = "";
10694
+ const pad = bin.length % 3;
10695
+ for (let i = 0; i < bin.length;) {
10696
+ if ((c0 = bin.charCodeAt(i++)) > 255 || (c1 = bin.charCodeAt(i++)) > 255 || (c2 = bin.charCodeAt(i++)) > 255) throw new TypeError("invalid character found");
10697
+ u32 = c0 << 16 | c1 << 8 | c2;
10698
+ asc += b64chs[u32 >> 18 & 63] + b64chs[u32 >> 12 & 63] + b64chs[u32 >> 6 & 63] + b64chs[u32 & 63];
10699
+ }
10700
+ return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
10701
+ };
10702
+ /**
10703
+ * does what `window.btoa` of web browsers do.
10704
+ * @param {String} bin binary string
10705
+ * @returns {string} Base64-encoded string
10706
+ */
10707
+ const _btoa = typeof btoa === "function" ? (bin) => btoa(bin) : _hasBuffer ? (bin) => Buffer.from(bin, "binary").toString("base64") : btoaPolyfill;
10708
+ const _fromUint8Array = _hasBuffer ? (u8a) => Buffer.from(u8a).toString("base64") : (u8a) => {
10709
+ const maxargs = 4096;
10710
+ let strs = [];
10711
+ for (let i = 0, l = u8a.length; i < l; i += maxargs) strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
10712
+ return _btoa(strs.join(""));
10713
+ };
10714
+ const cb_utob = (c) => {
10715
+ if (c.length < 2) {
10716
+ var cc = c.charCodeAt(0);
10717
+ return cc < 128 ? c : cc < 2048 ? _fromCC(192 | cc >>> 6) + _fromCC(128 | cc & 63) : _fromCC(224 | cc >>> 12 & 15) + _fromCC(128 | cc >>> 6 & 63) + _fromCC(128 | cc & 63);
10718
+ } else {
10719
+ var cc = 65536 + (c.charCodeAt(0) - 55296) * 1024 + (c.charCodeAt(1) - 56320);
10720
+ return _fromCC(240 | cc >>> 18 & 7) + _fromCC(128 | cc >>> 12 & 63) + _fromCC(128 | cc >>> 6 & 63) + _fromCC(128 | cc & 63);
10721
+ }
10722
+ };
10723
+ const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
10724
+ /**
10725
+ * @deprecated should have been internal use only.
10726
+ * @param {string} src UTF-8 string
10727
+ * @returns {string} UTF-16 string
10728
+ */
10729
+ const utob = (u) => u.replace(re_utob, cb_utob);
10730
+ const _encode$1 = _hasBuffer ? (s) => Buffer.from(s, "utf8").toString("base64") : _TE ? (s) => _fromUint8Array(_TE.encode(s)) : (s) => _btoa(utob(s));
10731
+ /**
10732
+ * converts a UTF-8-encoded string to a Base64 string.
10733
+ * @param {boolean} [urlsafe] if `true` make the result URL-safe
10734
+ * @returns {string} Base64 string
10735
+ */
10736
+ const encode$1 = (src, urlsafe = false) => urlsafe ? _mkUriSafe(_encode$1(src)) : _encode$1(src);
10737
+ /**
10738
+ * converts a UTF-8-encoded string to URL-safe Base64 RFC4648 §5.
10739
+ * @returns {string} Base64 string
10740
+ */
10741
+ const encodeURI = (src) => encode$1(src, true);
10742
+ const re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
10743
+ const cb_btou = (cccc) => {
10744
+ switch (cccc.length) {
10745
+ case 4:
10746
+ var offset = ((7 & cccc.charCodeAt(0)) << 18 | (63 & cccc.charCodeAt(1)) << 12 | (63 & cccc.charCodeAt(2)) << 6 | 63 & cccc.charCodeAt(3)) - 65536;
10747
+ return _fromCC((offset >>> 10) + 55296) + _fromCC((offset & 1023) + 56320);
10748
+ case 3: return _fromCC((15 & cccc.charCodeAt(0)) << 12 | (63 & cccc.charCodeAt(1)) << 6 | 63 & cccc.charCodeAt(2));
10749
+ default: return _fromCC((31 & cccc.charCodeAt(0)) << 6 | 63 & cccc.charCodeAt(1));
10750
+ }
10751
+ };
10752
+ /**
10753
+ * @deprecated should have been internal use only.
10754
+ * @param {string} src UTF-16 string
10755
+ * @returns {string} UTF-8 string
10756
+ */
10757
+ const btou = (b) => b.replace(re_btou, cb_btou);
10758
+ /**
10759
+ * polyfill version of `atob`
10760
+ */
10761
+ const atobPolyfill = (asc) => {
10762
+ asc = asc.replace(/\s+/g, "");
10763
+ if (!b64re.test(asc)) throw new TypeError("malformed base64.");
10764
+ asc += "==".slice(2 - (asc.length & 3));
10765
+ let u24, r1, r2;
10766
+ let binArray = [];
10767
+ for (let i = 0; i < asc.length;) {
10768
+ u24 = b64tab[asc.charAt(i++)] << 18 | b64tab[asc.charAt(i++)] << 12 | (r1 = b64tab[asc.charAt(i++)]) << 6 | (r2 = b64tab[asc.charAt(i++)]);
10769
+ if (r1 === 64) binArray.push(_fromCC(u24 >> 16 & 255));
10770
+ else if (r2 === 64) binArray.push(_fromCC(u24 >> 16 & 255, u24 >> 8 & 255));
10771
+ else binArray.push(_fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255));
10772
+ }
10773
+ return binArray.join("");
10774
+ };
10775
+ /**
10776
+ * does what `window.atob` of web browsers do.
10777
+ * @param {String} asc Base64-encoded string
10778
+ * @returns {string} binary string
10779
+ */
10780
+ const _atob = typeof atob === "function" ? (asc) => atob(_tidyB64(asc)) : _hasBuffer ? (asc) => Buffer.from(asc, "base64").toString("binary") : atobPolyfill;
10781
+ const _toUint8Array = _hasBuffer ? (a) => _U8Afrom(Buffer.from(a, "base64")) : (a) => _U8Afrom(_atob(a).split("").map((c) => c.charCodeAt(0)));
10782
+ const _decode$1 = _hasBuffer ? (a) => Buffer.from(a, "base64").toString("utf8") : _TD ? (a) => _TD.decode(_toUint8Array(a)) : (a) => btou(_atob(a));
10783
+ const _unURI = (a) => _tidyB64(a.replace(/[-_]/g, (m0) => m0 == "-" ? "+" : "/"));
10784
+ /**
10785
+ * converts a Base64 string to a UTF-8 string.
10786
+ * @param {String} src Base64 string. Both normal and URL-safe are supported
10787
+ * @returns {string} UTF-8 string
10788
+ */
10789
+ const decode$1 = (src) => _decode$1(_unURI(src));
10790
+
10673
10791
  //#endregion
10674
10792
  //#region ../slate-shared/dist/index.mjs
10793
+ const WORKSPACE_DOCS_SUBDIR = "output";
10794
+ function encodeRootId(rootDir) {
10795
+ return encodeURI(rootDir);
10796
+ }
10797
+ function decodeRootId(rootId) {
10798
+ return decode$1(rootId);
10799
+ }
10675
10800
  function classifyAssistantBlock(block) {
10676
10801
  switch (block.type) {
10677
10802
  case "text": return {
@@ -14850,7 +14975,7 @@ const portSchema = number().int().min(0).max(65535).default(7777);
14850
14975
  const envSchema = object({
14851
14976
  CLAUDE_CODE_OAUTH_TOKEN: nonEmptyStringSchema.optional(),
14852
14977
  CTXS_BRIDGE_PORT: portSchema,
14853
- CTXS_CLAUDE_IMAGE: nonEmptyStringSchema.default("ghcr.io/usecontextlayer/ctx-sandbox:0.3.3"),
14978
+ CTXS_CLAUDE_IMAGE: nonEmptyStringSchema.default("ghcr.io/usecontextlayer/ctx-sandbox:0.3.5"),
14854
14979
  CTXS_CLAUDE_MODEL: nonEmptyStringSchema.default("claude-opus-4-7"),
14855
14980
  CTXS_HOST_CLAUDE_CONFIG_DIR: nonEmptyStringSchema.default(() => path.join(os.homedir(), ".claude")),
14856
14981
  CTXS_HOST_CLAUDE_JSON_PATH: nonEmptyStringSchema.default(() => path.join(os.homedir(), ".claude.json"))
@@ -14859,7 +14984,9 @@ function parseSlateBridgeEnv(rawEnv) {
14859
14984
  return envSchema.parse(rawEnv);
14860
14985
  }
14861
14986
  const env = parseSlateBridgeEnv(process.env);
14862
- const WORKSPACE_DOCS_SUBDIR = "output";
14987
+ async function ensureWorkspaceDocsRoot(root) {
14988
+ await fs$1.mkdir(root, { recursive: true });
14989
+ }
14863
14990
  async function readWorkspaceFiles(root) {
14864
14991
  const files = [];
14865
14992
  async function walk(absDir) {
@@ -15248,25 +15375,24 @@ const CLAUDE_ARGS = [
15248
15375
  "--permission-mode",
15249
15376
  "bypassPermissions"
15250
15377
  ];
15251
- const projectDirGuard = async (c, next) => {
15252
- const encoded = c.req.param("projectDir") ?? "";
15253
- const decoded = Buffer.from(encoded, "base64url").toString("utf-8");
15254
- if (decoded === "" || !path.isAbsolute(decoded)) return c.text(`projectDir must decode to an absolute path: ${decoded}`, 404);
15255
- if (path.normalize(decoded) !== decoded) return c.text(`projectDir must be a normalized path: ${decoded}`, 404);
15378
+ const rootDirGuard = async (c, next) => {
15379
+ const decoded = decodeRootId(c.req.param("rootId") ?? "");
15380
+ if (decoded === "" || !path.isAbsolute(decoded)) return c.text(`rootId must decode to an absolute path: ${decoded}`, 404);
15381
+ if (path.normalize(decoded) !== decoded) return c.text(`rootId must decode to a normalized path: ${decoded}`, 404);
15256
15382
  let resolved;
15257
15383
  try {
15258
- if (!(await fs$1.stat(decoded)).isDirectory()) return c.text(`projectDir is not a directory: ${decoded}`, 404);
15384
+ if (!(await fs$1.stat(decoded)).isDirectory()) return c.text(`rootId must decode to a directory: ${decoded}`, 404);
15259
15385
  resolved = await fs$1.realpath(decoded);
15260
15386
  } catch (err) {
15261
15387
  const code = err.code;
15262
- if (code === "ENOENT" || code === "ENOTDIR") return c.text(`projectDir does not exist: ${decoded}`, 404);
15388
+ if (code === "ENOENT" || code === "ENOTDIR") return c.text(`rootId decodes to a path that does not exist: ${decoded}`, 404);
15263
15389
  throw err;
15264
15390
  }
15265
- c.set("projectDir", resolved);
15391
+ c.set("rootDir", resolved);
15266
15392
  await next();
15267
15393
  };
15268
- async function pickSessionFlag(projectDir, sessionId) {
15269
- return await getSessionInfo(sessionId, { dir: projectDir }) ? ["--resume", sessionId] : ["--session-id", sessionId];
15394
+ async function pickSessionFlag(rootDir, sessionId) {
15395
+ return await getSessionInfo(sessionId, { dir: rootDir }) ? ["--resume", sessionId] : ["--session-id", sessionId];
15270
15396
  }
15271
15397
  async function createBridgeServer(opts) {
15272
15398
  const port = opts.port ?? env.CTXS_BRIDGE_PORT;
@@ -15280,14 +15406,15 @@ async function createBridgeServer(opts) {
15280
15406
  const app = new Hono();
15281
15407
  const { injectWebSocket, upgradeWebSocket, wss } = createNodeWebSocket({ app });
15282
15408
  app.use(cors());
15283
- app.get("/:projectDir/sessions", projectDirGuard, async (c) => c.json(await listSessions({ dir: c.get("projectDir") })));
15284
- app.get("/:projectDir/sessions/:id", projectDirGuard, async (c) => {
15285
- const messages = await getSessionMessages(c.req.param("id"), { dir: c.get("projectDir") });
15409
+ app.get("/:rootId/sessions", rootDirGuard, async (c) => c.json(await listSessions({ dir: c.get("rootDir") })));
15410
+ app.get("/:rootId/sessions/:id", rootDirGuard, async (c) => {
15411
+ const messages = await getSessionMessages(c.req.param("id"), { dir: c.get("rootDir") });
15286
15412
  return c.json(translateFinalized(messages));
15287
15413
  });
15288
- app.get("/:projectDir/files/stream", projectDirGuard, (c) => {
15289
- const root = path.join(c.get("projectDir"), WORKSPACE_DOCS_SUBDIR);
15414
+ app.get("/:rootId/files/stream", rootDirGuard, (c) => {
15415
+ const root = path.join(c.get("rootDir"), WORKSPACE_DOCS_SUBDIR);
15290
15416
  return streamSSE(c, async (stream) => {
15417
+ await ensureWorkspaceDocsRoot(root);
15291
15418
  const files = await readWorkspaceFiles(root);
15292
15419
  await stream.writeSSE({
15293
15420
  data: JSON.stringify(files),
@@ -15321,10 +15448,10 @@ async function createBridgeServer(opts) {
15321
15448
  await subscription.unsubscribe();
15322
15449
  });
15323
15450
  });
15324
- app.get("/:projectDir/sessions/:id/wss", projectDirGuard, upgradeWebSocket((c) => {
15325
- const hostProjectDir = c.get("projectDir");
15451
+ app.get("/:rootId/sessions/:id/wss", rootDirGuard, upgradeWebSocket((c) => {
15452
+ const rootDir = c.get("rootDir");
15326
15453
  const sessionId = c.req.param("id") ?? "";
15327
- const sessionKey = `${hostProjectDir}::${sessionId}`;
15454
+ const sessionKey = `${rootDir}::${sessionId}`;
15328
15455
  if (activeSessions.has(sessionKey)) return { onOpen(_evt, wsCtx) {
15329
15456
  wsCtx.send(`${JSON.stringify({
15330
15457
  reason: "session_in_use",
@@ -15345,11 +15472,11 @@ async function createBridgeServer(opts) {
15345
15472
  session = await createClaudeMicrosandbox({
15346
15473
  hostClaudeConfigDir,
15347
15474
  hostClaudeJsonPath,
15348
- hostProjectDir,
15475
+ hostProjectDir: rootDir,
15349
15476
  image
15350
15477
  });
15351
15478
  if (ac.signal.aborted) return;
15352
- const sessionFlag = await pickSessionFlag(hostProjectDir, sessionId);
15479
+ const sessionFlag = await pickSessionFlag(rootDir, sessionId);
15353
15480
  if (ac.signal.aborted) return;
15354
15481
  const proc = session.spawn({
15355
15482
  args: [
@@ -15438,13 +15565,13 @@ async function writeJsonAtomic(filePath, value) {
15438
15565
  await rename(tmp, filePath);
15439
15566
  }
15440
15567
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
15441
- async function pollHistoryStable(sessionId, projectDir) {
15568
+ async function pollHistoryStable(sessionId, rootDir) {
15442
15569
  const deadline = Date.now() + 3e4;
15443
15570
  let prev = -1;
15444
15571
  let stableReads = 0;
15445
15572
  let history = [];
15446
15573
  while (Date.now() < deadline) {
15447
- history = await getSessionMessages(sessionId, { dir: projectDir });
15574
+ history = await getSessionMessages(sessionId, { dir: rootDir });
15448
15575
  if (history.length > 0 && history.length === prev) {
15449
15576
  stableReads += 1;
15450
15577
  if (stableReads >= 2) break;
@@ -15455,13 +15582,13 @@ async function pollHistoryStable(sessionId, projectDir) {
15455
15582
  return history;
15456
15583
  }
15457
15584
  async function captureScenario(server, scenario) {
15458
- const projectDir = await mkdtemp(path.join(os.tmpdir(), `parity-${scenario.name}-`));
15585
+ const rootDir = await mkdtemp(path.join(os.tmpdir(), `parity-${scenario.name}-`));
15459
15586
  const ac = new AbortController();
15460
15587
  let iter;
15461
15588
  try {
15462
- const encodedProjectDir = Buffer.from(projectDir, "utf-8").toString("base64url");
15589
+ const rootId = encodeRootId(rootDir);
15463
15590
  const sessionId = crypto.randomUUID();
15464
- const url = `ws://127.0.0.1:${server.port}/${encodedProjectDir}/sessions/${sessionId}/wss`;
15591
+ const url = `ws://127.0.0.1:${server.port}/${rootId}/sessions/${sessionId}/wss`;
15465
15592
  const queue = [];
15466
15593
  let resolveNext = null;
15467
15594
  const pushTurn = (text) => {
@@ -15521,13 +15648,13 @@ async function captureScenario(server, scenario) {
15521
15648
  if (next.value.type === "result" && !advance()) finished = true;
15522
15649
  }
15523
15650
  return {
15524
- history: await pollHistoryStable(sessionId, projectDir),
15651
+ history: await pollHistoryStable(sessionId, rootDir),
15525
15652
  stream
15526
15653
  };
15527
15654
  } finally {
15528
15655
  await iter?.return?.(void 0);
15529
15656
  ac.abort();
15530
- await rm(projectDir, {
15657
+ await rm(rootDir, {
15531
15658
  force: true,
15532
15659
  recursive: true
15533
15660
  });
@@ -18606,7 +18733,7 @@ async function runBridgeUninstall() {
18606
18733
 
18607
18734
  //#endregion
18608
18735
  //#region package.json
18609
- var version = "0.3.3";
18736
+ var version = "0.3.5";
18610
18737
 
18611
18738
  //#endregion
18612
18739
  //#region cli.ts
package/package.json CHANGED
@@ -14,15 +14,15 @@
14
14
  "devDependencies": {
15
15
  "commander": "^14.0.3",
16
16
  "consola": "^3.4.2",
17
- "@usecontextlayer/shared": "0.3.3",
18
- "@usecontextlayer/slate-bridge": "0.3.3"
17
+ "@usecontextlayer/shared": "0.3.5",
18
+ "@usecontextlayer/slate-bridge": "0.3.5"
19
19
  },
20
20
  "files": [
21
21
  "dist"
22
22
  ],
23
23
  "name": "@usecontextlayer/ctxs",
24
24
  "type": "module",
25
- "version": "0.3.3",
25
+ "version": "0.3.5",
26
26
  "scripts": {
27
27
  "build": "npx tsdown",
28
28
  "clean": "rm -rf dist *.tsbuildinfo",