@vfarcic/dot-ai 1.22.0 → 1.23.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/core/user-prompts-loader.d.ts +120 -1
- package/dist/core/user-prompts-loader.d.ts.map +1 -1
- package/dist/core/user-prompts-loader.js +522 -57
- package/dist/interfaces/mcp.d.ts +22 -0
- package/dist/interfaces/mcp.d.ts.map +1 -1
- package/dist/interfaces/mcp.js +118 -4
- package/dist/interfaces/openapi-generator.d.ts.map +1 -1
- package/dist/interfaces/openapi-generator.js +11 -5
- package/dist/interfaces/rest-api.d.ts +18 -4
- package/dist/interfaces/rest-api.d.ts.map +1 -1
- package/dist/interfaces/rest-api.js +138 -9
- package/dist/interfaces/routes/index.d.ts.map +1 -1
- package/dist/interfaces/routes/index.js +35 -0
- package/dist/interfaces/schemas/common.d.ts +33 -0
- package/dist/interfaces/schemas/common.d.ts.map +1 -1
- package/dist/interfaces/schemas/common.js +20 -1
- package/dist/interfaces/schemas/index.d.ts +2 -2
- package/dist/interfaces/schemas/index.d.ts.map +1 -1
- package/dist/interfaces/schemas/index.js +14 -3
- package/dist/interfaces/schemas/prompts.d.ts +155 -0
- package/dist/interfaces/schemas/prompts.d.ts.map +1 -1
- package/dist/interfaces/schemas/prompts.js +134 -1
- package/dist/tools/prompts.d.ts.map +1 -1
- package/dist/tools/prompts.js +37 -2
- package/package.json +6 -4
package/dist/interfaces/mcp.js
CHANGED
|
@@ -44,6 +44,37 @@ const plugin_registry_1 = require("../core/plugin-registry");
|
|
|
44
44
|
const SESSION_TTL_MS = 60 * 60 * 1000;
|
|
45
45
|
/** How often to check for expired sessions. */
|
|
46
46
|
const SESSION_GC_INTERVAL_MS = 5 * 60 * 1000;
|
|
47
|
+
/**
|
|
48
|
+
* PRD #647 F1 — hard raw-body byte ceiling for the untrusted prompts source
|
|
49
|
+
* ingest endpoint (POST /api/v1/prompts/sources).
|
|
50
|
+
*
|
|
51
|
+
* parseRequestBody buffers the whole body before JSON.parse + per-file
|
|
52
|
+
* base64-decode, all of which allocate fully BEFORE the 256 KiB *decoded* cap
|
|
53
|
+
* in ingestPromptsSource — so without a raw ceiling a multi-GB authenticated
|
|
54
|
+
* POST OOMs the shared process. 512 KiB is:
|
|
55
|
+
* - comfortably ABOVE the largest valid manifest: 256 KiB decoded ≈ ~342 KiB
|
|
56
|
+
* base64 + JSON/path/mode overhead (~350 KiB raw), and
|
|
57
|
+
* - testably BELOW the ~1 MiB nginx-ingress limit the integration suite
|
|
58
|
+
* traverses, so the app returns 413 before the proxy would.
|
|
59
|
+
* The cap is scoped to the ingest route only (see parseRequestBody's maxBytes
|
|
60
|
+
* argument); every other endpoint keeps today's uncapped behavior.
|
|
61
|
+
*/
|
|
62
|
+
const INGEST_MAX_RAW_BODY_BYTES = 512 * 1024;
|
|
63
|
+
/** Pathname of the prompts source ingest endpoint the raw-body cap is scoped to. */
|
|
64
|
+
const PROMPTS_INGEST_PATHNAME = '/api/v1/prompts/sources';
|
|
65
|
+
/**
|
|
66
|
+
* PRD #647 F1 — thrown by parseRequestBody when the raw request body exceeds the
|
|
67
|
+
* per-route ceiling, so the HTTP handler can map it to a 413 (matching the mock
|
|
68
|
+
* server's read-json-body.ts) instead of buffering an unbounded body.
|
|
69
|
+
*/
|
|
70
|
+
class RequestBodyTooLargeError extends Error {
|
|
71
|
+
limit;
|
|
72
|
+
constructor(limit) {
|
|
73
|
+
super(`Request body exceeds ${limit} bytes`);
|
|
74
|
+
this.name = 'RequestBodyTooLargeError';
|
|
75
|
+
this.limit = limit;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
47
78
|
class MCPServer {
|
|
48
79
|
dotAI;
|
|
49
80
|
initialized = false;
|
|
@@ -496,12 +527,34 @@ class MCPServer {
|
|
|
496
527
|
// Parse request body for POST requests
|
|
497
528
|
let body = undefined;
|
|
498
529
|
if (req.method === 'POST') {
|
|
499
|
-
body
|
|
530
|
+
// PRD #647 F1: cap the raw body for the untrusted ingest route
|
|
531
|
+
// only (default = today's uncapped behavior elsewhere) and map
|
|
532
|
+
// an oversize body to 413 (matches the mock server).
|
|
533
|
+
const maxBytes = this.isPromptsIngestRequest(req.url)
|
|
534
|
+
? INGEST_MAX_RAW_BODY_BYTES
|
|
535
|
+
: undefined;
|
|
536
|
+
try {
|
|
537
|
+
body = await this.parseRequestBody(req, maxBytes);
|
|
538
|
+
}
|
|
539
|
+
catch (error) {
|
|
540
|
+
if (error instanceof RequestBodyTooLargeError) {
|
|
541
|
+
this.logger.warn('Request body too large', {
|
|
542
|
+
url: (0, rest_api_1.sanitizeRequestUrlForLogging)(req.url),
|
|
543
|
+
limit: error.limit,
|
|
544
|
+
});
|
|
545
|
+
(0, error_response_1.sendErrorResponse)(res, 413, 'PAYLOAD_TOO_LARGE', error.message);
|
|
546
|
+
endSpan(413);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
throw error;
|
|
550
|
+
}
|
|
500
551
|
}
|
|
501
552
|
// Check if this is a REST API request
|
|
502
553
|
if (this.restApiRouter.isApiRequest(req.url || '')) {
|
|
503
554
|
this.logger.debug('Routing to REST API handler', {
|
|
504
|
-
|
|
555
|
+
// PRD #647 M5 (F2): scrub credential-bearing ?repo=/?source=
|
|
556
|
+
// values before they reach the log (matches the REST handler).
|
|
557
|
+
url: (0, rest_api_1.sanitizeRequestUrlForLogging)(req.url),
|
|
505
558
|
});
|
|
506
559
|
// Mark span as REST API request
|
|
507
560
|
span.setAttribute('request.type', 'rest-api');
|
|
@@ -603,11 +656,49 @@ class MCPServer {
|
|
|
603
656
|
}).on('error', reject);
|
|
604
657
|
});
|
|
605
658
|
}
|
|
606
|
-
|
|
659
|
+
/**
|
|
660
|
+
* Buffer and JSON-parse the request body.
|
|
661
|
+
*
|
|
662
|
+
* PRD #647 F1: when `maxBytes` is supplied (the untrusted ingest route), the
|
|
663
|
+
* body is rejected with RequestBodyTooLargeError as soon as the declared
|
|
664
|
+
* Content-Length OR the accumulated bytes exceed the ceiling — so an
|
|
665
|
+
* authenticated multi-GB POST can't buffer unbounded and OOM the shared
|
|
666
|
+
* process. When `maxBytes` is omitted (every other endpoint), behavior is
|
|
667
|
+
* exactly as before: no cap.
|
|
668
|
+
*/
|
|
669
|
+
async parseRequestBody(req, maxBytes) {
|
|
607
670
|
return new Promise((resolve, reject) => {
|
|
671
|
+
// Reject up front on a declared Content-Length over the cap, so we never
|
|
672
|
+
// start buffering a body we already know is too large.
|
|
673
|
+
if (maxBytes !== undefined) {
|
|
674
|
+
const declaredLen = parseInt(req.headers['content-length'] || '0', 10);
|
|
675
|
+
if (!Number.isNaN(declaredLen) && declaredLen > maxBytes) {
|
|
676
|
+
reject(new RequestBodyTooLargeError(maxBytes));
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
608
680
|
let body = '';
|
|
609
|
-
|
|
681
|
+
let received = 0;
|
|
682
|
+
let aborted = false;
|
|
683
|
+
req.on('data', chunk => {
|
|
684
|
+
if (aborted)
|
|
685
|
+
return;
|
|
686
|
+
// Defense-in-depth: enforce the cap against actual bytes in case the
|
|
687
|
+
// Content-Length header is absent or lies.
|
|
688
|
+
if (maxBytes !== undefined) {
|
|
689
|
+
received += chunk.length;
|
|
690
|
+
if (received > maxBytes) {
|
|
691
|
+
aborted = true;
|
|
692
|
+
req.destroy();
|
|
693
|
+
reject(new RequestBodyTooLargeError(maxBytes));
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
body += chunk.toString();
|
|
698
|
+
});
|
|
610
699
|
req.on('end', () => {
|
|
700
|
+
if (aborted)
|
|
701
|
+
return;
|
|
611
702
|
try {
|
|
612
703
|
resolve(body ? JSON.parse(body) : undefined);
|
|
613
704
|
}
|
|
@@ -618,6 +709,29 @@ class MCPServer {
|
|
|
618
709
|
req.on('error', reject);
|
|
619
710
|
});
|
|
620
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* PRD #647 F1: true only for the prompts source ingest endpoint, the one
|
|
714
|
+
* untrusted route the raw-body cap is scoped to. Parses the pathname so a
|
|
715
|
+
* query string can't bypass (or wrongly trip) the cap.
|
|
716
|
+
*
|
|
717
|
+
* PRD #647 C2 (CodeRabbit): the cap check runs BEFORE route dispatch, so a
|
|
718
|
+
* non-canonical pathname must be normalized here or it slips past the cap and
|
|
719
|
+
* buffers an uncapped body (DoS). A strict `===` let `POST /api/v1/prompts/
|
|
720
|
+
* sources/` (trailing slash) — the same ingest surface — skip `maxBytes`.
|
|
721
|
+
* Collapse trailing slashes (keeping a bare "/" intact) before comparing.
|
|
722
|
+
*/
|
|
723
|
+
isPromptsIngestRequest(url) {
|
|
724
|
+
if (!url)
|
|
725
|
+
return false;
|
|
726
|
+
try {
|
|
727
|
+
const pathname = new URL(url, 'http://internal.invalid').pathname;
|
|
728
|
+
const normalized = pathname.replace(/\/+$/, '') || '/';
|
|
729
|
+
return normalized === PROMPTS_INGEST_PATHNAME;
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
621
735
|
async stop() {
|
|
622
736
|
// Stop OAuth provider pruning timer
|
|
623
737
|
if (this.oauthProvider) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-generator.d.ts","sourceRoot":"","sources":["../../src/interfaces/openapi-generator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"openapi-generator.d.ts","sourceRoot":"","sources":["../../src/interfaces/openapi-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,13 @@ class OpenApiGenerator {
|
|
|
26
26
|
specCache;
|
|
27
27
|
lastCacheUpdate = 0;
|
|
28
28
|
cacheValidityMs = 60000; // 1 minute
|
|
29
|
-
|
|
29
|
+
// Keyed on the Zod schema OBJECT identity, not a structural stringify: two
|
|
30
|
+
// distinct schemas that differ only in their `.describe()` text serialize to
|
|
31
|
+
// the same string, so a string key would conflate them and leak one schema's
|
|
32
|
+
// descriptions into the other's endpoint (e.g. the prompts list vs. render
|
|
33
|
+
// `?source=` query). Identity keying gives each schema object its own entry
|
|
34
|
+
// while a schema reused across routes still hits the cache.
|
|
35
|
+
schemaCache = new WeakMap();
|
|
30
36
|
constructor(toolRegistry, logger, config = {}, routeRegistry) {
|
|
31
37
|
this.toolRegistry = toolRegistry;
|
|
32
38
|
this.routeRegistry = routeRegistry;
|
|
@@ -472,6 +478,7 @@ class OpenApiGenerator {
|
|
|
472
478
|
404: 'Not found',
|
|
473
479
|
405: 'Method not allowed',
|
|
474
480
|
409: 'Conflict',
|
|
481
|
+
413: 'Payload too large',
|
|
475
482
|
422: 'Unprocessable entity',
|
|
476
483
|
500: 'Internal server error',
|
|
477
484
|
502: 'Bad gateway',
|
|
@@ -483,16 +490,15 @@ class OpenApiGenerator {
|
|
|
483
490
|
* Convert Zod schema to JSON Schema with caching
|
|
484
491
|
*/
|
|
485
492
|
zodSchemaToJsonSchema(schema) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
return this.schemaCache.get(cacheKey);
|
|
493
|
+
if (this.schemaCache.has(schema)) {
|
|
494
|
+
return this.schemaCache.get(schema);
|
|
489
495
|
}
|
|
490
496
|
try {
|
|
491
497
|
const result = zod_1.z.toJSONSchema(schema);
|
|
492
498
|
// Remove $schema and additionalProperties (not valid in OpenAPI component schemas)
|
|
493
499
|
delete result.$schema;
|
|
494
500
|
delete result.additionalProperties;
|
|
495
|
-
this.schemaCache.set(
|
|
501
|
+
this.schemaCache.set(schema, result);
|
|
496
502
|
return result;
|
|
497
503
|
}
|
|
498
504
|
catch (error) {
|
|
@@ -20,9 +20,11 @@ import { PluginManager } from '../core/plugin-manager';
|
|
|
20
20
|
export declare const UNPARSEABLE_QUERY_PLACEHOLDER = "?<redacted-unparseable>";
|
|
21
21
|
/**
|
|
22
22
|
* F3: req.url is logged on every request; with PRD #581 the query string may
|
|
23
|
-
* carry `?repo=<user-supplied-url>` whose value can include credentials
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* carry `?repo=<user-supplied-url>` whose value can include credentials, and
|
|
24
|
+
* PRD #647 adds `?source=<identifier>` which is equally credential-bearing (it
|
|
25
|
+
* may be a `https://user:tok@host` git URL). This helper rewrites BOTH values
|
|
26
|
+
* to their credential-scrubbed form so the raw token doesn't reach the log.
|
|
27
|
+
* Everything else is preserved verbatim.
|
|
26
28
|
*
|
|
27
29
|
* CodeRabbit Major B: on parse failure, we no longer return the input
|
|
28
30
|
* verbatim — an unparseable URL is more likely than a parseable one to hide
|
|
@@ -66,7 +68,7 @@ export declare function sanitizeRequestUrlForLogging(url: string | undefined): s
|
|
|
66
68
|
* BEFORE any clone or shared-cache mutation, so a rejected override can never
|
|
67
69
|
* corrupt the env-var-configured cache.
|
|
68
70
|
*/
|
|
69
|
-
export declare function extractPromptsOverride(repoParam: unknown, pathParam?: unknown, branchParam?: unknown, gitToken?: string): {
|
|
71
|
+
export declare function extractPromptsOverride(repoParam: unknown, pathParam?: unknown, branchParam?: unknown, gitToken?: string, sourceParam?: unknown): {
|
|
70
72
|
ok: true;
|
|
71
73
|
override?: UserPromptsOverride;
|
|
72
74
|
} | {
|
|
@@ -287,6 +289,18 @@ export declare class RestApiRouter {
|
|
|
287
289
|
* Handle prompts cache refresh requests (PRD #386, extended PRD #581)
|
|
288
290
|
*/
|
|
289
291
|
private handlePromptsCacheRefresh;
|
|
292
|
+
/**
|
|
293
|
+
* Handle prompts source ingestion (PRD #647 M2).
|
|
294
|
+
*
|
|
295
|
+
* Accepts a JSON manifest { source, contentHash, files:[{path, content(base64),
|
|
296
|
+
* mode}] }, base64-decodes and caches the uploaded skill source keyed by its
|
|
297
|
+
* `source` identifier in the in-memory ingested cache. A later
|
|
298
|
+
* POST /api/v1/prompts/:promptName?source=<identifier> renders it through the
|
|
299
|
+
* existing render path with no git operation. The (scrubbed) source is echoed
|
|
300
|
+
* back. Bearer-gated by the same checkBearerAuth path as every non-OpenAPI
|
|
301
|
+
* request.
|
|
302
|
+
*/
|
|
303
|
+
private handlePromptsSourceIngest;
|
|
290
304
|
/**
|
|
291
305
|
* Handle visualization requests (PRD #317)
|
|
292
306
|
* Returns structured visualization data for a query session
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-api.d.ts","sourceRoot":"","sources":["../../src/interfaces/rest-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAAE,iBAAiB,EAAc,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAQtC,OAAO,
|
|
1
|
+
{"version":3,"file":"rest-api.d.ts","sourceRoot":"","sources":["../../src/interfaces/rest-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAAE,iBAAiB,EAAc,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAQtC,OAAO,EAOL,mBAAmB,EAEpB,MAAM,6BAA6B,CAAC;AAuCrC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAevD;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,4BAA4B,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,MAAM,GAAG,SAAS,CAyCpB;AAgDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,OAAO,EAClB,SAAS,CAAC,EAAE,OAAO,EACnB,WAAW,CAAC,EAAE,OAAO,EACrB,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,OAAO,GAEnB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACjB,CA0EJ;AAED;;GAEG;AACH,oBAAY,UAAU;IACpB,EAAE,MAAM;IACR,WAAW,MAAM;IACjB,SAAS,MAAM;IACf,kBAAkB,MAAM;IACxB,qBAAqB,MAAM;IAC3B,WAAW,MAAM;IACjB,mBAAmB,MAAM;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,IAAI,CAAC,EAAE;QACL,MAAM,EAAE,OAAO,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,QAAQ,EAAE,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,OAAO,GACP,MAAM,GACN,OAAO,GACP,MAAM,GACN,WAAW,CAAC;AAEhB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EACH,MAAM,GACN;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAClC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAA;KAAE,GACvC,KAAK,CAAC;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC,GACF,wBAAwB,GACxB,4BAA4B,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,aAAa,CAAC,CAAgB;gBAGpC,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,aAAa,EAC7B,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM;IAkCrC;;;;OAIG;IACG,aAAa,CACjB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,IAAI,CAAC;IAsGhB;;;OAGG;YACW,aAAa;IA4G3B;;OAEG;YACW,mBAAmB;IAuEjC;;OAEG;YACW,mBAAmB;IAuJjC;;OAEG;YACW,iBAAiB;IAqC/B;;OAEG;YACW,yBAAyB;IA2EvC;;;;OAIG;YACW,sBAAsB;IAyDpC;;;OAGG;YACW,qBAAqB;IAmKnC;;;;OAIG;YACW,mBAAmB;IAoLjC;;;OAGG;YACW,mBAAmB;IAmDjC;;;OAGG;YACW,iBAAiB;IAuL/B;;;OAGG;YACW,eAAe;IA0J7B;;;OAGG;YACW,aAAa;IAuK3B;;OAEG;YACW,wBAAwB;IA8GtC;;OAEG;YACW,uBAAuB;IAgHrC;;OAEG;YACW,yBAAyB;IAkGvC;;;;;;;;;;OAUG;YACW,yBAAyB;IAmEvC;;;;OAIG;YACW,eAAe;IAwW7B;;;;OAIG;YACW,kBAAkB;IAkHhC;;;OAGG;YACW,oBAAoB;IAiDlC;;;;OAIG;YACW,sBAAsB;IAuEpC;;;;OAIG;YACW,2BAA2B;IAyQzC;;;;OAIG;YACW,kBAAkB;IA6PhC;;OAEG;YACW,+BAA+B;IA8D7C;;OAEG;YACW,gBAAgB;IAwG9B;;OAEG;YACW,eAAe;IAkD7B;;OAEG;YACW,gBAAgB;IA6E9B;;OAEG;IACH,OAAO,CAAC,cAAc;IAStB;;OAEG;YACW,gBAAgB;IAS9B;;OAEG;YACW,iBAAiB;IAyB/B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAMvC;;OAEG;IACH,SAAS,IAAI,aAAa;IAI1B;;;OAGG;IACH,gBAAgB,IAAI,iBAAiB;CAGtC"}
|
|
@@ -74,9 +74,11 @@ const rbac_1 = require("../core/rbac");
|
|
|
74
74
|
exports.UNPARSEABLE_QUERY_PLACEHOLDER = '?<redacted-unparseable>';
|
|
75
75
|
/**
|
|
76
76
|
* F3: req.url is logged on every request; with PRD #581 the query string may
|
|
77
|
-
* carry `?repo=<user-supplied-url>` whose value can include credentials
|
|
78
|
-
*
|
|
79
|
-
*
|
|
77
|
+
* carry `?repo=<user-supplied-url>` whose value can include credentials, and
|
|
78
|
+
* PRD #647 adds `?source=<identifier>` which is equally credential-bearing (it
|
|
79
|
+
* may be a `https://user:tok@host` git URL). This helper rewrites BOTH values
|
|
80
|
+
* to their credential-scrubbed form so the raw token doesn't reach the log.
|
|
81
|
+
* Everything else is preserved verbatim.
|
|
80
82
|
*
|
|
81
83
|
* CodeRabbit Major B: on parse failure, we no longer return the input
|
|
82
84
|
* verbatim — an unparseable URL is more likely than a parseable one to hide
|
|
@@ -88,8 +90,19 @@ exports.UNPARSEABLE_QUERY_PLACEHOLDER = '?<redacted-unparseable>';
|
|
|
88
90
|
function sanitizeRequestUrlForLogging(url) {
|
|
89
91
|
if (!url)
|
|
90
92
|
return url;
|
|
91
|
-
|
|
93
|
+
// Fast path: only walk the URL when it carries a credential-bearing param
|
|
94
|
+
// we know how to scrub (PRD #581 `repo=`, PRD #647 `source=`).
|
|
95
|
+
//
|
|
96
|
+
// CodeRabbit C3: a percent-encoded param NAME (e.g. `r%65po=`, `s%6Frce=`)
|
|
97
|
+
// decodes to `repo`/`source` once parsed, so the literal-substring fast path
|
|
98
|
+
// would early-return WITHOUT scrubbing and leak the credential into the log.
|
|
99
|
+
// Any `%` means a name could be encoded, so fall through to the full
|
|
100
|
+
// parse-and-scrub below (URLSearchParams decodes the name, catching it).
|
|
101
|
+
if (!url.includes('repo=') &&
|
|
102
|
+
!url.includes('source=') &&
|
|
103
|
+
!url.includes('%')) {
|
|
92
104
|
return url;
|
|
105
|
+
}
|
|
93
106
|
try {
|
|
94
107
|
// req.url is path-relative; provide a dummy base for URL parsing.
|
|
95
108
|
const parsed = new node_url_1.URL(url, 'http://internal.invalid');
|
|
@@ -97,6 +110,13 @@ function sanitizeRequestUrlForLogging(url) {
|
|
|
97
110
|
if (repo) {
|
|
98
111
|
parsed.searchParams.set('repo', (0, user_prompts_loader_1.sanitizeUrlForLogging)(repo));
|
|
99
112
|
}
|
|
113
|
+
// PRD #647 M5 (F2): `?source=` is scrubbed with the same deep helper used
|
|
114
|
+
// for the echoed `source` (userinfo + credential-bearing query params), so
|
|
115
|
+
// `?source=https://user:tok@host` never appears unscrubbed in the log.
|
|
116
|
+
const source = parsed.searchParams.get('source');
|
|
117
|
+
if (source) {
|
|
118
|
+
parsed.searchParams.set('source', (0, user_prompts_loader_1.scrubSourceUrl)(source));
|
|
119
|
+
}
|
|
100
120
|
// Return path + search only (drop the dummy base).
|
|
101
121
|
return parsed.pathname + parsed.search + parsed.hash;
|
|
102
122
|
}
|
|
@@ -183,7 +203,22 @@ function readGitTokenHeader(req) {
|
|
|
183
203
|
* BEFORE any clone or shared-cache mutation, so a rejected override can never
|
|
184
204
|
* corrupt the env-var-configured cache.
|
|
185
205
|
*/
|
|
186
|
-
function extractPromptsOverride(repoParam, pathParam, branchParam, gitToken) {
|
|
206
|
+
function extractPromptsOverride(repoParam, pathParam, branchParam, gitToken, sourceParam) {
|
|
207
|
+
// PRD #647 D1: an explicit `?source=<identifier>` selects an already-ingested
|
|
208
|
+
// (CLI-uploaded) source. It is the explicit ingested signal, so it takes
|
|
209
|
+
// precedence over `?repo=` and the clone-qualifying params (path/branch/token)
|
|
210
|
+
// do not apply — they only describe a git clone, which an ingested source is
|
|
211
|
+
// not. Resolution against the in-memory ingested cache (and the "never clone"
|
|
212
|
+
// guarantee) happens in loadUserPrompts via override.ingestedSource. The raw
|
|
213
|
+
// identifier doubles as repoUrl ONLY so computePromptsSource echoes the
|
|
214
|
+
// scrubbed source; it is never cloned or logged unscrubbed on this path.
|
|
215
|
+
if (typeof sourceParam === 'string' && sourceParam.trim() !== '') {
|
|
216
|
+
const identifier = sourceParam.trim();
|
|
217
|
+
return {
|
|
218
|
+
ok: true,
|
|
219
|
+
override: { repoUrl: identifier, ingestedSource: identifier },
|
|
220
|
+
};
|
|
221
|
+
}
|
|
187
222
|
// Treat absent (null/undefined) repo as no override. path/branch/token only
|
|
188
223
|
// qualify an override, so without a repo they are ignored (the credential
|
|
189
224
|
// header is inert on the env-var path).
|
|
@@ -376,6 +411,7 @@ class RestApiRouter {
|
|
|
376
411
|
'GET:/api/v1/logs': () => this.handleGetLogs(req, res, requestId, searchParams),
|
|
377
412
|
'GET:/api/v1/prompts': () => this.handlePromptsListRequest(req, res, requestId, searchParams),
|
|
378
413
|
'POST:/api/v1/prompts/refresh': () => this.handlePromptsCacheRefresh(req, res, requestId, body),
|
|
414
|
+
'POST:/api/v1/prompts/sources': () => this.handlePromptsSourceIngest(req, res, requestId, body),
|
|
379
415
|
'POST:/api/v1/prompts/:promptName': () => this.handlePromptsGetRequest(req, res, requestId, params.promptName, body, searchParams),
|
|
380
416
|
'GET:/api/v1/visualize/:sessionId': () => this.handleVisualize(req, res, requestId, params.sessionId, searchParams),
|
|
381
417
|
'GET:/api/v1/events/remediations': () => this.handleRemediationSSE(req, res, requestId),
|
|
@@ -1227,7 +1263,11 @@ class RestApiRouter {
|
|
|
1227
1263
|
// candidate.subPath / candidate.branch (absent → unchanged behavior).
|
|
1228
1264
|
// PRD #621 M2: X-Dot-AI-Git-Token header authenticates the override clone
|
|
1229
1265
|
// (inert when no ?repo= override is present).
|
|
1230
|
-
|
|
1266
|
+
// PRD #647 list-by-source: ?source= selects an already-ingested source,
|
|
1267
|
+
// resolved from the in-memory upload cache with no git operation (same
|
|
1268
|
+
// signal as the render path). Absent → byte-identical to today (env-var /
|
|
1269
|
+
// built-in set), so the no-?source= behavior is unchanged.
|
|
1270
|
+
const overrideResult = extractPromptsOverride(searchParams.get('repo'), searchParams.get('path'), searchParams.get('branch'), readGitTokenHeader(req), searchParams.get('source'));
|
|
1231
1271
|
if (!overrideResult.ok) {
|
|
1232
1272
|
await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'VALIDATION_ERROR', overrideResult.message);
|
|
1233
1273
|
return;
|
|
@@ -1249,6 +1289,24 @@ class RestApiRouter {
|
|
|
1249
1289
|
});
|
|
1250
1290
|
}
|
|
1251
1291
|
catch (error) {
|
|
1292
|
+
// A per-request override (?repo=) whose source can't be loaded is a
|
|
1293
|
+
// bad-gateway condition, not a server fault: surface it (issue #575)
|
|
1294
|
+
// instead of silently serving built-in prompts with HTTP 200. The
|
|
1295
|
+
// message is already credential-scrubbed by the loader.
|
|
1296
|
+
if (error instanceof user_prompts_loader_1.UserPromptsOverrideError) {
|
|
1297
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.BAD_GATEWAY, 'PROMPTS_SOURCE_ERROR', error.message);
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
// PRD #647 list-by-source (D2): an unknown/evicted ?source= identifier is
|
|
1301
|
+
// a caller-actionable validation error — surface the re-upload guidance as
|
|
1302
|
+
// a 400 (same mapping the render handler uses), NOT a generic 500 or a
|
|
1303
|
+
// silent success-with-builtins. The message names POST
|
|
1304
|
+
// /api/v1/prompts/sources and carries no clone/git/scheme vocabulary.
|
|
1305
|
+
const listErrorMessage = error instanceof Error ? error.message : String(error);
|
|
1306
|
+
if (listErrorMessage.includes('Ingested source not found')) {
|
|
1307
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'VALIDATION_ERROR', listErrorMessage);
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1252
1310
|
this.logger.error('Prompts list request failed', error instanceof Error ? error : new Error(String(error)), {
|
|
1253
1311
|
requestId,
|
|
1254
1312
|
});
|
|
@@ -1268,7 +1326,11 @@ class RestApiRouter {
|
|
|
1268
1326
|
// candidate.subPath / candidate.branch (absent → unchanged behavior).
|
|
1269
1327
|
// PRD #621 M2: X-Dot-AI-Git-Token header authenticates the override clone
|
|
1270
1328
|
// (inert when no ?repo= override is present).
|
|
1271
|
-
|
|
1329
|
+
// PRD #647 D1/M3: ?source= selects an already-ingested source, resolved
|
|
1330
|
+
// from the in-memory upload cache with no git operation (precedence over
|
|
1331
|
+
// ?repo=). Absent → unchanged behavior, so the env-var/clone paths are
|
|
1332
|
+
// byte-identical to today.
|
|
1333
|
+
const overrideResult = extractPromptsOverride(searchParams.get('repo'), searchParams.get('path'), searchParams.get('branch'), readGitTokenHeader(req), searchParams.get('source'));
|
|
1272
1334
|
if (!overrideResult.ok) {
|
|
1273
1335
|
await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'VALIDATION_ERROR', overrideResult.message);
|
|
1274
1336
|
return;
|
|
@@ -1291,14 +1353,24 @@ class RestApiRouter {
|
|
|
1291
1353
|
});
|
|
1292
1354
|
}
|
|
1293
1355
|
catch (error) {
|
|
1356
|
+
// A per-request override (?repo=) whose source can't be loaded is a
|
|
1357
|
+
// bad-gateway condition (issue #575); surface it before the generic
|
|
1358
|
+
// validation/500 mapping. The message is already credential-scrubbed.
|
|
1359
|
+
if (error instanceof user_prompts_loader_1.UserPromptsOverrideError) {
|
|
1360
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.BAD_GATEWAY, 'PROMPTS_SOURCE_ERROR', error.message);
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1294
1363
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1295
1364
|
this.logger.error('Prompt get request failed', error instanceof Error ? error : new Error(String(error)), {
|
|
1296
1365
|
requestId,
|
|
1297
1366
|
promptName,
|
|
1298
1367
|
});
|
|
1299
|
-
// Check if it's a validation error (missing required arguments
|
|
1368
|
+
// Check if it's a validation error (missing required arguments, prompt not
|
|
1369
|
+
// found, or a missing ingested ?source= identifier — PRD #647 D2, which
|
|
1370
|
+
// carries re-upload guidance and must be a 400, not a 500).
|
|
1300
1371
|
const isValidationError = errorMessage.includes('Missing required arguments') ||
|
|
1301
|
-
errorMessage.includes('Prompt not found')
|
|
1372
|
+
errorMessage.includes('Prompt not found') ||
|
|
1373
|
+
errorMessage.includes('Ingested source not found');
|
|
1302
1374
|
await this.sendErrorResponse(res, requestId, isValidationError
|
|
1303
1375
|
? HttpStatus.BAD_REQUEST
|
|
1304
1376
|
: HttpStatus.INTERNAL_SERVER_ERROR, isValidationError ? 'VALIDATION_ERROR' : 'PROMPT_GET_ERROR', errorMessage);
|
|
@@ -1347,10 +1419,67 @@ class RestApiRouter {
|
|
|
1347
1419
|
});
|
|
1348
1420
|
}
|
|
1349
1421
|
catch (error) {
|
|
1422
|
+
// A per-request override (body.repo) whose source can't be loaded is a
|
|
1423
|
+
// bad-gateway condition (issue #575); surface it instead of a generic
|
|
1424
|
+
// 500. The message is already credential-scrubbed by the loader.
|
|
1425
|
+
if (error instanceof user_prompts_loader_1.UserPromptsOverrideError) {
|
|
1426
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.BAD_GATEWAY, 'PROMPTS_SOURCE_ERROR', error.message);
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1350
1429
|
this.logger.error('Prompts cache refresh failed', error instanceof Error ? error : new Error(String(error)), { requestId });
|
|
1351
1430
|
await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'PROMPTS_CACHE_REFRESH_ERROR', 'Failed to refresh prompts cache');
|
|
1352
1431
|
}
|
|
1353
1432
|
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Handle prompts source ingestion (PRD #647 M2).
|
|
1435
|
+
*
|
|
1436
|
+
* Accepts a JSON manifest { source, contentHash, files:[{path, content(base64),
|
|
1437
|
+
* mode}] }, base64-decodes and caches the uploaded skill source keyed by its
|
|
1438
|
+
* `source` identifier in the in-memory ingested cache. A later
|
|
1439
|
+
* POST /api/v1/prompts/:promptName?source=<identifier> renders it through the
|
|
1440
|
+
* existing render path with no git operation. The (scrubbed) source is echoed
|
|
1441
|
+
* back. Bearer-gated by the same checkBearerAuth path as every non-OpenAPI
|
|
1442
|
+
* request.
|
|
1443
|
+
*/
|
|
1444
|
+
async handlePromptsSourceIngest(req, res, requestId, body) {
|
|
1445
|
+
try {
|
|
1446
|
+
this.logger.info('Processing prompts source ingest request', {
|
|
1447
|
+
requestId,
|
|
1448
|
+
});
|
|
1449
|
+
// All fields are untrusted; ingestPromptsSource validates and decodes
|
|
1450
|
+
// them, throwing PromptsSourceValidationError (→ 400) on a malformed or
|
|
1451
|
+
// unsafe manifest before anything is cached.
|
|
1452
|
+
const manifest = body;
|
|
1453
|
+
const result = (0, user_prompts_loader_1.ingestPromptsSource)({
|
|
1454
|
+
source: manifest?.source,
|
|
1455
|
+
contentHash: manifest?.contentHash,
|
|
1456
|
+
files: manifest?.files,
|
|
1457
|
+
}, this.logger);
|
|
1458
|
+
const response = {
|
|
1459
|
+
success: true,
|
|
1460
|
+
data: result,
|
|
1461
|
+
meta: {
|
|
1462
|
+
timestamp: new Date().toISOString(),
|
|
1463
|
+
requestId,
|
|
1464
|
+
version: this.config.version,
|
|
1465
|
+
},
|
|
1466
|
+
};
|
|
1467
|
+
await this.sendJsonResponse(res, HttpStatus.OK, response);
|
|
1468
|
+
this.logger.info('Prompts source ingest completed', {
|
|
1469
|
+
requestId,
|
|
1470
|
+
source: result.source,
|
|
1471
|
+
fileCount: result.fileCount,
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
catch (error) {
|
|
1475
|
+
const isValidationError = error instanceof user_prompts_loader_1.PromptsSourceValidationError;
|
|
1476
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1477
|
+
this.logger.error('Prompts source ingest failed', error instanceof Error ? error : new Error(String(error)), { requestId });
|
|
1478
|
+
await this.sendErrorResponse(res, requestId, isValidationError
|
|
1479
|
+
? HttpStatus.BAD_REQUEST
|
|
1480
|
+
: HttpStatus.INTERNAL_SERVER_ERROR, isValidationError ? 'VALIDATION_ERROR' : 'PROMPTS_SOURCE_INGEST_ERROR', isValidationError ? errorMessage : 'Failed to ingest prompts source');
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1354
1483
|
/**
|
|
1355
1484
|
* Handle visualization requests (PRD #317)
|
|
1356
1485
|
* Returns structured visualization data for a query session
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/interfaces/routes/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/interfaces/routes/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAgN5E;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAAe,CAC5C,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,CACR,EA6XA,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAInE;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
|
|
@@ -286,8 +286,17 @@ exports.routeDefinitions = [
|
|
|
286
286
|
method: 'GET',
|
|
287
287
|
description: 'List all available prompts',
|
|
288
288
|
tags: ['Prompts'],
|
|
289
|
+
// PRD #647 list-by-source: declare the per-request query params the list
|
|
290
|
+
// endpoint accepts. `?source=` enumerates a previously-ingested source (no
|
|
291
|
+
// git clone); `?repo=`/`?path=`/`?branch=` are the existing git overrides.
|
|
292
|
+
query: schemas_1.PromptsListQuerySchema,
|
|
289
293
|
response: schemas_1.PromptsListResponseSchema,
|
|
290
294
|
errorResponses: {
|
|
295
|
+
// PRD #647 list-by-source (D2): an unknown/evicted ?source= identifier
|
|
296
|
+
// returns 400 VALIDATION_ERROR with re-upload guidance — same contract as
|
|
297
|
+
// the render endpoint's ?source= miss.
|
|
298
|
+
400: schemas_1.PromptValidationErrorSchema,
|
|
299
|
+
502: schemas_1.PromptsSourceErrorSchema,
|
|
291
300
|
500: schemas_1.InternalServerErrorSchema,
|
|
292
301
|
},
|
|
293
302
|
},
|
|
@@ -298,19 +307,45 @@ exports.routeDefinitions = [
|
|
|
298
307
|
tags: ['Prompts'],
|
|
299
308
|
response: schemas_1.PromptsCacheRefreshResponseSchema,
|
|
300
309
|
errorResponses: {
|
|
310
|
+
502: schemas_1.PromptsSourceErrorSchema,
|
|
301
311
|
500: schemas_1.PromptsCacheRefreshErrorSchema,
|
|
302
312
|
},
|
|
303
313
|
},
|
|
314
|
+
{
|
|
315
|
+
path: '/api/v1/prompts/sources',
|
|
316
|
+
method: 'POST',
|
|
317
|
+
description: 'Ingest (upload) a skill source the server caches and renders via POST /api/v1/prompts/:promptName?source=<identifier> with no git clone (PRD #647)',
|
|
318
|
+
tags: ['Prompts'],
|
|
319
|
+
body: schemas_1.PromptsSourceIngestRequestSchema,
|
|
320
|
+
response: schemas_1.PromptsSourceIngestResponseSchema,
|
|
321
|
+
errorResponses: {
|
|
322
|
+
400: schemas_1.PromptsSourceIngestErrorSchema,
|
|
323
|
+
// PRD #647 A7/A9 (CodeRabbit): the 512 KiB raw-body cap returns 413
|
|
324
|
+
// (PAYLOAD_TOO_LARGE) from mcp.ts before route dispatch — document it.
|
|
325
|
+
413: schemas_1.PromptsSourceIngestPayloadTooLargeErrorSchema,
|
|
326
|
+
// PRD #647 A8 (CodeRabbit): narrowed to the only 500 code this handler
|
|
327
|
+
// emits, instead of the shared broad InternalServerErrorSchema union.
|
|
328
|
+
500: schemas_1.PromptsSourceIngestServerErrorSchema,
|
|
329
|
+
},
|
|
330
|
+
},
|
|
304
331
|
{
|
|
305
332
|
path: '/api/v1/prompts/:promptName',
|
|
306
333
|
method: 'POST',
|
|
307
334
|
description: 'Get a prompt with rendered template arguments',
|
|
308
335
|
tags: ['Prompts'],
|
|
309
336
|
params: PromptNameParamsSchema,
|
|
337
|
+
// PRD #647 A6 (CodeRabbit): declare the per-request render query params
|
|
338
|
+
// (?source= ingest-render, plus ?repo=/?path=/?branch= overrides).
|
|
339
|
+
query: schemas_1.PromptGetQuerySchema,
|
|
310
340
|
body: schemas_1.PromptGetRequestSchema,
|
|
311
341
|
response: schemas_1.PromptGetResponseSchema,
|
|
312
342
|
errorResponses: {
|
|
343
|
+
// PRD #647 (CodeRabbit): the render handler returns 400 VALIDATION_ERROR
|
|
344
|
+
// when ?source= resolution fails (unresolvable ingested source → D2) or
|
|
345
|
+
// required arguments are missing — document that contract.
|
|
346
|
+
400: schemas_1.PromptValidationErrorSchema,
|
|
313
347
|
404: schemas_1.PromptNotFoundErrorSchema,
|
|
348
|
+
502: schemas_1.PromptsSourceErrorSchema,
|
|
314
349
|
500: schemas_1.InternalServerErrorSchema,
|
|
315
350
|
},
|
|
316
351
|
},
|
|
@@ -119,6 +119,23 @@ export declare const MethodNotAllowedErrorSchema: z.ZodObject<{
|
|
|
119
119
|
code: z.ZodLiteral<"METHOD_NOT_ALLOWED">;
|
|
120
120
|
}, z.core.$strip>;
|
|
121
121
|
}, z.core.$strip>;
|
|
122
|
+
/**
|
|
123
|
+
* 413 returned when a request body exceeds a per-route raw-body cap (PRD #647:
|
|
124
|
+
* the 512 KiB prompts source ingest cap). The handler emits PAYLOAD_TOO_LARGE.
|
|
125
|
+
*/
|
|
126
|
+
export declare const PayloadTooLargeErrorSchema: z.ZodObject<{
|
|
127
|
+
success: z.ZodLiteral<false>;
|
|
128
|
+
meta: z.ZodOptional<z.ZodObject<{
|
|
129
|
+
timestamp: z.ZodString;
|
|
130
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
131
|
+
version: z.ZodString;
|
|
132
|
+
}, z.core.$strip>>;
|
|
133
|
+
error: z.ZodObject<{
|
|
134
|
+
message: z.ZodString;
|
|
135
|
+
details: z.ZodOptional<z.ZodAny>;
|
|
136
|
+
code: z.ZodLiteral<"PAYLOAD_TOO_LARGE">;
|
|
137
|
+
}, z.core.$strip>;
|
|
138
|
+
}, z.core.$strip>;
|
|
122
139
|
export declare const ServiceUnavailableErrorSchema: z.ZodObject<{
|
|
123
140
|
success: z.ZodLiteral<false>;
|
|
124
141
|
meta: z.ZodOptional<z.ZodObject<{
|
|
@@ -137,6 +154,21 @@ export declare const ServiceUnavailableErrorSchema: z.ZodObject<{
|
|
|
137
154
|
}>;
|
|
138
155
|
}, z.core.$strip>;
|
|
139
156
|
}, z.core.$strip>;
|
|
157
|
+
export declare const BadGatewayErrorSchema: z.ZodObject<{
|
|
158
|
+
success: z.ZodLiteral<false>;
|
|
159
|
+
meta: z.ZodOptional<z.ZodObject<{
|
|
160
|
+
timestamp: z.ZodString;
|
|
161
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
162
|
+
version: z.ZodString;
|
|
163
|
+
}, z.core.$strip>>;
|
|
164
|
+
error: z.ZodObject<{
|
|
165
|
+
message: z.ZodString;
|
|
166
|
+
details: z.ZodOptional<z.ZodAny>;
|
|
167
|
+
code: z.ZodEnum<{
|
|
168
|
+
PROMPTS_SOURCE_ERROR: "PROMPTS_SOURCE_ERROR";
|
|
169
|
+
}>;
|
|
170
|
+
}, z.core.$strip>;
|
|
171
|
+
}, z.core.$strip>;
|
|
140
172
|
export declare const InternalServerErrorSchema: z.ZodObject<{
|
|
141
173
|
success: z.ZodLiteral<false>;
|
|
142
174
|
meta: z.ZodOptional<z.ZodObject<{
|
|
@@ -166,6 +198,7 @@ export declare const InternalServerErrorSchema: z.ZodObject<{
|
|
|
166
198
|
SESSION_RETRIEVAL_ERROR: "SESSION_RETRIEVAL_ERROR";
|
|
167
199
|
MIGRATION_ERROR: "MIGRATION_ERROR";
|
|
168
200
|
PROMPTS_CACHE_REFRESH_ERROR: "PROMPTS_CACHE_REFRESH_ERROR";
|
|
201
|
+
PROMPTS_SOURCE_INGEST_ERROR: "PROMPTS_SOURCE_INGEST_ERROR";
|
|
169
202
|
USER_MANAGEMENT_ERROR: "USER_MANAGEMENT_ERROR";
|
|
170
203
|
}>;
|
|
171
204
|
}, z.core.$strip>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/interfaces/schemas/common.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,UAAU;;;;iBAOrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;iBAOhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAChE,UAAU,EAAE,CAAC;;;;;;;;kBAOd;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAI9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAI9B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;iBAUhC,CAAC;AAEH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;iBAItC,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;iBASxC,CAAC;AAEH,eAAO,MAAM,yBAAyB
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/interfaces/schemas/common.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,UAAU;;;;iBAOrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;iBAOhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAChE,UAAU,EAAE,CAAC;;;;;;;;kBAOd;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAI9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAI9B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;iBAUhC,CAAC;AAEH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;iBAItC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;iBAIrC,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;iBASxC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;iBAQhC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyBpC,CAAC"}
|