opencode-telegram-bridge 1.0.7 → 1.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/README.md CHANGED
@@ -22,30 +22,6 @@ You still need OpenCode running separately:
22
22
  opencode serve
23
23
  ```
24
24
 
25
- ## Develop (for contributors)
26
- 1. Install dependencies:
27
-
28
- ```bash
29
- npm install
30
- ```
31
-
32
- 2. Create `.env` from the example:
33
-
34
- ```bash
35
- cp .env.example .env
36
- ```
37
-
38
- 3. Start OpenCode server in another terminal:
39
-
40
- ```bash
41
- opencode serve
42
- ```
43
-
44
- 4. Run the bot:
45
-
46
- ```bash
47
- npm run dev
48
- ```
49
-
50
- ## Releases
51
- This project uses Changesets. See `docs/release.md`.
25
+ ## Documentation
26
+ See the docs for configuration, usage, and systemd setup:
27
+ https://gabriel-trigo.github.io/opencode-telegram-bridge/
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import "../dist/index.js"
2
+ import "../dist/cli.js"
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,159 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import readline from "node:readline/promises";
6
+ import { stdin, stdout } from "node:process";
7
+ import { runBot } from "./run.js";
8
+ const die = (message) => {
9
+ console.error(message);
10
+ process.exit(1);
11
+ };
12
+ const runCommand = (command, args) => {
13
+ const result = spawnSync(command, args, {
14
+ encoding: "utf8",
15
+ stdio: "inherit",
16
+ });
17
+ if (result.status !== 0) {
18
+ throw new Error(`Command failed: ${command} ${args.join(" ")}`);
19
+ }
20
+ };
21
+ const resolveExecStart = () => {
22
+ const result = spawnSync("sh", ["-c", "command -v opencode-telegram-bridge"], {
23
+ encoding: "utf8",
24
+ });
25
+ const execStart = result.stdout.trim();
26
+ if (!execStart || result.status !== 0) {
27
+ throw new Error("opencode-telegram-bridge not found in PATH");
28
+ }
29
+ return execStart;
30
+ };
31
+ const readAnswer = async (prompt, options = {}) => {
32
+ const rl = readline.createInterface({ input: stdin, output: stdout });
33
+ const suffix = options.defaultValue ? ` (${options.defaultValue})` : "";
34
+ const answer = (await rl.question(`${prompt}${suffix}: `)).trim();
35
+ rl.close();
36
+ if (!answer && options.defaultValue) {
37
+ return options.defaultValue;
38
+ }
39
+ if (!answer && options.required) {
40
+ throw new Error(`Missing ${prompt}`);
41
+ }
42
+ return answer;
43
+ };
44
+ const confirmAnswer = async (prompt, defaultValue = false) => {
45
+ const rl = readline.createInterface({ input: stdin, output: stdout });
46
+ const suffix = defaultValue ? "(Y/n)" : "(y/N)";
47
+ const answer = (await rl.question(`${prompt} ${suffix}: `)).trim();
48
+ rl.close();
49
+ if (!answer) {
50
+ return defaultValue;
51
+ }
52
+ return answer.toLowerCase().startsWith("y");
53
+ };
54
+ const writeEnvFile = (envPath, values) => {
55
+ const lines = Object.entries(values).map(([key, value]) => `${key}=${value}`);
56
+ fs.mkdirSync(path.dirname(envPath), { recursive: true });
57
+ fs.writeFileSync(envPath, `${lines.join("\n")}\n`, "utf8");
58
+ };
59
+ const writeUnitFile = (unitPath, content) => {
60
+ fs.mkdirSync(path.dirname(unitPath), { recursive: true });
61
+ fs.writeFileSync(unitPath, content, "utf8");
62
+ };
63
+ const buildUnitFile = (envPath, execStart) => `
64
+ [Unit]
65
+ Description=OpenCode Telegram Bridge
66
+ After=network-online.target
67
+ Wants=network-online.target
68
+
69
+ [Service]
70
+ Type=simple
71
+ EnvironmentFile=${envPath}
72
+ ExecStart=${execStart}
73
+ Restart=on-failure
74
+ RestartSec=3
75
+
76
+ [Install]
77
+ WantedBy=default.target
78
+ `;
79
+ const runSetupWizard = async () => {
80
+ if (process.platform !== "linux") {
81
+ die("Setup currently supports Linux systemd only. See the docs for manual setup.");
82
+ }
83
+ const systemctlCheck = spawnSync("systemctl", ["--user", "--version"], {
84
+ encoding: "utf8",
85
+ });
86
+ if (systemctlCheck.status !== 0) {
87
+ die("systemctl --user is not available. Install systemd or use manual setup.");
88
+ }
89
+ const homeDir = os.homedir();
90
+ const envPath = path.join(homeDir, ".config", "opencode-telegram-bridge", "opencode-telegram-bridge.env");
91
+ const unitPath = path.join(homeDir, ".config", "systemd", "user", "opencode-telegram-bridge.service");
92
+ if (fs.existsSync(envPath) || fs.existsSync(unitPath)) {
93
+ const overwrite = await confirmAnswer("Existing config found. Overwrite?", false);
94
+ if (!overwrite) {
95
+ die("Setup cancelled.");
96
+ }
97
+ }
98
+ const botToken = await readAnswer("TELEGRAM_BOT_TOKEN", { required: true });
99
+ const allowedUserId = await readAnswer("TELEGRAM_ALLOWED_USER_ID", {
100
+ required: true,
101
+ });
102
+ if (!Number.isInteger(Number(allowedUserId))) {
103
+ throw new Error("TELEGRAM_ALLOWED_USER_ID must be an integer");
104
+ }
105
+ const serverUrl = await readAnswer("OPENCODE_SERVER_URL", {
106
+ defaultValue: "http://127.0.0.1:4096",
107
+ });
108
+ const serverUsername = await readAnswer("OPENCODE_SERVER_USERNAME", {
109
+ defaultValue: "opencode",
110
+ });
111
+ const serverPassword = await readAnswer("OPENCODE_SERVER_PASSWORD", {});
112
+ const opencodeRestartCommand = await readAnswer("OPENCODE_RESTART_COMMAND", {});
113
+ const opencodeRestartTimeoutMs = opencodeRestartCommand ? "30000" : "";
114
+ const execStart = resolveExecStart();
115
+ writeEnvFile(envPath, {
116
+ TELEGRAM_BOT_TOKEN: botToken,
117
+ TELEGRAM_ALLOWED_USER_ID: allowedUserId,
118
+ OPENCODE_SERVER_URL: serverUrl,
119
+ OPENCODE_SERVER_USERNAME: serverUsername,
120
+ OPENCODE_SERVER_PASSWORD: serverPassword,
121
+ OPENCODE_PROMPT_TIMEOUT_MS: "600000",
122
+ TELEGRAM_HANDLER_TIMEOUT_MS: "630000",
123
+ OPENCODE_RESTART_COMMAND: opencodeRestartCommand,
124
+ OPENCODE_RESTART_TIMEOUT_MS: opencodeRestartTimeoutMs,
125
+ OPENCODE_BRIDGE_RESTART_COMMAND: "systemctl --user restart opencode-telegram-bridge --no-block",
126
+ OPENCODE_BRIDGE_RESTART_TIMEOUT_MS: "30000",
127
+ });
128
+ const unitFile = buildUnitFile(envPath, execStart);
129
+ writeUnitFile(unitPath, unitFile);
130
+ runCommand("systemctl", ["--user", "daemon-reload"]);
131
+ runCommand("systemctl", ["--user", "enable", "--now", "opencode-telegram-bridge"]);
132
+ const enableLinger = await confirmAnswer("Enable linger so the service starts on boot without login?", false);
133
+ if (enableLinger) {
134
+ const user = os.userInfo().username;
135
+ const result = spawnSync("sudo", ["loginctl", "enable-linger", user], {
136
+ encoding: "utf8",
137
+ stdio: "inherit",
138
+ });
139
+ if (result.status !== 0) {
140
+ console.warn("Failed to enable linger. Run: sudo loginctl enable-linger");
141
+ }
142
+ }
143
+ console.log("Setup complete. Service is running.");
144
+ console.log(`Env file: ${envPath}`);
145
+ console.log(`Unit file: ${unitPath}`);
146
+ };
147
+ const command = process.argv[2];
148
+ if (!command) {
149
+ runBot();
150
+ }
151
+ else if (command === "setup") {
152
+ runSetupWizard().catch((error) => {
153
+ die(error instanceof Error ? error.message : "Setup failed.");
154
+ });
155
+ }
156
+ else {
157
+ die("Unknown command. Use: opencode-telegram-bridge [setup]");
158
+ }
159
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import "dotenv/config";
1
+ export {};
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js CHANGED
@@ -1,16 +1,3 @@
1
- import "dotenv/config";
2
- import { loadConfig } from "./config.js";
3
- import { startBot } from "./bot.js";
4
- import { createOpencodeBridge } from "./opencode.js";
5
- import { createProjectStore } from "./projects.js";
6
- import { createChatModelStore, createChatProjectStore, createPersistentSessionStore, } from "./state.js";
7
- const config = loadConfig();
8
- const sessionStore = createPersistentSessionStore();
9
- const opencode = createOpencodeBridge(config.opencode, { sessionStore });
10
- const projects = createProjectStore();
11
- const chatProjects = createChatProjectStore();
12
- const chatModels = createChatModelStore();
13
- const bot = startBot(config, opencode, projects, chatProjects, chatModels);
14
- process.once("SIGINT", () => bot.stop("SIGINT"));
15
- process.once("SIGTERM", () => bot.stop("SIGTERM"));
1
+ import { runBot } from "./run.js";
2
+ runBot();
16
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;AAC3B,MAAM,YAAY,GAAG,4BAA4B,EAAE,CAAA;AACnD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;AACxE,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAA;AACrC,MAAM,YAAY,GAAG,sBAAsB,EAAE,CAAA;AAC7C,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAA;AACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,CAAA;AAE1E,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;AAChD,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,MAAM,EAAE,CAAA"}
package/dist/run.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import "dotenv/config";
2
+ export declare const runBot: () => import("telegraf").Telegraf<import("telegraf").Context<import("@telegraf/types").Update>>;
3
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAYtB,eAAO,MAAM,MAAM,iGAalB,CAAA"}
package/dist/run.js ADDED
@@ -0,0 +1,19 @@
1
+ import "dotenv/config";
2
+ import { loadConfig } from "./config.js";
3
+ import { startBot } from "./bot.js";
4
+ import { createOpencodeBridge } from "./opencode.js";
5
+ import { createProjectStore } from "./projects.js";
6
+ import { createChatModelStore, createChatProjectStore, createPersistentSessionStore, } from "./state.js";
7
+ export const runBot = () => {
8
+ const config = loadConfig();
9
+ const sessionStore = createPersistentSessionStore();
10
+ const opencode = createOpencodeBridge(config.opencode, { sessionStore });
11
+ const projects = createProjectStore();
12
+ const chatProjects = createChatProjectStore();
13
+ const chatModels = createChatModelStore();
14
+ const bot = startBot(config, opencode, projects, chatProjects, chatModels);
15
+ process.once("SIGINT", () => bot.stop("SIGINT"));
16
+ process.once("SIGTERM", () => bot.stop("SIGTERM"));
17
+ return bot;
18
+ };
19
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,YAAY,CAAA;AAEnB,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,EAAE;IACzB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,YAAY,GAAG,4BAA4B,EAAE,CAAA;IACnD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;IACxE,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAA;IACrC,MAAM,YAAY,GAAG,sBAAsB,EAAE,CAAA;IAC7C,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAA;IACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,CAAA;IAE1E,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAChD,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAA;IAElD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
@@ -1,6 +1,6 @@
1
1
  # Configuration
2
2
 
3
- Set these environment variables in `.env` or in your service environment.
3
+ Set these environment variables in your shell or service environment.
4
4
 
5
5
  ## Required
6
6
  - `TELEGRAM_BOT_TOKEN` - Telegram bot token.
@@ -12,10 +12,13 @@ Set these environment variables in `.env` or in your service environment.
12
12
  - `OPENCODE_SERVER_PASSWORD` - Basic auth password.
13
13
  - `OPENCODE_PROMPT_TIMEOUT_MS` - Prompt timeout in milliseconds (default: 600000).
14
14
  - `TELEGRAM_HANDLER_TIMEOUT_MS` - Telegraf handler timeout in milliseconds (default: prompt timeout + 30000).
15
- - `OPENCODE_RESTART_COMMAND` - Command to restart OpenCode when `/reboot` is used (example: `sudo -n systemctl restart opencode`).
15
+ - `OPENCODE_RESTART_COMMAND` - Command to restart OpenCode when `/reboot` is used (example: `systemctl --user restart opencode --no-block`).
16
16
  - `OPENCODE_RESTART_TIMEOUT_MS` - Restart command timeout in milliseconds (default: 30000).
17
- - `OPENCODE_BRIDGE_RESTART_COMMAND` - Command to restart the Telegram bridge when `/restart` is used (example: `sudo -n systemctl restart opencode-telegram-bridge`).
17
+ - `OPENCODE_BRIDGE_RESTART_COMMAND` - Command to restart the Telegram bridge when `/restart` is used (example: `systemctl --user restart opencode-telegram-bridge --no-block`).
18
18
  - `OPENCODE_BRIDGE_RESTART_TIMEOUT_MS` - Restart command timeout in milliseconds (default: 30000).
19
19
 
20
20
  ## Data storage
21
21
  - Project aliases and chat selections are stored in `~/.opencode-telegram-bridge/projects.db`.
22
+
23
+ ## systemd user service
24
+ The setup wizard writes a user service env file at `~/.config/opencode-telegram-bridge/opencode-telegram-bridge.env`.
package/docs/index.md CHANGED
@@ -1,10 +1,9 @@
1
1
  # OpenCode Telegram Bridge
2
2
 
3
- Run a Telegram bot that forwards messages to an OpenCode backend and returns responses. This site is a minimal, markdown-first reference for operating the bridge.
3
+ Run a Telegram bot that forwards messages to an OpenCode backend and returns responses. These docs are for end users and aim to make setup and operation unambiguous.
4
4
 
5
5
  Start here:
6
6
  - Installation
7
7
  - Configuration
8
8
  - Usage
9
9
  - Systemd
10
- - Release
@@ -3,40 +3,21 @@
3
3
  ## Prerequisites
4
4
  - Node.js 18+
5
5
  - OpenCode CLI installed and available on PATH (see https://opencode.ai/docs/cli/)
6
+ - OpenCode server running (`opencode serve`)
6
7
 
7
- ## Install dependencies
8
+ ## Install
8
9
  ```bash
