osborn 0.9.154 β 0.9.156
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/Dockerfile.sandbox +1 -0
- package/dist/claude-llm.js +8 -1
- package/dist/index.js +59 -4
- package/package.json +1 -1
package/Dockerfile.sandbox
CHANGED
|
@@ -101,6 +101,7 @@ ENV OSBORN_CWD=/workspace
|
|
|
101
101
|
# Caveat: native user modules are tied to the image's Node ABI (currently 22) β
|
|
102
102
|
# a future Node-major image bump would need an `npm rebuild` of volume globals.
|
|
103
103
|
ENV NPM_CONFIG_PREFIX=/workspace/.npm-global
|
|
104
|
+
ENV NPM_CONFIG_CACHE=/tmp/npm-cache
|
|
104
105
|
ENV PATH=/workspace/.npm-global/bin:$PATH
|
|
105
106
|
|
|
106
107
|
WORKDIR /workspace
|
package/dist/claude-llm.js
CHANGED
|
@@ -140,10 +140,17 @@ function ensureCompactionSettings() {
|
|
|
140
140
|
else {
|
|
141
141
|
mkdirSync(claudeDir, { recursive: true });
|
|
142
142
|
}
|
|
143
|
+
let dirty = false;
|
|
143
144
|
if (settings.autoCompactWindow !== 1_000_000) {
|
|
144
145
|
settings.autoCompactWindow = 1_000_000;
|
|
146
|
+
dirty = true;
|
|
147
|
+
}
|
|
148
|
+
if (settings.cleanupPeriodDays !== 3650) {
|
|
149
|
+
settings.cleanupPeriodDays = 3650;
|
|
150
|
+
dirty = true;
|
|
151
|
+
}
|
|
152
|
+
if (dirty) {
|
|
145
153
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
146
|
-
console.log('πͺ autoCompactWindow set to 1,000,000 in', settingsPath);
|
|
147
154
|
}
|
|
148
155
|
}
|
|
149
156
|
catch (err) {
|
package/dist/index.js
CHANGED
|
@@ -267,6 +267,7 @@ let codeServerStartHook = null;
|
|
|
267
267
|
// code-server runs on 127.0.0.1:8300; the agent proxies it through its own
|
|
268
268
|
// public HTTP server so no Cloudflare tunnel is needed.
|
|
269
269
|
const IDE_TARGET = 'http://127.0.0.1:8300';
|
|
270
|
+
const OSBORN_API_PORT = parseInt(process.env.OSBORN_API_PORT || '8741', 10);
|
|
270
271
|
// Route prefixes that belong to the agent itself. The proxy fall-through checks
|
|
271
272
|
// this list FIRST so it never intercepts an agent API path.
|
|
272
273
|
const AGENT_ROUTE_PREFIXES = [
|
|
@@ -1498,21 +1499,44 @@ function startApiServer(workingDir, port) {
|
|
|
1498
1499
|
// 3 s until ready.
|
|
1499
1500
|
if (url.pathname === '/editor' && req.method === 'GET') {
|
|
1500
1501
|
// AUTH GATE (future): validate key / signed session before minting the cookie
|
|
1502
|
+
// Optional ?folder= param: must be an absolute path under /workspace with no
|
|
1503
|
+
// directory-traversal sequences. If absent or invalid, fall back to bare '/'.
|
|
1504
|
+
const rawFolder = url.searchParams.get('folder') ?? '';
|
|
1505
|
+
const folderParam = (() => {
|
|
1506
|
+
if (!rawFolder)
|
|
1507
|
+
return '';
|
|
1508
|
+
// Must be absolute, start with /workspace, no traversal sequences
|
|
1509
|
+
if (!rawFolder.startsWith('/workspace') ||
|
|
1510
|
+
rawFolder.includes('..') ||
|
|
1511
|
+
rawFolder.includes('\0'))
|
|
1512
|
+
return '';
|
|
1513
|
+
// Normalise and re-check (catches encoded or doubled slashes resolving outside /workspace)
|
|
1514
|
+
const normalised = rawFolder.replace(/\/+/g, '/');
|
|
1515
|
+
if (!normalised.startsWith('/workspace'))
|
|
1516
|
+
return '';
|
|
1517
|
+
return normalised;
|
|
1518
|
+
})();
|
|
1501
1519
|
if (ideProxyEnabled && ideSessionToken) {
|
|
1502
|
-
// code-server is up β set cookie and redirect
|
|
1520
|
+
// code-server is up β set cookie and redirect into the requested folder (if valid)
|
|
1521
|
+
// or to root if no valid folder was supplied.
|
|
1522
|
+
const location = folderParam ? `/?folder=${encodeURIComponent(folderParam)}` : '/';
|
|
1503
1523
|
res.writeHead(302, {
|
|
1504
1524
|
'Set-Cookie': `osborn_ide=${ideSessionToken}; Path=/; HttpOnly; SameSite=Lax`,
|
|
1505
|
-
'Location':
|
|
1525
|
+
'Location': location,
|
|
1506
1526
|
});
|
|
1507
1527
|
res.end();
|
|
1508
1528
|
}
|
|
1509
1529
|
else {
|
|
1510
|
-
// Not yet running β kick off start in the background, show "Startingβ¦" page
|
|
1530
|
+
// Not yet running β kick off start in the background, show "Startingβ¦" page.
|
|
1531
|
+
// Preserve ?folder= in the refresh URL so it survives the polling loop.
|
|
1511
1532
|
if (codeServerStartHook) {
|
|
1512
1533
|
codeServerStartHook().catch((err) => {
|
|
1513
1534
|
console.error('β startCodeServer() from /editor failed:', err);
|
|
1514
1535
|
});
|
|
1515
1536
|
}
|
|
1537
|
+
const refreshTarget = folderParam
|
|
1538
|
+
? `/editor?folder=${encodeURIComponent(folderParam)}`
|
|
1539
|
+
: '/editor';
|
|
1516
1540
|
res.writeHead(200, {
|
|
1517
1541
|
'Content-Type': 'text/html; charset=utf-8',
|
|
1518
1542
|
'Cache-Control': 'no-store, must-revalidate',
|
|
@@ -1520,7 +1544,7 @@ function startApiServer(workingDir, port) {
|
|
|
1520
1544
|
});
|
|
1521
1545
|
res.end(`<!DOCTYPE html><html><head>
|
|
1522
1546
|
<meta charset="utf-8">
|
|
1523
|
-
<meta http-equiv="refresh" content="3;url
|
|
1547
|
+
<meta http-equiv="refresh" content="3;url=${refreshTarget}">
|
|
1524
1548
|
<title>Starting editorβ¦</title>
|
|
1525
1549
|
<style>body{font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#1e1e1e;color:#ccc}</style>
|
|
1526
1550
|
</head><body><p>Starting your editorβ¦</p></body></html>`);
|
|
@@ -1528,6 +1552,37 @@ function startApiServer(workingDir, port) {
|
|
|
1528
1552
|
return;
|
|
1529
1553
|
}
|
|
1530
1554
|
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
1555
|
+
// ββ Port proxy: /proxy/PORT/* β localhost:PORT/proxy/PORT/* βββββββββββββ
|
|
1556
|
+
// Forwards to a local dev server while PRESERVING the full path so that
|
|
1557
|
+
// basePath-aware apps (e.g. Next.js with basePath: '/proxy/3000') receive
|
|
1558
|
+
// the complete URL and can strip the prefix themselves. No cookie required β
|
|
1559
|
+
// the machine is per-user and Fly's HTTPS is the outer auth boundary.
|
|
1560
|
+
const portProxyMatch = url.pathname.match(/^\/proxy\/(\d+)(\/.*)?$/);
|
|
1561
|
+
if (portProxyMatch) {
|
|
1562
|
+
const targetPort = parseInt(portProxyMatch[1]);
|
|
1563
|
+
if (targetPort > 1024 && targetPort < 65536 && targetPort !== 8300 && targetPort !== OSBORN_API_PORT) {
|
|
1564
|
+
const portProxyOptions = {
|
|
1565
|
+
hostname: '127.0.0.1',
|
|
1566
|
+
port: targetPort,
|
|
1567
|
+
path: url.pathname + (url.search || ''),
|
|
1568
|
+
method: req.method,
|
|
1569
|
+
headers: { ...req.headers, host: `127.0.0.1:${targetPort}` },
|
|
1570
|
+
};
|
|
1571
|
+
const portProxyReq = httpRequest(portProxyOptions, (portProxyRes) => {
|
|
1572
|
+
res.writeHead(portProxyRes.statusCode || 502, portProxyRes.headers);
|
|
1573
|
+
portProxyRes.pipe(res);
|
|
1574
|
+
});
|
|
1575
|
+
portProxyReq.on('error', () => {
|
|
1576
|
+
if (!res.headersSent) {
|
|
1577
|
+
res.writeHead(502);
|
|
1578
|
+
res.end('Port proxy error');
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
req.pipe(portProxyReq);
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
1531
1586
|
// ββ IDE reverse proxy (cookie-gated fall-through) ββββββββββββββββββββββββ
|
|
1532
1587
|
// Forwards to code-server ONLY when:
|
|
1533
1588
|
// 1. ideProxyEnabled is true (code-server is confirmed ready), AND
|