@mirrowel/opencode-openshare 0.2.1 → 0.3.0-dev.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.
package/README.md CHANGED
@@ -1,8 +1,28 @@
1
1
  # Open(code)Share plugin (`@mirrowel/opencode-openshare`)
2
2
 
3
- The opencode side of [Open(code)Share](../README.md) — session sharing for
3
+ The opencode side of [Open(code)Share](../README.md) - session sharing for
4
4
  OpenCode v1 and v2, self-hosted on your own worker.
5
5
 
6
+ **This package is only the plugin.** It captures and uploads sessions, but a
7
+ share needs a backend to upload to. OpenShare ships as four parts that
8
+ version and release together:
9
+
10
+ | Part | What it does | Where to get it |
11
+ |---|---|---|
12
+ | **This plugin** | hooks into opencode, syncs sessions | this npm package |
13
+ | **Worker** | the Cloudflare backend - stores shares, serves the viewer | deploy from the repo: `share-worker/` (3-command setup, see the repo README) |
14
+ | **Server** | self-hosted alternative to the worker - one Bun process, SQLite, Docker image | repo `share-server/` |
15
+ | **Viewer** | the share page - ships inside the worker/server assets; nothing to install separately | repo `share-viewer/` (embedded automatically) |
16
+
17
+ The repo also builds **offline snapshots** - a single self-contained HTML
18
+ file of any share, generated by the viewer's download button or the
19
+ `osh-snapshot` CLI - so a shared link keeps working even with no backend at
20
+ all.
21
+
22
+ Everything lives in one repository: **https://github.com/Mirrowel/openshare**
23
+ (clone it, or browse the [README](https://github.com/Mirrowel/openshare#readme)
24
+ for the full picture - deployment, configuration, and the viewer tour).
25
+
6
26
  Every plugin config key, its file chain, and the cross-package config surface are inventoried in [../CONFIGURATION.md](../CONFIGURATION.md).
7
27
 
8
28
  ## Artifacts
@@ -14,8 +34,9 @@ Every plugin config key, its file chain, and the cross-package config surface ar
14
34
  | `ci/openshare.js` | single-file, zero-dependency bundle of the same engine (committed; rebuilt by `npm run build`) | all-mode **by default** — it declares no TUI entry, but an explicit `shareMode` in config/env still wins |
15
35
 
16
36
  All three are behavior-identical where they overlap — `npm run
17
- test:regression` proves the single file and the package match the frozen
18
- original (`reference/openshare.legacy.js`) on the full share lifecycle.
37
+ test:regression` drives the single file and the package entry through
38
+ the same real lifecycle (a local worker, a mock provider, a real
39
+ `opencode serve`) and diffs the resulting share data.
19
40
 
20
41
  ## Install
21
42
 
@@ -36,7 +57,9 @@ itself):
36
57
  opencode plugin add @mirrowel/opencode-openshare
37
58
  ```
38
59
 
39
- Restart opencode; `/share` and `/openshare` appear in the TUI.
60
+ Restart opencode; `/share` and `/openshare` appear in the TUI. The
61
+ settings dialog's title carries the running plugin's version (read from
62
+ its own package manifest at runtime).
40
63
 
41
64
  Then configure. For an npm install the durable config file is the
42
65
  **settings home** (`~/.config/opencode/openshare.config.json` — also
@@ -61,12 +84,26 @@ spec `"file:///…/openshare/share-plugin"` in both config files (run
61
84
  no build at all: [`ci/openshare.js`](ci/README.md), committed ready to
62
85
  use.
63
86
 
64
- **With no token the plugin enrolls itself** on first contact: public
65
- servers (hoster set `AUTH_MODE=public`) take anyone; private ones need
66
- a `code` from the hoster (activation-budgeted invite). The enrolled
67
- token persists to `~/.config/opencode/openshare.config.json` — enroll
68
- once per server, then it just works. A revoked token re-enrolls the
69
- same way (public/code available); if enrollment is impossible the
87
+ **Server-held config sets**: set `configSet` (or
88
+ `OPENSHARE_CONFIG_SET`) to pick a named set provisioned on your server
89
+ token - the server applies its auth/TTL at create (nothing sensitive in
90
+ the repo), and suggests modelLabel/shareMode which the plugin adopts once
91
+ per boot when you haven't set them locally.
92
+
93
+ **Identity is never assumed**: with no token configured the plugin does
94
+ NOT silently enroll. A configured `code` IS expressed
95
+ intent — the engine enrolls with it headlessly (the CI shape). Without
96
+ one: public servers share **anonymously** (ownerless shares under the
97
+ server's global anonymous caps), private servers refuse with guidance.
98
+ Onboarding happens through the ask-once TUI flow — `/share` first run
99
+ or `/openshare` → Servers → Connect to a server offers "Onboard (get an
100
+ account)" vs "Stay anonymous" (with the differences explained), detects
101
+ whether a code is needed, and only then prompts for one; the outcome is
102
+ remembered per server (token = onboarded, `anonymous` flag = declined,
103
+ onboard later via the server menu's Enroll now). The enrolled token
104
+ persists to `~/.config/opencode/openshare.config.json` — enroll once per
105
+ server, then it just works. A revoked token re-enrolls the same way
106
+ (code configured / codeless server); if enrollment is impossible the
70
107
  plugin says so on stderr and stops — no retry storms.
71
108
 
72
109
  ## Share modes
@@ -80,7 +117,7 @@ No selfwiring: which parts you install IS the switch.
80
117
 
81
118
  Manual mode stores the opt-in in session metadata:
82
119
  `metadata.openshare = { v: 2, enabled: true, opts? }` with per-session
83
- overrides (`ttlDays`, `authLogin`, `authPassword`, `modelLabel`). The
120
+ overrides (`ttlHours`, `authLogin`, `authPassword`, `modelLabel`). The
84
121
  per-server binding map (`servers: {url: {id, secret, rev, url}}`) is
85
122
  written next to it — legacy flat bindings migrate at read. The key is
86
123
  stripped from everything synced and never reaches viewers.
@@ -89,6 +126,19 @@ On OpenCode v2: the package declaring its `tui` entrypoint is the presence
89
126
  signal (the v2 host auto-loads it); the single file declares none and
90
127
  stays all-mode.
91
128
 
129
+ ## Trust controls (config)
130
+
131
+ Client-side redaction runs at the engine's single enqueue choke point
132
+ (backfill and live events both), so stripped secrets never reach a
133
+ queue, spool or wire:
134
+
135
+ - `redact.patterns` — extra regexes (strings) redacted beside the built-in
136
+ detectors on UNGATED shares only (a gated share is already
137
+ login-private): GitHub/AWS/Slack/Google key shapes, `sk-` keys, Bearer
138
+ tokens, PEM private-key blocks, `.env`-shaped assignments and
139
+ connection-string passwords, replaced with `[redacted:<kind>]`. The
140
+ consent dialog previews counts and the first hits.
141
+
92
142
  ## TUI
93
143
 
94
144
  - `/share` — share the session (explicit consent on EVERY creation —
@@ -96,12 +146,13 @@ stays all-mode.
96
146
  session, shares the whole family. Already shared → the session menu,
97
147
  identical to `/openshare → This session`: copy links (per server, or
98
148
  all), stop sharing — delete or leave a snapshot, per-server stop on
99
- multi-server shares —, share to another server, session TTL and read
149
+ multi-server shares —, share to another server, session TTL, read
100
150
  auth. A credential conflict opens the collision dialog (copy debug
101
151
  info / share on a new share / don't share).
102
152
  - `/openshare` — grouped settings (compact root with live summaries →
103
153
  focused submenus): This session (share/stop — delete or leave a
104
- snapshot —, TTL override, session read auth), **Servers** (connection
154
+ snapshot —, TTL override, session read auth),
155
+ **Servers** (connection
105
156
  AND membership: legacy pair, connect, enroll, rotate, get-a-user-token
106
157
  for root holders, disconnect, per-server **admin panel** — users,
107
158
  tokens, codes, shares, purge —, test, remove), Sharing (share mode +
@@ -120,13 +171,17 @@ stays all-mode.
120
171
  value + source, full URLs/ids); Interface rows set menu width/height
121
172
  (host preference store).
122
173
 
174
+ ## Versioning
175
+
176
+ Releases are automatic (conventional commits). The plugin reports its version in the `/openshare` title; the system version ships in every part and the worker's `/health` carries it in full (`version · date · commit`). Designating a version by hand: `bash .github/scripts/release-kit.sh next <1.2.3|major|minor|patch>` in the repo (see DEVELOPMENT.md).
177
+
123
178
  ## Development
124
179
 
125
180
  ```bash
126
181
  npm install
127
182
  npm run build # tsc -> dist/ + bun bundle -> ci/openshare.js (Bun on PATH)
128
183
  npm run typecheck
129
- npm run test # full matrix via the parallel driver (~4.5 min)
184
+ npm run test # full matrix via the parallel driver (~2.5 min)
130
185
  npm run test:quick # everyday gate (~90s): parity + manual core + TUI + v2 + auth
131
186
  ```
132
187
 
@@ -135,20 +190,11 @@ watchdogged, port-swept, and drives real `opencode serve` instances with
135
190
  a mock OpenAI-compatible provider — except where noted). The canonical
136
191
  suite table — what each covers, including the big/backends scale suites —
137
192
  lives in [`../DEVELOPMENT.md`](../DEVELOPMENT.md#test-suites); the
138
- commands:
139
-
140
- - `test:regression` — parity of legacy / single-file / package across the
141
- full lifecycle.
142
- - `test:manual` — the engine scenarios (dormancy, backfill, tombstones,
143
- timers, turn sockets, all-mode gating).
144
- - `test:tui-flow` — share/manager flows, config write targets, enrollment,
145
- admin console, gone detection.
146
- - `test:v2` — the v2 adapter against a scripted mock v2 host.
147
- - `test:auth` / `test:server-auth` — the identity contract (52 checks)
148
- against the Cloudflare worker and the Bun backend.
149
- - `test:enroll` / `test:server-enroll`, `test:fanout`, `test:big` /
150
- `test:server-big` — enrollment lifecycle, multi-server fan-out,
151
- big-session scale (both backends).
193
+ everyday commands:
194
+
195
+ - `test:quick` — the everyday gate: parity + manual core + TUI + v2 + auth.
196
+ - `test:auth` / `test:server-auth` — the identity contract against the
197
+ Cloudflare worker and the Bun backend.
152
198
  - `test:cli`, `node scripts/npm-e2e.mjs` — real `opencode run` / the
153
199
  published npm package — on demand only.
154
200
 
package/ci/README.md CHANGED
@@ -31,10 +31,22 @@ README's [Configuration](../../README.md#configuration)):
31
31
 
32
32
  ```
33
33
  OPENSHARE_WORKER_URL=https://your-host
34
- OPENSHARE_CREATE_TOKEN=… # legacy: root token
34
+ OPENSHARE_CREATE_TOKEN=: # legacy: root token
35
35
  OPENSHARE_URL_FILE=share-urls.txt # each share URL appended + stderr line
36
36
  ```
37
37
 
38
+ CI conveniences:
39
+
40
+ - `OPENSHARE_CONFIG_JSON` - the whole config as one env var (same keys as
41
+ the config file), when mounting a file is awkward. A specific
42
+ `OPENSHARE_*` var still wins over it.
43
+ - `OPENSHARE_URL_FILE_FORMAT=json` - urlFile lines become
44
+ `{"id":"…","url":"…","status":"…","gated":false}` objects, one per
45
+ line, so workflow steps read fields instead of parsing URLs.
46
+ - `meta` in the config stamps free-form labels (env-interpolated, e.g.
47
+ `"branch": "${GITHUB_REF_NAME}"`) onto every share - visible in the
48
+ server's admin shares listing.
49
+
38
50
  ```jsonc
39
51
  // openshare.config.json — enrollment shape (no root token needed)
40
52
  {
@@ -44,22 +56,26 @@ OPENSHARE_URL_FILE=share-urls.txt # each share URL appended + stderr line
44
56
  }
45
57
  ```
46
58
 
47
- With no token held, the bundle **enrolls itself** on first contact
48
- (public servers take anyone; private ones consume the one-time code) and
49
- persists the token to `~/.config/opencode/openshare.config.json` -
50
- enroll once, then every run just shares. A revoked token re-enrolls the
51
- same way; if enrollment is impossible the error lands on stderr and
52
- sharing stops (no retry storms).
59
+ With no token held the bundle never silently enrolls: a configured
60
+ `code` enrolls with it headlessly (the CI shape — each code use is one
61
+ enrollment); without one, public servers share anonymously (ownerless
62
+ shares under the server's global anonymous caps) and private servers
63
+ refuse with guidance. When enrollment does run, the token persists to
64
+ `~/.config/opencode/openshare.config.json` — enroll once, then every
65
+ run just shares. A revoked token re-enrolls the same way (code
66
+ configured / codeless server); if enrollment is impossible the error
67
+ lands on stderr and sharing stops (no retry storms).
53
68
 
54
69
  **Ephemeral runners** (GitHub-hosted, containers): do NOT enroll through
55
70
  a code - each fresh machine would burn one activation. Ask the hoster
56
- for a pre-minted registered token (`osk_…`) and pass it via
57
- `OPENSHARE_CREATE_TOKEN` (or the config's token field) - registered
58
- tokens skip enrollment entirely and never expire on their own. Point
59
- `OPENSHARE_CONFIG_DIR` (or mount) the settings home at a cached path
60
- only if you want the token persisted; env alone is enough. Note:
61
- revoking that token (when it is the user's last) freezes control of all
62
- its shares server-side, even ones holding the share secret.
71
+ for a pre-registered token and pass it via `OPENSHARE_CREATE_TOKEN`
72
+ (or the config's token field); registered tokens skip enrollment
73
+ entirely and never expire on their own. A **scoped** token
74
+ (`POST /api/token {"kind":"scoped"}`, minted by any full token) is the
75
+ right credential here: it creates within its limits, reaches only the
76
+ shares it created, and revoking it never freezes the owner's account.
77
+ Point `OPENCODE_CONFIG_DIR` (or mount) the settings home at a cached
78
+ path only if you want the token persisted; env alone is enough.
63
79
 
64
80
  The bundle also supports **fan-out**: multiple `active` servers in the
65
81
  `servers` group each get the session (one urlFile line per server).