upfynai-code 3.0.4 → 3.2.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/README.md +69 -92
- package/bin/cli.js +191 -0
- package/dist/client/assets/AppContent-M14Au3SB.js +542 -0
- package/{client/dist/assets/BrowserPanel-0TLEl-IC.js → dist/client/assets/BrowserPanel-TFKm2NDJ.js} +2 -2
- package/dist/client/assets/DashboardPanel-C88HjsCh.js +1 -0
- package/dist/client/assets/FileTree-DvO1xnDE.js +1 -0
- package/{client/dist/assets/GitPanel-C_xFM-N2.js → dist/client/assets/GitPanel-D-slVlyy.js} +2 -2
- package/dist/client/assets/LoginModal-Chi4SYcr.js +21 -0
- package/{client/dist/assets/MarkdownPreview-CESjI261.js → dist/client/assets/MarkdownPreview-CuIix2u9.js} +1 -1
- package/dist/client/assets/MermaidBlock-Dq9uFv82.js +2 -0
- package/dist/client/assets/Onboarding-QYXx24dX.js +1 -0
- package/{client/dist/assets/PreviewPanel-CqCa92Tf.js → dist/client/assets/PreviewPanel-Dd8q-jo0.js} +1 -1
- package/dist/client/assets/SetupForm-CrspaUva.js +1 -0
- package/dist/client/assets/WorkflowsPanel-DIlYAdhB.js +1 -0
- package/dist/client/assets/index-CnNNzw9A.css +1 -0
- package/{client/dist/assets/index-HaY-3pK1.js → dist/client/assets/index-rUkK9FDP.js} +26 -26
- package/{client/dist/assets/vendor-codemirror-D2ALgpaX.js → dist/client/assets/vendor-codemirror-jc6nyJQg.js} +1 -1
- package/{client/dist/assets/vendor-diff-DNQpbhrT.js → dist/client/assets/vendor-diff-THJmAcEI.js} +1 -1
- package/{client/dist/assets/vendor-icons-GyYE35HP.js → dist/client/assets/vendor-icons-CfjIpdrD.js} +145 -155
- package/{client/dist/assets/vendor-markdown-CimbIo6Y.js → dist/client/assets/vendor-markdown-Cdm6NEGf.js} +1 -1
- package/dist/client/assets/vendor-mermaid-DTPaBx-U.js +2559 -0
- package/{client/dist/assets/vendor-react-96lCPsRK.js → dist/client/assets/vendor-react-wFkb6mSf.js} +1 -1
- package/{client/dist/assets/vendor-syntax-LS_Nt30I.js → dist/client/assets/vendor-syntax-C_UZR7tc.js} +1 -1
- package/dist/client/favicon.png +0 -0
- package/dist/client/icons/icon-128x128.png +0 -0
- package/dist/client/icons/icon-144x144.png +0 -0
- package/dist/client/icons/icon-152x152.png +0 -0
- package/dist/client/icons/icon-192x192.png +0 -0
- package/dist/client/icons/icon-384x384.png +0 -0
- package/dist/client/icons/icon-512x512.png +0 -0
- package/dist/client/icons/icon-72x72.png +0 -0
- package/dist/client/icons/icon-96x96.png +0 -0
- package/{client/dist → dist/client}/index.html +37 -36
- package/dist/client/logo-128.png +0 -0
- package/dist/client/logo-256.png +0 -0
- package/dist/client/logo-32.png +0 -0
- package/dist/client/logo-512.png +0 -0
- package/dist/client/logo-64.png +0 -0
- package/dist/client/logo.png +0 -0
- package/{client/dist → dist/client}/manifest.json +12 -12
- package/{client/dist → dist/client}/mcp-docs.html +1 -1
- package/{client/dist → dist/client}/sw.js +2 -2
- package/package.json +56 -105
- package/scripts/postinstall.js +9 -0
- package/scripts/prepublish.js +77 -0
- package/src/animation.js +228 -0
- package/src/auth.js +142 -0
- package/src/config.js +40 -0
- package/src/connect.js +416 -0
- package/src/launch.js +81 -0
- package/src/mcp.js +57 -0
- package/src/permissions.js +140 -0
- package/src/persistent-shell.js +261 -0
- package/src/server.js +54 -0
- package/client/dist/assets/AppContent-CwrTP6TW.js +0 -545
- package/client/dist/assets/CanvasFullScreen-D1GWQsGL.js +0 -1
- package/client/dist/assets/CanvasWorkspace-D7ORj358.js +0 -163
- package/client/dist/assets/DashboardPanel-BV7ybUDe.js +0 -1
- package/client/dist/assets/FileTree-5qfhBqdE.js +0 -1
- package/client/dist/assets/LoginModal-CImJHRjX.js +0 -13
- package/client/dist/assets/MermaidBlock-BFM21cwe.js +0 -2
- package/client/dist/assets/Onboarding-B3cteLu2.js +0 -1
- package/client/dist/assets/SetupForm-P6dsYgHO.js +0 -1
- package/client/dist/assets/WorkflowsPanel-CBoN80kc.js +0 -1
- package/client/dist/assets/index-46kkVu2i.css +0 -1
- package/client/dist/assets/pdf-CE_K4jFx.js +0 -12
- package/client/dist/assets/vendor-canvas-BZV40eAE.css +0 -1
- package/client/dist/assets/vendor-canvas-DvHJ_Pn2.js +0 -49
- package/client/dist/assets/vendor-mermaid-DucWyDEe.js +0 -2556
- package/client/dist/favicon.png +0 -0
- package/client/dist/icons/icon-128x128.png +0 -0
- package/client/dist/icons/icon-144x144.png +0 -0
- package/client/dist/icons/icon-152x152.png +0 -0
- package/client/dist/icons/icon-192x192.png +0 -0
- package/client/dist/icons/icon-384x384.png +0 -0
- package/client/dist/icons/icon-512x512.png +0 -0
- package/client/dist/icons/icon-72x72.png +0 -0
- package/client/dist/icons/icon-96x96.png +0 -0
- package/client/dist/logo-128.png +0 -0
- package/client/dist/logo-256.png +0 -0
- package/client/dist/logo-32.png +0 -0
- package/client/dist/logo-512.png +0 -0
- package/client/dist/logo-64.png +0 -0
- package/commands/upfynai-connect.md +0 -59
- package/commands/upfynai-disconnect.md +0 -31
- package/commands/upfynai-doctor.md +0 -99
- package/commands/upfynai-export.md +0 -49
- package/commands/upfynai-local.md +0 -82
- package/commands/upfynai-status.md +0 -75
- package/commands/upfynai-stop.md +0 -49
- package/commands/upfynai-uninstall.md +0 -58
- package/commands/upfynai.md +0 -69
- package/scripts/build-client.js +0 -17
- package/scripts/fix-node-pty.js +0 -67
- package/scripts/install-commands.js +0 -78
- package/server/agent-loop.js +0 -242
- package/server/auto-compact.js +0 -99
- package/server/browser.js +0 -131
- package/server/claude-sdk.js +0 -797
- package/server/cli-ui.js +0 -798
- package/server/cli.js +0 -751
- package/server/constants/config.js +0 -31
- package/server/cursor-cli.js +0 -270
- package/server/database/auth.db +0 -0
- package/server/database/db.js +0 -1547
- package/server/database/init.sql +0 -70
- package/server/index.js +0 -3813
- package/server/load-env.js +0 -26
- package/server/mcp-server.js +0 -621
- package/server/middleware/auth.js +0 -184
- package/server/middleware/relayHelpers.js +0 -44
- package/server/middleware/sandboxRouter.js +0 -174
- package/server/openai-codex.js +0 -403
- package/server/openrouter.js +0 -137
- package/server/projects.js +0 -1807
- package/server/provider-factory.js +0 -174
- package/server/relay-client.js +0 -390
- package/server/routes/agent.js +0 -1234
- package/server/routes/auth.js +0 -559
- package/server/routes/browser.js +0 -419
- package/server/routes/canvas.js +0 -53
- package/server/routes/cli-auth.js +0 -263
- package/server/routes/codex.js +0 -396
- package/server/routes/commands.js +0 -707
- package/server/routes/composio.js +0 -176
- package/server/routes/cursor.js +0 -770
- package/server/routes/dashboard.js +0 -295
- package/server/routes/git.js +0 -1208
- package/server/routes/keys.js +0 -34
- package/server/routes/mcp-utils.js +0 -48
- package/server/routes/mcp.js +0 -661
- package/server/routes/payments.js +0 -227
- package/server/routes/projects.js +0 -754
- package/server/routes/sessions.js +0 -146
- package/server/routes/settings.js +0 -261
- package/server/routes/taskmaster.js +0 -1928
- package/server/routes/user.js +0 -106
- package/server/routes/vapi-chat.js +0 -624
- package/server/routes/voice.js +0 -235
- package/server/routes/webhooks.js +0 -166
- package/server/routes/workflows.js +0 -312
- package/server/sandbox.js +0 -120
- package/server/services/browser-ai.js +0 -154
- package/server/services/composio.js +0 -204
- package/server/services/sessionRegistry.js +0 -139
- package/server/services/whisperService.js +0 -84
- package/server/services/workflowScheduler.js +0 -211
- package/server/tests/relay-flow.test.js +0 -570
- package/server/tests/sessions.test.js +0 -259
- package/server/utils/commandParser.js +0 -303
- package/server/utils/email.js +0 -66
- package/server/utils/gitConfig.js +0 -24
- package/server/utils/mcp-detector.js +0 -198
- package/server/utils/taskmaster-websocket.js +0 -129
- package/shared/integrationCatalog.d.ts +0 -12
- package/shared/integrationCatalog.js +0 -172
- package/shared/modelConstants.js +0 -96
- /package/{shared → dist}/agents/claude.js +0 -0
- /package/{shared → dist}/agents/codex.js +0 -0
- /package/{shared → dist}/agents/cursor.js +0 -0
- /package/{shared → dist}/agents/detect.js +0 -0
- /package/{shared → dist}/agents/exec.js +0 -0
- /package/{shared → dist}/agents/files.js +0 -0
- /package/{shared → dist}/agents/git.js +0 -0
- /package/{shared → dist}/agents/gitagent.js +0 -0
- /package/{shared → dist}/agents/index.js +0 -0
- /package/{shared → dist}/agents/shell.js +0 -0
- /package/{shared → dist}/agents/utils.js +0 -0
- /package/{client/dist → dist/client}/api-docs.html +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- /package/{client/dist → dist/client}/assets/vendor-i18n-DCFGyhQR.js +0 -0
- /package/{client/dist → dist/client}/assets/vendor-xterm-CZq1hqo1.js +0 -0
- /package/{client/dist → dist/client}/assets/vendor-xterm-qxJ8_QYu.css +0 -0
- /package/{client/dist → dist/client}/clear-cache.html +0 -0
- /package/{client/dist → dist/client}/convert-icons.md +0 -0
- /package/{client/dist → dist/client}/favicon.svg +0 -0
- /package/{client/dist → dist/client}/generate-icons.js +0 -0
- /package/{client/dist → dist/client}/icons/claude-ai-icon.svg +0 -0
- /package/{client/dist → dist/client}/icons/codex-white.svg +0 -0
- /package/{client/dist → dist/client}/icons/codex.svg +0 -0
- /package/{client/dist → dist/client}/icons/cursor-white.svg +0 -0
- /package/{client/dist → dist/client}/icons/cursor.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-128x128.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-144x144.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-152x152.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-192x192.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-384x384.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-512x512.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-72x72.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-96x96.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-template.svg +0 -0
- /package/{client/dist → dist/client}/logo.svg +0 -0
- /package/{client/dist → dist/client}/offline.html +0 -0
- /package/{client/dist → dist/client}/screenshots/cli-selection.png +0 -0
- /package/{client/dist → dist/client}/screenshots/desktop-main.png +0 -0
- /package/{client/dist → dist/client}/screenshots/mobile-chat.png +0 -0
- /package/{client/dist → dist/client}/screenshots/tools-modal.png +0 -0
- /package/{shared → dist}/gitagent/index.js +0 -0
- /package/{shared → dist}/gitagent/parser.js +0 -0
- /package/{shared → dist}/gitagent/prompt-builder.js +0 -0
package/scripts/fix-node-pty.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Fix node-pty spawn-helper permissions on macOS
|
|
4
|
-
*
|
|
5
|
-
* This script fixes a known issue with node-pty where the spawn-helper
|
|
6
|
-
* binary is shipped without execute permissions, causing "posix_spawnp failed" errors.
|
|
7
|
-
*
|
|
8
|
-
* @see https://github.com/microsoft/node-pty/issues/850
|
|
9
|
-
* @module scripts/fix-node-pty
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { promises as fs } from 'fs';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
import { fileURLToPath } from 'url';
|
|
15
|
-
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname = path.dirname(__filename);
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Fixes the spawn-helper binary permissions for node-pty on macOS.
|
|
21
|
-
*
|
|
22
|
-
* The node-pty package ships the spawn-helper binary without execute permissions
|
|
23
|
-
* (644 instead of 755), which causes "posix_spawnp failed" errors when trying
|
|
24
|
-
* to spawn terminal processes.
|
|
25
|
-
*
|
|
26
|
-
* This function:
|
|
27
|
-
* 1. Checks if running on macOS (darwin)
|
|
28
|
-
* 2. Locates spawn-helper binaries for both arm64 and x64 architectures
|
|
29
|
-
* 3. Sets execute permissions (755) on each binary found
|
|
30
|
-
*
|
|
31
|
-
* @async
|
|
32
|
-
* @function fixSpawnHelper
|
|
33
|
-
* @returns {Promise<void>} Resolves when permissions are fixed or skipped
|
|
34
|
-
* @example
|
|
35
|
-
* // Run as postinstall script
|
|
36
|
-
* await fixSpawnHelper();
|
|
37
|
-
*/
|
|
38
|
-
async function fixSpawnHelper() {
|
|
39
|
-
const nodeModulesPath = path.join(__dirname, '..', 'node_modules', 'node-pty', 'prebuilds');
|
|
40
|
-
|
|
41
|
-
// Only run on macOS
|
|
42
|
-
if (process.platform !== 'darwin') {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const darwinDirs = ['darwin-arm64', 'darwin-x64'];
|
|
47
|
-
|
|
48
|
-
for (const dir of darwinDirs) {
|
|
49
|
-
const spawnHelperPath = path.join(nodeModulesPath, dir, 'spawn-helper');
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
// Check if file exists
|
|
53
|
-
await fs.access(spawnHelperPath);
|
|
54
|
-
|
|
55
|
-
// Make it executable (755)
|
|
56
|
-
await fs.chmod(spawnHelperPath, 0o755);
|
|
57
|
-
console.log(`[postinstall] Fixed permissions for ${spawnHelperPath}`);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
// File doesn't exist or other error - ignore
|
|
60
|
-
if (err.code !== 'ENOENT') {
|
|
61
|
-
console.warn(`[postinstall] Warning: Could not fix ${spawnHelperPath}: ${err.message}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
fixSpawnHelper().catch(console.error);
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Upfyn-Code — Slash Command Installer
|
|
4
|
-
*
|
|
5
|
-
* Copies slash command .md files to ~/.claude/commands/
|
|
6
|
-
* so they become available as /upfynai-* inside your AI CLI.
|
|
7
|
-
*
|
|
8
|
-
* Run manually: node scripts/install-commands.js
|
|
9
|
-
* Or via CLI: uc install-commands
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import fs from 'fs';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
import os from 'os';
|
|
15
|
-
import { fileURLToPath } from 'url';
|
|
16
|
-
|
|
17
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
-
const __dirname = path.dirname(__filename);
|
|
19
|
-
|
|
20
|
-
const COMMANDS_SOURCE = path.join(__dirname, '..', 'commands');
|
|
21
|
-
const COMMANDS_DEST = path.join(os.homedir(), '.claude', 'commands');
|
|
22
|
-
|
|
23
|
-
const colors = {
|
|
24
|
-
reset: '\x1b[0m',
|
|
25
|
-
green: '\x1b[32m',
|
|
26
|
-
yellow: '\x1b[33m',
|
|
27
|
-
cyan: '\x1b[36m',
|
|
28
|
-
dim: '\x1b[2m',
|
|
29
|
-
bright: '\x1b[1m',
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
function main() {
|
|
33
|
-
console.log(`\n${colors.bright}Upfyn-Code — Installing Slash Commands${colors.reset}\n`);
|
|
34
|
-
|
|
35
|
-
// Ensure ~/.claude/commands/ exists
|
|
36
|
-
if (!fs.existsSync(COMMANDS_DEST)) {
|
|
37
|
-
fs.mkdirSync(COMMANDS_DEST, { recursive: true });
|
|
38
|
-
console.log(`${colors.green}✅${colors.reset} Created ${colors.dim}${COMMANDS_DEST}${colors.reset}`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Read all .md files from commands source
|
|
42
|
-
if (!fs.existsSync(COMMANDS_SOURCE)) {
|
|
43
|
-
console.error(`${colors.yellow}⚠️${colors.reset} Commands source not found: ${COMMANDS_SOURCE}`);
|
|
44
|
-
process.exit(0);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const files = fs.readdirSync(COMMANDS_SOURCE).filter(f => f.endsWith('.md'));
|
|
48
|
-
|
|
49
|
-
if (files.length === 0) {
|
|
50
|
-
console.log(`${colors.yellow}⚠️${colors.reset} No command files found in ${COMMANDS_SOURCE}`);
|
|
51
|
-
process.exit(0);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let installed = 0;
|
|
55
|
-
let updated = 0;
|
|
56
|
-
|
|
57
|
-
for (const file of files) {
|
|
58
|
-
const src = path.join(COMMANDS_SOURCE, file);
|
|
59
|
-
const dest = path.join(COMMANDS_DEST, file);
|
|
60
|
-
|
|
61
|
-
const existed = fs.existsSync(dest);
|
|
62
|
-
fs.copyFileSync(src, dest);
|
|
63
|
-
|
|
64
|
-
if (existed) {
|
|
65
|
-
updated++;
|
|
66
|
-
console.log(`${colors.cyan}🔄${colors.reset} Updated /${file.replace('.md', '')}`);
|
|
67
|
-
} else {
|
|
68
|
-
installed++;
|
|
69
|
-
console.log(`${colors.green}✅${colors.reset} Installed /${file.replace('.md', '')}`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
console.log(`\n${colors.bright}Done!${colors.reset} ${installed} installed, ${updated} updated`);
|
|
74
|
-
console.log(`${colors.dim}Commands are now available in your AI CLI${colors.reset}`);
|
|
75
|
-
console.log(`${colors.dim}Try: /upfynai to start the web UI${colors.reset}\n`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
main();
|
package/server/agent-loop.js
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Loop: Stream → Tool Execute → Loop
|
|
3
|
-
* Ported from opencode-ai/opencode internal/llm/agent/agent.go
|
|
4
|
-
*
|
|
5
|
-
* For cloud mode (BYOK): when the AI returns tool_use calls, execute them
|
|
6
|
-
* server-side (or via relay), append results, and loop until no more tool calls.
|
|
7
|
-
*
|
|
8
|
-
* This implements the core agentic loop pattern:
|
|
9
|
-
* 1. Stream response from LLM
|
|
10
|
-
* 2. Collect tool calls from the response
|
|
11
|
-
* 3. Execute tools (file read, shell, etc.)
|
|
12
|
-
* 4. Append tool results to message history
|
|
13
|
-
* 5. Loop back to step 1 until AI finishes without tool calls
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { streamChat, calculateCost } from './provider-factory.js';
|
|
17
|
-
import { checkNeedsCompact, buildSummarizationPrompt, compactMessages } from './auto-compact.js';
|
|
18
|
-
|
|
19
|
-
const MAX_TOOL_LOOPS = 25; // Safety limit to prevent infinite loops
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Available server-side tools (opencode pattern: tool registry per agent type)
|
|
23
|
-
* These are tools the AI can call during the agent loop.
|
|
24
|
-
* For relay mode, tool execution is forwarded to the CLI.
|
|
25
|
-
* For cloud mode, tools execute on the server or sandbox.
|
|
26
|
-
*/
|
|
27
|
-
const TOOLS_SCHEMA = [
|
|
28
|
-
{
|
|
29
|
-
type: 'function',
|
|
30
|
-
function: {
|
|
31
|
-
name: 'read_file',
|
|
32
|
-
description: 'Read the contents of a file',
|
|
33
|
-
parameters: {
|
|
34
|
-
type: 'object',
|
|
35
|
-
properties: {
|
|
36
|
-
path: { type: 'string', description: 'File path to read' },
|
|
37
|
-
},
|
|
38
|
-
required: ['path'],
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
type: 'function',
|
|
44
|
-
function: {
|
|
45
|
-
name: 'write_file',
|
|
46
|
-
description: 'Write content to a file',
|
|
47
|
-
parameters: {
|
|
48
|
-
type: 'object',
|
|
49
|
-
properties: {
|
|
50
|
-
path: { type: 'string', description: 'File path to write' },
|
|
51
|
-
content: { type: 'string', description: 'File content' },
|
|
52
|
-
},
|
|
53
|
-
required: ['path', 'content'],
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
type: 'function',
|
|
59
|
-
function: {
|
|
60
|
-
name: 'run_command',
|
|
61
|
-
description: 'Execute a shell command',
|
|
62
|
-
parameters: {
|
|
63
|
-
type: 'object',
|
|
64
|
-
properties: {
|
|
65
|
-
command: { type: 'string', description: 'Shell command to run' },
|
|
66
|
-
cwd: { type: 'string', description: 'Working directory (optional)' },
|
|
67
|
-
},
|
|
68
|
-
required: ['command'],
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
type: 'function',
|
|
74
|
-
function: {
|
|
75
|
-
name: 'list_files',
|
|
76
|
-
description: 'List files in a directory',
|
|
77
|
-
parameters: {
|
|
78
|
-
type: 'object',
|
|
79
|
-
properties: {
|
|
80
|
-
path: { type: 'string', description: 'Directory path' },
|
|
81
|
-
pattern: { type: 'string', description: 'Glob pattern (optional)' },
|
|
82
|
-
},
|
|
83
|
-
required: ['path'],
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
type: 'function',
|
|
89
|
-
function: {
|
|
90
|
-
name: 'search_files',
|
|
91
|
-
description: 'Search for text in files',
|
|
92
|
-
parameters: {
|
|
93
|
-
type: 'object',
|
|
94
|
-
properties: {
|
|
95
|
-
query: { type: 'string', description: 'Search query (regex)' },
|
|
96
|
-
path: { type: 'string', description: 'Directory to search in' },
|
|
97
|
-
},
|
|
98
|
-
required: ['query'],
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
];
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Run the agent loop (opencode pattern: processGeneration).
|
|
106
|
-
*
|
|
107
|
-
* @param {string} model - LLM model name
|
|
108
|
-
* @param {Array} messages - Message history
|
|
109
|
-
* @param {object} options - { apiKey, systemPrompt, onEvent, executeTool, maxLoops }
|
|
110
|
-
* @returns {Promise<{ messages, usage, cost }>}
|
|
111
|
-
*
|
|
112
|
-
* options.onEvent(event) - Called for each streaming event:
|
|
113
|
-
* { type: 'content_delta', content: '...' }
|
|
114
|
-
* { type: 'tool_call', name: '...', input: {...} }
|
|
115
|
-
* { type: 'tool_result', name: '...', output: '...' }
|
|
116
|
-
* { type: 'complete' }
|
|
117
|
-
* { type: 'error', error: '...' }
|
|
118
|
-
*
|
|
119
|
-
* options.executeTool(name, input) - Executes a tool, returns string result.
|
|
120
|
-
* If not provided, tool calls are skipped (text-only mode).
|
|
121
|
-
*/
|
|
122
|
-
export async function runAgentLoop(model, messages, options = {}) {
|
|
123
|
-
const {
|
|
124
|
-
apiKey,
|
|
125
|
-
systemPrompt,
|
|
126
|
-
onEvent = () => {},
|
|
127
|
-
executeTool,
|
|
128
|
-
maxLoops = MAX_TOOL_LOOPS,
|
|
129
|
-
} = options;
|
|
130
|
-
|
|
131
|
-
let totalUsage = { promptTokens: 0, completionTokens: 0 };
|
|
132
|
-
let loopCount = 0;
|
|
133
|
-
let currentMessages = [...messages];
|
|
134
|
-
|
|
135
|
-
// Add system prompt if provided
|
|
136
|
-
if (systemPrompt && (!currentMessages.length || currentMessages[0].role !== 'system')) {
|
|
137
|
-
currentMessages.unshift({ role: 'system', content: systemPrompt });
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
while (loopCount < maxLoops) {
|
|
141
|
-
loopCount++;
|
|
142
|
-
|
|
143
|
-
// Check if we need auto-compact (opencode pattern: summarize at 85% context)
|
|
144
|
-
const compactCheck = checkNeedsCompact(currentMessages, model);
|
|
145
|
-
if (compactCheck.needsCompact) {
|
|
146
|
-
onEvent({ type: 'system', message: 'Compacting conversation context...' });
|
|
147
|
-
// In a real implementation, this would call the LLM to summarize
|
|
148
|
-
// For now, just trim old messages
|
|
149
|
-
if (currentMessages.length > 10) {
|
|
150
|
-
const recent = currentMessages.slice(-6);
|
|
151
|
-
currentMessages = [currentMessages[0], ...recent]; // keep system + recent
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Stream response from LLM
|
|
156
|
-
let assistantContent = '';
|
|
157
|
-
let toolCalls = [];
|
|
158
|
-
let currentToolCall = null;
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
for await (const event of streamChat(model, currentMessages, {
|
|
162
|
-
apiKey,
|
|
163
|
-
tools: executeTool ? TOOLS_SCHEMA : undefined,
|
|
164
|
-
})) {
|
|
165
|
-
if (event.type === 'content_delta') {
|
|
166
|
-
assistantContent += event.content;
|
|
167
|
-
onEvent({ type: 'content_delta', content: event.content });
|
|
168
|
-
} else if (event.type === 'tool_call_start') {
|
|
169
|
-
currentToolCall = { name: event.name, id: event.id, input: '' };
|
|
170
|
-
} else if (event.type === 'tool_call_delta') {
|
|
171
|
-
if (currentToolCall) currentToolCall.input += event.content;
|
|
172
|
-
} else if (event.type === 'tool_call_end') {
|
|
173
|
-
if (currentToolCall) {
|
|
174
|
-
try { currentToolCall.input = JSON.parse(currentToolCall.input); } catch {}
|
|
175
|
-
toolCalls.push(currentToolCall);
|
|
176
|
-
onEvent({ type: 'tool_call', name: currentToolCall.name, input: currentToolCall.input });
|
|
177
|
-
currentToolCall = null;
|
|
178
|
-
}
|
|
179
|
-
} else if (event.type === 'usage') {
|
|
180
|
-
totalUsage.promptTokens += event.promptTokens;
|
|
181
|
-
totalUsage.completionTokens += event.completionTokens;
|
|
182
|
-
} else if (event.type === 'complete') {
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
} catch (err) {
|
|
187
|
-
onEvent({ type: 'error', error: err.message });
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Append assistant message
|
|
192
|
-
const assistantMsg = { role: 'assistant', content: assistantContent };
|
|
193
|
-
if (toolCalls.length > 0) {
|
|
194
|
-
assistantMsg.tool_calls = toolCalls.map(tc => ({
|
|
195
|
-
id: tc.id || `call_${Date.now()}`,
|
|
196
|
-
type: 'function',
|
|
197
|
-
function: { name: tc.name, arguments: JSON.stringify(tc.input) },
|
|
198
|
-
}));
|
|
199
|
-
}
|
|
200
|
-
currentMessages.push(assistantMsg);
|
|
201
|
-
|
|
202
|
-
// If no tool calls, we're done (opencode pattern: finish reason != ToolUse → return)
|
|
203
|
-
if (toolCalls.length === 0 || !executeTool) {
|
|
204
|
-
onEvent({ type: 'complete' });
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Execute tools and append results (opencode pattern: tool execution loop)
|
|
209
|
-
for (const tc of toolCalls) {
|
|
210
|
-
let result;
|
|
211
|
-
try {
|
|
212
|
-
result = await executeTool(tc.name, tc.input);
|
|
213
|
-
onEvent({ type: 'tool_result', name: tc.name, output: result });
|
|
214
|
-
} catch (err) {
|
|
215
|
-
result = `Error: ${err.message}`;
|
|
216
|
-
onEvent({ type: 'tool_result', name: tc.name, output: result, isError: true });
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
currentMessages.push({
|
|
220
|
-
role: 'tool',
|
|
221
|
-
tool_call_id: tc.id || `call_${Date.now()}`,
|
|
222
|
-
content: typeof result === 'string' ? result : JSON.stringify(result),
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Loop back to get next response (step 5 → step 1)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (loopCount >= maxLoops) {
|
|
230
|
-
onEvent({ type: 'error', error: `Agent loop exceeded maximum iterations (${maxLoops})` });
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const cost = calculateCost(model, totalUsage.promptTokens, totalUsage.completionTokens);
|
|
234
|
-
|
|
235
|
-
return {
|
|
236
|
-
messages: currentMessages,
|
|
237
|
-
usage: totalUsage,
|
|
238
|
-
costCents: cost,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export { TOOLS_SCHEMA };
|
package/server/auto-compact.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-Compact Context Summarization
|
|
3
|
-
* Ported from opencode-ai/opencode internal/llm/agent/agent.go (summarize logic)
|
|
4
|
-
*
|
|
5
|
-
* When a conversation approaches the context window limit, this module
|
|
6
|
-
* summarizes the conversation and returns a condensed version.
|
|
7
|
-
*
|
|
8
|
-
* Used for BYOK cloud mode (OpenRouter, direct API calls) where the server
|
|
9
|
-
* manages the message history. For relay mode, the CLI handles context internally.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// Model context windows (opencode pattern: models.go ContextWindow)
|
|
13
|
-
const CONTEXT_WINDOWS = {
|
|
14
|
-
'claude-sonnet-4-20250514': 200000,
|
|
15
|
-
'claude-opus-4-20250514': 200000,
|
|
16
|
-
'claude-haiku-4-5-20251001': 200000,
|
|
17
|
-
'gpt-4o': 128000,
|
|
18
|
-
'gpt-4o-mini': 128000,
|
|
19
|
-
'o1': 200000,
|
|
20
|
-
'o3-mini': 200000,
|
|
21
|
-
'deepseek-chat': 64000,
|
|
22
|
-
'deepseek-reasoner': 64000,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// Rough token estimation (~4 chars per token)
|
|
26
|
-
function estimateTokens(text) {
|
|
27
|
-
return Math.ceil((text || '').length / 4);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Check if a message history needs compaction.
|
|
32
|
-
* @param {Array} messages - Array of { role, content } messages
|
|
33
|
-
* @param {string} model - Model name
|
|
34
|
-
* @param {number} threshold - Fraction of context window to trigger (default 0.85)
|
|
35
|
-
* @returns {{ needsCompact: boolean, estimatedTokens: number, contextWindow: number }}
|
|
36
|
-
*/
|
|
37
|
-
export function checkNeedsCompact(messages, model, threshold = 0.85) {
|
|
38
|
-
const contextWindow = CONTEXT_WINDOWS[model] || 128000;
|
|
39
|
-
const totalTokens = messages.reduce((sum, m) => sum + estimateTokens(m.content), 0);
|
|
40
|
-
return {
|
|
41
|
-
needsCompact: totalTokens > contextWindow * threshold,
|
|
42
|
-
estimatedTokens: totalTokens,
|
|
43
|
-
contextWindow,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Build a summarization prompt for compacting conversation history.
|
|
49
|
-
* Returns messages array suitable for sending to an LLM.
|
|
50
|
-
*
|
|
51
|
-
* @param {Array} messages - Full message history
|
|
52
|
-
* @param {string} systemPrompt - Original system prompt
|
|
53
|
-
* @returns {Array} Messages for the summarization call
|
|
54
|
-
*/
|
|
55
|
-
export function buildSummarizationPrompt(messages, systemPrompt = '') {
|
|
56
|
-
// Take the conversation content and ask for a summary
|
|
57
|
-
// (opencode pattern: AgentSummarizer with dedicated prompt)
|
|
58
|
-
const conversationText = messages
|
|
59
|
-
.map(m => `[${m.role}]: ${(m.content || '').slice(0, 2000)}`)
|
|
60
|
-
.join('\n\n');
|
|
61
|
-
|
|
62
|
-
return [
|
|
63
|
-
{
|
|
64
|
-
role: 'system',
|
|
65
|
-
content: `You are a conversation summarizer. Summarize the following conversation between a user and an AI coding assistant. Focus on:
|
|
66
|
-
1. Key decisions made
|
|
67
|
-
2. Files modified and why
|
|
68
|
-
3. Current state of the task
|
|
69
|
-
4. Any unresolved issues or next steps
|
|
70
|
-
|
|
71
|
-
Keep the summary concise but preserve all critical technical details. The summary will be used as context for continuing the conversation.`,
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
role: 'user',
|
|
75
|
-
content: `Summarize this conversation:\n\n${conversationText}`,
|
|
76
|
-
},
|
|
77
|
-
];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Compact a message history by replacing old messages with a summary.
|
|
82
|
-
* The summary becomes the first message, preserving the most recent messages.
|
|
83
|
-
*
|
|
84
|
-
* @param {Array} messages - Full message history
|
|
85
|
-
* @param {string} summary - Generated summary text
|
|
86
|
-
* @param {number} keepRecent - Number of recent messages to keep verbatim (default 4)
|
|
87
|
-
* @returns {Array} Compacted messages
|
|
88
|
-
*/
|
|
89
|
-
export function compactMessages(messages, summary, keepRecent = 4) {
|
|
90
|
-
const recentMessages = messages.slice(-keepRecent);
|
|
91
|
-
|
|
92
|
-
return [
|
|
93
|
-
{
|
|
94
|
-
role: 'system',
|
|
95
|
-
content: `[Conversation Summary]\n${summary}\n\n[The conversation continues below with the most recent messages]`,
|
|
96
|
-
},
|
|
97
|
-
...recentMessages,
|
|
98
|
-
];
|
|
99
|
-
}
|
package/server/browser.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser Client — connects the backend to the Steel browser service on Railway.
|
|
3
|
-
* All browser operations are proxied to the Steel service via HTTP.
|
|
4
|
-
* Mirrors the sandbox.js client pattern.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const BROWSER_SERVICE_URL = process.env.BROWSER_SERVICE_URL || 'http://localhost:4400';
|
|
8
|
-
const BROWSER_SERVICE_SECRET = process.env.BROWSER_SERVICE_SECRET || '';
|
|
9
|
-
|
|
10
|
-
// Warn if no secret in production — inter-service calls will be unauthenticated
|
|
11
|
-
if (!BROWSER_SERVICE_SECRET && process.env.NODE_ENV === 'production') {
|
|
12
|
-
console.error('[browser] WARNING: BROWSER_SERVICE_SECRET not set — browser service calls are unauthenticated');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function browserFetch(path, userId, body = null, method = null) {
|
|
16
|
-
const opts = {
|
|
17
|
-
method: method || (body ? 'POST' : 'GET'),
|
|
18
|
-
headers: {
|
|
19
|
-
'Content-Type': 'application/json',
|
|
20
|
-
'x-browser-secret': BROWSER_SERVICE_SECRET,
|
|
21
|
-
'x-user-id': String(userId),
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
if (body) opts.body = JSON.stringify(body);
|
|
25
|
-
|
|
26
|
-
const res = await fetch(`${BROWSER_SERVICE_URL}${path}`, opts);
|
|
27
|
-
const data = await res.json();
|
|
28
|
-
if (!res.ok) throw new Error(data.error || data.message || `Browser service error: ${res.status}`);
|
|
29
|
-
return data;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const browserClient = {
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Check if the browser service is reachable.
|
|
36
|
-
*/
|
|
37
|
-
async isAvailable() {
|
|
38
|
-
try {
|
|
39
|
-
const res = await fetch(`${BROWSER_SERVICE_URL}/api/health`, {
|
|
40
|
-
signal: AbortSignal.timeout(3000),
|
|
41
|
-
});
|
|
42
|
-
return res.ok;
|
|
43
|
-
} catch {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Create a new browser session.
|
|
50
|
-
* Returns: { id, wsEndpoint, debugUrl, sessionViewerUrl, ... }
|
|
51
|
-
*/
|
|
52
|
-
async createSession(userId, options = {}) {
|
|
53
|
-
const sessionOpts = {
|
|
54
|
-
sessionTimeout: options.timeout || 1800000, // 30 min default
|
|
55
|
-
blockAds: options.blockAds !== false,
|
|
56
|
-
solveCaptchas: options.solveCaptchas || false,
|
|
57
|
-
useProxy: options.useProxy || false,
|
|
58
|
-
dimensions: options.dimensions || { width: 1280, height: 800 },
|
|
59
|
-
userAgent: options.userAgent || undefined,
|
|
60
|
-
// Tag with userId for isolation
|
|
61
|
-
sessionContext: {
|
|
62
|
-
userId: String(userId),
|
|
63
|
-
createdAt: new Date().toISOString(),
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
return browserFetch('/v1/sessions', userId, sessionOpts);
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Get session details.
|
|
72
|
-
*/
|
|
73
|
-
async getSession(userId, sessionId) {
|
|
74
|
-
return browserFetch(`/v1/sessions/${sessionId}`, userId);
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Release (close) a browser session.
|
|
79
|
-
*/
|
|
80
|
-
async releaseSession(userId, sessionId) {
|
|
81
|
-
return browserFetch(`/v1/sessions/${sessionId}/release`, userId, {}, 'POST');
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* List active sessions.
|
|
86
|
-
*/
|
|
87
|
-
async listSessions(userId) {
|
|
88
|
-
return browserFetch('/v1/sessions', userId);
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Get the session viewer URL for embedding in an iframe.
|
|
93
|
-
* Steel's built-in viewer provides an interactive browser view.
|
|
94
|
-
*/
|
|
95
|
-
getSessionViewerUrl(sessionId) {
|
|
96
|
-
return `${BROWSER_SERVICE_URL}/v1/sessions/${sessionId}/viewer?interactive=true&showControls=true`;
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Get the CDP WebSocket URL for connecting Stagehand/Playwright.
|
|
101
|
-
*/
|
|
102
|
-
getCdpWsUrl(sessionId) {
|
|
103
|
-
const wsBase = BROWSER_SERVICE_URL.replace(/^http/, 'ws');
|
|
104
|
-
return `${wsBase}?sessionId=${sessionId}`;
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Capture a screenshot of the current page.
|
|
109
|
-
*/
|
|
110
|
-
async screenshot(userId, sessionId) {
|
|
111
|
-
return browserFetch(`/v1/sessions/${sessionId}/screenshot`, userId, {});
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Scrape a URL — returns page content.
|
|
116
|
-
*/
|
|
117
|
-
async scrape(userId, sessionId, url) {
|
|
118
|
-
return browserFetch('/v1/scrape', userId, { url, sessionId });
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Get the sandbox dev server URL that the browser can reach internally.
|
|
123
|
-
* The sandbox and browser share Railway's internal network.
|
|
124
|
-
*/
|
|
125
|
-
getSandboxPreviewUrl(userId, port) {
|
|
126
|
-
const sandboxUrl = process.env.SANDBOX_SERVICE_URL || 'http://localhost:4300';
|
|
127
|
-
return `${sandboxUrl}/proxy/${userId}/${port}`;
|
|
128
|
-
},
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
export { browserClient, BROWSER_SERVICE_URL, BROWSER_SERVICE_SECRET };
|