@solidnumber/cli 1.9.30 → 1.10.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/dist/commands/doctor.js +94 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/mcp.d.ts +17 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +261 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/schema.d.ts.map +1 -1
- package/dist/commands/schema.js +60 -0
- package/dist/commands/schema.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +1 -0
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +41 -7
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/dry-run.d.ts +11 -0
- package/dist/lib/dry-run.d.ts.map +1 -1
- package/dist/lib/dry-run.js +90 -0
- package/dist/lib/dry-run.js.map +1 -1
- package/dist/lib/list-envelope.d.ts +67 -0
- package/dist/lib/list-envelope.d.ts.map +1 -0
- package/dist/lib/list-envelope.js +170 -0
- package/dist/lib/list-envelope.js.map +1 -0
- package/dist/lib/mcp-client-config.d.ts +57 -0
- package/dist/lib/mcp-client-config.d.ts.map +1 -0
- package/dist/lib/mcp-client-config.js +166 -0
- package/dist/lib/mcp-client-config.js.map +1 -0
- package/dist/lib/program-registry.d.ts +19 -0
- package/dist/lib/program-registry.d.ts.map +1 -0
- package/dist/lib/program-registry.js +17 -0
- package/dist/lib/program-registry.js.map +1 -0
- package/dist/lib/scope-diagnostic.d.ts +42 -0
- package/dist/lib/scope-diagnostic.d.ts.map +1 -0
- package/dist/lib/scope-diagnostic.js +104 -0
- package/dist/lib/scope-diagnostic.js.map +1 -0
- package/dist/lib/verb-manifest.d.ts +66 -0
- package/dist/lib/verb-manifest.d.ts.map +1 -0
- package/dist/lib/verb-manifest.js +104 -0
- package/dist/lib/verb-manifest.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List-envelope normalization (Sprint 1 T1.4).
|
|
3
|
+
*
|
|
4
|
+
* Backend list endpoints return an inconsistent bag of keys:
|
|
5
|
+
* { pages: [...], total: N }
|
|
6
|
+
* { results: [...] }
|
|
7
|
+
* { logs: [...], has_more: true }
|
|
8
|
+
* { agents: [...] }
|
|
9
|
+
* { api_keys: [...] }
|
|
10
|
+
* { items: [...] }
|
|
11
|
+
* ...
|
|
12
|
+
*
|
|
13
|
+
* That's fine for humans but forces agents to write `d.pages || d.items
|
|
14
|
+
* || d.results || d.logs || ...` fallbacks before they can loop. This
|
|
15
|
+
* module adds a stable `.items` alias + `total / page / has_more`
|
|
16
|
+
* siblings on every response whose body looks like a list envelope.
|
|
17
|
+
* Original keys are preserved so existing callers never break.
|
|
18
|
+
*
|
|
19
|
+
* Pure — no HTTP, no axios. Wired into the response interceptor in
|
|
20
|
+
* api-client.ts; unit-tested with synthetic inputs.
|
|
21
|
+
*
|
|
22
|
+
* Opt-out: `SOLID_LEGACY_LIST_SHAPES=1` disables normalization for one
|
|
23
|
+
* minor release while consumers migrate (per T1.7 two-step rollout).
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Keys backend responses use to expose a list of rows, in preference
|
|
27
|
+
* order. The FIRST key found on the response body whose value is an
|
|
28
|
+
* array is promoted to `.items`. Order matters: when multiple keys
|
|
29
|
+
* coexist (rare), the earlier one wins.
|
|
30
|
+
*/
|
|
31
|
+
export declare const KNOWN_LIST_KEYS: readonly string[];
|
|
32
|
+
export interface NormalizedEnvelope {
|
|
33
|
+
items: unknown[];
|
|
34
|
+
total: number | null;
|
|
35
|
+
page: number | string | null;
|
|
36
|
+
has_more: boolean | null;
|
|
37
|
+
/** Which source key the alias came from ("items" when already canonical). */
|
|
38
|
+
_source_key: string | null;
|
|
39
|
+
}
|
|
40
|
+
/** Truthy values for the SOLID_LEGACY_LIST_SHAPES opt-out. */
|
|
41
|
+
export declare function legacyListShapesEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Detect which known list key (if any) holds the array payload.
|
|
44
|
+
* Returns null when the body is not shaped like a list response.
|
|
45
|
+
* Pure.
|
|
46
|
+
*/
|
|
47
|
+
export declare function detectListKey(body: unknown, knownKeys?: readonly string[]): string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Produce a canonical {items, total, page, has_more} view of a list
|
|
50
|
+
* response. Returns null when the body isn't a list envelope, so callers
|
|
51
|
+
* can short-circuit without mutating.
|
|
52
|
+
*/
|
|
53
|
+
export declare function normalizeListEnvelope(body: unknown): NormalizedEnvelope | null;
|
|
54
|
+
/**
|
|
55
|
+
* Apply the normalization in-place on a response body, adding the
|
|
56
|
+
* aliased keys without removing existing ones. Returns the body back
|
|
57
|
+
* for easy chaining in the axios response interceptor.
|
|
58
|
+
*
|
|
59
|
+
* No-op when:
|
|
60
|
+
* - body is not a list envelope
|
|
61
|
+
* - body already has `items` (then we just ensure `total` is present)
|
|
62
|
+
* - SOLID_LEGACY_LIST_SHAPES is truthy
|
|
63
|
+
*
|
|
64
|
+
* Pure wrt env: caller can pass its own env map for tests.
|
|
65
|
+
*/
|
|
66
|
+
export declare function applyListEnvelope<T>(body: T, env?: NodeJS.ProcessEnv): T;
|
|
67
|
+
//# sourceMappingURL=list-envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-envelope.d.ts","sourceRoot":"","sources":["../../src/lib/list-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EA4B5C,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,8DAA8D;AAC9D,wBAAgB,uBAAuB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAGxE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,OAAO,EACb,SAAS,GAAE,SAAS,MAAM,EAAoB,GAC7C,MAAM,GAAG,IAAI,CAOf;AAsBD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,kBAAkB,GAAG,IAAI,CAuB9E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,IAAI,EAAE,CAAC,EACP,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GACtB,CAAC,CAoBH"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* List-envelope normalization (Sprint 1 T1.4).
|
|
4
|
+
*
|
|
5
|
+
* Backend list endpoints return an inconsistent bag of keys:
|
|
6
|
+
* { pages: [...], total: N }
|
|
7
|
+
* { results: [...] }
|
|
8
|
+
* { logs: [...], has_more: true }
|
|
9
|
+
* { agents: [...] }
|
|
10
|
+
* { api_keys: [...] }
|
|
11
|
+
* { items: [...] }
|
|
12
|
+
* ...
|
|
13
|
+
*
|
|
14
|
+
* That's fine for humans but forces agents to write `d.pages || d.items
|
|
15
|
+
* || d.results || d.logs || ...` fallbacks before they can loop. This
|
|
16
|
+
* module adds a stable `.items` alias + `total / page / has_more`
|
|
17
|
+
* siblings on every response whose body looks like a list envelope.
|
|
18
|
+
* Original keys are preserved so existing callers never break.
|
|
19
|
+
*
|
|
20
|
+
* Pure — no HTTP, no axios. Wired into the response interceptor in
|
|
21
|
+
* api-client.ts; unit-tested with synthetic inputs.
|
|
22
|
+
*
|
|
23
|
+
* Opt-out: `SOLID_LEGACY_LIST_SHAPES=1` disables normalization for one
|
|
24
|
+
* minor release while consumers migrate (per T1.7 two-step rollout).
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.KNOWN_LIST_KEYS = void 0;
|
|
28
|
+
exports.legacyListShapesEnabled = legacyListShapesEnabled;
|
|
29
|
+
exports.detectListKey = detectListKey;
|
|
30
|
+
exports.normalizeListEnvelope = normalizeListEnvelope;
|
|
31
|
+
exports.applyListEnvelope = applyListEnvelope;
|
|
32
|
+
/**
|
|
33
|
+
* Keys backend responses use to expose a list of rows, in preference
|
|
34
|
+
* order. The FIRST key found on the response body whose value is an
|
|
35
|
+
* array is promoted to `.items`. Order matters: when multiple keys
|
|
36
|
+
* coexist (rare), the earlier one wins.
|
|
37
|
+
*/
|
|
38
|
+
exports.KNOWN_LIST_KEYS = [
|
|
39
|
+
// Already canonical — nothing to alias.
|
|
40
|
+
'items',
|
|
41
|
+
// CMS / CRM / commerce
|
|
42
|
+
'pages',
|
|
43
|
+
'results',
|
|
44
|
+
'leads',
|
|
45
|
+
'agents',
|
|
46
|
+
'templates',
|
|
47
|
+
'companies',
|
|
48
|
+
'sites',
|
|
49
|
+
'api_keys',
|
|
50
|
+
'logs',
|
|
51
|
+
'rows',
|
|
52
|
+
'records',
|
|
53
|
+
'entries',
|
|
54
|
+
'products',
|
|
55
|
+
'services',
|
|
56
|
+
'contacts',
|
|
57
|
+
'customers',
|
|
58
|
+
'orders',
|
|
59
|
+
'invoices',
|
|
60
|
+
'transactions',
|
|
61
|
+
'webhooks',
|
|
62
|
+
'chains',
|
|
63
|
+
'flows',
|
|
64
|
+
'modules',
|
|
65
|
+
'domains',
|
|
66
|
+
];
|
|
67
|
+
/** Truthy values for the SOLID_LEGACY_LIST_SHAPES opt-out. */
|
|
68
|
+
function legacyListShapesEnabled(env) {
|
|
69
|
+
const v = (env ?? process.env).SOLID_LEGACY_LIST_SHAPES;
|
|
70
|
+
return typeof v === 'string' && /^(1|true|yes|on)$/i.test(v);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Detect which known list key (if any) holds the array payload.
|
|
74
|
+
* Returns null when the body is not shaped like a list response.
|
|
75
|
+
* Pure.
|
|
76
|
+
*/
|
|
77
|
+
function detectListKey(body, knownKeys = exports.KNOWN_LIST_KEYS) {
|
|
78
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
79
|
+
return null;
|
|
80
|
+
const record = body;
|
|
81
|
+
for (const k of knownKeys) {
|
|
82
|
+
if (Array.isArray(record[k]))
|
|
83
|
+
return k;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
function safeNumber(v) {
|
|
88
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
89
|
+
return v;
|
|
90
|
+
if (typeof v === 'string' && v.trim() !== '' && !Number.isNaN(Number(v))) {
|
|
91
|
+
return Number(v);
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function safePage(v) {
|
|
96
|
+
if (v == null)
|
|
97
|
+
return null;
|
|
98
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
99
|
+
return v;
|
|
100
|
+
if (typeof v === 'string' && v.trim() !== '')
|
|
101
|
+
return v;
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function safeBool(v) {
|
|
105
|
+
if (typeof v === 'boolean')
|
|
106
|
+
return v;
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Produce a canonical {items, total, page, has_more} view of a list
|
|
111
|
+
* response. Returns null when the body isn't a list envelope, so callers
|
|
112
|
+
* can short-circuit without mutating.
|
|
113
|
+
*/
|
|
114
|
+
function normalizeListEnvelope(body) {
|
|
115
|
+
const key = detectListKey(body);
|
|
116
|
+
if (!key)
|
|
117
|
+
return null;
|
|
118
|
+
const record = body;
|
|
119
|
+
const items = record[key] ?? [];
|
|
120
|
+
const total = safeNumber(record.total) ??
|
|
121
|
+
safeNumber(record.count) ??
|
|
122
|
+
items.length;
|
|
123
|
+
const page = safePage(record.page) ??
|
|
124
|
+
safePage(record.cursor) ??
|
|
125
|
+
safePage(record.next_cursor) ??
|
|
126
|
+
null;
|
|
127
|
+
const has_more = safeBool(record.has_more) ?? safeBool(record.hasMore) ?? null;
|
|
128
|
+
return {
|
|
129
|
+
items,
|
|
130
|
+
total,
|
|
131
|
+
page,
|
|
132
|
+
has_more,
|
|
133
|
+
_source_key: key,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Apply the normalization in-place on a response body, adding the
|
|
138
|
+
* aliased keys without removing existing ones. Returns the body back
|
|
139
|
+
* for easy chaining in the axios response interceptor.
|
|
140
|
+
*
|
|
141
|
+
* No-op when:
|
|
142
|
+
* - body is not a list envelope
|
|
143
|
+
* - body already has `items` (then we just ensure `total` is present)
|
|
144
|
+
* - SOLID_LEGACY_LIST_SHAPES is truthy
|
|
145
|
+
*
|
|
146
|
+
* Pure wrt env: caller can pass its own env map for tests.
|
|
147
|
+
*/
|
|
148
|
+
function applyListEnvelope(body, env) {
|
|
149
|
+
if (legacyListShapesEnabled(env))
|
|
150
|
+
return body;
|
|
151
|
+
const normalized = normalizeListEnvelope(body);
|
|
152
|
+
if (!normalized)
|
|
153
|
+
return body;
|
|
154
|
+
const record = body;
|
|
155
|
+
// Only fill in what's missing; never clobber a caller-supplied field.
|
|
156
|
+
if (!('items' in record) || record.items === undefined) {
|
|
157
|
+
record.items = normalized.items;
|
|
158
|
+
}
|
|
159
|
+
if (!('total' in record) || record.total === undefined || record.total === null) {
|
|
160
|
+
record.total = normalized.total;
|
|
161
|
+
}
|
|
162
|
+
if (!('page' in record) || record.page === undefined) {
|
|
163
|
+
record.page = normalized.page;
|
|
164
|
+
}
|
|
165
|
+
if (!('has_more' in record) || record.has_more === undefined) {
|
|
166
|
+
record.has_more = normalized.has_more;
|
|
167
|
+
}
|
|
168
|
+
return body;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=list-envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-envelope.js","sourceRoot":"","sources":["../../src/lib/list-envelope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type McpClient = 'claude' | 'cursor' | 'windsurf';
|
|
2
|
+
export declare const SUPPORTED_CLIENTS: McpClient[];
|
|
3
|
+
/** Returns true if the string is a known supported client. */
|
|
4
|
+
export declare function isSupportedClient(value: string | undefined | null): value is McpClient;
|
|
5
|
+
export interface ServerEntry {
|
|
6
|
+
command: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
env?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
export interface McpConfigFile {
|
|
11
|
+
mcpServers?: Record<string, ServerEntry>;
|
|
12
|
+
[k: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Per-OS default path for each supported client. Returns the path
|
|
16
|
+
* relative to `homeDir` so callers can pass their own for testing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function configPathForClient(client: McpClient, opts?: {
|
|
19
|
+
platform?: NodeJS.Platform;
|
|
20
|
+
homeDir?: string;
|
|
21
|
+
}): string;
|
|
22
|
+
export interface BuildEntryInput {
|
|
23
|
+
/** API key or token the MCP server will use to call the backend. */
|
|
24
|
+
apiKey?: string;
|
|
25
|
+
/** Base URL for the backend. Defaults to api.solidnumber.com. */
|
|
26
|
+
apiUrl?: string;
|
|
27
|
+
/** Npm package to invoke via npx. Defaults to @solidnumber/mcp. */
|
|
28
|
+
packageName?: string;
|
|
29
|
+
/** Optional extra env vars (merged in). */
|
|
30
|
+
extraEnv?: Record<string, string>;
|
|
31
|
+
/** Company-id pin (adds X-Company-Id via SOLID_COMPANY_ID env). */
|
|
32
|
+
companyId?: number | string;
|
|
33
|
+
}
|
|
34
|
+
/** Default package that ships the stdio MCP server. */
|
|
35
|
+
export declare const DEFAULT_MCP_PACKAGE = "@solidnumber/mcp";
|
|
36
|
+
/**
|
|
37
|
+
* Build the `mcpServers.solid` entry. Pure — no I/O, no env reads.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildServerEntry(input?: BuildEntryInput): ServerEntry;
|
|
40
|
+
/**
|
|
41
|
+
* Merge a new `solid` server entry into an existing config file.
|
|
42
|
+
* Preserves any other mcpServers keys and any top-level keys we don't
|
|
43
|
+
* recognize. Returns a NEW object — input is not mutated.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mergeIntoConfig(existing: McpConfigFile | null | undefined, entry: ServerEntry, serverKey?: string): McpConfigFile;
|
|
46
|
+
/**
|
|
47
|
+
* Remove the `solid` entry from a config, preserving other keys.
|
|
48
|
+
* Returns the NEW config (unmutated input). When only `solid` existed
|
|
49
|
+
* in mcpServers, keeps an empty `mcpServers: {}` to be explicit.
|
|
50
|
+
*/
|
|
51
|
+
export declare function removeFromConfig(existing: McpConfigFile | null | undefined, serverKey?: string): McpConfigFile;
|
|
52
|
+
/**
|
|
53
|
+
* Pretty-print a config with stable key order + 2-space indent. Writes
|
|
54
|
+
* this string is the caller's responsibility.
|
|
55
|
+
*/
|
|
56
|
+
export declare function serializeConfig(cfg: McpConfigFile): string;
|
|
57
|
+
//# sourceMappingURL=mcp-client-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client-config.d.ts","sourceRoot":"","sources":["../../src/lib/mcp-client-config.ts"],"names":[],"mappings":"AAwBA,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEzD,eAAO,MAAM,iBAAiB,EAAE,SAAS,EAAqC,CAAC;AAE/E,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,IAAI,SAAS,CAEtF;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAEzC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1D,MAAM,CAiCR;AAED,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAED,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,eAAoB,GAAG,WAAW,CAkBzE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,EAC1C,KAAK,EAAE,WAAW,EAClB,SAAS,GAAE,MAAgB,GAC1B,aAAa,CAMf;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,EAC1C,SAAS,GAAE,MAAgB,GAC1B,aAAa,CAOf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAE1D"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DEFAULT_MCP_PACKAGE = exports.SUPPORTED_CLIENTS = void 0;
|
|
37
|
+
exports.isSupportedClient = isSupportedClient;
|
|
38
|
+
exports.configPathForClient = configPathForClient;
|
|
39
|
+
exports.buildServerEntry = buildServerEntry;
|
|
40
|
+
exports.mergeIntoConfig = mergeIntoConfig;
|
|
41
|
+
exports.removeFromConfig = removeFromConfig;
|
|
42
|
+
exports.serializeConfig = serializeConfig;
|
|
43
|
+
/**
|
|
44
|
+
* MCP client config helpers (Sprint 1 T1.5).
|
|
45
|
+
*
|
|
46
|
+
* Write/update the client-side JSON that tells Claude Desktop / Cursor /
|
|
47
|
+
* Windsurf how to launch the Solid# MCP server. All pure — no fs here,
|
|
48
|
+
* no network. Callers in src/commands/mcp.ts handle I/O.
|
|
49
|
+
*
|
|
50
|
+
* Supported client shapes (as of 2026-04):
|
|
51
|
+
*
|
|
52
|
+
* Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
|
|
53
|
+
* {
|
|
54
|
+
* "mcpServers": {
|
|
55
|
+
* "solid": { "command": "npx", "args": ["@solidnumber/mcp"],
|
|
56
|
+
* "env": { "SOLID_API_KEY": "sk_..." } }
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* Cursor (~/.cursor/mcp.json): same shape as Claude Desktop
|
|
61
|
+
*
|
|
62
|
+
* Windsurf (~/.codeium/windsurf/mcp_config.json): same shape
|
|
63
|
+
*/
|
|
64
|
+
const os = __importStar(require("os"));
|
|
65
|
+
const path = __importStar(require("path"));
|
|
66
|
+
exports.SUPPORTED_CLIENTS = ['claude', 'cursor', 'windsurf'];
|
|
67
|
+
/** Returns true if the string is a known supported client. */
|
|
68
|
+
function isSupportedClient(value) {
|
|
69
|
+
return !!value && exports.SUPPORTED_CLIENTS.includes(value);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Per-OS default path for each supported client. Returns the path
|
|
73
|
+
* relative to `homeDir` so callers can pass their own for testing.
|
|
74
|
+
*/
|
|
75
|
+
function configPathForClient(client, opts = {}) {
|
|
76
|
+
const platform = opts.platform ?? process.platform;
|
|
77
|
+
const home = opts.homeDir ?? os.homedir();
|
|
78
|
+
switch (client) {
|
|
79
|
+
case 'claude':
|
|
80
|
+
if (platform === 'darwin') {
|
|
81
|
+
return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
82
|
+
}
|
|
83
|
+
if (platform === 'win32') {
|
|
84
|
+
// %APPDATA% → fall back to ~/AppData/Roaming when not set
|
|
85
|
+
const appdata = opts.platform === undefined ? process.env.APPDATA : undefined;
|
|
86
|
+
const base = appdata ?? path.join(home, 'AppData', 'Roaming');
|
|
87
|
+
return path.join(base, 'Claude', 'claude_desktop_config.json');
|
|
88
|
+
}
|
|
89
|
+
// Linux — Claude Desktop isn't officially supported there but keep
|
|
90
|
+
// a sane default so tests have something to assert.
|
|
91
|
+
return path.join(home, '.config', 'Claude', 'claude_desktop_config.json');
|
|
92
|
+
case 'cursor':
|
|
93
|
+
return path.join(home, '.cursor', 'mcp.json');
|
|
94
|
+
case 'windsurf':
|
|
95
|
+
if (platform === 'darwin' || platform === 'linux') {
|
|
96
|
+
return path.join(home, '.codeium', 'windsurf', 'mcp_config.json');
|
|
97
|
+
}
|
|
98
|
+
if (platform === 'win32') {
|
|
99
|
+
const appdata = opts.platform === undefined ? process.env.APPDATA : undefined;
|
|
100
|
+
const base = appdata ?? path.join(home, 'AppData', 'Roaming');
|
|
101
|
+
return path.join(base, 'Codeium', 'Windsurf', 'mcp_config.json');
|
|
102
|
+
}
|
|
103
|
+
return path.join(home, '.codeium', 'windsurf', 'mcp_config.json');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Default package that ships the stdio MCP server. */
|
|
107
|
+
exports.DEFAULT_MCP_PACKAGE = '@solidnumber/mcp';
|
|
108
|
+
/**
|
|
109
|
+
* Build the `mcpServers.solid` entry. Pure — no I/O, no env reads.
|
|
110
|
+
*/
|
|
111
|
+
function buildServerEntry(input = {}) {
|
|
112
|
+
const env = {};
|
|
113
|
+
if (input.apiKey)
|
|
114
|
+
env.SOLID_API_KEY = input.apiKey;
|
|
115
|
+
if (input.apiUrl)
|
|
116
|
+
env.SOLID_API_URL = input.apiUrl;
|
|
117
|
+
if (input.companyId !== undefined && input.companyId !== null && input.companyId !== '') {
|
|
118
|
+
env.SOLID_COMPANY_ID = String(input.companyId);
|
|
119
|
+
}
|
|
120
|
+
if (input.extraEnv) {
|
|
121
|
+
for (const [k, v] of Object.entries(input.extraEnv)) {
|
|
122
|
+
if (typeof v === 'string' && v.length > 0)
|
|
123
|
+
env[k] = v;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const entry = {
|
|
127
|
+
command: 'npx',
|
|
128
|
+
args: ['-y', input.packageName ?? exports.DEFAULT_MCP_PACKAGE],
|
|
129
|
+
};
|
|
130
|
+
if (Object.keys(env).length > 0)
|
|
131
|
+
entry.env = env;
|
|
132
|
+
return entry;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Merge a new `solid` server entry into an existing config file.
|
|
136
|
+
* Preserves any other mcpServers keys and any top-level keys we don't
|
|
137
|
+
* recognize. Returns a NEW object — input is not mutated.
|
|
138
|
+
*/
|
|
139
|
+
function mergeIntoConfig(existing, entry, serverKey = 'solid') {
|
|
140
|
+
const base = existing && typeof existing === 'object' ? { ...existing } : {};
|
|
141
|
+
const servers = { ...(base.mcpServers ?? {}) };
|
|
142
|
+
servers[serverKey] = entry;
|
|
143
|
+
base.mcpServers = servers;
|
|
144
|
+
return base;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Remove the `solid` entry from a config, preserving other keys.
|
|
148
|
+
* Returns the NEW config (unmutated input). When only `solid` existed
|
|
149
|
+
* in mcpServers, keeps an empty `mcpServers: {}` to be explicit.
|
|
150
|
+
*/
|
|
151
|
+
function removeFromConfig(existing, serverKey = 'solid') {
|
|
152
|
+
const base = existing && typeof existing === 'object' ? { ...existing } : {};
|
|
153
|
+
if (base.mcpServers && typeof base.mcpServers === 'object') {
|
|
154
|
+
const { [serverKey]: _, ...rest } = base.mcpServers;
|
|
155
|
+
base.mcpServers = rest;
|
|
156
|
+
}
|
|
157
|
+
return base;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Pretty-print a config with stable key order + 2-space indent. Writes
|
|
161
|
+
* this string is the caller's responsibility.
|
|
162
|
+
*/
|
|
163
|
+
function serializeConfig(cfg) {
|
|
164
|
+
return JSON.stringify(cfg, null, 2) + '\n';
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=mcp-client-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client-config.js","sourceRoot":"","sources":["../../src/lib/mcp-client-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* program-registry — lazy handle on the root Commander program so any
|
|
3
|
+
* command module can introspect the full verb tree without creating an
|
|
4
|
+
* import cycle with index.ts.
|
|
5
|
+
*
|
|
6
|
+
* Sprint 1 T1.3: `solid schema verbs --json` needs to walk every
|
|
7
|
+
* subcommand. Without this registry, schema.ts would have to import
|
|
8
|
+
* program from index.ts, which already imports schemaCommand from
|
|
9
|
+
* schema.ts — cycle.
|
|
10
|
+
*
|
|
11
|
+
* Pure module state: one writer (index.ts) + many readers (schema,
|
|
12
|
+
* doctor, etc.). No async, no I/O.
|
|
13
|
+
*/
|
|
14
|
+
import type { Command } from 'commander';
|
|
15
|
+
export declare function setProgram(p: Command): void;
|
|
16
|
+
export declare function getProgram(): Command | null;
|
|
17
|
+
/** Reset for tests. */
|
|
18
|
+
export declare function resetProgramForTest(): void;
|
|
19
|
+
//# sourceMappingURL=program-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program-registry.d.ts","sourceRoot":"","sources":["../../src/lib/program-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAE3C;AAED,wBAAgB,UAAU,IAAI,OAAO,GAAG,IAAI,CAE3C;AAED,uBAAuB;AACvB,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setProgram = setProgram;
|
|
4
|
+
exports.getProgram = getProgram;
|
|
5
|
+
exports.resetProgramForTest = resetProgramForTest;
|
|
6
|
+
let _program = null;
|
|
7
|
+
function setProgram(p) {
|
|
8
|
+
_program = p;
|
|
9
|
+
}
|
|
10
|
+
function getProgram() {
|
|
11
|
+
return _program;
|
|
12
|
+
}
|
|
13
|
+
/** Reset for tests. */
|
|
14
|
+
function resetProgramForTest() {
|
|
15
|
+
_program = null;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=program-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program-registry.js","sourceRoot":"","sources":["../../src/lib/program-registry.ts"],"names":[],"mappings":";;AAiBA,gCAEC;AAED,gCAEC;AAGD,kDAEC;AAbD,IAAI,QAAQ,GAAmB,IAAI,CAAC;AAEpC,SAAgB,UAAU,CAAC,CAAU;IACnC,QAAQ,GAAG,CAAC,CAAC;AACf,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,uBAAuB;AACvB,SAAgB,mBAAmB;IACjC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope/feature-gap diagnostic (Sprint 1 T1.6).
|
|
3
|
+
*
|
|
4
|
+
* Compares the scopes currently granted on an API key against the scopes
|
|
5
|
+
* the tenant's enabled features *should* grant (per backend service
|
|
6
|
+
* services/scope_expansion.py, Sprint 0 T0.5). Surfaces mismatches so
|
|
7
|
+
* `solid doctor` can tell the operator: "your key is missing agents:read
|
|
8
|
+
* because your tenant enabled the agents feature after the key was
|
|
9
|
+
* issued — rotate the key and you'll pick up the new scopes."
|
|
10
|
+
*
|
|
11
|
+
* Pure — takes primitives, returns primitives. No HTTP, no fs. The
|
|
12
|
+
* backend has the same map in services/scope_expansion.py; this is a
|
|
13
|
+
* narrow client-side mirror so operators see drift without a round-trip.
|
|
14
|
+
* Kept small on purpose: if it diverges from the backend map, the only
|
|
15
|
+
* impact is a diagnostic miss — enforcement is still server-side.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Feature key → scopes it should grant on issuance.
|
|
19
|
+
* MUST stay in lock-step with solid-backend/services/scope_expansion.py
|
|
20
|
+
* FEATURE_TO_SCOPES. Keep small — every entry is a permission decision.
|
|
21
|
+
*/
|
|
22
|
+
export declare const FEATURE_TO_SCOPES: Readonly<Record<string, readonly string[]>>;
|
|
23
|
+
/**
|
|
24
|
+
* Given the key's current scopes and the tenant's feature flags, return
|
|
25
|
+
* a sorted list of scopes that SHOULD be granted but aren't.
|
|
26
|
+
*
|
|
27
|
+
* Pure — no I/O.
|
|
28
|
+
*/
|
|
29
|
+
export declare function computeScopeGap(currentScopes: readonly string[] | null | undefined, featureSettings: Record<string, unknown> | null | undefined, featureToScopes?: Readonly<Record<string, readonly string[]>>): string[];
|
|
30
|
+
export interface ScopeDiagnosticFinding {
|
|
31
|
+
severity: 'ok' | 'warn' | 'fail';
|
|
32
|
+
code: 'SCOPE_GAP' | 'NO_FEATURES' | 'NO_SCOPES' | 'ALL_GRANTED';
|
|
33
|
+
message: string;
|
|
34
|
+
missing_scopes: string[];
|
|
35
|
+
hint?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Wrap computeScopeGap with a pre-canned diagnostic payload — the shape
|
|
39
|
+
* `solid doctor` surfaces to the operator. Pure.
|
|
40
|
+
*/
|
|
41
|
+
export declare function diagnoseScopeGap(currentScopes: readonly string[] | null | undefined, featureSettings: Record<string, unknown> | null | undefined): ScopeDiagnosticFinding;
|
|
42
|
+
//# sourceMappingURL=scope-diagnostic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-diagnostic.d.ts","sourceRoot":"","sources":["../../src/lib/scope-diagnostic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAGzE,CAAC;AASF;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EACnD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EAC3D,eAAe,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAqB,GAC/E,MAAM,EAAE,CAgBV;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,IAAI,EAAE,WAAW,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,CAAC;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EACnD,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAC1D,sBAAsB,CAkCxB"}
|