@mirrowel/opencode-openshare 0.0.0-stage → 0.1.0

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 +175 -2
  3. package/ci/README.md +56 -0
  4. package/ci/openshare.config.json +5 -0
  5. package/ci/openshare.js +2265 -0
  6. package/dist/commands.d.ts +7 -0
  7. package/dist/commands.js +440 -0
  8. package/dist/config.d.ts +134 -0
  9. package/dist/config.js +517 -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 +4762 -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 +22 -0
  37. package/src/commands.ts +452 -0
  38. package/src/config.ts +518 -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,176 @@
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
+ | `openshare.js` | single-file, zero-dependency bundle of the same engine (built 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
+ Interactive (manual sharing, `/share` command, sidebar chip):
21
+
22
+ ```jsonc
23
+ // opencode.json
24
+ { "plugin": ["@mirrowel/opencode-openshare"] }
25
+ // tui.json (global ~/.config/opencode/tui.json or project)
26
+ { "plugin": ["@mirrowel/opencode-openshare/tui"] }
27
+ ```
28
+
29
+ CI (share everything, link to a file):
30
+
31
+ ```jsonc
32
+ // OPENCODE_CONFIG one-file setup
33
+ {
34
+ "plugin": ["/path/to/openshare.js"]
35
+ // + openshare.config.json next to the bundle (servers group or the
36
+ // legacy env pair), OPENSHARE_URL_FILE collects each share URL
37
+ // (also printed to stderr)
38
+ }
39
+ ```
40
+
41
+ Then configure (`openshare.config.json` next to the package — see the
42
+ [main README](../README.md#configuration) for all keys):
43
+
44
+ ```jsonc
45
+ { "workerUrl": "https://your-worker.workers.dev", "createToken": "…" }
46
+ ```
47
+
48
+ Or the multi-server form (canonical; `workerUrl`/`createToken` above is
49
+ the single-server shorthand):
50
+
51
+ ```jsonc
52
+ {
53
+ "servers": {
54
+ "https://your-worker.workers.dev": { "token": "osk_…", "active": true },
55
+ "https://team-server.example.com": { "code": "osc_invite-from-hoster" }
56
+ }
57
+ }
58
+ ```
59
+
60
+ **With no token the plugin enrolls itself** on first contact: public
61
+ servers (hoster set `AUTH_MODE=public`) take anyone; private ones need
62
+ a `code` from the hoster (activation-budgeted invite). The enrolled
63
+ token persists to `~/.config/opencode/openshare.config.json` — enroll
64
+ once per server, then it just works. A revoked token re-enrolls the
65
+ same way (public/code available); if enrollment is impossible the
66
+ plugin says so on stderr and stops — no retry storms.
67
+
68
+ ## Share modes
69
+
70
+ `shareMode` is explicit config (`"all"` / `"manual"`, env
71
+ `OPENSHARE_SHARE_MODE` > plugin options > config file) or — when unset —
72
+ **TUI presence**: the server scans the tui.json layers (global,
73
+ `OPENCODE_TUI_CONFIG`, project directory chain) for the openshare TUI
74
+ entry. Registered → manual (opt-in per session). Not registered → all.
75
+ No selfwiring: which parts you install IS the switch.
76
+
77
+ Manual mode stores the opt-in in session metadata:
78
+ `metadata.openshare = { v: 2, enabled: true, opts? }` with per-session
79
+ overrides (`ttlDays`, `authLogin`, `authPassword`, `modelLabel`). The
80
+ per-server binding map (`servers: {url: {id, secret, rev, url}}`) is
81
+ written next to it — legacy flat bindings migrate at read. The key is
82
+ stripped from everything synced and never reaches viewers.
83
+
84
+ On OpenCode v2: the package declaring its `tui` entrypoint is the presence
85
+ signal (the v2 host auto-loads it); the single file declares none and
86
+ stays all-mode.
87
+
88
+ ## TUI
89
+
90
+ - `/share` — share the session (one-time consent; copies the URL). On a
91
+ child session, shares the whole family. Already shared → the share
92
+ manager: copy links (per server), per-server stop / leave-as-snapshot,
93
+ share to another server, stop all. A credential conflict opens the
94
+ collision dialog (copy debug info / share on a new share / don't share).
95
+ - `/openshare` — grouped settings (compact root with live summaries →
96
+ focused submenus): This session (share/stop — delete or leave a
97
+ snapshot —, TTL override, session read auth), **Servers** (connection
98
+ AND membership: legacy pair, connect, enroll, rotate, get-a-user-token
99
+ for root holders, disconnect, per-server **admin panel** — users,
100
+ tokens, codes, shares, purge —, test, remove), Sharing (share mode +
101
+ booleans via selection menus), Read auth, Interface (one combined
102
+ Menu-size picker: live width cycling + height slider + capped-height
103
+ preview), Advanced, Config files. Danger rows red, dirty rows green,
104
+ `[i]` panes paged (`## sections`, 1-9 jump), messages wrap in real
105
+ bodies (no clipped one-liners).
106
+ - Sidebar chip — share status next to the session title (`·N` when the
107
+ session streams to several servers); click copies the link. A share
108
+ whose page died server-side renders `⊘ gone` — clicking opens the share
109
+ manager, which verifies every bound server, auto-clears dead entries
110
+ and offers Share again (see Gone detection in the root README).
111
+ - Every menu row carries an `[i]` detail pane (what it does, current
112
+ value + source, full URLs/ids); Interface rows set menu width/height
113
+ (host preference store).
114
+
115
+ ## Development
116
+
117
+ ```bash
118
+ npm install
119
+ npm run build # tsc -> dist/ + bun bundle -> openshare.js
120
+ npm run typecheck
121
+ npm run test # full matrix via the parallel driver (~4.5 min)
122
+ npm run test:quick # everyday gate (~90s): parity + manual core + TUI + v2 + auth
123
+ ```
124
+
125
+ Suites (shared machinery in `scripts/suite-lib.mjs`; every run is
126
+ watchdogged, port-swept, and drives the real production path — local
127
+ `wrangler dev` worker + real `opencode serve` with a mock OpenAI-compatible
128
+ provider):
129
+
130
+ - `test:regression` — parity of legacy / single-file / package across the
131
+ full lifecycle (create → prompt → completed → delete → 404).
132
+ - `test:manual` — dormancy, flag activation + backfill, restart attach,
133
+ unshare, re-share, per-session auth opts, all-override, pre-existing
134
+ gates, undo tombstones, completed debounce, turn-mode drops.
135
+ - `test:tui-flow` — share/unshare flows, config round-trip, connect
136
+ flow (enroll + persist + rotate), admin console round-trip.
137
+ - `test:v2` — the v2 adapter against a scripted mock v2 host (event
138
+ classification, message conversion, live re-sync, delete).
139
+ - `test:auth` — server identity contract: enrollment, code burn, rotate,
140
+ freeze (secret dead too), limits, reassign, public mode (48 checks).
141
+ - `test:enroll` — the plugin's enrollment lifecycle end to end (code
142
+ enroll, token persistence across processes, revoke → re-enroll,
143
+ blocked path, public zero-config).
144
+ - `test:fanout` — multi-server: v2 bindings, content parity across
145
+ servers, mid-session join with full-history backfill, per-session
146
+ opt-outs.
147
+ - `test:cli` — real `opencode run` end-to-end (single file + package +
148
+ silenced outputs), on-demand only (uses the default model).
149
+ - `test:server-auth` / `test:server-enroll` — the SAME auth/enroll
150
+ suites against the portable Bun backend (`../share-server`): the
151
+ two-backends-one-protocol parity guarantee.
152
+
153
+ Layout:
154
+
155
+ ```
156
+ src/
157
+ config.ts config load/save (servers group, field-level merge), shareMode, tui.json scan
158
+ core.ts the engine: fan-out remotes (socket/spool/rev per server),
159
+ ws handshake, chunked syncs, backfill, collisions, opt-outs
160
+ server.ts v1 entry {id, server, setup}
161
+ tui.tsx v1 TUI entry + host facade
162
+ commands.ts /share (toggle + fan-out manager + collision dialog) /openshare
163
+ share-flow.ts host-agnostic flows: flag, bindings v1/v2, enrollment,
164
+ rotate/disconnect, admin client, unshare multi-delete
165
+ settings.ts settings dialog + servers section + admin console
166
+ sidebar.tsx sidebar_title slot chip (v1, box-rooted)
167
+ tui-host.ts host facade interface
168
+ v2-server.ts v2 setup: event mapping + client adapter + v2->v1 conversion
169
+ v2-tui.ts v2 TUI adapter (dialogs, keymap, kv)
170
+ v2-types.ts structural v2 types (no SDK import)
171
+ ```
172
+
173
+ The TUI surface (palette, dialogs, sidebar) is verified live; the flow
174
+ logic is covered headlessly. The v2 integration is verified against a
175
+ scripted host; run it live on a beta build via
176
+ `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,5 @@
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.",
3
+ "workerUrl": "",
4
+ "createToken": ""
5
+ }