opencode-gateway 0.1.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/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/binding/execution.d.ts +24 -0
- package/dist/binding/execution.js +1 -0
- package/dist/binding/gateway.d.ts +71 -0
- package/dist/binding/gateway.js +1 -0
- package/dist/binding/index.d.ts +15 -0
- package/dist/binding/index.js +4 -0
- package/dist/binding/opencode.d.ts +123 -0
- package/dist/binding/opencode.js +1 -0
- package/dist/cli/args.d.ts +9 -0
- package/dist/cli/args.js +53 -0
- package/dist/cli/doctor.d.ts +6 -0
- package/dist/cli/doctor.js +59 -0
- package/dist/cli/init.d.ts +6 -0
- package/dist/cli/init.js +35 -0
- package/dist/cli/opencode-config.d.ts +10 -0
- package/dist/cli/opencode-config.js +62 -0
- package/dist/cli/paths.d.ts +7 -0
- package/dist/cli/paths.js +22 -0
- package/dist/cli/templates.d.ts +1 -0
- package/dist/cli/templates.js +26 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +314 -0
- package/dist/config/cron.d.ts +7 -0
- package/dist/config/cron.js +52 -0
- package/dist/config/gateway.d.ts +26 -0
- package/dist/config/gateway.js +142 -0
- package/dist/config/paths.d.ts +10 -0
- package/dist/config/paths.js +33 -0
- package/dist/config/telegram.d.ts +13 -0
- package/dist/config/telegram.js +91 -0
- package/dist/cron/runtime.d.ts +40 -0
- package/dist/cron/runtime.js +237 -0
- package/dist/delivery/telegram.d.ts +16 -0
- package/dist/delivery/telegram.js +75 -0
- package/dist/delivery/text.d.ts +21 -0
- package/dist/delivery/text.js +175 -0
- package/dist/gateway.d.ts +33 -0
- package/dist/gateway.js +105 -0
- package/dist/host/file-sender.d.ts +16 -0
- package/dist/host/file-sender.js +59 -0
- package/dist/host/noop.d.ts +4 -0
- package/dist/host/noop.js +14 -0
- package/dist/host/transport.d.ts +9 -0
- package/dist/host/transport.js +35 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +52 -0
- package/dist/mailbox/router.d.ts +7 -0
- package/dist/mailbox/router.js +16 -0
- package/dist/media/mime.d.ts +2 -0
- package/dist/media/mime.js +45 -0
- package/dist/opencode/adapter.d.ts +19 -0
- package/dist/opencode/adapter.js +291 -0
- package/dist/opencode/driver-hub.d.ts +15 -0
- package/dist/opencode/driver-hub.js +82 -0
- package/dist/opencode/event-normalize.d.ts +48 -0
- package/dist/opencode/event-normalize.js +48 -0
- package/dist/opencode/event-stream.d.ts +23 -0
- package/dist/opencode/event-stream.js +65 -0
- package/dist/opencode/events.d.ts +2 -0
- package/dist/opencode/events.js +1 -0
- package/dist/questions/client.d.ts +5 -0
- package/dist/questions/client.js +36 -0
- package/dist/questions/format.d.ts +3 -0
- package/dist/questions/format.js +36 -0
- package/dist/questions/normalize.d.ts +10 -0
- package/dist/questions/normalize.js +45 -0
- package/dist/questions/parser.d.ts +11 -0
- package/dist/questions/parser.js +96 -0
- package/dist/questions/runtime.d.ts +53 -0
- package/dist/questions/runtime.js +195 -0
- package/dist/questions/types.d.ts +22 -0
- package/dist/questions/types.js +1 -0
- package/dist/runtime/attachments.d.ts +3 -0
- package/dist/runtime/attachments.js +12 -0
- package/dist/runtime/executor.d.ts +24 -0
- package/dist/runtime/executor.js +188 -0
- package/dist/runtime/mailbox.d.ts +25 -0
- package/dist/runtime/mailbox.js +112 -0
- package/dist/runtime/opencode-runner.d.ts +26 -0
- package/dist/runtime/opencode-runner.js +79 -0
- package/dist/session/context.d.ts +10 -0
- package/dist/session/context.js +44 -0
- package/dist/session/conversation-key.d.ts +3 -0
- package/dist/session/conversation-key.js +3 -0
- package/dist/session/switcher.d.ts +25 -0
- package/dist/session/switcher.js +59 -0
- package/dist/store/migrations.d.ts +2 -0
- package/dist/store/migrations.js +183 -0
- package/dist/store/sqlite.d.ts +127 -0
- package/dist/store/sqlite.js +678 -0
- package/dist/telegram/client.d.ts +35 -0
- package/dist/telegram/client.js +179 -0
- package/dist/telegram/media.d.ts +13 -0
- package/dist/telegram/media.js +65 -0
- package/dist/telegram/normalize.d.ts +47 -0
- package/dist/telegram/normalize.js +119 -0
- package/dist/telegram/poller.d.ts +29 -0
- package/dist/telegram/poller.js +97 -0
- package/dist/telegram/runtime.d.ts +51 -0
- package/dist/telegram/runtime.js +133 -0
- package/dist/telegram/state.d.ts +36 -0
- package/dist/telegram/state.js +128 -0
- package/dist/telegram/types.d.ts +80 -0
- package/dist/telegram/types.js +1 -0
- package/dist/tools/channel-new-session.d.ts +4 -0
- package/dist/tools/channel-new-session.js +27 -0
- package/dist/tools/channel-send-file.d.ts +9 -0
- package/dist/tools/channel-send-file.js +27 -0
- package/dist/tools/channel-target.d.ts +7 -0
- package/dist/tools/channel-target.js +28 -0
- package/dist/tools/cron-list.d.ts +3 -0
- package/dist/tools/cron-list.js +34 -0
- package/dist/tools/cron-remove.d.ts +3 -0
- package/dist/tools/cron-remove.js +12 -0
- package/dist/tools/cron-run.d.ts +3 -0
- package/dist/tools/cron-run.js +20 -0
- package/dist/tools/cron-upsert.d.ts +3 -0
- package/dist/tools/cron-upsert.js +37 -0
- package/dist/tools/gateway-dispatch-cron.d.ts +3 -0
- package/dist/tools/gateway-dispatch-cron.js +33 -0
- package/dist/tools/gateway-status.d.ts +3 -0
- package/dist/tools/gateway-status.js +25 -0
- package/dist/tools/telegram-send-test.d.ts +3 -0
- package/dist/tools/telegram-send-test.js +26 -0
- package/dist/tools/telegram-status.d.ts +3 -0
- package/dist/tools/telegram-status.js +49 -0
- package/dist/tools/time.d.ts +3 -0
- package/dist/tools/time.js +25 -0
- package/dist/utils/error.d.ts +1 -0
- package/dist/utils/error.js +57 -0
- package/generated/wasm/pkg/opencode_gateway_ffi.d.ts +23 -0
- package/generated/wasm/pkg/opencode_gateway_ffi.js +574 -0
- package/generated/wasm/pkg/opencode_gateway_ffi_bg.wasm +0 -0
- package/generated/wasm/pkg/opencode_gateway_ffi_bg.wasm.d.ts +22 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 M4n5ter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# opencode-gateway
|
|
2
|
+
|
|
3
|
+
Gateway plugin for OpenCode.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
Initialize your OpenCode config:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx opencode-gateway init
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This ensures:
|
|
14
|
+
|
|
15
|
+
- `plugin: ["opencode-gateway"]` exists in `opencode.json`
|
|
16
|
+
- `opencode-gateway.toml` exists next to `opencode.json`
|
|
17
|
+
|
|
18
|
+
By default the CLI uses `OPENCODE_CONFIG_DIR` when it is set, otherwise it
|
|
19
|
+
writes to:
|
|
20
|
+
|
|
21
|
+
- `~/.config/opencode/opencode.json`
|
|
22
|
+
- `~/.config/opencode/opencode-gateway.toml`
|
|
23
|
+
|
|
24
|
+
Check what it resolved:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx opencode-gateway doctor
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then start OpenCode normally:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
opencode serve
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
If you want a separate managed config tree instead of editing your existing
|
|
37
|
+
OpenCode config:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx opencode-gateway init --managed
|
|
41
|
+
export OPENCODE_CONFIG="$HOME/.config/opencode-gateway/opencode/opencode.json"
|
|
42
|
+
export OPENCODE_CONFIG_DIR="$HOME/.config/opencode-gateway/opencode"
|
|
43
|
+
opencode serve
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Example gateway config
|
|
47
|
+
|
|
48
|
+
```toml
|
|
49
|
+
[gateway]
|
|
50
|
+
state_db = "/home/you/.local/share/opencode-gateway/state.db"
|
|
51
|
+
|
|
52
|
+
[cron]
|
|
53
|
+
enabled = true
|
|
54
|
+
tick_seconds = 5
|
|
55
|
+
max_concurrent_runs = 1
|
|
56
|
+
|
|
57
|
+
[channels.telegram]
|
|
58
|
+
enabled = false
|
|
59
|
+
bot_token_env = "TELEGRAM_BOT_TOKEN"
|
|
60
|
+
poll_timeout_seconds = 25
|
|
61
|
+
allowed_chats = []
|
|
62
|
+
allowed_users = []
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
When Telegram is enabled, export the bot token through the configured
|
|
66
|
+
environment variable, for example:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
export TELEGRAM_BOT_TOKEN="..."
|
|
70
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type BindingExecutionObservation = {
|
|
2
|
+
kind: "messageUpdated";
|
|
3
|
+
sessionId: string;
|
|
4
|
+
messageId: string;
|
|
5
|
+
role: string;
|
|
6
|
+
parentId: string | null;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "textPartUpdated";
|
|
9
|
+
sessionId: string;
|
|
10
|
+
messageId: string;
|
|
11
|
+
partId: string;
|
|
12
|
+
text: string | null;
|
|
13
|
+
delta: string | null;
|
|
14
|
+
ignored: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "textPartDelta";
|
|
17
|
+
messageId: string;
|
|
18
|
+
partId: string;
|
|
19
|
+
delta: string;
|
|
20
|
+
};
|
|
21
|
+
export type BindingProgressiveDirective = {
|
|
22
|
+
kind: string;
|
|
23
|
+
text: string | null;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export type GatewayStatusSnapshot = {
|
|
2
|
+
runtimeMode: string;
|
|
3
|
+
supportsTelegram: boolean;
|
|
4
|
+
supportsCron: boolean;
|
|
5
|
+
hasWebUi: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type BindingCronJobSpec = {
|
|
8
|
+
id: string;
|
|
9
|
+
schedule: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
deliveryChannel: string | null;
|
|
12
|
+
deliveryTarget: string | null;
|
|
13
|
+
deliveryTopic: string | null;
|
|
14
|
+
};
|
|
15
|
+
export type BindingRuntimeReport = {
|
|
16
|
+
conversationKey: string;
|
|
17
|
+
responseText: string;
|
|
18
|
+
delivered: boolean;
|
|
19
|
+
recordedAtMs: bigint;
|
|
20
|
+
};
|
|
21
|
+
export type BindingHostAck = {
|
|
22
|
+
errorMessage: string | null;
|
|
23
|
+
};
|
|
24
|
+
export type BindingDeliveryTarget = {
|
|
25
|
+
channel: string;
|
|
26
|
+
target: string;
|
|
27
|
+
topic: string | null;
|
|
28
|
+
};
|
|
29
|
+
export type BindingInboundAttachment = {
|
|
30
|
+
kind: "image";
|
|
31
|
+
mimeType: string;
|
|
32
|
+
fileName: string | null;
|
|
33
|
+
localPath: string;
|
|
34
|
+
};
|
|
35
|
+
export type BindingInboundMessage = {
|
|
36
|
+
deliveryTarget: BindingDeliveryTarget;
|
|
37
|
+
sender: string;
|
|
38
|
+
text: string | null;
|
|
39
|
+
attachments: BindingInboundAttachment[];
|
|
40
|
+
mailboxKey?: string | null;
|
|
41
|
+
};
|
|
42
|
+
export type BindingPromptPart = {
|
|
43
|
+
kind: "text";
|
|
44
|
+
text: string;
|
|
45
|
+
} | {
|
|
46
|
+
kind: "file";
|
|
47
|
+
mimeType: string;
|
|
48
|
+
fileName: string | null;
|
|
49
|
+
localPath: string;
|
|
50
|
+
};
|
|
51
|
+
export type BindingPreparedExecution = {
|
|
52
|
+
conversationKey: string;
|
|
53
|
+
promptParts: BindingPromptPart[];
|
|
54
|
+
replyTarget: BindingDeliveryTarget | null;
|
|
55
|
+
};
|
|
56
|
+
export type BindingOutboundMessage = {
|
|
57
|
+
deliveryTarget: BindingDeliveryTarget;
|
|
58
|
+
body: string;
|
|
59
|
+
};
|
|
60
|
+
export type BindingTransportHost = {
|
|
61
|
+
sendMessage(message: BindingOutboundMessage): Promise<BindingHostAck>;
|
|
62
|
+
};
|
|
63
|
+
export type BindingLoggerHost = {
|
|
64
|
+
log(level: string, message: string): void;
|
|
65
|
+
};
|
|
66
|
+
export type GatewayContract = {
|
|
67
|
+
gatewayStatus(): GatewayStatusSnapshot;
|
|
68
|
+
conversationKeyForDeliveryTarget(target: BindingDeliveryTarget): string;
|
|
69
|
+
nextCronRunAt(job: BindingCronJobSpec, afterMs: number, timeZone: string): number;
|
|
70
|
+
normalizeCronTimeZone(timeZone: string): string;
|
|
71
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BindingCronJobSpec, BindingInboundMessage, BindingPreparedExecution, GatewayContract } from "./gateway";
|
|
2
|
+
import type { BindingOpencodeExecutionInput, OpencodeExecutionDriver } from "./opencode";
|
|
3
|
+
export type { BindingExecutionObservation, BindingProgressiveDirective } from "./execution";
|
|
4
|
+
export type { BindingCronJobSpec, BindingDeliveryTarget, BindingHostAck, BindingInboundAttachment, BindingInboundMessage, BindingLoggerHost, BindingOutboundMessage, BindingPreparedExecution, BindingPromptPart, BindingRuntimeReport, BindingTransportHost, GatewayContract, GatewayStatusSnapshot, } from "./gateway";
|
|
5
|
+
export type { BindingOpencodeCommand, BindingOpencodeCommandPart, BindingOpencodeCommandResult, BindingOpencodeDriverStep, BindingOpencodeExecutionInput, BindingOpencodeMessage, BindingOpencodeMessagePart, BindingOpencodePrompt, OpencodeExecutionDriver, } from "./opencode";
|
|
6
|
+
export type GatewayBindingModule = GatewayContract & {
|
|
7
|
+
prepareInboundExecution: (message: BindingInboundMessage) => BindingPreparedExecution;
|
|
8
|
+
prepareCronExecution: (job: BindingCronJobSpec) => BindingPreparedExecution;
|
|
9
|
+
OpencodeExecutionDriver: {
|
|
10
|
+
new (input: BindingOpencodeExecutionInput): OpencodeExecutionDriver;
|
|
11
|
+
};
|
|
12
|
+
initSync?: (module?: BufferSource | WebAssembly.Module) => unknown;
|
|
13
|
+
default?: (module?: BufferSource | WebAssembly.Module) => Promise<unknown>;
|
|
14
|
+
};
|
|
15
|
+
export declare function loadGatewayBindingModule(): Promise<GatewayBindingModule>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { BindingExecutionObservation, BindingProgressiveDirective } from "./execution";
|
|
2
|
+
import type { BindingPromptPart } from "./gateway";
|
|
3
|
+
export type BindingOpencodePrompt = {
|
|
4
|
+
promptKey: string;
|
|
5
|
+
parts: BindingPromptPart[];
|
|
6
|
+
};
|
|
7
|
+
export type BindingOpencodeExecutionInput = {
|
|
8
|
+
conversationKey: string;
|
|
9
|
+
persistedSessionId: string | null;
|
|
10
|
+
mode: "progressive" | "oneshot";
|
|
11
|
+
flushIntervalMs: number;
|
|
12
|
+
prompts: BindingOpencodePrompt[];
|
|
13
|
+
};
|
|
14
|
+
export type BindingOpencodeMessagePart = {
|
|
15
|
+
messageId: string;
|
|
16
|
+
partId: string;
|
|
17
|
+
type: string;
|
|
18
|
+
text: string | null;
|
|
19
|
+
ignored: boolean;
|
|
20
|
+
};
|
|
21
|
+
export type BindingOpencodeMessage = {
|
|
22
|
+
messageId: string;
|
|
23
|
+
role: string;
|
|
24
|
+
parentId: string | null;
|
|
25
|
+
parts: BindingOpencodeMessagePart[];
|
|
26
|
+
};
|
|
27
|
+
export type BindingOpencodeCommand = {
|
|
28
|
+
kind: "lookupSession";
|
|
29
|
+
sessionId: string;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "createSession";
|
|
32
|
+
title: string;
|
|
33
|
+
} | {
|
|
34
|
+
kind: "waitUntilIdle";
|
|
35
|
+
sessionId: string;
|
|
36
|
+
} | {
|
|
37
|
+
kind: "appendPrompt";
|
|
38
|
+
sessionId: string;
|
|
39
|
+
messageId: string;
|
|
40
|
+
parts: BindingOpencodeCommandPart[];
|
|
41
|
+
} | {
|
|
42
|
+
kind: "sendPromptAsync";
|
|
43
|
+
sessionId: string;
|
|
44
|
+
messageId: string;
|
|
45
|
+
parts: BindingOpencodeCommandPart[];
|
|
46
|
+
} | {
|
|
47
|
+
kind: "awaitPromptResponse";
|
|
48
|
+
sessionId: string;
|
|
49
|
+
messageId: string;
|
|
50
|
+
} | {
|
|
51
|
+
kind: "readMessage";
|
|
52
|
+
sessionId: string;
|
|
53
|
+
messageId: string;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "listMessages";
|
|
56
|
+
sessionId: string;
|
|
57
|
+
};
|
|
58
|
+
export type BindingOpencodeCommandResult = {
|
|
59
|
+
kind: "lookupSession";
|
|
60
|
+
sessionId: string;
|
|
61
|
+
found: boolean;
|
|
62
|
+
} | {
|
|
63
|
+
kind: "createSession";
|
|
64
|
+
sessionId: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: "waitUntilIdle";
|
|
67
|
+
sessionId: string;
|
|
68
|
+
} | {
|
|
69
|
+
kind: "appendPrompt";
|
|
70
|
+
sessionId: string;
|
|
71
|
+
} | {
|
|
72
|
+
kind: "sendPromptAsync";
|
|
73
|
+
sessionId: string;
|
|
74
|
+
} | {
|
|
75
|
+
kind: "awaitPromptResponse";
|
|
76
|
+
sessionId: string;
|
|
77
|
+
messageId: string;
|
|
78
|
+
parts: BindingOpencodeMessagePart[];
|
|
79
|
+
} | {
|
|
80
|
+
kind: "readMessage";
|
|
81
|
+
sessionId: string;
|
|
82
|
+
messageId: string;
|
|
83
|
+
parts: BindingOpencodeMessagePart[];
|
|
84
|
+
} | {
|
|
85
|
+
kind: "listMessages";
|
|
86
|
+
sessionId: string;
|
|
87
|
+
messages: BindingOpencodeMessage[];
|
|
88
|
+
} | {
|
|
89
|
+
kind: "error";
|
|
90
|
+
commandKind: string;
|
|
91
|
+
sessionId: string | null;
|
|
92
|
+
code: "missingSession" | "unknown";
|
|
93
|
+
message: string;
|
|
94
|
+
};
|
|
95
|
+
export type BindingOpencodeCommandPart = {
|
|
96
|
+
kind: "text";
|
|
97
|
+
partId: string;
|
|
98
|
+
text: string;
|
|
99
|
+
} | {
|
|
100
|
+
kind: "file";
|
|
101
|
+
partId: string;
|
|
102
|
+
mimeType: string;
|
|
103
|
+
fileName: string | null;
|
|
104
|
+
localPath: string;
|
|
105
|
+
};
|
|
106
|
+
export type BindingOpencodeDriverStep = {
|
|
107
|
+
kind: "command";
|
|
108
|
+
command: BindingOpencodeCommand;
|
|
109
|
+
} | {
|
|
110
|
+
kind: "complete";
|
|
111
|
+
sessionId: string;
|
|
112
|
+
responseText: string;
|
|
113
|
+
finalText: string | null;
|
|
114
|
+
} | {
|
|
115
|
+
kind: "failed";
|
|
116
|
+
message: string;
|
|
117
|
+
};
|
|
118
|
+
export type OpencodeExecutionDriver = {
|
|
119
|
+
start(): BindingOpencodeDriverStep;
|
|
120
|
+
resume(result: BindingOpencodeCommandResult): BindingOpencodeDriverStep;
|
|
121
|
+
observeEvent(observation: BindingExecutionObservation, nowMs: number): BindingProgressiveDirective;
|
|
122
|
+
free?(): void;
|
|
123
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
export function parseCliCommand(argv) {
|
|
3
|
+
const [command, ...rest] = argv;
|
|
4
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
5
|
+
return { kind: "help" };
|
|
6
|
+
}
|
|
7
|
+
if (command !== "init" && command !== "doctor") {
|
|
8
|
+
throw new Error(`unknown command: ${command}`);
|
|
9
|
+
}
|
|
10
|
+
let managed = false;
|
|
11
|
+
let configDir = null;
|
|
12
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
13
|
+
const argument = rest[index];
|
|
14
|
+
if (argument === "--managed") {
|
|
15
|
+
managed = true;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (argument === "--config-dir") {
|
|
19
|
+
const value = rest[index + 1];
|
|
20
|
+
if (!value) {
|
|
21
|
+
throw new Error("--config-dir requires a value");
|
|
22
|
+
}
|
|
23
|
+
configDir = resolve(value);
|
|
24
|
+
index += 1;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (argument === "--help" || argument === "-h") {
|
|
28
|
+
return { kind: "help" };
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`unknown argument: ${argument}`);
|
|
31
|
+
}
|
|
32
|
+
if (managed && configDir !== null) {
|
|
33
|
+
throw new Error("--managed cannot be combined with --config-dir");
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
kind: command,
|
|
37
|
+
managed,
|
|
38
|
+
configDir,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function formatCliHelp() {
|
|
42
|
+
return [
|
|
43
|
+
"opencode-gateway",
|
|
44
|
+
"",
|
|
45
|
+
"Commands:",
|
|
46
|
+
" opencode-gateway init [--managed] [--config-dir <path>]",
|
|
47
|
+
" opencode-gateway doctor [--managed] [--config-dir <path>]",
|
|
48
|
+
"",
|
|
49
|
+
"Defaults:",
|
|
50
|
+
" init/doctor use OPENCODE_CONFIG_DIR when set, otherwise ~/.config/opencode",
|
|
51
|
+
" --managed uses ~/.config/opencode-gateway/opencode",
|
|
52
|
+
].join("\n");
|
|
53
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { GATEWAY_CONFIG_FILE, OPENCODE_CONFIG_FILE } from "../config/paths";
|
|
4
|
+
import { parseOpencodeConfig } from "./opencode-config";
|
|
5
|
+
import { pathExists, resolveCliConfigDir } from "./paths";
|
|
6
|
+
export async function runDoctor(options, env) {
|
|
7
|
+
const configDir = resolveCliConfigDir(options, env);
|
|
8
|
+
const opencodeConfigPath = join(configDir, OPENCODE_CONFIG_FILE);
|
|
9
|
+
const gatewayConfigPath = join(configDir, GATEWAY_CONFIG_FILE);
|
|
10
|
+
const opencodeStatus = await inspectOpencodeConfig(opencodeConfigPath);
|
|
11
|
+
const gatewayOverride = env.OPENCODE_GATEWAY_CONFIG?.trim() || null;
|
|
12
|
+
console.log("doctor report");
|
|
13
|
+
console.log(` config dir: ${configDir}`);
|
|
14
|
+
console.log(` opencode config: ${await describePath(opencodeConfigPath)}`);
|
|
15
|
+
console.log(` gateway config: ${await describePath(gatewayConfigPath)}`);
|
|
16
|
+
console.log(` gateway config override: ${gatewayOverride ?? "not set"}`);
|
|
17
|
+
console.log(` plugin configured: ${opencodeStatus.pluginConfigured}`);
|
|
18
|
+
console.log(` TELEGRAM_BOT_TOKEN: ${env.TELEGRAM_BOT_TOKEN?.trim() ? "set" : "missing"}`);
|
|
19
|
+
if (opencodeStatus.error !== null) {
|
|
20
|
+
console.log(` opencode config error: ${opencodeStatus.error}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function describePath(path) {
|
|
24
|
+
return (await pathExists(path)) ? `present at ${path}` : `missing at ${path}`;
|
|
25
|
+
}
|
|
26
|
+
async function inspectOpencodeConfig(path) {
|
|
27
|
+
if (!(await pathExists(path))) {
|
|
28
|
+
return {
|
|
29
|
+
pluginConfigured: "no",
|
|
30
|
+
error: null,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const parsed = parseOpencodeConfig(await readFile(path, "utf8"), path);
|
|
35
|
+
const plugins = parsed.plugin;
|
|
36
|
+
if (plugins === undefined) {
|
|
37
|
+
return {
|
|
38
|
+
pluginConfigured: "no",
|
|
39
|
+
error: null,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!Array.isArray(plugins)) {
|
|
43
|
+
return {
|
|
44
|
+
pluginConfigured: "invalid",
|
|
45
|
+
error: "`plugin` is not an array",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
pluginConfigured: plugins.some((entry) => entry === "opencode-gateway") ? "yes" : "no",
|
|
50
|
+
error: null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
return {
|
|
55
|
+
pluginConfigured: "unknown",
|
|
56
|
+
error: error instanceof Error ? error.message : String(error),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { defaultGatewayStateDbPath, GATEWAY_CONFIG_FILE, OPENCODE_CONFIG_FILE, } from "../config/paths";
|
|
4
|
+
import { createDefaultOpencodeConfig, ensureGatewayPlugin, parseOpencodeConfig, stringifyOpencodeConfig, } from "./opencode-config";
|
|
5
|
+
import { pathExists, resolveCliConfigDir } from "./paths";
|
|
6
|
+
import { buildGatewayConfigTemplate } from "./templates";
|
|
7
|
+
export async function runInit(options, env) {
|
|
8
|
+
const configDir = resolveCliConfigDir(options, env);
|
|
9
|
+
const opencodeConfigPath = join(configDir, OPENCODE_CONFIG_FILE);
|
|
10
|
+
const gatewayConfigPath = join(configDir, GATEWAY_CONFIG_FILE);
|
|
11
|
+
await mkdir(configDir, { recursive: true });
|
|
12
|
+
let opencodeStatus = "already present";
|
|
13
|
+
if (!(await pathExists(opencodeConfigPath))) {
|
|
14
|
+
await writeFile(opencodeConfigPath, stringifyOpencodeConfig(createDefaultOpencodeConfig(options.managed)));
|
|
15
|
+
opencodeStatus = "created";
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const source = await readFile(opencodeConfigPath, "utf8");
|
|
19
|
+
const parsed = parseOpencodeConfig(source, opencodeConfigPath);
|
|
20
|
+
const next = ensureGatewayPlugin(parsed);
|
|
21
|
+
if (next.changed) {
|
|
22
|
+
await writeFile(opencodeConfigPath, stringifyOpencodeConfig(next.document));
|
|
23
|
+
opencodeStatus = "updated";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
let gatewayStatus = "already present";
|
|
27
|
+
if (!(await pathExists(gatewayConfigPath))) {
|
|
28
|
+
await mkdir(dirname(gatewayConfigPath), { recursive: true });
|
|
29
|
+
await writeFile(gatewayConfigPath, buildGatewayConfigTemplate(defaultGatewayStateDbPath(env)));
|
|
30
|
+
gatewayStatus = "created";
|
|
31
|
+
}
|
|
32
|
+
console.log(`config dir: ${configDir}`);
|
|
33
|
+
console.log(`opencode config: ${opencodeConfigPath} (${opencodeStatus})`);
|
|
34
|
+
console.log(`gateway config: ${gatewayConfigPath} (${gatewayStatus})`);
|
|
35
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type OpencodeConfigDocument = Record<string, unknown>;
|
|
2
|
+
export type EnsurePluginResult = {
|
|
3
|
+
changed: boolean;
|
|
4
|
+
document: OpencodeConfigDocument;
|
|
5
|
+
};
|
|
6
|
+
export declare function createDefaultOpencodeConfig(managed: boolean): OpencodeConfigDocument;
|
|
7
|
+
export declare function ensureGatewayPlugin(document: OpencodeConfigDocument): EnsurePluginResult;
|
|
8
|
+
export declare function parseOpencodeConfig(source: string, path: string): OpencodeConfigDocument;
|
|
9
|
+
export declare function stringifyOpencodeConfig(document: OpencodeConfigDocument): string;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const OPENCODE_SCHEMA_URL = "https://opencode.ai/config.json";
|
|
2
|
+
const PACKAGE_NAME = "opencode-gateway";
|
|
3
|
+
export function createDefaultOpencodeConfig(managed) {
|
|
4
|
+
const document = {
|
|
5
|
+
$schema: OPENCODE_SCHEMA_URL,
|
|
6
|
+
plugin: [PACKAGE_NAME],
|
|
7
|
+
};
|
|
8
|
+
if (managed) {
|
|
9
|
+
document.server = {
|
|
10
|
+
hostname: "127.0.0.1",
|
|
11
|
+
port: 4096,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return document;
|
|
15
|
+
}
|
|
16
|
+
export function ensureGatewayPlugin(document) {
|
|
17
|
+
const plugins = document.plugin;
|
|
18
|
+
if (plugins === undefined) {
|
|
19
|
+
return {
|
|
20
|
+
changed: true,
|
|
21
|
+
document: {
|
|
22
|
+
...document,
|
|
23
|
+
plugin: [PACKAGE_NAME],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (!Array.isArray(plugins)) {
|
|
28
|
+
throw new Error("opencode.json field `plugin` must be an array when present");
|
|
29
|
+
}
|
|
30
|
+
if (plugins.some((entry) => entry === PACKAGE_NAME)) {
|
|
31
|
+
return {
|
|
32
|
+
changed: false,
|
|
33
|
+
document,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
changed: true,
|
|
38
|
+
document: {
|
|
39
|
+
...document,
|
|
40
|
+
plugin: [...plugins, PACKAGE_NAME],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function parseOpencodeConfig(source, path) {
|
|
45
|
+
let parsed;
|
|
46
|
+
try {
|
|
47
|
+
parsed = JSON.parse(source);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new Error(`failed to parse opencode config ${path}: ${formatError(error)}`);
|
|
51
|
+
}
|
|
52
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
53
|
+
throw new Error(`opencode config ${path} must decode to a JSON object`);
|
|
54
|
+
}
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
export function stringifyOpencodeConfig(document) {
|
|
58
|
+
return `${JSON.stringify(document, null, 2)}\n`;
|
|
59
|
+
}
|
|
60
|
+
function formatError(error) {
|
|
61
|
+
return error instanceof Error ? error.message : String(error);
|
|
62
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type CliPathOptions = {
|
|
2
|
+
managed: boolean;
|
|
3
|
+
configDir: string | null;
|
|
4
|
+
};
|
|
5
|
+
export declare function resolveCliConfigDir(options: CliPathOptions, env: Record<string, string | undefined>): string;
|
|
6
|
+
export declare function pathExists(path: string): Promise<boolean>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { resolveManagedOpencodeConfigDir, resolveOpencodeConfigDir } from "../config/paths";
|
|
5
|
+
export function resolveCliConfigDir(options, env) {
|
|
6
|
+
if (options.configDir !== null) {
|
|
7
|
+
return resolve(options.configDir);
|
|
8
|
+
}
|
|
9
|
+
if (options.managed) {
|
|
10
|
+
return resolveManagedOpencodeConfigDir(env);
|
|
11
|
+
}
|
|
12
|
+
return resolveOpencodeConfigDir(env);
|
|
13
|
+
}
|
|
14
|
+
export async function pathExists(path) {
|
|
15
|
+
try {
|
|
16
|
+
await access(path, constants.F_OK);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildGatewayConfigTemplate(stateDbPath: string): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function buildGatewayConfigTemplate(stateDbPath) {
|
|
2
|
+
return [
|
|
3
|
+
"# Opencode Gateway configuration",
|
|
4
|
+
"# Fill in secrets and provider details before enabling real integrations.",
|
|
5
|
+
"",
|
|
6
|
+
"[gateway]",
|
|
7
|
+
`state_db = "${escapeTomlString(stateDbPath)}"`,
|
|
8
|
+
"",
|
|
9
|
+
"[cron]",
|
|
10
|
+
"enabled = true",
|
|
11
|
+
"tick_seconds = 5",
|
|
12
|
+
"max_concurrent_runs = 1",
|
|
13
|
+
'# timezone = "Asia/Shanghai"',
|
|
14
|
+
"",
|
|
15
|
+
"[channels.telegram]",
|
|
16
|
+
"enabled = false",
|
|
17
|
+
'bot_token_env = "TELEGRAM_BOT_TOKEN"',
|
|
18
|
+
"poll_timeout_seconds = 25",
|
|
19
|
+
"allowed_chats = []",
|
|
20
|
+
"allowed_users = []",
|
|
21
|
+
"",
|
|
22
|
+
].join("\n");
|
|
23
|
+
}
|
|
24
|
+
function escapeTomlString(value) {
|
|
25
|
+
return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
26
|
+
}
|
package/dist/cli.d.ts
ADDED