opencode-muse-auth 0.4.0 → 0.4.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 +1 -1
- package/README.md +125 -52
- package/dist/auth.js +20 -9
- package/package.json +9 -9
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# opencode-muse-auth
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
**Muse Code monthly subscription** — no pay-as-you-go API key, no third-party CLI.
|
|
3
|
+
Muse Code subscription sign-in for opencode
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
[](https://github.com/TheStreamCode/opencode-muse-auth/actions/workflows/ci.yml)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.npmjs.com/package/opencode-muse-auth)
|
|
8
|
+
|
|
9
|
+
opencode plugin that registers the **Muse Code subscription** login method for the built-in `meta`
|
|
10
|
+
provider. No API key to copy and no third-party CLI to install. A successful login does not guarantee
|
|
11
|
+
model access: Meta may reject subscription requests from clients other than Muse Code.
|
|
12
|
+
|
|
13
|
+
> **Unofficial and experimental.** This plugin uses a Muse Code subscription credential from
|
|
14
|
+
> opencode. Meta's terms limit that credential to Muse Code, so this use may not be supported and
|
|
15
|
+
> Meta may suspend or revoke accounts or credentials. Use it at your own risk; the authors cannot
|
|
16
|
+
> recover an account. Read [DISCLAIMER.md](DISCLAIMER.md) before installing.
|
|
11
17
|
|
|
12
18
|
## Install
|
|
13
19
|
|
|
20
|
+
Add the plugin to `opencode.json`; opencode installs it from npm:
|
|
21
|
+
|
|
14
22
|
```jsonc
|
|
15
23
|
// opencode.json
|
|
16
24
|
{
|
|
@@ -19,64 +27,129 @@ Use Meta **Muse Spark** in [opencode](https://opencode.ai) billed to your
|
|
|
19
27
|
}
|
|
20
28
|
```
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
## Sign in
|
|
31
|
+
|
|
32
|
+
1. Run `/connect` in opencode.
|
|
33
|
+
2. Pick the `meta` provider and the **Muse Code subscription** method.
|
|
34
|
+
3. Open the printed Meta device page, enter the printed code, and approve the login in your own
|
|
35
|
+
browser. The login completes by itself.
|
|
36
|
+
4. Select a `muse-*` model exposed by the `meta` provider, for example `meta/muse-spark-1.3`.
|
|
37
|
+
Meta's subscription restrictions still apply to requests from opencode.
|
|
38
|
+
|
|
39
|
+
The plugin mints a stable, account-bound inference key and hands it to opencode. The OAuth access
|
|
40
|
+
token from the login flow is kept in memory only and is never written to disk.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- Registers an `auth` hook for the built-in `meta` provider; there is no separate provider id to
|
|
45
|
+
configure.
|
|
46
|
+
- Runs the Meta device-code exchange (RFC 8628) inside `/connect`, then mints the subscription
|
|
47
|
+
inference key through the Model API.
|
|
48
|
+
- Routes through the Responses API; the `meta.txt` system prompt applies automatically to `muse-*`
|
|
49
|
+
model ids.
|
|
50
|
+
- V1 storage: the OpenCode auth store is the primary source, and the loader falls back to the
|
|
51
|
+
legacy cache only when the store has no key.
|
|
52
|
+
- V2 storage: the credential lives in the OpenCode integration store, and no local cache is
|
|
53
|
+
written on this path.
|
|
54
|
+
- Never re-mints on its own; re-run `/connect` only after a 401, because the mint endpoint is
|
|
55
|
+
aggressively rate-limited.
|
|
56
|
+
- Flow parameters are compatible with the published behavior of oh-my-pi (MIT); see [NOTICE](NOTICE)
|
|
57
|
+
and [AUTH_CONTRACT.md](AUTH_CONTRACT.md).
|
|
30
58
|
|
|
31
59
|
## Requirements
|
|
32
60
|
|
|
33
|
-
- opencode with plugin support
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
The inference key is held by OpenCode (V1 auth store or V2 integration store);
|
|
61
|
-
the legacy cache, when present, stores only `apiKey`, `accountId`, and `email`.
|
|
62
|
-
The OAuth access token is never written to disk. To remove access:
|
|
63
|
-
|
|
64
|
-
- disconnect the provider or log out in OpenCode (this removes the key OpenCode
|
|
65
|
-
uses; the store is `~/.local/share/opencode/auth.json`);
|
|
66
|
-
- delete `~/.config/opencode/muse-code-sub.json` if it exists from an older
|
|
67
|
-
version;
|
|
61
|
+
- opencode with plugin support, including the v2 plugin module shape accepted by opencode 1.18.29+
|
|
62
|
+
and 2.x.
|
|
63
|
+
- Node.js 22.6 or newer for development; CI runs Node.js 22 and 24.
|
|
64
|
+
- An active Muse Code subscription on the Meta account used at login.
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
| Setting | Purpose | Default |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `plugins` entry in `opencode.json` | Loads the plugin. | not set |
|
|
71
|
+
| Auth store | Primary credential storage used by the V1 loader. | `~/.local/share/opencode/auth.json` |
|
|
72
|
+
| Legacy cache | Read-only fallback for credentials written by earlier versions. | `~/.config/opencode/muse-code-sub.json` |
|
|
73
|
+
| `/connect` method | Selects Muse Code subscription login for the `meta` provider. | `meta` provider |
|
|
74
|
+
|
|
75
|
+
## Privacy and security
|
|
76
|
+
|
|
77
|
+
The inference key is held by opencode: in the V1 auth store or the V2 integration store. The
|
|
78
|
+
legacy cache, when present, stores only `apiKey`, `accountId`, and `email`; the OAuth access token
|
|
79
|
+
is never written to disk. The plugin talks only to `auth.meta.com` for the device login and
|
|
80
|
+
`api.meta.ai` for key minting and inference. See [SECURITY.md](SECURITY.md) for vulnerability
|
|
81
|
+
reporting.
|
|
82
|
+
|
|
83
|
+
To remove access:
|
|
84
|
+
|
|
85
|
+
- disconnect the provider or log out in opencode (the store is
|
|
86
|
+
`~/.local/share/opencode/auth.json`);
|
|
87
|
+
- delete `~/.config/opencode/muse-code-sub.json` if an older version created it;
|
|
68
88
|
- run `muse logout` if you also want to clear the Muse Code CLI session;
|
|
69
89
|
- remove `opencode-muse-auth` from `opencode.json`.
|
|
70
90
|
|
|
91
|
+
## Troubleshooting
|
|
92
|
+
|
|
93
|
+
### `/connect` shows no Muse Code subscription method
|
|
94
|
+
|
|
95
|
+
The plugin is not loaded. Check the JSON syntax of `opencode.json`, the plugin name spelling, and
|
|
96
|
+
the package installation, then restart opencode.
|
|
97
|
+
|
|
98
|
+
### Requests fail with 401 or access was revoked
|
|
99
|
+
|
|
100
|
+
A 401 may indicate a rotated or revoked key. Re-run `/connect` once; the plugin never re-mints on
|
|
101
|
+
its own.
|
|
102
|
+
|
|
103
|
+
### Meta rejects subscription requests from this client
|
|
104
|
+
|
|
105
|
+
Meta may return a 403 mentioning `client_attestation_denied` or that Muse Code subscriptions cover
|
|
106
|
+
the Muse Code client only. This is a client restriction, not evidence of an expired key. Signing in
|
|
107
|
+
again does not remove the restriction. Use Muse Code for the subscription, or a separate
|
|
108
|
+
pay-as-you-go Meta Model API key for other clients, subject to Meta's terms.
|
|
109
|
+
|
|
110
|
+
### The login times out or is denied
|
|
111
|
+
|
|
112
|
+
Approve the printed code in the browser before the flow expires, then retry. A login rejected by
|
|
113
|
+
Meta usually means the account has no active Muse Code subscription.
|
|
114
|
+
|
|
115
|
+
### A legacy cache file is still present
|
|
116
|
+
|
|
117
|
+
It is harmless: the V1 loader prefers the OpenCode auth store and reads the cache only when the
|
|
118
|
+
store has no key. Delete the file if you are migrating to V2 storage.
|
|
119
|
+
|
|
71
120
|
## Development
|
|
72
121
|
|
|
73
|
-
|
|
122
|
+
Requirements: Node.js 22.6 or newer and npm.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
git clone https://github.com/TheStreamCode/opencode-muse-auth.git
|
|
126
|
+
cd opencode-muse-auth
|
|
74
127
|
npm install
|
|
75
128
|
npm run build
|
|
76
129
|
npm test
|
|
77
130
|
```
|
|
78
131
|
|
|
132
|
+
`npm test` builds with `tsc` and runs the TypeScript test suite with the Node test runner. CI runs
|
|
133
|
+
the same gate on Node.js 22 and 24; Node 22 needs 22.6 or newer for test-runner type stripping.
|
|
134
|
+
No Meta account is needed for the test suite.
|
|
135
|
+
|
|
136
|
+
## Support
|
|
137
|
+
|
|
138
|
+
Usage questions and workflows belong in GitHub Discussions; reproducible defects belong in GitHub
|
|
139
|
+
Issues using the bug report form. See [SUPPORT.md](SUPPORT.md). Problems in opencode or Meta
|
|
140
|
+
themselves belong to their respective projects.
|
|
141
|
+
|
|
142
|
+
## Contributing
|
|
143
|
+
|
|
144
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Pull requests must pass the required CI checks.
|
|
145
|
+
|
|
79
146
|
## License
|
|
80
147
|
|
|
81
|
-
MIT — see [LICENSE](LICENSE). The software is provided "as is"; see
|
|
148
|
+
MIT — see [`LICENSE`](LICENSE). The software is provided "as is"; see
|
|
82
149
|
[DISCLAIMER.md](DISCLAIMER.md) for the risk notice and liability terms.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
Maintained by [Michael Gasperini (Mikesoft)](https://mikesoft.it).
|
|
154
|
+
If this project is useful to you, consider
|
|
155
|
+
[sponsoring its maintenance](https://github.com/sponsors/TheStreamCode).
|
package/dist/auth.js
CHANGED
|
@@ -101,9 +101,12 @@ export async function pollToken(deviceCode, intervalS, expiresInS) {
|
|
|
101
101
|
let interval = Math.max(MIN_INTERVAL_S, Number(intervalS) || 5);
|
|
102
102
|
const deadline = Date.now() + Number(expiresInS || 600) * 1000;
|
|
103
103
|
let slowDowns = 0;
|
|
104
|
+
let transportFailures = 0;
|
|
104
105
|
while (Date.now() < deadline) {
|
|
105
|
-
// The token endpoint answers
|
|
106
|
-
//
|
|
106
|
+
// The token endpoint answers OAuth errors (e.g. 400 authorization_pending) as part of the
|
|
107
|
+
// normal flow. A rejected fetch, an unparseable body, or a body carrying neither a token nor an
|
|
108
|
+
// OAuth error is a transport/proxy blip, not a verdict: count it and keep polling until the
|
|
109
|
+
// deadline. Only an explicit, terminal OAuth error aborts the device login.
|
|
107
110
|
let body = null;
|
|
108
111
|
try {
|
|
109
112
|
const res = await fetch(TOKEN_URL, {
|
|
@@ -126,18 +129,22 @@ export async function pollToken(deviceCode, intervalS, expiresInS) {
|
|
|
126
129
|
catch {
|
|
127
130
|
body = null;
|
|
128
131
|
}
|
|
129
|
-
if (body &&
|
|
130
|
-
if (typeof body.access_token !== "string" || !body.access_token) {
|
|
131
|
-
throw new Error("token response has no access_token");
|
|
132
|
-
}
|
|
132
|
+
if (body && typeof body.access_token === "string" && body.access_token) {
|
|
133
133
|
return body.access_token;
|
|
134
134
|
}
|
|
135
135
|
if (body?.error === "slow_down") {
|
|
136
136
|
slowDowns += 1;
|
|
137
137
|
interval = Math.max(MIN_INTERVAL_S, interval + SLOW_DOWN_STEP_S);
|
|
138
138
|
}
|
|
139
|
-
else if (body?.error
|
|
140
|
-
|
|
139
|
+
else if (body?.error === "authorization_pending") {
|
|
140
|
+
// Expected while the user completes approval: keep polling at the current interval.
|
|
141
|
+
}
|
|
142
|
+
else if (typeof body?.error === "string" && body.error) {
|
|
143
|
+
throw new Error(`login failed: ${body.error}`);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Unreadable response: no body, no token, and no OAuth error to classify.
|
|
147
|
+
transportFailures += 1;
|
|
141
148
|
}
|
|
142
149
|
const remaining = deadline - Date.now();
|
|
143
150
|
if (remaining <= 0)
|
|
@@ -146,7 +153,11 @@ export async function pollToken(deviceCode, intervalS, expiresInS) {
|
|
|
146
153
|
setTimeout(resolve, Math.min(interval, remaining) * 1000);
|
|
147
154
|
await promise;
|
|
148
155
|
}
|
|
149
|
-
throw new Error(slowDowns
|
|
156
|
+
throw new Error(slowDowns
|
|
157
|
+
? "login timed out after slow_down responses"
|
|
158
|
+
: transportFailures > 0
|
|
159
|
+
? `login timed out waiting for approval after ${transportFailures} network error${transportFailures === 1 ? "" : "s"}`
|
|
160
|
+
: "login timed out waiting for approval");
|
|
150
161
|
}
|
|
151
162
|
export async function mintKey(accessToken) {
|
|
152
163
|
const res = await fetch(KEY_URL, {
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-muse-auth",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Unofficial and experimental: Muse Spark in opencode using a Muse Code subscription credential. May conflict with Meta's terms; use at your own risk. See DISCLAIMER.md.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"license": "MIT",
|
|
9
|
-
"author":
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
"url": "git+https://github.com/TheStreamCode/opencode-muse-auth.git"
|
|
9
|
+
"author": {
|
|
10
|
+
"name": "Michael Gasperini",
|
|
11
|
+
"url": "https://mikesoft.it"
|
|
13
12
|
},
|
|
14
|
-
"
|
|
13
|
+
"repository": "github:TheStreamCode/opencode-muse-auth",
|
|
14
|
+
"homepage": "https://www.npmjs.com/package/opencode-muse-auth",
|
|
15
15
|
"bugs": {
|
|
16
16
|
"url": "https://github.com/TheStreamCode/opencode-muse-auth/issues"
|
|
17
17
|
},
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
"prepublishOnly": "npm run build"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@opencode-ai/plugin": "^1.19.
|
|
46
|
-
"@types/node": "^
|
|
45
|
+
"@opencode-ai/plugin": "^1.19.3",
|
|
46
|
+
"@types/node": "^26.6.5",
|
|
47
47
|
"typescript": "^7.0.2"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@opencode/plugin": "^2.0.
|
|
50
|
+
"@opencode/plugin": "^2.0.32"
|
|
51
51
|
}
|
|
52
52
|
}
|