@thestreamcode/dsh-muse-code 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.
@@ -0,0 +1,33 @@
1
+ # Muse auth contract (v1)
2
+
3
+ This bundle implements the same Meta device-code flow as the other Muse Code
4
+ plugins. Change this file and the pinned constants/tests together:
5
+
6
+ - `opencode-muse-auth`
7
+ - `hermes-muse-code`
8
+ - `openclaw-muse-code`
9
+ - `dsh-muse-code`
10
+
11
+ ## Wire contract
12
+
13
+ | Item | Value |
14
+ |---|---|
15
+ | `CLIENT_ID` | `1031625952748946` |
16
+ | Device authorization | `https://auth.meta.com/oidc/device/authorization/` |
17
+ | Device token | `https://auth.meta.com/oidc/device/token/` |
18
+ | Key mint | `https://api.meta.ai/muse-code/key` |
19
+ | API version header | `x-api-version: 1.0.0` |
20
+ | Credential reference | `MUSE_CODE_SUB_TOKEN` |
21
+ | Inference base URL | `https://api.meta.ai/v1` |
22
+
23
+ ## Behaviour
24
+
25
+ - Device flow: request a device code, poll the token endpoint, accept
26
+ `authorization_pending`, increase the interval on `slow_down`, stop on a
27
+ terminal error or at `expires_in`.
28
+ - Key mint: exchange the OAuth access token for the account-bound inference
29
+ key; reject an inactive subscription or a pending payment.
30
+ - Persistence: the key is stored through the DSH credential seam under
31
+ `MUSE_CODE_SUB_TOKEN`; the OAuth access token stays in memory.
32
+ - Any change to this contract bumps `AUTH_CONTRACT_VERSION` in the code and
33
+ requires updating the pinned tests in all four 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"), or by DeepSeek. "Meta", "Muse", "Muse Code", "Muse Spark" and related
6
+ names are 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 DeepSeek Harness, 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 stored 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Michael Gasperini (Mikesoft)
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,160 @@
1
- # Temporary Holding Version
1
+ # dsh-muse-code
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
+ Muse Code subscription models and device-code sign-in for DeepSeek Harness
4
+
5
+ [![CI](https://github.com/TheStreamCode/dsh-muse-code/actions/workflows/ci.yml/badge.svg)](https://github.com/TheStreamCode/dsh-muse-code/actions/workflows/ci.yml)
6
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![Release](https://img.shields.io/github/v/release/TheStreamCode/dsh-muse-code)](https://github.com/TheStreamCode/dsh-muse-code/releases/latest)
8
+
9
+ DeepSeek Harness (DSH) bundle that serves Meta Muse Code models through a Muse Code
10
+ subscription and adds a native device-code login flow. No API key is copied by hand: the
11
+ plugin runs Meta's device login, mints the account-bound inference key, and stores it in the
12
+ DSH credential store.
13
+
14
+ > **Unofficial and experimental.** This plugin uses a Muse Code subscription credential from
15
+ > DeepSeek Harness. Meta's terms limit that credential to Muse Code, so this use may not be
16
+ > supported and Meta may suspend or revoke accounts or credentials. Use it at your own risk;
17
+ > the authors cannot recover an account. Read [DISCLAIMER.md](DISCLAIMER.md) before installing.
18
+
19
+ ## Install
20
+
21
+ From the npm registry:
22
+
23
+ ```bash
24
+ dsh plugin --profile desktop add @thestreamcode/dsh-muse-code
25
+ ```
26
+
27
+ From the public GitHub repository:
28
+
29
+ ```bash
30
+ dsh plugin --profile desktop add github:TheStreamCode/dsh-muse-code
31
+ ```
32
+
33
+ From a local checkout:
34
+
35
+ ```bash
36
+ dsh plugin --profile desktop add link:C:\path\to\dsh-muse-code
37
+ ```
38
+
39
+ The bundle inserts its route and auth plugin through `cordis.patch.yml`; no manual profile
40
+ edit is required. Restart DSH if the profile does not pick up the bundle immediately.
41
+
42
+ ## Quick start
43
+
44
+ 1. Install the bundle with the command above.
45
+ 2. Open **Settings**, select **Authorization** (the credential and sign-in surface), then
46
+ **Muse Code (subscription)**.
47
+ 3. Open the printed Meta URL, enter the printed code, and approve the login.
48
+ 4. Select the `muse-code` provider and a Muse model in the DSH model picker, for example
49
+ `muse-code/muse-spark-1.3`.
50
+
51
+ The plugin commits the minted key as the `MUSE_CODE_SUB_TOKEN` credential, and the route
52
+ reads it per request, so the key never appears in a configuration file.
53
+
54
+ ## Features
55
+
56
+ - Adds a `muse-code` model route through a second `@deepseek-ai/dsh-llm-pi-ai` instance
57
+ (`api: openai-responses`, `baseURL: https://api.meta.ai/v1`), so the existing `llm-pi-ai`
58
+ configuration is not replaced.
59
+ - Registers the **Muse Code (subscription)** authorization flow: Meta device login, key
60
+ mint, and credential commit.
61
+ - Stores the key in the DSH credential store under `MUSE_CODE_SUB_TOKEN` and reads it through
62
+ `apiKeyEnv`.
63
+ - Serves `muse-spark-1.3`, `muse-spark-1.2`, `muse-wonder-1.0`, and `muse-wonder-full-1.0`
64
+ with text and image input, a 1,048,576-token context window, and reasoning-effort mapping
65
+ from `minimal` to `max`.
66
+ - Accepts an explicit `MUSE_CODE_SUB_TOKEN` environment variable instead of the stored
67
+ credential.
68
+
69
+ ## Requirements
70
+
71
+ - DeepSeek Harness `0.2.1-rc.2` or newer.
72
+ - Node.js 20 or newer on the DSH host; CI runs Node.js 20, 22, and 24.
73
+ - A Meta account with an active Muse Code subscription.
74
+ - The plugin needs no separate service and no third-party CLI.
75
+
76
+ ## Configuration
77
+
78
+ | Setting | Purpose | Default |
79
+ | --- | --- | --- |
80
+ | `MUSE_CODE_SUB_TOKEN` | Inference key read by the `muse-code` route through `apiKeyEnv`. An explicit value wins over the stored credential. | from the authorization flow |
81
+ | `muse-code` provider | Provider entry added by `cordis.patch.yml`; select it in the DSH model picker. | enabled after install |
82
+ | Model id | Model served by the route; availability depends on the subscription plan. | `muse-spark-1.3` |
83
+ | Reasoning effort | Maps DSH effort levels to Meta's wire vocabulary. | provider default |
84
+
85
+ ## Privacy and security
86
+
87
+ The subscription key is stored only in the DSH credential store under `MUSE_CODE_SUB_TOKEN`;
88
+ it is never written to a configuration file, and the OAuth access token stays in memory. The
89
+ plugin talks only to `auth.meta.com` for the device login and `api.meta.ai` for key minting and
90
+ inference. Do not share credential caches or exported keys.
91
+
92
+ To remove access, delete the `MUSE_CODE_SUB_TOKEN` credential from the DSH credential store or
93
+ sign out of the authorization record, then remove the bundle:
94
+
95
+ ```bash
96
+ dsh plugin --profile desktop remove @thestreamcode/dsh-muse-code
97
+ ```
98
+
99
+ See [SECURITY.md](SECURITY.md) for vulnerability reporting and
100
+ [AUTH_CONTRACT.md](AUTH_CONTRACT.md) for the pinned login contract.
101
+
102
+ ## Troubleshooting
103
+
104
+ ### The provider does not appear in the model picker
105
+
106
+ The bundle was not loaded by the active profile. Re-run the install command with the same
107
+ profile, restart DSH, and check that `cordis.patch.yml` is part of the installed bundle.
108
+
109
+ ### Muse Code subscription is inactive for this account
110
+
111
+ The signed-in account has no active Muse Code subscription, or the subscription lapsed.
112
+ Renew it, or sign in with the account that holds the subscription.
113
+
114
+ ### The device login expires or is denied
115
+
116
+ Start the authorization flow again and approve the printed code before the expiry printed by
117
+ the flow. The login completes in your own browser on Meta's authorization page.
118
+
119
+ ### Requests fail with 401 after a subscription change
120
+
121
+ The stored key was revoked or rotated. Remove the `MUSE_CODE_SUB_TOKEN` credential, run the
122
+ authorization flow again, and restart DSH if a running process cached the old value.
123
+
124
+ ## Development
125
+
126
+ Requirements: Node.js 20 or newer and npm.
127
+
128
+ ```bash
129
+ git clone https://github.com/TheStreamCode/dsh-muse-code.git
130
+ cd dsh-muse-code
131
+ npm install
132
+ npm run typecheck
133
+ npm test
134
+ ```
135
+
136
+ `npm test` builds `lib/` and runs the `node:test` suite. `lib/` is committed because DSH
137
+ bundle installs from Git may not run a build; regenerate it with `npm run build` after any
138
+ source change. The offline tests cover the device flow, the credential wiring, and the plugin
139
+ manifest; no Meta account is needed.
140
+
141
+ ## Support
142
+
143
+ Usage questions and workflows belong in GitHub Discussions; reproducible defects belong in
144
+ GitHub Issues using the bug report form. See [SUPPORT.md](SUPPORT.md). The plugin cannot
145
+ obtain, restore, or protect a Meta account, subscription, or credential.
146
+
147
+ ## Contributing
148
+
149
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Pull requests must pass the required CI checks.
150
+
151
+ ## License
152
+
153
+ MIT — see [`LICENSE`](LICENSE). The software is provided "as is"; see
154
+ [DISCLAIMER.md](DISCLAIMER.md) for the risk notice and liability terms.
155
+
156
+ ---
157
+
158
+ Maintained by [Michael Gasperini (Mikesoft)](https://mikesoft.it).
159
+ If this project is useful to you, consider
160
+ [sponsoring its maintenance](https://github.com/sponsors/TheStreamCode).
@@ -0,0 +1,68 @@
1
+ # Muse Code subscription provider for DeepSeek Harness.
2
+ #
3
+ # A second pi-ai adapter instance serves the `muse-code` route, so the
4
+ # existing `llm-pi-ai` provider configuration is not replaced. The auth
5
+ # plugin registers the device-code flow and stores the minted key under the
6
+ # MUSE_CODE_SUB_TOKEN credential reference used by apiKeyEnv.
7
+ - insert:
8
+ - id: muse-code-llm
9
+ name: '@deepseek-ai/dsh-llm-pi-ai'
10
+ config:
11
+ providers:
12
+ muse-code:
13
+ displayName: Muse Code (subscription)
14
+ apiKeyEnv: MUSE_CODE_SUB_TOKEN
15
+ api: openai-responses
16
+ baseURL: https://api.meta.ai/v1
17
+ models:
18
+ - id: muse-spark-1.3
19
+ name: Muse Spark 1.3
20
+ contextWindow: 1048576
21
+ maxTokens: 131072
22
+ input: [text, image]
23
+ reasoningEfforts:
24
+ minimal: minimal
25
+ low: low
26
+ medium: medium
27
+ high: high
28
+ xhigh: xhigh
29
+ max: max
30
+ - id: muse-spark-1.2
31
+ name: Muse Spark 1.2
32
+ contextWindow: 1048576
33
+ maxTokens: 131072
34
+ input: [text, image]
35
+ reasoningEfforts:
36
+ minimal: minimal
37
+ low: low
38
+ medium: medium
39
+ high: high
40
+ xhigh: xhigh
41
+ max: max
42
+ - id: muse-wonder-1.0
43
+ name: Muse Wonder 1.0
44
+ contextWindow: 1048576
45
+ maxTokens: 131072
46
+ input: [text, image]
47
+ reasoningEfforts:
48
+ minimal: minimal
49
+ low: low
50
+ medium: medium
51
+ high: high
52
+ xhigh: xhigh
53
+ max: max
54
+ - id: muse-wonder-full-1.0
55
+ name: Muse Wonder Full 1.0
56
+ contextWindow: 1048576
57
+ maxTokens: 131072
58
+ input: [text, image]
59
+ reasoningEfforts:
60
+ minimal: minimal
61
+ low: low
62
+ medium: medium
63
+ high: high
64
+ xhigh: xhigh
65
+ max: max
66
+ - id: muse-code-auth
67
+ name: '@thestreamcode/dsh-muse-code'
68
+ config: {}
package/lib/auth.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ export declare const CLIENT_ID = "1031625952748946";
2
+ export declare const DEVICE_URL = "https://auth.meta.com/oidc/device/authorization/";
3
+ export declare const TOKEN_URL = "https://auth.meta.com/oidc/device/token/";
4
+ export declare const KEY_URL = "https://api.meta.ai/muse-code/key";
5
+ export declare const API_VERSION = "1.0.0";
6
+ export declare const AUTH_CONTRACT_VERSION = 1;
7
+ export interface DeviceAuthorization {
8
+ device_code: string;
9
+ user_code: string;
10
+ verification_uri: string;
11
+ verification_uri_complete?: string;
12
+ interval: number;
13
+ expires_in: number;
14
+ }
15
+ export interface MintedCredential {
16
+ apiKey: string;
17
+ accountId: string;
18
+ email?: string;
19
+ }
20
+ export declare function deviceAuthorize(signal?: AbortSignal): Promise<DeviceAuthorization>;
21
+ export declare function pollToken(deviceCode: string, intervalS: number, expiresInS: number, signal?: AbortSignal): Promise<string>;
22
+ export declare function mintKey(accessToken: string, signal?: AbortSignal): Promise<MintedCredential>;
package/lib/auth.js ADDED
@@ -0,0 +1,157 @@
1
+ // Meta device-code login and subscription-key minting for Muse Code.
2
+ // The wire contract is shared with the other Muse Code plugins; see
3
+ // AUTH_CONTRACT.md before changing any constant here.
4
+ export const CLIENT_ID = "1031625952748946";
5
+ export const DEVICE_URL = "https://auth.meta.com/oidc/device/authorization/";
6
+ export const TOKEN_URL = "https://auth.meta.com/oidc/device/token/";
7
+ export const KEY_URL = "https://api.meta.ai/muse-code/key";
8
+ export const API_VERSION = "1.0.0";
9
+ export const AUTH_CONTRACT_VERSION = 1;
10
+ const HEADERS = {
11
+ Accept: "application/json",
12
+ "x-api-version": API_VERSION,
13
+ };
14
+ const REQUEST_TIMEOUT_MS = 25_000;
15
+ const MIN_INTERVAL_S = 1;
16
+ const SLOW_DOWN_STEP_S = 5;
17
+ function combinedSignal(signal) {
18
+ const timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
19
+ return signal ? AbortSignal.any([timeout, signal]) : timeout;
20
+ }
21
+ async function postForm(url, params, signal) {
22
+ const res = await fetch(url, {
23
+ method: "POST",
24
+ headers: { ...HEADERS, "Content-Type": "application/x-www-form-urlencoded" },
25
+ body: new URLSearchParams(params).toString(),
26
+ signal: combinedSignal(signal),
27
+ });
28
+ const text = await res.text();
29
+ if (!res.ok)
30
+ throw new Error(`HTTP ${res.status} ${text.slice(0, 200)}`.trim());
31
+ return JSON.parse(text);
32
+ }
33
+ function delay(ms, signal) {
34
+ return new Promise((resolve, reject) => {
35
+ const timer = setTimeout(resolve, ms);
36
+ if (!signal)
37
+ return;
38
+ if (signal.aborted) {
39
+ clearTimeout(timer);
40
+ reject(new Error("authorization cancelled"));
41
+ return;
42
+ }
43
+ signal.addEventListener("abort", () => {
44
+ clearTimeout(timer);
45
+ reject(new Error("authorization cancelled"));
46
+ }, { once: true });
47
+ });
48
+ }
49
+ export async function deviceAuthorize(signal) {
50
+ const body = (await postForm(DEVICE_URL, { client_id: CLIENT_ID }, signal));
51
+ const { device_code, user_code, verification_uri, interval, expires_in } = body;
52
+ if (typeof device_code !== "string" ||
53
+ !device_code ||
54
+ typeof user_code !== "string" ||
55
+ !user_code ||
56
+ typeof verification_uri !== "string" ||
57
+ !verification_uri ||
58
+ typeof interval !== "number" ||
59
+ typeof expires_in !== "number") {
60
+ throw new Error("device authorization response is missing fields");
61
+ }
62
+ const complete = body.verification_uri_complete;
63
+ return {
64
+ device_code,
65
+ user_code,
66
+ verification_uri,
67
+ ...(typeof complete === "string" && complete ? { verification_uri_complete: complete } : {}),
68
+ interval,
69
+ expires_in,
70
+ };
71
+ }
72
+ export async function pollToken(deviceCode, intervalS, expiresInS, signal) {
73
+ let interval = Math.max(MIN_INTERVAL_S, Number(intervalS) || 5);
74
+ const deadline = Date.now() + Number(expiresInS || 600) * 1000;
75
+ let slowDowns = 0;
76
+ while (Date.now() < deadline) {
77
+ if (signal?.aborted)
78
+ throw new Error("authorization cancelled");
79
+ // The token endpoint answers pending/slow_down polls with HTTP errors as
80
+ // part of the normal flow: classify the body instead of throwing.
81
+ let body = null;
82
+ try {
83
+ const res = await fetch(TOKEN_URL, {
84
+ method: "POST",
85
+ headers: { ...HEADERS, "Content-Type": "application/x-www-form-urlencoded" },
86
+ body: new URLSearchParams({
87
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
88
+ client_id: CLIENT_ID,
89
+ device_code: deviceCode,
90
+ }).toString(),
91
+ signal: combinedSignal(signal),
92
+ });
93
+ try {
94
+ body = (await res.json());
95
+ }
96
+ catch {
97
+ body = null;
98
+ }
99
+ }
100
+ catch (error) {
101
+ if (signal?.aborted)
102
+ throw new Error("authorization cancelled");
103
+ if (error instanceof Error && error.name === "AbortError")
104
+ throw error;
105
+ body = null;
106
+ }
107
+ if (body && !body.error) {
108
+ if (typeof body.access_token !== "string" || !body.access_token) {
109
+ throw new Error("token response has no access_token");
110
+ }
111
+ return body.access_token;
112
+ }
113
+ if (body?.error === "slow_down") {
114
+ slowDowns += 1;
115
+ interval = Math.max(MIN_INTERVAL_S, interval + SLOW_DOWN_STEP_S);
116
+ }
117
+ else if (body?.error !== "authorization_pending") {
118
+ throw new Error(`login failed: ${String(body?.error || "unreadable token response")}`);
119
+ }
120
+ const remaining = deadline - Date.now();
121
+ if (remaining <= 0)
122
+ break;
123
+ await delay(Math.min(interval, remaining) * 1000, signal);
124
+ }
125
+ throw new Error(slowDowns ? "login timed out after slow_down responses" : "login timed out waiting for approval");
126
+ }
127
+ export async function mintKey(accessToken, signal) {
128
+ const res = await fetch(KEY_URL, {
129
+ method: "POST",
130
+ headers: {
131
+ ...HEADERS,
132
+ "Content-Type": "application/json",
133
+ Authorization: `Bearer ${accessToken}`,
134
+ },
135
+ body: JSON.stringify({ onboard: true }),
136
+ signal: combinedSignal(signal),
137
+ });
138
+ const text = await res.text();
139
+ if (!res.ok)
140
+ throw new Error(`key exchange failed: HTTP ${res.status} ${text.slice(0, 200)}`.trim());
141
+ const body = JSON.parse(text);
142
+ if (body.is_subs_active === false) {
143
+ throw new Error("Muse Code subscription is inactive for this account");
144
+ }
145
+ if (typeof body.api_key !== "string" || !body.api_key.trim()) {
146
+ const action = String(body.action_url || body.require_payment_action_url || "").trim();
147
+ if (body.require_payment === true || action) {
148
+ throw new Error(`Muse Code subscription is required${action ? ": " + action : ""}`);
149
+ }
150
+ throw new Error("key response is missing api_key");
151
+ }
152
+ const email = String(body.user_email || "").trim().toLowerCase() || undefined;
153
+ const accountId = String(body.user_id || "").trim() || email;
154
+ if (!accountId)
155
+ throw new Error("key response is missing account identity");
156
+ return { apiKey: body.api_key.trim(), accountId, ...(email ? { email } : {}) };
157
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ type CredentialStore = {
2
+ set(reference: string, value: string): Promise<void>;
3
+ modifyRecord(key: string, update: () => Promise<unknown>): Promise<unknown>;
4
+ };
5
+ type AuthorizationSession = {
6
+ signal?: AbortSignal;
7
+ notify(input: {
8
+ message: string;
9
+ url?: string;
10
+ }): void;
11
+ };
12
+ type AuthorizationFlow = {
13
+ key: string;
14
+ label: string;
15
+ methods: Array<{
16
+ id: string;
17
+ label: string;
18
+ }>;
19
+ run(session: AuthorizationSession): Promise<void>;
20
+ };
21
+ type HostContext = {
22
+ credentials: CredentialStore;
23
+ authorization: {
24
+ registerFlow(flow: AuthorizationFlow): () => void;
25
+ };
26
+ effect?(callback: () => (() => void) | void): () => void;
27
+ };
28
+ export declare const name = "dsh-muse-code";
29
+ export declare const inject: string[];
30
+ export declare function apply(ctx: HostContext): void;
31
+ export {};
package/lib/index.js ADDED
@@ -0,0 +1,40 @@
1
+ // DeepSeek Harness bundle: Muse Code subscription route + login flow.
2
+ //
3
+ // The route is declared by cordis.patch.yml through a second pi-ai adapter
4
+ // instance. This plugin owns the human sign-in: it runs Meta's device flow,
5
+ // mints the account-bound inference key, stores it under the
6
+ // MUSE_CODE_SUB_TOKEN credential reference, and commits the authorization
7
+ // record the seam requires.
8
+ import { deviceAuthorize, mintKey, pollToken } from "./auth.js";
9
+ export const name = "dsh-muse-code";
10
+ export const inject = ["credentials", "authorization"];
11
+ const FLOW_KEY = "dsh-muse-code/subscription";
12
+ const CREDENTIAL_REF = "MUSE_CODE_SUB_TOKEN";
13
+ export function apply(ctx) {
14
+ const flow = {
15
+ key: FLOW_KEY,
16
+ label: "Muse Code (subscription)",
17
+ methods: [{ id: "device", label: "Sign in with Meta device code" }],
18
+ async run(session) {
19
+ const device = await deviceAuthorize(session.signal);
20
+ const url = device.verification_uri_complete || device.verification_uri;
21
+ session.notify({ message: `Open ${url} and enter code ${device.user_code}`, url });
22
+ const accessToken = await pollToken(device.device_code, device.interval, device.expires_in, session.signal);
23
+ const minted = await mintKey(accessToken, session.signal);
24
+ await ctx.credentials.set(CREDENTIAL_REF, minted.apiKey);
25
+ await ctx.credentials.modifyRecord(FLOW_KEY, async () => ({
26
+ kind: "grant",
27
+ payload: {
28
+ accountId: minted.accountId,
29
+ ...(minted.email ? { email: minted.email } : {}),
30
+ },
31
+ }));
32
+ session.notify({ message: `Signed in as ${minted.email || minted.accountId}` });
33
+ },
34
+ };
35
+ const register = () => ctx.authorization.registerFlow(flow);
36
+ if (ctx.effect)
37
+ ctx.effect(register);
38
+ else
39
+ register();
40
+ }
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@thestreamcode/dsh-muse-code",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Muse Code subscription model provider and credential login for DeepSeek Harness.",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Michael Gasperini",
8
+ "url": "https://mikesoft.it"
9
+ },
10
+ "homepage": "https://github.com/TheStreamCode/dsh-muse-code",
11
+ "repository": "github:TheStreamCode/dsh-muse-code",
12
+ "bugs": {
13
+ "url": "https://github.com/TheStreamCode/dsh-muse-code/issues"
14
+ },
15
+ "type": "module",
16
+ "main": "./lib/index.js",
17
+ "exports": {
18
+ ".": "./lib/index.js",
19
+ "./package.json": "./package.json"
20
+ },
21
+ "files": [
22
+ "lib",
23
+ "cordis.patch.yml",
24
+ "README.md",
25
+ "DISCLAIMER.md",
26
+ "AUTH_CONTRACT.md",
27
+ "LICENSE"
28
+ ],
29
+ "dsh": {
30
+ "bundle": {
31
+ "patch": "./cordis.patch.yml"
32
+ }
33
+ },
34
+ "engines": {
35
+ "node": ">=20"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json",
39
+ "typecheck": "tsc -p tsconfig.json --noEmit",
40
+ "test": "npm run build && node --test test/*.test.mjs",
41
+ "prepack": "npm run build"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^26.6.5",
45
+ "typescript": "^5.9.3"
46
+ }
47
+ }