@mtreeai/msapling-cli 2.3.6-beta.8 → 2.3.6-beta.9
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/dist/index.js +45 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,6 +79,11 @@ var init_src = __esm({
|
|
|
79
79
|
MSaplingClient = class {
|
|
80
80
|
apiUrl;
|
|
81
81
|
token;
|
|
82
|
+
// CLI-AUTH-CSRF-01: simple cookie jar so we can implement the backend's
|
|
83
|
+
// double-submit CSRF pattern (msapling_csrftoken cookie ↔ X-CSRF-Token header).
|
|
84
|
+
// Without this, every authenticated POST/PATCH/DELETE returns 403
|
|
85
|
+
// CSRF_COOKIE_MISSING even with a valid Bearer token.
|
|
86
|
+
cookies = /* @__PURE__ */ new Map();
|
|
82
87
|
constructor(options = {}) {
|
|
83
88
|
this.apiUrl = (options.apiUrl || "https://api.msapling.com").replace(/\/$/, "");
|
|
84
89
|
this.token = options.token || null;
|
|
@@ -89,12 +94,38 @@ var init_src = __esm({
|
|
|
89
94
|
getApiUrl() {
|
|
90
95
|
return this.apiUrl;
|
|
91
96
|
}
|
|
97
|
+
serializeCookies() {
|
|
98
|
+
return Array.from(this.cookies.entries()).map(([k, v]) => `${k}=${v}`).join("; ");
|
|
99
|
+
}
|
|
100
|
+
updateCookiesFromResponse(response) {
|
|
101
|
+
const setCookieArr = response.headers.getSetCookie?.() ?? [];
|
|
102
|
+
for (const line of setCookieArr) {
|
|
103
|
+
const firstSemi = line.indexOf(";");
|
|
104
|
+
const pair = firstSemi >= 0 ? line.slice(0, firstSemi) : line;
|
|
105
|
+
const eq = pair.indexOf("=");
|
|
106
|
+
if (eq <= 0) continue;
|
|
107
|
+
const name = pair.slice(0, eq).trim();
|
|
108
|
+
const value = pair.slice(eq + 1).trim();
|
|
109
|
+
if (!name || !value) continue;
|
|
110
|
+
this.cookies.set(name, value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
92
113
|
async request(path2, options = {}) {
|
|
93
114
|
const headers = new Headers(options.headers);
|
|
94
115
|
if (this.token) {
|
|
95
116
|
headers.set("Authorization", `Bearer ${this.token}`);
|
|
96
117
|
}
|
|
97
|
-
headers.
|
|
118
|
+
if (!headers.has("Content-Type")) {
|
|
119
|
+
headers.set("Content-Type", "application/json");
|
|
120
|
+
}
|
|
121
|
+
if (this.cookies.size > 0) {
|
|
122
|
+
headers.set("Cookie", this.serializeCookies());
|
|
123
|
+
}
|
|
124
|
+
const method = (options.method || "GET").toUpperCase();
|
|
125
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
126
|
+
const csrf = this.cookies.get("msapling_csrftoken");
|
|
127
|
+
if (csrf) headers.set("X-CSRF-Token", csrf);
|
|
128
|
+
}
|
|
98
129
|
const controller = new AbortController();
|
|
99
130
|
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
100
131
|
try {
|
|
@@ -106,6 +137,7 @@ var init_src = __esm({
|
|
|
106
137
|
keepalive: true
|
|
107
138
|
});
|
|
108
139
|
clearTimeout(timeout);
|
|
140
|
+
this.updateCookiesFromResponse(response);
|
|
109
141
|
if (!response.ok) {
|
|
110
142
|
let detail = "Unknown error";
|
|
111
143
|
let code = "unknown";
|
|
@@ -314,6 +346,7 @@ var init_src = __esm({
|
|
|
314
346
|
signal: controller.signal
|
|
315
347
|
});
|
|
316
348
|
clearTimeout(timeout);
|
|
349
|
+
this.updateCookiesFromResponse(response);
|
|
317
350
|
if (response.ok) {
|
|
318
351
|
const data = await response.json();
|
|
319
352
|
return { kind: "success", token: data.access_token };
|
|
@@ -352,6 +385,7 @@ var init_src = __esm({
|
|
|
352
385
|
signal: controller.signal
|
|
353
386
|
});
|
|
354
387
|
clearTimeout(timeout);
|
|
388
|
+
this.updateCookiesFromResponse(response);
|
|
355
389
|
if (response.ok) {
|
|
356
390
|
const data = await response.json();
|
|
357
391
|
return { kind: "success", token: data.access_token };
|
|
@@ -381,16 +415,21 @@ var init_src = __esm({
|
|
|
381
415
|
const controller = new AbortController();
|
|
382
416
|
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
383
417
|
try {
|
|
418
|
+
const csrf = this.cookies.get("msapling_csrftoken");
|
|
419
|
+
const verifyHeaders = {
|
|
420
|
+
"Authorization": `Bearer ${this.token}`,
|
|
421
|
+
"Content-Type": "application/json"
|
|
422
|
+
};
|
|
423
|
+
if (this.cookies.size > 0) verifyHeaders["Cookie"] = this.serializeCookies();
|
|
424
|
+
if (csrf) verifyHeaders["X-CSRF-Token"] = csrf;
|
|
384
425
|
const response = await fetch(`${this.apiUrl}/api/mfa/login-verify`, {
|
|
385
426
|
method: "POST",
|
|
386
|
-
headers:
|
|
387
|
-
"Authorization": `Bearer ${this.token}`,
|
|
388
|
-
"Content-Type": "application/json"
|
|
389
|
-
},
|
|
427
|
+
headers: verifyHeaders,
|
|
390
428
|
body: JSON.stringify({ code }),
|
|
391
429
|
signal: controller.signal
|
|
392
430
|
});
|
|
393
431
|
clearTimeout(timeout);
|
|
432
|
+
this.updateCookiesFromResponse(response);
|
|
394
433
|
if (response.ok) {
|
|
395
434
|
const data = await response.json();
|
|
396
435
|
return { kind: "success", token: data.access_token };
|
|
@@ -13134,7 +13173,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
13134
13173
|
var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
|
|
13135
13174
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
13136
13175
|
"\u25CF MSapling CLI v",
|
|
13137
|
-
"2.3.6-beta.
|
|
13176
|
+
"2.3.6-beta.9"
|
|
13138
13177
|
] }),
|
|
13139
13178
|
/* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
|
|
13140
13179
|
] });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mtreeai/msapling-cli",
|
|
3
|
-
"version": "2.3.6-beta.
|
|
3
|
+
"version": "2.3.6-beta.9",
|
|
4
4
|
"description": "MSapling CLI — React/Ink terminal client for the MSapling backend (chat, projects, MDrive, agent tools). Proprietary; redistribution prohibited.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "MSapling Team",
|