open-agents-ai 0.185.75 → 0.185.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +268 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -65537,7 +65537,10 @@ body {
65537
65537
  <div id="tabs" style="display:flex;gap:0;background:#1e1e22;border-bottom:1px solid #2a2a30;padding:0 16px;flex-shrink:0">
65538
65538
  <button class="tab active" onclick="switchTab('chat')" id="tab-chat" style="background:none;border:none;border-bottom:2px solid #b2920a;color:#b2920a;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">chat</button>
65539
65539
  <button class="tab" onclick="switchTab('agent')" id="tab-agent" style="background:none;border:none;border-bottom:2px solid transparent;color:#555;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">agent</button>
65540
- <button class="tab" onclick="switchTab('jobs')" id="tab-jobs" style="background:none;border:none;border-bottom:2px solid transparent;color:#555;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">jobs</button>
65540
+ <button class="tab" onclick="switchTab('jobs')" id="tab-jobs" style="background:none;border:none;border-bottom:2px solid transparent;color:#555;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">dashboard</button>
65541
+ <button class="tab" onclick="switchTab('config')" id="tab-config" style="background:none;border:none;border-bottom:2px solid transparent;color:#555;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">config</button>
65542
+ <button class="tab" onclick="switchTab('activity')" id="tab-activity" style="background:none;border:none;border-bottom:2px solid transparent;color:#555;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">activity</button>
65543
+ <span id="token-counter" style="margin-left:auto;font-size:0.6rem;color:#555">0 tokens</span>
65541
65544
  </div>
65542
65545
  <div id="conversation"></div>
65543
65546
  <div id="agent-panel" style="display:none;flex:1;overflow-y:auto;padding:12px 16px">
@@ -65551,10 +65554,33 @@ body {
65551
65554
  </div>
65552
65555
  <div id="jobs-panel" style="display:none;flex:1;overflow-y:auto;padding:12px 16px">
65553
65556
  <div id="dashboard-health" style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap"></div>
65557
+ <div id="dashboard-daemons" style="margin-bottom:16px"></div>
65554
65558
  <div id="dashboard-usage" style="margin-bottom:16px"></div>
65555
65559
  <h3 style="color:#b2920a;font-size:0.7rem;margin-bottom:8px">Job History</h3>
65556
65560
  <div id="jobs-list" style="font-size:0.78rem"></div>
65557
65561
  </div>
65562
+ <div id="config-panel" style="display:none;flex:1;overflow-y:auto;padding:12px 16px">
65563
+ <h3 style="color:#b2920a;font-size:0.7rem;margin-bottom:12px">Server Configuration</h3>
65564
+ <div id="config-content" style="font-size:0.78rem"></div>
65565
+ <h3 style="color:#b2920a;font-size:0.7rem;margin:16px 0 8px">Model</h3>
65566
+ <div style="display:flex;gap:8px;align-items:center">
65567
+ <select id="config-model-select" style="background:#2a2a30;border:1px solid #3a3a42;color:#b0b0b0;padding:4px 8px;border-radius:3px;font-family:inherit;font-size:0.7rem;flex:1"></select>
65568
+ <button onclick="switchModel()" style="background:#2a2a30;border:1px solid #3a3a42;color:#b2920a;padding:4px 12px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">switch</button>
65569
+ </div>
65570
+ <h3 style="color:#b2920a;font-size:0.7rem;margin:16px 0 8px">Endpoint</h3>
65571
+ <div id="config-endpoint" style="font-size:0.78rem;color:#888"></div>
65572
+ <h3 style="color:#b2920a;font-size:0.7rem;margin:16px 0 8px">Profiles</h3>
65573
+ <div id="config-profiles" style="font-size:0.78rem"></div>
65574
+ <h3 style="color:#b2920a;font-size:0.7rem;margin:16px 0 8px">Export Conversation</h3>
65575
+ <div style="display:flex;gap:8px">
65576
+ <button onclick="exportChat('md')" style="background:#2a2a30;border:1px solid #3a3a42;color:#b2920a;padding:4px 12px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">markdown</button>
65577
+ <button onclick="exportChat('json')" style="background:#2a2a30;border:1px solid #3a3a42;color:#b2920a;padding:4px 12px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">JSON</button>
65578
+ </div>
65579
+ </div>
65580
+ <div id="activity-panel" style="display:none;flex:1;overflow-y:auto;padding:12px 16px">
65581
+ <h3 style="color:#b2920a;font-size:0.7rem;margin-bottom:12px">Recent Activity (Audit Log)</h3>
65582
+ <div id="activity-feed" style="font-size:0.72rem"></div>
65583
+ </div>
65558
65584
 
65559
65585
  <div id="footer">
65560
65586
  <span id="system-prompt-toggle" onclick="toggleSystemPrompt()">sys</span>
@@ -65733,6 +65759,7 @@ async function sendMessage() {
65733
65759
  }
65734
65760
 
65735
65761
  messages.push({ role: 'assistant', content: fullContent });
65762
+ updateTokenCounter(fullContent.split(/\\s+/).length * 1.3 | 0); // rough token estimate
65736
65763
  // Re-render with copy button
65737
65764
  msgDiv.innerHTML = renderMarkdown(fullContent);
65738
65765
  const actions = document.createElement('div');
@@ -65780,16 +65807,135 @@ function closeKeyModal() {
65780
65807
  }
65781
65808
 
65782
65809
  // Tab switching
65810
+ const allPanels = ['conversation','agent-panel','jobs-panel','config-panel','activity-panel'];
65783
65811
  function switchTab(tab) {
65784
- document.getElementById('conversation').style.display = tab === 'chat' ? 'flex' : 'none';
65785
- document.getElementById('agent-panel').style.display = tab === 'agent' ? 'block' : 'none';
65786
- document.getElementById('jobs-panel').style.display = tab === 'jobs' ? 'block' : 'none';
65812
+ const panelMap = {chat:'conversation',agent:'agent-panel',jobs:'jobs-panel',config:'config-panel',activity:'activity-panel'};
65813
+ allPanels.forEach(id => { const el = document.getElementById(id); if(el) el.style.display = 'none'; });
65814
+ const panel = document.getElementById(panelMap[tab]);
65815
+ if (panel) panel.style.display = tab === 'chat' ? 'flex' : 'block';
65787
65816
  document.getElementById('footer').style.display = tab === 'chat' ? 'flex' : 'none';
65788
65817
  document.querySelectorAll('.tab').forEach(t => { t.style.borderBottomColor = 'transparent'; t.style.color = '#555'; });
65789
65818
  const active = document.getElementById('tab-' + tab);
65790
65819
  if (active) { active.style.borderBottomColor = '#b2920a'; active.style.color = '#b2920a'; }
65791
65820
  if (tab === 'jobs') loadJobs();
65792
65821
  if (tab === 'agent') loadProfiles();
65822
+ if (tab === 'config') loadConfig();
65823
+ if (tab === 'activity') loadActivity();
65824
+ }
65825
+
65826
+ // Token counter
65827
+ let totalTokens = 0;
65828
+ function updateTokenCounter(n) { totalTokens += n; document.getElementById('token-counter').textContent = totalTokens.toLocaleString() + ' tokens'; }
65829
+
65830
+ // Config panel
65831
+ async function loadConfig() {
65832
+ try {
65833
+ const [cfg, ep, models] = await Promise.all([
65834
+ fetch('/v1/config', { headers: headers() }).then(r => r.json()),
65835
+ fetch('/v1/config/endpoint', { headers: headers() }).then(r => r.json()),
65836
+ fetch('/v1/models', { headers: headers() }).then(r => r.json()),
65837
+ ]);
65838
+ const c = cfg.config || {};
65839
+ document.getElementById('config-content').innerHTML =
65840
+ '<table style="width:100%">' +
65841
+ Object.entries(c).map(([k,v]) =>
65842
+ '<tr style="border-bottom:1px solid #2a2a30"><td style="padding:4px;color:#888">' + k + '</td>' +
65843
+ '<td style="padding:4px;color:#b0b0b0">' + (v === '[redacted]' ? '<span style="color:#555">[redacted]</span>' : String(v)) + '</td></tr>'
65844
+ ).join('') + '</table>';
65845
+ document.getElementById('config-endpoint').textContent = ep.url + ' (' + (ep.backendType || 'unknown') + ')';
65846
+ // Populate model switcher
65847
+ const sel = document.getElementById('config-model-select');
65848
+ sel.innerHTML = '';
65849
+ for (const m of (models.data || [])) {
65850
+ const opt = document.createElement('option');
65851
+ opt.value = m.id.replace(/^local\\//, '');
65852
+ opt.textContent = m.id.replace(/^local\\//, '');
65853
+ if (opt.value === c.model) opt.selected = true;
65854
+ sel.appendChild(opt);
65855
+ }
65856
+ } catch {}
65857
+ // Profiles
65858
+ try {
65859
+ const r = await fetch('/v1/profiles', { headers: headers() });
65860
+ const d = await r.json();
65861
+ document.getElementById('config-profiles').innerHTML = (d.profiles || []).map(p =>
65862
+ '<div style="background:#1e1e22;border:1px solid #2a2a30;border-radius:3px;padding:6px 10px;margin:4px 0">' +
65863
+ '<span style="color:#b2920a">' + p.name + '</span>' +
65864
+ (p.encrypted ? ' <span style="color:#555;font-size:0.6rem">(encrypted)</span>' : '') +
65865
+ ' <span style="color:#555;font-size:0.6rem">' + (p.source || '') + '</span></div>'
65866
+ ).join('') || '<span style="color:#555">No profiles</span>';
65867
+ } catch {}
65868
+ }
65869
+
65870
+ async function switchModel() {
65871
+ const model = document.getElementById('config-model-select').value;
65872
+ if (!model) return;
65873
+ try {
65874
+ await fetch('/v1/config/model', {
65875
+ method: 'PUT', headers: headers(),
65876
+ body: JSON.stringify({ model }),
65877
+ });
65878
+ loadConfig();
65879
+ modelSelect.value = model; // sync chat model picker
65880
+ } catch {}
65881
+ }
65882
+
65883
+ // Export conversation
65884
+ function exportChat(fmt) {
65885
+ let content;
65886
+ if (fmt === 'json') {
65887
+ content = JSON.stringify(messages, null, 2);
65888
+ } else {
65889
+ content = messages.map(m => (m.role === 'user' ? '**You:** ' : '**Agent:** ') + m.content).join('\\n\\n');
65890
+ }
65891
+ const blob = new Blob([content], { type: fmt === 'json' ? 'application/json' : 'text/markdown' });
65892
+ const a = document.createElement('a');
65893
+ a.href = URL.createObjectURL(blob);
65894
+ a.download = 'conversation.' + (fmt === 'json' ? 'json' : 'md');
65895
+ a.click();
65896
+ }
65897
+
65898
+ // Activity feed (audit log)
65899
+ async function loadActivity() {
65900
+ try {
65901
+ const r = await fetch('/v1/audit?limit=50', { headers: headers() });
65902
+ const d = await r.json();
65903
+ const feed = document.getElementById('activity-feed');
65904
+ if (!d.records?.length) { feed.innerHTML = '<span style="color:#555">No activity yet</span>'; return; }
65905
+ feed.innerHTML = d.records.map(r => {
65906
+ const time = r.ts?.split('T')[1]?.slice(0,8) || '';
65907
+ const color = r.status >= 400 ? '#ff4444' : r.status >= 300 ? '#b2920a' : '#4ec94e';
65908
+ return '<div style="padding:3px 0;border-bottom:1px solid #1e1e22">' +
65909
+ '<span style="color:#555">' + time + '</span> ' +
65910
+ '<span style="color:' + color + '">' + r.status + '</span> ' +
65911
+ '<span style="color:#888">' + r.method + '</span> ' +
65912
+ '<span style="color:#b0b0b0">' + r.path + '</span> ' +
65913
+ '<span style="color:#555">' + r.latencyMs + 'ms</span> ' +
65914
+ '<span style="color:#555;font-size:0.6rem">' + (r.user || '') + '</span></div>';
65915
+ }).join('');
65916
+ } catch {}
65917
+ }
65918
+
65919
+ // Daemon monitor (on dashboard tab)
65920
+ async function loadDaemons() {
65921
+ // The daemon registry is server-side (TUI). We can't query it via REST yet.
65922
+ // Show running jobs as a proxy for active processes.
65923
+ try {
65924
+ const r = await fetch('/v1/runs?status=running', { headers: headers() });
65925
+ const d = await r.json();
65926
+ const el = document.getElementById('dashboard-daemons');
65927
+ if (!d.runs?.length) {
65928
+ el.innerHTML = '<div style="background:#1e1e22;border:1px solid #2a2a30;border-radius:3px;padding:8px 12px;color:#555;font-size:0.7rem">No active processes</div>';
65929
+ return;
65930
+ }
65931
+ el.innerHTML = '<h3 style="color:#b2920a;font-size:0.7rem;margin-bottom:8px">Active Processes</h3>' +
65932
+ d.runs.map(j =>
65933
+ '<div style="background:#1e1e22;border-left:2px solid #b2920a;padding:6px 10px;margin:4px 0;font-size:0.72rem">' +
65934
+ '<span style="color:#b2920a">' + (j.id||'').slice(0,12) + '</span> ' +
65935
+ '<span style="color:#4ec94e">running</span> ' +
65936
+ '<span style="color:#888">' + (j.task||'').slice(0,50) + '</span></div>'
65937
+ ).join('');
65938
+ } catch {}
65793
65939
  }
65794
65940
 
65795
65941
  // Agent task
@@ -65910,6 +66056,7 @@ async function loadDashboard() {
65910
66056
 
65911
66057
  async function loadJobs() {
65912
66058
  loadDashboard();
66059
+ loadDaemons();
65913
66060
  const list = document.getElementById('jobs-list');
65914
66061
  try {
65915
66062
  const r = await fetch('/v1/runs', { headers: headers() });
@@ -65977,6 +66124,98 @@ var init_logger = __esm({
65977
66124
  }
65978
66125
  });
65979
66126
 
66127
+ // packages/cli/dist/api/openapi.js
66128
+ function getOpenApiSpec() {
66129
+ return {
66130
+ openapi: "3.0.3",
66131
+ info: {
66132
+ title: "Open Agents REST API",
66133
+ description: "AI coding agent powered by open-weight models. Enterprise REST API for agentic task execution, OpenAI-compatible inference, and system management.",
66134
+ version: "0.185.75",
66135
+ license: { name: "CC-BY-NC-4.0", url: "https://creativecommons.org/licenses/by-nc/4.0/" }
66136
+ },
66137
+ servers: [{ url: "http://localhost:11435", description: "Local development" }],
66138
+ security: [{ BearerAuth: [] }],
66139
+ components: {
66140
+ securitySchemes: {
66141
+ BearerAuth: { type: "http", scheme: "bearer", description: "API key with scope (read/run/admin)" }
66142
+ }
66143
+ },
66144
+ paths: {
66145
+ "/health": { get: { summary: "Liveness probe", tags: ["Health"], security: [], responses: { 200: { description: "Server is alive" } } } },
66146
+ "/health/ready": { get: { summary: "Readiness probe (checks backend)", tags: ["Health"], security: [], responses: { 200: { description: "Backend reachable" }, 503: { description: "Backend unreachable" } } } },
66147
+ "/health/startup": { get: { summary: "Startup complete", tags: ["Health"], security: [], responses: { 200: { description: "Started" } } } },
66148
+ "/version": { get: { summary: "Version info", tags: ["Health"], security: [], responses: { 200: { description: "Version, node, platform" } } } },
66149
+ "/metrics": { get: { summary: "Prometheus metrics", tags: ["Health"], security: [], responses: { 200: { description: "Prometheus text format" } } } },
66150
+ "/v1/models": { get: { summary: "List available models", tags: ["Inference"], security: [{ BearerAuth: [] }], responses: { 200: { description: "OpenAI-format model list" } } } },
66151
+ "/v1/chat/completions": { post: { summary: "Chat completion (OpenAI-compatible)", tags: ["Inference"], security: [{ BearerAuth: [] }], requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["model", "messages"], properties: { model: { type: "string" }, messages: { type: "array" }, stream: { type: "boolean" }, max_tokens: { type: "integer" }, temperature: { type: "number" } } } } } }, responses: { 200: { description: "Chat response" } } } },
66152
+ "/v1/embeddings": { post: { summary: "Generate embeddings", tags: ["Inference"], responses: { 200: { description: "Embedding vectors" } } } },
66153
+ "/v1/run": { post: { summary: "Submit agentic task", tags: ["Agentic"], requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["task"], properties: { task: { type: "string" }, model: { type: "string" }, stream: { type: "boolean" }, profile: { type: "string" }, working_directory: { type: "string" }, isolate: { type: "boolean" } } } } } }, responses: { 202: { description: "Task accepted" } } } },
66154
+ "/v1/runs": { get: { summary: "List all runs", tags: ["Agentic"], parameters: [{ name: "limit", in: "query", schema: { type: "integer" } }, { name: "offset", in: "query", schema: { type: "integer" } }, { name: "status", in: "query", schema: { type: "string" } }], responses: { 200: { description: "Run list with pagination" } } } },
66155
+ "/v1/runs/{id}": { get: { summary: "Get run status", tags: ["Agentic"], parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }], responses: { 200: { description: "Run details" }, 404: { description: "Not found" } } }, delete: { summary: "Abort run", tags: ["Agentic"], responses: { 200: { description: "Aborted" } } } },
66156
+ "/v1/config": { get: { summary: "Get configuration", tags: ["Config"], responses: { 200: { description: "Current config" } } }, patch: { summary: "Update configuration", tags: ["Config"], responses: { 200: { description: "Updated" } } } },
66157
+ "/v1/config/model": { get: { summary: "Current model", tags: ["Config"], responses: { 200: { description: "Model name" } } }, put: { summary: "Switch model", tags: ["Config"], responses: { 200: { description: "Switched" } } } },
66158
+ "/v1/config/endpoint": { get: { summary: "Current endpoint", tags: ["Config"], responses: { 200: { description: "Endpoint URL" } } }, put: { summary: "Switch endpoint", tags: ["Config"], responses: { 200: { description: "Switched" } } } },
66159
+ "/v1/usage": { get: { summary: "Token usage and rate limits", tags: ["Metering"], responses: { 200: { description: "Usage stats" } } } },
66160
+ "/v1/audit": { get: { summary: "Query audit log", tags: ["Audit"], parameters: [{ name: "since", in: "query", schema: { type: "string" } }, { name: "user", in: "query", schema: { type: "string" } }, { name: "limit", in: "query", schema: { type: "integer" } }], responses: { 200: { description: "Audit records" } } } },
66161
+ "/v1/commands": { get: { summary: "List slash commands", tags: ["Commands"], responses: { 200: { description: "Command list" } } } },
66162
+ "/v1/commands/{cmd}": { post: { summary: "Execute slash command", tags: ["Commands"], responses: { 200: { description: "Command output" } } } },
66163
+ "/v1/profiles": { get: { summary: "List tool profiles", tags: ["Profiles"], responses: { 200: { description: "Profile list" } } }, post: { summary: "Create profile", tags: ["Profiles"], responses: { 201: { description: "Created" } } } },
66164
+ "/v1/profiles/{name}": { get: { summary: "Get profile", tags: ["Profiles"], responses: { 200: { description: "Profile details" } } }, delete: { summary: "Delete profile", tags: ["Profiles"], responses: { 200: { description: "Deleted" } } } }
66165
+ }
66166
+ };
66167
+ }
66168
+ function getSwaggerUI() {
66169
+ return `<!DOCTYPE html><html><head><title>OA API Docs</title>
66170
+ <style>body{font-family:'SF Mono',monospace;background:#1a1a1e;color:#b0b0b0;margin:0;padding:20px}
66171
+ h1{color:#b2920a;font-size:1.2rem}h2{color:#b2920a;font-size:0.9rem;margin-top:20px}
66172
+ .endpoint{background:#1e1e22;border:1px solid #2a2a30;border-radius:3px;padding:10px;margin:8px 0}
66173
+ .method{font-weight:bold;padding:2px 8px;border-radius:2px;font-size:0.75rem}
66174
+ .get{color:#4ec94e}.post{color:#b2920a}.put{color:#4e94c9}.patch{color:#c9944e}.delete{color:#c94e4e}
66175
+ .path{color:#b0b0b0;margin-left:8px}.summary{color:#888;font-size:0.75rem;margin-left:12px}
66176
+ .tag{margin-top:16px;border-bottom:1px solid #2a2a30;padding-bottom:4px}
66177
+ a{color:#b2920a}
66178
+ </style></head><body>
66179
+ <h1>Open Agents API</h1>
66180
+ <p>Interactive docs. For full spec: <a href="/openapi.json">/openapi.json</a></p>
66181
+ <div id="docs"></div>
66182
+ <script>
66183
+ fetch('/openapi.json').then(r=>r.json()).then(spec=>{
66184
+ const docs=document.getElementById('docs');
66185
+ const tags={};
66186
+ for(const[path,methods]of Object.entries(spec.paths)){
66187
+ for(const[method,op]of Object.entries(methods)){
66188
+ const tag=(op.tags||['Other'])[0];
66189
+ if(!tags[tag])tags[tag]=[];
66190
+ tags[tag].push({method,path,summary:op.summary||''});
66191
+ }
66192
+ }
66193
+ for(const[tag,endpoints]of Object.entries(tags)){
66194
+ docs.innerHTML+='<div class="tag"><h2>'+tag+'</h2></div>';
66195
+ for(const ep of endpoints){
66196
+ docs.innerHTML+='<div class="endpoint"><span class="method '+ep.method+'">'+ep.method.toUpperCase()+'</span><span class="path">'+ep.path+'</span><span class="summary">'+ep.summary+'</span></div>';
66197
+ }
66198
+ }
66199
+ });
66200
+ </script></body></html>`;
66201
+ }
66202
+ var init_openapi = __esm({
66203
+ "packages/cli/dist/api/openapi.js"() {
66204
+ "use strict";
66205
+ }
66206
+ });
66207
+
66208
+ // packages/cli/dist/api/auth-oidc.js
66209
+ var OIDC_ISSUER, OIDC_AUDIENCE, OIDC_SCOPE_CLAIM;
66210
+ var init_auth_oidc = __esm({
66211
+ "packages/cli/dist/api/auth-oidc.js"() {
66212
+ "use strict";
66213
+ OIDC_ISSUER = process.env["OA_OIDC_ISSUER"] || "";
66214
+ OIDC_AUDIENCE = process.env["OA_OIDC_AUDIENCE"] || "";
66215
+ OIDC_SCOPE_CLAIM = process.env["OA_OIDC_SCOPE_CLAIM"] || "scope";
66216
+ }
66217
+ });
66218
+
65980
66219
  // packages/cli/dist/api/profiles.js
