@mirrowel/opencode-openshare 0.0.0-stage → 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +181 -2
  3. package/ci/README.md +56 -0
  4. package/ci/openshare.config.json +10 -0
  5. package/ci/openshare.js +2270 -0
  6. package/dist/commands.d.ts +7 -0
  7. package/dist/commands.js +440 -0
  8. package/dist/config.d.ts +138 -0
  9. package/dist/config.js +524 -0
  10. package/dist/core.d.ts +117 -0
  11. package/dist/core.js +1944 -0
  12. package/dist/dialogs.d.ts +94 -0
  13. package/dist/dialogs.js +725 -0
  14. package/dist/server.d.ts +64 -0
  15. package/dist/server.js +30 -0
  16. package/dist/settings.d.ts +24 -0
  17. package/dist/settings.js +1226 -0
  18. package/dist/share-flow.d.ts +287 -0
  19. package/dist/share-flow.js +516 -0
  20. package/dist/sidebar.d.ts +19 -0
  21. package/dist/sidebar.js +144 -0
  22. package/dist/tui-host.d.ts +158 -0
  23. package/dist/tui-host.js +6 -0
  24. package/dist/tui.d.ts +17 -0
  25. package/dist/tui.js +4719 -0
  26. package/dist/ui-prefs.d.ts +16 -0
  27. package/dist/ui-prefs.js +27 -0
  28. package/dist/v2-server.d.ts +81 -0
  29. package/dist/v2-server.js +402 -0
  30. package/dist/v2-tui.d.ts +24 -0
  31. package/dist/v2-tui.js +313 -0
  32. package/dist/v2-types.d.ts +134 -0
  33. package/dist/v2-types.js +8 -0
  34. package/openshare.config.json +16 -0
  35. package/package.json +98 -4
  36. package/src/banner.txt +25 -0
  37. package/src/commands.ts +452 -0
  38. package/src/config.ts +524 -0
  39. package/src/core.ts +1997 -0
  40. package/src/dialogs.tsx +971 -0
  41. package/src/server.ts +47 -0
  42. package/src/settings.ts +1239 -0
  43. package/src/share-flow.ts +551 -0
  44. package/src/sidebar.tsx +187 -0
  45. package/src/tui-host.ts +134 -0
  46. package/src/tui.tsx +318 -0
  47. package/src/ui-prefs.ts +36 -0
  48. package/src/v2-server.ts +438 -0
  49. package/src/v2-tui.ts +315 -0
  50. package/src/v2-types.ts +102 -0
  51. package/tsconfig.json +18 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mirrowel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,182 @@
