viber-channel 0.8.9 → 0.8.10

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.
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Bow-out decision: should this process yield the channel to the PLUGIN copy? (#365, #495)
3
+ *
4
+ * WHY THE GUARD EXISTS (#365 / #280 step-29). In an AGENT session (a vibe-master
5
+ * terminal) the channel must be the PLUGIN copy — only it can inject inbound
6
+ * messages. The manually-registered viber-dev-channel MCP loads in the same
7
+ * session with the same env; rather than race it for the channel lock, it bows
8
+ * out deterministically. Normal sessions (no VIBER_CHANNEL_AGENT_SESSION) are
9
+ * unaffected.
10
+ *
11
+ * WHY IT ONLY CONCERNS THE MCP SERVER MODE (#495). A CLI subcommand
12
+ * (`enroll-runner`, `run-runner`, `connect`, `--help`) serves no tool and takes
13
+ * no channel lock, so it cannot conflict with the plugin copy. Evaluating this
14
+ * guard before the subcommand dispatch made every subcommand exit 0 without
15
+ * doing anything inside an agent session — silent, and falsely successful. The
16
+ * call site therefore sits AFTER the dispatch block; see viber-channel.ts.
17
+ *
18
+ * The dev-backend exclusion is DERIVED from the environment (single source of
19
+ * truth, isDevBackend) rather than trusting VIBER_CHANNEL_AGENT_SESSION not to
20
+ * leak from the parent shell: on the dev backend there is NO plugin to yield to
21
+ * (it is disabled in this repo, #365), so yielding would mean no channel at all
22
+ * (`-32000`, no registration).
23
+ */
24
+ import { isDevBackend } from "./auth.ts";
25
+
26
+ /**
27
+ * Pure function of the environment — takes the env explicitly so it is testable
28
+ * without mutating `process.env`.
29
+ *
30
+ * `VIBER_BASE_URL` is defaulted to `""` HERE, deliberately: `isDevBackend()`
31
+ * falls back to `process.env` when its argument is `undefined`, which would let
32
+ * the real environment of a test runner decide the outcome.
33
+ */
34
+ export function shouldBowOutToPlugin(
35
+ env: Record<string, string | undefined>,
36
+ ): boolean {
37
+ return (
38
+ !isDevBackend(env.VIBER_BASE_URL ?? "") &&
39
+ env.VIBER_CHANNEL_AGENT_SESSION === "1" &&
40
+ env.VIBER_CHANNEL_PLUGIN !== "1"
41
+ );
42
+ }
43
+
44
+ /** The stderr line emitted when the process bows out. Shared with the tests. */
45
+ export const BOW_OUT_MESSAGE =
46
+ "[viber-channel] agent session: yielding to the plugin channel copy (manual MCP registration exits)\n";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viber-channel",
3
- "version": "0.8.9",
3
+ "version": "0.8.10",
4
4
  "description": "Voice + text MCP channel between a Claude Code session and the Viber UI (https://viber.dgypx.dev). Push transcripts to Claude; send_message tool delivers text back to the UI.",
5
5
  "type": "module",
6
6
  "bin": {
package/viber-channel.ts CHANGED
@@ -61,6 +61,7 @@ import { buildChannelInstructions, IDENTITY_BUDGET } from "./lib/channel_instruc
61
61
  import { CLAUDE_TOOL_DEFS } from "./lib/claude_tool_defs.ts";
62
62
  import { warnIfStale } from "./lib/version_check.ts";
63
63
  import { capabilitiesText } from "./lib/capabilities.ts";
64
+ import { BOW_OUT_MESSAGE, shouldBowOutToPlugin } from "./lib/cli_bow_out.ts";
64
65
 
65
66
  // ---- CLI subcommand dispatch (must happen before lock acquire + loadAuth) ----
66
67
  //
@@ -68,32 +69,6 @@ import { capabilitiesText } from "./lib/capabilities.ts";
68
69
  // exits — it does not start the MCP server. Any other invocation (no args, or
69
70
  // the unknown subcommand) falls through to the MCP server below.
70
71
  {
71
- // #280 step-29: in an AGENT session (vibe-master terminal), the channel must
72
- // be the PLUGIN copy (`--channels plugin:viber-channel@…` — only it can
73
- // inject inbound messages). The manually-registered viber-dev-channel MCP
74
- // loads in the same session with the same env; instead of racing it for the
75
- // channel lock, it bows out deterministically here. Normal sessions
76
- // (no VIBER_CHANNEL_AGENT_SESSION) are unaffected.
77
- //
78
- // BUT the yield is a STAGING/PROD-only behavior: the plugin channel only runs
79
- // there (it is disabled in the Viber dev repo, #365). On the DEV backend there
80
- // is NO plugin to yield to, so yielding = no channel at all (`-32000`, no
81
- // registration). Derive this from the environment (single source of truth,
82
- // isDevBackend) instead of relying on VIBER_CHANNEL_AGENT_SESSION not leaking
83
- // from the parent shell into the spawned dev terminal (the real bug: that var
84
- // leaked and terminal.ts never cleared it, so the dev channel silently bowed
85
- // out). Default (non-dev) behavior is unchanged.
86
- if (
87
- !isDevBackend() &&
88
- process.env.VIBER_CHANNEL_AGENT_SESSION === "1" &&
89
- process.env.VIBER_CHANNEL_PLUGIN !== "1"
90
- ) {
91
- process.stderr.write(
92
- "[viber-channel] agent session: yielding to the plugin channel copy (manual MCP registration exits)\n",
93
- );
94
- process.exit(0);
95
- }
96
-
97
72
  const sub = process.argv[2];
98
73
  if (sub === "--help" || sub === "-h") {
99
74
  process.stdout.write(
@@ -165,6 +140,23 @@ import { capabilitiesText } from "./lib/capabilities.ts";
165
140
  }
166
141
  }
167
142
 
143
+ // ---- Bow-out guard (#365) — MCP SERVER MODE ONLY ----
144
+ //
145
+ // THIS ORDER IS SIGNIFICANT (#495): the guard must stay AFTER the dispatch block
146
+ // above. Every branch of that block ends in `process.exit` on every path (catch
147
+ // included), so reaching this line means "no subcommand was handled" — i.e. this
148
+ // is an MCP server launch, the only mode where yielding to the plugin copy makes
149
+ // sense. Evaluating it earlier (as it was) made `enroll-runner`, `run-runner` and
150
+ // `connect` exit 0 without doing anything inside an agent session: silent, and
151
+ // falsely successful. Do NOT hoist this back up. Rationale: lib/cli_bow_out.ts.
152
+ //
153
+ // It also sits BEFORE warnIfStale() so the yielding process makes no network
154
+ // call on its way out.
155
+ if (shouldBowOutToPlugin(process.env)) {
156
+ process.stderr.write(BOW_OUT_MESSAGE);
157
+ process.exit(0);
158
+ }
159
+
168
160
  // Server path (fell through the dispatch block): warn if this install is behind
169
161
  // npm latest, but fire-and-forget so it never delays mcp.connect — the nudge
170
162
  // lands on stderr shortly after startup (#330).
@@ -363,9 +355,10 @@ process.stdin.on("end", () => {
363
355
 
364
356
  // ---- Auth from .viber/auth.json ----
365
357
 
358
+ // isDevBackend is no longer imported here: its only use in this file was the
359
+ // bow-out condition, now owned by lib/cli_bow_out.ts (#495).
366
360
  import {
367
361
  authFilePath,
368
- isDevBackend,
369
362
  loadAuth,
370
363
  persistInstanceCredentials,
371
364
  } from "./lib/auth.ts";