9
- npm install
10
+ npm install -g opencode-telegram-bridge
10
11
  ```
11
12
 
12
- ## Start OpenCode server
13
+ ## Setup (Linux systemd)
13
14
  ```bash
14
- opencode serve
15
+ opencode-telegram-bridge setup
15
16
  ```
16
17
 
17
- ## Configure environment
18
- Copy the example file and fill in values:
19
- ```bash
20
- cp .env.example .env
21
- ```
18
+ Setup currently supports Linux systemd only. On other platforms, run the bot manually.
22
19
 
23
20
  ## Run the bot
24
21
  ```bash
25
- npm run dev
26
- ```
27
-
28
- For production, build and run:
29
- ```bash
30
- npm run build
31
- npm start
32
- ```
33
-
34
- ## Install via npm
35
- ```bash
36
- npm install -g opencode-telegram-bridge
37
- ```
38
-
39
- Run:
40
- ```bash
41
22
  opencode-telegram-bridge
42
23
  ```
package/docs/systemd.md CHANGED
@@ -1,30 +1,35 @@
1
- # systemd Service (Linux)
1
+ # systemd Service (Linux user)
2
2
 
3
- This project ships a systemd unit template in `systemd/opencode-telegram-bridge.service`.
3
+ This project ships a user systemd unit template in `systemd/opencode-telegram-bridge.service`.
4
4
 
5
- ## Install
6
- 1. Build the project:
5
+ ## Recommended: setup wizard
6
+ ```bash
7
+ opencode-telegram-bridge setup
8
+ ```
9
+
10
+ ## Manual install
11
+ 1. Install the package:
7
12
 
8
13
  ```bash