65981
66220
  import { existsSync as existsSync53, readFileSync as readFileSync42, writeFileSync as writeFileSync25, mkdirSync as mkdirSync27, readdirSync as readdirSync20, unlinkSync as unlinkSync12 } from "node:fs";
65982
66221
  import { join as join70 } from "node:path";
@@ -66912,6 +67151,20 @@ async function handleV1Run(req, res) {
66912
67151
  jsonResponse(res, 400, { error: "Missing required field: task" });
66913
67152
  return;
66914
67153
  }
67154
+ if (task.length > 5e4) {
67155
+ jsonResponse(res, 400, { error: "Task too long", message: "Max 50,000 characters", length: task.length });
67156
+ return;
67157
+ }
67158
+ const sandbox = requestBody["sandbox"] || process.env["OA_DEFAULT_SANDBOX"] || "none";
67159
+ if (sandbox === "container") {
67160
+ try {
67161
+ const { execSync: es } = __require("node:child_process");
67162
+ es("docker --version", { stdio: "pipe", timeout: 5e3 });
67163
+ } catch {
67164
+ jsonResponse(res, 400, { error: "Container sandbox unavailable", message: "Docker not found. Install Docker or use sandbox:'none'." });
67165
+ return;
67166
+ }
67167
+ }
66915
67168
  const id = `job-${randomBytes16(3).toString("hex")}`;
66916
67169
  const dir = jobsDir();
66917
67170
  const workingDir = requestBody["working_directory"] || req.headers["x-working-directory"];
@@ -67278,6 +67531,15 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
67278
67531
  handleMetrics(res);
67279
67532
  return;
67280
67533
  }
67534
+ if (pathname === "/openapi.json" && method === "GET") {
67535
+ jsonResponse(res, 200, getOpenApiSpec());
67536
+ return;
67537
+ }
67538
+ if (pathname === "/docs" && method === "GET") {
67539
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
67540
+ res.end(getSwaggerUI());
67541
+ return;
67542
+ }
67281
67543
  if (pathname === "/" && method === "GET" && req.headers.accept?.includes("text/html")) {
67282
67544
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
67283
67545
  res.end(getWebUI());
@@ -67707,6 +67969,8 @@ var init_serve = __esm({
67707
67969
  init_audit_log();
67708
67970
  init_web_ui();
67709
67971
  init_logger();
67972
+ init_openapi();
67973
+ init_auth_oidc();
67710
67974
  init_oa_directory();
67711
67975
  init_render();
67712
67976
  init_profiles();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.185.75",
3
+ "version": "0.185.77",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",