@askthew/mcp-plugin 0.4.12 → 0.4.14
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/README.md +6 -0
- package/dist/cli.js +155 -6
- package/dist/cloud-client.d.ts +16 -7
- package/dist/cloud-client.js +88 -29
- package/dist/install.d.ts +5 -0
- package/dist/install.js +37 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,18 +61,24 @@ The v1 MCP tools are:
|
|
|
61
61
|
|
|
62
62
|
## CLI
|
|
63
63
|
|
|
64
|
+
If the binary is not on your PATH, prefix commands with `npx -y --package @askthew/mcp-plugin`.
|
|
65
|
+
|
|
64
66
|
```bash
|
|
65
67
|
askthew-mcp # stdio MCP server
|
|
66
68
|
askthew-mcp install --host claude_code
|
|
67
69
|
askthew-mcp install --host claude_code --bind <paid_bind_token>
|
|
70
|
+
askthew-mcp doctor
|
|
71
|
+
askthew-mcp identity status
|
|
68
72
|
askthew-mcp bind
|
|
69
73
|
askthew-mcp token rotate
|
|
70
74
|
askthew-mcp token revoke
|
|
71
75
|
askthew-mcp export
|
|
72
76
|
askthew-mcp delete-me --confirm
|
|
77
|
+
askthew-mcp uninstall --host claude_code
|
|
73
78
|
```
|
|
74
79
|
|
|
75
80
|
`token revoke` revokes only the current token. Install-level revocation is handled by the workspace web endpoint or `delete-me`.
|
|
81
|
+
`uninstall` removes the MCP config and marked agent-instructions block, but leaves the local identity and outbox on disk so you can reinstall or delete cloud data intentionally.
|
|
76
82
|
|
|
77
83
|
## Development
|
|
78
84
|
|
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
2
3
|
import { spawn } from "node:child_process";
|
|
3
4
|
import prompts from "prompts";
|
|
4
5
|
import { AskTheWCloudClient, completeSignup, loadCloudToken, saveCloudToken, startSignup } from "./cloud-client.js";
|
|
6
|
+
import { cloudTokenPath, installMetadataPath, localStorePath } from "./lib/paths.js";
|
|
5
7
|
import { Outbox } from "./outbox.js";
|
|
6
|
-
import { DEFAULT_SERVER_NAME, dataDirSummary, installHostConfig, packageVersion, writeBehaviorInstructions, writeInstallMetadata, } from "./install.js";
|
|
8
|
+
import { DEFAULT_SERVER_NAME, dataDirSummary, installHostConfig, packageVersion, removeBehaviorInstructions, resolveSettingsPath, uninstallHostConfig, writeBehaviorInstructions, writeInstallMetadata, } from "./install.js";
|
|
7
9
|
import { runStdioServer } from "./index.js";
|
|
8
10
|
function usage() {
|
|
9
11
|
return [
|
|
@@ -12,7 +14,10 @@ function usage() {
|
|
|
12
14
|
"Usage:",
|
|
13
15
|
" askthew-mcp",
|
|
14
16
|
" askthew-mcp install --host <claude_code|codex|cursor> [--bind <paid-bind-token>] [--skip-auth]",
|
|
17
|
+
" askthew-mcp uninstall --host <claude_code|codex|cursor> [--dry-run]",
|
|
15
18
|
" askthew-mcp bind [--allow-pending]",
|
|
19
|
+
" askthew-mcp identity status [--json]",
|
|
20
|
+
" askthew-mcp doctor [--host <claude_code|codex|cursor>] [--json]",
|
|
16
21
|
" askthew-mcp token rotate|revoke",
|
|
17
22
|
" askthew-mcp export",
|
|
18
23
|
" askthew-mcp delete-me --confirm",
|
|
@@ -170,6 +175,22 @@ async function installCommand(argv) {
|
|
|
170
175
|
process.exitCode = 1;
|
|
171
176
|
}
|
|
172
177
|
}
|
|
178
|
+
async function uninstallCommand(argv) {
|
|
179
|
+
const args = parseArgs(argv);
|
|
180
|
+
const hostType = requireHost(stringArg(args, "--host"));
|
|
181
|
+
const dryRun = boolArg(args, "--dry-run");
|
|
182
|
+
const config = uninstallHostConfig({
|
|
183
|
+
hostType,
|
|
184
|
+
serverName: DEFAULT_SERVER_NAME,
|
|
185
|
+
dryRun,
|
|
186
|
+
});
|
|
187
|
+
const instructions = removeBehaviorInstructions({ hostType, dryRun });
|
|
188
|
+
console.log(`${dryRun ? "Would uninstall" : "Uninstalled"} Ask The W for ${hostType}.`);
|
|
189
|
+
console.log(`${config.removed ? "Removed" : "No matching"} MCP config: ${config.settingsPath}`);
|
|
190
|
+
console.log(`${instructions.removed ? "Removed" : "No matching"} agent instructions: ${instructions.filePath}`);
|
|
191
|
+
console.log(`Local identity and outbox were left in ${dataDirSummary()}.`);
|
|
192
|
+
console.log("Run `askthew-mcp delete-me --confirm` before uninstall if you also want to delete install-scoped cloud data.");
|
|
193
|
+
}
|
|
173
194
|
function openBrowser(url) {
|
|
174
195
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
175
196
|
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
@@ -223,9 +244,121 @@ async function bindCommand(argv) {
|
|
|
223
244
|
}
|
|
224
245
|
throw new Error("Timed out waiting for workspace binding.");
|
|
225
246
|
}
|
|
247
|
+
function pendingOutboxCount() {
|
|
248
|
+
if (!fs.existsSync(localStorePath()))
|
|
249
|
+
return 0;
|
|
250
|
+
try {
|
|
251
|
+
return new Outbox().pendingCount();
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function printObject(value, asJson) {
|
|
258
|
+
if (asJson) {
|
|
259
|
+
console.log(JSON.stringify(value, null, 2));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
263
|
+
if (entry === undefined)
|
|
264
|
+
continue;
|
|
265
|
+
if (typeof entry === "object" && entry !== null) {
|
|
266
|
+
console.log(`${key}: ${JSON.stringify(entry)}`);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
console.log(`${key}: ${entry}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async function identityStatusCommand(argv) {
|
|
274
|
+
const args = parseArgs(argv);
|
|
275
|
+
const tokenFile = loadCloudToken();
|
|
276
|
+
const client = new AskTheWCloudClient({ apiUrl: apiUrl(args) });
|
|
277
|
+
const status = {
|
|
278
|
+
ok: true,
|
|
279
|
+
configured: Boolean(tokenFile?.token),
|
|
280
|
+
data_dir: dataDirSummary(),
|
|
281
|
+
token_path: cloudTokenPath(),
|
|
282
|
+
install_metadata_path: installMetadataPath(),
|
|
283
|
+
outbox_pending: pendingOutboxCount(),
|
|
284
|
+
api_url: stringArg(args, "--api-url") || tokenFile?.api_url || "https://app.askthew.com",
|
|
285
|
+
};
|
|
286
|
+
if (!tokenFile?.token) {
|
|
287
|
+
status.message = "No local Ask The W identity is configured. Run `askthew-mcp install --host <host>`.";
|
|
288
|
+
printObject(status, boolArg(args, "--json"));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
status.install_id = tokenFile.install_id;
|
|
292
|
+
status.tier = tokenFile.tier ?? "free";
|
|
293
|
+
status.token_purpose = tokenFile.token_purpose ?? "device";
|
|
294
|
+
const cloud = await client.request("/me", { method: "GET" });
|
|
295
|
+
status.cloud_ok = cloud.ok;
|
|
296
|
+
status.cloud_status = cloud.status;
|
|
297
|
+
status.cloud = cloud.body;
|
|
298
|
+
printObject(status, boolArg(args, "--json"));
|
|
299
|
+
}
|
|
300
|
+
async function probeHealth(baseUrl) {
|
|
301
|
+
try {
|
|
302
|
+
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/v1/agent/health`, {
|
|
303
|
+
method: "GET",
|
|
304
|
+
headers: { Accept: "application/json" },
|
|
305
|
+
});
|
|
306
|
+
const body = await response.json().catch(() => ({
|
|
307
|
+
ok: false,
|
|
308
|
+
code: "invalid_health_response",
|
|
309
|
+
message: "Health endpoint did not return JSON.",
|
|
310
|
+
}));
|
|
311
|
+
return { ok: response.ok && body?.ok !== false, status: response.status, body };
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
return {
|
|
315
|
+
ok: false,
|
|
316
|
+
status: 0,
|
|
317
|
+
body: {
|
|
318
|
+
ok: false,
|
|
319
|
+
code: "network_error",
|
|
320
|
+
message: error instanceof Error ? error.message : "Could not reach Ask The W.",
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function doctorCommand(argv) {
|
|
326
|
+
const args = parseArgs(argv);
|
|
327
|
+
const host = stringArg(args, "--host");
|
|
328
|
+
const hostType = host ? requireHost(host) : null;
|
|
329
|
+
const baseUrl = apiUrl(args);
|
|
330
|
+
const tokenFile = loadCloudToken();
|
|
331
|
+
const health = await probeHealth(baseUrl);
|
|
332
|
+
const pendingCount = pendingOutboxCount();
|
|
333
|
+
const checks = {
|
|
334
|
+
cli_version: packageVersion(),
|
|
335
|
+
api_url: baseUrl,
|
|
336
|
+
api_health: health,
|
|
337
|
+
data_dir: dataDirSummary(),
|
|
338
|
+
token_file_exists: fs.existsSync(cloudTokenPath()),
|
|
339
|
+
install_metadata_exists: fs.existsSync(installMetadataPath()),
|
|
340
|
+
outbox_exists: fs.existsSync(localStorePath()),
|
|
341
|
+
outbox_pending: pendingCount,
|
|
342
|
+
identity_configured: Boolean(tokenFile?.token),
|
|
343
|
+
};
|
|
344
|
+
if (hostType) {
|
|
345
|
+
checks.host = hostType;
|
|
346
|
+
checks.settings_path = resolveSettingsPath({ hostType });
|
|
347
|
+
}
|
|
348
|
+
if (tokenFile?.token) {
|
|
349
|
+
const cloud = await new AskTheWCloudClient({ apiUrl: baseUrl }).request("/me", { method: "GET" });
|
|
350
|
+
checks.identity_cloud = cloud;
|
|
351
|
+
}
|
|
352
|
+
const ok = Boolean(health.ok);
|
|
353
|
+
printObject({ ok, checks }, boolArg(args, "--json"));
|
|
354
|
+
if (!ok && boolArg(args, "--strict")) {
|
|
355
|
+
process.exitCode = 1;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
226
358
|
async function tokenCommand(argv) {
|
|
227
359
|
const [action] = argv;
|
|
228
|
-
const
|
|
360
|
+
const args = parseArgs(argv.slice(1));
|
|
361
|
+
const client = new AskTheWCloudClient({ apiUrl: apiUrl(args) });
|
|
229
362
|
if (action === "rotate") {
|
|
230
363
|
const result = await client.request("/token/rotate", { method: "POST", body: "{}" });
|
|
231
364
|
const body = result.body;
|
|
@@ -248,8 +381,9 @@ async function tokenCommand(argv) {
|
|
|
248
381
|
}
|
|
249
382
|
throw new Error("Usage: askthew-mcp token rotate|revoke");
|
|
250
383
|
}
|
|
251
|
-
async function exportCommand() {
|
|
252
|
-
const
|
|
384
|
+
async function exportCommand(argv = []) {
|
|
385
|
+
const args = parseArgs(argv);
|
|
386
|
+
const client = new AskTheWCloudClient({ apiUrl: apiUrl(args) });
|
|
253
387
|
const result = await client.request("/export", { method: "GET" });
|
|
254
388
|
if (!result.ok) {
|
|
255
389
|
const body = result.body;
|
|
@@ -266,7 +400,7 @@ async function deleteMeCommand(argv) {
|
|
|
266
400
|
if (tokenFile?.tier === "paid") {
|
|
267
401
|
console.error("This install is workspace-bound. Install-scoped data will be deleted; workspace data is retained.");
|
|
268
402
|
}
|
|
269
|
-
const client = new AskTheWCloudClient();
|
|
403
|
+
const client = new AskTheWCloudClient({ apiUrl: apiUrl(args) });
|
|
270
404
|
const result = await client.request("/me", { method: "DELETE" });
|
|
271
405
|
const body = result.body;
|
|
272
406
|
if (!result.ok || body.ok === false)
|
|
@@ -291,16 +425,31 @@ async function main() {
|
|
|
291
425
|
await installCommand(argv);
|
|
292
426
|
return;
|
|
293
427
|
}
|
|
428
|
+
if (command === "uninstall") {
|
|
429
|
+
await uninstallCommand(argv);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
294
432
|
if (command === "bind") {
|
|
295
433
|
await bindCommand(argv);
|
|
296
434
|
return;
|
|
297
435
|
}
|
|
436
|
+
if (command === "identity") {
|
|
437
|
+
const [action, ...rest] = argv;
|
|
438
|
+
if (action !== "status")
|
|
439
|
+
throw new Error("Usage: askthew-mcp identity status [--json]");
|
|
440
|
+
await identityStatusCommand(rest);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (command === "doctor") {
|
|
444
|
+
await doctorCommand(argv);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
298
447
|
if (command === "token") {
|
|
299
448
|
await tokenCommand(argv);
|
|
300
449
|
return;
|
|
301
450
|
}
|
|
302
451
|
if (command === "export") {
|
|
303
|
-
await exportCommand();
|
|
452
|
+
await exportCommand(argv);
|
|
304
453
|
return;
|
|
305
454
|
}
|
|
306
455
|
if (command === "delete-me") {
|
package/dist/cloud-client.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type CloudClientOptions = {
|
|
|
13
13
|
fetcher?: Fetcher;
|
|
14
14
|
env?: NodeJS.ProcessEnv;
|
|
15
15
|
};
|
|
16
|
+
export declare function trimBaseUrl(value?: string): string;
|
|
16
17
|
export declare function loadCloudToken(env?: NodeJS.ProcessEnv): CloudTokenFile | null;
|
|
17
18
|
export declare function saveCloudToken(tokenFile: CloudTokenFile, env?: NodeJS.ProcessEnv): void;
|
|
18
19
|
export declare class AskTheWCloudClient {
|
|
@@ -24,12 +25,12 @@ export declare class AskTheWCloudClient {
|
|
|
24
25
|
request(path: string, init?: RequestInit): Promise<{
|
|
25
26
|
ok: boolean;
|
|
26
27
|
status: number;
|
|
27
|
-
body:
|
|
28
|
+
body: Record<string, unknown>;
|
|
28
29
|
}>;
|
|
29
30
|
captureSignal(payload: Record<string, unknown>): Promise<{
|
|
30
31
|
ok: boolean;
|
|
31
32
|
status: number;
|
|
32
|
-
body:
|
|
33
|
+
body: Record<string, unknown>;
|
|
33
34
|
}>;
|
|
34
35
|
listSignals(input?: {
|
|
35
36
|
sessionId?: string;
|
|
@@ -43,7 +44,7 @@ export declare class AskTheWCloudClient {
|
|
|
43
44
|
createDecision(payload: Record<string, unknown>): Promise<{
|
|
44
45
|
ok: boolean;
|
|
45
46
|
status: number;
|
|
46
|
-
body:
|
|
47
|
+
body: Record<string, unknown>;
|
|
47
48
|
}>;
|
|
48
49
|
listDecisions(input?: {
|
|
49
50
|
limit?: number;
|
|
@@ -55,12 +56,12 @@ export declare class AskTheWCloudClient {
|
|
|
55
56
|
recap(input: Record<string, unknown>): Promise<{
|
|
56
57
|
ok: boolean;
|
|
57
58
|
status: number;
|
|
58
|
-
body:
|
|
59
|
+
body: Record<string, unknown>;
|
|
59
60
|
}>;
|
|
60
61
|
coach(input: Record<string, unknown>): Promise<{
|
|
61
62
|
ok: boolean;
|
|
62
63
|
status: number;
|
|
63
|
-
body:
|
|
64
|
+
body: Record<string, unknown>;
|
|
64
65
|
}>;
|
|
65
66
|
}
|
|
66
67
|
export declare function startSignup(input: {
|
|
@@ -68,7 +69,11 @@ export declare function startSignup(input: {
|
|
|
68
69
|
apiUrl?: string;
|
|
69
70
|
clientHint?: string;
|
|
70
71
|
fetcher?: Fetcher;
|
|
71
|
-
}): Promise<
|
|
72
|
+
}): Promise<Record<string, unknown> | {
|
|
73
|
+
ok: boolean;
|
|
74
|
+
code: string;
|
|
75
|
+
message: string;
|
|
76
|
+
}>;
|
|
72
77
|
export declare function completeSignup(input: {
|
|
73
78
|
email: string;
|
|
74
79
|
code: string;
|
|
@@ -77,4 +82,8 @@ export declare function completeSignup(input: {
|
|
|
77
82
|
tokenPurpose?: "conversation" | "device";
|
|
78
83
|
fetcher?: Fetcher;
|
|
79
84
|
env?: NodeJS.ProcessEnv;
|
|
80
|
-
}): Promise<
|
|
85
|
+
}): Promise<Record<string, unknown> | {
|
|
86
|
+
ok: boolean;
|
|
87
|
+
code: string;
|
|
88
|
+
message: string;
|
|
89
|
+
}>;
|
package/dist/cloud-client.js
CHANGED
|
@@ -1,7 +1,41 @@
|
|
|
1
1
|
import { cloudTokenPath, readJsonFile, writePrivateJson } from "./lib/paths.js";
|
|
2
|
-
function trimBaseUrl(value) {
|
|
2
|
+
export function trimBaseUrl(value) {
|
|
3
3
|
return (value?.trim() || "https://app.askthew.com").replace(/\/+$/, "");
|
|
4
4
|
}
|
|
5
|
+
function errorMessage(error, fallback) {
|
|
6
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
7
|
+
}
|
|
8
|
+
async function readResponseBody(response) {
|
|
9
|
+
const text = await response.text().catch(() => "");
|
|
10
|
+
if (!text.trim()) {
|
|
11
|
+
return {
|
|
12
|
+
ok: false,
|
|
13
|
+
code: response.ok ? "empty_response" : `http_${response.status}`,
|
|
14
|
+
message: `Ask The W returned HTTP ${response.status} with no JSON response body.`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(text);
|
|
19
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
20
|
+
return parsed;
|
|
21
|
+
}
|
|
22
|
+
return { ok: response.ok, value: parsed };
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
code: "invalid_json_response",
|
|
28
|
+
message: `Ask The W returned HTTP ${response.status} with a non-JSON response body.`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function networkFailure(error) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
code: "network_error",
|
|
36
|
+
message: `Could not reach Ask The W: ${errorMessage(error, "network request failed")}`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
5
39
|
export function loadCloudToken(env = process.env) {
|
|
6
40
|
return readJsonFile(cloudTokenPath(env));
|
|
7
41
|
}
|
|
@@ -58,16 +92,21 @@ export class AskTheWCloudClient {
|
|
|
58
92
|
},
|
|
59
93
|
};
|
|
60
94
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
95
|
+
try {
|
|
96
|
+
const response = await this.fetcher(`${this.apiUrl}/api/v1/agent${path}`, {
|
|
97
|
+
...init,
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "application/json",
|
|
100
|
+
Authorization: `Bearer ${this.token}`,
|
|
101
|
+
...(init.headers ?? {}),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const body = await readResponseBody(response);
|
|
105
|
+
return { ok: response.ok, status: response.status, body };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return { ok: false, status: 0, body: networkFailure(error) };
|
|
109
|
+
}
|
|
71
110
|
}
|
|
72
111
|
async captureSignal(payload) {
|
|
73
112
|
return this.request("/signals", {
|
|
@@ -117,27 +156,47 @@ export class AskTheWCloudClient {
|
|
|
117
156
|
}
|
|
118
157
|
export async function startSignup(input) {
|
|
119
158
|
const base = trimBaseUrl(input.apiUrl);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
159
|
+
try {
|
|
160
|
+
const response = await (input.fetcher ?? fetch)(`${base}/api/v1/agent/signup`, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: { "Content-Type": "application/json" },
|
|
163
|
+
body: JSON.stringify({ email: input.email, client_hint: input.clientHint }),
|
|
164
|
+
});
|
|
165
|
+
return readResponseBody(response);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
return networkFailure(error);
|
|
169
|
+
}
|
|
126
170
|
}
|
|
127
171
|
export async function completeSignup(input) {
|
|
128
172
|
const base = trimBaseUrl(input.apiUrl);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
173
|
+
let response;
|
|
174
|
+
let body;
|
|
175
|
+
try {
|
|
176
|
+
response = await (input.fetcher ?? fetch)(`${base}/api/v1/agent/verify`, {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: { "Content-Type": "application/json" },
|
|
179
|
+
body: JSON.stringify({
|
|
180
|
+
email: input.email,
|
|
181
|
+
code: input.code,
|
|
182
|
+
client_hint: input.clientHint,
|
|
183
|
+
token_purpose: input.tokenPurpose ?? "device",
|
|
184
|
+
}),
|
|
185
|
+
});
|
|
186
|
+
body = await readResponseBody(response);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
return networkFailure(error);
|
|
190
|
+
}
|
|
191
|
+
if (response.ok &&
|
|
192
|
+
typeof body === "object" &&
|
|
193
|
+
body !== null &&
|
|
194
|
+
"ok" in body &&
|
|
195
|
+
body.ok &&
|
|
196
|
+
"token" in body &&
|
|
197
|
+
typeof body.token === "string" &&
|
|
198
|
+
"install_id" in body &&
|
|
199
|
+
typeof body.install_id === "string") {
|
|
141
200
|
saveCloudToken({
|
|
142
201
|
token: body.token,
|
|
143
202
|
install_id: body.install_id,
|
package/dist/install.d.ts
CHANGED
|
@@ -82,6 +82,11 @@ export declare function writeBehaviorInstructions(input: InstructionInstallInput
|
|
|
82
82
|
text: string;
|
|
83
83
|
wroteFile: boolean;
|
|
84
84
|
};
|
|
85
|
+
export declare function removeBehaviorInstructions(input: InstructionInstallInput): {
|
|
86
|
+
filePath: string;
|
|
87
|
+
removed: boolean;
|
|
88
|
+
wroteFile: boolean;
|
|
89
|
+
};
|
|
85
90
|
export declare function writeInstallMetadata(input: {
|
|
86
91
|
hostType: SupportedHostType;
|
|
87
92
|
apiUrl?: string;
|
package/dist/install.js
CHANGED
|
@@ -201,8 +201,9 @@ export function uninstallHostConfig(input) {
|
|
|
201
201
|
let next = raw;
|
|
202
202
|
let removed = false;
|
|
203
203
|
if (input.hostType === "codex") {
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
const stripped = removeCodexTomlServer(raw, serverName);
|
|
205
|
+
next = `${stripped}${stripped ? "\n" : ""}`;
|
|
206
|
+
removed = stripped.trimEnd() !== raw.trimEnd();
|
|
206
207
|
}
|
|
207
208
|
else {
|
|
208
209
|
const parsed = raw.trim() ? JSON.parse(raw) : {};
|
|
@@ -224,9 +225,9 @@ export function uninstallHostConfig(input) {
|
|
|
224
225
|
next = `${JSON.stringify({ ...parsed, mcpServers: servers }, null, 2)}\n`;
|
|
225
226
|
}
|
|
226
227
|
}
|
|
227
|
-
if (!input.dryRun)
|
|
228
|
+
if (removed && !input.dryRun)
|
|
228
229
|
fs.writeFileSync(settingsPath, next, "utf8");
|
|
229
|
-
return { settingsPath, removed, wroteFile: !input.dryRun };
|
|
230
|
+
return { settingsPath, removed, wroteFile: removed && !input.dryRun };
|
|
230
231
|
}
|
|
231
232
|
export function instructionFileForHost(hostType, cwd = process.cwd()) {
|
|
232
233
|
return path.join(path.resolve(cwd), hostType === "claude_code" ? "CLAUDE.md" : "AGENTS.md");
|
|
@@ -269,6 +270,38 @@ export function writeBehaviorInstructions(input) {
|
|
|
269
270
|
wroteFile: !input.dryRun,
|
|
270
271
|
};
|
|
271
272
|
}
|
|
273
|
+
function removeMarkedBlock(existing) {
|
|
274
|
+
const start = existing.indexOf(INSTRUCTIONS_START);
|
|
275
|
+
const end = existing.indexOf(INSTRUCTIONS_END);
|
|
276
|
+
if (start < 0 || end < start) {
|
|
277
|
+
return { text: existing, removed: false };
|
|
278
|
+
}
|
|
279
|
+
const after = end + INSTRUCTIONS_END.length;
|
|
280
|
+
const text = `${existing.slice(0, start).trimEnd()}${existing.slice(after).trimStart() ? "\n\n" : ""}${existing
|
|
281
|
+
.slice(after)
|
|
282
|
+
.trimStart()}`.trimEnd();
|
|
283
|
+
return { text: text ? `${text}\n` : "", removed: true };
|
|
284
|
+
}
|
|
285
|
+
export function removeBehaviorInstructions(input) {
|
|
286
|
+
const filePath = instructionFileForHost(input.hostType, input.cwd);
|
|
287
|
+
if (!fs.existsSync(filePath)) {
|
|
288
|
+
return {
|
|
289
|
+
filePath,
|
|
290
|
+
removed: false,
|
|
291
|
+
wroteFile: false,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
const existing = fs.readFileSync(filePath, "utf8");
|
|
295
|
+
const next = removeMarkedBlock(existing);
|
|
296
|
+
if (next.removed && !input.dryRun) {
|
|
297
|
+
fs.writeFileSync(filePath, next.text, "utf8");
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
filePath,
|
|
301
|
+
removed: next.removed,
|
|
302
|
+
wroteFile: next.removed && !input.dryRun,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
272
305
|
export function writeInstallMetadata(input) {
|
|
273
306
|
const metadata = {
|
|
274
307
|
host: input.hostType,
|