@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.
- package/LICENSE +21 -0
- package/README.md +181 -2
- package/ci/README.md +56 -0
- package/ci/openshare.config.json +10 -0
- package/ci/openshare.js +2270 -0
- package/dist/commands.d.ts +7 -0
- package/dist/commands.js +440 -0
- package/dist/config.d.ts +138 -0
- package/dist/config.js +524 -0
- package/dist/core.d.ts +117 -0
- package/dist/core.js +1944 -0
- package/dist/dialogs.d.ts +94 -0
- package/dist/dialogs.js +725 -0
- package/dist/server.d.ts +64 -0
- package/dist/server.js +30 -0
- package/dist/settings.d.ts +24 -0
- package/dist/settings.js +1226 -0
- package/dist/share-flow.d.ts +287 -0
- package/dist/share-flow.js +516 -0
- package/dist/sidebar.d.ts +19 -0
- package/dist/sidebar.js +144 -0
- package/dist/tui-host.d.ts +158 -0
- package/dist/tui-host.js +6 -0
- package/dist/tui.d.ts +17 -0
- package/dist/tui.js +4719 -0
- package/dist/ui-prefs.d.ts +16 -0
- package/dist/ui-prefs.js +27 -0
- package/dist/v2-server.d.ts +81 -0
- package/dist/v2-server.js +402 -0
- package/dist/v2-tui.d.ts +24 -0
- package/dist/v2-tui.js +313 -0
- package/dist/v2-types.d.ts +134 -0
- package/dist/v2-types.js +8 -0
- package/openshare.config.json +16 -0
- package/package.json +98 -4
- package/src/banner.txt +25 -0
- package/src/commands.ts +452 -0
- package/src/config.ts +524 -0
- package/src/core.ts +1997 -0
- package/src/dialogs.tsx +971 -0
- package/src/server.ts +47 -0
- package/src/settings.ts +1239 -0
- package/src/share-flow.ts +551 -0
- package/src/sidebar.tsx +187 -0
- package/src/tui-host.ts +134 -0
- package/src/tui.tsx +318 -0
- package/src/ui-prefs.ts +36 -0
- package/src/v2-server.ts +438 -0
- package/src/v2-tui.ts +315 -0
- package/src/v2-types.ts +102 -0
- 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
|
-
#
|
|
1
|
+
# Open(code)Share plugin (`@mirrowel/opencode-openshare`)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
}
|