cleargate 0.1.0-alpha.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/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @cleargate/cli
2
+
3
+ Scoped CLI package for the ClearGate platform; connects AI agent teams to the ClearGate MCP server via the `cleargate` binary. Full documentation ships in the sprint DoD.
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/admin-api/index.ts
32
+ var admin_api_exports = {};
33
+ __export(admin_api_exports, {
34
+ AdminApiError: () => AdminApiError,
35
+ AdminAuthFileSchema: () => AdminAuthFileSchema,
36
+ ErrorBodySchema: () => ErrorBodySchema,
37
+ InviteCreatedSchema: () => InviteCreatedSchema,
38
+ MemberSchema: () => MemberSchema,
39
+ ProjectSchema: () => ProjectSchema,
40
+ TokenIssuedSchema: () => TokenIssuedSchema,
41
+ TokenMetaSchema: () => TokenMetaSchema,
42
+ createAdminApiClient: () => createAdminApiClient,
43
+ loadAdminAuth: () => loadAdminAuth,
44
+ redactSensitive: () => redactSensitive
45
+ });
46
+ module.exports = __toCommonJS(admin_api_exports);
47
+
48
+ // src/admin-api/errors.ts
49
+ var AdminApiError = class extends Error {
50
+ constructor(kind, status, details, message) {
51
+ super(message);
52
+ this.kind = kind;
53
+ this.status = status;
54
+ this.details = details;
55
+ this.name = "AdminApiError";
56
+ }
57
+ kind;
58
+ status;
59
+ details;
60
+ };
61
+
62
+ // src/admin-api/responses.ts
63
+ var import_zod = require("zod");
64
+ var ProjectSchema = import_zod.z.object({
65
+ id: import_zod.z.string(),
66
+ name: import_zod.z.string(),
67
+ created_by: import_zod.z.string(),
68
+ created_at: import_zod.z.string(),
69
+ deleted_at: import_zod.z.string().nullable()
70
+ }).strict();
71
+ var MemberSchema = import_zod.z.object({
72
+ id: import_zod.z.string(),
73
+ project_id: import_zod.z.string(),
74
+ email: import_zod.z.string(),
75
+ role: import_zod.z.string(),
76
+ display_name: import_zod.z.string().nullable().optional(),
77
+ created_at: import_zod.z.string(),
78
+ status: import_zod.z.enum(["pending", "active"])
79
+ }).strict();
80
+ var InviteCreatedSchema = import_zod.z.object({
81
+ member: MemberSchema,
82
+ invite_url: import_zod.z.string(),
83
+ invite_token: import_zod.z.string(),
84
+ invite_expires_in: import_zod.z.number().int()
85
+ }).strict();
86
+ var TokenMetaSchema = import_zod.z.object({
87
+ id: import_zod.z.string(),
88
+ member_id: import_zod.z.string(),
89
+ name: import_zod.z.string(),
90
+ created_at: import_zod.z.string(),
91
+ expires_at: import_zod.z.string().nullable().optional(),
92
+ last_used_at: import_zod.z.string().nullable().optional(),
93
+ revoked_at: import_zod.z.string().nullable().optional()
94
+ }).strict();
95
+ var TokenIssuedSchema = import_zod.z.object({
96
+ id: import_zod.z.string(),
97
+ member_id: import_zod.z.string(),
98
+ name: import_zod.z.string(),
99
+ created_at: import_zod.z.string(),
100
+ expires_at: import_zod.z.string().nullable().optional(),
101
+ last_used_at: import_zod.z.string().nullable().optional(),
102
+ revoked_at: import_zod.z.string().nullable().optional(),
103
+ token: import_zod.z.string()
104
+ }).strict();
105
+ var ErrorBodySchema = import_zod.z.object({
106
+ error: import_zod.z.string(),
107
+ details: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
108
+ }).strict();
109
+
110
+ // src/admin-api/redact.ts
111
+ var SENSITIVE_KEYS = /* @__PURE__ */ new Set(["token", "refresh_token", "invite_token"]);
112
+ function redactSensitive(obj) {
113
+ if (Array.isArray(obj)) {
114
+ return obj.map((item) => redactSensitive(item));
115
+ }
116
+ if (obj !== null && typeof obj === "object") {
117
+ const result = {};
118
+ for (const [key, value] of Object.entries(obj)) {
119
+ if (SENSITIVE_KEYS.has(key)) {
120
+ result[key] = "<redacted>";
121
+ } else {
122
+ result[key] = redactSensitive(value);
123
+ }
124
+ }
125
+ return result;
126
+ }
127
+ return obj;
128
+ }
129
+
130
+ // src/admin-api/client.ts
131
+ function defaultWarn(msg) {
132
+ process.stderr.write(msg + "\n");
133
+ }
134
+ var AdminApiClientImpl = class {
135
+ baseUrl;
136
+ token;
137
+ fetchFn;
138
+ warn;
139
+ userAgent;
140
+ constructor(opts) {
141
+ this.baseUrl = opts.baseUrl.replace(/\/$/, "");
142
+ this.token = opts.token;
143
+ this.fetchFn = opts.fetch ?? globalThis.fetch;
144
+ this.warn = opts.warn ?? defaultWarn;
145
+ this.userAgent = opts.userAgent ?? `cleargate`;
146
+ }
147
+ debugLog(method, path2, status, body) {
148
+ if (process.env["CLEARGATE_LOG_LEVEL"] === "debug") {
149
+ const redacted = redactSensitive(body);
150
+ this.warn(`[admin-api] ${method} ${path2} \u2192 ${status} ${JSON.stringify(redacted)}`);
151
+ }
152
+ }
153
+ async request(method, urlPath, body) {
154
+ const url = `${this.baseUrl}/admin-api/v1${urlPath}`;
155
+ const headers = {
156
+ Authorization: `Bearer ${this.token}`,
157
+ "User-Agent": this.userAgent,
158
+ Accept: "application/json"
159
+ };
160
+ if (body !== void 0) {
161
+ headers["Content-Type"] = "application/json";
162
+ }
163
+ let response;
164
+ try {
165
+ response = await this.fetchFn(url, {
166
+ method,
167
+ headers,
168
+ body: body !== void 0 ? JSON.stringify(body) : void 0
169
+ });
170
+ } catch (err) {
171
+ throw new AdminApiError(
172
+ "network",
173
+ null,
174
+ err,
175
+ `cannot reach ${this.baseUrl} (${err instanceof Error ? err.message : String(err)})`
176
+ );
177
+ }
178
+ let responseBody = null;
179
+ const contentType = response.headers.get("content-type") ?? "";
180
+ if (contentType.includes("application/json")) {
181
+ try {
182
+ responseBody = await response.json();
183
+ } catch {
184
+ responseBody = null;
185
+ }
186
+ }
187
+ this.debugLog(method, urlPath, response.status, responseBody);
188
+ if (!response.ok) {
189
+ const errBody = responseBody;
190
+ if (response.status === 401) {
191
+ throw new AdminApiError("auth", 401, responseBody, "Admin token rejected.");
192
+ }
193
+ if (response.status === 403) {
194
+ throw new AdminApiError("forbidden", 403, responseBody, "Token is not admin-role.");
195
+ }
196
+ if (response.status === 404) {
197
+ throw new AdminApiError("not_found", 404, responseBody, "Not found.");
198
+ }
199
+ if (response.status === 400 || response.status === 409) {
200
+ throw new AdminApiError(
201
+ "invalid_request",
202
+ response.status,
203
+ errBody?.details ?? responseBody,
204
+ `Invalid request: ${errBody?.error ?? "unknown"}`
205
+ );
206
+ }
207
+ if (response.status >= 500) {
208
+ throw new AdminApiError(
209
+ "server",
210
+ response.status,
211
+ responseBody,
212
+ `Server error ${response.status}.`
213
+ );
214
+ }
215
+ throw new AdminApiError(
216
+ "server",
217
+ response.status,
218
+ responseBody,
219
+ `Unexpected status ${response.status}.`
220
+ );
221
+ }
222
+ return responseBody;
223
+ }
224
+ async createProject(input) {
225
+ const raw = await this.request("POST", "/projects", { name: input.name });
226
+ const parsed = ProjectSchema.safeParse(raw);
227
+ if (!parsed.success) {
228
+ throw new AdminApiError(
229
+ "response_shape",
230
+ null,
231
+ parsed.error,
232
+ "Server returned unexpected response shape (CLI may be out of date)."
233
+ );
234
+ }
235
+ return parsed.data;
236
+ }
237
+ async inviteMember(input) {
238
+ const body = {
239
+ email: input.email,
240
+ role: input.role
241
+ };
242
+ if (input.displayName !== void 0) {
243
+ body["display_name"] = input.displayName;
244
+ }
245
+ const raw = await this.request(
246
+ "POST",
247
+ `/projects/${input.projectId}/members`,
248
+ body
249
+ );
250
+ const parsed = InviteCreatedSchema.safeParse(raw);
251
+ if (!parsed.success) {
252
+ throw new AdminApiError(
253
+ "response_shape",
254
+ null,
255
+ parsed.error,
256
+ "Server returned unexpected response shape (CLI may be out of date)."
257
+ );
258
+ }
259
+ return parsed.data;
260
+ }
261
+ async issueToken(input) {
262
+ const body = {
263
+ member_id: input.memberId,
264
+ name: input.name
265
+ };
266
+ if (input.expiresAt !== void 0) {
267
+ body["expires_at"] = input.expiresAt;
268
+ }
269
+ const raw = await this.request(
270
+ "POST",
271
+ `/projects/${input.projectId}/tokens`,
272
+ body
273
+ );
274
+ const parsed = TokenIssuedSchema.safeParse(raw);
275
+ if (!parsed.success) {
276
+ throw new AdminApiError(
277
+ "response_shape",
278
+ null,
279
+ parsed.error,
280
+ "Server returned unexpected response shape (CLI may be out of date)."
281
+ );
282
+ }
283
+ return parsed.data;
284
+ }
285
+ async revokeToken(input) {
286
+ await this.request("DELETE", `/tokens/${input.tokenId}`, void 0);
287
+ }
288
+ };
289
+ function createAdminApiClient(opts) {
290
+ return new AdminApiClientImpl(opts);
291
+ }
292
+
293
+ // src/admin-api/admin-auth.ts
294
+ var fs = __toESM(require("fs"), 1);
295
+ var os = __toESM(require("os"), 1);
296
+ var path = __toESM(require("path"), 1);
297
+ var import_zod2 = require("zod");
298
+ var AdminAuthFileSchema = import_zod2.z.object({
299
+ version: import_zod2.z.literal(1),
300
+ token: import_zod2.z.string().min(1)
301
+ }).strict();
302
+ var MISSING_TOKEN_ERROR = "No admin token. Set CLEARGATE_ADMIN_TOKEN or write ~/.cleargate/admin-auth.json (chmod 600). See README \xA7admin-jwt.";
303
+ function loadAdminAuth(opts) {
304
+ const env = opts?.env ?? process.env;
305
+ const warn = opts?.warn ?? ((msg) => process.stderr.write(msg + "\n"));
306
+ const envToken = env["CLEARGATE_ADMIN_TOKEN"];
307
+ if (envToken) {
308
+ return { token: envToken, source: "env" };
309
+ }
310
+ const homedirFn = opts?.homedir ?? os.homedir;
311
+ const filePath = opts?.filePath ?? path.join(homedirFn(), ".cleargate", "admin-auth.json");
312
+ let raw;
313
+ try {
314
+ raw = fs.readFileSync(filePath, "utf8");
315
+ } catch (err) {
316
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
317
+ throw new Error(MISSING_TOKEN_ERROR);
318
+ }
319
+ throw new Error(`Failed to read admin-auth file at ${filePath}: ${String(err)}`);
320
+ }
321
+ try {
322
+ const stat = fs.statSync(filePath);
323
+ const mode = stat.mode & 511;
324
+ if (mode & 63) {
325
+ warn(
326
+ `cleargate-admin: warning: ${filePath} is group/world readable (mode ${mode.toString(8).padStart(3, "0")}). Run: chmod 600 ${filePath}`
327
+ );
328
+ }
329
+ } catch {
330
+ }
331
+ let parsed;
332
+ try {
333
+ parsed = JSON.parse(raw);
334
+ } catch {
335
+ throw new Error(`Failed to parse admin-auth file at ${filePath}: invalid JSON`);
336
+ }
337
+ const result = AdminAuthFileSchema.safeParse(parsed);
338
+ if (!result.success) {
339
+ throw new Error(
340
+ `Invalid admin-auth file at ${filePath}: ${result.error.message}`
341
+ );
342
+ }
343
+ return { token: result.data.token, source: "file" };
344
+ }
345
+ // Annotate the CommonJS export names for ESM import in node:
346
+ 0 && (module.exports = {
347
+ AdminApiError,
348
+ AdminAuthFileSchema,
349
+ ErrorBodySchema,
350
+ InviteCreatedSchema,
351
+ MemberSchema,
352
+ ProjectSchema,
353
+ TokenIssuedSchema,
354
+ TokenMetaSchema,
355
+ createAdminApiClient,
356
+ loadAdminAuth,
357
+ redactSensitive
358
+ });
359
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/admin-api/index.ts","../../src/admin-api/errors.ts","../../src/admin-api/responses.ts","../../src/admin-api/redact.ts","../../src/admin-api/client.ts","../../src/admin-api/admin-auth.ts"],"sourcesContent":["/**\n * Barrel export for admin-api module.\n * Consumed by cleargate-admin commands via cleargate/admin-api.\n */\nexport * from './client.js';\nexport * from './errors.js';\nexport * from './redact.js';\nexport * from './responses.js';\nexport * from './admin-auth.js';\n","/**\n * Typed error class for all admin API failures.\n * kind → exit code mapping lives in mcp/scripts/commands/_render-error.ts\n */\nexport class AdminApiError extends Error {\n constructor(\n public readonly kind:\n | 'network'\n | 'auth'\n | 'forbidden'\n | 'not_found'\n | 'invalid_request'\n | 'server'\n | 'response_shape',\n public readonly status: number | null,\n public readonly details: unknown,\n message: string,\n ) {\n super(message);\n this.name = 'AdminApiError';\n }\n}\n","/**\n * Vendored Zod response schemas — hand-authored from\n * mcp/src/admin-api/__snapshots__/openapi.test.ts.snap\n *\n * Snapshot drift is detected by cleargate-cli/test/admin-api/snapshot-drift.test.ts,\n * which reads the snapshot file at runtime and asserts field-set equality.\n */\nimport { z } from 'zod';\n\nexport const ProjectSchema = z\n .object({\n id: z.string(),\n name: z.string(),\n created_by: z.string(),\n created_at: z.string(),\n deleted_at: z.string().nullable(),\n })\n .strict();\n\nexport type Project = z.infer<typeof ProjectSchema>;\n\nexport const MemberSchema = z\n .object({\n id: z.string(),\n project_id: z.string(),\n email: z.string(),\n role: z.string(),\n display_name: z.string().nullable().optional(),\n created_at: z.string(),\n status: z.enum(['pending', 'active']),\n })\n .strict();\n\nexport type Member = z.infer<typeof MemberSchema>;\n\nexport const InviteCreatedSchema = z\n .object({\n member: MemberSchema,\n invite_url: z.string(),\n invite_token: z.string(),\n invite_expires_in: z.number().int(),\n })\n .strict();\n\nexport type InviteCreated = z.infer<typeof InviteCreatedSchema>;\n\nexport const TokenMetaSchema = z\n .object({\n id: z.string(),\n member_id: z.string(),\n name: z.string(),\n created_at: z.string(),\n expires_at: z.string().nullable().optional(),\n last_used_at: z.string().nullable().optional(),\n revoked_at: z.string().nullable().optional(),\n })\n .strict();\n\nexport type TokenMeta = z.infer<typeof TokenMetaSchema>;\n\n// TokenIssued = TokenMeta + plaintext token field (returned exactly once)\nexport const TokenIssuedSchema = z\n .object({\n id: z.string(),\n member_id: z.string(),\n name: z.string(),\n created_at: z.string(),\n expires_at: z.string().nullable().optional(),\n last_used_at: z.string().nullable().optional(),\n revoked_at: z.string().nullable().optional(),\n token: z.string(),\n })\n .strict();\n\nexport type TokenIssued = z.infer<typeof TokenIssuedSchema>;\n\nexport const ErrorBodySchema = z\n .object({\n error: z.string(),\n details: z.record(z.string(), z.unknown()).optional(),\n })\n .strict();\n\nexport type ErrorBody = z.infer<typeof ErrorBodySchema>;\n","/**\n * Recursively replaces sensitive key values with '<redacted>'.\n * Used in debug log paths — never in success output.\n * Keys stripped: token, refresh_token, invite_token\n */\nconst SENSITIVE_KEYS = new Set(['token', 'refresh_token', 'invite_token']);\n\nexport function redactSensitive(obj: unknown): unknown {\n if (Array.isArray(obj)) {\n return obj.map((item) => redactSensitive(item));\n }\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (SENSITIVE_KEYS.has(key)) {\n result[key] = '<redacted>';\n } else {\n result[key] = redactSensitive(value);\n }\n }\n return result;\n }\n return obj;\n}\n","/**\n * AdminApiClient — typed HTTP client for the ClearGate admin API.\n *\n * Key implementation notes:\n * - CLI method args are camelCase; wire bodies are snake_case (converted internally)\n * - DELETE requests MUST omit Content-Type (Fastify 5 FST_ERR_CTP_EMPTY_JSON_BODY)\n * - All 2xx responses are validated through vendored Zod schemas\n * - Errors map to AdminApiError with kind → exit code table in D6\n */\nimport { AdminApiError } from './errors.js';\nimport {\n ProjectSchema,\n InviteCreatedSchema,\n TokenIssuedSchema,\n type Project,\n type InviteCreated,\n type TokenIssued,\n} from './responses.js';\nimport { redactSensitive } from './redact.js';\n\nexport interface AdminApiClientOptions {\n baseUrl: string;\n token: string;\n fetch?: typeof globalThis.fetch;\n warn?: (msg: string) => void;\n userAgent?: string;\n}\n\nexport interface AdminApiClient {\n createProject(input: { name: string }): Promise<Project>;\n inviteMember(input: {\n projectId: string;\n email: string;\n role: 'user' | 'service';\n displayName?: string;\n }): Promise<InviteCreated>;\n issueToken(input: {\n projectId: string;\n memberId: string;\n name: string;\n expiresAt?: string;\n }): Promise<TokenIssued>;\n revokeToken(input: { tokenId: string }): Promise<void>;\n}\n\nfunction defaultWarn(msg: string): void {\n process.stderr.write(msg + '\\n');\n}\n\nclass AdminApiClientImpl implements AdminApiClient {\n private readonly baseUrl: string;\n private readonly token: string;\n private readonly fetchFn: typeof globalThis.fetch;\n private readonly warn: (msg: string) => void;\n private readonly userAgent: string;\n\n constructor(opts: AdminApiClientOptions) {\n this.baseUrl = opts.baseUrl.replace(/\\/$/, '');\n this.token = opts.token;\n this.fetchFn = opts.fetch ?? globalThis.fetch;\n this.warn = opts.warn ?? defaultWarn;\n this.userAgent = opts.userAgent ?? `cleargate`;\n }\n\n private debugLog(method: string, path: string, status: number, body: unknown): void {\n if (process.env['CLEARGATE_LOG_LEVEL'] === 'debug') {\n const redacted = redactSensitive(body);\n this.warn(`[admin-api] ${method} ${path} → ${status} ${JSON.stringify(redacted)}`);\n }\n }\n\n private async request<T>(\n method: string,\n urlPath: string,\n body?: Record<string, unknown>,\n ): Promise<T> {\n const url = `${this.baseUrl}/admin-api/v1${urlPath}`;\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': this.userAgent,\n Accept: 'application/json',\n };\n\n // CRITICAL: omit Content-Type on requests without body (DELETE)\n // Fastify 5 throws FST_ERR_CTP_EMPTY_JSON_BODY if Content-Type is set with empty body\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n let response: Response;\n try {\n response = await this.fetchFn(url, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n } catch (err) {\n throw new AdminApiError(\n 'network',\n null,\n err,\n `cannot reach ${this.baseUrl} (${err instanceof Error ? err.message : String(err)})`,\n );\n }\n\n // Parse response body when present\n let responseBody: unknown = null;\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) {\n try {\n responseBody = await response.json();\n } catch {\n responseBody = null;\n }\n }\n\n this.debugLog(method, urlPath, response.status, responseBody);\n\n // Map HTTP status to AdminApiError kinds\n if (!response.ok) {\n const errBody = responseBody as { error?: string; details?: unknown } | null;\n if (response.status === 401) {\n throw new AdminApiError('auth', 401, responseBody, 'Admin token rejected.');\n }\n if (response.status === 403) {\n throw new AdminApiError('forbidden', 403, responseBody, 'Token is not admin-role.');\n }\n if (response.status === 404) {\n throw new AdminApiError('not_found', 404, responseBody, 'Not found.');\n }\n if (response.status === 400 || response.status === 409) {\n throw new AdminApiError(\n 'invalid_request',\n response.status,\n errBody?.details ?? responseBody,\n `Invalid request: ${errBody?.error ?? 'unknown'}`,\n );\n }\n if (response.status >= 500) {\n throw new AdminApiError(\n 'server',\n response.status,\n responseBody,\n `Server error ${response.status}.`,\n );\n }\n throw new AdminApiError(\n 'server',\n response.status,\n responseBody,\n `Unexpected status ${response.status}.`,\n );\n }\n\n return responseBody as T;\n }\n\n async createProject(input: { name: string }): Promise<Project> {\n const raw = await this.request<unknown>('POST', '/projects', { name: input.name });\n const parsed = ProjectSchema.safeParse(raw);\n if (!parsed.success) {\n throw new AdminApiError(\n 'response_shape',\n null,\n parsed.error,\n 'Server returned unexpected response shape (CLI may be out of date).',\n );\n }\n return parsed.data;\n }\n\n async inviteMember(input: {\n projectId: string;\n email: string;\n role: 'user' | 'service';\n displayName?: string;\n }): Promise<InviteCreated> {\n const body: Record<string, unknown> = {\n email: input.email,\n role: input.role,\n };\n if (input.displayName !== undefined) {\n body['display_name'] = input.displayName;\n }\n const raw = await this.request<unknown>(\n 'POST',\n `/projects/${input.projectId}/members`,\n body,\n );\n const parsed = InviteCreatedSchema.safeParse(raw);\n if (!parsed.success) {\n // Try MemberSchema in case the server returned a member-only response\n throw new AdminApiError(\n 'response_shape',\n null,\n parsed.error,\n 'Server returned unexpected response shape (CLI may be out of date).',\n );\n }\n return parsed.data;\n }\n\n async issueToken(input: {\n projectId: string;\n memberId: string;\n name: string;\n expiresAt?: string;\n }): Promise<TokenIssued> {\n const body: Record<string, unknown> = {\n member_id: input.memberId,\n name: input.name,\n };\n if (input.expiresAt !== undefined) {\n body['expires_at'] = input.expiresAt;\n }\n const raw = await this.request<unknown>(\n 'POST',\n `/projects/${input.projectId}/tokens`,\n body,\n );\n const parsed = TokenIssuedSchema.safeParse(raw);\n if (!parsed.success) {\n throw new AdminApiError(\n 'response_shape',\n null,\n parsed.error,\n 'Server returned unexpected response shape (CLI may be out of date).',\n );\n }\n return parsed.data;\n }\n\n async revokeToken(input: { tokenId: string }): Promise<void> {\n // No body — Content-Type must be omitted (Fastify 5 CTP quirk)\n await this.request<unknown>('DELETE', `/tokens/${input.tokenId}`, undefined);\n }\n}\n\nexport function createAdminApiClient(opts: AdminApiClientOptions): AdminApiClient {\n return new AdminApiClientImpl(opts);\n}\n","/**\n * Loads an admin JWT for use with cleargate-admin CLI commands.\n *\n * Load order:\n * 1. CLEARGATE_ADMIN_TOKEN env var (wins immediately — file is not read)\n * 2. ~/.cleargate/admin-auth.json (shape: { version: 1, token: string })\n *\n * DISTINCT from FileTokenStore: that file holds user profile → refresh-token maps.\n * This file holds a single admin JWT acquired out-of-band via dev-issue-token.\n */\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { z } from 'zod';\n\nexport const AdminAuthFileSchema = z\n .object({\n version: z.literal(1),\n token: z.string().min(1),\n })\n .strict();\n\nexport interface LoadAdminAuthOptions {\n env?: NodeJS.ProcessEnv;\n filePath?: string;\n homedir?: () => string;\n warn?: (msg: string) => void;\n}\n\nexport interface AdminAuth {\n token: string;\n source: 'env' | 'file';\n}\n\nconst MISSING_TOKEN_ERROR =\n 'No admin token. Set CLEARGATE_ADMIN_TOKEN or write ~/.cleargate/admin-auth.json (chmod 600). See README §admin-jwt.';\n\nexport function loadAdminAuth(opts?: LoadAdminAuthOptions): AdminAuth {\n const env = opts?.env ?? process.env;\n const warn = opts?.warn ?? ((msg: string) => process.stderr.write(msg + '\\n'));\n\n // Env wins — file is not read at all when env is set\n const envToken = env['CLEARGATE_ADMIN_TOKEN'];\n if (envToken) {\n return { token: envToken, source: 'env' };\n }\n\n // Resolve file path\n const homedirFn = opts?.homedir ?? os.homedir;\n const filePath =\n opts?.filePath ?? path.join(homedirFn(), '.cleargate', 'admin-auth.json');\n\n // Try file\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf8');\n } catch (err) {\n if (err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(MISSING_TOKEN_ERROR);\n }\n throw new Error(`Failed to read admin-auth file at ${filePath}: ${String(err)}`);\n }\n\n // Check file permissions (warn if too permissive)\n try {\n const stat = fs.statSync(filePath);\n const mode = stat.mode & 0o777;\n if (mode & 0o077) {\n warn(\n `cleargate-admin: warning: ${filePath} is group/world readable (mode ${(mode).toString(8).padStart(3, '0')}). Run: chmod 600 ${filePath}`,\n );\n }\n } catch {\n // If we can't stat the file, ignore — the read already succeeded\n }\n\n // Parse JSON\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`Failed to parse admin-auth file at ${filePath}: invalid JSON`);\n }\n\n // Validate with strict schema\n const result = AdminAuthFileSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(\n `Invalid admin-auth file at ${filePath}: ${result.error.message}`,\n );\n }\n\n return { token: result.data.token, source: 'file' };\n}\n"],"mappings":"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","names":["path","import_zod"]}
@@ -0,0 +1,135 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Vendored Zod response schemas — hand-authored from
5
+ * mcp/src/admin-api/__snapshots__/openapi.test.ts.snap
6
+ *
7
+ * Snapshot drift is detected by cleargate-cli/test/admin-api/snapshot-drift.test.ts,
8
+ * which reads the snapshot file at runtime and asserts field-set equality.
9
+ */
10
+
11
+ declare const ProjectSchema: z.ZodObject<{
12
+ id: z.ZodString;
13
+ name: z.ZodString;
14
+ created_by: z.ZodString;
15
+ created_at: z.ZodString;
16
+ deleted_at: z.ZodNullable<z.ZodString>;
17
+ }, z.core.$strict>;
18
+ type Project = z.infer<typeof ProjectSchema>;
19
+ declare const MemberSchema: z.ZodObject<{
20
+ id: z.ZodString;
21
+ project_id: z.ZodString;
22
+ email: z.ZodString;
23
+ role: z.ZodString;
24
+ display_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
25
+ created_at: z.ZodString;
26
+ status: z.ZodEnum<{
27
+ pending: "pending";
28
+ active: "active";
29
+ }>;
30
+ }, z.core.$strict>;
31
+ type Member = z.infer<typeof MemberSchema>;
32
+ declare const InviteCreatedSchema: z.ZodObject<{
33
+ member: z.ZodObject<{
34
+ id: z.ZodString;
35
+ project_id: z.ZodString;
36
+ email: z.ZodString;
37
+ role: z.ZodString;
38
+ display_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
39
+ created_at: z.ZodString;
40
+ status: z.ZodEnum<{
41
+ pending: "pending";
42
+ active: "active";
43
+ }>;
44
+ }, z.core.$strict>;
45
+ invite_url: z.ZodString;
46
+ invite_token: z.ZodString;
47
+ invite_expires_in: z.ZodNumber;
48
+ }, z.core.$strict>;
49
+ type InviteCreated = z.infer<typeof InviteCreatedSchema>;
50
+ declare const TokenMetaSchema: z.ZodObject<{
51
+ id: z.ZodString;
52
+ member_id: z.ZodString;
53
+ name: z.ZodString;
54
+ created_at: z.ZodString;
55
+ expires_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
56
+ last_used_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
57
+ revoked_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
58
+ }, z.core.$strict>;
59
+ type TokenMeta = z.infer<typeof TokenMetaSchema>;
60
+ declare const TokenIssuedSchema: z.ZodObject<{
61
+ id: z.ZodString;
62
+ member_id: z.ZodString;
63
+ name: z.ZodString;
64
+ created_at: z.ZodString;
65
+ expires_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
66
+ last_used_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
67
+ revoked_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
68
+ token: z.ZodString;
69
+ }, z.core.$strict>;
70
+ type TokenIssued = z.infer<typeof TokenIssuedSchema>;
71
+ declare const ErrorBodySchema: z.ZodObject<{
72
+ error: z.ZodString;
73
+ details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
74
+ }, z.core.$strict>;
75
+ type ErrorBody = z.infer<typeof ErrorBodySchema>;
76
+
77
+ interface AdminApiClientOptions {
78
+ baseUrl: string;
79
+ token: string;
80
+ fetch?: typeof globalThis.fetch;
81
+ warn?: (msg: string) => void;
82
+ userAgent?: string;
83
+ }
84
+ interface AdminApiClient {
85
+ createProject(input: {
86
+ name: string;
87
+ }): Promise<Project>;
88
+ inviteMember(input: {
89
+ projectId: string;
90
+ email: string;
91
+ role: 'user' | 'service';
92
+ displayName?: string;
93
+ }): Promise<InviteCreated>;
94
+ issueToken(input: {
95
+ projectId: string;
96
+ memberId: string;
97
+ name: string;
98
+ expiresAt?: string;
99
+ }): Promise<TokenIssued>;
100
+ revokeToken(input: {
101
+ tokenId: string;
102
+ }): Promise<void>;
103
+ }
104
+ declare function createAdminApiClient(opts: AdminApiClientOptions): AdminApiClient;
105
+
106
+ /**
107
+ * Typed error class for all admin API failures.
108
+ * kind → exit code mapping lives in mcp/scripts/commands/_render-error.ts
109
+ */
110
+ declare class AdminApiError extends Error {
111
+ readonly kind: 'network' | 'auth' | 'forbidden' | 'not_found' | 'invalid_request' | 'server' | 'response_shape';
112
+ readonly status: number | null;
113
+ readonly details: unknown;
114
+ constructor(kind: 'network' | 'auth' | 'forbidden' | 'not_found' | 'invalid_request' | 'server' | 'response_shape', status: number | null, details: unknown, message: string);
115
+ }
116
+
117
+ declare function redactSensitive(obj: unknown): unknown;
118
+
119
+ declare const AdminAuthFileSchema: z.ZodObject<{
120
+ version: z.ZodLiteral<1>;
121
+ token: z.ZodString;
122
+ }, z.core.$strict>;
123
+ interface LoadAdminAuthOptions {
124
+ env?: NodeJS.ProcessEnv;
125
+ filePath?: string;
126
+ homedir?: () => string;
127
+ warn?: (msg: string) => void;
128
+ }
129
+ interface AdminAuth {
130
+ token: string;
131
+ source: 'env' | 'file';
132
+ }
133
+ declare function loadAdminAuth(opts?: LoadAdminAuthOptions): AdminAuth;
134
+
135
+ export { type AdminApiClient, type AdminApiClientOptions, AdminApiError, type AdminAuth, AdminAuthFileSchema, type ErrorBody, ErrorBodySchema, type InviteCreated, InviteCreatedSchema, type LoadAdminAuthOptions, type Member, MemberSchema, type Project, ProjectSchema, type TokenIssued, TokenIssuedSchema, type TokenMeta, TokenMetaSchema, createAdminApiClient, loadAdminAuth, redactSensitive };
@@ -0,0 +1,135 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Vendored Zod response schemas — hand-authored from
5
+ * mcp/src/admin-api/__snapshots__/openapi.test.ts.snap
6
+ *
7
+ * Snapshot drift is detected by cleargate-cli/test/admin-api/snapshot-drift.test.ts,
8
+ * which reads the snapshot file at runtime and asserts field-set equality.
9
+ */
10
+
11
+ declare const ProjectSchema: z.ZodObject<{
12
+ id: z.ZodString;
13
+ name: z.ZodString;
14
+ created_by: z.ZodString;
15
+ created_at: z.ZodString;
16
+ deleted_at: z.ZodNullable<z.ZodString>;
17
+ }, z.core.$strict>;
18
+ type Project = z.infer<typeof ProjectSchema>;
19
+ declare const MemberSchema: z.ZodObject<{
20
+ id: z.ZodString;
21
+ project_id: z.ZodString;
22
+ email: z.ZodString;
23
+ role: z.ZodString;
24
+ display_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
25
+ created_at: z.ZodString;
26
+ status: z.ZodEnum<{
27
+ pending: "pending";
28
+ active: "active";
29
+ }>;
30
+ }, z.core.$strict>;
31
+ type Member = z.infer<typeof MemberSchema>;
32
+ declare const InviteCreatedSchema: z.ZodObject<{
33
+ member: z.ZodObject<{
34
+ id: z.ZodString;
35
+ project_id: z.ZodString;
36
+ email: z.ZodString;
37
+ role: z.ZodString;
38
+ display_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
39
+ created_at: z.ZodString;
40
+ status: z.ZodEnum<{
41
+ pending: "pending";
42
+ active: "active";
43
+ }>;
44
+ }, z.core.$strict>;
45
+ invite_url: z.ZodString;
46
+ invite_token: z.ZodString;
47
+ invite_expires_in: z.ZodNumber;
48
+ }, z.core.$strict>;
49
+ type InviteCreated = z.infer<typeof InviteCreatedSchema>;
50
+ declare const TokenMetaSchema: z.ZodObject<{
51
+ id: z.ZodString;
52
+ member_id: z.ZodString;
53
+ name: z.ZodString;
54
+ created_at: z.ZodString;
55
+ expires_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
56
+ last_used_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
57
+ revoked_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
58
+ }, z.core.$strict>;
59
+ type TokenMeta = z.infer<typeof TokenMetaSchema>;
60
+ declare const TokenIssuedSchema: z.ZodObject<{
61
+ id: z.ZodString;
62
+ member_id: z.ZodString;
63
+ name: z.ZodString;
64
+ created_at: z.ZodString;
65
+ expires_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
66
+ last_used_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
67
+ revoked_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
68
+ token: z.ZodString;
69
+ }, z.core.$strict>;
70
+ type TokenIssued = z.infer<typeof TokenIssuedSchema>;
71
+ declare const ErrorBodySchema: z.ZodObject<{
72
+ error: z.ZodString;
73
+ details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
74
+ }, z.core.$strict>;
75
+ type ErrorBody = z.infer<typeof ErrorBodySchema>;
76
+
77
+ interface AdminApiClientOptions {
78
+ baseUrl: string;
79
+ token: string;
80
+ fetch?: typeof globalThis.fetch;
81
+ warn?: (msg: string) => void;
82
+ userAgent?: string;
83
+ }
84
+ interface AdminApiClient {
85
+ createProject(input: {
86
+ name: string;
87
+ }): Promise<Project>;
88
+ inviteMember(input: {
89
+ projectId: string;
90
+ email: string;
91
+ role: 'user' | 'service';
92
+ displayName?: string;
93
+ }): Promise<InviteCreated>;
94
+ issueToken(input: {
95
+ projectId: string;
96
+ memberId: string;
97
+ name: string;
98
+ expiresAt?: string;
99
+ }): Promise<TokenIssued>;
100
+ revokeToken(input: {
101
+ tokenId: string;
102
+ }): Promise<void>;
103
+ }
104
+ declare function createAdminApiClient(opts: AdminApiClientOptions): AdminApiClient;
105
+
106
+ /**
107
+ * Typed error class for all admin API failures.
108
+ * kind → exit code mapping lives in mcp/scripts/commands/_render-error.ts
109
+ */
110
+ declare class AdminApiError extends Error {
111
+ readonly kind: 'network' | 'auth' | 'forbidden' | 'not_found' | 'invalid_request' | 'server' | 'response_shape';
112
+ readonly status: number | null;
113
+ readonly details: unknown;
114
+ constructor(kind: 'network' | 'auth' | 'forbidden' | 'not_found' | 'invalid_request' | 'server' | 'response_shape', status: number | null, details: unknown, message: string);
115
+ }
116
+
117
+ declare function redactSensitive(obj: unknown): unknown;
118
+
119
+ declare const AdminAuthFileSchema: z.ZodObject<{
120
+ version: z.ZodLiteral<1>;
121
+ token: z.ZodString;
122
+ }, z.core.$strict>;
123
+ interface LoadAdminAuthOptions {
124
+ env?: NodeJS.ProcessEnv;
125
+ filePath?: string;
126
+ homedir?: () => string;
127
+ warn?: (msg: string) => void;
128
+ }
129
+ interface AdminAuth {
130
+ token: string;
131
+ source: 'env' | 'file';
132
+ }
133
+ declare function loadAdminAuth(opts?: LoadAdminAuthOptions): AdminAuth;
134
+
135
+ export { type AdminApiClient, type AdminApiClientOptions, AdminApiError, type AdminAuth, AdminAuthFileSchema, type ErrorBody, ErrorBodySchema, type InviteCreated, InviteCreatedSchema, type LoadAdminAuthOptions, type Member, MemberSchema, type Project, ProjectSchema, type TokenIssued, TokenIssuedSchema, type TokenMeta, TokenMetaSchema, createAdminApiClient, loadAdminAuth, redactSensitive };