acbridge 0.0.1 → 1.0.1
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 +40 -0
- package/README.md +79 -3
- package/dist/acbridge.mjs +321 -0
- package/hub/dist/hub.cjs +189 -0
- package/package.json +32 -4
- package/plugin/.claude-plugin/marketplace.json +17 -0
- package/plugin/README.md +223 -0
- package/plugin/plugins/alexa/.claude-plugin/plugin.json +13 -0
- package/plugin/plugins/alexa/.mcp.json +8 -0
- package/plugin/plugins/alexa/dist/channel.mjs +165 -0
- package/plugin/plugins/alexa/hooks/hooks.json +166 -0
- package/plugin/plugins/alexa/scripts/codex-signal.mjs +406 -0
- package/plugin/plugins/alexa/scripts/gate.mjs +99 -0
- package/plugin/plugins/alexa/scripts/scope.mjs +270 -0
- package/plugin/plugins/alexa/scripts/signal.mjs +503 -0
- package/plugin/plugins/alexa/scripts/statusline-guard.mjs +75 -0
- package/plugin/plugins/alexa/scripts/statusline.mjs +322 -0
package/package.json
CHANGED
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "acbridge",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "AC Bridge
|
|
5
|
-
"homepage": "https://ac-bridge.com",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Control AC Bridge from any terminal: sign in, run the bridge, and drive smart-home devices and Profiles.",
|
|
6
5
|
"license": "UNLICENSED",
|
|
7
|
-
"
|
|
6
|
+
"author": "AC BRIDGE LIMITED",
|
|
7
|
+
"homepage": "https://ac-bridge.com",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"email": "support@ac-bridge.com"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"ac-bridge",
|
|
13
|
+
"smart-home",
|
|
14
|
+
"home-automation",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"codex"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"acbridge": "dist/acbridge.mjs"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist/acbridge.mjs",
|
|
30
|
+
"plugin",
|
|
31
|
+
"hub/dist/hub.cjs"
|
|
32
|
+
],
|
|
33
|
+
"optionalDependencies": {
|
|
34
|
+
"@napi-rs/keyring": "^1.3.0"
|
|
35
|
+
}
|
|
8
36
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bridge",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "AC BRIDGE LIMITED"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "Voice-bridge channels for Claude Code — drive a session by voice through Alexa.",
|
|
8
|
+
"version": "0.2.0"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "alexa",
|
|
13
|
+
"source": "./plugins/alexa",
|
|
14
|
+
"description": "Alexa voice-bridge channel: inject prompts, read replies back, and approve permission prompts by voice."
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
package/plugin/README.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# plugin/ — the AC Bridge Claude Code plugin
|
|
2
|
+
|
|
3
|
+
A **Claude Code plugin marketplace** named `bridge` whose single `alexa` plugin is *how the channel
|
|
4
|
+
MCP server actually reaches Claude Code*. It bundles the channel (`../channel/`) as an MCP stdio
|
|
5
|
+
server plus two hooks — a **PreToolUse permission-gate** and a **session-state signal** feed for
|
|
6
|
+
Profiles (see [`../docs/CLAUDE-SIGNALS.md`](../docs/CLAUDE-SIGNALS.md)). Installed once per machine by `acbridge setup`,
|
|
7
|
+
then loaded on every session with `claude --channels plugin:alexa@bridge` — **no `--dangerously-*`
|
|
8
|
+
flag**. This directory is the *packaging* of the `channel/` workspace for that installed-plugin path.
|
|
9
|
+
|
|
10
|
+
This same `plugins/alexa/scripts/` directory also carries `codex-signal.mjs` — the Codex CLI sibling
|
|
11
|
+
of `signal.mjs` (below). It is **not** registered in this plugin's own `hooks/hooks.json`; Codex CLI
|
|
12
|
+
has its own hooks engine entirely, and `acbridge setup` installs `codex-signal.mjs` directly into
|
|
13
|
+
Codex's `~/.codex/hooks.json` instead (§ below).
|
|
14
|
+
|
|
15
|
+
## Layout
|
|
16
|
+
|
|
17
|
+
| Path (under `plugin/`) | Role |
|
|
18
|
+
|------------------------|------|
|
|
19
|
+
| `.claude-plugin/marketplace.json` | Marketplace manifest — declares the marketplace `name: "bridge"` and lists its one plugin, `alexa` (`source: ./plugins/alexa`). |
|
|
20
|
+
| `plugins/alexa/.claude-plugin/plugin.json` | Plugin manifest — `name: "alexa"`, `version 0.1.0`; declares a `channels` capability bound to the `alexa` MCP server. |
|
|
21
|
+
| `plugins/alexa/.mcp.json` | MCP server definition — spawns the channel as `node ${CLAUDE_PLUGIN_ROOT}/dist/channel.mjs`. |
|
|
22
|
+
| `plugins/alexa/hooks/hooks.json` | Registers the `PreToolUse` gate (`→ scripts/gate.mjs`) **plus** the Profiles session-state signals on `SessionStart`/`SessionEnd`/`UserPromptSubmit`/`Stop`/`StopFailure`/`Notification`/`PreToolUse[AskUserQuestion\|ExitPlanMode\|Workflow]`/`PostToolUse`/`PostToolUseFailure`/`SubagentStart`/`SubagentStop`/`PreCompact`/`PostCompact`/`PermissionDenied`/`TaskCompleted` (all `→ scripts/signal.mjs`). |
|
|
23
|
+
| `plugins/alexa/scripts/gate.mjs` | The PreToolUse permission gate (below). |
|
|
24
|
+
| `plugins/alexa/scripts/signal.mjs` | The Profiles session-state signal — maps each lifecycle hook → one of the 28 wire states (+ on every POST: a transcript-tail `context` reading and the raw **mode/model ferry** — the last `permission-mode` record + assistant model id, which the hub diffs into the mode/`modelChanged` states) → `POST /local/session/state`; successful tools → a state-less tick on `POST /local/session/context` (below). |
|
|
25
|
+
| `plugins/alexa/scripts/statusline.mjs` | The statusline TEE — installed as Claude's `statusLine` command by `acbridge` (reversible; chains any existing statusline): POSTs the exact context % + rate-limit % + `model.id`/`display_name` to `/local/session/context` (the `usageHigh` data feed and the freshest `modelChanged` source), then renders. |
|
|
26
|
+
| `plugins/alexa/scripts/codex-signal.mjs` | The Codex CLI sibling of `signal.mjs` (below) — installed into `~/.codex/hooks.json`, **not** this plugin's `hooks/hooks.json`. |
|
|
27
|
+
| `plugins/alexa/dist/channel.mjs` | The bundled channel MCP server — **build artifact** copied from `../channel/` (gitignored; absent until built). |
|
|
28
|
+
|
|
29
|
+
`${CLAUDE_PLUGIN_ROOT}` is set by Claude Code to the installed plugin's root, so both the `.mcp.json`
|
|
30
|
+
server and the hook resolve relative to wherever the plugin was installed.
|
|
31
|
+
|
|
32
|
+
## How it's built
|
|
33
|
+
|
|
34
|
+
`../channel/build.mjs` bundles `channel/src/index.ts` into one self-contained minified ESM file at
|
|
35
|
+
`channel/dist/channel.mjs` (esbuild, `format: esm`, `target: node22`), then **copies that same
|
|
36
|
+
bundle** to `plugin/plugins/alexa/dist/channel.mjs`:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
// channel/build.mjs
|
|
40
|
+
const pluginDist = "../plugin/plugins/alexa/dist";
|
|
41
|
+
copyFileSync("dist/channel.mjs", `${pluginDist}/channel.mjs`);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Both `dist/` outputs are gitignored — regenerate with the channel workspace build. The plugin's
|
|
45
|
+
`.mcp.json` loads exactly this file via `${CLAUDE_PLUGIN_ROOT}/dist/channel.mjs`.
|
|
46
|
+
|
|
47
|
+
## How it's installed & loaded
|
|
48
|
+
|
|
49
|
+
**Installed-plugin path (normal).** `acbridge setup` (`cli/src/commands/setup.ts`) does two things,
|
|
50
|
+
once per machine:
|
|
51
|
+
|
|
52
|
+
1. **Install** the marketplace + plugin — `cli/src/plugin-install.ts` runs
|
|
53
|
+
`claude plugin marketplace add <repo>/plugin` (the dir is `MARKETPLACE_DIR` in
|
|
54
|
+
`cli/src/paths.ts`) then `claude plugin install alexa@bridge`. Both are idempotent; the install
|
|
55
|
+
itself *is* the MCP trust (no per-launch approval prompt).
|
|
56
|
+
2. **Allowlist** the channel in OS managed settings — `../node-core/src/claude-setup/managed-settings.ts`
|
|
57
|
+
(`cli/src/managed-settings.ts` is a thin adapter over it) writes
|
|
58
|
+
`/etc/claude-code/managed-settings.json` (Linux/WSL) with `channelsEnabled: true` and
|
|
59
|
+
`allowedChannelPlugins: [{ marketplace: "bridge", plugin: "alexa" }]`. That write sits **inside
|
|
60
|
+
`if (voice)`**, so on the shipping **voice-off default nothing is written and setup never asks for
|
|
61
|
+
sudo** ([`../docs/VOICE-FLAG.md`](../docs/VOICE-FLAG.md)). With voice off the settings are returned
|
|
62
|
+
**unchanged** — the old auto-withdrawal of previously-injected deny rules was itself removed in
|
|
63
|
+
2026-07 (v1.0.0 is the first released build, so there are no real upgraders to un-restrict, and
|
|
64
|
+
silently editing a user's own `permissions.deny` is worse than the vestigial cleanup); tool policy
|
|
65
|
+
is the user's, not ours to mutate.
|
|
66
|
+
|
|
67
|
+
Sessions then launch with:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
claude --channels plugin:alexa@bridge # cli/src/commands/run.ts (acbridge run)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
With voice on, that allowlist is what lets the channel load with **no** dangerous flag — without it
|
|
74
|
+
Claude **refuses** the channel as "not on the approved list". It is mandatory for a voice session, not
|
|
75
|
+
optional.
|
|
76
|
+
|
|
77
|
+
**Dev path (contrast).** From a from-source checkout you can skip the marketplace entirely and load
|
|
78
|
+
the channel straight from a project/user `.mcp.json` `alexa` entry:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
claude --dangerously-load-development-channels server:alexa . # stale form in ../channel/mcp.json
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This bypasses the marketplace + managed-settings allowlist and is for local development only; the
|
|
85
|
+
productized launch never uses a `--dangerously-*` flag.
|
|
86
|
+
|
|
87
|
+
## The permission-gate hook
|
|
88
|
+
|
|
89
|
+
`scripts/gate.mjs` runs on **every tool call** (`PreToolUse`, matcher `*`) and adds **no tool
|
|
90
|
+
restrictions of its own** (owner decision, 2026-07 security review). It reads the event JSON on stdin
|
|
91
|
+
purely to honour the hook contract — the decision comes from the **session**, not the event:
|
|
92
|
+
|
|
93
|
+
- **a voice session ⇒ `allow`, unconditionally.** The hands-free flow is the product: a permission
|
|
94
|
+
prompt mid-session has to be relayed to Alexa as a spoken yes/no for something the user already
|
|
95
|
+
authorised at install time. Anyone who wants restrictions configures them in their own Claude Code
|
|
96
|
+
settings — one place, under their control, not split across a plugin they did not write.
|
|
97
|
+
- **every other session ⇒ print nothing = defer** to Claude Code's normal permission flow.
|
|
98
|
+
|
|
99
|
+
**What that gives up, stated plainly** (the file's own header says it too, so nobody "fixes" this later
|
|
100
|
+
by re-adding rules): a PreToolUse `allow` **short-circuits** Claude Code's permission system, including
|
|
101
|
+
restrictions the user set in their own config. So on a voice session a misheard or prompt-injected
|
|
102
|
+
request can read a secret — `~/.ssh`, `~/.aws`, `.env`, the bridge's own token dir — and the `reply`
|
|
103
|
+
tool is outbound egress, so it can be **spoken back**. An earlier version denied exactly those reads
|
|
104
|
+
plus a set of destructive `Bash` patterns (`rm -rf`, `mkfs`, `dd if=`, fork bombs, force-push); **all of
|
|
105
|
+
those rules are gone**, along with their mirrored managed-settings deny list — and, since 2026-07, the
|
|
106
|
+
auto-withdrawal machinery too (`../node-core/src/claude-setup/managed-settings.ts`: setup neither
|
|
107
|
+
enforces nor removes deny rules; a dev machine still carrying the old ones drops them by hand).
|
|
108
|
+
|
|
109
|
+
**Why the voice scope is load-bearing:** the plugin stays installed when the voice flag is off — its
|
|
110
|
+
hooks are how agent state reaches the hub, which is what device Profiles react to
|
|
111
|
+
([`../docs/VOICE-FLAG.md`](../docs/VOICE-FLAG.md)). Without this check a voice-off build would be
|
|
112
|
+
overriding the user's permissions everywhere, for a feature it does not ship.
|
|
113
|
+
|
|
114
|
+
The signal is the **positive** `voice: true` marker every voice writer stamps into the session
|
|
115
|
+
handshake (`$BRIDGE_SECRET_DIR/sessions/<CLAUDE_CODE_SESSION_ID>.json` — written by
|
|
116
|
+
`../channel/src/handshake.ts` and `acbridge run`, and upgraded by the channel at MCP init, before it
|
|
117
|
+
connects, so before any `reply` egress can exist). **Absence is never treated as presence:** no marker,
|
|
118
|
+
no file, no session id, or an unreadable file all **defer**, as does any parse/logic error. Deferring is
|
|
119
|
+
strictly less permissive than allowing, so every uncertain case lands on the user's own configuration.
|
|
120
|
+
|
|
121
|
+
**Physical-device safety is separate and unaffected.** Security-sensitive smart-home commands (locks,
|
|
122
|
+
garages, valves, cameras) go through the hub's own fail-closed gate — `../hub/src/device-gate.ts` and
|
|
123
|
+
`../relay/src/device-confirm.ts`. Reading a file and unlocking a front door are different risks, and
|
|
124
|
+
only the first one is opened up.
|
|
125
|
+
|
|
126
|
+
## The session-state signal hook
|
|
127
|
+
|
|
128
|
+
`scripts/signal.mjs` turns Claude's session lifecycle into a smart-home scene (Profiles — actuated by the
|
|
129
|
+
hub's RulesEngine; the signal catalog is [`../docs/CLAUDE-SIGNALS.md`](../docs/CLAUDE-SIGNALS.md)). One
|
|
130
|
+
dependency-free script, registered against several hooks; it maps
|
|
131
|
+
the firing hook (+ payload) onto **one of the curated/extended session states** and POSTs a lightweight
|
|
132
|
+
`SessionStateRequest` to the local hub's loopback control API (`POST /local/session/state`, the same
|
|
133
|
+
`x-bridge-hub-token` guard as `/local/*`, **no `Origin`**). The hub's RulesEngine fires the bound
|
|
134
|
+
Profile rule **locally** (sub-second, never via the relay); the relay only receives the signal after,
|
|
135
|
+
for read-back.
|
|
136
|
+
|
|
137
|
+
| state | native hook | note |
|
|
138
|
+
|-------|-------------|------|
|
|
139
|
+
| `started` | `SessionStart` | `source:"compact"` is **silent** (compaction is not a new session) |
|
|
140
|
+
| `resumed` | `SessionStart` (`source:"resume"`) | distinguishes a resume from a cold `started` |
|
|
141
|
+
| `ended` | `SessionEnd` | |
|
|
142
|
+
| `working` | `UserPromptSubmit` | also `PostToolUse[Failure]` on `AskUserQuestion`/`ExitPlanMode` — answering/deciding resumes the turn (a plan **rejection** must not read as an error) |
|
|
143
|
+
| `finished` | `Stop` | fires at the end of **every** turn → the hub debounces per `(session,state)` |
|
|
144
|
+
| `needsInput` | `PreToolUse` (`AskUserQuestion`), `Notification` (`agent_needs_input`/`elicitation_dialog`) | Claude asked YOU a question |
|
|
145
|
+
| `planReady` | `PreToolUse` (`ExitPlanMode`) | a plan awaits your approval |
|
|
146
|
+
| `idle` | `Notification` (`idle_prompt`, or text fallback) | typed `notification_type` preferred; regex fallback for older Claude Code |
|
|
147
|
+
| `needs_permission` | `Notification` (`permission_prompt`, or text fallback) | same typed-first split |
|
|
148
|
+
| `rateLimited` | `Notification` (`rate_limit_warning`), `StopFailure` (`error_type:"rate_limit"`) | usage-limit hit/warning |
|
|
149
|
+
| `error` | `StopFailure` (non-rate-limit), `PostToolUseFailure`, `Notification` (`mcp_server_error`) | plus the legacy best-effort `PostToolUse` `is_error` check |
|
|
150
|
+
| `subagentStart` / `subagentFinished` | `SubagentStart` / `SubagentStop` | |
|
|
151
|
+
| `compacting` / `compactDone` | `PreCompact` / `PostCompact` | `compactDone` re-arms the hub's context latches |
|
|
152
|
+
| `permissionDenied` | `PermissionDenied` | a tool call auto-blocked without asking |
|
|
153
|
+
| `taskCompleted` | `TaskCompleted`, `Notification` (`agent_completed`) | background work finished |
|
|
154
|
+
| `workflowStarted` | `PreToolUse` (`Workflow`) | multi-agent orchestration kicked off (expect a subagent burst) |
|
|
155
|
+
| `contextLow` / `contextCritical` / `usageHigh` | *(hub-synthesized)* | the hub's `SessionContextTracker` fires them at the **profile's slider pct** from the `context` reading attached to every POST (transcript tail), `/local/session/context` ticks (successful tools), and the statusline tee (`scripts/statusline.mjs` — also the only source of rate-limit %). Claude's own `context_low` notification is deliberately unmapped. |
|
|
156
|
+
| `modePlan` / `modeAuto` / `modeBypass` / `modeNormal` | *(hub-synthesized)* | Claude Code has **no hook and no statusline field** for a permission-mode switch, so every POST ferries the transcript's last raw `permissionMode` (`plan` / `auto`\|`acceptEdits` / `bypassPermissions` / `default`) and the hub diffs it per session — **baseline-quiet** (the first observation never fires; a payload `permission_mode`, if Claude Code ever ships one, wins over the transcript). |
|
|
157
|
+
| `modelChanged` | *(hub-synthesized)* | same ferry: the last assistant `message.model` id (+ the statusline's `model.id`/`display_name`) diffed per session; the announcement speaks the display name or a prettified id ("Opus 4.8"). |
|
|
158
|
+
|
|
159
|
+
**Activation.** `signal.mjs` resolves this run's hub coordinates from (1) the `BRIDGE_*` launch env,
|
|
160
|
+
(2) the per-session handshake file (`SECRET_DIR/sessions/<claude session id>.json`) — written by
|
|
161
|
+
`acbridge run` **or self-written by the channel** on a bare `claude --channels …` — or (3)
|
|
162
|
+
**auto-profiles**: if neither exists but the hub is set up on this machine (a `hub-token` file is
|
|
163
|
+
present) and auto-profiles is on (the default), it **self-derives** the id (`project@machine`) and
|
|
164
|
+
fires. That is what makes lights react on **any** session while the hub daemon runs (voice stays
|
|
165
|
+
flag-gated — only the channel MCP server, which boots solely under `--channels`, does voice). The hub
|
|
166
|
+
only actuates repos **bound** to a Profile, so unbound repos no-op. **No hub configured on this
|
|
167
|
+
machine (no token) ⇒ exit 0, POST nothing.** Off-switch: `BRIDGE_AUTO_PROFILES=0` or `"autoProfiles":
|
|
168
|
+
false` in `config.json`. **Fail-safe:** any parse/IO/network error ⇒ exit 0 (a hook must never block
|
|
169
|
+
Claude); the POST is bounded by a 2 s timeout.
|
|
170
|
+
|
|
171
|
+
## The Codex session-state signal hook
|
|
172
|
+
|
|
173
|
+
`scripts/codex-signal.mjs` is the Codex CLI sibling of `signal.mjs` above, for a repo bound to a
|
|
174
|
+
**Codex** profile. It is installed by `acbridge setup` (`node-core/src/codex-setup/hooks.ts`,
|
|
175
|
+
merge-safe/reversible — every foreign hook survives verbatim; off-switch `BRIDGE_CODEX_HOOKS=0` /
|
|
176
|
+
`"autoCodexHooks": false`) directly into **Codex's own** `~/.codex/hooks.json`, registered against 9
|
|
177
|
+
lifecycle events: `SessionStart`, `UserPromptSubmit`, `Stop`, `PermissionRequest`, `PreCompact`,
|
|
178
|
+
`PostCompact`, `SubagentStart`, `SubagentStop`, `PostToolUse`. Codex requires a one-time in-TUI
|
|
179
|
+
`/hooks` review before a third-party hook actually runs — `acbridge setup` prints that reminder when
|
|
180
|
+
it changes the file, since it can't bypass the gate.
|
|
181
|
+
|
|
182
|
+
Unlike `signal.mjs`, Codex's hooks engine posts a single JSON object on stdin per event (no HTTP-hook
|
|
183
|
+
form) and ships `model`/`permission_mode` on **every** event, so the mode/model ferry is a straight
|
|
184
|
+
field read — no transcript walk needed for that part. The context ferry still walks Codex's own
|
|
185
|
+
rollout JSONL the same defensive last-64KB-tail way `signal.mjs` reads Claude's transcript, looking
|
|
186
|
+
for `token_count` rows. It POSTs to the SAME `/local/session/state` / `/local/session/context` hub
|
|
187
|
+
endpoints as `signal.mjs`, with `agent:"codex"` on the body — the hub's `RulesEngine` matches that
|
|
188
|
+
against a compiled rule's `trigger.agent` (a Codex profile's binding lane, `codexBindings`), so a
|
|
189
|
+
repo can react to a Codex session exactly like it reacts to Claude, distinguished by the `~codex` id
|
|
190
|
+
suffix on a self-derived session. `PermissionRequest` is observe-and-abstain: classified for the
|
|
191
|
+
device signal, but this script **never** writes to stdout (Codex reads a permission hook's stdout as
|
|
192
|
+
an approve/deny decision).
|
|
193
|
+
|
|
194
|
+
Codex has no `SessionEnd`/`Notification`/`StopFailure`/`PermissionDenied`/`TaskCompleted` hooks and
|
|
195
|
+
no rate-limit data; this hook script reaches 17 states directly (the 16 originals plus `error`, from
|
|
196
|
+
an error-shaped tool result on `PostToolUse`), and the adapter/voice surface reaches the rest of the
|
|
197
|
+
23-state codex mask — `CODEX_EVENTS` (`contracts/src/profiles.ts`, 23 of the 28 wire states) — which
|
|
198
|
+
is exactly what the Codex profile editor offers. Same activation
|
|
199
|
+
ladder and fail-safes as `signal.mjs` (BRIDGE_* env → per-session handshake → self-derive when a hub
|
|
200
|
+
is configured on this machine; `BRIDGE_AUTO_PROFILES=0` off-switch; any parse/IO/network error exits
|
|
201
|
+
0) — with one structural difference and one lifecycle gap, both recorded in
|
|
202
|
+
[`../docs/reviews/XPLAT-2026-07-28.md`](../docs/reviews/XPLAT-2026-07-28.md):
|
|
203
|
+
|
|
204
|
+
- **Local home + fixed port only.** Unlike `signal.mjs`, this script does **not** resolve the
|
|
205
|
+
WSL↔Windows bridge home: it pins the local `~/.claude-bridge` (`BRIDGE_SECRET_DIR` aside) and the
|
|
206
|
+
env/config `hubPort` — no `resolveBridgeHome()`, no beacon-port pickup, and no
|
|
207
|
+
`hooks-last-error.json` breadcrumb on rejection. With the hub on the other side of a WSL↔Windows
|
|
208
|
+
split (or bound off 8790 without a matching `config.json`), hook-lane Codex Profiles silently no-op.
|
|
209
|
+
- **No `ended`, ever.** No Codex hook can produce the `ended` state (Codex ships no `SessionEnd`),
|
|
210
|
+
and the hub keys Profile scene revert + arbiter stand-down on `ended` — so a hook-lane Codex session
|
|
211
|
+
that exits leaves its scene applied and its arbiter claim held on a daemon-kept hub until the next
|
|
212
|
+
session starts or the hub itself stops. The adapter lane (`acbridge run --agent codex`) does post
|
|
213
|
+
`ended` and is unaffected.
|
|
214
|
+
|
|
215
|
+
Full detail: [`../docs/CLAUDE-SIGNALS.md`](../docs/CLAUDE-SIGNALS.md) §7a.
|
|
216
|
+
|
|
217
|
+
## See also
|
|
218
|
+
|
|
219
|
+
- `../channel/` — the channel MCP server source this plugin bundles (`dist/channel.mjs`).
|
|
220
|
+
- [`../docs/CLAUDE-SIGNALS.md`](../docs/CLAUDE-SIGNALS.md) — the catalog of Claude session-state signals
|
|
221
|
+
`signal.mjs` maps onto Profiles (and where the hub's RulesEngine consumes them).
|
|
222
|
+
- `../CLAUDE.md` — the project overview and the Channels-vs-`/rc` architectural constraint.
|
|
223
|
+
- `../docs/DEPLOY.md` — the full go-live runbook (relay → hub/channel → skill → test).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "alexa",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Alexa voice-bridge channel for Claude Code (inject + reply + permission relay + the full Profiles session-state catalog with context readings).",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "AC BRIDGE LIMITED"
|
|
7
|
+
},
|
|
8
|
+
"channels": [
|
|
9
|
+
{
|
|
10
|
+
"server": "alexa"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|