9
- npm install
10
- npm run build
14
+ npm install -g opencode-telegram-bridge
11
15
  ```
12
16
 
13
17
  2. Copy the service and env files:
14
18
 
15
19
  ```bash
16
- sudo mkdir -p /etc/opencode-telegram-bridge
17
- sudo cp systemd/opencode-telegram-bridge.service /etc/systemd/system/opencode-telegram-bridge.service
18
- sudo cp systemd/opencode-telegram-bridge.env.example /etc/opencode-telegram-bridge.env
20
+ mkdir -p ~/.config/systemd/user
21
+ mkdir -p ~/.config/opencode-telegram-bridge
22
+ cp systemd/opencode-telegram-bridge.service ~/.config/systemd/user/opencode-telegram-bridge.service
23
+ cp systemd/opencode-telegram-bridge.env.example ~/.config/opencode-telegram-bridge/opencode-telegram-bridge.env
19
24
  ```
20
25
 
21
26
  3. Edit the env file:
22
27
 
23
28
  ```bash
24
- sudo nano /etc/opencode-telegram-bridge.env
29
+ nano ~/.config/opencode-telegram-bridge/opencode-telegram-bridge.env
25
30
  ```
26
31
 
27
- 4. Update the service paths if needed:
32
+ 4. Update the service path if needed:
28
33
 
29
34
  - `ExecStart=/usr/bin/opencode-telegram-bridge`
30
35
  - If you installed via npm in a non-standard location, set `ExecStart` to the output of `command -v opencode-telegram-bridge`.
@@ -32,11 +37,18 @@ sudo nano /etc/opencode-telegram-bridge.env
32
37
  5. Enable and start:
33
38
 
34
39
  ```bash
