@useorgx/orgx-opencode-plugin 0.1.0-alpha.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/README.md +98 -0
- package/dist/OpenCodeDriver.d.ts +66 -0
- package/dist/OpenCodeDriver.d.ts.map +1 -0
- package/dist/OpenCodeDriver.js +323 -0
- package/dist/OpenCodeDriver.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +32 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/peer.d.ts +31 -0
- package/dist/peer.d.ts.map +1 -0
- package/dist/peer.js +139 -0
- package/dist/peer.js.map +1 -0
- package/dist/workGraphOutbox.d.ts +16 -0
- package/dist/workGraphOutbox.d.ts.map +1 -0
- package/dist/workGraphOutbox.js +44 -0
- package/dist/workGraphOutbox.js.map +1 -0
- package/package.json +39 -0
- package/plugin.manifest.json +9 -0
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# orgx-opencode-plugin
|
|
2
|
+
|
|
3
|
+
OrgX plugin peer for **OpenCode**. One of three reference peers (alongside `orgx-claude-code-plugin` and `orgx-codex-plugin`) that implements [`@useorgx/orgx-gateway-sdk`](https://github.com/useorgx/orgx-gateway-sdk) Protocol v1.
|
|
4
|
+
|
|
5
|
+
**The peer model:** this plugin opens its own authenticated WebSocket to OrgX server, receives `task.dispatch` messages, runs them in your local OpenCode session (your subscription pays the tokens), and posts receipts + deviations back. It also writes compact, redacted Work Graph events locally so audit-first reconciliation can preserve progress and fingerprints across signup. No central broker. If another peer goes down, this one keeps running.
|
|
6
|
+
|
|
7
|
+
## Install + run
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @useorgx/orgx-opencode-plugin
|
|
11
|
+
# or pnpm: pnpm add -g @useorgx/orgx-opencode-plugin
|
|
12
|
+
|
|
13
|
+
export ORGX_API_KEY=oxk_...
|
|
14
|
+
export ORGX_WORKSPACE_ID=<uuid>
|
|
15
|
+
orgx-opencode-plugin
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or programmatic:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { startPeer } from '@useorgx/orgx-opencode-plugin';
|
|
22
|
+
|
|
23
|
+
const peer = await startPeer({
|
|
24
|
+
apiKey: process.env.ORGX_API_KEY!,
|
|
25
|
+
workspaceId: process.env.ORGX_WORKSPACE_ID!,
|
|
26
|
+
});
|
|
27
|
+
// later:
|
|
28
|
+
await peer.stop();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## How it talks to OpenCode
|
|
32
|
+
|
|
33
|
+
The peer discovers the local OpenCode daemon via its state file:
|
|
34
|
+
|
|
35
|
+
| OS | Path |
|
|
36
|
+
|---|---|
|
|
37
|
+
| macOS / Linux | `~/.opencode/state.json` |
|
|
38
|
+
| Windows | `%APPDATA%/opencode/state.json` |
|
|
39
|
+
|
|
40
|
+
The state file tells us which local port the daemon listens on. The driver then:
|
|
41
|
+
|
|
42
|
+
1. `GET /status` — verifies auth + reports subscription health
|
|
43
|
+
2. `POST /sessions` — creates a fresh session bound to the dispatched task
|
|
44
|
+
3. `GET /sessions/:id/events` (NDJSON stream) — drives progress
|
|
45
|
+
|
|
46
|
+
Each `file_edit` / `tool_call` event becomes a `task.step` wire message. Every skill rule fetched from `/api/v1/plan-skills` runs against the event stream; matches become `task.deviation` events (deduped per (run_id, skill_id, fingerprint)).
|
|
47
|
+
|
|
48
|
+
## Work Graph reconciliation
|
|
49
|
+
|
|
50
|
+
The driver writes passive event summaries to
|
|
51
|
+
`~/.config/useorgx/wizard/hooks/events.jsonl` by default. Set
|
|
52
|
+
`ORGX_WIZARD_HOOK_OUTBOX` to override the path, or pass
|
|
53
|
+
`workGraphOutboxPath: false` when starting the peer programmatically to disable
|
|
54
|
+
the local trail.
|
|
55
|
+
|
|
56
|
+
These JSONL records are intentionally compact. They include source client,
|
|
57
|
+
event kind, run/session handles, repo path, evidence refs, and counts; they do
|
|
58
|
+
not include raw prompts, raw transcripts, API keys, tokens, or storage state.
|
|
59
|
+
The OrgX wizard can later use them to detect missed OrgX writeback, generate a
|
|
60
|
+
shareable public Work Graph readout, and hydrate the fingerprint into a signed-up
|
|
61
|
+
workspace.
|
|
62
|
+
|
|
63
|
+
## License heartbeat
|
|
64
|
+
|
|
65
|
+
`startPeer()` posts `POST /api/v1/licenses/heartbeat` on boot and then every 7 days. The manifest is read from `plugin.manifest.json`; when the fingerprint + signature are missing (dev builds), the server marks the license `degraded` in permissive mode — read-only features keep working, but deviation ingestion 402s until a signed manifest ships.
|
|
66
|
+
|
|
67
|
+
## Skills
|
|
68
|
+
|
|
69
|
+
Rules are fetched once per peer boot from `GET /api/v1/plan-skills?workspace_id=…`. The shape is:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
{
|
|
73
|
+
skills: [
|
|
74
|
+
{
|
|
75
|
+
id: 'parametrize-tests',
|
|
76
|
+
rules: [
|
|
77
|
+
{ pattern: 'parametrize', on: 'file_edit',
|
|
78
|
+
dedupe_fingerprint: '...', evidence_kind: 'test_style_shift' }
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Additions / demotions take effect on peer restart (or via an in-band "rules reload" message in a follow-up).
|
|
86
|
+
|
|
87
|
+
## Development
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install
|
|
91
|
+
npm run type-check
|
|
92
|
+
npm test
|
|
93
|
+
npm run build
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Status
|
|
97
|
+
|
|
98
|
+
Alpha. Part of the Sovereign Execution initiative (`993cabeb`).
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCodeDriver — the implementation detail inside orgx-opencode-plugin
|
|
3
|
+
* that actually drives the user's OpenCode session.
|
|
4
|
+
*
|
|
5
|
+
* OpenCode exposes a local HTTP API on http://127.0.0.1:<port> when the
|
|
6
|
+
* user has the `opencode` daemon running. We discover the port via the
|
|
7
|
+
* standard XDG state file (~/.opencode/state.json on macOS/Linux,
|
|
8
|
+
* %APPDATA%/opencode/state.json on Windows). When the daemon isn't
|
|
9
|
+
* reachable, detect() reports not-installed and the peer surfaces this
|
|
10
|
+
* to the server on handshake — the server then routes new tasks to a
|
|
11
|
+
* different driver.
|
|
12
|
+
*
|
|
13
|
+
* Dispatch flow:
|
|
14
|
+
* 1. POST /sessions → create a fresh session bound to the task
|
|
15
|
+
* 2. POST /sessions/:id/messages with the rendered prompt
|
|
16
|
+
* 3. Poll /sessions/:id/events until a terminal ('assistant_completed'
|
|
17
|
+
* or 'error') arrives, streaming task.step events as each tool call
|
|
18
|
+
* or file_edit lands.
|
|
19
|
+
* 4. Emit task.completed with the accumulated tokens + outcome.
|
|
20
|
+
*
|
|
21
|
+
* Deviation-reporting: every file_edit event is checked against the
|
|
22
|
+
* configured skill rules (fetched from /api/v1/plan-skills on peer
|
|
23
|
+
* connect and cached per run). When a rule matches, we emit
|
|
24
|
+
* task.deviation — the peer forwards it to the server.
|
|
25
|
+
*/
|
|
26
|
+
import type { Driver, DriverStatus, DriverProbe, DispatchableTask, PeerToServerMessage } from '@useorgx/orgx-gateway-sdk';
|
|
27
|
+
type SkillRule = {
|
|
28
|
+
skill_id: string;
|
|
29
|
+
match: {
|
|
30
|
+
pattern: string;
|
|
31
|
+
on: 'file_edit' | 'tool_call';
|
|
32
|
+
};
|
|
33
|
+
dedupe_fingerprint: string;
|
|
34
|
+
evidence_kind: string;
|
|
35
|
+
};
|
|
36
|
+
export type OpenCodeDriverOptions = {
|
|
37
|
+
/** Override the state-file lookup — primarily for tests. */
|
|
38
|
+
statePath?: string;
|
|
39
|
+
/** Async fetcher of skill rules (injected by the peer). */
|
|
40
|
+
skillRules?: () => Promise<SkillRule[]>;
|
|
41
|
+
/** Timeout for each polling round, in ms. */
|
|
42
|
+
pollTimeoutMs?: number;
|
|
43
|
+
/** Local Work Graph JSONL outbox path. Set false to disable. */
|
|
44
|
+
workGraphOutboxPath?: string | false;
|
|
45
|
+
/** Source client name for Work Graph reconciliation. */
|
|
46
|
+
sourceClient?: string;
|
|
47
|
+
};
|
|
48
|
+
export declare class OpenCodeDriver implements Driver {
|
|
49
|
+
readonly id: "opencode";
|
|
50
|
+
private portCache;
|
|
51
|
+
private readonly opts;
|
|
52
|
+
constructor(opts?: OpenCodeDriverOptions);
|
|
53
|
+
detect(): Promise<DriverStatus>;
|
|
54
|
+
probe(): Promise<DriverProbe>;
|
|
55
|
+
dispatch(task: DispatchableTask, context: {
|
|
56
|
+
run_id: string;
|
|
57
|
+
idempotency_key: string;
|
|
58
|
+
}): AsyncIterable<PeerToServerMessage>;
|
|
59
|
+
cancel(runId: string): Promise<void>;
|
|
60
|
+
private recordWorkGraph;
|
|
61
|
+
private resolvePort;
|
|
62
|
+
private createSession;
|
|
63
|
+
private streamSessionEvents;
|
|
64
|
+
}
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=OpenCodeDriver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenCodeDriver.d.ts","sourceRoot":"","sources":["../src/OpenCodeDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,2BAA2B,CAAC;AAiBnC,KAAK,SAAS,GAAG;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,WAAW,GAAG,WAAW,CAAA;KAAE,CAAC;IAC1D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACxC,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACrC,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,qBAAa,cAAe,YAAW,MAAM;IAC3C,QAAQ,CAAC,EAAE,EAAG,UAAU,CAAU;IAElC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;gBAEjC,IAAI,GAAE,qBAA0B;IAItC,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IA+B/B,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IAwB5B,QAAQ,CACb,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GACnD,aAAa,CAAC,mBAAmB,CAAC;IA0I/B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAW5B,eAAe;YA0Bf,WAAW;YAYX,aAAa;YA6BZ,mBAAmB;CA6BnC"}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCodeDriver — the implementation detail inside orgx-opencode-plugin
|
|
3
|
+
* that actually drives the user's OpenCode session.
|
|
4
|
+
*
|
|
5
|
+
* OpenCode exposes a local HTTP API on http://127.0.0.1:<port> when the
|
|
6
|
+
* user has the `opencode` daemon running. We discover the port via the
|
|
7
|
+
* standard XDG state file (~/.opencode/state.json on macOS/Linux,
|
|
8
|
+
* %APPDATA%/opencode/state.json on Windows). When the daemon isn't
|
|
9
|
+
* reachable, detect() reports not-installed and the peer surfaces this
|
|
10
|
+
* to the server on handshake — the server then routes new tasks to a
|
|
11
|
+
* different driver.
|
|
12
|
+
*
|
|
13
|
+
* Dispatch flow:
|
|
14
|
+
* 1. POST /sessions → create a fresh session bound to the task
|
|
15
|
+
* 2. POST /sessions/:id/messages with the rendered prompt
|
|
16
|
+
* 3. Poll /sessions/:id/events until a terminal ('assistant_completed'
|
|
17
|
+
* or 'error') arrives, streaming task.step events as each tool call
|
|
18
|
+
* or file_edit lands.
|
|
19
|
+
* 4. Emit task.completed with the accumulated tokens + outcome.
|
|
20
|
+
*
|
|
21
|
+
* Deviation-reporting: every file_edit event is checked against the
|
|
22
|
+
* configured skill rules (fetched from /api/v1/plan-skills on peer
|
|
23
|
+
* connect and cached per run). When a rule matches, we emit
|
|
24
|
+
* task.deviation — the peer forwards it to the server.
|
|
25
|
+
*/
|
|
26
|
+
import { promises as fs } from 'fs';
|
|
27
|
+
import { homedir, platform } from 'os';
|
|
28
|
+
import { join } from 'path';
|
|
29
|
+
import { recordWorkGraphEvent } from './workGraphOutbox';
|
|
30
|
+
export class OpenCodeDriver {
|
|
31
|
+
id = 'opencode';
|
|
32
|
+
portCache = null;
|
|
33
|
+
opts;
|
|
34
|
+
constructor(opts = {}) {
|
|
35
|
+
this.opts = opts;
|
|
36
|
+
}
|
|
37
|
+
async detect() {
|
|
38
|
+
try {
|
|
39
|
+
const port = await this.resolvePort();
|
|
40
|
+
this.portCache = port;
|
|
41
|
+
const res = await fetch(`http://127.0.0.1:${port}/status`, {
|
|
42
|
+
method: 'GET',
|
|
43
|
+
headers: { 'x-orgx-probe': 'detect' },
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
return {
|
|
47
|
+
installed: true,
|
|
48
|
+
authenticated: false,
|
|
49
|
+
error: `status ${res.status}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const body = (await res.json());
|
|
53
|
+
return {
|
|
54
|
+
installed: true,
|
|
55
|
+
authenticated: body.authed !== false,
|
|
56
|
+
version: body.version,
|
|
57
|
+
subscription_active: body.authed !== false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
return {
|
|
62
|
+
installed: false,
|
|
63
|
+
authenticated: false,
|
|
64
|
+
error: err instanceof Error ? err.message : String(err),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async probe() {
|
|
69
|
+
try {
|
|
70
|
+
const port = await this.resolvePort();
|
|
71
|
+
const res = await fetch(`http://127.0.0.1:${port}/status`, {
|
|
72
|
+
method: 'GET',
|
|
73
|
+
headers: { 'x-orgx-probe': 'liveness' },
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
return { subscription_active: false, session_alive: false };
|
|
77
|
+
}
|
|
78
|
+
const body = (await res.json());
|
|
79
|
+
return {
|
|
80
|
+
subscription_active: body.authed !== false,
|
|
81
|
+
session_alive: (body.session_count ?? 0) >= 0,
|
|
82
|
+
queue_depth: body.session_count ?? 0,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { subscription_active: false, session_alive: false };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async *dispatch(task, context) {
|
|
90
|
+
const port = this.portCache ?? (await this.resolvePort());
|
|
91
|
+
const sessionHandle = await this.createSession(port, {
|
|
92
|
+
title: task.title,
|
|
93
|
+
description: task.description,
|
|
94
|
+
repo_path: task.repo_path,
|
|
95
|
+
skill_ids: task.skill_ids,
|
|
96
|
+
idempotency_key: context.idempotency_key,
|
|
97
|
+
});
|
|
98
|
+
const startedAt = new Date().toISOString();
|
|
99
|
+
await this.recordWorkGraph('task_started', task, context, {
|
|
100
|
+
sessionHandle,
|
|
101
|
+
timestamp: startedAt,
|
|
102
|
+
task_title_chars: task.title.length,
|
|
103
|
+
description_chars: task.description?.length ?? 0,
|
|
104
|
+
skill_count: task.skill_ids?.length ?? 0,
|
|
105
|
+
});
|
|
106
|
+
yield {
|
|
107
|
+
kind: 'task.started',
|
|
108
|
+
run_id: context.run_id,
|
|
109
|
+
started_at: startedAt,
|
|
110
|
+
session_handle: sessionHandle,
|
|
111
|
+
};
|
|
112
|
+
const rules = await (this.opts.skillRules?.() ?? Promise.resolve([]));
|
|
113
|
+
const seenFingerprints = new Set();
|
|
114
|
+
let tokens = 0;
|
|
115
|
+
let firstResponseAt = null;
|
|
116
|
+
for await (const event of this.streamSessionEvents(port, sessionHandle)) {
|
|
117
|
+
if (!firstResponseAt && (event.kind === 'chat' || event.kind === 'tool_call')) {
|
|
118
|
+
firstResponseAt = new Date().toISOString();
|
|
119
|
+
}
|
|
120
|
+
if (event.kind === 'error') {
|
|
121
|
+
await this.recordWorkGraph('task_failed', task, context, {
|
|
122
|
+
sessionHandle,
|
|
123
|
+
recoverable: event.recoverable === true,
|
|
124
|
+
reason_chars: event.message.length,
|
|
125
|
+
});
|
|
126
|
+
yield {
|
|
127
|
+
kind: 'task.failed',
|
|
128
|
+
run_id: context.run_id,
|
|
129
|
+
reason: event.message,
|
|
130
|
+
recoverable: event.recoverable === true,
|
|
131
|
+
};
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (event.kind === 'assistant_completed') {
|
|
135
|
+
tokens = event.tokens_used;
|
|
136
|
+
await this.recordWorkGraph('task_completed', task, context, {
|
|
137
|
+
sessionHandle,
|
|
138
|
+
tokens_used: tokens,
|
|
139
|
+
first_response_seen: Boolean(firstResponseAt),
|
|
140
|
+
});
|
|
141
|
+
yield {
|
|
142
|
+
kind: 'task.completed',
|
|
143
|
+
run_id: context.run_id,
|
|
144
|
+
outcome_kind: 'shipped',
|
|
145
|
+
started_at: startedAt,
|
|
146
|
+
first_response_at: firstResponseAt ?? startedAt,
|
|
147
|
+
completed_at: new Date().toISOString(),
|
|
148
|
+
tokens_used: tokens,
|
|
149
|
+
provider: 'anthropic',
|
|
150
|
+
source_sub_type: 'subscription',
|
|
151
|
+
source_driver: 'opencode',
|
|
152
|
+
// OpenCode subscriptions don't expose a cost per token — we
|
|
153
|
+
// emit 0 so the server's BYOK aggregator records "zero
|
|
154
|
+
// server-side" and leaves saved_estimate_cents to a follow-up
|
|
155
|
+
// job that backfills from task_type_baselines.
|
|
156
|
+
cost_estimate_cents: 0,
|
|
157
|
+
};
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Narrate progress to the server + check deviation rules.
|
|
161
|
+
if (event.kind === 'file_edit' || event.kind === 'tool_call') {
|
|
162
|
+
const summary = event.kind === 'file_edit'
|
|
163
|
+
? `edit ${event.path} — ${event.summary}`
|
|
164
|
+
: `call ${event.tool} — ${event.summary}`;
|
|
165
|
+
await this.recordWorkGraph('task_step', task, context, {
|
|
166
|
+
sessionHandle,
|
|
167
|
+
step_kind: event.kind,
|
|
168
|
+
evidence_ref: event.kind === 'file_edit'
|
|
169
|
+
? event.diff_ref ?? event.path
|
|
170
|
+
: event.ref ?? event.tool,
|
|
171
|
+
summary_chars: summary.length,
|
|
172
|
+
});
|
|
173
|
+
yield {
|
|
174
|
+
kind: 'task.step',
|
|
175
|
+
run_id: context.run_id,
|
|
176
|
+
step: {
|
|
177
|
+
kind: event.kind,
|
|
178
|
+
summary,
|
|
179
|
+
evidence_ref: event.kind === 'file_edit'
|
|
180
|
+
? event.diff_ref
|
|
181
|
+
: event.ref,
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
for (const rule of rules) {
|
|
185
|
+
if (rule.match.on !== event.kind)
|
|
186
|
+
continue;
|
|
187
|
+
const text = event.kind === 'file_edit'
|
|
188
|
+
? `${event.path} ${event.summary}`
|
|
189
|
+
: `${event.tool} ${event.summary}`;
|
|
190
|
+
if (!new RegExp(rule.match.pattern).test(text))
|
|
191
|
+
continue;
|
|
192
|
+
const dedupe = `${rule.skill_id}:${rule.dedupe_fingerprint}:${context.run_id}`;
|
|
193
|
+
if (seenFingerprints.has(dedupe))
|
|
194
|
+
continue;
|
|
195
|
+
seenFingerprints.add(dedupe);
|
|
196
|
+
await this.recordWorkGraph('task_deviation', task, context, {
|
|
197
|
+
sessionHandle,
|
|
198
|
+
skill_id: rule.skill_id,
|
|
199
|
+
evidence_kind: rule.evidence_kind,
|
|
200
|
+
dedupe_key: dedupe,
|
|
201
|
+
});
|
|
202
|
+
yield {
|
|
203
|
+
kind: 'task.deviation',
|
|
204
|
+
run_id: context.run_id,
|
|
205
|
+
skill_id: rule.skill_id,
|
|
206
|
+
evidence_kind: rule.evidence_kind,
|
|
207
|
+
evidence_ref: event.kind === 'file_edit'
|
|
208
|
+
? event.diff_ref ?? event.path
|
|
209
|
+
: event.ref ?? event.tool,
|
|
210
|
+
dedupe_key: dedupe,
|
|
211
|
+
severity: 'warn',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async cancel(runId) {
|
|
218
|
+
const port = this.portCache ?? (await this.resolvePort());
|
|
219
|
+
await fetch(`http://127.0.0.1:${port}/sessions/cancel`, {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: { 'Content-Type': 'application/json' },
|
|
222
|
+
body: JSON.stringify({ correlation_run_id: runId }),
|
|
223
|
+
}).catch(() => undefined);
|
|
224
|
+
}
|
|
225
|
+
// ───── Internals ──────────────────────────────────────────────────────
|
|
226
|
+
async recordWorkGraph(event, task, context, summary) {
|
|
227
|
+
await recordWorkGraphEvent({
|
|
228
|
+
outboxPath: this.opts.workGraphOutboxPath,
|
|
229
|
+
sourceClient: this.opts.sourceClient ?? 'opencode',
|
|
230
|
+
event,
|
|
231
|
+
runId: context.run_id,
|
|
232
|
+
sessionHandle: typeof summary.sessionHandle === 'string'
|
|
233
|
+
? summary.sessionHandle
|
|
234
|
+
: undefined,
|
|
235
|
+
cwd: typeof task.repo_path === 'string' && task.repo_path.trim()
|
|
236
|
+
? task.repo_path
|
|
237
|
+
: undefined,
|
|
238
|
+
summary: {
|
|
239
|
+
...summary,
|
|
240
|
+
idempotency_key: context.idempotency_key,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
async resolvePort() {
|
|
245
|
+
if (this.portCache !== null)
|
|
246
|
+
return this.portCache;
|
|
247
|
+
const path = this.opts.statePath ?? defaultStatePath();
|
|
248
|
+
const raw = await fs.readFile(path, 'utf8');
|
|
249
|
+
const state = JSON.parse(raw);
|
|
250
|
+
if (!Number.isInteger(state.port) || state.port <= 0) {
|
|
251
|
+
throw new Error(`OpenCode state file missing port: ${path}`);
|
|
252
|
+
}
|
|
253
|
+
this.portCache = state.port;
|
|
254
|
+
return state.port;
|
|
255
|
+
}
|
|
256
|
+
async createSession(port, body) {
|
|
257
|
+
const res = await fetch(`http://127.0.0.1:${port}/sessions`, {
|
|
258
|
+
method: 'POST',
|
|
259
|
+
headers: {
|
|
260
|
+
'Content-Type': 'application/json',
|
|
261
|
+
'Idempotency-Key': body.idempotency_key,
|
|
262
|
+
},
|
|
263
|
+
body: JSON.stringify({
|
|
264
|
+
title: body.title,
|
|
265
|
+
initial_prompt: renderPrompt(body),
|
|
266
|
+
cwd: body.repo_path,
|
|
267
|
+
}),
|
|
268
|
+
});
|
|
269
|
+
if (!res.ok) {
|
|
270
|
+
throw new Error(`OpenCode /sessions ${res.status}`);
|
|
271
|
+
}
|
|
272
|
+
const json = (await res.json());
|
|
273
|
+
return json.session_id;
|
|
274
|
+
}
|
|
275
|
+
async *streamSessionEvents(port, sessionId) {
|
|
276
|
+
const url = `http://127.0.0.1:${port}/sessions/${sessionId}/events`;
|
|
277
|
+
const res = await fetch(url, { headers: { accept: 'application/x-ndjson' } });
|
|
278
|
+
if (!res.ok || !res.body) {
|
|
279
|
+
throw new Error(`OpenCode /events ${res.status}`);
|
|
280
|
+
}
|
|
281
|
+
const reader = res.body.getReader();
|
|
282
|
+
const decoder = new TextDecoder();
|
|
283
|
+
let buffer = '';
|
|
284
|
+
while (true) {
|
|
285
|
+
const { value, done } = await reader.read();
|
|
286
|
+
if (done)
|
|
287
|
+
return;
|
|
288
|
+
buffer += decoder.decode(value, { stream: true });
|
|
289
|
+
let nl;
|
|
290
|
+
while ((nl = buffer.indexOf('\n')) !== -1) {
|
|
291
|
+
const line = buffer.slice(0, nl).trim();
|
|
292
|
+
buffer = buffer.slice(nl + 1);
|
|
293
|
+
if (!line)
|
|
294
|
+
continue;
|
|
295
|
+
try {
|
|
296
|
+
yield JSON.parse(line);
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// skip malformed lines
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function defaultStatePath() {
|
|
306
|
+
if (platform() === 'win32') {
|
|
307
|
+
const base = process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming');
|
|
308
|
+
return join(base, 'opencode', 'state.json');
|
|
309
|
+
}
|
|
310
|
+
return join(homedir(), '.opencode', 'state.json');
|
|
311
|
+
}
|
|
312
|
+
function renderPrompt(body) {
|
|
313
|
+
const parts = [body.title];
|
|
314
|
+
if (body.description)
|
|
315
|
+
parts.push('\n\n', body.description);
|
|
316
|
+
if (body.skill_ids?.length) {
|
|
317
|
+
parts.push('\n\nSkills to honor:\n');
|
|
318
|
+
for (const id of body.skill_ids)
|
|
319
|
+
parts.push(` - ${id}\n`);
|
|
320
|
+
}
|
|
321
|
+
return parts.join('');
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=OpenCodeDriver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenCodeDriver.js","sourceRoot":"","sources":["../src/OpenCodeDriver.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;GAIG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Minimal CLI entrypoint so users can run:
|
|
4
|
+
*
|
|
5
|
+
* ORGX_API_KEY=oxk_... ORGX_WORKSPACE_ID=... orgx-opencode-plugin
|
|
6
|
+
*/
|
|
7
|
+
import { startPeer } from './peer.js';
|
|
8
|
+
async function main() {
|
|
9
|
+
const apiKey = process.env.ORGX_API_KEY;
|
|
10
|
+
const workspaceId = process.env.ORGX_WORKSPACE_ID;
|
|
11
|
+
const baseUrl = process.env.ORGX_BASE_URL ?? 'https://useorgx.com';
|
|
12
|
+
if (!apiKey || !workspaceId) {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.error('Missing ORGX_API_KEY and/or ORGX_WORKSPACE_ID. Export both and retry.');
|
|
15
|
+
process.exit(2);
|
|
16
|
+
}
|
|
17
|
+
const peer = await startPeer({ apiKey, workspaceId, baseUrl });
|
|
18
|
+
const shutdown = async () => {
|
|
19
|
+
await peer.stop();
|
|
20
|
+
process.exit(0);
|
|
21
|
+
};
|
|
22
|
+
process.on('SIGINT', shutdown);
|
|
23
|
+
process.on('SIGTERM', shutdown);
|
|
24
|
+
// eslint-disable-next-line no-console
|
|
25
|
+
console.log('[orgx-opencode-plugin] peer running — ctrl-c to stop. Dispatches will arrive when OrgX sends them.');
|
|
26
|
+
}
|
|
27
|
+
main().catch((err) => {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.error('[orgx-opencode-plugin] fatal', err);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACxC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAClD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,qBAAqB,CAAC;IAEnE,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,sCAAsC;QACtC,OAAO,CAAC,KAAK,CACX,uEAAuE,CACxE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;IAE/D,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChC,sCAAsC;IACtC,OAAO,CAAC,GAAG,CACT,oGAAoG,CACrG,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA8B,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC"}
|
package/dist/peer.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Peer runtime — wires OpenCodeDriver into PeerClient and ships the
|
|
3
|
+
* license heartbeat on boot + every 24h.
|
|
4
|
+
*
|
|
5
|
+
* Usage (CLI or programmatic):
|
|
6
|
+
*
|
|
7
|
+
* import { startPeer } from '@useorgx/orgx-opencode-plugin';
|
|
8
|
+
* await startPeer({
|
|
9
|
+
* apiKey: process.env.ORGX_API_KEY,
|
|
10
|
+
* workspaceId: process.env.ORGX_WORKSPACE_ID,
|
|
11
|
+
* baseUrl: 'wss://useorgx.com',
|
|
12
|
+
* });
|
|
13
|
+
*/
|
|
14
|
+
import { type Driver } from '@useorgx/orgx-gateway-sdk';
|
|
15
|
+
export type StartPeerOptions = {
|
|
16
|
+
apiKey: string;
|
|
17
|
+
workspaceId: string;
|
|
18
|
+
/** Default: https://useorgx.com */
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
/** Override for tests. */
|
|
21
|
+
driver?: Driver;
|
|
22
|
+
/** Skip the license heartbeat (tests). */
|
|
23
|
+
skipHeartbeat?: boolean;
|
|
24
|
+
/** Local Work Graph JSONL outbox path. Set false to disable. */
|
|
25
|
+
workGraphOutboxPath?: string | false;
|
|
26
|
+
};
|
|
27
|
+
export type StartedPeer = {
|
|
28
|
+
stop: () => Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
export declare function startPeer(opts: StartPeerOptions): Promise<StartedPeer>;
|
|
31
|
+
//# sourceMappingURL=peer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer.d.ts","sourceRoot":"","sources":["../src/peer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAOpE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,CAAC;AAEF,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAuD5E"}
|
package/dist/peer.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Peer runtime — wires OpenCodeDriver into PeerClient and ships the
|
|
3
|
+
* license heartbeat on boot + every 24h.
|
|
4
|
+
*
|
|
5
|
+
* Usage (CLI or programmatic):
|
|
6
|
+
*
|
|
7
|
+
* import { startPeer } from '@useorgx/orgx-opencode-plugin';
|
|
8
|
+
* await startPeer({
|
|
9
|
+
* apiKey: process.env.ORGX_API_KEY,
|
|
10
|
+
* workspaceId: process.env.ORGX_WORKSPACE_ID,
|
|
11
|
+
* baseUrl: 'wss://useorgx.com',
|
|
12
|
+
* });
|
|
13
|
+
*/
|
|
14
|
+
import { PeerClient } from '@useorgx/orgx-gateway-sdk';
|
|
15
|
+
import { readFile } from 'fs/promises';
|
|
16
|
+
import { resolve } from 'path';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
import { OpenCodeDriver } from './OpenCodeDriver';
|
|
19
|
+
export async function startPeer(opts) {
|
|
20
|
+
const baseUrl = opts.baseUrl ?? 'https://useorgx.com';
|
|
21
|
+
const driver = opts.driver ??
|
|
22
|
+
new OpenCodeDriver({
|
|
23
|
+
skillRules: async () => fetchSkillRules(baseUrl, opts),
|
|
24
|
+
workGraphOutboxPath: opts.workGraphOutboxPath,
|
|
25
|
+
});
|
|
26
|
+
const manifest = await loadManifest();
|
|
27
|
+
const client = new PeerClient({
|
|
28
|
+
baseUrl: httpsToWss(baseUrl),
|
|
29
|
+
apiKey: opts.apiKey,
|
|
30
|
+
workspaceId: opts.workspaceId,
|
|
31
|
+
pluginId: '@useorgx/orgx-opencode-plugin',
|
|
32
|
+
drivers: [driver],
|
|
33
|
+
onOpen: () => {
|
|
34
|
+
// eslint-disable-next-line no-console
|
|
35
|
+
console.log('[orgx-opencode-plugin] connected');
|
|
36
|
+
},
|
|
37
|
+
onClose: (code, reason) => {
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.warn('[orgx-opencode-plugin] closed', { code, reason });
|
|
40
|
+
},
|
|
41
|
+
onError: (err) => {
|
|
42
|
+
// eslint-disable-next-line no-console
|
|
43
|
+
console.error('[orgx-opencode-plugin] error', err);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
client.connect();
|
|
47
|
+
let heartbeatTimer = null;
|
|
48
|
+
if (!opts.skipHeartbeat) {
|
|
49
|
+
await postHeartbeat(baseUrl, opts, manifest).catch((err) => {
|
|
50
|
+
// eslint-disable-next-line no-console
|
|
51
|
+
console.warn('[orgx-opencode-plugin] initial heartbeat failed', err);
|
|
52
|
+
});
|
|
53
|
+
heartbeatTimer = setInterval(() => {
|
|
54
|
+
void postHeartbeat(baseUrl, opts, manifest).catch((err) => {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.warn('[orgx-opencode-plugin] weekly heartbeat failed', err);
|
|
57
|
+
});
|
|
58
|
+
}, 7 * 24 * 60 * 60 * 1000);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
stop: async () => {
|
|
62
|
+
if (heartbeatTimer)
|
|
63
|
+
clearInterval(heartbeatTimer);
|
|
64
|
+
client.disconnect();
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async function loadManifest() {
|
|
69
|
+
// Manifest lives at package root. Resolved via import.meta.url so ESM + CJS agree.
|
|
70
|
+
const here = fileURLToPath(import.meta.url);
|
|
71
|
+
const manifestPath = resolve(here, '../..', 'plugin.manifest.json');
|
|
72
|
+
try {
|
|
73
|
+
const raw = await readFile(manifestPath, 'utf8');
|
|
74
|
+
return JSON.parse(raw);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// In dev, bail to a placeholder so the peer can still start. The
|
|
78
|
+
// server will mark the license 'degraded' in permissive mode.
|
|
79
|
+
return {
|
|
80
|
+
plugin_name: '@useorgx/orgx-opencode-plugin',
|
|
81
|
+
version: '0.0.0-dev',
|
|
82
|
+
manifest_fingerprint: 'dev-placeholder',
|
|
83
|
+
signature: '',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function postHeartbeat(baseUrl, opts, manifest) {
|
|
88
|
+
const r = await fetch(`${baseUrl.replace(/\/$/, '')}/api/v1/licenses/heartbeat`, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: {
|
|
91
|
+
'Content-Type': 'application/json',
|
|
92
|
+
Authorization: `Bearer ${opts.apiKey}`,
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify({
|
|
95
|
+
workspace_id: opts.workspaceId,
|
|
96
|
+
plugin_name: manifest.plugin_name,
|
|
97
|
+
version: manifest.version,
|
|
98
|
+
manifest_fingerprint: manifest.manifest_fingerprint,
|
|
99
|
+
signature: manifest.signature,
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
if (!r.ok) {
|
|
103
|
+
throw new Error(`heartbeat ${r.status}: ${await r.text().catch(() => '')}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function fetchSkillRules(baseUrl, opts) {
|
|
107
|
+
try {
|
|
108
|
+
const r = await fetch(`${baseUrl.replace(/\/$/, '')}/api/v1/plan-skills?workspace_id=${encodeURIComponent(opts.workspaceId)}`, {
|
|
109
|
+
method: 'GET',
|
|
110
|
+
headers: { Authorization: `Bearer ${opts.apiKey}` },
|
|
111
|
+
});
|
|
112
|
+
if (!r.ok)
|
|
113
|
+
return [];
|
|
114
|
+
const body = (await r.json());
|
|
115
|
+
const rules = [];
|
|
116
|
+
for (const skill of body.skills ?? []) {
|
|
117
|
+
for (const rule of skill.rules ?? []) {
|
|
118
|
+
rules.push({
|
|
119
|
+
skill_id: skill.id,
|
|
120
|
+
match: { pattern: rule.pattern, on: rule.on },
|
|
121
|
+
dedupe_fingerprint: rule.dedupe_fingerprint,
|
|
122
|
+
evidence_kind: rule.evidence_kind,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return rules;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function httpsToWss(url) {
|
|
133
|
+
if (url.startsWith('https://'))
|
|
134
|
+
return 'wss://' + url.slice('https://'.length);
|
|
135
|
+
if (url.startsWith('http://'))
|
|
136
|
+
return 'ws://' + url.slice('http://'.length);
|
|
137
|
+
return url;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=peer.js.map
|
package/dist/peer.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer.js","sourceRoot":"","sources":["../src/peer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type WorkGraphEventInput = {
|
|
2
|
+
outboxPath?: string | false;
|
|
3
|
+
sourceClient?: string;
|
|
4
|
+
event: string;
|
|
5
|
+
runId?: string;
|
|
6
|
+
sessionHandle?: string;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
timestamp?: string;
|
|
9
|
+
summary?: Record<string, unknown>;
|
|
10
|
+
env?: NodeJS.ProcessEnv;
|
|
11
|
+
};
|
|
12
|
+
export declare function resolveWorkGraphOutboxPath(outboxPath?: string | false, env?: NodeJS.ProcessEnv): string | null;
|
|
13
|
+
export declare function buildWorkGraphEventRecord({ sourceClient, event, runId, sessionHandle, cwd, timestamp, summary, }: WorkGraphEventInput): Record<string, unknown>;
|
|
14
|
+
export declare function recordWorkGraphEvent(input: WorkGraphEventInput): Promise<boolean>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=workGraphOutbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workGraphOutbox.d.ts","sourceRoot":"","sources":["../src/workGraphOutbox.ts"],"names":[],"mappings":"AAIA,KAAK,mBAAmB,GAAG;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,CAAC;AAEF,wBAAgB,0BAA0B,CACxC,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,EAC3B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,IAAI,CAOf;AAED,wBAAgB,yBAAyB,CAAC,EACxC,YAAyB,EACzB,KAAK,EACL,KAAK,EACL,aAAa,EACb,GAAG,EACH,SAAoC,EACpC,OAAY,GACb,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAY/C;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,OAAO,CAAC,CAkBlB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { appendFile, mkdir } from 'fs/promises';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
export function resolveWorkGraphOutboxPath(outboxPath, env = process.env) {
|
|
5
|
+
if (outboxPath === false)
|
|
6
|
+
return null;
|
|
7
|
+
const explicit = typeof outboxPath === 'string' ? outboxPath.trim() : '';
|
|
8
|
+
if (explicit)
|
|
9
|
+
return explicit;
|
|
10
|
+
const envPath = env.ORGX_WIZARD_HOOK_OUTBOX?.trim();
|
|
11
|
+
if (envPath)
|
|
12
|
+
return envPath;
|
|
13
|
+
return join(homedir(), '.config', 'useorgx', 'wizard', 'hooks', 'events.jsonl');
|
|
14
|
+
}
|
|
15
|
+
export function buildWorkGraphEventRecord({ sourceClient = 'opencode', event, runId, sessionHandle, cwd, timestamp = new Date().toISOString(), summary = {}, }) {
|
|
16
|
+
return {
|
|
17
|
+
schema_version: '2026-05-07',
|
|
18
|
+
source: 'orgx_opencode_plugin_runtime_hook',
|
|
19
|
+
source_client: sourceClient,
|
|
20
|
+
event,
|
|
21
|
+
session_id: runId,
|
|
22
|
+
turn_id: sessionHandle,
|
|
23
|
+
cwd,
|
|
24
|
+
timestamp,
|
|
25
|
+
summary,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export async function recordWorkGraphEvent(input) {
|
|
29
|
+
const outbox = resolveWorkGraphOutboxPath(input.outboxPath, input.env);
|
|
30
|
+
if (!outbox)
|
|
31
|
+
return false;
|
|
32
|
+
try {
|
|
33
|
+
await mkdir(dirname(outbox), { recursive: true, mode: 0o700 });
|
|
34
|
+
await appendFile(outbox, `${JSON.stringify(buildWorkGraphEventRecord(input))}\n`, {
|
|
35
|
+
encoding: 'utf8',
|
|
36
|
+
mode: 0o600,
|
|
37
|
+
});
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=workGraphOutbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workGraphOutbox.js","sourceRoot":"","sources":["../src/workGraphOutbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAcrC,MAAM,UAAU,0BAA0B,CACxC,UAA2B,EAC3B,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,UAAU,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,QAAQ,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,OAAO,GAAG,GAAG,CAAC,uBAAuB,EAAE,IAAI,EAAE,CAAC;IACpD,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,EACxC,YAAY,GAAG,UAAU,EACzB,KAAK,EACL,KAAK,EACL,aAAa,EACb,GAAG,EACH,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EACpC,OAAO,GAAG,EAAE,GACQ;IACpB,OAAO;QACL,cAAc,EAAE,YAAY;QAC5B,MAAM,EAAE,mCAAmC;QAC3C,aAAa,EAAE,YAAY;QAC3B,KAAK;QACL,UAAU,EAAE,KAAK;QACjB,OAAO,EAAE,aAAa;QACtB,GAAG;QACH,SAAS;QACT,OAAO;KACR,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,KAA0B;IAE1B,MAAM,MAAM,GAAG,0BAA0B,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACvE,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE1B,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/D,MAAM,UAAU,CACd,MAAM,EACN,GAAG,IAAI,CAAC,SAAS,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC,IAAI,EACvD;YACE,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,KAAK;SACZ,CACF,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@useorgx/orgx-opencode-plugin",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "OrgX plugin peer for OpenCode with task dispatch, execution receipts, deviations, and passive Work Graph reconciliation events.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"orgx-opencode-plugin": "dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": ["dist", "README.md", "plugin.manifest.json"],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"type-check": "tsc --noEmit",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"start": "node dist/cli.js"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@useorgx/orgx-gateway-sdk": "https://github.com/useorgx/orgx-gateway-sdk#main"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^22.0.0",
|
|
30
|
+
"typescript": "^5.4.0",
|
|
31
|
+
"vitest": "^2.0.0"
|
|
32
|
+
},
|
|
33
|
+
"keywords": ["orgx", "opencode", "plugin-peer", "byok"],
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/useorgx/orgx-opencode-plugin.git"
|
|
37
|
+
},
|
|
38
|
+
"license": "UNLICENSED"
|
|
39
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"plugin_name": "@useorgx/orgx-opencode-plugin",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"manifest_fingerprint": "",
|
|
5
|
+
"signature": "",
|
|
6
|
+
"capabilities": ["gateway:drive", "plugin:heartbeat"],
|
|
7
|
+
"driver_ids": ["opencode"],
|
|
8
|
+
"notes": "fingerprint + signature are populated by the npm prepublish step; the dev build ships unsigned and gets 'degraded' status from the server."
|
|
9
|
+
}
|