@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.
@@ -41,6 +41,15 @@ export interface UserPromptsOverride {
41
41
  * or any log/error/source surface.
42
42
  */
43
43
  gitToken?: string;
44
+ /**
45
+ * PRD #647 D1: when set, this override resolves to an already-ingested source
46
+ * (uploaded via POST /api/v1/prompts/sources) identified by this string —
47
+ * NOT a git clone. loadUserPrompts reads the cached uploaded files directly
48
+ * and NEVER calls cloneRepo, so a `local:<label>` or server-unreachable
49
+ * `?repo=` URL is served without any git operation. `repoUrl` carries the
50
+ * same identifier only for the scrubbed `source` echo (computePromptsSource).
51
+ */
52
+ ingestedSource?: string;
44
53
  }
45
54
  /**
46
55
  * Cache state for tracking repository freshness.
@@ -54,6 +63,88 @@ interface CacheState {
54
63
  branch: string;
55
64
  subPath: string;
56
65
  }
66
+ /**
67
+ * PRD #647 F5 / D2-M4 — max number of distinct ingested sources held in memory.
68
+ * The cache is push-populated by authenticated uploads, so without a bound it
69
+ * grows unbounded across distinct identifiers (a memory-growth vector). This
70
+ * is a simple access-ordered LRU cap: on overflow the least-recently-used entry
71
+ * (oldest `uploadedAt`, refreshed on every successful render) is evicted, and a
72
+ * later `?source=` render of an evicted identifier hits the existing D2
73
+ * render-miss guidance (re-upload required). Correctness over tuning per the
74
+ * frozen contract — 50 distinct sources is ample for the CLI's per-host usage.
75
+ */
76
+ export declare const MAX_INGESTED_SOURCES = 50;
77
+ /**
78
+ * Thrown by ingestPromptsSource on a malformed/unsafe upload so the REST handler
79
+ * can map it to a 400 (vs. a 500 for unexpected IO failures).
80
+ */
81
+ export declare class PromptsSourceValidationError extends Error {
82
+ constructor(message: string);
83
+ }
84
+ /**
85
+ * PRD #647 D2 — thrown when a render names an ingested `?source=<identifier>`
86
+ * that is not (or no longer) cached. It carries actionable re-upload guidance
87
+ * and is mapped to a 400 VALIDATION_ERROR by the REST handler. Deliberately
88
+ * distinct from the generic "Prompt not found" so the caller knows to re-upload
89
+ * the source rather than wonder why a known skill name is missing — and it never
90
+ * triggers (nor mentions) a git clone, since ingested identifiers are never
91
+ * cloned.
92
+ */
93
+ export declare class IngestedSourceNotFoundError extends Error {
94
+ constructor(message: string);
95
+ }
96
+ /**
97
+ * PRD #647 D5 — sanitize an untrusted POSIX `mode` string from an uploaded
98
+ * manifest into a safe numeric file mode. Strips the special bits
99
+ * (setuid 04000, setgid 02000, sticky 01000) so an uploaded skill file can never
100
+ * carry an exec-escalation surprise, and keeps only the standard rwx permission
101
+ * bits (07777 → 0777). Anything unparseable falls back to a sane 0644.
102
+ */
103
+ export declare function sanitizeIngestFileMode(mode: unknown): number;
104
+ /** Shape of the uploaded manifest (validated at runtime; all fields untrusted). */
105
+ export interface IngestPromptsSourceInput {
106
+ source: unknown;
107
+ contentHash?: unknown;
108
+ files: unknown;
109
+ }
110
+ /** Result echoed back to the caller after a successful ingest. */
111
+ export interface IngestPromptsSourceResult {
112
+ /** Credential-scrubbed identifier the render path uses via ?source=. */
113
+ source: string;
114
+ contentHash?: string;
115
+ fileCount: number;
116
+ /**
117
+ * 'ingested' — the manifest was decoded, hardened, and (re)written.
118
+ * 'unchanged' — PRD #647 D3 short-circuit: an identical contentHash was
119
+ * already cached for this identifier, so nothing was re-decoded or rewritten.
120
+ */
121
+ status: 'ingested' | 'unchanged';
122
+ }
123
+ /**
124
+ * PRD #647 M2/M4/D5 — validate, base64-decode, harden, and cache an uploaded
125
+ * skill source.
126
+ *
127
+ * The decoded files are written to a fresh 0700 staging directory, then
128
+ * atomically promoted into the per-identifier slot (a hash of the identifier)
129
+ * and registered in the in-memory ingested cache so the existing render path
130
+ * (loadUserPrompts → loadPromptsFromDir) resolves them with no git operation.
131
+ * The atomic promote (F3) means a failed/invalid re-upload never destroys the
132
+ * prior cached entry.
133
+ *
134
+ * Hardening, all applied BEFORE the prior slot is touched so a rejected upload
135
+ * is never partially cached:
136
+ * - D3 dedup: an identical `contentHash` already cached for this identifier
137
+ * short-circuits with status 'unchanged' — nothing is re-decoded or rewritten.
138
+ * - D5 file-count cap: more than MAX_INGEST_FILES files is rejected up front.
139
+ * - D5 total-size cap: summed DECODED bytes over MAX_INGEST_TOTAL_BYTES is rejected.
140
+ * - D5 zip-slip: every file path goes through sanitizeRelativePath (reused
141
+ * from git-utils) to reject traversal/absolute paths.
142
+ * - F3 NUL byte: a `\0` in a file path is rejected with a 400 BEFORE any fs
143
+ * call (sanitizeRelativePath does not catch it).
144
+ * - D5 mode bits: each file's POSIX `mode` is sanitized (setuid/setgid/sticky
145
+ * stripped) via sanitizeIngestFileMode before the file is written.
146
+ */
147
+ export declare function ingestPromptsSource(input: IngestPromptsSourceInput, logger: Logger): IngestPromptsSourceResult;
57
148
  /**
58
149
  * Read user prompts configuration from environment variables
59
150
  * Returns null if DOT_AI_USER_PROMPTS_REPO is not set
@@ -116,18 +207,46 @@ export declare function sanitizeUrlForLogging(url: string): string;
116
207
  * helper was deferred per the M2 follow-up scope.
117
208
  */