35
- sudo systemctl daemon-reload
36
- sudo systemctl enable --now opencode-telegram-bridge
40
+ systemctl --user daemon-reload
41
+ systemctl --user enable --now opencode-telegram-bridge
42
+ ```
43
+
44
+ ## Start on boot (optional)
45
+ User services normally start on login. To run on boot without a login session:
46
+
47
+ ```bash
48
+ sudo loginctl enable-linger $(whoami)
37
49
  ```
38
50
 
39
51
  ## Logs
40
52
  ```bash
41
- sudo journalctl -u opencode-telegram-bridge -f
53
+ journalctl --user -u opencode-telegram-bridge -f
42
54
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-telegram-bridge",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -6,6 +6,6 @@ OPENCODE_SERVER_PASSWORD=
6
6
  OPENCODE_PROMPT_TIMEOUT_MS=600000
7
7
  TELEGRAM_HANDLER_TIMEOUT_MS=630000
8
8
  OPENCODE_RESTART_COMMAND=
9
- OPENCODE_RESTART_TIMEOUT_MS=30000
10
- OPENCODE_BRIDGE_RESTART_COMMAND=
9
+ OPENCODE_RESTART_TIMEOUT_MS=
10
+ OPENCODE_BRIDGE_RESTART_COMMAND=systemctl --user restart opencode-telegram-bridge --no-block
11
11
  OPENCODE_BRIDGE_RESTART_TIMEOUT_MS=30000
