opencode-muse-auth 0.3.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/AUTH_CONTRACT.md +32 -0
- package/DISCLAIMER.md +62 -0
- package/LICENSE +1 -1
- package/README.md +125 -29
- package/dist/auth.d.ts +8 -0
- package/dist/auth.js +42 -9
- package/dist/index.js +46 -15
- package/package.json +13 -11
package/AUTH_CONTRACT.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Muse auth contract (v1)
|
|
2
|
+
|
|
3
|
+
The three Muse Code plugins implement the same Meta device-code flow. Change
|
|
4
|
+
this file and the pinned constants/tests together in all three repositories:
|
|
5
|
+
|
|
6
|
+
- `opencode-muse-auth`
|
|
7
|
+
- `hermes-muse-code`
|
|
8
|
+
- `openclaw-muse-code`
|
|
9
|
+
|
|
10
|
+
## Wire contract
|
|
11
|
+
|
|
12
|
+
| Item | Value |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `CLIENT_ID` | `1031625952748946` |
|
|
15
|
+
| Device authorization | `https://auth.meta.com/oidc/device/authorization/` |
|
|
16
|
+
| Device token | `https://auth.meta.com/oidc/device/token/` |
|
|
17
|
+
| Key mint | `https://api.meta.ai/muse-code/key` |
|
|
18
|
+
| API version header | `x-api-version: 1.0.0` |
|
|
19
|
+
| Cache fields | `apiKey`, `accountId`, `email` (never the OAuth access token) |
|
|
20
|
+
| Inference base URL | `https://api.meta.ai/v1` |
|
|
21
|
+
|
|
22
|
+
## Behaviour
|
|
23
|
+
|
|
24
|
+
- Device flow: request a device code, poll the token endpoint, accept
|
|
25
|
+
`authorization_pending`, increase the interval on `slow_down`, stop on a
|
|
26
|
+
terminal error or at `expires_in`.
|
|
27
|
+
- Key mint: exchange the OAuth access token for the account-bound inference
|
|
28
|
+
key; reject an inactive subscription or a pending payment.
|
|
29
|
+
- Persistence: only the inference key and account identity are stored; the
|
|
30
|
+
OAuth access token stays in memory.
|
|
31
|
+
- Any change to this contract bumps `AUTH_CONTRACT_VERSION` in the code and
|
|
32
|
+
requires updating the pinned tests in all three repositories.
|
package/DISCLAIMER.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Disclaimer and risk notice
|
|
2
|
+
|
|
3
|
+
This project is an unofficial, community-built integration. It is not affiliated
|
|
4
|
+
with, endorsed by, or supported by Meta Platforms, Inc. or its affiliates
|
|
5
|
+
("Meta"). "Meta", "Muse", "Muse Code", "Muse Spark" and related names are
|
|
6
|
+
trademarks of Meta.
|
|
7
|
+
|
|
8
|
+
## Why this notice exists
|
|
9
|
+
|
|
10
|
+
This plugin signs in with a Meta account that has an active Muse Code
|
|
11
|
+
subscription and uses the subscription-linked credential to send inference
|
|
12
|
+
requests from opencode, a third-party coding client.
|
|
13
|
+
|
|
14
|
+
Meta's [Meta Model API Terms of Service](https://dev.meta.ai/legal/terms-of-service),
|
|
15
|
+
Section 3.2, states that a credential connected for a Coding Harness
|
|
16
|
+
Subscription "is for use only with the coding harness under your Coding Harness
|
|
17
|
+
Subscription" and "may not be used to access the Services outside the coding
|
|
18
|
+
harness or to circumvent your subscription's usage limits or billing". Meta's
|
|
19
|
+
[Muse Code subscription documentation](https://dev.meta.ai/docs/muse-code/subscriptions)
|
|
20
|
+
likewise states that the subscription credential "is for use with Muse Code
|
|
21
|
+
only".
|
|
22
|
+
|
|
23
|
+
This project therefore relies on a use that Meta does not document as supported.
|
|
24
|
+
Meta may change, restrict or enforce its terms at any time, and may suspend,
|
|
25
|
+
restrict or terminate accounts, subscriptions or credentials involved. The
|
|
26
|
+
authors cannot prevent, predict or reverse that outcome.
|
|
27
|
+
|
|
28
|
+
## No warranty, no guarantee
|
|
29
|
+
|
|
30
|
+
- The integration is provided "as is"; there is no guarantee that it is
|
|
31
|
+
permitted by Meta's terms, that it will keep working, or that the subscription
|
|
32
|
+
will remain available.
|
|
33
|
+
- The authors cannot restore an account, subscription, credential or data after
|
|
34
|
+
Meta takes action, and cannot intervene with Meta on your behalf.
|
|
35
|
+
- This notice is informational and is not legal advice.
|
|
36
|
+
|
|
37
|
+
## Your responsibility
|
|
38
|
+
|
|
39
|
+
By installing or using this software you acknowledge that:
|
|
40
|
+
|
|
41
|
+
1. you are responsible for complying with Meta's terms, including Section 3.2;
|
|
42
|
+
2. you use your own account and credentials and accept the risk of account or
|
|
43
|
+
subscription action by Meta;
|
|
44
|
+
3. you will not use this software where losing access would be unacceptable, and
|
|
45
|
+
you will not share credential caches or exported keys;
|
|
46
|
+
4. you will delete cached credentials when you stop using the software.
|
|
47
|
+
|
|
48
|
+
## Limitation of liability
|
|
49
|
+
|
|
50
|
+
To the maximum extent permitted by applicable law, the authors and contributors
|
|
51
|
+
are not liable for any loss or damage arising out of or related to this software
|
|
52
|
+
or its use, including account suspension, subscription termination, credential
|
|
53
|
+
revocation, loss of data, or service interruption. Nothing in this notice
|
|
54
|
+
excludes or limits liability that cannot be excluded or limited by applicable
|
|
55
|
+
law. The [MIT License](LICENSE) contains the warranty disclaimer and liability
|
|
56
|
+
limitation that govern the software; this notice does not modify it.
|
|
57
|
+
|
|
58
|
+
## Clearly supported alternatives
|
|
59
|
+
|
|
60
|
+
If you need an integration that is unambiguously within Meta's terms, use a Meta
|
|
61
|
+
Model API key billed pay-as-you-go, or drive Muse Code through its official
|
|
62
|
+
`muse serve` session interface.
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,59 +1,155 @@
|
|
|
1
1
|
# opencode-muse-auth
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Muse Code subscription sign-in for opencode
|
|
4
|
+
|
|
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.
|
|
5
17
|
|
|
6
18
|
## Install
|
|
7
19
|
|
|
20
|
+
Add the plugin to `opencode.json`; opencode installs it from npm:
|
|
21
|
+
|
|
8
22
|
```jsonc
|
|
9
23
|
// opencode.json
|
|
10
24
|
{
|
|
11
25
|
"$schema": "https://opencode.ai/config.json",
|
|
12
|
-
"
|
|
26
|
+
"plugins": ["opencode-muse-auth"]
|
|
13
27
|
}
|
|
14
28
|
```
|
|
15
29
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
20
43
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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).
|
|
24
58
|
|
|
25
59
|
## Requirements
|
|
26
60
|
|
|
27
|
-
- opencode with plugin support
|
|
28
|
-
|
|
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;
|
|
88
|
+
- run `muse logout` if you also want to clear the Muse Code CLI session;
|
|
89
|
+
- remove `opencode-muse-auth` from `opencode.json`.
|
|
90
|
+
|
|
91
|
+
## Troubleshooting
|
|
29
92
|
|
|
30
|
-
|
|
93
|
+
### `/connect` shows no Muse Code subscription method
|
|
31
94
|
|
|
32
|
-
The plugin
|
|
33
|
-
|
|
34
|
-
automatically to `muse-*` model ids):
|
|
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.
|
|
35
97
|
|
|
36
|
-
|
|
37
|
-
mints the stable account-bound inference key via the Model API. The key is
|
|
38
|
-
cached locally (`~/.config/opencode/muse-code-sub.json`, owner-only
|
|
39
|
-
permissions where supported) and never printed. The cache stores exactly
|
|
40
|
-
`apiKey`, `accountId`, and `email` — the OAuth access token from the
|
|
41
|
-
login flow is kept in memory only and never written to disk.
|
|
42
|
-
- **Runtime** — an auth `loader` injects the cached key on every startup.
|
|
43
|
-
The mint endpoint is aggressively rate-limited, so the plugin never
|
|
44
|
-
re-mints on its own; re-run `/connect` only if access is revoked (401).
|
|
98
|
+
### Requests fail with 401 or access was revoked
|
|
45
99
|
|
|
46
|
-
|
|
47
|
-
|
|
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.
|
|
48
119
|
|
|
49
120
|
## Development
|
|
50
121
|
|
|
51
|
-
|
|
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
|
|
52
127
|
npm install
|
|
53
128
|
npm run build
|
|
54
129
|
npm test
|
|
55
130
|
```
|
|
56
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
|
+
|
|
57
146
|
## License
|
|
58
147
|
|
|
59
|
-
MIT — see [LICENSE](LICENSE).
|
|
148
|
+
MIT — see [`LICENSE`](LICENSE). The software is provided "as is"; see
|
|
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.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const CLIENT_ID = "1031625952748946";
|
|
|
2
2
|
export declare const DEVICE_URL = "https://auth.meta.com/oidc/device/authorization/";
|
|
3
3
|
export declare const TOKEN_URL = "https://auth.meta.com/oidc/device/token/";
|
|
4
4
|
export declare const KEY_URL = "https://api.meta.ai/muse-code/key";
|
|
5
|
+
export declare const AUTH_CONTRACT_VERSION = 1;
|
|
5
6
|
export declare const CACHE_PATH: string;
|
|
6
7
|
export interface DeviceAuthorization {
|
|
7
8
|
device_code: string;
|
|
@@ -24,6 +25,13 @@ export interface CachedCredential {
|
|
|
24
25
|
email?: string;
|
|
25
26
|
}
|
|
26
27
|
export declare function readCache(path?: string): Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the provider key for the V1 auth loader.
|
|
30
|
+
*
|
|
31
|
+
* OpenCode's auth store is authoritative; the local cache is a legacy fallback
|
|
32
|
+
* for installs created before the host store became the primary source.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveLoaderKey(auth: () => Promise<unknown>, readCacheFn?: () => Promise<string>): Promise<string>;
|
|
27
35
|
export declare function writeCache(credentials: MintedCredential, path?: string): Promise<void>;
|
|
28
36
|
export declare function deviceAuthorize(): Promise<DeviceAuthorization>;
|
|
29
37
|
export declare function pollToken(deviceCode: string, intervalS: number, expiresInS: number): Promise<string>;
|
package/dist/auth.js
CHANGED
|
@@ -8,6 +8,7 @@ export const CLIENT_ID = "1031625952748946";
|
|
|
8
8
|
export const DEVICE_URL = "https://auth.meta.com/oidc/device/authorization/";
|
|
9
9
|
export const TOKEN_URL = "https://auth.meta.com/oidc/device/token/";
|
|
10
10
|
export const KEY_URL = "https://api.meta.ai/muse-code/key";
|
|
11
|
+
export const AUTH_CONTRACT_VERSION = 1;
|
|
11
12
|
export const CACHE_PATH = join(homedir(), ".config", "opencode", "muse-code-sub.json");
|
|
12
13
|
const HEADERS = {
|
|
13
14
|
Accept: "application/json",
|
|
@@ -25,6 +26,27 @@ export async function readCache(path = CACHE_PATH) {
|
|
|
25
26
|
return "";
|
|
26
27
|
}
|
|
27
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the provider key for the V1 auth loader.
|
|
31
|
+
*
|
|
32
|
+
* OpenCode's auth store is authoritative; the local cache is a legacy fallback
|
|
33
|
+
* for installs created before the host store became the primary source.
|
|
34
|
+
*/
|
|
35
|
+
export async function resolveLoaderKey(auth, readCacheFn = readCache) {
|
|
36
|
+
try {
|
|
37
|
+
const stored = await auth();
|
|
38
|
+
if (stored && typeof stored === "object") {
|
|
39
|
+
const candidate = stored;
|
|
40
|
+
if (candidate.type === "api" && typeof candidate.key === "string" && candidate.key.trim()) {
|
|
41
|
+
return candidate.key.trim();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Fall back to the legacy cache below.
|
|
47
|
+
}
|
|
48
|
+
return readCacheFn();
|
|
49
|
+
}
|
|
28
50
|
export async function writeCache(credentials, path = CACHE_PATH) {
|
|
29
51
|
// Retention minimization: persist only what inference needs. The OAuth
|
|
30
52
|
// access token has unknown broader scope and nothing reads it back, so it
|
|
@@ -79,9 +101,12 @@ export async function pollToken(deviceCode, intervalS, expiresInS) {
|
|
|
79
101
|
let interval = Math.max(MIN_INTERVAL_S, Number(intervalS) || 5);
|
|
80
102
|
const deadline = Date.now() + Number(expiresInS || 600) * 1000;
|
|
81
103
|
let slowDowns = 0;
|
|
104
|
+
let transportFailures = 0;
|
|
82
105
|
while (Date.now() < deadline) {
|
|
83
|
-
// The token endpoint answers
|
|
84
|
-
//
|
|
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.
|
|
85
110
|
let body = null;
|
|
86
111
|
try {
|
|
87
112
|
const res = await fetch(TOKEN_URL, {
|
|
@@ -104,18 +129,22 @@ export async function pollToken(deviceCode, intervalS, expiresInS) {
|
|
|
104
129
|
catch {
|
|
105
130
|
body = null;
|
|
106
131
|
}
|
|
107
|
-
if (body &&
|
|
108
|
-
if (typeof body.access_token !== "string" || !body.access_token) {
|
|
109
|
-
throw new Error("token response has no access_token");
|
|
110
|
-
}
|
|
132
|
+
if (body && typeof body.access_token === "string" && body.access_token) {
|
|
111
133
|
return body.access_token;
|
|
112
134
|
}
|
|
113
135
|
if (body?.error === "slow_down") {
|
|
114
136
|
slowDowns += 1;
|
|
115
137
|
interval = Math.max(MIN_INTERVAL_S, interval + SLOW_DOWN_STEP_S);
|
|
116
138
|
}
|
|
117
|
-
else if (body?.error
|
|
118
|
-
|
|
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;
|
|
119
148
|
}
|
|
120
149
|
const remaining = deadline - Date.now();
|
|
121
150
|
if (remaining <= 0)
|
|
@@ -124,7 +153,11 @@ export async function pollToken(deviceCode, intervalS, expiresInS) {
|
|
|
124
153
|
setTimeout(resolve, Math.min(interval, remaining) * 1000);
|
|
125
154
|
await promise;
|
|
126
155
|
}
|
|
127
|
-
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");
|
|
128
161
|
}
|
|
129
162
|
export async function mintKey(accessToken) {
|
|
130
163
|
const res = await fetch(KEY_URL, {
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
import { Plugin as V2Plugin, Integration } from "@opencode/plugin";
|
|
2
|
-
import {
|
|
3
|
-
const
|
|
2
|
+
import { writeCache, deviceAuthorize, pollToken, mintKey, resolveLoaderKey, } from "./auth.js";
|
|
3
|
+
const TEN_YEARS_MS = 10 * 365 * 24 * 60 * 60 * 1000;
|
|
4
|
+
/** Keep diagnostics useful without ever copying a credential into a log line. */
|
|
5
|
+
function describeError(error) {
|
|
6
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
7
|
+
return text
|
|
8
|
+
.replace(/Bearer\s+\S+/gi, "Bearer ***")
|
|
9
|
+
.replace(/LLM\|[^\s"']+/g, "LLM|***")
|
|
10
|
+
.slice(0, 300);
|
|
11
|
+
}
|
|
12
|
+
async function logAuthFailure(client, error) {
|
|
13
|
+
try {
|
|
14
|
+
const candidate = client;
|
|
15
|
+
await candidate?.app?.log?.({
|
|
16
|
+
body: {
|
|
17
|
+
service: "opencode-muse-auth",
|
|
18
|
+
level: "error",
|
|
19
|
+
message: "Muse Code login failed",
|
|
20
|
+
extra: { error: describeError(error) },
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Logging must never break the auth flow.
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const MuseCodeAuth = async ({ client }) => {
|
|
4
29
|
return {
|
|
5
30
|
auth: {
|
|
6
31
|
provider: "meta",
|
|
@@ -22,7 +47,8 @@ const MuseCodeAuth = async () => {
|
|
|
22
47
|
await writeCache(minted);
|
|
23
48
|
return { type: "success", key: minted.apiKey };
|
|
24
49
|
}
|
|
25
|
-
catch {
|
|
50
|
+
catch (error) {
|
|
51
|
+
await logAuthFailure(client, error);
|
|
26
52
|
return { type: "failed" };
|
|
27
53
|
}
|
|
28
54
|
},
|
|
@@ -30,8 +56,8 @@ const MuseCodeAuth = async () => {
|
|
|
30
56
|
},
|
|
31
57
|
},
|
|
32
58
|
],
|
|
33
|
-
loader: async () => {
|
|
34
|
-
const key = await
|
|
59
|
+
loader: async (auth) => {
|
|
60
|
+
const key = await resolveLoaderKey(auth);
|
|
35
61
|
return key ? { apiKey: key } : {};
|
|
36
62
|
},
|
|
37
63
|
},
|
|
@@ -54,16 +80,21 @@ export default {
|
|
|
54
80
|
const device = await deviceAuthorize();
|
|
55
81
|
const url = device.verification_uri_complete || device.verification_uri;
|
|
56
82
|
const callback = (async () => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
try {
|
|
84
|
+
const access = await pollToken(device.device_code, device.interval, device.expires_in);
|
|
85
|
+
const minted = await mintKey(access);
|
|
86
|
+
return {
|
|
87
|
+
type: "oauth",
|
|
88
|
+
methodID,
|
|
89
|
+
access: minted.apiKey,
|
|
90
|
+
refresh: "",
|
|
91
|
+
expires: Date.now() + TEN_YEARS_MS,
|
|
92
|
+
metadata: { accountId: minted.accountId, ...(minted.email ? { email: minted.email } : {}) },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
throw new Error(`Muse Code login failed: ${describeError(error)}`);
|
|
97
|
+
}
|
|
67
98
|
})();
|
|
68
99
|
// The runtime awaits this promise after displaying the device code.
|
|
69
100
|
// Attach a handler immediately in case the exchange fails first.
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-muse-auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Muse Spark in opencode
|
|
3
|
+
"version": "0.4.1",
|
|
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
|
},
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
"dist",
|
|
32
32
|
"README.md",
|
|
33
33
|
"LICENSE",
|
|
34
|
-
"NOTICE"
|
|
34
|
+
"NOTICE",
|
|
35
|
+
"DISCLAIMER.md",
|
|
36
|
+
"AUTH_CONTRACT.md"
|
|
35
37
|
],
|
|
36
38
|
"scripts": {
|
|
37
39
|
"build": "tsc",
|
|
@@ -40,11 +42,11 @@
|
|
|
40
42
|
"prepublishOnly": "npm run build"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
|
-
"@opencode-ai/plugin": "^1.19.
|
|
44
|
-
"@types/node": "^
|
|
45
|
+
"@opencode-ai/plugin": "^1.19.3",
|
|
46
|
+
"@types/node": "^26.6.5",
|
|
45
47
|
"typescript": "^7.0.2"
|
|
46
48
|
},
|
|
47
49
|
"dependencies": {
|
|
48
|
-
"@opencode/plugin": "^2.0.
|
|
50
|
+
"@opencode/plugin": "^2.0.32"
|
|
49
51
|
}
|
|
50
52
|
}
|