feat(photon): gRPC-native iMessage channel (no webhook)

Make Photon iMessage a first-class persistent-connection channel like
Discord/Slack, using the spectrum-ts gRPC stream for both directions.

- Inbound: the sidecar forwards the SDK's app.messages gRPC stream to the
  adapter over a loopback GET /inbound (NDJSON) instead of webhooks. Drops
  the aiohttp webhook server, HMAC signature verification, public URL, and
  PHOTON_WEBHOOK_* config; adapter reconnects with backoff.
- Management plane: device login uses client_id=photon-cli against the
  single dashboard host (Bearer), matching the official photon-hq/cli;
  find-or-create "Hermes Agent" project, enable Spectrum, rotate secret,
  register user (with phone dedup), surface the assigned iMessage line.
- SDK projectId is the project's spectrumProjectId, not the dashboard id;
  runtime creds persist to ~/.hermes/.env like every other channel.
- CLI: 6-step setup, webhook subcommands removed.
- Tests/docs updated for the gRPC flow; sidecar pins spectrum-ts ^1.17.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
underthestars-zhy 2026-06-08 16:03:51 -07:00 committed by Teknium
parent c3420d91ad
commit 4e4d27875f
12 changed files with 1323 additions and 1176 deletions

View file

@ -1,52 +1,37 @@
name: photon-platform
label: Photon iMessage
kind: platform
version: 0.1.0
version: 0.2.0
description: >
Photon Spectrum gateway adapter for Hermes Agent.
Connects to iMessage (and other Spectrum interfaces) through Photon's
managed Spectrum platform. Inbound messages arrive as signed webhooks
on a local aiohttp server; outbound messages are sent via a small
supervised Node sidecar that runs the `spectrum-ts` SDK (Photon does
not currently expose a public HTTP send endpoint).
managed Spectrum platform. Both directions run over the `spectrum-ts`
SDK's long-lived gRPC stream via a small supervised Node sidecar —
inbound messages arrive on the SDK's `app.messages` stream (no webhook,
no public URL, no signing secret), and outbound messages are sent over
the same sidecar.
The plugin ships with a `hermes photon` CLI for the one-time login
+ project + user setup, persists Spectrum credentials to
``~/.hermes/auth.json`` under ``credential_pool.photon`` (token) and
``credential_pool.photon_project`` (project id + secret), and exposes
Photon's free shared-line model so users can get started without a
paid plan.
The plugin ships with a `hermes photon` CLI for the one-time device
login + project + user setup. Runtime credentials are written to
``~/.hermes/.env`` (``PHOTON_PROJECT_ID`` = the Spectrum project id,
``PHOTON_PROJECT_SECRET``) like every other channel, with management
metadata (device token, dashboard project id) in ``~/.hermes/auth.json``.
Photon's free shared-line model lets users get started without a paid plan.
author: NousResearch
requires_env:
- name: PHOTON_PROJECT_ID
description: "Spectrum project ID (set by `hermes photon setup`)"
prompt: "Photon Spectrum project ID"
description: "Spectrum project id (the project's spectrumProjectId; set by `hermes photon setup`)"
prompt: "Photon Spectrum project id"
url: "https://app.photon.codes/"
password: false
- name: PHOTON_PROJECT_SECRET
description: "Spectrum project secret (set by `hermes photon setup`)"
prompt: "Photon Spectrum project secret"
description: "Project secret paired with the Spectrum project id (set by `hermes photon setup`)"
prompt: "Photon project secret"
url: "https://app.photon.codes/"
password: true
optional_env:
- name: PHOTON_WEBHOOK_SECRET
description: "Per-URL HMAC-SHA256 signing secret returned at webhook registration"
prompt: "Photon webhook signing secret"
password: true
- name: PHOTON_WEBHOOK_PORT
description: "Local port the webhook receiver listens on (default 8788)"
prompt: "Webhook receiver port"
password: false
- name: PHOTON_WEBHOOK_PATH
description: "Path the webhook receiver listens on (default /photon/webhook)"
prompt: "Webhook receiver path"
password: false
- name: PHOTON_WEBHOOK_BIND
description: "Bind address for the webhook receiver (default 0.0.0.0)"
prompt: "Webhook bind address"
password: false
- name: PHOTON_SIDECAR_PORT
description: "Loopback port for the Node sidecar control channel (default 8789)"
description: "Loopback port for the Node sidecar control + inbound channel (default 8789)"
prompt: "Sidecar control port"
password: false
- name: PHOTON_SIDECAR_AUTOSTART
@ -57,12 +42,8 @@ optional_env:
description: "Path to the node binary (default: shutil.which('node'))"
prompt: "Node executable path"
password: false
- name: PHOTON_API_HOST
description: "Spectrum management API host (default https://spectrum.photon.codes)"
prompt: "Spectrum API host"
password: false
- name: PHOTON_DASHBOARD_HOST
description: "Dashboard API host (default https://app.photon.codes)"
description: "Photon Dashboard API host (default https://app.photon.codes)"
prompt: "Dashboard host"
password: false
- name: PHOTON_ALLOWED_USERS
@ -82,8 +63,8 @@ optional_env:
prompt: "Group mention patterns"
password: false
- name: PHOTON_HOME_CHANNEL
description: "Default Spectrum space ID for cron / notification delivery"
prompt: "Home space ID"
description: "Default Spectrum space id for cron / notification delivery"
prompt: "Home space id"
password: false
- name: PHOTON_HOME_CHANNEL_NAME
description: "Human label for the home channel"