@@ -5,10 +5,10 @@ Wants=network-online.target
5
5
 
6
6
  [Service]
7
7
  Type=simple
8
- EnvironmentFile=/etc/opencode-telegram-bridge.env
8
+ EnvironmentFile=%h/.config/opencode-telegram-bridge/opencode-telegram-bridge.env
9
9
  ExecStart=/usr/bin/opencode-telegram-bridge
10
10
  Restart=on-failure
11
11
  RestartSec=3
12
12
 
13
13
  [Install]
14
- WantedBy=multi-user.target
14
+ WantedBy=default.target
package/docs/release.md DELETED
@@ -1,112 +0,0 @@
1
- # Release
2
-
3
- This project is published as an npm package.
4
-
5
- ## Mental model
6
- Every CI run (PRs and pushes) requires a changeset for non-doc changes or it
7
- fails. A push to `main` creates or updates the release PR. A merged PR leads to
8
- a push to `main`, so it also updates the release PR. The release PR accumulates
9
- changesets until it is merged. When the release PR is merged, npm publishes a
10
- new version and a GitHub release is created using the accumulated changesets.
11
-
12
- ## CI + release flowcharts
13
-
14
- ### Pull request opened
15
- ```
16
- PR opened/updated
17
- |
18
- v
19
- CI workflow (pull_request)
20
- |
21
- +--> npm ci
22
- +--> changeset check (diff base...head)
23
- | |
24
- | +--> relevant change + no changeset -> FAIL
25
- | +--> docs-only or has changeset -> OK
26
- +--> typecheck + tests + build
27
- |
28
- v
29
- Status checks gate merge to main
30
- ```
31
-
32
- ### Push to main
33
- ```
34
- Push to main
35
- |
36
- v
37
- CI workflow (push)
38
- |
39
- +--> npm ci
40
- +--> changeset check (diff before...after)
41
- | |
42
- | +--> relevant change + no changeset -> FAIL
43
- | +--> docs-only or has changeset -> OK
44
- +--> typecheck + tests + build
45
- |
46
- v
47
- Release workflow (push)
48
- |
49
- +--> changesets/action
50
- |
51
- +--> changesets present?
52
- | |
53
- | +--> yes: create/update release PR (changeset-release/main)
54
- | | (no publish)
55
- | |
56
- | +--> no: run `npm run release` (publish to npm)
57
- |
58
- +--> if published: create GitHub release tag (unless it already exists)
59
- ```
60
-
61
- ## Manual release (local)
62
- 1. Add a changeset for your change:
63
-
64
- ```bash
65
- npx changeset
66
- ```
67
-
68
- 2. Version the release:
69
-
70
- ```bash
71
- npm run version
72
- ```
73
-
74
- 3. Publish:
75
-
76
- ```bash
77
- npm run release
78
- ```
79
-
80
- The `prepublishOnly` script runs the build to ensure `dist/` is included.
81
-
82
- ## Required setup (GitHub Actions)
83
- - Configure npm Trusted Publishing for this repo and workflow file (`release.yml`).
84
- - The workflow requires `id-token: write` permission to publish via OIDC.
85
- - npm CLI 11.5.1+ is required for trusted publishing.
86
-
87
- ## CI enforcement
88
- CI fails if a relevant code change lands without a `.changeset/*.md` file.
89
- Docs-only updates (`docs/`, `README.md`, `.github/`) do not require a changeset.
90
-
91
- ## FAQ
92
-
93
- ### How is the version bump decided?
94
- You pick it when you run `npx changeset`. The changeset file records whether the
95
- change is a `patch`, `minor`, or `major`. When you run `npm run version`, all
96
- pending changesets are read and the highest required bump wins.
97
-
98
- ### How is the changelog generated?
99
- The changelog is generated from the text in the changeset files, not from
100
- commits. Each changeset contributes a short release note entry.
101
-
102
- ### Does each merged PR cause a new package version?
103
- No. Merged PRs with changesets update the release PR. A new version is published
104
- only when the release PR is merged.
105
-
106
- ### What happens if I push to main without a changeset?
107
- CI fails. Add a changeset in a follow-up commit and push again. Do not rewrite
108
- history on main.
109
-
110
- ### Do I need one changeset per commit or per push?
111
- No. You need one changeset per release-worthy change (often one per PR). A
112
- single changeset can cover multiple commits.