@solidnumber/cli 1.9.30 → 1.10.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.
Files changed (49) hide show
  1. package/dist/commands/doctor.js +109 -0
  2. package/dist/commands/doctor.js.map +1 -1
  3. package/dist/commands/mcp.d.ts +17 -0
  4. package/dist/commands/mcp.d.ts.map +1 -0
  5. package/dist/commands/mcp.js +268 -0
  6. package/dist/commands/mcp.js.map +1 -0
  7. package/dist/commands/pages.js +5 -2
  8. package/dist/commands/pages.js.map +1 -1
  9. package/dist/commands/schema.d.ts.map +1 -1
  10. package/dist/commands/schema.js +60 -0
  11. package/dist/commands/schema.js.map +1 -1
  12. package/dist/index.js +7 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/api-client.d.ts +1 -0
  15. package/dist/lib/api-client.d.ts.map +1 -1
  16. package/dist/lib/api-client.js +55 -9
  17. package/dist/lib/api-client.js.map +1 -1
  18. package/dist/lib/command-kit.d.ts +21 -0
  19. package/dist/lib/command-kit.d.ts.map +1 -1
  20. package/dist/lib/command-kit.js +65 -24
  21. package/dist/lib/command-kit.js.map +1 -1
  22. package/dist/lib/config.d.ts.map +1 -1
  23. package/dist/lib/config.js +17 -2
  24. package/dist/lib/config.js.map +1 -1
  25. package/dist/lib/dry-run.d.ts +11 -0
  26. package/dist/lib/dry-run.d.ts.map +1 -1
  27. package/dist/lib/dry-run.js +90 -0
  28. package/dist/lib/dry-run.js.map +1 -1
  29. package/dist/lib/list-envelope.d.ts +67 -0
  30. package/dist/lib/list-envelope.d.ts.map +1 -0
  31. package/dist/lib/list-envelope.js +170 -0
  32. package/dist/lib/list-envelope.js.map +1 -0
  33. package/dist/lib/mcp-client-config.d.ts +57 -0
  34. package/dist/lib/mcp-client-config.d.ts.map +1 -0
  35. package/dist/lib/mcp-client-config.js +166 -0
  36. package/dist/lib/mcp-client-config.js.map +1 -0
  37. package/dist/lib/program-registry.d.ts +19 -0
  38. package/dist/lib/program-registry.d.ts.map +1 -0
  39. package/dist/lib/program-registry.js +17 -0
  40. package/dist/lib/program-registry.js.map +1 -0
  41. package/dist/lib/scope-diagnostic.d.ts +42 -0
  42. package/dist/lib/scope-diagnostic.d.ts.map +1 -0
  43. package/dist/lib/scope-diagnostic.js +104 -0
  44. package/dist/lib/scope-diagnostic.js.map +1 -0
  45. package/dist/lib/verb-manifest.d.ts +66 -0
  46. package/dist/lib/verb-manifest.d.ts.map +1 -0
  47. package/dist/lib/verb-manifest.js +104 -0
  48. package/dist/lib/verb-manifest.js.map +1 -0
  49. 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"}