@thestreamcode/dsh-muse-code 0.1.1 → 0.2.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 +12 -5
- package/README.md +124 -65
- package/client/client.js +151 -0
- package/cordis.patch.yml +11 -64
- package/lib/index.d.ts +168 -23
- package/lib/index.js +381 -34
- package/lib/open-browser.d.ts +10 -0
- package/lib/open-browser.js +38 -0
- package/locale/en.json +6 -0
- package/package.json +14 -3
- package/lib/auth.d.ts +0 -22
- package/lib/auth.js +0 -157
package/locale/en.json
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thestreamcode/dsh-muse-code",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Muse Code subscription
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Unofficial DeepSeek Harness integration for Meta's Muse Code subscription: declares the shipped Meta provider route and adds its sign-in command. Use at your own risk.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Michael Gasperini",
|
|
@@ -16,10 +16,14 @@
|
|
|
16
16
|
"main": "./lib/index.js",
|
|
17
17
|
"exports": {
|
|
18
18
|
".": "./lib/index.js",
|
|
19
|
-
"./
|
|
19
|
+
"./client": "./client/client.js",
|
|
20
|
+
"./package.json": "./package.json",
|
|
21
|
+
"./locale/*.json": "./locale/*.json"
|
|
20
22
|
},
|
|
21
23
|
"files": [
|
|
22
24
|
"lib",
|
|
25
|
+
"client/client.js",
|
|
26
|
+
"locale/*.json",
|
|
23
27
|
"cordis.patch.yml",
|
|
24
28
|
"README.md",
|
|
25
29
|
"DISCLAIMER.md",
|
|
@@ -29,6 +33,13 @@
|
|
|
29
33
|
"dsh": {
|
|
30
34
|
"bundle": {
|
|
31
35
|
"patch": "./cordis.patch.yml"
|
|
36
|
+
},
|
|
37
|
+
"client": {
|
|
38
|
+
"platform": "web",
|
|
39
|
+
"immediately": true,
|
|
40
|
+
"inject": [
|
|
41
|
+
"@deepseek-ai/dsh-client-ui-settings-models"
|
|
42
|
+
]
|
|
32
43
|
}
|
|
33
44
|
},
|
|
34
45
|
"engines": {
|
package/lib/auth.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
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
|
-
}
|