1
- # Temporary Holding Version
1
+ # Open(code)Share plugin (`@mirrowel/opencode-openshare`)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The opencode side of [Open(code)Share](../README.md) — session sharing for
4
+ OpenCode v1 and v2, self-hosted on your own worker.
5
+
6
+ ## Artifacts
7
+
8
+ | Artifact | What it is | Share mode |
9
+ |---|---|---|
10
+ | `dist/server.js` | package server entry (opencode.json `plugin` array) | all, unless the TUI is registered |
11
+ | `dist/tui.js` | package TUI entry (tui.json `plugin` array) | its presence flips the server to manual |
12
+ | `ci/openshare.js` | single-file, zero-dependency bundle of the same engine (committed; rebuilt by `npm run build`) | always all — the CI bundle |
13
+
14
+ All three are behavior-identical where they overlap — `npm run
15
+ test:regression` proves the single file and the package match the frozen
16
+ original (`reference/openshare.legacy.js`) on the full share lifecycle.
17
+
18
+ ## Install
19
+
20
+ From the registry (published). **v1 hosts** — the bare package name in
21
+ BOTH config files (no `/tui` suffix; opencode resolves the entrypoints
22
+ itself):
23
+
24
+ ```jsonc
25
+ // opencode.json
26
+ { "plugin": ["@mirrowel/opencode-openshare"] }
27
+ // tui.json (global ~/.config/opencode/tui.json or project)
28
+ { "plugin": ["@mirrowel/opencode-openshare"] }
29
+ ```
30
+
31
+ **v2 hosts** — the CLI writes the config for you:
32
+
33
+ ```sh
34
+ opencode plugin add @mirrowel/opencode-openshare
35
+ ```
36
+
37
+ Restart opencode; `/share` and `/openshare` appear in the TUI.
38
+
39
+ Then configure. For an npm install the durable config file is the
40
+ **settings home** (`~/.config/opencode/openshare.config.json` — also
41
+ what `/openshare` writes); "next to the package" only applies to file
42
+ installs (the CI bundle):
43
+
44
+ ```jsonc
45
+ {
46
+ "servers": {
47
+ "https://your-worker.workers.dev": { "token": "osk_…", "active": true },
48
+ "https://team-server.example.com": { "code": "osc_invite-from-hoster" }
49
+ }
50
+ }
51
+ ```
52
+
53
+ `workerUrl`/`createToken` is the legacy single-server shorthand for the
54
+ same thing — the `servers` form above is canonical.
55
+
56
+ From a local checkout (developing / tracking main): use the directory
57
+ spec `"file:///…/openshare/share-plugin"` in both config files (run
58
+ `npm run build` first — `dist/` is not committed). The CI bundle needs
59
+ no build at all: [`ci/openshare.js`](ci/README.md), committed ready to
60
+ use.
61
+
62
+ **With no token the plugin enrolls itself** on first contact: public
63
+ servers (hoster set `AUTH_MODE=public`) take anyone; private ones need
64
+ a `code` from the hoster (activation-budgeted invite). The enrolled
65
+ token persists to `~/.config/opencode/openshare.config.json` — enroll
66
+ once per server, then it just works. A revoked token re-enrolls the
67
+ same way (public/code available); if enrollment is impossible the
68
+ plugin says so on stderr and stops — no retry storms.
69
+
70
+ ## Share modes
71
+
72
+ `shareMode` is explicit config (`"all"` / `"manual"`, env
73
+ `OPENSHARE_SHARE_MODE` > plugin options > config file) or — when unset —
74
+ **TUI presence**: the server scans the tui.json layers (global,
75
+ `OPENCODE_TUI_CONFIG`, project directory chain) for the openshare TUI
76
+ entry. Registered → manual (opt-in per session). Not registered → all.
77
+ No selfwiring: which parts you install IS the switch.
78
+
79
+ Manual mode stores the opt-in in session metadata:
80
+ `metadata.openshare = { v: 2, enabled: true, opts? }` with per-session
81
+ overrides (`ttlDays`, `authLogin`, `authPassword`, `modelLabel`). The
82
+ per-server binding map (`servers: {url: {id, secret, rev, url}}`) is
83
+ written next to it — legacy flat bindings migrate at read. The key is
84
+ stripped from everything synced and never reaches viewers.
85
+
86
+ On OpenCode v2: the package declaring its `tui` entrypoint is the presence
87
+ signal (the v2 host auto-loads it); the single file declares none and
88
+ stays all-mode.
89
+
90
+ ## TUI
91
+
92
+ - `/share` — share the session (one-time consent; copies the URL). On a
93
+ child session, shares the whole family. Already shared → the share
94
+ manager: copy links (per server), per-server stop / leave-as-snapshot,
95
+ share to another server, stop all. A credential conflict opens the
96
+ collision dialog (copy debug info / share on a new share / don't share).
97
+ - `/openshare` — grouped settings (compact root with live summaries →
98
+ focused submenus): This session (share/stop — delete or leave a
99
+ snapshot —, TTL override, session read auth), **Servers** (connection
100
+ AND membership: legacy pair, connect, enroll, rotate, get-a-user-token
101
+ for root holders, disconnect, per-server **admin panel** — users,
102
+ tokens, codes, shares, purge —, test, remove), Sharing (share mode +
103
+ booleans via selection menus), Read auth, Interface (one combined
104
+ Menu-size picker: live width cycling + height slider + capped-height
105
+ preview), Advanced, Config files. Danger rows red, dirty rows green,
106
+ `[i]` panes paged (`## sections`, 1-9 jump), messages wrap in real
107
+ bodies (no clipped one-liners).
108
+ - Sidebar chip — share status next to the session title (`·N` when the
109
+ session streams to several servers); click copies the link. A share
110
+ whose page died server-side renders `⊘ gone` — clicking opens the share
111
+ manager, which verifies every bound server, auto-clears dead entries
112
+ and offers Share again (see Gone detection in the root README).
113
+ - Every menu row carries an `[i]` detail pane (what it does, current
114
+ value + source, full URLs/ids); Interface rows set menu width/height
115
+ (host preference store).
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ npm install
121
+ npm run build # tsc -> dist/ + bun bundle -> ci/openshare.js (Bun on PATH)
122
+ npm run typecheck
123
+ npm run test # full matrix via the parallel driver (~4.5 min)
124
+ npm run test:quick # everyday gate (~90s): parity + manual core + TUI + v2 + auth
125
+ ```
126
+
127
+ Suites (shared machinery in `scripts/suite-lib.mjs`; every run is
128
+ watchdogged, port-swept, and drives real `opencode serve` instances with
129
+ a mock OpenAI-compatible provider — except where noted):
130
+
131
+ - `test:regression` — parity of legacy / single-file / package across the
132
+ full lifecycle (create → prompt → completed → delete → 404).
133
+ - `test:manual` — dormancy, flag activation + backfill, restart attach,
134
+ unshare, re-share, per-session auth opts, all-override, pre-existing
135
+ gates, undo tombstones, completed debounce, turn-mode drops.
136
+ - `test:tui-flow` — share/unshare flows, config round-trip, connect
137
+ flow (enroll + persist + rotate), admin console round-trip.
138
+ - `test:v2` — the v2 adapter against a **scripted mock v2 host** (event
139
+ classification, message conversion, live re-sync, delete).
140
+ - `test:auth` — the **server identity contract** exercised over HTTP/WS
141
+ (no opencode instance): enrollment, code burn, rotate, freeze (secret
142
+ dead too), limits, reassign, public mode (48 checks).
143
+ - `test:enroll` — the plugin's enrollment lifecycle end to end (code
144
+ enroll, token persistence across processes, revoke → re-enroll,
145
+ blocked path, public zero-config).
146
+ - `test:fanout` — multi-server: v2 bindings, content parity across
147
+ servers, mid-session join with full-history backfill, per-session
148
+ opt-outs.
149
+ - `test:cli` — real `opencode run` end to end (single file + package +
150
+ silenced outputs; mock provider, sandboxed XDG homes) — on demand.
151
+ - `node scripts/npm-e2e.mjs` — the **published** npm package installed
152
+ by registry spec, end to end — on demand.
153
+ - `test:server-auth` / `test:server-enroll` — the SAME auth/enroll
154
+ suites against the portable Bun backend (`../share-server`): the
155
+ two-backends-one-protocol parity guarantee.
156
+
157
+ Layout:
158
+
159
+ ```
160
+ src/
161
+ config.ts config load/save (servers group, field-level merge), shareMode, tui.json scan
162
+ core.ts the engine: fan-out remotes (socket/spool/rev per server),
163
+ ws handshake, chunked syncs, backfill, collisions, opt-outs
164
+ server.ts v1 entry {id, server, setup}
165
+ tui.tsx v1 TUI entry + host facade
166
+ commands.ts /share (toggle + fan-out manager + collision dialog) /openshare
167
+ share-flow.ts host-agnostic flows: flag, bindings v1/v2, enrollment,
168
+ rotate/disconnect, admin client, unshare multi-delete
169
+ settings.ts grouped settings tree + servers section + admin console
170
+ dialogs.tsx MenuDialog/InfoDialog/PromptDialog + the sizing core (75% budget)
171
+ ui-prefs.ts menu size prefs (host kv store)
172
+ sidebar.tsx sidebar_title slot chip (v1, box-rooted)
173
+ tui-host.ts host facade interface
174
+ v2-server.ts v2 setup: event mapping + client adapter + v2->v1 conversion
175
+ v2-tui.ts v2 TUI adapter (dialogs, keymap, kv)
176
+ v2-types.ts structural v2 types (no SDK import)
177
+ ```
178
+
179
+ The TUI surface (palette, dialogs, sidebar) is verified live; the flow
180
+ logic is covered headlessly. The v2 integration is verified against a
181
+ scripted host; run it live on a beta build via
182
+ `bun run dev` in your beta checkout with both package entries registered.
package/ci/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # Open(code)Share — CI bundle (`openshare.js`)
2
+
3
+ The single-file, zero-dependency build of the plugin. **Always shares all
4
+ sessions** — this is the headless/CI artifact, behavior-identical to the
5
+ package server (`../dist/server.js`), proven by `npm run test:regression`.
6
+
7
+ This folder is deliberately separate from the package root so that loading
8
+ the plugin by directory (`plugin: ["file:///…/share-plugin"]`) can only
9
+ ever resolve the package — never this file.
10
+
11
+ ## Use
12
+
13
+ Reference the FILE (not this folder) from a one-file config:
14
+
15
+ ```jsonc
16
+ // OPENCODE_CONFIG (or opencode.json)
17
+ {
18
+ "plugin": ["C:/path/to/share-plugin/ci/openshare.js"]
19
+ }
20
+ ```
21
+
22
+ Configure via env (CI) or the `openshare.config.json` next to the file —
23
+ either the legacy pair or the `servers` group (recommended; see the root
24
+ README's [Configuration](../../README.md#configuration)):
25
+
26
+ ```
27
+ OPENSHARE_WORKER_URL=https://your-host
28
+ OPENSHARE_CREATE_TOKEN=… # legacy: root token
29
+ OPENSHARE_URL_FILE=share-urls.txt # each share URL appended + stderr line
30
+ ```
31
+
32
+ ```jsonc
33
+ // openshare.config.json — enrollment shape (no root token needed)
34
+ {
35
+ "servers": {
36
+ "https://your-host": { "code": "osc_…" } // or "token": "osk_…"
37
+ }
38
+ }
39
+ ```
40
+
41
+ With no token held, the bundle **enrolls itself** on first contact
42
+ (public servers take anyone; private ones consume the one-time code) and
43
+ persists the token to `~/.config/opencode/openshare.config.json` —
44
+ enroll once, then every run just shares. A revoked token re-enrolls the
45
+ same way; if enrollment is impossible the error lands on stderr and
46
+ sharing stops (no retry storms). The bundle also supports **fan-out**:
47
+ multiple `active` servers in the `servers` group each get the session
48
+ (one urlFile line per server).
49
+
50
+ On OpenCode v2, plugin targets should be directories/packages — wrap this
51
+ file in a folder with a `package.json` exporting it (`{"exports": {".": "./openshare.js"}}`)
52
+ or install the package instead; the bundle itself works unchanged (its
53
+ `setup` is the v2 adapter, and with no package.json present it stays
54
+ share-all).
55
+
56
+ Rebuild after changing `../src/`: `npm run build` in the package root.
@@ -0,0 +1,10 @@
1
+ {
2
+ "$comment": "Open(code)Share CI bundle config. This file sits NEXT TO openshare.js and is read at load; OPENSHARE_* env vars always win (the CI way). The single-file bundle ALWAYS shares all sessions. Prefer the servers form below (workerUrl/createToken is the legacy single-server shorthand). Empty string means unset. urlFile/announceStderr control the two URL outputs; 'none' disables the file.",
3
+ "servers": {
4
+ "https://your-worker.workers.dev": { "token": "" }
5
+ },
6
+ "workerUrl": "",
7
+ "createToken": "",
8
+ "urlFile": "",
9
+ "announceStderr": true
10
+ }