owpenwork 0.1.1 → 0.1.3

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.
Files changed (4) hide show
  1. package/README.md +31 -30
  2. package/dist/cli.js +60 -20
  3. package/install.sh +63 -58
  4. package/package.json +5 -3
package/README.md CHANGED
@@ -10,7 +10,19 @@ One-command install (recommended):
10
10
  curl -fsSL https://raw.githubusercontent.com/different-ai/openwork/dev/packages/owpenbot/install.sh | bash
11
11
  ```
12
12
 
13
- Then follow the printed next steps (run `owpenbot setup`, link WhatsApp, start the bridge).
13
+ Or install from npm:
14
+
15
+ ```bash
16
+ npm install -g owpenwork
17
+ ```
18
+
19
+ Quick run without install:
20
+
21
+ ```bash
22
+ npx owpenwork
23
+ ```
24
+
25
+ Then follow the prompts (setup, QR login, start).
14
26
 
15
27
  1) One-command setup (installs deps, builds, creates `.env` if missing):
16
28
 
@@ -29,30 +41,18 @@ Recommended:
29
41
  - `OPENCODE_SERVER_USERNAME`
30
42
  - `OPENCODE_SERVER_PASSWORD`
31
43
 
32
- 3) Run setup (writes `~/.owpenbot/owpenbot.json`):
33
-
34
- ```bash
35
- owpenbot setup
36
- ```
37
-
38
- 4) Link WhatsApp (QR):
39
-
40
- ```bash
41
- owpenbot whatsapp login
42
- ```
43
-
44
- 5) Start the bridge:
44
+ 3) Run owpenwork and follow the guided setup:
45
45
 
46
46
  ```bash
47
- owpenbot start
47
+ owpenwork
48
48
  ```
49
49
 
50
- Owpenbot keeps the WhatsApp session alive once connected.
50
+ Owpenwork keeps the WhatsApp session alive once connected.
51
51
 
52
52
  6) Pair a user with the bot (only if DM policy is pairing):
53
53
 
54
- - Run `owpenbot pairing list` to view pending codes.
55
- - Approve a code: `owpenbot pairing approve <code>`.
54
+ - Run `owpenwork pairing list` to view pending codes.
55
+ - Approve a code: `owpenwork pairing approve <code>`.
56
56
  - The user can then message again to receive OpenCode replies.
57
57
 
58
58
  ## Usage Flows
@@ -61,8 +61,8 @@ Owpenbot keeps the WhatsApp session alive once connected.
61
61
 
62
62
  Use your own WhatsApp account as the bot and test from a second number you control.
63
63
 
64
- 1) Run `owpenbot setup` and choose “personal number.”
65
- 2) Run `owpenbot whatsapp login` to scan the QR.
64
+ 1) Run `owpenwork` and choose “personal number.”
65
+ 2) Scan the QR when prompted.
66
66
  3) Message yourself or from a second number; your number is already allowlisted.
67
67
 
68
68
  Note: WhatsApp’s “message yourself” thread is not reliable for bot testing.
@@ -72,9 +72,9 @@ Note: WhatsApp’s “message yourself” thread is not reliable for bot testing
72
72
  Use a separate WhatsApp number as the bot account so it stays independent from your personal chat history.
73
73
 
74
74
  1) Create a new WhatsApp account for the dedicated number.
75
- 2) Run `owpenbot setup` and choose “dedicated number.”
76
- 3) Run `owpenbot whatsapp login` to scan the QR.
77
- 4) If DM policy is pairing, approve codes with `owpenbot pairing approve <code>`.
75
+ 2) Run `owpenwork` and choose “dedicated number.”
76
+ 3) Scan the QR when prompted.
77
+ 4) If DM policy is pairing, approve codes with `owpenwork pairing approve <code>`.
78
78
 
79
79
  ## Telegram (Untested)
80
80
 
@@ -85,18 +85,17 @@ Telegram support is wired but not E2E tested yet. To try it:
85
85
  ## Commands
86
86
 
87
87
  ```bash
