@dosu/cli 0.16.0 → 0.16.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/bin/dosu.js +255 -235
- package/package.json +1 -1
package/bin/dosu.js
CHANGED
|
@@ -2082,6 +2082,232 @@ var init_esm = __esm(() => {
|
|
|
2082
2082
|
} = import__.default);
|
|
2083
2083
|
});
|
|
2084
2084
|
|
|
2085
|
+
// src/config/config.ts
|
|
2086
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2087
|
+
import { join } from "node:path";
|
|
2088
|
+
function getConfigDir() {
|
|
2089
|
+
const dirName = process.env.DOSU_DEV === "true" ? "dosu-cli-dev" : "dosu-cli";
|
|
2090
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
2091
|
+
if (xdgConfig)
|
|
2092
|
+
return join(xdgConfig, dirName);
|
|
2093
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
2094
|
+
return join(home, ".config", dirName);
|
|
2095
|
+
}
|
|
2096
|
+
function getConfigPath() {
|
|
2097
|
+
const dir = getConfigDir();
|
|
2098
|
+
if (!existsSync(dir)) {
|
|
2099
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2100
|
+
}
|
|
2101
|
+
return join(dir, "config.json");
|
|
2102
|
+
}
|
|
2103
|
+
function loadConfig() {
|
|
2104
|
+
const path = getConfigPath();
|
|
2105
|
+
if (!existsSync(path)) {
|
|
2106
|
+
return emptyConfig();
|
|
2107
|
+
}
|
|
2108
|
+
try {
|
|
2109
|
+
const data = readFileSync(path, "utf-8");
|
|
2110
|
+
return JSON.parse(data);
|
|
2111
|
+
} catch {
|
|
2112
|
+
return emptyConfig();
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
function saveConfig(cfg) {
|
|
2116
|
+
const path = getConfigPath();
|
|
2117
|
+
const dir = getConfigDir();
|
|
2118
|
+
if (!existsSync(dir)) {
|
|
2119
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2120
|
+
}
|
|
2121
|
+
writeFileSync(path, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
2122
|
+
}
|
|
2123
|
+
function isAuthenticated(cfg) {
|
|
2124
|
+
return cfg.access_token !== "";
|
|
2125
|
+
}
|
|
2126
|
+
function isTokenExpired(cfg) {
|
|
2127
|
+
if (cfg.expires_at === 0)
|
|
2128
|
+
return false;
|
|
2129
|
+
return Math.floor(Date.now() / 1000) > cfg.expires_at - 300;
|
|
2130
|
+
}
|
|
2131
|
+
function emptyConfig() {
|
|
2132
|
+
return {
|
|
2133
|
+
access_token: "",
|
|
2134
|
+
refresh_token: "",
|
|
2135
|
+
expires_at: 0
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
var MODE_OSS = "oss";
|
|
2139
|
+
var init_config = () => {};
|
|
2140
|
+
|
|
2141
|
+
// src/config/constants.ts
|
|
2142
|
+
function getWebAppURL() {
|
|
2143
|
+
return process.env.DOSU_WEB_APP_URL_OVERRIDE ?? "https://app.dosu.dev" ?? "";
|
|
2144
|
+
}
|
|
2145
|
+
function getBackendURL() {
|
|
2146
|
+
return process.env.DOSU_BACKEND_URL_OVERRIDE ?? "https://api.dosu.dev" ?? "";
|
|
2147
|
+
}
|
|
2148
|
+
function getSupabaseURL() {
|
|
2149
|
+
return process.env.SUPABASE_URL_OVERRIDE ?? "https://wldmetsoicvieidlsqrb.supabase.co" ?? "";
|
|
2150
|
+
}
|
|
2151
|
+
function getSupabaseAnonKey() {
|
|
2152
|
+
return process.env.SUPABASE_ANON_KEY_OVERRIDE ?? "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6IndsbG1ldHNvaWN2aWVpZGxzcXJiIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzQ0NjA1NjUsImV4cCI6MjA1MDAzNjU2NX0.15LIxqSMVRnxsvLrk0GPjL1aSYPfOeaeFMdJXqgc5Xs" ?? "";
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
// src/client/client.ts
|
|
2156
|
+
var exports_client = {};
|
|
2157
|
+
__export(exports_client, {
|
|
2158
|
+
SessionExpiredError: () => SessionExpiredError,
|
|
2159
|
+
Client: () => Client
|
|
2160
|
+
});
|
|
2161
|
+
|
|
2162
|
+
class Client {
|
|
2163
|
+
baseURL;
|
|
2164
|
+
config;
|
|
2165
|
+
constructor(cfg) {
|
|
2166
|
+
this.baseURL = getBackendURL();
|
|
2167
|
+
this.config = cfg;
|
|
2168
|
+
}
|
|
2169
|
+
async doRequest(method, path, body) {
|
|
2170
|
+
if (!isAuthenticated(this.config)) {
|
|
2171
|
+
throw new Error("not authenticated - please run setup first");
|
|
2172
|
+
}
|
|
2173
|
+
if (isTokenExpired(this.config)) {
|
|
2174
|
+
await this.refreshToken();
|
|
2175
|
+
}
|
|
2176
|
+
let resp = await this.doRequestOnce(method, path, body);
|
|
2177
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
2178
|
+
try {
|
|
2179
|
+
await this.refreshToken();
|
|
2180
|
+
} catch {
|
|
2181
|
+
throw new SessionExpiredError;
|
|
2182
|
+
}
|
|
2183
|
+
resp = await this.doRequestOnce(method, path, body);
|
|
2184
|
+
}
|
|
2185
|
+
return resp;
|
|
2186
|
+
}
|
|
2187
|
+
async doRequestRaw(method, path) {
|
|
2188
|
+
return this.doRequestOnce(method, path);
|
|
2189
|
+
}
|
|
2190
|
+
async doRequestOnce(method, path, body) {
|
|
2191
|
+
const url = this.baseURL + path;
|
|
2192
|
+
const headers = {
|
|
2193
|
+
"Content-Type": "application/json",
|
|
2194
|
+
"Supabase-Access-Token": this.config.access_token
|
|
2195
|
+
};
|
|
2196
|
+
const options = { method, headers };
|
|
2197
|
+
if (body !== undefined) {
|
|
2198
|
+
options.body = JSON.stringify(body);
|
|
2199
|
+
}
|
|
2200
|
+
const controller = new AbortController;
|
|
2201
|
+
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
2202
|
+
options.signal = controller.signal;
|
|
2203
|
+
try {
|
|
2204
|
+
return await fetch(url, options);
|
|
2205
|
+
} finally {
|
|
2206
|
+
clearTimeout(timeout);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
async get(path) {
|
|
2210
|
+
return this.doRequest("GET", path);
|
|
2211
|
+
}
|
|
2212
|
+
async post(path, body) {
|
|
2213
|
+
return this.doRequest("POST", path, body);
|
|
2214
|
+
}
|
|
2215
|
+
async put(path, body) {
|
|
2216
|
+
return this.doRequest("PUT", path, body);
|
|
2217
|
+
}
|
|
2218
|
+
async delete(path) {
|
|
2219
|
+
return this.doRequest("DELETE", path);
|
|
2220
|
+
}
|
|
2221
|
+
async refreshToken() {
|
|
2222
|
+
if (!this.config.refresh_token) {
|
|
2223
|
+
throw new Error("no refresh token available");
|
|
2224
|
+
}
|
|
2225
|
+
const supabaseURL = getSupabaseURL();
|
|
2226
|
+
const endpoint = `${supabaseURL}/auth/v1/token?grant_type=refresh_token`;
|
|
2227
|
+
const resp = await fetch(endpoint, {
|
|
2228
|
+
method: "POST",
|
|
2229
|
+
headers: {
|
|
2230
|
+
"Content-Type": "application/json",
|
|
2231
|
+
apikey: getSupabaseAnonKey()
|
|
2232
|
+
},
|
|
2233
|
+
body: JSON.stringify({ refresh_token: this.config.refresh_token })
|
|
2234
|
+
});
|
|
2235
|
+
if (resp.status !== 200) {
|
|
2236
|
+
throw new Error(`refresh failed with status ${resp.status}`);
|
|
2237
|
+
}
|
|
2238
|
+
const data = await resp.json();
|
|
2239
|
+
this.config.access_token = data.access_token;
|
|
2240
|
+
this.config.refresh_token = data.refresh_token;
|
|
2241
|
+
this.config.expires_at = Math.floor(Date.now() / 1000) + data.expires_in;
|
|
2242
|
+
saveConfig(this.config);
|
|
2243
|
+
}
|
|
2244
|
+
async getDeployments() {
|
|
2245
|
+
const resp = await this.get("/v1/mcp/deployments");
|
|
2246
|
+
if (resp.status !== 200) {
|
|
2247
|
+
let detail = await readErrorBody(resp);
|
|
2248
|
+
if (!detail || detail === "Internal Server Error") {
|
|
2249
|
+
detail = "check backend logs for details";
|
|
2250
|
+
}
|
|
2251
|
+
throw new Error(`failed to fetch deployments (status ${resp.status}): ${detail}`);
|
|
2252
|
+
}
|
|
2253
|
+
return resp.json();
|
|
2254
|
+
}
|
|
2255
|
+
async getOrgs() {
|
|
2256
|
+
const resp = await this.get("/v1/mcp/orgs");
|
|
2257
|
+
if (resp.status !== 200) {
|
|
2258
|
+
const detail = await readErrorBody(resp);
|
|
2259
|
+
throw new Error(`failed to fetch orgs (status ${resp.status}): ${detail}`);
|
|
2260
|
+
}
|
|
2261
|
+
return resp.json();
|
|
2262
|
+
}
|
|
2263
|
+
async validateAPIKey(apiKey, deploymentID) {
|
|
2264
|
+
try {
|
|
2265
|
+
const endpoint = `${this.baseURL}/v1/mcp/deployments/${encodeURIComponent(deploymentID)}`;
|
|
2266
|
+
const controller = new AbortController;
|
|
2267
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
2268
|
+
try {
|
|
2269
|
+
const resp = await fetch(endpoint, {
|
|
2270
|
+
method: "GET",
|
|
2271
|
+
headers: { "X-Dosu-API-Key": apiKey },
|
|
2272
|
+
signal: controller.signal
|
|
2273
|
+
});
|
|
2274
|
+
return resp.status !== 401 && resp.status !== 403;
|
|
2275
|
+
} finally {
|
|
2276
|
+
clearTimeout(timeout);
|
|
2277
|
+
}
|
|
2278
|
+
} catch {
|
|
2279
|
+
return true;
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
async createAPIKey(deploymentID, name) {
|
|
2283
|
+
const path = `/v1/mcp/deployments/${deploymentID}/api-keys`;
|
|
2284
|
+
const resp = await this.post(path, { name });
|
|
2285
|
+
if (resp.status !== 200 && resp.status !== 201) {
|
|
2286
|
+
const detail = await readErrorBody(resp);
|
|
2287
|
+
throw new Error(`failed to create API key (status ${resp.status}): ${detail}`);
|
|
2288
|
+
}
|
|
2289
|
+
return resp.json();
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
async function readErrorBody(resp) {
|
|
2293
|
+
try {
|
|
2294
|
+
const text = await resp.text();
|
|
2295
|
+
return text.slice(0, 1024);
|
|
2296
|
+
} catch {
|
|
2297
|
+
return "";
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
var SessionExpiredError;
|
|
2301
|
+
var init_client = __esm(() => {
|
|
2302
|
+
init_config();
|
|
2303
|
+
SessionExpiredError = class SessionExpiredError extends Error {
|
|
2304
|
+
constructor() {
|
|
2305
|
+
super("session expired");
|
|
2306
|
+
this.name = "SessionExpiredError";
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
2309
|
+
});
|
|
2310
|
+
|
|
2085
2311
|
// node_modules/picocolors/picocolors.js
|
|
2086
2312
|
var require_picocolors = __commonJS((exports, module) => {
|
|
2087
2313
|
var p = process || {};
|
|
@@ -4267,81 +4493,11 @@ var init_dist4 = __esm(() => {
|
|
|
4267
4493
|
allowErrorProps = SuperJSON.allowErrorProps;
|
|
4268
4494
|
});
|
|
4269
4495
|
|
|
4270
|
-
// src/config/config.ts
|
|
4271
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4272
|
-
import { join } from "node:path";
|
|
4273
|
-
function getConfigDir() {
|
|
4274
|
-
const dirName = process.env.DOSU_DEV === "true" ? "dosu-cli-dev" : "dosu-cli";
|
|
4275
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
4276
|
-
if (xdgConfig)
|
|
4277
|
-
return join(xdgConfig, dirName);
|
|
4278
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
4279
|
-
return join(home, ".config", dirName);
|
|
4280
|
-
}
|
|
4281
|
-
function getConfigPath() {
|
|
4282
|
-
const dir = getConfigDir();
|
|
4283
|
-
if (!existsSync(dir)) {
|
|
4284
|
-
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4285
|
-
}
|
|
4286
|
-
return join(dir, "config.json");
|
|
4287
|
-
}
|
|
4288
|
-
function loadConfig() {
|
|
4289
|
-
const path = getConfigPath();
|
|
4290
|
-
if (!existsSync(path)) {
|
|
4291
|
-
return emptyConfig();
|
|
4292
|
-
}
|
|
4293
|
-
try {
|
|
4294
|
-
const data = readFileSync(path, "utf-8");
|
|
4295
|
-
return JSON.parse(data);
|
|
4296
|
-
} catch {
|
|
4297
|
-
return emptyConfig();
|
|
4298
|
-
}
|
|
4299
|
-
}
|
|
4300
|
-
function saveConfig(cfg) {
|
|
4301
|
-
const path = getConfigPath();
|
|
4302
|
-
const dir = getConfigDir();
|
|
4303
|
-
if (!existsSync(dir)) {
|
|
4304
|
-
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4305
|
-
}
|
|
4306
|
-
writeFileSync(path, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
4307
|
-
}
|
|
4308
|
-
function isAuthenticated(cfg) {
|
|
4309
|
-
return cfg.access_token !== "";
|
|
4310
|
-
}
|
|
4311
|
-
function isTokenExpired(cfg) {
|
|
4312
|
-
if (cfg.expires_at === 0)
|
|
4313
|
-
return false;
|
|
4314
|
-
return Math.floor(Date.now() / 1000) > cfg.expires_at - 300;
|
|
4315
|
-
}
|
|
4316
|
-
function emptyConfig() {
|
|
4317
|
-
return {
|
|
4318
|
-
access_token: "",
|
|
4319
|
-
refresh_token: "",
|
|
4320
|
-
expires_at: 0
|
|
4321
|
-
};
|
|
4322
|
-
}
|
|
4323
|
-
var MODE_OSS = "oss";
|
|
4324
|
-
var init_config = () => {};
|
|
4325
|
-
|
|
4326
|
-
// src/config/constants.ts
|
|
4327
|
-
function getWebAppURL() {
|
|
4328
|
-
return process.env.DOSU_WEB_APP_URL_OVERRIDE ?? "https://app.dosu.dev" ?? "";
|
|
4329
|
-
}
|
|
4330
|
-
function getBackendURL() {
|
|
4331
|
-
return process.env.DOSU_BACKEND_URL_OVERRIDE ?? "https://api.dosu.dev" ?? "";
|
|
4332
|
-
}
|
|
4333
|
-
function getSupabaseURL() {
|
|
4334
|
-
return process.env.SUPABASE_URL_OVERRIDE ?? "https://wldmetsoicvieidlsqrb.supabase.co" ?? "";
|
|
4335
|
-
}
|
|
4336
|
-
function getSupabaseAnonKey() {
|
|
4337
|
-
return process.env.SUPABASE_ANON_KEY_OVERRIDE ?? "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6IndsbG1ldHNvaWN2aWVpZGxzcXJiIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzQ0NjA1NjUsImV4cCI6MjA1MDAzNjU2NX0.15LIxqSMVRnxsvLrk0GPjL1aSYPfOeaeFMdJXqgc5Xs" ?? "";
|
|
4338
|
-
}
|
|
4339
|
-
|
|
4340
4496
|
// src/version/version.ts
|
|
4341
4497
|
function getVersionString() {
|
|
4342
4498
|
return `v${VERSION}`;
|
|
4343
4499
|
}
|
|
4344
|
-
var VERSION = "0.16.
|
|
4500
|
+
var VERSION = "0.16.1";
|
|
4345
4501
|
|
|
4346
4502
|
// src/debug/logger.ts
|
|
4347
4503
|
import {
|
|
@@ -4474,162 +4630,6 @@ var init_logger = __esm(() => {
|
|
|
4474
4630
|
};
|
|
4475
4631
|
});
|
|
4476
4632
|
|
|
4477
|
-
// src/client/client.ts
|
|
4478
|
-
var exports_client = {};
|
|
4479
|
-
__export(exports_client, {
|
|
4480
|
-
SessionExpiredError: () => SessionExpiredError,
|
|
4481
|
-
Client: () => Client
|
|
4482
|
-
});
|
|
4483
|
-
|
|
4484
|
-
class Client {
|
|
4485
|
-
baseURL;
|
|
4486
|
-
config;
|
|
4487
|
-
constructor(cfg) {
|
|
4488
|
-
this.baseURL = getBackendURL();
|
|
4489
|
-
this.config = cfg;
|
|
4490
|
-
}
|
|
4491
|
-
async doRequest(method, path, body) {
|
|
4492
|
-
if (!isAuthenticated(this.config)) {
|
|
4493
|
-
throw new Error("not authenticated - please run setup first");
|
|
4494
|
-
}
|
|
4495
|
-
if (isTokenExpired(this.config)) {
|
|
4496
|
-
await this.refreshToken();
|
|
4497
|
-
}
|
|
4498
|
-
let resp = await this.doRequestOnce(method, path, body);
|
|
4499
|
-
if (resp.status === 401 || resp.status === 403) {
|
|
4500
|
-
try {
|
|
4501
|
-
await this.refreshToken();
|
|
4502
|
-
} catch {
|
|
4503
|
-
throw new SessionExpiredError;
|
|
4504
|
-
}
|
|
4505
|
-
resp = await this.doRequestOnce(method, path, body);
|
|
4506
|
-
}
|
|
4507
|
-
return resp;
|
|
4508
|
-
}
|
|
4509
|
-
async doRequestRaw(method, path) {
|
|
4510
|
-
return this.doRequestOnce(method, path);
|
|
4511
|
-
}
|
|
4512
|
-
async doRequestOnce(method, path, body) {
|
|
4513
|
-
const url = this.baseURL + path;
|
|
4514
|
-
const headers = {
|
|
4515
|
-
"Content-Type": "application/json",
|
|
4516
|
-
"Supabase-Access-Token": this.config.access_token
|
|
4517
|
-
};
|
|
4518
|
-
const options = { method, headers };
|
|
4519
|
-
if (body !== undefined) {
|
|
4520
|
-
options.body = JSON.stringify(body);
|
|
4521
|
-
}
|
|
4522
|
-
const controller = new AbortController;
|
|
4523
|
-
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
4524
|
-
options.signal = controller.signal;
|
|
4525
|
-
try {
|
|
4526
|
-
return await fetch(url, options);
|
|
4527
|
-
} finally {
|
|
4528
|
-
clearTimeout(timeout);
|
|
4529
|
-
}
|
|
4530
|
-
}
|
|
4531
|
-
async get(path) {
|
|
4532
|
-
return this.doRequest("GET", path);
|
|
4533
|
-
}
|
|
4534
|
-
async post(path, body) {
|
|
4535
|
-
return this.doRequest("POST", path, body);
|
|
4536
|
-
}
|
|
4537
|
-
async put(path, body) {
|
|
4538
|
-
return this.doRequest("PUT", path, body);
|
|
4539
|
-
}
|
|
4540
|
-
async delete(path) {
|
|
4541
|
-
return this.doRequest("DELETE", path);
|
|
4542
|
-
}
|
|
4543
|
-
async refreshToken() {
|
|
4544
|
-
if (!this.config.refresh_token) {
|
|
4545
|
-
throw new Error("no refresh token available");
|
|
4546
|
-
}
|
|
4547
|
-
const supabaseURL = getSupabaseURL();
|
|
4548
|
-
const endpoint = `${supabaseURL}/auth/v1/token?grant_type=refresh_token`;
|
|
4549
|
-
const resp = await fetch(endpoint, {
|
|
4550
|
-
method: "POST",
|
|
4551
|
-
headers: {
|
|
4552
|
-
"Content-Type": "application/json",
|
|
4553
|
-
apikey: getSupabaseAnonKey()
|
|
4554
|
-
},
|
|
4555
|
-
body: JSON.stringify({ refresh_token: this.config.refresh_token })
|
|
4556
|
-
});
|
|
4557
|
-
if (resp.status !== 200) {
|
|
4558
|
-
throw new Error(`refresh failed with status ${resp.status}`);
|
|
4559
|
-
}
|
|
4560
|
-
const data = await resp.json();
|
|
4561
|
-
this.config.access_token = data.access_token;
|
|
4562
|
-
this.config.refresh_token = data.refresh_token;
|
|
4563
|
-
this.config.expires_at = Math.floor(Date.now() / 1000) + data.expires_in;
|
|
4564
|
-
saveConfig(this.config);
|
|
4565
|
-
}
|
|
4566
|
-
async getDeployments() {
|
|
4567
|
-
const resp = await this.get("/v1/mcp/deployments");
|
|
4568
|
-
if (resp.status !== 200) {
|
|
4569
|
-
let detail = await readErrorBody(resp);
|
|
4570
|
-
if (!detail || detail === "Internal Server Error") {
|
|
4571
|
-
detail = "check backend logs for details";
|
|
4572
|
-
}
|
|
4573
|
-
throw new Error(`failed to fetch deployments (status ${resp.status}): ${detail}`);
|
|
4574
|
-
}
|
|
4575
|
-
return resp.json();
|
|
4576
|
-
}
|
|
4577
|
-
async getOrgs() {
|
|
4578
|
-
const resp = await this.get("/v1/mcp/orgs");
|
|
4579
|
-
if (resp.status !== 200) {
|
|
4580
|
-
const detail = await readErrorBody(resp);
|
|
4581
|
-
throw new Error(`failed to fetch orgs (status ${resp.status}): ${detail}`);
|
|
4582
|
-
}
|
|
4583
|
-
return resp.json();
|
|
4584
|
-
}
|
|
4585
|
-
async validateAPIKey(apiKey, deploymentID) {
|
|
4586
|
-
try {
|
|
4587
|
-
const endpoint = `${this.baseURL}/v1/mcp/deployments/${encodeURIComponent(deploymentID)}`;
|
|
4588
|
-
const controller = new AbortController;
|
|
4589
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
4590
|
-
try {
|
|
4591
|
-
const resp = await fetch(endpoint, {
|
|
4592
|
-
method: "GET",
|
|
4593
|
-
headers: { "X-Dosu-API-Key": apiKey },
|
|
4594
|
-
signal: controller.signal
|
|
4595
|
-
});
|
|
4596
|
-
return resp.status !== 401 && resp.status !== 403;
|
|
4597
|
-
} finally {
|
|
4598
|
-
clearTimeout(timeout);
|
|
4599
|
-
}
|
|
4600
|
-
} catch {
|
|
4601
|
-
return true;
|
|
4602
|
-
}
|
|
4603
|
-
}
|
|
4604
|
-
async createAPIKey(deploymentID, name) {
|
|
4605
|
-
const path = `/v1/mcp/deployments/${deploymentID}/api-keys`;
|
|
4606
|
-
const resp = await this.post(path, { name });
|
|
4607
|
-
if (resp.status !== 200 && resp.status !== 201) {
|
|
4608
|
-
const detail = await readErrorBody(resp);
|
|
4609
|
-
throw new Error(`failed to create API key (status ${resp.status}): ${detail}`);
|
|
4610
|
-
}
|
|
4611
|
-
return resp.json();
|
|
4612
|
-
}
|
|
4613
|
-
}
|
|
4614
|
-
async function readErrorBody(resp) {
|
|
4615
|
-
try {
|
|
4616
|
-
const text = await resp.text();
|
|
4617
|
-
return text.slice(0, 1024);
|
|
4618
|
-
} catch {
|
|
4619
|
-
return "";
|
|
4620
|
-
}
|
|
4621
|
-
}
|
|
4622
|
-
var SessionExpiredError;
|
|
4623
|
-
var init_client = __esm(() => {
|
|
4624
|
-
init_config();
|
|
4625
|
-
SessionExpiredError = class SessionExpiredError extends Error {
|
|
4626
|
-
constructor() {
|
|
4627
|
-
super("session expired");
|
|
4628
|
-
this.name = "SessionExpiredError";
|
|
4629
|
-
}
|
|
4630
|
-
};
|
|
4631
|
-
});
|
|
4632
|
-
|
|
4633
4633
|
// src/client/trpc.ts
|
|
4634
4634
|
var exports_trpc = {};
|
|
4635
4635
|
__export(exports_trpc, {
|
|
@@ -12239,6 +12239,7 @@ var init_flow3 = __esm(() => {
|
|
|
12239
12239
|
|
|
12240
12240
|
// src/cli/cli.ts
|
|
12241
12241
|
init_esm();
|
|
12242
|
+
init_client();
|
|
12242
12243
|
import { readFileSync as readFileSync11, unlinkSync as unlinkSync2 } from "node:fs";
|
|
12243
12244
|
|
|
12244
12245
|
// src/commands/analytics.ts
|
|
@@ -14341,10 +14342,17 @@ function createProgram() {
|
|
|
14341
14342
|
return;
|
|
14342
14343
|
}
|
|
14343
14344
|
const cfg = loadConfig();
|
|
14344
|
-
if (isAuthenticated(cfg)
|
|
14345
|
-
|
|
14346
|
-
|
|
14347
|
-
|
|
14345
|
+
if (isAuthenticated(cfg)) {
|
|
14346
|
+
if (!isTokenExpired(cfg)) {
|
|
14347
|
+
console.log("You are already logged in.");
|
|
14348
|
+
console.log("Run 'dosu logout' first to re-authenticate.");
|
|
14349
|
+
return;
|
|
14350
|
+
}
|
|
14351
|
+
if (await ensureFreshSession(cfg)) {
|
|
14352
|
+
console.log("Session refreshed.");
|
|
14353
|
+
console.log(`Credentials saved to ${getConfigPath()}`);
|
|
14354
|
+
return;
|
|
14355
|
+
}
|
|
14348
14356
|
}
|
|
14349
14357
|
console.log("Opening browser for authentication...");
|
|
14350
14358
|
const { startOAuthFlow: startOAuthFlow2 } = await Promise.resolve().then(() => (init_flow(), exports_flow));
|
|
@@ -14384,14 +14392,14 @@ function createProgram() {
|
|
|
14384
14392
|
saveConfig(cfg);
|
|
14385
14393
|
console.log("Successfully logged out.");
|
|
14386
14394
|
});
|
|
14387
|
-
program2.command("status").description("Show current authentication and MCP status").action(() => {
|
|
14395
|
+
program2.command("status").description("Show current authentication and MCP status").action(async () => {
|
|
14388
14396
|
const cfg = loadConfig();
|
|
14389
14397
|
if (!isAuthenticated(cfg)) {
|
|
14390
14398
|
console.log("Status: Not logged in");
|
|
14391
14399
|
console.log("Run 'dosu login' to authenticate.");
|
|
14392
14400
|
return;
|
|
14393
14401
|
}
|
|
14394
|
-
if (isTokenExpired(cfg)) {
|
|
14402
|
+
if (isTokenExpired(cfg) && !await ensureFreshSession(cfg)) {
|
|
14395
14403
|
console.log("Status: Token expired");
|
|
14396
14404
|
console.log("Run 'dosu login' to re-authenticate.");
|
|
14397
14405
|
} else {
|
|
@@ -14409,7 +14417,7 @@ function createProgram() {
|
|
|
14409
14417
|
}
|
|
14410
14418
|
});
|
|
14411
14419
|
const mcp = program2.command("mcp").description("Manage MCP server integrations");
|
|
14412
|
-
mcp.command("add <agent>").description("Add Dosu MCP to an AI tool").option("-g, --global", "Add globally (all projects) instead of project-local", false).option("--show-secret", "Print full manual configuration secrets", false).action((toolId, opts) => {
|
|
14420
|
+
mcp.command("add <agent>").description("Add Dosu MCP to an AI tool").option("-g, --global", "Add globally (all projects) instead of project-local", false).option("--show-secret", "Print full manual configuration secrets", false).action(async (toolId, opts) => {
|
|
14413
14421
|
let provider;
|
|
14414
14422
|
try {
|
|
14415
14423
|
provider = getProvider(toolId.toLowerCase());
|
|
@@ -14420,7 +14428,7 @@ function createProgram() {
|
|
|
14420
14428
|
if (!isAuthenticated(cfg)) {
|
|
14421
14429
|
throw new Error("not logged in. Run 'dosu login' first");
|
|
14422
14430
|
}
|
|
14423
|
-
if (isTokenExpired(cfg)) {
|
|
14431
|
+
if (isTokenExpired(cfg) && !await ensureFreshSession(cfg)) {
|
|
14424
14432
|
throw new Error("session expired. Run 'dosu login' to re-authenticate");
|
|
14425
14433
|
}
|
|
14426
14434
|
if (cfg.mode !== MODE_OSS && !cfg.deployment_id) {
|
|
@@ -14545,6 +14553,18 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
14545
14553
|
});
|
|
14546
14554
|
return program2;
|
|
14547
14555
|
}
|
|
14556
|
+
async function ensureFreshSession(cfg) {
|
|
14557
|
+
if (!isTokenExpired(cfg))
|
|
14558
|
+
return true;
|
|
14559
|
+
try {
|
|
14560
|
+
logger.debug("cli", "token expired, attempting refresh");
|
|
14561
|
+
await new Client(cfg).refreshToken();
|
|
14562
|
+
return true;
|
|
14563
|
+
} catch (err) {
|
|
14564
|
+
logger.debug("cli", `token refresh failed: ${err instanceof Error ? err.message : err}`);
|
|
14565
|
+
return false;
|
|
14566
|
+
}
|
|
14567
|
+
}
|
|
14548
14568
|
async function execute() {
|
|
14549
14569
|
const program2 = createProgram();
|
|
14550
14570
|
await program2.parseAsync(process.argv);
|