@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.
- package/dist/cli.mjs +157 -30
- 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.
|
|
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
|
-
|
|
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
|
|
15252
|
-
const
|
|
15253
|
-
|
|
15254
|
-
if (
|
|
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(`
|
|
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(`
|
|
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("
|
|
15391
|
+
c.set("rootDir", resolved);
|
|
15266
15392
|
await next();
|
|
15267
15393
|
};
|
|
15268
|
-
async function pickSessionFlag(
|
|
15269
|
-
return await getSessionInfo(sessionId, { dir:
|
|
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("/:
|
|
15284
|
-
app.get("/:
|
|
15285
|
-
const messages = await getSessionMessages(c.req.param("id"), { dir: c.get("
|
|
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("/:
|
|
15289
|
-
const root = path.join(c.get("
|
|
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("/:
|
|
15325
|
-
const
|
|
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 = `${
|
|
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(
|
|
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,
|
|
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:
|
|
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
|
|
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
|
|
15589
|
+
const rootId = encodeRootId(rootDir);
|
|
15463
15590
|
const sessionId = crypto.randomUUID();
|
|
15464
|
-
const url = `ws://127.0.0.1:${server.port}/${
|
|
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,
|
|
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(
|
|
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.
|
|
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.
|
|
18
|
-
"@usecontextlayer/slate-bridge": "0.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.
|
|
25
|
+
"version": "0.3.5",
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "npx tsdown",
|
|
28
28
|
"clean": "rm -rf dist *.tsbuildinfo",
|