118
209
  export declare function scrubSourceUrl(url: string): string;
210
+ /**
211
+ * Raised when a PER-REQUEST prompts-repo override (PRD #581/#621) fails to load —
212
+ * e.g. the clone is rejected (missing/wrong forwarded credential) or the source is
213
+ * unreachable. An env-var-configured repo failure falls back to built-in prompts,
214
+ * but an override is an explicit caller request, so its failure must surface as an
215
+ * error instead of silently returning fewer skills ("fail open" — issue #575).
216
+ *
217
+ * The `message` is already credential-scrubbed by the thrower, so callers may
218
+ * surface it to clients directly.
219
+ */
220
+ export declare class UserPromptsOverrideError extends Error {
221
+ constructor(message: string);
222
+ }
119
223
  /**
120
224
  * Load user prompts from a git repository.
121
225
  *
122
226
  * When `override` is supplied, fetches from that repository for this call only,
123
227
  * ignoring DOT_AI_USER_PROMPTS_REPO env vars. Otherwise, uses env-var configuration.
124
- * Returns empty array if not configured or on error.
228
+ *
229
+ * Returns an empty array when not configured, or when an ENV-VAR-configured repo
230
+ * fails to load (graceful fallback to built-in prompts). When a per-request
231
+ * `override` fails to load, throws {@link UserPromptsOverrideError} instead — the
232
+ * caller asked for that specific source, so the failure must not be swallowed.
125
233
  */
126
234
  export declare function loadUserPrompts(logger: Logger, forceRefresh?: boolean, override?: UserPromptsOverride): Promise<Prompt[]>;
127
235
  /**
128
236
  * Clear the cache state (useful for testing)
129
237
  */
130
238
  export declare function clearUserPromptsCache(): void;
239
+ /**
240
+ * Clear the ingested-source cache (PRD #647, useful for testing).
241
+ * Only drops the in-memory registry; on-disk directories are left to be
242
+ * overwritten by the next upload or cleaned with the tmp directory.
243
+ */
244
+ export declare function clearIngestedPromptsSources(): void;
245
+ /**
246
+ * Inspect the ingested-source cache (PRD #647, for testing/debugging).
247
+ * Returns the scrubbed identifiers currently cached.
248
+ */
249
+ export declare function getIngestedPromptsSources(): string[];
131
250
  /**
132
251
  * Get current cache state (for testing/debugging)
133
252
  */
@@ -1 +1 @@
1
- {"version":3,"file":"user-prompts-loader.d.ts","sourceRoot":"","sources":["../../src/core/user-prompts-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAO1C,OAAO,EAAE,MAAM,EAA8B,MAAM,kBAAkB,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,UAAU,UAAU;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,IAAI,CAsB/D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAS3E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,mBAAmB,GAC5B,iBAAiB,CAqDnB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAqB1C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUzD;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAoBlD;AA6VD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,YAAY,GAAE,OAAe,EAC7B,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CA0JnB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,UAAU,GAAG,IAAI,CAE5D"}
1
+ {"version":3,"file":"user-prompts-loader.d.ts","sourceRoot":"","sources":["../../src/core/user-prompts-loader.ts"],"names":[],"mappings":"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"}