opencode-agent-flow 0.1.0 → 0.1.1
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/CHANGELOG.md +34 -0
- package/README.md +93 -12
- package/bin/cli.mjs +3 -1
- package/docs/opencode-v2-api-notes.md +33 -1
- package/lib/demo.mjs +34 -2
- package/lib/upstream.mjs +6 -2
- package/package.json +39 -8
- package/server.mjs +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
|
+
and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.1.1] - 2026-10-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Actionable HTTP 401 error reporting without exposing upstream credentials.
|
|
12
|
+
- Connected OpenCode version in backend logs.
|
|
13
|
+
- Cyclic demo activity: tools, working states and bounded events every 1.5 seconds.
|
|
14
|
+
- Animated GIF and MP4 README demos, excluded from the npm package.
|
|
15
|
+
- CI for Node.js 20/22/24 on Linux and Node.js 22 on macOS/Windows.
|
|
16
|
+
- Bug report, feature request and pull request templates, plus security/discussion links.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Verified compatibility with a clean OpenCode 2.0.32 installation.
|
|
21
|
+
- Documented local/remote setup and generated or explicit server passwords.
|
|
22
|
+
- Normalized package.json metadata with `npm pkg fix`.
|
|
23
|
+
- CLI help reads the release version from package.json.
|
|
24
|
+
|
|
25
|
+
## [0.1.0] - 2026-10-16
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Initial release: read-only OpenCode V2 dashboard over public HTTP and SSE.
|
|
30
|
+
- WebGL agent trees, grouped parallel calls, tools, approval waits, tokens and estimated costs.
|
|
31
|
+
- Local/remote upstream support, fictional demo data and zero runtime npm dependencies.
|
|
32
|
+
|
|
33
|
+
[0.1.1]: https://github.com/orybakov/opencode-agent-flow/compare/v0.1.0...v0.1.1
|
|
34
|
+
[0.1.0]: https://github.com/orybakov/opencode-agent-flow/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# opencode-agent-flow
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/opencode-agent-flow)
|
|
4
|
+
[](https://github.com/orybakov/opencode-agent-flow/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
|
|
8
|
+
**Watch your agents work, not just their logs.**
|
|
9
|
+
|
|
3
10
|
A live, read-only visualization of OpenCode V2 orchestration. See agent trees,
|
|
4
11
|
grouped parallel calls, current tools, approval waits, token usage and estimated
|
|
5
12
|
costs in one WebGL dashboard. Connect to a local or remote OpenCode server using
|
|
@@ -7,23 +14,79 @@ only its public HTTP API and SSE—no database access required.
|
|
|
7
14
|
|
|
8
15
|
Zero runtime npm dependencies. Node.js 20+; no frontend build step.
|
|
9
16
|
|
|
10
|
-

|
|
18
|
+
|
|
19
|
+
[Watch the MP4 demo](https://github.com/orybakov/opencode-agent-flow/raw/refs/heads/main/docs/demo.mp4) · fictional data; no credentials or private sessions.
|
|
11
20
|
|
|
12
21
|
## Quick start
|
|
13
22
|
|
|
23
|
+
### Local OpenCode server
|
|
24
|
+
|
|
25
|
+
Start OpenCode V2 in one terminal:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
opencode serve --hostname 127.0.0.1 --port 4096
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
In another terminal, set `OPENCODE_SERVER_PASSWORD` to the password printed by
|
|
32
|
+
that server (see below), then run:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npx opencode-agent-flow --open
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Remote OpenCode server
|
|
39
|
+
|
|
40
|
+
Set `OPENCODE_SERVER_PASSWORD` in your local terminal to the remote server's
|
|
41
|
+
password, then connect over HTTPS:
|
|
42
|
+
|
|
14
43
|
```sh
|
|
15
|
-
npx opencode-agent-flow
|
|
16
|
-
# Protected server: supply the password through your environment, not CLI flags.
|
|
17
|
-
OPENCODE_SERVER_PASSWORD="$YOUR_OPENCODE_PASSWORD" npx opencode-agent-flow
|
|
18
|
-
# Remote server
|
|
19
|
-
OPENCODE_SERVER_PASSWORD="$YOUR_OPENCODE_PASSWORD" npx opencode-agent-flow --server https://opencode.example.org
|
|
20
|
-
# Fictional data, no server required
|
|
21
|
-
npx opencode-agent-flow --demo --open
|
|
44
|
+
npx opencode-agent-flow --server https://opencode.example.org --open
|
|
22
45
|
```
|
|
23
46
|
|
|
47
|
+
The dashboard itself stays on loopback. The remote server must expose the V2
|
|
48
|
+
HTTP API and SSE; no database access or browser-side credentials are needed.
|
|
49
|
+
|
|
50
|
+
For a preview without any OpenCode server: `npx opencode-agent-flow --demo --open`.
|
|
51
|
+
Demo tools, agent states and events cycle every 1.5 seconds.
|
|
52
|
+
|
|
24
53
|
Open <http://127.0.0.1:3340>. For an unpublished local checkout:
|
|
25
54
|
`node bin/cli.mjs --demo`.
|
|
26
55
|
|
|
56
|
+
## Where do I get the password?
|
|
57
|
+
|
|
58
|
+
On a clean **OpenCode 2.0.32**, `opencode serve` generates a password when none
|
|
59
|
+
is configured and prints it in the launching terminal on the **`server password`**
|
|
60
|
+
line, after the listening URL. Copy that value privately into the
|
|
61
|
+
`OPENCODE_SERVER_PASSWORD` environment variable of the Agent Flow process.
|
|
62
|
+
The default HTTP Basic username is **`opencode`**; use `--username` or
|
|
63
|
+
`OPENCODE_SERVER_USERNAME` if yours differs.
|
|
64
|
+
|
|
65
|
+
Alternatively, choose a strong password yourself and set
|
|
66
|
+
`OPENCODE_SERVER_PASSWORD` **before starting `opencode serve`**, then supply the
|
|
67
|
+
same value to Agent Flow. For example, after loading it from your secret manager
|
|
68
|
+
into a shell variable (POSIX shell):
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
export OPENCODE_SERVER_PASSWORD="$YOUR_OPENCODE_PASSWORD"
|
|
72
|
+
opencode serve --hostname 127.0.0.1 --port 4096
|
|
73
|
+
# In another terminal, export the same environment variable, then:
|
|
74
|
+
npx opencode-agent-flow --open
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
In PowerShell, set an environment variable with
|
|
78
|
+
`$env:OPENCODE_SERVER_PASSWORD = $YourOpenCodePassword` instead. Do not put the
|
|
79
|
+
password in a URL, CLI flag, screenshot, issue or committed config. Agent Flow
|
|
80
|
+
does not discover credentials from the OpenCode database or managed service.
|
|
81
|
+
An already-running server does not inherit environment changes: set the value
|
|
82
|
+
when launching it, not only when launching this dashboard.
|
|
83
|
+
|
|
84
|
+
The generated-password output and explicit environment password were verified
|
|
85
|
+
against an isolated 2.0.32 installation. Current V2 docs describe the
|
|
86
|
+
[client/server model](https://opencode.ai/v2/docs/cli) and
|
|
87
|
+
[foreground server environment](https://opencode.ai/v2/docs/network#foreground);
|
|
88
|
+
`opencode serve` is distinct from the automatically managed background service.
|
|
89
|
+
|
|
27
90
|
## Options
|
|
28
91
|
|
|
29
92
|
| Flag | Environment fallback | Default |
|
|
@@ -57,7 +120,19 @@ individual-call popovers preserve the orchestration hierarchy.
|
|
|
57
120
|
including the root. The recent activity window applies to root selection; explicit
|
|
58
121
|
session mode loads the complete selected tree, including dormant children.
|
|
59
122
|
|
|
60
|
-
##
|
|
123
|
+
## Compatibility
|
|
124
|
+
|
|
125
|
+
- Verified with **OpenCode 2.0.30 and 2.0.32** on Linux (official V2 npm package:
|
|
126
|
+
`@opencode/cli`, not the V1 `opencode-ai` package).
|
|
127
|
+
- Tests pass locally on Linux with **Node.js 20, 22 and 24**.
|
|
128
|
+
- CI runs the test suite, syntax checks and package dry-run on Linux with Node
|
|
129
|
+
20/22/24, plus **macOS and Windows with Node 22**. Those platforms are covered
|
|
130
|
+
by CI tests only, not a verified live OpenCode/browser deployment; the first
|
|
131
|
+
hosted run happens after push.
|
|
132
|
+
- The V2 HTTP API is **experimental**; future changes may require adapter updates.
|
|
133
|
+
Non-V2 servers remain in connecting state.
|
|
134
|
+
|
|
135
|
+
## Security and limitations
|
|
61
136
|
|
|
62
137
|
- Loopback binding by default. **The UI has no authentication**; exposing it on
|
|
63
138
|
another interface reveals session titles, paths and activity. Use an authenticated
|
|
@@ -70,8 +145,6 @@ session mode loads the complete selected tree, including dormant children.
|
|
|
70
145
|
**best-effort**, not a guarantee. Do not expose it to a network without a
|
|
71
146
|
reverse proxy providing authentication. Report vulnerabilities via
|
|
72
147
|
[GitHub Security Advisories](https://github.com/orybakov/opencode-agent-flow/security/advisories/new).
|
|
73
|
-
- Tested against **OpenCode 2.0.30**. The V2 HTTP API is **experimental**; future
|
|
74
|
-
changes may require adapter updates. Non-V2 servers remain in connecting state.
|
|
75
148
|
- SSE is live-only, without replay. Reconciliation recovers durable snapshots,
|
|
76
149
|
not every transient event. No historical permission-decision journal is available.
|
|
77
150
|
- Token/cost panels use available history (at least 12 hours/current day for
|
|
@@ -87,7 +160,8 @@ subscriptions may differ, and these figures are not billing statements.
|
|
|
87
160
|
|
|
88
161
|
```sh
|
|
89
162
|
node bin/cli.mjs --demo
|
|
90
|
-
node
|
|
163
|
+
node scripts/check-syntax.mjs
|
|
164
|
+
npm test
|
|
91
165
|
npm pack --dry-run
|
|
92
166
|
```
|
|
93
167
|
|
|
@@ -95,6 +169,13 @@ See [architecture](docs/architecture.md) and
|
|
|
95
169
|
[public API notes](docs/opencode-v2-api-notes.md). Vendored browser modules avoid
|
|
96
170
|
CDN/network dependencies; there is no install or bundling step.
|
|
97
171
|
|
|
172
|
+
## Roadmap
|
|
173
|
+
|
|
174
|
+
- Ensemble team views and richer orchestration context.
|
|
175
|
+
- Memory and rendering performance for thousands of sessions.
|
|
176
|
+
- A small, ready-to-run Docker image.
|
|
177
|
+
- Additional themes and visual customization.
|
|
178
|
+
|
|
98
179
|
## License and acknowledgements
|
|
99
180
|
|
|
100
181
|
MIT © 2026 orybakov. WebGL rendering uses [three.js](https://threejs.org/),
|
package/bin/cli.mjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
3
4
|
import { start } from '../server.mjs';
|
|
4
5
|
process.on('unhandledRejection', error => console.error('Unhandled rejection:', error?.name || 'Error'));
|
|
5
6
|
process.on('uncaughtException', error => console.error('Uncaught exception:', error?.name || 'Error'));
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
9
|
+
const help = `opencode-agent-flow ${version} — live OpenCode V2 agent visualization
|
|
8
10
|
|
|
9
11
|
Usage: npx opencode-agent-flow [options]
|
|
10
12
|
--server URL OpenCode V2 URL (OPENCODE_URL; http://127.0.0.1:4096)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenCode V2 public API: contributor notes
|
|
2
2
|
|
|
3
|
-
Contract verified against OpenCode **2.0.30**. The server describes this HTTP API
|
|
3
|
+
Contract verified against OpenCode **2.0.30** and clean **2.0.32**. The server describes this HTTP API
|
|
4
4
|
as experimental. Version checks reject non-V2 servers but do not guarantee that
|
|
5
5
|
every future V2 revision preserves this contract.
|
|
6
6
|
|
|
@@ -84,6 +84,38 @@ messages, location-scoped pending requests, Basic authentication and a live glob
|
|
|
84
84
|
stream containing multiple locations. Remote deployments and future versions
|
|
85
85
|
require additional verification; capability changes should fail clearly.
|
|
86
86
|
|
|
87
|
+
### Clean 2.0.32 verification (2026-10-17)
|
|
88
|
+
|
|
89
|
+
Installed official `@opencode/cli@2.0.32` locally with isolated HOME/XDG paths;
|
|
90
|
+
the similarly named `opencode-ai` npm package is V1. No existing service or data
|
|
91
|
+
was used. Root and linked child sessions were created through `POST /api/session`
|
|
92
|
+
without model calls. One CLIProxy `gemini-3.8-flash-high` turn read a fixture
|
|
93
|
+
README using `read` and completed successfully.
|
|
94
|
+
|
|
95
|
+
Session/message/active snapshots and the global SSE envelope still match the
|
|
96
|
+
contract above. Observed 22 frames including `session.tool.input.started/ended`,
|
|
97
|
+
`session.tool.called/success`, `session.step.*`, `session.usage.updated`,
|
|
98
|
+
`session.text.*`, and `session.execution.started/succeeded`. Tool events retain
|
|
99
|
+
`data.sessionID`, `data.assistantMessageID`, and `data.id`. The BFF emitted four
|
|
100
|
+
live state frames (idle → working → tool completion → idle), retained the child,
|
|
101
|
+
and reported 10,301 input tokens, matching the two assistant snapshots. Upstream
|
|
102
|
+
output tokens were zero despite a text answer; the BFF preserves reported usage
|
|
103
|
+
rather than inventing output counts.
|
|
104
|
+
|
|
105
|
+
Both `/api/state` and `/api/state?session=ID`, `/api/sessions`, and the browser
|
|
106
|
+
session view were verified. Browser viewport: 1440×900; no console errors or
|
|
107
|
+
failed requests (headless WebGL ReadPixels performance warnings only).
|
|
108
|
+
|
|
109
|
+
Authentication caveat: an unset password does **not** produce an unprotected
|
|
110
|
+
server on this clean version. `serve` generates a password and unauthenticated
|
|
111
|
+
requests return 401. Basic auth works with both its generated password and an
|
|
112
|
+
explicit `OPENCODE_SERVER_PASSWORD`; a real password-free upstream scenario was
|
|
113
|
+
therefore unavailable. Wrong credentials now produce an actionable 401 log
|
|
114
|
+
without exposing credentials, while retaining the existing retry behavior.
|
|
115
|
+
Successful initialization logs the negotiated server version. No event-schema
|
|
116
|
+
adapter changes were needed. Multi-location streams and permission/form flows
|
|
117
|
+
were not re-exercised on 2.0.32; their earlier 2.0.30 checks remain the evidence.
|
|
118
|
+
|
|
87
119
|
Sources: [API](https://opencode.ai/v2/docs/api),
|
|
88
120
|
[client](https://opencode.ai/v2/docs/build/client), published
|
|
89
121
|
`@opencode/client@2.0.30` event types, and live read-only HTTP/SSE observations.
|
package/lib/demo.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function demo() {
|
|
1
|
+
export function demo(onChange = () => {}, { interval = setInterval, cancel = clearInterval, clock = Date.now } = {}) {
|
|
2
2
|
const now = Date.now(), sessions = new Map(), messages = new Map();
|
|
3
3
|
const agents = ['lead', 'builder', 'researcher', 'reviewer', 'builder', 'tester', 'designer'];
|
|
4
4
|
agents.forEach((agent, i) => {
|
|
@@ -6,5 +6,37 @@ export function demo() {
|
|
|
6
6
|
sessions.set(id, { id, parentID: i ? 'ses_demo0' : undefined, title: i ? `${agent} · public API integration` : 'Build a better developer experience', agent, model, time: { created: now - 3600000, updated: now - i * 1000 }, tokens: { input: 24000 + i * 7000, output: 3200, cache: { read: 180000 + i * 19000, write: 0 } } });
|
|
7
7
|
messages.set(id, [{ id: `msg_demo${i}`, type: 'assistant', model, time: { created: now - 90000 + i * 5000, completed: now - 20000 }, tokens: { input: 12000, output: 1200, cache: { read: 48000, write: 0 } }, content: [{ id: `call_demo${i}`, type: 'tool', name: ['read', 'patch', 'webfetch', 'grep', 'shell', 'shell', 'read'][i], state: { status: i === 1 || i === 2 ? 'running' : 'completed', input: { path: ['src/index.ts', 'src/api/client.ts', 'https://example.org/docs', 'src/**/*.ts', 'npm test', 'npm run check', 'docs/design.md'][i] } }, time: { created: now - 30000 + i * 2000, completed: now - 10000 } }], finish: 'tool-calls' }]);
|
|
8
8
|
});
|
|
9
|
-
|
|
9
|
+
let timer, tick = 0;
|
|
10
|
+
const tools = [
|
|
11
|
+
['read', 'src/index.ts'], ['patch', 'src/api/client.ts'],
|
|
12
|
+
['webfetch', 'https://example.org/docs'], ['grep', 'src/**/*.ts'],
|
|
13
|
+
['shell', 'npm test'], ['shell', 'npm run check'], ['read', 'docs/design.md']
|
|
14
|
+
];
|
|
15
|
+
const state = { sessions, messages, active: { ses_demo1: { type: 'running' }, ses_demo2: { type: 'running' } }, approvals: [], health: { ok: true, status: 200 }, version: '2.0.32', connection: 'connected', cutoff: () => clock() - 86400000,
|
|
16
|
+
start: async () => {
|
|
17
|
+
if (timer !== undefined) return;
|
|
18
|
+
timer = interval(() => {
|
|
19
|
+
tick++;
|
|
20
|
+
const time = clock();
|
|
21
|
+
state.active = {};
|
|
22
|
+
agents.forEach((agent, i) => {
|
|
23
|
+
const id = `ses_demo${i}`, previous = sessions.get(id);
|
|
24
|
+
const working = i === 0 || (tick + i) % 4 !== 0;
|
|
25
|
+
if (working) state.active[id] = { type: 'running' };
|
|
26
|
+
sessions.set(id, { ...previous, time: { ...previous.time, updated: time }, tokens: { ...previous.tokens, output: previous.tokens.output + 24 } });
|
|
27
|
+
// Replace arrays/objects to invalidate the BFF's identity-based caches.
|
|
28
|
+
const history = messages.get(id).map(message => ({ ...message, time: { ...message.time, completed: time }, content: message.content.map(tool => ({ ...tool, state: { ...tool.state, status: 'completed' }, time: { ...tool.time, completed: time } })) }));
|
|
29
|
+
const [name, path] = tools[(tick + i) % tools.length];
|
|
30
|
+
const tool = { id: `call_demo${i}_${tick}`, type: 'tool', name, state: { status: working ? 'running' : 'completed', input: { path } }, time: { created: time, ...(working ? {} : { completed: time }) } };
|
|
31
|
+
history.push({ id: `msg_demo${i}_${tick}`, type: 'assistant', model: previous.model, time: { created: time }, tokens: { input: 12000, output: 1200 + tick % 100, cache: { read: 48000, write: 0 } }, content: [tool], finish: 'tool-calls' });
|
|
32
|
+
messages.set(id, history.slice(-12));
|
|
33
|
+
});
|
|
34
|
+
onChange();
|
|
35
|
+
}, 1500);
|
|
36
|
+
timer.unref?.();
|
|
37
|
+
},
|
|
38
|
+
stop() { if (timer !== undefined) cancel(timer); timer = undefined; },
|
|
39
|
+
ensureTree: async () => {}
|
|
40
|
+
};
|
|
41
|
+
return state;
|
|
10
42
|
}
|
package/lib/upstream.mjs
CHANGED
|
@@ -167,7 +167,7 @@ export class Upstream {
|
|
|
167
167
|
void this.initialize();
|
|
168
168
|
}
|
|
169
169
|
async initialize() {
|
|
170
|
-
let delay = 1000;
|
|
170
|
+
let delay = 1000, lastError;
|
|
171
171
|
while (!this.abort.signal.aborted) {
|
|
172
172
|
try {
|
|
173
173
|
let info;
|
|
@@ -175,11 +175,15 @@ export class Upstream {
|
|
|
175
175
|
this.version = info.version || info.data?.version;
|
|
176
176
|
if (!/^2\./.test(this.version || '')) throw new Error('Unsupported server: OpenCode V2 /api/info version 2.x is required.');
|
|
177
177
|
await this.sync(); if (this.abort.signal.aborted) return; void this.stream();
|
|
178
|
+
console.log(`Connected to OpenCode ${this.version}.`);
|
|
178
179
|
this.syncTimer = setInterval(() => { void this.sync().catch(() => console.error('OpenCode reconciliation failed; retrying in 60s.')); }, 60000);
|
|
179
180
|
this.pendingTimer = setInterval(() => { void this.pollPending(); }, 5000);
|
|
180
181
|
return;
|
|
181
|
-
} catch {
|
|
182
|
+
} catch (error) {
|
|
182
183
|
if (this.abort.signal.aborted) return;
|
|
184
|
+
// Keep retrying, but do not silently hide authentication/version failures.
|
|
185
|
+
const message = error.message;
|
|
186
|
+
if (message !== lastError) { console.error(message); lastError = message; }
|
|
183
187
|
this.connection = 'connecting'; this.changed();
|
|
184
188
|
await new Promise(resolve => { const finish = () => { clearTimeout(timer); this.abort.signal.removeEventListener('abort', finish); resolve(); }; const timer = setTimeout(finish, delay); this.abort.signal.addEventListener('abort', finish, { once: true }); });
|
|
185
189
|
delay = Math.min(delay * 2, 30000);
|
package/package.json
CHANGED
|
@@ -1,17 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-agent-flow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Live agent flow visualization for OpenCode V2 over its public HTTP API and SSE",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
|
|
6
|
+
"bin": {
|
|
7
|
+
"opencode-agent-flow": "bin/cli.mjs"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20"
|
|
11
|
+
},
|
|
8
12
|
"license": "MIT",
|
|
9
13
|
"author": "orybakov (https://github.com/orybakov)",
|
|
10
|
-
"repository": {
|
|
11
|
-
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/orybakov/opencode-agent-flow.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/orybakov/opencode-agent-flow/issues"
|
|
20
|
+
},
|
|
12
21
|
"homepage": "https://github.com/orybakov/opencode-agent-flow#readme",
|
|
13
|
-
"keywords": [
|
|
22
|
+
"keywords": [
|
|
23
|
+
"opencode",
|
|
24
|
+
"ai-agents",
|
|
25
|
+
"visualizer",
|
|
26
|
+
"observability",
|
|
27
|
+
"webgl"
|
|
28
|
+
],
|
|
14
29
|
"exports": {},
|
|
15
|
-
"files": [
|
|
16
|
-
|
|
30
|
+
"files": [
|
|
31
|
+
"bin",
|
|
32
|
+
"server.mjs",
|
|
33
|
+
"lib",
|
|
34
|
+
"public",
|
|
35
|
+
"prices.json",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE",
|
|
38
|
+
"SECURITY.md",
|
|
39
|
+
"CONTRIBUTING.md",
|
|
40
|
+
"CHANGELOG.md",
|
|
41
|
+
"docs/*.md",
|
|
42
|
+
"docs/screenshot.png"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"start": "node bin/cli.mjs",
|
|
46
|
+
"test": "node --test"
|
|
47
|
+
}
|
|
17
48
|
}
|
package/server.mjs
CHANGED
|
@@ -15,7 +15,7 @@ const json = s => { try { return JSON.parse(s || '{}'); } catch { return {}; } }
|
|
|
15
15
|
const modelId = m => clip((typeof m === 'string' ? m : m?.id || m?.modelID) || 'unknown', 100);
|
|
16
16
|
const rate = m => prices.models[m] || Object.entries(prices.models).find(([k]) => m.startsWith(k))?.[1] || prices.default;
|
|
17
17
|
let snapshot, signature = '', timer;
|
|
18
|
-
const upstream = config.demo ? demo() : new Upstream(config, schedule);
|
|
18
|
+
const upstream = config.demo ? demo(schedule) : new Upstream(config, schedule);
|
|
19
19
|
let approvals = [], permissionHealth = { ok: false, status: null };
|
|
20
20
|
const clients = new Set();
|
|
21
21
|
const rowCache = new Map();
|