agenttwin 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/api.js +91 -5
- package/dist/cli.js +15 -15
- package/dist/dashboard/assets/dashboard/agent-home.css +12 -9
- package/dist/dashboard/assets/dashboard/dashboard.css +4 -0
- package/dist/dashboard/assets/dashboard/inventory.json +1 -1
- package/dist/dashboard/assets/dashboard/runs.json +1 -1
- package/dist/dashboard/assets/next/static/It81cabE0Vc-SUEO_z8CQ/_buildManifest.js +1 -0
- package/dist/dashboard/assets/next/static/It81cabE0Vc-SUEO_z8CQ/_ssgManifest.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/255-09fb0d129bb12f51.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/772-85561381eedc95da.js +67 -0
- package/dist/dashboard/assets/next/static/chunks/app/_not-found/page-5dc6a60918e8652d.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/dashboard/layout-fa1f6ba56d7af38a.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/dashboard/page-9b4372b1e47bc3bc.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/demo/page-760a47a5e4dee43f.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/docs/[[...slug]]/page-80a0c2c82d5b4efd.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/layout-1a40c59e535513d4.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/page-de4735052adab906.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/pricing/page-7703fc31d6dff0f7.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/sitemap.xml/route-ef5a216a578eb7a8.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/framework-292291387d6b2e39.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/main-aea6e71289384293.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/main-app-99295a8cc37e8a47.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/webpack-330c9b9dd99c0d55.js +1 -0
- package/dist/dashboard/assets/next/static/css/1323d7da89b3fa5e.css +1 -0
- package/dist/dashboard/assets/next/static/css/45513ebc8c6ae800.css +1 -0
- package/dist/dashboard/assets/next/static/css/61ec74d503f353d4.css +1 -0
- package/dist/dashboard/assets/next/static/css/62e3209163fa5e55.css +1 -0
- package/dist/dashboard/assets/next/static/css/9282330a2fd1ab73.css +1 -0
- package/dist/dashboard/assets/next/static/css/c5ac36ef5726e746.css +1 -0
- package/dist/dashboard/assets/next/static/media/inter-var.e4af272c.woff2 +0 -0
- package/dist/dashboard/assets/next/static/media/moonlit-coast.bbe901a1.png +0 -0
- package/dist/dashboard/assets/next/static/media/plexmono-400.d3ebbfd6.woff2 +0 -0
- package/dist/dashboard/index.html +1 -140
- package/dist/gateway.js +91 -5
- package/package.json +1 -1
- package/dist/dashboard/assets/dashboard/agent-home.js +0 -264
- package/dist/dashboard/assets/dashboard/dashboard.js +0 -436
- package/dist/dashboard/assets/dashboard/hosted.js +0 -89
- package/dist/dashboard/assets/dashboard/workflows.js +0 -39
- package/dist/dashboard/assets/vendor/dompurify.LICENSE +0 -202
- package/dist/dashboard/assets/vendor/marked.LICENSE +0 -44
- package/dist/dashboard/assets/vendor/marked.js +0 -82
- package/dist/dashboard/assets/vendor/purify.min.js +0 -7
|
@@ -1,140 +1 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
5
|
-
<title>Dashboard · AgentTwin</title>
|
|
6
|
-
<meta name="description" content="AgentTwin workspace: workflow tests, observed activities, Git revisions and failed checks — all from recorded evidence.">
|
|
7
|
-
<meta name="theme-color" content="#101013">
|
|
8
|
-
<link rel="icon" href="assets/logo/favicon.svg"><link rel="stylesheet" href="assets/brand.css"><link rel="stylesheet" href="assets/dashboard/dashboard.css"><link rel="stylesheet" href="assets/dashboard/workspace.css?v=0.10.0"><link rel="stylesheet" href="assets/dashboard/agent-home.css?v=0.10.0">
|
|
9
|
-
<script src="assets/dashboard/dashboard.js?v=0.10.2" type="module"></script><script src="assets/vendor/marked.js" defer></script>
|
|
10
|
-
<script src="assets/vendor/purify.min.js" defer></script>
|
|
11
|
-
<script src="assets/dashboard/agent-home.js?v=0.10.1" type="module"></script><script src="assets/dashboard/hosted.js?v=0.4.0" defer></script><script src="assets/dashboard/workflows.js?v=0.2.0" type="module"></script>
|
|
12
|
-
</head>
|
|
13
|
-
<body>
|
|
14
|
-
<a class="skip" href="#main">Skip to content</a>
|
|
15
|
-
<div class="app-frame">
|
|
16
|
-
<p class="announce-strip"><span class="record-dot"></span>AgentTwin / workflow testing</p>
|
|
17
|
-
<header class="topbar"><a class="brand" href="#home" aria-label="AgentTwin home"><img src="assets/logo/mark-twin-rings.svg" width="23" height="23" alt="">AgentTwin</a><button id="sidebar-toggle" class="sidebar-toggle" type="button" aria-label="Toggle sidebar" aria-expanded="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M9 4v16"/></svg></button><nav class="home-switcher" aria-label="Switch view"><button type="button" data-view="home" class="active" aria-current="page"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="m3 10.5 9-7.5 9 7.5"/><path d="M5.5 9v10.5h13V9"/></svg>Home<span class="pill-count" id="home-badge" hidden>0</span></button><button type="button" data-view="environment"><img src="assets/icons/ui/stack-simple.svg" alt="">Environment</button><button type="button" data-view="configure"><img src="assets/icons/ui/git-branch.svg" alt="">Workflows</button><button type="button" data-view="history"><img src="assets/icons/ui/clock.svg" alt="">Runs</button><button type="button" data-view="settings"><img src="assets/icons/ui/stack-simple.svg" alt="">Settings</button></nav><div class="home-top-actions"><button type="button" id="home-search" class="home-search" aria-label="Search"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><circle cx="11" cy="11" r="6.5"/><path d="m16 16 4.5 4.5"/></svg><span class="hs-label">Search</span><kbd>⌘K</kbd></button><div class="home-bell-wrap"><button type="button" id="home-bell" class="home-icon-btn" aria-label="Notifications" aria-haspopup="true" aria-expanded="false" aria-controls="home-bell-menu"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M18 9a6 6 0 1 0-12 0c0 6-2 7-2 7h16s-2-1-2-7"/><path d="M10 20a2.2 2.2 0 0 0 4 0"/></svg><span class="bell-badge" id="bell-badge" hidden>0</span></button><div id="home-bell-menu" class="home-menu bell-menu" hidden><p class="menu-label">Notifications</p><div id="bell-items"></div></div></div><button type="button" id="home-expand" class="home-icon-btn" aria-label="Toggle full screen"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M9 3H3v6M15 3h6v6M9 21H3v-6M15 21h6v-6"/></svg></button></div><div class="workspace-name"><span id="workspace-label">Workspace</span><span class="workspace-divider">/</span><span id="current-view">Environment</span></div><div class="account-control"><button type="button" id="account-toggle" class="workspace-avatar" aria-label="Open account menu" aria-expanded="false" aria-controls="account-menu">AT</button><div id="account-menu" class="account-menu" hidden><p class="menu-label">Workspace</p><strong id="account-workspace">My workspace</strong><p id="account-user-name">Your name not set</p><p id="account-menu-status" class="muted">Not connected</p><button type="button" data-action="settings">Account & settings</button><button type="button" data-action="history">View runs</button><button type="button" id="account-disconnect" disabled>Disconnect account</button></div></div></header>
|
|
18
|
-
<div class="app-layout"><nav class="project-tabs app-nav" aria-label="Project views">
|
|
19
|
-
<div class="sidebar-primary"><button id="sidebar-discover" class="sidebar-create" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>Connect project</button>
|
|
20
|
-
</div>
|
|
21
|
-
<div id="sidebar-projects" aria-label="Your repositories"></div>
|
|
22
|
-
<div class="sidebar-bottom"><a class="nav-item" href="docs/"><svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M12 5v15M12 5C9 3 5 3 2 4v15c3-1 7-1 10 1M12 5c3-2 7-2 10-1v15c-3-1-7-1-10 1"/></svg>Documentation<span class="nav-external" aria-hidden="true">↗</span></a>
|
|
23
|
-
</div></nav>
|
|
24
|
-
<main id="main">
|
|
25
|
-
<p id="api-message" class="coverage" role="status" aria-live="polite"></p>
|
|
26
|
-
<section id="home-view" hidden aria-label="Agent home">
|
|
27
|
-
<div class="home-hero"><div id="home-sky" aria-hidden="true"></div>
|
|
28
|
-
<h1 class="home-overview-title">What should your agent test?</h1>
|
|
29
|
-
<section class="home-assistant-bottom" aria-label="Workflow assistant">
|
|
30
|
-
<button type="button" id="assistant-dock-toggle" aria-expanded="false" aria-controls="assistant-dock-panel">Ask about a workflow or test result <span aria-hidden="true">↑</span></button>
|
|
31
|
-
<div class="hero-content" id="assistant-dock-panel" hidden>
|
|
32
|
-
<button type="button" id="assistant-dock-close" aria-label="Minimize workflow assistant">Minimize ↓</button>
|
|
33
|
-
<div class="home-composer" role="form" aria-label="Workflow assistant">
|
|
34
|
-
<div class="assistant-heading"><strong>Ask AgentTwin</strong><span id="assistant-project-context"></span></div>
|
|
35
|
-
<p id="assistant-connection-status" role="status"></p><button type="button" id="assistant-connect">Connect your account</button>
|
|
36
|
-
<label class="visually-hidden" for="composer-input">Ask about your workflow</label>
|
|
37
|
-
<textarea id="composer-input" rows="3" placeholder="What would you like to test?" spellcheck="false"></textarea>
|
|
38
|
-
<div class="composer-row"><span class="assistant-send-hint">Ask about your workflows or run results</span><button type="button" class="composer-send" id="composer-send" aria-label="Send message">Send ↑</button></div>
|
|
39
|
-
<div id="chat-thread" class="chat-thread" aria-label="Workflow conversation" hidden></div>
|
|
40
|
-
<p id="composer-note" class="composer-note" role="status"></p>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
</section>
|
|
44
|
-
</div>
|
|
45
|
-
<div class="home-body">
|
|
46
|
-
<section id="home-next-step" class="next-step home-action" aria-label="Next step"></section>
|
|
47
|
-
<div class="home-grid-head"><span class="grid-hint">Your workspace</span><button type="button" id="home-customize" aria-pressed="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="4" y="4" width="6.5" height="6.5" rx="1.5"/><rect x="13.5" y="4" width="6.5" height="6.5" rx="1.5"/><rect x="4" y="13.5" width="6.5" height="6.5" rx="1.5"/><rect x="13.5" y="13.5" width="6.5" height="6.5" rx="1.5"/></svg>Customize</button></div>
|
|
48
|
-
<div id="home-widget-controls" class="home-widget-controls" hidden></div>
|
|
49
|
-
<div class="home-widgets">
|
|
50
|
-
<article class="widget area-project" id="widget-project"><header><h2>Project discovery</h2></header><div class="w-body" id="project-body"></div></article>
|
|
51
|
-
<article class="widget area-working" id="widget-working"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 3v12a3 3 0 0 0 3 3h9"/><path d="M6 3 3 6m3-3 3 3"/><path d="m15 15 3 3 3-3"/></svg></span><h2>Working now</h2><span class="w-count" id="working-count"></span><span class="w-head-meta" id="working-meta"></span></header><div class="w-body" id="working-body"></div></article>
|
|
52
|
-
<article class="widget area-receipts" id="widget-receipts"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 3h9l4 4v14H6Z"/><path d="M15 3v4h4"/><path d="M9 12h7M9 16h7"/></svg></span><h2>Test results</h2><span class="w-count warn" id="receipts-count"></span><span class="w-head-spin" id="receipts-spin" hidden><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="spin"><path d="M12 3a9 9 0 1 0 9 9" stroke-linecap="round"/></svg></span></header><div class="w-body" id="receipts-body"></div></article>
|
|
53
|
-
<article class="widget area-git" id="widget-git"><header><h2>Git activity</h2><span id="git-meta" class="w-head-meta"></span></header><div class="w-body" id="git-body"></div></article>
|
|
54
|
-
<article class="widget area-sandbox" id="widget-sandbox"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="5" width="18" height="14" rx="3"/><path d="M3 10h18M8 5v5"/></svg></span><h2>Last execution</h2><span class="w-head-meta" id="sandbox-meta"></span></header><div class="w-body" id="sandbox-body"></div></article>
|
|
55
|
-
<article class="widget area-services" id="widget-services"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="2.5"/><path d="M5.5 5.5 10 10m4 4 4.5 4.5M18.5 5.5 14 10m-4 4-4.5 4.5"/></svg></span><h2>Observed activities</h2></header><div class="w-body" id="services-body"></div></article>
|
|
56
|
-
<article class="widget area-failures" id="widget-failures"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="9"/><path d="m9 9 6 6M15 9l-6 6"/></svg></span><h2>Needs attention</h2><span class="w-head-meta" id="failures-meta"></span></header><div class="w-body" id="failures-body"></div></article>
|
|
57
|
-
</div>
|
|
58
|
-
|
|
59
|
-
</div>
|
|
60
|
-
</section>
|
|
61
|
-
<section id="environment-view">
|
|
62
|
-
<div class="heading"><div><p class="eyebrow">Project / environment</p><h1 id="environment-title">A test world for your agent.</h1><p id="environment-subtitle" class="muted">Discover your project. Test what your agent does before it touches production.</p></div><div class="actions"><label class="upload">Import discovery<input id="inventory-import" type="file" accept="application/json,.json"></label><button id="configure-run" class="primary" type="button">Review workflows →</button></div></div>
|
|
63
|
-
<div class="discovery-onboarding">
|
|
64
|
-
<aside class="terminal-panel"><div class="terminal-title"><h2>Connect your project</h2><span>From your terminal</span></div>
|
|
65
|
-
<div class="terminal-content"><div class="terminal-command"><span class="command-prompt" aria-hidden="true">❯</span><code>npx agenttwin init</code><button id="copy-init" type="button" aria-label="Copy discovery command">Copy command <span aria-hidden="true">↗</span></button></div><p class="terminal-comment">Run in your repository · Node.js 22.12+</p>
|
|
66
|
-
<div class="terminal-output" id="terminal-output"></div>
|
|
67
|
-
<details class="discovery-help"><summary>Analyze with a coding agent</summary><p>Init offers OpenCode when installed. To launch it directly, run <code>npx agenttwin init --agent opencode</code>. Other agents can use the generated <code>AGENT-TASK.md</code>.</p></details>
|
|
68
|
-
<details class="discovery-help"><summary>Send findings to this workspace</summary><p>Connect your account in Settings, configure <code>AGENTTWIN_TOKEN</code> in your terminal, then run <code>npx agenttwin sync</code>. This sends discovery metadata. Application files upload separately when you execute a reviewed bundle.</p><button type="button" data-action="settings">Open connection settings →</button></details>
|
|
69
|
-
</div></aside>
|
|
70
|
-
<section class="workflow-area"><div class="section-title"><h2 id="discovery-workflows-title">Discovery</h2><button id="discovery-workflows-link" type="button" data-action="configure">View workflows →</button></div><div id="environment-workflows"></div></section>
|
|
71
|
-
</div>
|
|
72
|
-
<div class="welcome-next"><ol class="welcome-steps"><li><span>01</span><div><strong>Discover</strong><p>Find tools and code paths.</p></div></li><li><span>02</span><div><strong>Review</strong><p>Choose what needs testing.</p></div></li><li><span>03</span><div><strong>Run safely</strong><p>Test through the CLI.</p></div></li></ol><button id="welcome-select-software" type="button">Or select your tools manually <span aria-hidden="true">→</span></button></div>
|
|
73
|
-
<div id="environment-coverage" class="coverage-strip"></div><div class="environment-grid">
|
|
74
|
-
<section class="discovery-area"><div class="section-title"><h2>Selected resources</h2><div class="actions"><button id="add-app-toggle" type="button">Add custom software</button><button id="select-software" type="button">Select software</button></div></div><p id="inventory-context" class="coverage">No project connected. Import the inventory from your CLI to see its dependencies and workflow candidates.</p>
|
|
75
|
-
<form id="add-app" class="app-declaration" hidden><label>App or tool name<input id="app-name" required maxlength="80" placeholder="For example, Apple Mail"></label><label>How the agent uses it<select id="app-interface"><option value="desktop">Desktop interface · no API required</option><option value="browser">Browser interface</option><option value="api">API / connector</option><option value="terminal">Terminal / command line</option></select></label><button type="submit">Add to environment</button></form>
|
|
76
|
-
<section class="topology-panel"><header class="topology-heading"><h3>Environment map</h3><span id="resource-count">0 resources</span><div class="map-controls"><button id="map-zoom-out" type="button" aria-label="Zoom out">−</button><span id="map-zoom-label">100%</span><button id="map-zoom-in" type="button" aria-label="Zoom in">+</button><button id="map-fit" type="button">Reset view</button><button id="map-arrange" type="button">Arrange</button></div></header><div class="map-workspace"><div id="map-canvas" class="topology-canvas" tabindex="0" aria-label="Environment map. Drag empty space to pan; drag cards to move them. Use arrow keys to pan."><p id="topology-label" class="topology-label">Selected resources</p><div id="map-scroll-space" class="map-scroll-space"><div id="map-stage" class="map-stage"><svg id="map-edges" class="map-edges" aria-hidden="true"></svg><div id="environment-project" class="environment-project" data-node="project" tabindex="0" aria-label="Move project card"></div><div id="environment-tools" class="resource-grid"></div></div></div><div id="map-empty" class="map-empty"><img src="assets/icons/ui/plugs-connected.svg" width="26" height="26" alt=""><h3>No resources selected</h3><p>Run init in your project, then import .agenttwin/inventory.json. You can also declare a resource manually.</p><button id="map-select-tools" type="button">Browse resource catalog</button></div><span class="map-hint">Drag to pan · drag cards to arrange</span></div><aside class="map-resource-rail"><label>Selected resources <span id="map-list-count"></span><input id="map-resource-search" type="search" placeholder="Find a selected tool"></label><div id="map-resource-list"></div><div id="map-resource-detail"><p>Select a resource to inspect coverage.</p></div></aside></div><div class="map-pagination"><span id="map-page-label"></span><span>Scroll to explore · select a name to locate its card</span></div></section><p id="environment-message" role="status" aria-live="polite"></p><div id="removed-resource" class="removal-notice" role="status" hidden><span id="removed-resource-message"></span><button id="undo-remove-resource" type="button">Undo</button></div>
|
|
77
|
-
</section>
|
|
78
|
-
</div>
|
|
79
|
-
<details class="catalog-drawer"><summary>Select software <span>choose what your workflow uses</span></summary><div class="catalog-body"><div id="catalog-slot"><div class="connector-toolbar"><label>Find a connector<input id="connector-search" type="search" placeholder="Search services"></label><button id="select-connectors" type="button">Select all</button><button id="clear-connectors" type="button">Clear selection</button></div><p id="connector-status" role="status" aria-live="polite"></p><div id="connector-catalog" class="connector-catalog"></div><button id="export-connectors" type="button">Export selection ↓</button></div></div></details>
|
|
80
|
-
<details class="coverage-note"><summary>Runtime coverage</summary><p>Node workflows with custom HTTP collections and bounded Slack operations run through the CLI. PostgreSQL and Redis worlds can use an explicitly selected cloud runner. Other provider adapters and browser/desktop execution require implementation. Native macOS apps need a compatible macOS runtime. Production accounts are not copied.</p></details>
|
|
81
|
-
</section>
|
|
82
|
-
<section id="configure-view" hidden>
|
|
83
|
-
<div class="heading"><div><p class="eyebrow">Project / workflows</p><h1 id="workflows-title">Workflows</h1><p id="workflows-subtitle" class="muted">Choose a code path. Define the world it runs in. Check the outcome.</p></div></div>
|
|
84
|
-
<div id="workflow-workspace"></div><details class="workflow-register advanced-workflows"><summary>Advanced: saved configurations and failure scenarios</summary><section id="saved-workflows">
|
|
85
|
-
<div class="section-title"><h2>Saved configurations</h2><button type="button" id="workflow-refresh">Refresh</button></div>
|
|
86
|
-
<p class="coverage">Manage the starting state, fault conditions and expected outcomes for your workflows.</p>
|
|
87
|
-
<div id="workflow-saved-list"></div>
|
|
88
|
-
<details id="workflow-editor"><summary>Edit configuration</summary>
|
|
89
|
-
<label for="workflow-spec">agenttwin.world.json</label><textarea id="workflow-spec" rows="14" spellcheck="false" placeholder="Paste your world configuration or import a reviewed bundle above."></textarea>
|
|
90
|
-
<fieldset class="fault-controls"><legend>Failure scenario</legend>
|
|
91
|
-
<p class="coverage">Edit a draft, then rebuild and review its bundle before execution. Uploaded bundles keep their verified configuration.</p>
|
|
92
|
-
<label>Condition<select id="workflow-fault-type"><option value="none">Healthy execution — no injected fault</option><option value="timeout-after-write">Timeout after write</option></select></label>
|
|
93
|
-
<label>Target operation<select id="workflow-fault-target"><option value="">Load a configuration first</option></select></label>
|
|
94
|
-
<label>Response delay (milliseconds)<input id="workflow-fault-delay" type="number" min="1" max="10000" value="1200"></label>
|
|
95
|
-
<button type="button" id="workflow-apply-fault">Apply to draft</button>
|
|
96
|
-
<p class="coverage">Timeout after write commits a write, then delays its response. Test whether retries duplicate actions. Runs report whether the fault actually activated. Other fault types are not implemented.</p>
|
|
97
|
-
</fieldset>
|
|
98
|
-
<div class="actions"><button type="button" id="workflow-save">Save draft</button><button type="button" id="workflow-new">New draft</button><button type="button" id="workflow-export">Download world configuration</button></div>
|
|
99
|
-
</details><p id="workflow-save-status" class="coverage" role="status"></p>
|
|
100
|
-
</section></details>
|
|
101
|
-
<section class="setup-card bundle-upload"><div class="section-title"><h2>Optional cloud execution</h2><label class="upload">Upload prepared workflow<input id="bundle-import" type="file" accept="application/json,.json"></label></div><p class="setup-note">Upload the file prepared by your coding agent. AgentTwin checks the files and configuration before you run.</p><p id="workflow-connection" class="connection-note">Connect your account in Settings before executing. <button type="button" data-action="settings">Open settings →</button></p><div id="bundle-review"><p class="coverage">Select a prepared workflow bundle to review its files and configuration.</p></div><label class="bundle-consent"><input id="bundle-consent" type="checkbox">I reviewed these application files, fixture data and operation mappings for upload.</label><button id="submit-hosted-run" class="primary" type="button" disabled>Run in cloud →</button></section>
|
|
102
|
-
|
|
103
|
-
<details class="coverage-note"><summary>Runtime coverage</summary><p>Node workflows with custom HTTP collections and bounded Slack operations run through the CLI. PostgreSQL and Redis worlds can use an explicitly selected cloud runner. Other provider adapters and browser/desktop execution require implementation. Native macOS apps need a compatible macOS runtime. Production accounts are not copied.</p></details>
|
|
104
|
-
</section>
|
|
105
|
-
<section id="settings-view" hidden>
|
|
106
|
-
<div class="heading"><div><h1>Settings</h1><p class="muted">Your workspace, account connection and dashboard preferences.</p></div></div>
|
|
107
|
-
<form id="workspace-preferences" class="settings-section">
|
|
108
|
-
<div class="settings-section-heading"><h2>Workspace</h2><p>Preferences are saved in this browser. They do not change other team members’ settings.</p></div>
|
|
109
|
-
<div class="setting-row"><div><label for="workspace-display-name">Company or workspace name</label><p>Shown alongside your name in the account menu.</p></div><input id="workspace-display-name" type="text" maxlength="40" required value="My workspace" autocomplete="off"></div>
|
|
110
|
-
<div class="setting-row"><div><label for="workspace-user-name">Your name</label><p>Shown in your account menu on this browser.</p></div><input id="workspace-user-name" type="text" maxlength="60" autocomplete="name"></div><div class="settings-section-heading"><h2>Project preferences</h2><p>Guidance for new preparation tasks. Each reviewed workflow keeps its own execution limit.</p></div>
|
|
111
|
-
<div class="setting-row"><div><label for="default-run-timeout">Default execution limit</label><p>Suggested duration in the task sent to your coding agent.</p></div><div class="setting-unit"><input id="default-run-timeout" type="number" min="1" max="300" step="1" value="20" required><span>seconds</span></div></div>
|
|
112
|
-
<div class="setting-row"><div><label for="receipt-load-limit">Results loaded per refresh</label><p>Limit receipt requests for large workspaces. The API lists the latest 100 jobs; unloaded receipts are fetched on subsequent refreshes.</p></div><input id="receipt-load-limit" type="number" min="1" max="100" value="25" required></div>
|
|
113
|
-
<div class="settings-section-heading"><h2>Dashboard</h2></div>
|
|
114
|
-
<div class="setting-row"><div><label for="auto-refresh">Refresh automatically</label><p>Keep discovery and active run status up to date while this tab is visible. Manual refresh stays available.</p></div><input id="auto-refresh" type="checkbox" checked role="switch"></div>
|
|
115
|
-
<div class="setting-row"><div><label for="compact-density">Compact lists</label><p>Fit more workflows and recorded runs on screen.</p></div><input id="compact-density" type="checkbox" role="switch"></div>
|
|
116
|
-
<div class="setting-row"><div><label for="reduce-motion">Reduce motion</label><p>Disable transitions and smooth scrolling. Your system preference is also respected.</p></div><input id="reduce-motion" type="checkbox" role="switch"></div>
|
|
117
|
-
<div class="settings-save"><p id="preferences-message" role="status" aria-live="polite">No unsaved changes.</p><button type="button" id="reset-preferences">Reset defaults</button><button type="submit" class="primary">Save preferences</button></div>
|
|
118
|
-
</form>
|
|
119
|
-
<form id="api-connect" class="settings-section api-connect setup-card">
|
|
120
|
-
<div class="settings-section-heading"><h2>Account connection</h2><p>Connect to sync project discovery, submit workflows and retrieve evidence.</p></div>
|
|
121
|
-
<p id="account-connection-status" class="connection-status">Not connected</p>
|
|
122
|
-
<label for="api-url">AgentTwin API URL</label><input id="api-url" type="url" required placeholder="https://your-agenttwin-api.example">
|
|
123
|
-
<label for="api-token">Account token</label><div class="token-input"><input id="api-token" type="password" required autocomplete="off" placeholder="at_user_…"><button id="toggle-token" type="button" aria-label="Show account token" aria-pressed="false">Show</button></div>
|
|
124
|
-
<div class="actions"><button type="submit" class="primary">Connect account</button><button type="button" id="api-sync-settings" disabled>Refresh workspace</button><button type="button" id="api-disconnect" disabled>Disconnect</button></div>
|
|
125
|
-
<p class="setup-note">Your token stays in memory for this tab and is cleared on disconnect or reload. It is never saved with your preferences.</p>
|
|
126
|
-
</form>
|
|
127
|
-
<section class="settings-section"><h2>Execution</h2><p>Test supported Node workflows with <code>npx agenttwin run</code> in your repository. Service twins start in-process; no containers or VM are required.</p><p>Choose <code>--runtime hosted</code> or <code>--runtime e2b</code> when the workflow needs a cloud machine. PostgreSQL and Redis currently require that runner. CLI execution uses your computer and is not an isolation boundary.</p><p>Run <code>npx agenttwin sync</code> to send discovery, Git metadata and CLI test evidence to this workspace. Review recorded output and fixtures before syncing.</p></section>
|
|
128
|
-
<section class="settings-section"><h2>Workflow assistant</h2><p id="settings-chat-status" role="status">Connect your account to check model availability.</p><p class="setup-note">The API operator configures the model. Provider credentials stay on the server. Messages, synced metadata, saved contracts and selected run evidence are sent to the configured model. Chat explains your project and results; it does not execute code. Source analysis requires a separate upload.</p></section><section class="settings-section settings-about"><h2>About this workspace</h2><dl><div><dt>Execution</dt><dd>CLI by default; cloud when required</dd></div><div><dt>Starting state</dt><dd>Reviewed fixtures and new test identities</dd></div><div><dt>Project sync</dt><dd>Discovery metadata; application files upload only with a reviewed bundle</dd></div></dl><a href="docs/">Read the documentation ↗</a></section>
|
|
129
|
-
</section>
|
|
130
|
-
<section id="history-view" hidden>
|
|
131
|
-
<div class="heading"><div><p class="eyebrow">Project / runs</p><h1 id="runs-title">Runs</h1><p id="runs-subtitle" class="muted">Activities, test outcomes and Git revisions from CLI and cloud runs.</p></div><div class="actions"><button id="reload" type="button">Refresh evidence</button><label class="primary upload">Import run.json<input id="import" type="file" accept="application/json,.json" multiple></label></div></div>
|
|
132
|
-
<section class="workflow-register submitted-jobs"><div class="section-title"><h2>All runs</h2><button id="refresh-api-jobs" type="button" disabled>Refresh</button></div><div id="api-jobs"><p class="coverage">Connect your account in Settings to load submitted runs.</p></div></section>
|
|
133
|
-
<p id="message" role="status" aria-live="polite"></p>
|
|
134
|
-
<section class="stats" id="stats" aria-label="Run summary"></section>
|
|
135
|
-
<div class="workspace"><aside class="run-sidebar"><div class="list-heading"><h2>Inspected results</h2><span id="run-count"></span></div><label class="search-label">Filter runs<input id="search" type="search" placeholder="Search workflow or run ID"></label><div id="runs" aria-label="Select a run"><p class="muted">Loading run evidence…</p></div></aside><article id="detail" class="detail" aria-live="polite"><div class="empty"><h2>Loading evidence</h2><p>Fetching recorded test results.</p></div></article></div>
|
|
136
|
-
</section>
|
|
137
|
-
|
|
138
|
-
</main></div></div>
|
|
139
|
-
</body>
|
|
140
|
-
</html>
|
|
1
|
+
<!DOCTYPE html><!--It81cabE0Vc_SUEO_z8CQ--><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="assets/logo/mark-twin-rings.svg"/><link rel="preload" as="image" href="assets/icons/ui/stack-simple.svg"/><link rel="preload" as="image" href="assets/icons/ui/git-branch.svg"/><link rel="preload" as="image" href="assets/icons/ui/clock.svg"/><link rel="preload" as="image" href="assets/icons/ui/plugs-connected.svg"/><link rel="stylesheet" href="assets/next/static/css/1323d7da89b3fa5e.css" data-precedence="next"/><link rel="stylesheet" href="assets/next/static/css/45513ebc8c6ae800.css" data-precedence="next"/><link rel="stylesheet" href="assets/next/static/css/62e3209163fa5e55.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="assets/next/static/chunks/webpack-330c9b9dd99c0d55.js"/><script src="assets/next/static/chunks/4bd1b696-c023c6e3521b1417.js" async=""></script><script src="assets/next/static/chunks/255-09fb0d129bb12f51.js" async=""></script><script src="assets/next/static/chunks/main-app-99295a8cc37e8a47.js" async=""></script><script src="assets/next/static/chunks/772-85561381eedc95da.js" async=""></script><script src="assets/next/static/chunks/app/dashboard/page-9b4372b1e47bc3bc.js" async=""></script><meta name="theme-color" content="#090909"/><link rel="icon" type="image/svg+xml" href="assets/logo/favicon.svg?v=white-rings"/><link rel="preload" href="assets/fonts/syne-400.ttf" as="font" type="font/ttf" crossorigin="anonymous"/><meta name="agenttwin-api-url" content="https://agenttwin-api.212.56.40.125.sslip.io"/><title>Workspace | AgentTwin</title><meta name="description" content="Discover your project, test workflows and inspect recorded results."/><script src="assets/next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><a class="skip" href="#main">Skip to content</a><div class="app-frame"><p class="announce-strip"><span class="record-dot"></span>AgentTwin / workflow testing</p><header class="topbar"><a class="brand" href="#home" aria-label="AgentTwin home"><img src="assets/logo/mark-twin-rings.svg" width="23" height="23" alt=""/>AgentTwin</a><button id="sidebar-toggle" class="sidebar-toggle" type="button" aria-label="Toggle sidebar" aria-expanded="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"></rect><path d="M9 4v16"></path></svg></button><nav class="home-switcher" aria-label="Switch view"><button type="button" data-view="home" class="active" aria-current="page"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="m3 10.5 9-7.5 9 7.5"></path><path d="M5.5 9v10.5h13V9"></path></svg>Home<span class="pill-count" id="home-badge" hidden="">0</span></button><button type="button" data-view="environment"><img src="assets/icons/ui/stack-simple.svg" alt=""/>Environment</button><button type="button" data-view="configure"><img src="assets/icons/ui/git-branch.svg" alt=""/>Workflows</button><button type="button" data-view="history"><img src="assets/icons/ui/clock.svg" alt=""/>Runs</button><button type="button" data-view="settings"><img src="assets/icons/ui/stack-simple.svg" alt=""/>Settings</button></nav><div class="home-top-actions"><button type="button" id="home-search" class="home-search" aria-label="Search"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><circle cx="11" cy="11" r="6.5"></circle><path d="m16 16 4.5 4.5"></path></svg><span class="hs-label">Search</span><kbd>⌘K</kbd></button><div class="home-bell-wrap"><button type="button" id="home-bell" class="home-icon-btn" aria-label="Notifications" aria-haspopup="true" aria-expanded="false" aria-controls="home-bell-menu"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M18 9a6 6 0 1 0-12 0c0 6-2 7-2 7h16s-2-1-2-7"></path><path d="M10 20a2.2 2.2 0 0 0 4 0"></path></svg><span class="bell-badge" id="bell-badge" hidden="">0</span></button><div id="home-bell-menu" class="home-menu bell-menu" hidden=""><p class="menu-label">Notifications</p><div id="bell-items"></div></div></div><button type="button" id="home-expand" class="home-icon-btn" aria-label="Toggle full screen"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M9 3H3v6M15 3h6v6M9 21H3v-6M15 21h6v-6"></path></svg></button></div><div class="workspace-name"><span id="workspace-label">Workspace</span><span class="workspace-divider">/</span><span id="current-view">Environment</span></div><div class="account-control"><button type="button" id="account-toggle" class="workspace-avatar" aria-label="Open account menu" aria-expanded="false" aria-controls="account-menu">AT</button><div id="account-menu" class="account-menu" hidden=""><p class="menu-label">Workspace</p><strong id="account-workspace">My workspace</strong><p id="account-user-name">Your name not set</p><p id="account-menu-status" class="muted">Not connected</p><button type="button" data-action="settings">Account & settings</button><button type="button" data-action="history">View runs</button><button type="button" id="account-disconnect" disabled="">Disconnect account</button></div></div></header><div class="app-layout"><nav class="project-tabs app-nav" aria-label="Project views"><div class="sidebar-primary"><button id="sidebar-discover" class="sidebar-create" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M12 5v14M5 12h14"></path></svg>Connect project</button></div><div id="sidebar-projects" aria-label="Your repositories"></div><div class="sidebar-bottom"><a class="nav-item" href="https://agenttwin.si/docs/"><svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M12 5v15M12 5C9 3 5 3 2 4v15c3-1 7-1 10 1M12 5c3-2 7-2 10-1v15c-3-1-7-1-10 1"></path></svg>Documentation<span class="nav-external" aria-hidden="true">↗</span></a></div></nav><main id="main"><p id="api-message" class="coverage" role="status" aria-live="polite"></p><section id="home-view" hidden="" aria-label="Agent home"><div class="home-hero"><div id="home-sky" aria-hidden="true"></div><h1 class="home-overview-title">What should your agent test?</h1><section class="home-assistant-bottom" aria-label="Workflow assistant"><div class="assistant-entry"><button type="button" id="assistant-dock-toggle" aria-expanded="false" aria-controls="assistant-dock-panel">Ask about a workflow or test result <span aria-hidden="true">↑</span></button><button type="button" id="assistant-voice-entry" class="voice-button" aria-label="Dictate a message" title="Dictate a message"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><rect x="9" y="3" width="6" height="12" rx="3"></rect><path d="M5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8"></path></svg></button></div><div class="hero-content" id="assistant-dock-panel" hidden=""><button type="button" id="assistant-dock-close" aria-label="Minimize workflow assistant">Minimize ↓</button><div class="home-composer" role="form" aria-label="Workflow assistant"><div class="assistant-heading"><strong>Ask AgentTwin</strong><span id="assistant-project-context"></span></div><p id="assistant-connection-status" role="status"></p><button type="button" id="assistant-connect">Connect your account</button><label class="visually-hidden" for="composer-input">Ask about your workflow</label><textarea id="composer-input" rows="3" placeholder="What would you like to test?" spellCheck="false"></textarea><div class="composer-row"><span class="assistant-send-hint">Ask about your workflows or run results</span><button type="button" id="composer-voice" class="voice-button" aria-label="Dictate a message" title="Dictate a message"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><rect x="9" y="3" width="6" height="12" rx="3"></rect><path d="M5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8"></path></svg></button><button type="button" class="composer-send" id="composer-send" aria-label="Send message">Send ↑</button></div><div id="chat-thread" class="chat-thread" aria-label="Workflow conversation" hidden=""></div><p id="composer-note" class="composer-note" role="status"></p></div></div></section></div><div class="home-body"><section id="home-next-step" class="next-step home-action" aria-label="Next step"></section><div class="home-grid-head"><span class="grid-hint">Your workspace</span><button type="button" id="home-customize" aria-pressed="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="4" y="4" width="6.5" height="6.5" rx="1.5"></rect><rect x="13.5" y="4" width="6.5" height="6.5" rx="1.5"></rect><rect x="4" y="13.5" width="6.5" height="6.5" rx="1.5"></rect><rect x="13.5" y="13.5" width="6.5" height="6.5" rx="1.5"></rect></svg>Customize</button></div><div id="home-widget-controls" class="home-widget-controls" hidden=""></div><div class="home-widgets"><div class="widget-column"><article class="widget area-project" id="widget-project"><header><h2>Project discovery</h2></header><div class="w-body" id="project-body"></div></article><article class="widget area-git" id="widget-git"><header><h2>Git activity</h2><span id="git-meta" class="w-head-meta"></span></header><div class="w-body" id="git-body"></div></article><article class="widget area-working" id="widget-working"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 3v12a3 3 0 0 0 3 3h9"></path><path d="M6 3 3 6m3-3 3 3"></path><path d="m15 15 3 3 3-3"></path></svg></span><h2>Working now</h2><span class="w-count" id="working-count"></span><span class="w-head-meta" id="working-meta"></span></header><div class="w-body" id="working-body"></div></article><article class="widget area-sandbox" id="widget-sandbox"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="5" width="18" height="14" rx="3"></rect><path d="M3 10h18M8 5v5"></path></svg></span><h2>Last execution</h2><span class="w-head-meta" id="sandbox-meta"></span></header><div class="w-body" id="sandbox-body"></div></article></div><div class="widget-column"><article class="widget area-receipts" id="widget-receipts"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 3h9l4 4v14H6Z"></path><path d="M15 3v4h4"></path><path d="M9 12h7M9 16h7"></path></svg></span><h2>Test results</h2><span class="w-count warn" id="receipts-count"></span><span class="w-head-spin" id="receipts-spin" hidden=""><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="spin"><path d="M12 3a9 9 0 1 0 9 9" stroke-linecap="round"></path></svg></span></header><div class="w-body" id="receipts-body"></div></article><article class="widget area-services" id="widget-services"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="9"></circle><circle cx="12" cy="12" r="2.5"></circle><path d="M5.5 5.5 10 10m4 4 4.5 4.5M18.5 5.5 14 10m-4 4-4.5 4.5"></path></svg></span><h2>Observed activities</h2></header><div class="w-body" id="services-body"></div></article><article class="widget area-failures" id="widget-failures"><header><span class="w-ico" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="9"></circle><path d="m9 9 6 6M15 9l-6 6"></path></svg></span><h2>Needs attention</h2><span class="w-head-meta" id="failures-meta"></span></header><div class="w-body" id="failures-body"></div></article></div></div></div></section><section id="environment-view"><div class="heading"><div><p class="eyebrow">Project / environment</p><h1 id="environment-title">A test world for your agent.</h1><p id="environment-subtitle" class="muted">Discover your project. Test what your agent does before it touches production.</p></div><div class="actions"><label class="upload">Import discovery<input id="inventory-import" type="file" accept="application/json,.json"/></label><button id="configure-run" class="primary" type="button">Review workflows →</button></div></div><div class="discovery-onboarding"><aside class="terminal-panel"><div class="terminal-title"><h2>Connect your project</h2><span>From your terminal</span></div><div class="terminal-content"><div class="terminal-command"><span class="command-prompt" aria-hidden="true">❯</span><code>npx agenttwin init</code><button id="copy-init" type="button" aria-label="Copy discovery command">Copy command <span aria-hidden="true">↗</span></button></div><p class="terminal-comment">Run in your repository · Node.js 22.12+</p><div class="terminal-output" id="terminal-output"></div><details class="discovery-help"><summary>Analyze with a coding agent</summary><p>Init offers OpenCode when installed. To launch it directly, run <code>npx agenttwin init --agent opencode</code>. Other agents can use the generated <code>AGENT-TASK.md</code>.</p></details><details class="discovery-help"><summary>Send findings to this workspace</summary><p>Connect your account in Settings, configure <code>AGENTTWIN_TOKEN</code> in your terminal, then run <code>npx agenttwin sync</code>. This sends discovery metadata. Application files upload separately when you execute a reviewed bundle.</p><button type="button" data-action="settings">Open connection settings →</button></details></div></aside><section class="workflow-area"><div class="section-title"><h2 id="discovery-workflows-title">Discovery</h2><button id="discovery-workflows-link" type="button" data-action="configure">View workflows →</button></div><div id="environment-workflows"></div></section></div><div class="welcome-next"><ol class="welcome-steps"><li><span>01</span><div><strong>Discover</strong><p>Find tools and code paths.</p></div></li><li><span>02</span><div><strong>Review</strong><p>Choose what needs testing.</p></div></li><li><span>03</span><div><strong>Run safely</strong><p>Test through the CLI.</p></div></li></ol><button id="welcome-select-software" type="button">Or select your tools manually <span aria-hidden="true">→</span></button></div><div id="environment-coverage" class="coverage-strip"></div><div class="environment-grid"><section class="discovery-area"><div class="section-title"><h2>Selected resources</h2><div class="actions"><button id="add-app-toggle" type="button">Add custom software</button><button id="select-software" type="button">Select software</button></div></div><p id="inventory-context" class="coverage">No project connected. Import the inventory from your CLI to see its dependencies and workflow candidates.</p><form id="add-app" class="app-declaration" hidden=""><label>App or tool name<input id="app-name" required="" maxLength="80" placeholder="For example, Apple Mail"/></label><label>How the agent uses it<select id="app-interface"><option value="desktop">Desktop interface · no API required</option><option value="browser">Browser interface</option><option value="api">API / connector</option><option value="terminal">Terminal / command line</option></select></label><button type="submit">Add to environment</button></form><section class="topology-panel"><header class="topology-heading"><h3>Environment map</h3><span id="resource-count">0 resources</span><div class="map-controls"><button id="map-zoom-out" type="button" aria-label="Zoom out">−</button><span id="map-zoom-label">100%</span><button id="map-zoom-in" type="button" aria-label="Zoom in">+</button><button id="map-fit" type="button">Reset view</button><button id="map-arrange" type="button">Arrange</button></div></header><div class="map-workspace"><div id="map-canvas" class="topology-canvas" tabindex="0" aria-label="Environment map. Drag empty space to pan; drag cards to move them. Use arrow keys to pan."><p id="topology-label" class="topology-label">Selected resources</p><div id="map-scroll-space" class="map-scroll-space"><div id="map-stage" class="map-stage"><svg id="map-edges" class="map-edges" aria-hidden="true"></svg><div id="environment-project" class="environment-project" data-node="project" tabindex="0" aria-label="Move project card"></div><div id="environment-tools" class="resource-grid"></div></div></div><div id="map-empty" class="map-empty"><img src="assets/icons/ui/plugs-connected.svg" width="26" height="26" alt=""/><h3>No resources selected</h3><p>Run init in your project, then import .agenttwin/inventory.json. You can also declare a resource manually.</p><button id="map-select-tools" type="button">Browse resource catalog</button></div><span class="map-hint">Drag to pan · drag cards to arrange</span></div><aside class="map-resource-rail"><label>Selected resources <span id="map-list-count"></span><input id="map-resource-search" type="search" placeholder="Find a selected tool"/></label><div id="map-resource-list"></div><div id="map-resource-detail"><p>Select a resource to inspect coverage.</p></div></aside></div><div class="map-pagination"><span id="map-page-label"></span><span>Scroll to explore · select a name to locate its card</span></div></section><p id="environment-message" role="status" aria-live="polite"></p><div id="removed-resource" class="removal-notice" role="status" hidden=""><span id="removed-resource-message"></span><button id="undo-remove-resource" type="button">Undo</button></div></section></div><details class="catalog-drawer"><summary>Select software <span>choose what your workflow uses</span></summary><div class="catalog-body"><div id="catalog-slot"><div class="connector-toolbar"><label>Find a connector<input id="connector-search" type="search" placeholder="Search services"/></label><button id="select-connectors" type="button">Select all</button><button id="clear-connectors" type="button">Clear selection</button></div><p id="connector-status" role="status" aria-live="polite"></p><div id="connector-catalog" class="connector-catalog"></div><button id="export-connectors" type="button">Export selection ↓</button></div></div></details><details class="coverage-note"><summary>Runtime coverage</summary><p>Node workflows with custom HTTP collections and bounded Slack operations run through the CLI. PostgreSQL and Redis worlds can use an explicitly selected cloud runner. Other provider adapters and browser/desktop execution require implementation. Native macOS apps need a compatible macOS runtime. Production accounts are not copied.</p></details></section><section id="configure-view" hidden=""><div class="heading"><div><p class="eyebrow">Project / workflows</p><h1 id="workflows-title">Workflows</h1><p id="workflows-subtitle" class="muted">Choose a code path. Define the world it runs in. Check the outcome.</p></div></div><div id="workflow-workspace"></div><details class="workflow-register advanced-workflows"><summary>Advanced: saved configurations and failure scenarios</summary><section id="saved-workflows"><div class="section-title"><h2>Saved configurations</h2><button type="button" id="workflow-refresh">Refresh</button></div><p class="coverage">Manage the starting state, fault conditions and expected outcomes for your workflows.</p><div id="workflow-saved-list"></div><details id="workflow-editor"><summary>Edit configuration</summary><label for="workflow-spec">agenttwin.world.json</label><textarea id="workflow-spec" rows="14" spellCheck="false" placeholder="Paste your world configuration or import a reviewed bundle above."></textarea><fieldset class="fault-controls"><legend>Failure scenario</legend><p class="coverage">Edit a draft, then rebuild and review its bundle before execution. Uploaded bundles keep their verified configuration.</p><label>Condition<select id="workflow-fault-type"><option value="none">Healthy execution — no injected fault</option><option value="timeout-after-write">Timeout after write</option></select></label><label>Target operation<select id="workflow-fault-target"><option value="">Load a configuration first</option></select></label><label>Response delay (milliseconds)<input id="workflow-fault-delay" type="number" min="1" max="10000" value="1200"/></label><button type="button" id="workflow-apply-fault">Apply to draft</button><p class="coverage">Timeout after write commits a write, then delays its response. Test whether retries duplicate actions. Runs report whether the fault actually activated. Other fault types are not implemented.</p></fieldset><div class="actions"><button type="button" id="workflow-save">Save draft</button><button type="button" id="workflow-new">New draft</button><button type="button" id="workflow-export">Download world configuration</button></div></details><p id="workflow-save-status" class="coverage" role="status"></p></section></details><section class="setup-card bundle-upload"><div class="section-title"><h2>Optional cloud execution</h2><label class="upload">Upload prepared workflow<input id="bundle-import" type="file" accept="application/json,.json"/></label></div><p class="setup-note">Upload the file prepared by your coding agent. AgentTwin checks the files and configuration before you run.</p><p id="workflow-connection" class="connection-note">Connect your account in Settings before executing. <button type="button" data-action="settings">Open settings →</button></p><div id="bundle-review"><p class="coverage">Select a prepared workflow bundle to review its files and configuration.</p></div><label class="bundle-consent"><input id="bundle-consent" type="checkbox"/>I reviewed these application files, fixture data and operation mappings for upload.</label><button id="submit-hosted-run" class="primary" type="button" disabled="">Run in cloud →</button></section><details class="coverage-note"><summary>Runtime coverage</summary><p>Node workflows with custom HTTP collections and bounded Slack operations run through the CLI. PostgreSQL and Redis worlds can use an explicitly selected cloud runner. Other provider adapters and browser/desktop execution require implementation. Native macOS apps need a compatible macOS runtime. Production accounts are not copied.</p></details></section><section id="settings-view" hidden=""><div class="heading"><div><h1>Settings</h1><p class="muted">Your workspace, account connection and dashboard preferences.</p></div></div><form id="workspace-preferences" class="settings-section"><div class="settings-section-heading"><h2>Workspace</h2><p>Preferences are saved in this browser. They do not change other team members’ settings.</p></div><div class="setting-row"><div><label for="workspace-display-name">Company or workspace name</label><p>Shown alongside your name in the account menu.</p></div><input id="workspace-display-name" type="text" maxLength="40" required="" autoComplete="off" value="My workspace"/></div><div class="setting-row"><div><label for="workspace-user-name">Your name</label><p>Shown in your account menu on this browser.</p></div><input id="workspace-user-name" type="text" maxLength="60" autoComplete="name"/></div><div class="settings-section-heading"><h2>Project preferences</h2><p>Guidance for new preparation tasks. Each reviewed workflow keeps its own execution limit.</p></div><div class="setting-row"><div><label for="default-run-timeout">Default execution limit</label><p>Suggested duration in the task sent to your coding agent.</p></div><div class="setting-unit"><input id="default-run-timeout" type="number" min="1" max="300" step="1" required="" value="20"/><span>seconds</span></div></div><div class="setting-row"><div><label for="receipt-load-limit">Results loaded per refresh</label><p>Limit receipt requests for large workspaces. The API lists the latest 100 jobs; unloaded receipts are fetched on subsequent refreshes.</p></div><input id="receipt-load-limit" type="number" min="1" max="100" required="" value="25"/></div><div class="settings-section-heading"><h2>Dashboard</h2></div><div class="setting-row"><div><label for="auto-refresh">Refresh automatically</label><p>Keep discovery and active run status up to date while this tab is visible. Manual refresh stays available.</p></div><input id="auto-refresh" type="checkbox" role="switch" checked=""/></div><div class="setting-row"><div><label for="compact-density">Compact lists</label><p>Fit more workflows and recorded runs on screen.</p></div><input id="compact-density" type="checkbox" role="switch"/></div><div class="setting-row"><div><label for="reduce-motion">Reduce motion</label><p>Disable transitions and smooth scrolling. Your system preference is also respected.</p></div><input id="reduce-motion" type="checkbox" role="switch"/></div><div class="settings-save"><p id="preferences-message" role="status" aria-live="polite">No unsaved changes.</p><button type="button" id="reset-preferences">Reset defaults</button><button type="submit" class="primary">Save preferences</button></div></form><form id="api-connect" class="settings-section api-connect setup-card"><div class="settings-section-heading"><h2>Account connection</h2><p>Connect to sync project discovery, submit workflows and retrieve evidence.</p></div><p id="account-connection-status" class="connection-status">Not connected</p><label for="api-url">AgentTwin API URL</label><input id="api-url" type="url" required="" placeholder="https://your-agenttwin-api.example"/><label for="api-token">Account token</label><div class="token-input"><input id="api-token" type="password" required="" autoComplete="off" placeholder="at_user_…"/><button id="toggle-token" type="button" aria-label="Show account token" aria-pressed="false">Show</button></div><div class="actions"><button type="submit" class="primary">Connect account</button><button type="button" id="api-sync-settings" disabled="">Refresh workspace</button><button type="button" id="api-disconnect" disabled="">Disconnect</button></div><p class="setup-note">Your token stays in memory for this tab and is cleared on disconnect or reload. It is never saved with your preferences.</p></form><section class="settings-section"><h2>Execution</h2><p>Test supported Node workflows with <code>npx agenttwin run</code> in your repository. Service twins start in-process; no containers or VM are required.</p><p>Choose <code>--runtime hosted</code> or <code>--runtime e2b</code> when the workflow needs a cloud machine. PostgreSQL and Redis currently require that runner. CLI execution uses your computer and is not an isolation boundary.</p><p>Run <code>npx agenttwin sync</code> to send discovery, Git metadata and CLI test evidence to this workspace. Review recorded output and fixtures before syncing.</p></section><section class="settings-section"><h2>Workflow assistant</h2><p id="settings-chat-status" role="status">Connect your account to check model availability.</p><p class="setup-note">The API operator configures the model. Provider credentials stay on the server. Messages, synced metadata, saved contracts and selected run evidence are sent to the configured model. Chat explains your project and results; it does not execute code. Source analysis requires a separate upload.</p></section><section class="settings-section settings-about"><h2>About this workspace</h2><dl><div><dt>Execution</dt><dd>CLI by default; cloud when required</dd></div><div><dt>Starting state</dt><dd>Reviewed fixtures and new test identities</dd></div><div><dt>Project sync</dt><dd>Discovery metadata; application files upload only with a reviewed bundle</dd></div></dl><a href="https://agenttwin.si/docs/">Read the documentation ↗</a></section></section><section id="history-view" hidden=""><div class="heading"><div><p class="eyebrow">Project / runs</p><h1 id="runs-title">Runs</h1><p id="runs-subtitle" class="muted">Activities, test outcomes and Git revisions from CLI and cloud runs.</p></div><div class="actions"><button id="reload" type="button">Refresh evidence</button><label class="primary upload">Import run.json<input id="import" type="file" accept="application/json,.json" multiple=""/></label></div></div><section class="workflow-register submitted-jobs"><div class="section-title"><h2>All runs</h2><button id="refresh-api-jobs" type="button" disabled="">Refresh</button></div><div id="api-jobs"><p class="coverage">Connect your account in Settings to load submitted runs.</p></div></section><p id="message" role="status" aria-live="polite"></p><section class="stats" id="stats" aria-label="Run summary"></section><div class="workspace"><aside class="run-sidebar"><div class="list-heading"><h2>Inspected results</h2><span id="run-count"></span></div><label class="search-label">Filter runs<input id="search" type="search" placeholder="Search workflow or run ID"/></label><div id="runs" aria-label="Select a run"><p class="muted">Loading run evidence…</p></div></aside><article id="detail" class="detail" aria-live="polite"><div class="empty"><h2>Loading evidence</h2><p>Fetching recorded test results.</p></div></article></div></section></main></div></div><!--$--><!--/$--><script src="assets/next/static/chunks/webpack-330c9b9dd99c0d55.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[9766,[],\"\"]\n3:I[8924,[],\"\"]\n4:I[2966,[\"772\",\"static/chunks/772-85561381eedc95da.js\",\"105\",\"static/chunks/app/dashboard/page-9b4372b1e47bc3bc.js\"],\"default\"]\n5:I[4431,[],\"OutletBoundary\"]\n7:I[5278,[],\"AsyncMetadataOutlet\"]\n9:I[4431,[],\"ViewportBoundary\"]\nb:I[4431,[],\"MetadataBoundary\"]\nc:\"$Sreact.suspense\"\ne:I[7150,[],\"\"]\n:HL[\"assets/next/static/css/1323d7da89b3fa5e.css\",\"style\"]\n:HL[\"assets/next/static/css/45513ebc8c6ae800.css\",\"style\"]\n:HL[\"assets/next/static/css/62e3209163fa5e55.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"It81cabE0Vc-SUEO_z8CQ\",\"p\":\"\",\"c\":[\"\",\"dashboard\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"dashboard\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"assets/next/static/css/1323d7da89b3fa5e.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"meta\",null,{\"name\":\"theme-color\",\"content\":\"#090909\"}],[\"$\",\"link\",null,{\"rel\":\"icon\",\"type\":\"image/svg+xml\",\"href\":\"assets/logo/favicon.svg?v=white-rings\"}],[\"$\",\"link\",null,{\"rel\":\"preload\",\"href\":\"assets/fonts/syne-400.ttf\",\"as\":\"font\",\"type\":\"font/ttf\",\"crossOrigin\":\"anonymous\"}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}]]}],{\"children\":[\"dashboard\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"assets/next/static/css/45513ebc8c6ae800.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"assets/next/static/css/62e3209163fa5e55.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[[\"$\",\"meta\",null,{\"name\":\"agenttwin-api-url\",\"content\":\"https://agenttwin-api.212.56.40.125.sslip.io\"}],[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L4\",null,{}],null,[\"$\",\"$L5\",null,{\"children\":[\"$L6\",[\"$\",\"$L7\",null,{\"promise\":\"$@8\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[[\"$\",\"$L9\",null,{\"children\":\"$La\"}],null],[\"$\",\"$Lb\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$c\",null,{\"fallback\":null,\"children\":\"$Ld\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$e\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n6:null\n"])</script><script>self.__next_f.push([1,"8:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Workspace | AgentTwin\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Discover your project, test workflows and inspect recorded results.\"}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"d:\"$8:metadata\"\n"])</script></body></html>
|
package/dist/gateway.js
CHANGED
|
@@ -938,7 +938,13 @@ async function launchSession(cwd) {
|
|
|
938
938
|
});
|
|
939
939
|
}
|
|
940
940
|
async function loadDashboardDiscovery(cwd, inventory) {
|
|
941
|
-
|
|
941
|
+
let previousHead;
|
|
942
|
+
try {
|
|
943
|
+
previousHead = JSON.parse(await readFile6(join6(cwd, ".agenttwin/git.json"), "utf8")).head;
|
|
944
|
+
} catch {
|
|
945
|
+
}
|
|
946
|
+
const git = await captureGit(cwd, previousHead);
|
|
947
|
+
const base = discoveryTransfer({ ...dashboardInventory(cwd, inventory), ...git ? { git } : {} });
|
|
942
948
|
let raw;
|
|
943
949
|
try {
|
|
944
950
|
raw = await readFile6(join6(cwd, ".agenttwin/agent-discovery.json"), "utf8");
|
|
@@ -960,6 +966,7 @@ async function loadDashboardDiscovery(cwd, inventory) {
|
|
|
960
966
|
var init_session = __esm({
|
|
961
967
|
"src/commands/session.ts"() {
|
|
962
968
|
"use strict";
|
|
969
|
+
init_git();
|
|
963
970
|
init_project_products();
|
|
964
971
|
init_dashboard_transfer();
|
|
965
972
|
init_dashboard();
|
|
@@ -974,6 +981,28 @@ import { mkdir as mkdir3, readFile as readFile8, writeFile as writeFile4, rename
|
|
|
974
981
|
import { resolve as resolve2, join as join8, dirname as dirname2, extname } from "path";
|
|
975
982
|
import { tmpdir as tmpdir2 } from "os";
|
|
976
983
|
|
|
984
|
+
// src/runtime/transcription.ts
|
|
985
|
+
var transcriptionConfiguration = () => ({ available: Boolean(process.env.OPENAI_API_KEY), model: "gpt-4o-mini-transcribe" });
|
|
986
|
+
var formats = { "audio/webm": "webm", "audio/mp4": "mp4", "audio/ogg": "ogg", "audio/wav": "wav", "audio/mpeg": "mp3" };
|
|
987
|
+
function transcriptionAudio(input) {
|
|
988
|
+
if (typeof input.audio !== "string" || typeof input.mimeType !== "string" || !formats[input.mimeType]) throw new Error("Unsupported recording format");
|
|
989
|
+
const bytes = Buffer.from(input.audio, "base64");
|
|
990
|
+
if (!bytes.length || bytes.length > 4 * 1024 * 1024 || bytes.toString("base64") !== input.audio) throw new Error("Recording must be valid base64 and at most 4 MiB");
|
|
991
|
+
return { bytes, mimeType: input.mimeType, extension: formats[input.mimeType] };
|
|
992
|
+
}
|
|
993
|
+
async function transcribeAudio(audio, signal) {
|
|
994
|
+
if (!transcriptionConfiguration().available) throw new Error("transcription_not_configured");
|
|
995
|
+
const form = new FormData();
|
|
996
|
+
form.set("model", "gpt-4o-mini-transcribe");
|
|
997
|
+
form.set("response_format", "json");
|
|
998
|
+
form.set("file", new Blob([new Uint8Array(audio.bytes)], { type: audio.mimeType }), `recording.${audio.extension}`);
|
|
999
|
+
const response = await fetch("https://api.openai.com/v1/audio/transcriptions", { method: "POST", redirect: "error", signal, headers: { authorization: `Bearer ${process.env.OPENAI_API_KEY}` }, body: form });
|
|
1000
|
+
if (!response.ok) throw new Error(`transcription_request_failed_${response.status}`);
|
|
1001
|
+
const data = await response.json();
|
|
1002
|
+
if (typeof data.text !== "string" || !data.text.trim() || data.text.length > 16e3) throw new Error("transcription_returned_no_text");
|
|
1003
|
+
return data.text.trim();
|
|
1004
|
+
}
|
|
1005
|
+
|
|
977
1006
|
// src/runtime/chat.ts
|
|
978
1007
|
function chatConfiguration() {
|
|
979
1008
|
return { available: Boolean(process.env.OPENAI_API_KEY && process.env.AGENTTWIN_CHAT_MODEL), provider: "openai", model: process.env.AGENTTWIN_CHAT_MODEL ?? null };
|
|
@@ -1621,12 +1650,12 @@ function uploadPath(path) {
|
|
|
1621
1650
|
if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || path.split("/").some((p) => !p || p === "." || p === ".." || /^(\.env.*|\.git|\.ssh|\.aws|\.azure|\.config|\.npmrc|\.netrc|credentials.*)$/i.test(p)) || /\.(pem|key|p12|pfx)$/i.test(path)) throw new Error("Unsafe uploaded file path");
|
|
1622
1651
|
return path;
|
|
1623
1652
|
}
|
|
1624
|
-
async function body(req) {
|
|
1653
|
+
async function body(req, limit = 28 * 1024 * 1024) {
|
|
1625
1654
|
const chunks = [];
|
|
1626
1655
|
let bytes = 0;
|
|
1627
1656
|
for await (const chunk of req) {
|
|
1628
1657
|
bytes += chunk.length;
|
|
1629
|
-
if (bytes >
|
|
1658
|
+
if (bytes > limit) throw new Error("Request exceeds size limit");
|
|
1630
1659
|
chunks.push(Buffer.from(chunk));
|
|
1631
1660
|
}
|
|
1632
1661
|
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
@@ -1649,6 +1678,7 @@ async function startGateway(opts) {
|
|
|
1649
1678
|
const controllers = /* @__PURE__ */ new Map();
|
|
1650
1679
|
const chatTasks = /* @__PURE__ */ new Set();
|
|
1651
1680
|
const chatControllers = /* @__PURE__ */ new Map();
|
|
1681
|
+
const voiceControllers = /* @__PURE__ */ new Map();
|
|
1652
1682
|
const directory = resolve2(opts.directory);
|
|
1653
1683
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
1654
1684
|
const storePath = join8(directory, "state.json");
|
|
@@ -2027,7 +2057,63 @@ async function startGateway(opts) {
|
|
|
2027
2057
|
if (path === "/v1/chat" && req.method === "GET") {
|
|
2028
2058
|
const project = selectedInventory?.name ?? null;
|
|
2029
2059
|
const thread = store.chats?.find((t) => t.accountId === account.id && t.project === project);
|
|
2030
|
-
send(res, 200, { ...chatConfiguration(), project, thread: thread ?? null, busy: chatControllers.has(account.id) });
|
|
2060
|
+
send(res, 200, { ...chatConfiguration(), transcription: transcriptionConfiguration(), project, thread: thread ?? null, busy: chatControllers.has(account.id) });
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
if (path === "/v1/transcriptions" && req.method === "POST") {
|
|
2064
|
+
if (stopping) {
|
|
2065
|
+
send(res, 503, { error: "stopping" });
|
|
2066
|
+
return;
|
|
2067
|
+
}
|
|
2068
|
+
if (!transcriptionConfiguration().available) {
|
|
2069
|
+
send(res, 503, { error: "transcription_not_configured", message: "Voice input is unavailable on this server." });
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
if (voiceControllers.has(account.id) || voiceControllers.size >= 4) {
|
|
2073
|
+
send(res, 429, { error: "transcription_busy" });
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
const audio = transcriptionAudio(await body(req, 6 * 1024 * 1024));
|
|
2077
|
+
if (!account.active) {
|
|
2078
|
+
send(res, 401, { error: "unauthorized" });
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
if (voiceControllers.has(account.id) || voiceControllers.size >= 4) {
|
|
2082
|
+
send(res, 429, { error: "transcription_busy" });
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2086
|
+
const usage = account.voiceUsageDay === day ? account.voiceRequests ?? 0 : 0;
|
|
2087
|
+
if (usage >= 50) {
|
|
2088
|
+
send(res, 429, { error: "daily_voice_limit" });
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
2091
|
+
const controller = new AbortController();
|
|
2092
|
+
voiceControllers.set(account.id, controller);
|
|
2093
|
+
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
2094
|
+
let finish;
|
|
2095
|
+
const task = new Promise((done) => {
|
|
2096
|
+
finish = done;
|
|
2097
|
+
});
|
|
2098
|
+
chatTasks.add(task);
|
|
2099
|
+
try {
|
|
2100
|
+
account.voiceUsageDay = day;
|
|
2101
|
+
account.voiceRequests = usage + 1;
|
|
2102
|
+
await save();
|
|
2103
|
+
const text = await transcribeAudio(audio, controller.signal);
|
|
2104
|
+
if (!account.active) {
|
|
2105
|
+
send(res, 401, { error: "unauthorized" });
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
send(res, 200, { text });
|
|
2109
|
+
} catch {
|
|
2110
|
+
send(res, 502, { error: "transcription_failed", message: "Could not transcribe this recording. Please try again or type your message." });
|
|
2111
|
+
} finally {
|
|
2112
|
+
clearTimeout(timeout);
|
|
2113
|
+
voiceControllers.delete(account.id);
|
|
2114
|
+
chatTasks.delete(task);
|
|
2115
|
+
finish();
|
|
2116
|
+
}
|
|
2031
2117
|
return;
|
|
2032
2118
|
}
|
|
2033
2119
|
if (path === "/v1/chat" && req.method === "POST") {
|
|
@@ -2180,7 +2266,7 @@ async function startGateway(opts) {
|
|
|
2180
2266
|
});
|
|
2181
2267
|
return { server, async close() {
|
|
2182
2268
|
stopping = true;
|
|
2183
|
-
for (const controller of chatControllers.values()) controller.abort();
|
|
2269
|
+
for (const controller of [...chatControllers.values(), ...voiceControllers.values()]) controller.abort();
|
|
2184
2270
|
server.close();
|
|
2185
2271
|
await Promise.allSettled([...workers, ...chatTasks]);
|
|
2186
2272
|
await writes;
|