88
- owpenbot setup
89
- owpenbot whatsapp login
90
- owpenbot start
91
- owpenbot pairing list
92
- owpenbot pairing approve <code>
93
- owpenbot status
88
+ owpenwork
89
+ owpenwork --non-interactive
90
+ owpenwork pairing list
91
+ owpenwork pairing approve <code>
92
+ owpenwork status
94
93
  ```
95
94
 
96
95
  ## Defaults
97
96
 
98
97
  - SQLite at `~/.owpenbot/owpenbot.db` unless overridden.
99
- - Config stored at `~/.owpenbot/owpenbot.json` (created by `owpenbot setup`).
98
+ - Config stored at `~/.owpenbot/owpenbot.json` (created by `owpenwork` or `owpenwork setup`).
100
99
  - DM policy defaults to `pairing` unless changed in setup.
101
100
  - Group chats are disabled unless `GROUPS_ENABLED=true`.
102
101
 
@@ -105,4 +104,6 @@ owpenbot status
105
104
  ```bash
106
105
  pnpm -C packages/owpenbot test:unit
107
106
  pnpm -C packages/owpenbot test:smoke
107
+ pnpm -C packages/owpenbot test:cli
108
+ pnpm -C packages/owpenbot test:npx
108
109
  ```
package/dist/cli.js CHANGED
@@ -1,4 +1,6 @@
1
+ #!/usr/bin/env node
1
2
  import fs from "node:fs";
3
+ import path from "node:path";
2
4
  import { createInterface } from "node:readline/promises";
3
5
  import { Command } from "commander";
4
6
  import { startBridge } from "./bridge.js";
@@ -10,7 +12,9 @@ const program = new Command();
10
12
  program
11
13
  .name("owpenbot")
12
14
  .description("OpenCode WhatsApp + Telegram bridge")
13
- .argument("[path]");
15
+ .argument("[path]")
16
+ .option("--non-interactive", "Run setup defaults and exit", false)
17
+ .option("--check", "Validate config/auth and exit", false);
14
18
  const runStart = async (pathOverride) => {
15
19
  if (pathOverride?.trim()) {
16
20
  process.env.OPENCODE_DIRECTORY = pathOverride.trim();
@@ -21,7 +25,7 @@ const runStart = async (pathOverride) => {
21
25
  process.env.OPENCODE_DIRECTORY = config.opencodeDirectory;
22
26
  }
23
27
  const bridge = await startBridge(config, logger);
24
- logger.info("Commands: owpenbot whatsapp login, owpenbot pairing list, owpenbot status");
28
+ logger.info("Commands: owpenwork whatsapp login, owpenwork pairing list, owpenwork status");
25
29
  const shutdown = async () => {
26
30
  logger.info("shutting down");
27
31
  await bridge.stop();
@@ -30,26 +34,10 @@ const runStart = async (pathOverride) => {
30
34
  process.on("SIGINT", shutdown);
31
35
  process.on("SIGTERM", shutdown);
32
36
  };
33
- program
34
- .command("start")
35
- .description("Start the bridge")
36
- .action(() => runStart());
37
- program.action((pathArg) => {
38
- if (process.argv.includes("--help") || process.argv.includes("-h")) {
39
- program.outputHelp();
40
- return;
41
- }
42
- return runStart(pathArg);
43
- });
44
- program
45
- .command("setup")
46
- .description("Create or update owpenbot.json for WhatsApp")
47
- .option("--non-interactive", "Write defaults without prompts", false)
48
- .action(async (opts) => {
49
- const config = loadConfig(process.env, { requireOpencode: false });
37
+ async function runSetupWizard(config, nonInteractive) {
50
38
  const { config: existing } = readConfigFile(config.configPath);
51
39
  const next = existing ?? { version: 1 };
52
- if (opts.nonInteractive) {
40
+ if (nonInteractive) {
53
41
  next.version = 1;
54
42
  next.channels = next.channels ?? {};
55
43
  next.channels.whatsapp = {
@@ -124,6 +112,58 @@ program
124
112
  };
125
113
  writeConfigFile(config.configPath, next);
126
114
  console.log(`Wrote ${config.configPath}`);
115
+ }
116
+ async function runGuidedFlow(pathArg, opts) {
117
+ if (pathArg?.trim()) {
118
+ process.env.OPENCODE_DIRECTORY = pathArg.trim();
119
+ }
120
+ let config = loadConfig(process.env, { requireOpencode: false });
121
+ if (!process.env.OPENCODE_DIRECTORY) {
122
+ process.env.OPENCODE_DIRECTORY = config.opencodeDirectory;
123
+ }
124
+ if (opts.check) {
125
+ const authPath = path.join(config.whatsappAuthDir, "creds.json");
126
+ const linked = fs.existsSync(authPath);
127
+ const cfgExists = fs.existsSync(config.configPath);
128
+ console.log(`Config: ${cfgExists ? "yes" : "no"}`);
129
+ console.log(`WhatsApp linked: ${linked ? "yes" : "no"}`);
130
+ process.exit(linked && cfgExists ? 0 : 1);
131
+ }
132
+ const cfgExists = fs.existsSync(config.configPath);
133
+ const cfgHasWhatsApp = config.configFile.channels?.whatsapp;
134
+ if (!cfgExists || !cfgHasWhatsApp) {
135
+ await runSetupWizard(config, opts.nonInteractive);
136
+ if (opts.nonInteractive) {
137
+ console.log("Next: owpenwork whatsapp login");
138
+ return;
139
+ }
140
+ }
141
+ config = loadConfig(process.env, { requireOpencode: false });
142
+ const authPath = path.join(config.whatsappAuthDir, "creds.json");
143
+ if (!fs.existsSync(authPath)) {
144
+ await loginWhatsApp(config, createLogger(config.logLevel));
145
+ }
146
+ await runStart(pathArg);
147
+ }
148
+ program
149
+ .command("start")
150
+ .description("Start the bridge")
151
+ .action(() => runStart());
152
+ program.action((pathArg) => {
153
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
154
+ program.outputHelp();
155
+ return;
156
+ }
157
+ const opts = program.opts();
158
+ return runGuidedFlow(pathArg, { nonInteractive: Boolean(opts.nonInteractive), check: Boolean(opts.check) });
159
+ });
160
+ program
161
+ .command("setup")
162
+ .description("Create or update owpenbot.json for WhatsApp")
163
+ .option("--non-interactive", "Write defaults without prompts", false)
164
+ .action(async (opts) => {
165
+ const config = loadConfig(process.env, { requireOpencode: false });
166
+ await runSetupWizard(config, Boolean(opts.nonInteractive));
127
167
  });
128
168
  program
129
169
  .command("pairing-code")
package/install.sh CHANGED
@@ -5,6 +5,7 @@ OWPENBOT_REF="${OWPENBOT_REF:-dev}"
5
5
  OWPENBOT_REPO="${OWPENBOT_REPO:-https://github.com/different-ai/openwork.git}"
6
6
  OWPENBOT_INSTALL_DIR="${OWPENBOT_INSTALL_DIR:-$HOME/.owpenbot/openwork}"
7
7
  OWPENBOT_BIN_DIR="${OWPENBOT_BIN_DIR:-$HOME/.local/bin}"
8
+ OWPENBOT_INSTALL_METHOD="${OWPENBOT_INSTALL_METHOD:-npm}"
8
9
 
9
10
  usage() {
10
11
  cat <<'EOF'
@@ -15,6 +16,7 @@ Environment variables:
15
16
  OWPENBOT_REPO Git repo (default: https://github.com/different-ai/openwork.git)
16
17
  OWPENBOT_REF Git ref/branch (default: dev)
17
18
  OWPENBOT_BIN_DIR Bin directory for owpenbot shim (default: ~/.local/bin)
19
+ OWPENBOT_INSTALL_METHOD Install method: npm|git (default: npm)
18
20
 
19
21
  Example:
20
22
  OWPENBOT_INSTALL_DIR=~/owpenbot curl -fsSL https://raw.githubusercontent.com/different-ai/openwork/dev/packages/owpenbot/install.sh | bash
@@ -33,82 +35,87 @@ require_bin() {
33
35
  fi
34
36
  }
35
37
 
36
- require_bin git
37
38
  require_bin node
38
39
 
39
- if ! command -v pnpm >/dev/null 2>&1; then
40
- if command -v corepack >/dev/null 2>&1; then
41
- corepack enable >/dev/null 2>&1 || true
42
- corepack prepare pnpm@10.27.0 --activate
43
- else
44
- echo "pnpm is required. Install pnpm or enable corepack, then retry." >&2
45
- exit 1
40
+ if [[ "$OWPENBOT_INSTALL_METHOD" == "npm" ]]; then
41
+ echo "Installing owpenwork via npm..."
42
+ npm install -g owpenwork
43
+ else
44
+ require_bin git
45
+ if ! command -v pnpm >/dev/null 2>&1; then
46
+ if command -v corepack >/dev/null 2>&1; then
47
+ corepack enable >/dev/null 2>&1 || true
48
+ corepack prepare pnpm@10.27.0 --activate
49
+ else
50
+ echo "pnpm is required. Install pnpm or enable corepack, then retry." >&2
51
+ exit 1
52
+ fi
46
53
  fi
47
- fi
48
54
 
49
- if [[ -d "$OWPENBOT_INSTALL_DIR/.git" ]]; then
50
- echo "Updating owpenbot source in $OWPENBOT_INSTALL_DIR"
51
- git -C "$OWPENBOT_INSTALL_DIR" fetch origin --prune
52
- if git -C "$OWPENBOT_INSTALL_DIR" show-ref --verify --quiet "refs/remotes/origin/$OWPENBOT_REF"; then
53
- git -C "$OWPENBOT_INSTALL_DIR" checkout -B "$OWPENBOT_REF" "origin/$OWPENBOT_REF"
54
- git -C "$OWPENBOT_INSTALL_DIR" pull --ff-only origin "$OWPENBOT_REF"
55
+ if [[ -d "$OWPENBOT_INSTALL_DIR/.git" ]]; then
56
+ echo "Updating owpenbot source in $OWPENBOT_INSTALL_DIR"
57
+ git -C "$OWPENBOT_INSTALL_DIR" fetch origin --prune
58
+ if git -C "$OWPENBOT_INSTALL_DIR" show-ref --verify --quiet "refs/remotes/origin/$OWPENBOT_REF"; then
59
+ git -C "$OWPENBOT_INSTALL_DIR" checkout -B "$OWPENBOT_REF" "origin/$OWPENBOT_REF"
60
+ git -C "$OWPENBOT_INSTALL_DIR" pull --ff-only origin "$OWPENBOT_REF"
61
+ else
62
+ git -C "$OWPENBOT_INSTALL_DIR" checkout -f
63
+ git -C "$OWPENBOT_INSTALL_DIR" pull --ff-only
64
+ fi
55
65
  else
56
- git -C "$OWPENBOT_INSTALL_DIR" checkout -f
57
- git -C "$OWPENBOT_INSTALL_DIR" pull --ff-only
66
+ echo "Cloning owpenbot source to $OWPENBOT_INSTALL_DIR"
67
+ mkdir -p "$OWPENBOT_INSTALL_DIR"
68
+ git clone --depth 1 "$OWPENBOT_REPO" "$OWPENBOT_INSTALL_DIR"
69
+ if git -C "$OWPENBOT_INSTALL_DIR" show-ref --verify --quiet "refs/remotes/origin/$OWPENBOT_REF"; then
70
+ git -C "$OWPENBOT_INSTALL_DIR" checkout -B "$OWPENBOT_REF" "origin/$OWPENBOT_REF"
71
+ fi
58
72
  fi
59
- else
60
- echo "Cloning owpenbot source to $OWPENBOT_INSTALL_DIR"
61
- mkdir -p "$OWPENBOT_INSTALL_DIR"
62
- git clone --depth 1 "$OWPENBOT_REPO" "$OWPENBOT_INSTALL_DIR"
63
- if git -C "$OWPENBOT_INSTALL_DIR" show-ref --verify --quiet "refs/remotes/origin/$OWPENBOT_REF"; then
64
- git -C "$OWPENBOT_INSTALL_DIR" checkout -B "$OWPENBOT_REF" "origin/$OWPENBOT_REF"
65
- fi
66
- fi
67
73
 
68
- if [[ ! -d "$OWPENBOT_INSTALL_DIR/packages/owpenbot" ]]; then
69
- echo "owpenbot package not found on ref '$OWPENBOT_REF'. Trying dev/main..." >&2
70
- git -C "$OWPENBOT_INSTALL_DIR" fetch origin --prune
71
- if git -C "$OWPENBOT_INSTALL_DIR" show-ref --verify --quiet refs/remotes/origin/dev; then
72
- git -C "$OWPENBOT_INSTALL_DIR" checkout -B dev origin/dev
73
- elif git -C "$OWPENBOT_INSTALL_DIR" show-ref --verify --quiet refs/remotes/origin/main; then
74
- git -C "$OWPENBOT_INSTALL_DIR" checkout -B main origin/main
74
+ if [[ ! -d "$OWPENBOT_INSTALL_DIR/packages/owpenbot" ]]; then
75
+ echo "owpenbot package not found on ref '$OWPENBOT_REF'. Trying dev/main..." >&2
76
+ git -C "$OWPENBOT_INSTALL_DIR" fetch origin --prune
77
+ if git -C "$OWPENBOT_INSTALL_DIR" show-ref --verify --quiet refs/remotes/origin/dev; then
78
+ git -C "$OWPENBOT_INSTALL_DIR" checkout -B dev origin/dev
79
+ elif git -C "$OWPENBOT_INSTALL_DIR" show-ref --verify --quiet refs/remotes/origin/main; then
80
+ git -C "$OWPENBOT_INSTALL_DIR" checkout -B main origin/main
81
+ fi
75
82
  fi
76
- fi
77
83
 
78
- if [[ ! -d "$OWPENBOT_INSTALL_DIR/packages/owpenbot" ]]; then
79
- echo "owpenbot package not found after checkout. Aborting." >&2
80
- exit 1
81
- fi
84
+ if [[ ! -d "$OWPENBOT_INSTALL_DIR/packages/owpenbot" ]]; then
85
+ echo "owpenbot package not found after checkout. Aborting." >&2
86
+ exit 1
87
+ fi
82
88
 
83
- echo "Installing dependencies..."
84
- pnpm -C "$OWPENBOT_INSTALL_DIR" install
89
+ echo "Installing dependencies..."
90
+ pnpm -C "$OWPENBOT_INSTALL_DIR" install
85
91
 
86
- echo "Building owpenbot..."
87
- pnpm -C "$OWPENBOT_INSTALL_DIR/packages/owpenbot" build
92
+ echo "Building owpenbot..."
93
+ pnpm -C "$OWPENBOT_INSTALL_DIR/packages/owpenbot" build
88
94
 
89
- ENV_PATH="$OWPENBOT_INSTALL_DIR/packages/owpenbot/.env"
90
- ENV_EXAMPLE="$OWPENBOT_INSTALL_DIR/packages/owpenbot/.env.example"
91
- if [[ ! -f "$ENV_PATH" ]]; then
92
- if [[ -f "$ENV_EXAMPLE" ]]; then
93
- cp "$ENV_EXAMPLE" "$ENV_PATH"
94
- echo "Created $ENV_PATH"
95
- else
96
- cat <<EOF > "$ENV_PATH"
95
+ ENV_PATH="$OWPENBOT_INSTALL_DIR/packages/owpenbot/.env"
96
+ ENV_EXAMPLE="$OWPENBOT_INSTALL_DIR/packages/owpenbot/.env.example"
97
+ if [[ ! -f "$ENV_PATH" ]]; then
98
+ if [[ -f "$ENV_EXAMPLE" ]]; then
99
+ cp "$ENV_EXAMPLE" "$ENV_PATH"
100
+ echo "Created $ENV_PATH"
101
+ else
102
+ cat <<EOF > "$ENV_PATH"
97
103
  OPENCODE_URL=http://127.0.0.1:4096
98
104
  OPENCODE_DIRECTORY=
99
105
  WHATSAPP_AUTH_DIR=~/.owpenbot/whatsapp
100
106
  EOF
101
- echo "Created $ENV_PATH (minimal)"
107
+ echo "Created $ENV_PATH (minimal)"
108
+ fi
102
109
  fi
103
- fi
104
110
 
105
- mkdir -p "$OWPENBOT_BIN_DIR"
106
- cat <<EOF > "$OWPENBOT_BIN_DIR/owpenbot"
111
+ mkdir -p "$OWPENBOT_BIN_DIR"
112
+ cat <<EOF > "$OWPENBOT_BIN_DIR/owpenbot"
107
113
  #!/usr/bin/env bash
108
114
  set -euo pipefail
109
115
  node "$OWPENBOT_INSTALL_DIR/packages/owpenbot/dist/cli.js" "$@"
110
116
  EOF
111
- chmod 755 "$OWPENBOT_BIN_DIR/owpenbot"
117
+ chmod 755 "$OWPENBOT_BIN_DIR/owpenbot"
118
+ fi
112
119
 
113
120
  if ! echo ":$PATH:" | grep -q ":$OWPENBOT_BIN_DIR:"; then
114
121
  shell_name="$(basename "${SHELL:-}" 2>/dev/null || true)"
@@ -133,10 +140,8 @@ cat <<EOF
133
140
  Owpenbot installed.
134
141
 
135
142
  Next steps:
136
- 1) Edit $ENV_PATH (optional)
137
- 2) Run setup: owpenbot setup
138
- 3) Link WhatsApp: owpenbot whatsapp login
139
- 4) Start bridge: owpenbot start
143
+ 1) Run owpenwork: owpenwork
144
+ 2) Follow the guided setup + QR login
140
145
 
141
146
  Owpenbot will print a QR code during login and keep the session alive.
142
147
  EOF
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "owpenwork",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "WhatsApp bridge for a running OpenCode server",
5
5
  "private": false,
6
6
  "type": "module",
@@ -11,7 +11,7 @@
11
11
  "license": "MIT",
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "https://github.com/different-ai/openwork.git",
14
+ "url": "git+https://github.com/different-ai/openwork.git",
15
15
  "directory": "packages/owpenbot"
16
16
  },
17
17
  "keywords": [
@@ -35,7 +35,9 @@
35
35
  "typecheck": "tsc -p tsconfig.json --noEmit",
36
36
  "setup": "node scripts/setup.mjs",
37
37
  "test:unit": "pnpm build && node --test test/*.test.js",
38
- "test:smoke": "node scripts/smoke.mjs"
38
+ "test:smoke": "node scripts/smoke.mjs",
39
+ "test:cli": "pnpm build && node scripts/test-cli.mjs",
40
+ "test:npx": "node scripts/test-npx.mjs"
39
41
  },
40
42
  "dependencies": {
41
43
  "@opencode-ai/sdk": "^1.1.19",