opencode-muse-auth 0.3.0 → 0.4.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.
- package/AUTH_CONTRACT.md +32 -0
- package/DISCLAIMER.md +62 -0
- package/README.md +34 -11
- package/dist/auth.d.ts +8 -0
- package/dist/auth.js +22 -0
- package/dist/index.js +46 -15
- package/package.json +5 -3
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/README.md
CHANGED
|
@@ -3,13 +3,19 @@
|
|
|
3
3
|
Use Meta **Muse Spark** in [opencode](https://opencode.ai) billed to your
|
|
4
4
|
**Muse Code monthly subscription** — no pay-as-you-go API key, no third-party CLI.
|
|
5
5
|
|
|
6
|
+
> **Unofficial and experimental.** This plugin uses a Muse Code subscription
|
|
7
|
+
> credential from opencode. Meta's terms limit that credential to Muse Code, so
|
|
8
|
+
> this use may not be supported and Meta may suspend or revoke accounts or
|
|
9
|
+
> credentials. Use it at your own risk; the authors cannot recover an account.
|
|
10
|
+
> Read [DISCLAIMER.md](DISCLAIMER.md) before installing.
|
|
11
|
+
|
|
6
12
|
## Install
|
|
7
13
|
|
|
8
14
|
```jsonc
|
|
9
15
|
// opencode.json
|
|
10
16
|
{
|
|
11
17
|
"$schema": "https://opencode.ai/config.json",
|
|
12
|
-
"
|
|
18
|
+
"plugins": ["opencode-muse-auth"]
|
|
13
19
|
}
|
|
14
20
|
```
|
|
15
21
|
|
|
@@ -19,7 +25,7 @@ Enter the code in your browser (your own Meta account) and the login
|
|
|
19
25
|
completes by itself.
|
|
20
26
|
|
|
21
27
|
Verified end-to-end against the published package: isolated project with
|
|
22
|
-
only `"
|
|
28
|
+
only `"plugins": ["opencode-muse-auth"]`, live chat on `meta/muse-spark-1.3`
|
|
23
29
|
billed to subscription.
|
|
24
30
|
|
|
25
31
|
## Requirements
|
|
@@ -35,16 +41,32 @@ automatically to `muse-*` model ids):
|
|
|
35
41
|
|
|
36
42
|
- **Login** — Meta device-code exchange (RFC 8628) inside `/connect`, then
|
|
37
43
|
mints the stable account-bound inference key via the Model API. The key is
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
The
|
|
44
|
-
|
|
44
|
+
never printed and the OAuth access token from the login flow is kept in
|
|
45
|
+
memory only.
|
|
46
|
+
- **V1 storage** — OpenCode's auth store is the primary source; the auth
|
|
47
|
+
`loader` falls back to the legacy cache
|
|
48
|
+
(`~/.config/opencode/muse-code-sub.json`) only when the store has no key.
|
|
49
|
+
The cache is still written so upgrades keep working.
|
|
50
|
+
- **V2 storage** — the credential lives in OpenCode's integration store; the
|
|
51
|
+
plugin does not write the local cache on this path.
|
|
52
|
+
- **Runtime** — the mint endpoint is aggressively rate-limited, so the plugin
|
|
53
|
+
never re-mints on its own; re-run `/connect` only if access is revoked (401).
|
|
45
54
|
|
|
46
55
|
Flow parameters are compatible with the published behavior of oh-my-pi (MIT);
|
|
47
|
-
see [NOTICE](NOTICE).
|
|
56
|
+
see [NOTICE](NOTICE) and [AUTH_CONTRACT.md](AUTH_CONTRACT.md).
|
|
57
|
+
|
|
58
|
+
## Credentials and removal
|
|
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;
|
|
68
|
+
- run `muse logout` if you also want to clear the Muse Code CLI session;
|
|
69
|
+
- remove `opencode-muse-auth` from `opencode.json`.
|
|
48
70
|
|
|
49
71
|
## Development
|
|
50
72
|
|
|
@@ -56,4 +78,5 @@ npm test
|
|
|
56
78
|
|
|
57
79
|
## License
|
|
58
80
|
|
|
59
|
-
MIT — see [LICENSE](LICENSE).
|
|
81
|
+
MIT — see [LICENSE](LICENSE). The software is provided "as is"; see
|
|
82
|
+
[DISCLAIMER.md](DISCLAIMER.md) for the risk notice and liability terms.
|
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
|
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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-muse-auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Muse Spark in opencode
|
|
3
|
+
"version": "0.4.0",
|
|
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",
|
|
@@ -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",
|