@phux/opencode 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +52 -3
- package/dist/index.js +451 -97
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @phux/opencode
|
|
2
2
|
|
|
3
|
-
OpenCode
|
|
3
|
+
OpenCode plugin for operating shared terminals through an external local
|
|
4
4
|
phux server and appending cache-preserving fleet context. Installation,
|
|
5
5
|
configuration, tool behavior, target precedence,
|
|
6
6
|
lifecycle gaps, and safety boundaries live in the canonical
|
|
@@ -17,7 +17,7 @@ npm run gates
|
|
|
17
17
|
npm run smoke:opencode
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
`smoke:opencode` starts an isolated OpenCode
|
|
20
|
+
`smoke:opencode` starts an isolated OpenCode server and verifies that its
|
|
21
21
|
resolved config accepts the built plugin URL. Plugin setup, registration,
|
|
22
22
|
cleanup, and tool behavior are covered by `npm run gates`. The smoke requires
|
|
23
23
|
an `opencode2` executable; set `OPENCODE_BIN` to choose it.
|
package/dist/index.d.ts
CHANGED
|
@@ -224,6 +224,30 @@ interface AgentRecord {
|
|
|
224
224
|
readonly attention?: AgentAttention;
|
|
225
225
|
readonly session: string;
|
|
226
226
|
}
|
|
227
|
+
/** Closed `AgentEventsJsonlV1` record types (`docs/spec/L1.md`). */
|
|
228
|
+
declare const AGENT_EVENT_TYPES: readonly ["session_start", "prompt", "tool_start", "tool_end", "notification", "ask", "stop", "session_end", "state", "provider_raw"];
|
|
229
|
+
type AgentEventType = (typeof AGENT_EVENT_TYPES)[number];
|
|
230
|
+
/** `phux agent session open --json` document. */
|
|
231
|
+
interface AgentSessionOpenResult {
|
|
232
|
+
readonly schema_version: 1;
|
|
233
|
+
readonly resource: string;
|
|
234
|
+
readonly parent: string;
|
|
235
|
+
readonly provider: string;
|
|
236
|
+
readonly native_id: string | null;
|
|
237
|
+
}
|
|
238
|
+
/** `phux agent emit --json` stamped header. */
|
|
239
|
+
interface AgentEmitResult {
|
|
240
|
+
readonly schema_version: 1;
|
|
241
|
+
readonly resource: string;
|
|
242
|
+
readonly seq: number;
|
|
243
|
+
readonly ts_ms: number;
|
|
244
|
+
readonly type: AgentEventType;
|
|
245
|
+
}
|
|
246
|
+
/** Projection of `phux agent session close`'s `@N\\tclosed` line. */
|
|
247
|
+
interface AgentSessionCloseResult {
|
|
248
|
+
readonly resource: string;
|
|
249
|
+
readonly closed: true;
|
|
250
|
+
}
|
|
227
251
|
|
|
228
252
|
interface PhuxCliOptions {
|
|
229
253
|
readonly executable?: string;
|
|
@@ -271,6 +295,13 @@ interface RunOptions extends ExecutionOptions {
|
|
|
271
295
|
interface AgentTargetOptions extends ExecutionOptions {
|
|
272
296
|
readonly target: string;
|
|
273
297
|
}
|
|
298
|
+
interface AgentSessionOpenOptions extends ExecutionOptions {
|
|
299
|
+
readonly provider: string;
|
|
300
|
+
readonly nativeId?: string;
|
|
301
|
+
}
|
|
302
|
+
interface AgentEmitOptions extends ExecutionOptions {
|
|
303
|
+
readonly data?: Readonly<Record<string, unknown>>;
|
|
304
|
+
}
|
|
274
305
|
type SplitDirection = "horizontal" | "vertical";
|
|
275
306
|
interface PlacementOptions {
|
|
276
307
|
readonly target?: string;
|
|
@@ -344,6 +375,12 @@ declare class PhuxCli {
|
|
|
344
375
|
agentSet(target: string, record: AgentRecord, options?: ExecutionOptions): Promise<AgentRecord>;
|
|
345
376
|
/** Clear a declaration and require the CLI's confirmed tombstone response. */
|
|
346
377
|
agentClear(target: string, options?: ExecutionOptions): Promise<void>;
|
|
378
|
+
/** Open an AgentSession bound to a pane; the caller becomes its producer. */
|
|
379
|
+
agentSessionOpen(target: string, options: AgentSessionOpenOptions): Promise<AgentSessionOpenResult>;
|
|
380
|
+
/** Append one closed-type record. No-op-refused by the server if this client is not the opener. */
|
|
381
|
+
agentEmit(target: string, type: AgentEventType, options?: AgentEmitOptions): Promise<AgentEmitResult>;
|
|
382
|
+
/** Close a pane's AgentSession; the parent pane is untouched. */
|
|
383
|
+
agentSessionClose(target: string, options?: ExecutionOptions): Promise<AgentSessionCloseResult>;
|
|
347
384
|
renderedSnapshot(options: RenderedSnapshotOptions): Promise<RenderedFrame>;
|
|
348
385
|
snapshot(options?: SnapshotOptions): Promise<ScreenState>;
|
|
349
386
|
wait(options?: WaitOptions): Promise<WaitOutcome>;
|
|
@@ -470,6 +507,9 @@ interface OpenCodeLifecycleAdapter {
|
|
|
470
507
|
}): Promise<AgentStateList>;
|
|
471
508
|
agentSet(target: string, record: AgentRecord, options?: ExecutionOptions): Promise<AgentRecord>;
|
|
472
509
|
agentClear(target: string, options?: ExecutionOptions): Promise<void>;
|
|
510
|
+
agentSessionOpen?(target: string, options: AgentSessionOpenOptions): Promise<AgentSessionOpenResult>;
|
|
511
|
+
agentEmit?(target: string, type: AgentEventType, options?: AgentEmitOptions): Promise<AgentEmitResult>;
|
|
512
|
+
agentSessionClose?(target: string, options?: ExecutionOptions): Promise<AgentSessionCloseResult>;
|
|
473
513
|
}
|
|
474
514
|
interface OpenCodeLifecycleOptions {
|
|
475
515
|
readonly cli?: OpenCodeLifecycleAdapter;
|
|
@@ -488,6 +528,8 @@ declare class OpenCodeLifecycle {
|
|
|
488
528
|
private readonly target;
|
|
489
529
|
private readonly states;
|
|
490
530
|
private readonly owned;
|
|
531
|
+
private readonly sessions;
|
|
532
|
+
private readonly openedPanes;
|
|
491
533
|
private tail;
|
|
492
534
|
private disposed;
|
|
493
535
|
constructor(options: OpenCodeLifecycleOptions);
|
|
@@ -502,6 +544,9 @@ declare class OpenCodeLifecycle {
|
|
|
502
544
|
observeState(sessionId: string, state: OpenCodeLifecycleState): Promise<void>;
|
|
503
545
|
/** A tool invocation is an honest working signal if no status event was seen yet. */
|
|
504
546
|
targetSelected(sessionId: string): Promise<void>;
|
|
547
|
+
ask(sessionId: string, data?: Readonly<Record<string, unknown>>): Promise<void>;
|
|
548
|
+
toolStart(sessionId: string, toolName: string, toolUseId?: string): Promise<void>;
|
|
549
|
+
toolEnd(sessionId: string, toolName: string, toolUseId?: string): Promise<void>;
|
|
505
550
|
deleteSession(sessionId: string): Promise<void>;
|
|
506
551
|
dispose(): Promise<void>;
|
|
507
552
|
settled(): Promise<void>;
|
|
@@ -510,6 +555,10 @@ declare class OpenCodeLifecycle {
|
|
|
510
555
|
private clearSession;
|
|
511
556
|
private clearOwned;
|
|
512
557
|
private execution;
|
|
558
|
+
private emitter;
|
|
559
|
+
private bindSession;
|
|
560
|
+
private emit;
|
|
561
|
+
private finishSession;
|
|
513
562
|
}
|
|
514
563
|
interface OpenCodeLifecycleEvent {
|
|
515
564
|
readonly type: string;
|
|
@@ -528,8 +577,8 @@ interface PhuxOpenCodeOptions {
|
|
|
528
577
|
readonly env?: NodeJS.ProcessEnv;
|
|
529
578
|
readonly onLifecycleError?: (error: unknown) => void;
|
|
530
579
|
}
|
|
531
|
-
/** Build
|
|
532
|
-
declare function createPhuxPlugin(defaults?: PhuxOpenCodeOptions): Plugin
|
|
533
|
-
declare const PhuxPlugin: Plugin
|
|
580
|
+
/** Build an OpenCode plugin with optional test-only defaults. */
|
|
581
|
+
declare function createPhuxPlugin(defaults?: PhuxOpenCodeOptions): Plugin;
|
|
582
|
+
declare const PhuxPlugin: Plugin;
|
|
534
583
|
|
|
535
584
|
export { type AgentTargetOptions, type CreateOptions, DEFAULT_SHORT_TIMEOUT_MS, type ExecutionOptions, MAX_MODEL_BYTES, MAX_MODEL_LINES, OpenCodeLifecycle, type OpenCodeLifecycleAdapter, type OpenCodeLifecycleEvent, type OpenCodeLifecycleOptions, type OpenCodeLifecycleState, PhuxCli, type PhuxCliOptions, PhuxContextAwareness, type PhuxContextAwarenessOptions, type PhuxContextEmission, type PhuxContextIdentity, type PhuxOpenCodeOptions, PhuxPlugin, type PhuxProbe, type PhuxToolDefinition, type PhuxToolMetadata, type PhuxToolRuntime, type RunOptions, type SnapshotOptions, type ToolContext, type ToolResult, type WaitOptions, type WaitOutcome, boundedResult, contextAwarenessEnabled, createPhuxPlugin, createPhuxTools, PhuxPlugin as default, handleLifecycleEvent, normalizeTerminalIdentity, resolveTarget };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
tool
|
|
4
|
+
} from "@opencode-ai/plugin";
|
|
3
5
|
|
|
4
|
-
// ../
|
|
6
|
+
// ../runtime/src/errors.ts
|
|
5
7
|
var PhuxError = class extends Error {
|
|
6
8
|
code;
|
|
7
9
|
argv;
|
|
@@ -17,7 +19,7 @@ var PhuxError = class extends Error {
|
|
|
17
19
|
}
|
|
18
20
|
};
|
|
19
21
|
|
|
20
|
-
// ../
|
|
22
|
+
// ../runtime/src/runner.ts
|
|
21
23
|
import { spawn } from "child_process";
|
|
22
24
|
var DEFAULT_MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
23
25
|
var nodeProcessRunner = (request) => new Promise((resolve, reject) => {
|
|
@@ -141,7 +143,19 @@ function isNoSuchProcess(error) {
|
|
|
141
143
|
return error instanceof Error && "code" in error && error.code === "ESRCH";
|
|
142
144
|
}
|
|
143
145
|
|
|
144
|
-
// ../
|
|
146
|
+
// ../runtime/src/schemas.ts
|
|
147
|
+
var AGENT_EVENT_TYPES = [
|
|
148
|
+
"session_start",
|
|
149
|
+
"prompt",
|
|
150
|
+
"tool_start",
|
|
151
|
+
"tool_end",
|
|
152
|
+
"notification",
|
|
153
|
+
"ask",
|
|
154
|
+
"stop",
|
|
155
|
+
"session_end",
|
|
156
|
+
"state",
|
|
157
|
+
"provider_raw"
|
|
158
|
+
];
|
|
145
159
|
var SchemaValidationError = class extends Error {
|
|
146
160
|
constructor(path, expectation) {
|
|
147
161
|
super(`${path} must be ${expectation}`);
|
|
@@ -555,8 +569,49 @@ function parseAgentStateList(value) {
|
|
|
555
569
|
});
|
|
556
570
|
return { schema_version: 1, agents };
|
|
557
571
|
}
|
|
572
|
+
function isAgentEventType(value) {
|
|
573
|
+
return AGENT_EVENT_TYPES.includes(value);
|
|
574
|
+
}
|
|
575
|
+
function parseAgentSessionOpenResult(value) {
|
|
576
|
+
const root = record(value, "$ (phux agent session open --json CLI shape)");
|
|
577
|
+
if (root.schema_version !== 1) {
|
|
578
|
+
throw new SchemaValidationError("$.schema_version", "the supported value 1");
|
|
579
|
+
}
|
|
580
|
+
const resource = string(root.resource, "$.resource");
|
|
581
|
+
const parent = string(root.parent, "$.parent");
|
|
582
|
+
const provider = string(root.provider, "$.provider");
|
|
583
|
+
if (resource.trim().length === 0) throw new SchemaValidationError("$.resource", "non-empty");
|
|
584
|
+
if (parent.trim().length === 0) throw new SchemaValidationError("$.parent", "non-empty");
|
|
585
|
+
if (provider.trim().length === 0) throw new SchemaValidationError("$.provider", "non-empty");
|
|
586
|
+
return {
|
|
587
|
+
schema_version: 1,
|
|
588
|
+
resource,
|
|
589
|
+
parent,
|
|
590
|
+
provider,
|
|
591
|
+
native_id: root.native_id === null || root.native_id === void 0 ? null : string(root.native_id, "$.native_id")
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
function parseAgentEmitResult(value) {
|
|
595
|
+
const root = record(value, "$ (phux agent emit --json CLI shape)");
|
|
596
|
+
if (root.schema_version !== 1) {
|
|
597
|
+
throw new SchemaValidationError("$.schema_version", "the supported value 1");
|
|
598
|
+
}
|
|
599
|
+
const resource = string(root.resource, "$.resource");
|
|
600
|
+
if (resource.trim().length === 0) throw new SchemaValidationError("$.resource", "non-empty");
|
|
601
|
+
const type = string(root.type, "$.type");
|
|
602
|
+
if (!isAgentEventType(type)) {
|
|
603
|
+
throw new SchemaValidationError("$.type", AGENT_EVENT_TYPES.map((item) => JSON.stringify(item)).join(", "));
|
|
604
|
+
}
|
|
605
|
+
return {
|
|
606
|
+
schema_version: 1,
|
|
607
|
+
resource,
|
|
608
|
+
seq: integer(root.seq, "$.seq", 1),
|
|
609
|
+
ts_ms: integer(root.ts_ms, "$.ts_ms", 0),
|
|
610
|
+
type
|
|
611
|
+
};
|
|
612
|
+
}
|
|
558
613
|
|
|
559
|
-
// ../
|
|
614
|
+
// ../runtime/src/adapter.ts
|
|
560
615
|
var MINIMUM_PHUX_VERSION = "0.1.0";
|
|
561
616
|
var VERSION_PATTERN = /^phux\s+v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/;
|
|
562
617
|
var PhuxCli = class {
|
|
@@ -723,6 +778,44 @@ var PhuxCli = class {
|
|
|
723
778
|
throw invalidResponse("agent clear", this.executable, args, "expected @N\\t- confirmation");
|
|
724
779
|
}
|
|
725
780
|
}
|
|
781
|
+
/** Open an AgentSession bound to a pane; the caller becomes its producer. */
|
|
782
|
+
async agentSessionOpen(target, options) {
|
|
783
|
+
if (target.trim().length === 0) throw new TypeError("target must be non-empty");
|
|
784
|
+
if (options.provider.trim().length === 0) throw new TypeError("provider must be non-empty");
|
|
785
|
+
const args = ["agent", "session", "open", target, "--provider", options.provider];
|
|
786
|
+
if (options.nativeId !== void 0) {
|
|
787
|
+
if (options.nativeId.trim().length === 0) throw new TypeError("nativeId must be non-empty");
|
|
788
|
+
args.push("--native-id", options.nativeId);
|
|
789
|
+
}
|
|
790
|
+
args.push("--json");
|
|
791
|
+
this.pushSocket(args);
|
|
792
|
+
return this.jsonCommand("agent session open", args, options, parseAgentSessionOpenResult);
|
|
793
|
+
}
|
|
794
|
+
/** Append one closed-type record. No-op-refused by the server if this client is not the opener. */
|
|
795
|
+
async agentEmit(target, type, options = {}) {
|
|
796
|
+
if (target.trim().length === 0) throw new TypeError("target must be non-empty");
|
|
797
|
+
if (!isAgentEventType(type)) {
|
|
798
|
+
throw new TypeError(`type must be one of ${AGENT_EVENT_TYPES.join(", ")}`);
|
|
799
|
+
}
|
|
800
|
+
const args = ["agent", "emit", target, "--type", type];
|
|
801
|
+
if (options.data !== void 0) {
|
|
802
|
+
if (typeof options.data !== "object" || Array.isArray(options.data)) {
|
|
803
|
+
throw new TypeError("data must be a JSON object");
|
|
804
|
+
}
|
|
805
|
+
args.push("--data", JSON.stringify(options.data));
|
|
806
|
+
}
|
|
807
|
+
args.push("--json");
|
|
808
|
+
this.pushSocket(args);
|
|
809
|
+
return this.jsonCommand("agent emit", args, options, parseAgentEmitResult);
|
|
810
|
+
}
|
|
811
|
+
/** Close a pane's AgentSession; the parent pane is untouched. */
|
|
812
|
+
async agentSessionClose(target, options = {}) {
|
|
813
|
+
if (target.trim().length === 0) throw new TypeError("target must be non-empty");
|
|
814
|
+
const args = ["agent", "session", "close", target];
|
|
815
|
+
this.pushSocket(args);
|
|
816
|
+
const result = await this.completed("agent session close", args, options, false);
|
|
817
|
+
return parseSessionClosed("agent session close", this.executable, result.stdout, args);
|
|
818
|
+
}
|
|
726
819
|
async renderedSnapshot(options) {
|
|
727
820
|
requirePositiveInteger(options.cols, "cols");
|
|
728
821
|
requirePositiveInteger(options.rows, "rows");
|
|
@@ -942,6 +1035,15 @@ function parseAgentConfirmation(verb, executable, stdout, args) {
|
|
|
942
1035
|
}
|
|
943
1036
|
return parseJson(verb, executable, line.slice(tab + 1), args, parseAgentRecord);
|
|
944
1037
|
}
|
|
1038
|
+
function parseSessionClosed(verb, executable, stdout, args) {
|
|
1039
|
+
const line = stdout.trim();
|
|
1040
|
+
const tab = line.indexOf(" ");
|
|
1041
|
+
const resource = tab < 0 ? "" : line.slice(0, tab);
|
|
1042
|
+
if (resource.length === 0 || line.slice(tab + 1) !== "closed") {
|
|
1043
|
+
throw invalidResponse(verb, executable, args, "expected @N\\tclosed confirmation");
|
|
1044
|
+
}
|
|
1045
|
+
return { resource, closed: true };
|
|
1046
|
+
}
|
|
945
1047
|
function parseWatchLines(stdout, executable, args) {
|
|
946
1048
|
const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
|
|
947
1049
|
return lines.map((line, index) => {
|
|
@@ -1071,8 +1173,117 @@ function requirePositiveInteger(value, name) {
|
|
|
1071
1173
|
throw new RangeError(`${name} must be a positive safe integer`);
|
|
1072
1174
|
}
|
|
1073
1175
|
}
|
|
1176
|
+
function hasAgentSessionCli(cli) {
|
|
1177
|
+
const candidate = cli;
|
|
1178
|
+
return typeof candidate.agentSessionOpen === "function" && typeof candidate.agentEmit === "function" && typeof candidate.agentSessionClose === "function";
|
|
1179
|
+
}
|
|
1180
|
+
function isAgentSessionUnsupported(error) {
|
|
1181
|
+
if (!(error instanceof PhuxError) || error.code !== "command_failed") return false;
|
|
1182
|
+
const haystack = `${error.message}
|
|
1183
|
+
${error.stderr ?? ""}`;
|
|
1184
|
+
return /unsupported_server|unrecognized subcommand|invalid subcommand|unknown command|not a valid command/i.test(haystack);
|
|
1185
|
+
}
|
|
1186
|
+
var AgentSessionEmitter = class {
|
|
1187
|
+
cli;
|
|
1188
|
+
provider;
|
|
1189
|
+
onError;
|
|
1190
|
+
target = null;
|
|
1191
|
+
nativeId = null;
|
|
1192
|
+
opened = false;
|
|
1193
|
+
unavailable = false;
|
|
1194
|
+
constructor(cli, options) {
|
|
1195
|
+
this.cli = cli !== null && hasAgentSessionCli(cli) ? cli : null;
|
|
1196
|
+
this.provider = options.provider;
|
|
1197
|
+
this.onError = options.onError ?? (() => {
|
|
1198
|
+
});
|
|
1199
|
+
if (this.cli === null) this.unavailable = true;
|
|
1200
|
+
if (this.provider.trim().length === 0) throw new TypeError("provider must be non-empty");
|
|
1201
|
+
}
|
|
1202
|
+
get isOpen() {
|
|
1203
|
+
return this.opened;
|
|
1204
|
+
}
|
|
1205
|
+
get isUnavailable() {
|
|
1206
|
+
return this.unavailable;
|
|
1207
|
+
}
|
|
1208
|
+
/** Take over a session this process left open (extension reload). */
|
|
1209
|
+
adopt(target) {
|
|
1210
|
+
this.target = target;
|
|
1211
|
+
this.opened = target !== null;
|
|
1212
|
+
this.nativeId = null;
|
|
1213
|
+
}
|
|
1214
|
+
async bind(target, nativeId, options = {}) {
|
|
1215
|
+
if (this.unavailable) return;
|
|
1216
|
+
if (target === this.target && this.opened && this.nativeId === nativeId) return;
|
|
1217
|
+
if (this.opened && (this.target !== target || this.nativeId !== nativeId)) {
|
|
1218
|
+
await this.finish(options);
|
|
1219
|
+
}
|
|
1220
|
+
if (target === null) return;
|
|
1221
|
+
await this.open(target, nativeId, options);
|
|
1222
|
+
}
|
|
1223
|
+
async emit(type, data, options = {}) {
|
|
1224
|
+
if (this.unavailable || !this.opened || this.target === null || this.cli === null) return;
|
|
1225
|
+
try {
|
|
1226
|
+
await this.cli.agentEmit(this.target, type, {
|
|
1227
|
+
...options,
|
|
1228
|
+
...data === void 0 ? {} : { data }
|
|
1229
|
+
});
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
this.onError(error);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
async finish(options = {}) {
|
|
1235
|
+
if (this.unavailable || !this.opened || this.target === null || this.cli === null) {
|
|
1236
|
+
this.opened = false;
|
|
1237
|
+
this.target = null;
|
|
1238
|
+
this.nativeId = null;
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
const target = this.target;
|
|
1242
|
+
this.opened = false;
|
|
1243
|
+
this.target = null;
|
|
1244
|
+
this.nativeId = null;
|
|
1245
|
+
try {
|
|
1246
|
+
await this.cli.agentEmit(target, "session_end", options);
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
this.onError(error);
|
|
1249
|
+
}
|
|
1250
|
+
try {
|
|
1251
|
+
await this.cli.agentSessionClose(target, options);
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
this.onError(error);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
async open(target, nativeId, options) {
|
|
1257
|
+
if (this.cli === null) return;
|
|
1258
|
+
try {
|
|
1259
|
+
await this.cli.agentSessionOpen(target, {
|
|
1260
|
+
provider: this.provider,
|
|
1261
|
+
nativeId,
|
|
1262
|
+
...options
|
|
1263
|
+
});
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
if (isAgentSessionUnsupported(error)) {
|
|
1266
|
+
this.unavailable = true;
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
this.onError(error);
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
this.target = target;
|
|
1273
|
+
this.nativeId = nativeId;
|
|
1274
|
+
this.opened = true;
|
|
1275
|
+
try {
|
|
1276
|
+
await this.cli.agentEmit(target, "session_start", {
|
|
1277
|
+
...options,
|
|
1278
|
+
data: { provider: this.provider, native_id: nativeId }
|
|
1279
|
+
});
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
this.onError(error);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1074
1285
|
|
|
1075
|
-
// ../
|
|
1286
|
+
// ../runtime/src/awareness.ts
|
|
1076
1287
|
var PHUX_CONTEXT_VERSION = 1;
|
|
1077
1288
|
var DEFAULT_CONTEXT_TIMEOUT_MS = 1e3;
|
|
1078
1289
|
var DEFAULT_CONTEXT_MAX_BYTES = 8 * 1024;
|
|
@@ -1320,6 +1531,8 @@ var OpenCodeLifecycle = class {
|
|
|
1320
1531
|
target;
|
|
1321
1532
|
states = /* @__PURE__ */ new Map();
|
|
1322
1533
|
owned = /* @__PURE__ */ new Map();
|
|
1534
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1535
|
+
openedPanes = /* @__PURE__ */ new Map();
|
|
1323
1536
|
tail = Promise.resolve();
|
|
1324
1537
|
disposed = false;
|
|
1325
1538
|
constructor(options) {
|
|
@@ -1343,15 +1556,50 @@ var OpenCodeLifecycle = class {
|
|
|
1343
1556
|
observeState(sessionId, state) {
|
|
1344
1557
|
if (this.disposed) return this.tail;
|
|
1345
1558
|
this.states.set(sessionId, state);
|
|
1346
|
-
return this.enqueue(() =>
|
|
1559
|
+
return this.enqueue(async () => {
|
|
1560
|
+
await this.publish(sessionId);
|
|
1561
|
+
await this.emit(sessionId, state === "working" ? "prompt" : "stop");
|
|
1562
|
+
});
|
|
1347
1563
|
}
|
|
1348
1564
|
/** A tool invocation is an honest working signal if no status event was seen yet. */
|
|
1349
1565
|
targetSelected(sessionId) {
|
|
1350
|
-
|
|
1566
|
+
if (this.disposed) return this.tail;
|
|
1567
|
+
if (!this.states.has(sessionId)) this.states.set(sessionId, "working");
|
|
1568
|
+
return this.enqueue(() => this.publish(sessionId));
|
|
1569
|
+
}
|
|
1570
|
+
ask(sessionId, data = { kind: "permission" }) {
|
|
1571
|
+
if (this.disposed) return this.tail;
|
|
1572
|
+
return this.enqueue(async () => {
|
|
1573
|
+
await this.publish(sessionId);
|
|
1574
|
+
await this.emit(sessionId, "ask", data);
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
toolStart(sessionId, toolName, toolUseId) {
|
|
1578
|
+
if (this.disposed) return this.tail;
|
|
1579
|
+
const data = {
|
|
1580
|
+
tool_name: toolName,
|
|
1581
|
+
...toolUseId === void 0 ? {} : { tool_use_id: toolUseId }
|
|
1582
|
+
};
|
|
1583
|
+
return this.enqueue(async () => {
|
|
1584
|
+
await this.publish(sessionId);
|
|
1585
|
+
await this.emit(sessionId, "tool_start", data);
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
toolEnd(sessionId, toolName, toolUseId) {
|
|
1589
|
+
if (this.disposed) return this.tail;
|
|
1590
|
+
const data = {
|
|
1591
|
+
tool_name: toolName,
|
|
1592
|
+
...toolUseId === void 0 ? {} : { tool_use_id: toolUseId }
|
|
1593
|
+
};
|
|
1594
|
+
return this.enqueue(async () => {
|
|
1595
|
+
await this.publish(sessionId);
|
|
1596
|
+
await this.emit(sessionId, "tool_end", data);
|
|
1597
|
+
});
|
|
1351
1598
|
}
|
|
1352
1599
|
deleteSession(sessionId) {
|
|
1353
1600
|
this.states.delete(sessionId);
|
|
1354
1601
|
return this.enqueue(async () => {
|
|
1602
|
+
await this.finishSession(sessionId);
|
|
1355
1603
|
await this.clearSession(sessionId);
|
|
1356
1604
|
});
|
|
1357
1605
|
}
|
|
@@ -1359,10 +1607,11 @@ var OpenCodeLifecycle = class {
|
|
|
1359
1607
|
if (this.disposed) return this.tail;
|
|
1360
1608
|
this.disposed = true;
|
|
1361
1609
|
this.states.clear();
|
|
1362
|
-
const sessions = [...this.owned.keys()];
|
|
1610
|
+
const sessions = [.../* @__PURE__ */ new Set([...this.owned.keys(), ...this.sessions.keys()])];
|
|
1363
1611
|
this.enqueue(async () => {
|
|
1364
1612
|
for (const sessionId of sessions) {
|
|
1365
1613
|
try {
|
|
1614
|
+
await this.finishSession(sessionId);
|
|
1366
1615
|
await this.clearSession(sessionId);
|
|
1367
1616
|
} catch (error) {
|
|
1368
1617
|
this.onError(error);
|
|
@@ -1388,7 +1637,11 @@ var OpenCodeLifecycle = class {
|
|
|
1388
1637
|
await this.clearOwned(previous);
|
|
1389
1638
|
this.owned.delete(sessionId);
|
|
1390
1639
|
}
|
|
1391
|
-
if (target === void 0)
|
|
1640
|
+
if (target === void 0) {
|
|
1641
|
+
await this.emitter(sessionId).bind(null, sessionId, this.execution());
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
await this.bindSession(sessionId, target);
|
|
1392
1645
|
if (previous !== void 0 && previous.target === target) return;
|
|
1393
1646
|
const binding = { target, owner: `opencode:${sessionId}` };
|
|
1394
1647
|
this.owned.set(sessionId, binding);
|
|
@@ -1416,6 +1669,43 @@ var OpenCodeLifecycle = class {
|
|
|
1416
1669
|
execution() {
|
|
1417
1670
|
return { signal: new AbortController().signal, timeoutMs: this.timeoutMs };
|
|
1418
1671
|
}
|
|
1672
|
+
emitter(sessionId) {
|
|
1673
|
+
const existing = this.sessions.get(sessionId);
|
|
1674
|
+
if (existing !== void 0) return existing;
|
|
1675
|
+
const created = new AgentSessionEmitter(
|
|
1676
|
+
hasAgentSessionCli(this.cli) ? this.cli : null,
|
|
1677
|
+
{ provider: "opencode", onError: this.onError }
|
|
1678
|
+
);
|
|
1679
|
+
this.sessions.set(sessionId, created);
|
|
1680
|
+
return created;
|
|
1681
|
+
}
|
|
1682
|
+
async bindSession(sessionId, target) {
|
|
1683
|
+
const owner = this.openedPanes.get(target);
|
|
1684
|
+
if (owner !== void 0 && owner !== sessionId) return;
|
|
1685
|
+
const previous = [...this.openedPanes.entries()].find((entry) => entry[1] === sessionId);
|
|
1686
|
+
if (previous !== void 0 && previous[0] !== target) this.openedPanes.delete(previous[0]);
|
|
1687
|
+
const emitter = this.emitter(sessionId);
|
|
1688
|
+
await emitter.bind(target, sessionId, this.execution());
|
|
1689
|
+
if (emitter.isOpen) this.openedPanes.set(target, sessionId);
|
|
1690
|
+
}
|
|
1691
|
+
async emit(sessionId, type, data) {
|
|
1692
|
+
if (this.disposed) return;
|
|
1693
|
+
const target = this.target();
|
|
1694
|
+
if (target !== void 0) {
|
|
1695
|
+
const owner = this.openedPanes.get(target);
|
|
1696
|
+
if (owner !== void 0 && owner !== sessionId) return;
|
|
1697
|
+
}
|
|
1698
|
+
await this.emitter(sessionId).emit(type, data, this.execution());
|
|
1699
|
+
}
|
|
1700
|
+
async finishSession(sessionId) {
|
|
1701
|
+
const emitter = this.sessions.get(sessionId);
|
|
1702
|
+
if (emitter === void 0) return;
|
|
1703
|
+
await emitter.finish(this.execution());
|
|
1704
|
+
this.sessions.delete(sessionId);
|
|
1705
|
+
for (const [pane, owner] of this.openedPanes) {
|
|
1706
|
+
if (owner === sessionId) this.openedPanes.delete(pane);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1419
1709
|
};
|
|
1420
1710
|
function handleLifecycleEvent(lifecycle, event) {
|
|
1421
1711
|
switch (event.type) {
|
|
@@ -1441,10 +1731,36 @@ function handleLifecycleEvent(lifecycle, event) {
|
|
|
1441
1731
|
const sessionID = info !== null && typeof info === "object" ? info.id : void 0;
|
|
1442
1732
|
return typeof sessionID === "string" ? lifecycle.deleteSession(sessionID) : Promise.resolve();
|
|
1443
1733
|
}
|
|
1734
|
+
case "permission.asked": {
|
|
1735
|
+
const sessionID = sessionIdOf(event.properties);
|
|
1736
|
+
return sessionID === void 0 ? Promise.resolve() : lifecycle.ask(sessionID, permissionAskData(event.properties));
|
|
1737
|
+
}
|
|
1444
1738
|
default:
|
|
1445
1739
|
return Promise.resolve();
|
|
1446
1740
|
}
|
|
1447
1741
|
}
|
|
1742
|
+
function sessionIdOf(properties) {
|
|
1743
|
+
if (typeof properties.sessionID === "string") return properties.sessionID;
|
|
1744
|
+
if (typeof properties.sessionId === "string") return properties.sessionId;
|
|
1745
|
+
const info = properties.info;
|
|
1746
|
+
if (info !== null && typeof info === "object") {
|
|
1747
|
+
const id = info.id;
|
|
1748
|
+
if (typeof id === "string") return id;
|
|
1749
|
+
const nested = info.sessionID;
|
|
1750
|
+
if (typeof nested === "string") return nested;
|
|
1751
|
+
}
|
|
1752
|
+
return void 0;
|
|
1753
|
+
}
|
|
1754
|
+
function permissionAskData(properties) {
|
|
1755
|
+
const id = typeof properties.id === "string" ? properties.id : typeof properties.permissionID === "string" ? properties.permissionID : void 0;
|
|
1756
|
+
const permission = properties.permission;
|
|
1757
|
+
const question = typeof permission === "string" ? permission : typeof properties.title === "string" ? properties.title : void 0;
|
|
1758
|
+
return {
|
|
1759
|
+
kind: "permission",
|
|
1760
|
+
...id === void 0 ? {} : { id },
|
|
1761
|
+
...question === void 0 ? {} : { question }
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1448
1764
|
function lifecycleRecord(owner) {
|
|
1449
1765
|
return { name: "opencode", kind: "opencode", session: owner };
|
|
1450
1766
|
}
|
|
@@ -1720,106 +2036,144 @@ function byteLength(text) {
|
|
|
1720
2036
|
|
|
1721
2037
|
// src/index.ts
|
|
1722
2038
|
function createPhuxPlugin(defaults = {}) {
|
|
1723
|
-
return
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
const
|
|
1743
|
-
const
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
...self === null ? {} : { self },
|
|
1748
|
-
...selected === void 0 ? {} : { selected }
|
|
1749
|
-
};
|
|
2039
|
+
return async (_input, configured) => {
|
|
2040
|
+
const options = mergeOptions(defaults, configured ?? {});
|
|
2041
|
+
const environment = options.env ?? process.env;
|
|
2042
|
+
const environmentTarget = readEnvironmentTarget(environment.PHUX_TARGET);
|
|
2043
|
+
const cli = options.cli ?? new PhuxCli(cliOptions(options, environment));
|
|
2044
|
+
let selectedTarget;
|
|
2045
|
+
const currentTarget = () => selectedTarget ?? environmentTarget;
|
|
2046
|
+
const lifecycle = new OpenCodeLifecycle({
|
|
2047
|
+
cli,
|
|
2048
|
+
target: currentTarget,
|
|
2049
|
+
...options.lifecycleTimeoutMs === void 0 ? {} : { timeoutMs: options.lifecycleTimeoutMs },
|
|
2050
|
+
...options.onLifecycleError === void 0 ? {} : { onError: options.onLifecycleError }
|
|
2051
|
+
});
|
|
2052
|
+
const awareness = new PhuxContextAwareness(cli, {
|
|
2053
|
+
enabled: options.contextAwareness ?? contextAwarenessEnabled(environment.PHUX_CONTEXT_AWARENESS),
|
|
2054
|
+
...options.contextTimeoutMs === void 0 ? {} : { timeoutMs: options.contextTimeoutMs }
|
|
2055
|
+
});
|
|
2056
|
+
const latestContext = /* @__PURE__ */ new Map();
|
|
2057
|
+
const contextIdentity = () => {
|
|
2058
|
+
const self = normalizeTerminalIdentity(environment.PHUX_TERMINAL_ID);
|
|
2059
|
+
const selected = currentTarget();
|
|
2060
|
+
return {
|
|
2061
|
+
...self === null ? {} : { self },
|
|
2062
|
+
...selected === void 0 ? {} : { selected }
|
|
1750
2063
|
};
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
2064
|
+
};
|
|
2065
|
+
const tools = createPhuxTools({
|
|
2066
|
+
cli,
|
|
2067
|
+
...environmentTarget === void 0 ? {} : { environmentTarget },
|
|
2068
|
+
getSelectedTarget: () => selectedTarget,
|
|
2069
|
+
selectTarget: (target) => {
|
|
2070
|
+
selectedTarget = target;
|
|
2071
|
+
},
|
|
2072
|
+
targetSelected: (toolContext) => {
|
|
2073
|
+
void lifecycle.targetSelected(toolContext.sessionID);
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2076
|
+
return {
|
|
2077
|
+
tool: openCodeTools(tools),
|
|
2078
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
2079
|
+
if (input.sessionID === void 0) return;
|
|
2080
|
+
const emission = await awareness.next(input.sessionID, contextIdentity());
|
|
2081
|
+
if (emission !== null) latestContext.set(input.sessionID, emission.text);
|
|
2082
|
+
const text = latestContext.get(input.sessionID);
|
|
1769
2083
|
if (text === void 0) return;
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
)
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
toolRegistration.dispose(),
|
|
1788
|
-
contextRegistration.dispose(),
|
|
1789
|
-
eventTask,
|
|
1790
|
-
lifecycle.dispose()
|
|
1791
|
-
]);
|
|
1792
|
-
};
|
|
1793
|
-
}
|
|
1794
|
-
});
|
|
1795
|
-
}
|
|
1796
|
-
var PhuxPlugin = createPhuxPlugin();
|
|
1797
|
-
var index_default = PhuxPlugin;
|
|
1798
|
-
async function consumeEvents(events, lifecycle, awareness, latestContext, onError) {
|
|
1799
|
-
try {
|
|
1800
|
-
for await (const event of events) {
|
|
1801
|
-
if (!isLifecycleEvent(event)) continue;
|
|
1802
|
-
await handleLifecycleEvent(lifecycle, event);
|
|
1803
|
-
if (event.type === "session.deleted") {
|
|
2084
|
+
output.system.push(text);
|
|
2085
|
+
},
|
|
2086
|
+
"tool.execute.before": async (input) => {
|
|
2087
|
+
await lifecycle.toolStart(input.sessionID, input.tool, input.callID);
|
|
2088
|
+
},
|
|
2089
|
+
"tool.execute.after": async (input) => {
|
|
2090
|
+
await lifecycle.toolEnd(input.sessionID, input.tool, input.callID);
|
|
2091
|
+
},
|
|
2092
|
+
"permission.ask": async (input) => {
|
|
2093
|
+
const sessionID = input.sessionID;
|
|
2094
|
+
if (typeof sessionID !== "string") return;
|
|
2095
|
+
await lifecycle.ask(sessionID, { kind: "permission" });
|
|
2096
|
+
},
|
|
2097
|
+
event: async ({ event }) => {
|
|
2098
|
+
if (!isLifecycleEvent(event)) return;
|
|
2099
|
+
await handleLifecycleEvent(lifecycle, event);
|
|
2100
|
+
if (event.type !== "session.deleted") return;
|
|
1804
2101
|
const info = event.properties.info;
|
|
1805
2102
|
const sessionID = info !== null && typeof info === "object" ? info.id : void 0;
|
|
1806
|
-
if (typeof sessionID !== "string")
|
|
2103
|
+
if (typeof sessionID !== "string") return;
|
|
1807
2104
|
awareness.delete(sessionID);
|
|
1808
2105
|
latestContext.delete(sessionID);
|
|
1809
|
-
}
|
|
2106
|
+
},
|
|
2107
|
+
dispose: async () => lifecycle.dispose()
|
|
2108
|
+
};
|
|
2109
|
+
};
|
|
2110
|
+
}
|
|
2111
|
+
var PhuxPlugin = createPhuxPlugin();
|
|
2112
|
+
var index_default = PhuxPlugin;
|
|
2113
|
+
var z = tool.schema;
|
|
2114
|
+
var localTimeoutArgument = () => z.number().int().min(1).max(36e5).optional();
|
|
2115
|
+
var targetArgument = () => nonBlankString(512).optional();
|
|
2116
|
+
function nonBlankString(maxLength) {
|
|
2117
|
+
return z.string().min(1).max(maxLength).regex(/\S/);
|
|
2118
|
+
}
|
|
2119
|
+
function openCodeTools(tools) {
|
|
2120
|
+
return {
|
|
2121
|
+
phux_list: adaptTool(tools.phux_list, { local_timeout_ms: localTimeoutArgument() }),
|
|
2122
|
+
phux_create: adaptTool(tools.phux_create, {
|
|
2123
|
+
name: nonBlankString(255),
|
|
2124
|
+
cwd: nonBlankString(4096).optional(),
|
|
2125
|
+
command: z.array(z.string().max(65536)).min(1).max(256).optional(),
|
|
2126
|
+
local_timeout_ms: localTimeoutArgument()
|
|
2127
|
+
}),
|
|
2128
|
+
phux_snapshot: adaptTool(tools.phux_snapshot, {
|
|
2129
|
+
target: targetArgument(),
|
|
2130
|
+
scrollback: z.number().int().min(0).max(1e5).optional(),
|
|
2131
|
+
cells: z.boolean().optional(),
|
|
2132
|
+
local_timeout_ms: localTimeoutArgument()
|
|
2133
|
+
}),
|
|
2134
|
+
phux_send_keys: adaptTool(tools.phux_send_keys, {
|
|
2135
|
+
target: targetArgument(),
|
|
2136
|
+
keys: z.array(nonBlankString(65536)).min(1).max(256),
|
|
2137
|
+
local_timeout_ms: localTimeoutArgument()
|
|
2138
|
+
}),
|
|
2139
|
+
phux_run: adaptTool(tools.phux_run, {
|
|
2140
|
+
target: targetArgument(),
|
|
2141
|
+
command: nonBlankString(65536),
|
|
2142
|
+
timeout_seconds: z.number().int().min(0).max(86400).optional(),
|
|
2143
|
+
local_timeout_ms: localTimeoutArgument()
|
|
2144
|
+
}),
|
|
2145
|
+
phux_wait: adaptTool(tools.phux_wait, {
|
|
2146
|
+
target: targetArgument(),
|
|
2147
|
+
until: nonBlankString(4096).optional(),
|
|
2148
|
+
idle_ms: z.number().int().min(0).max(864e5).optional(),
|
|
2149
|
+
timeout_seconds: z.number().int().min(1).max(86400).optional(),
|
|
2150
|
+
local_timeout_ms: localTimeoutArgument()
|
|
2151
|
+
})
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
function adaptTool(definition, args) {
|
|
2155
|
+
return tool({
|
|
2156
|
+
description: definition.description,
|
|
2157
|
+
args,
|
|
2158
|
+
execute: async (input, context) => {
|
|
2159
|
+
const result = await definition.execute(input, phuxToolContext(context));
|
|
2160
|
+
return { output: result.content, metadata: result.metadata };
|
|
1810
2161
|
}
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
function phuxToolContext(context) {
|
|
2165
|
+
return {
|
|
2166
|
+
sessionID: context.sessionID,
|
|
2167
|
+
messageID: context.messageID,
|
|
2168
|
+
agent: context.agent,
|
|
2169
|
+
id: context.messageID
|
|
2170
|
+
};
|
|
1814
2171
|
}
|
|
1815
2172
|
function isLifecycleEvent(value) {
|
|
1816
2173
|
if (value === null || typeof value !== "object") return false;
|
|
1817
2174
|
const candidate = value;
|
|
1818
2175
|
return typeof candidate.type === "string" && candidate.properties !== null && typeof candidate.properties === "object";
|
|
1819
2176
|
}
|
|
1820
|
-
function isAbortError(error) {
|
|
1821
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
1822
|
-
}
|
|
1823
2177
|
function mergeOptions(defaults, configured) {
|
|
1824
2178
|
return { ...defaults, ...configured };
|
|
1825
2179
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phux/opencode",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "OpenCode
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "OpenCode tools and fleet context for shared phux terminals",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/no-phux/phux.git",
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
"prepack": "npm run build"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@opencode-ai/plugin": "
|
|
39
|
+
"@opencode-ai/plugin": "1.18.30"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@types/node": "
|
|
42
|
+
"@types/node": "26.5.0",
|
|
43
43
|
"tsup": "8.5.1",
|
|
44
|
-
"typescript": "
|
|
44
|
+
"typescript": "6.0.3"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": ">=22"
|