veryfront 0.1.216 → 0.1.217

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 (160) hide show
  1. package/esm/cli/commands/generate/integration-generator-helpers.d.ts +35 -0
  2. package/esm/cli/commands/generate/integration-generator-helpers.d.ts.map +1 -0
  3. package/esm/cli/commands/generate/integration-generator-helpers.js +123 -0
  4. package/esm/cli/commands/generate/integration-generator.d.ts +1 -2
  5. package/esm/cli/commands/generate/integration-generator.d.ts.map +1 -1
  6. package/esm/cli/commands/generate/integration-generator.js +1 -123
  7. package/esm/cli/commands/knowledge/command-helpers.d.ts +48 -0
  8. package/esm/cli/commands/knowledge/command-helpers.d.ts.map +1 -0
  9. package/esm/cli/commands/knowledge/command-helpers.js +216 -0
  10. package/esm/cli/commands/knowledge/command.d.ts +11 -16
  11. package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
  12. package/esm/cli/commands/knowledge/command.js +25 -212
  13. package/esm/cli/mcp/remote-file-tool-helpers.d.ts +5 -0
  14. package/esm/cli/mcp/remote-file-tool-helpers.d.ts.map +1 -0
  15. package/esm/cli/mcp/remote-file-tool-helpers.js +22 -0
  16. package/esm/cli/mcp/remote-file-tools.d.ts.map +1 -1
  17. package/esm/cli/mcp/remote-file-tools.js +1 -22
  18. package/esm/cli/templates/integration-loader-helpers.d.ts +8 -0
  19. package/esm/cli/templates/integration-loader-helpers.d.ts.map +1 -0
  20. package/esm/cli/templates/integration-loader-helpers.js +29 -0
  21. package/esm/cli/templates/integration-loader.d.ts.map +1 -1
  22. package/esm/cli/templates/integration-loader.js +5 -21
  23. package/esm/cli/templates/manifest.d.ts +1 -0
  24. package/esm/cli/templates/manifest.js +2 -1
  25. package/esm/deno.js +2 -2
  26. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  27. package/esm/src/agent/runtime/index.js +48 -90
  28. package/esm/src/chat/ag-ui-helpers.d.ts +17 -0
  29. package/esm/src/chat/ag-ui-helpers.d.ts.map +1 -0
  30. package/esm/src/chat/ag-ui-helpers.js +111 -0
  31. package/esm/src/chat/ag-ui.d.ts.map +1 -1
  32. package/esm/src/chat/ag-ui.js +1 -111
  33. package/esm/src/config/schemas/config.schema.d.ts +1 -0
  34. package/esm/src/config/schemas/config.schema.d.ts.map +1 -1
  35. package/esm/src/config/schemas/config.schema.js +11 -0
  36. package/esm/src/config/schemas/index.d.ts +11 -1
  37. package/esm/src/config/schemas/index.d.ts.map +1 -1
  38. package/esm/src/errors/error-registry-helpers.d.ts +6 -0
  39. package/esm/src/errors/error-registry-helpers.d.ts.map +1 -0
  40. package/esm/src/errors/error-registry-helpers.js +9 -0
  41. package/esm/src/errors/error-registry.d.ts.map +1 -1
  42. package/esm/src/errors/error-registry.js +4 -3
  43. package/esm/src/extensions/factory-loader.d.ts +29 -0
  44. package/esm/src/extensions/factory-loader.d.ts.map +1 -0
  45. package/esm/src/extensions/factory-loader.js +63 -0
  46. package/esm/src/extensions/index.d.ts +16 -0
  47. package/esm/src/extensions/index.d.ts.map +1 -1
  48. package/esm/src/extensions/index.js +16 -0
  49. package/esm/src/extensions/orchestrate.d.ts +54 -0
  50. package/esm/src/extensions/orchestrate.d.ts.map +1 -0
  51. package/esm/src/extensions/orchestrate.js +116 -0
  52. package/esm/src/html/styles-builder/plugin-loader.d.ts.map +1 -1
  53. package/esm/src/html/styles-builder/plugin-loader.js +23 -0
  54. package/esm/src/html/styles-builder/tailwind-plugin-allowlist.d.ts +29 -0
  55. package/esm/src/html/styles-builder/tailwind-plugin-allowlist.d.ts.map +1 -0
  56. package/esm/src/html/styles-builder/tailwind-plugin-allowlist.js +48 -0
  57. package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.d.ts +15 -0
  58. package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.d.ts.map +1 -0
  59. package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.js +22 -0
  60. package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts +0 -2
  61. package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
  62. package/esm/src/modules/react-loader/ssr-module-loader/loader.js +4 -24
  63. package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.d.ts +19 -0
  64. package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.d.ts.map +1 -0
  65. package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.js +26 -0
  66. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
  67. package/esm/src/platform/adapters/fs/veryfront/adapter.js +4 -20
  68. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts +1 -0
  69. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts.map +1 -1
  70. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.js +3 -0
  71. package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts.map +1 -1
  72. package/esm/src/platform/adapters/fs/veryfront/read-operations.js +4 -7
  73. package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.d.ts +28 -0
  74. package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.d.ts.map +1 -0
  75. package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.js +41 -0
  76. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +1 -4
  77. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
  78. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +7 -40
  79. package/esm/src/provider/runtime-loader/tool-input-status.d.ts +17 -0
  80. package/esm/src/provider/runtime-loader/tool-input-status.d.ts.map +1 -0
  81. package/esm/src/provider/runtime-loader/tool-input-status.js +155 -0
  82. package/esm/src/provider/runtime-loader.d.ts +2 -3
  83. package/esm/src/provider/runtime-loader.d.ts.map +1 -1
  84. package/esm/src/provider/runtime-loader.js +2 -155
  85. package/esm/src/rendering/orchestrator/pipeline-helpers.d.ts +8 -0
  86. package/esm/src/rendering/orchestrator/pipeline-helpers.d.ts.map +1 -0
  87. package/esm/src/rendering/orchestrator/pipeline-helpers.js +20 -0
  88. package/esm/src/rendering/orchestrator/pipeline.d.ts +0 -3
  89. package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
  90. package/esm/src/rendering/orchestrator/pipeline.js +4 -22
  91. package/esm/src/routing/api/module-loader/loader-helpers.d.ts +10 -0
  92. package/esm/src/routing/api/module-loader/loader-helpers.d.ts.map +1 -0
  93. package/esm/src/routing/api/module-loader/loader-helpers.js +62 -0
  94. package/esm/src/routing/api/module-loader/loader.d.ts +1 -1
  95. package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
  96. package/esm/src/routing/api/module-loader/loader.js +2 -60
  97. package/esm/src/server/bootstrap.d.ts +22 -2
  98. package/esm/src/server/bootstrap.d.ts.map +1 -1
  99. package/esm/src/server/bootstrap.js +67 -5
  100. package/esm/src/server/dev-ui/manifest.d.ts +2 -0
  101. package/esm/src/server/dev-ui/manifest.js +3 -1
  102. package/esm/src/server/handlers/request/api/project-discovery.d.ts.map +1 -1
  103. package/esm/src/server/handlers/request/api/project-discovery.js +14 -8
  104. package/esm/src/server/production-server.js +1 -1
  105. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.d.ts.map +1 -1
  106. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.js +2 -2
  107. package/esm/src/studio/bridge/bridge-bundle.generated.d.ts.map +1 -1
  108. package/esm/src/studio/bridge/bridge-bundle.generated.js +1 -1
  109. package/esm/src/tool/index.d.ts +1 -1
  110. package/esm/src/tool/index.d.ts.map +1 -1
  111. package/esm/src/tool/types.d.ts +20 -0
  112. package/esm/src/tool/types.d.ts.map +1 -1
  113. package/esm/src/utils/version-constant.d.ts +1 -1
  114. package/esm/src/utils/version-constant.js +1 -1
  115. package/package.json +1 -1
  116. package/src/cli/commands/generate/integration-generator-helpers.ts +185 -0
  117. package/src/cli/commands/generate/integration-generator.ts +12 -168
  118. package/src/cli/commands/knowledge/command-helpers.ts +295 -0
  119. package/src/cli/commands/knowledge/command.ts +33 -259
  120. package/src/cli/mcp/remote-file-tool-helpers.ts +27 -0
  121. package/src/cli/mcp/remote-file-tools.ts +6 -28
  122. package/src/cli/templates/integration-loader-helpers.ts +49 -0
  123. package/src/cli/templates/integration-loader.ts +10 -28
  124. package/src/cli/templates/manifest.js +2 -1
  125. package/src/deno.js +2 -2
  126. package/src/src/agent/runtime/index.ts +77 -94
  127. package/src/src/chat/ag-ui-helpers.ts +139 -0
  128. package/src/src/chat/ag-ui.ts +11 -139
  129. package/src/src/config/schemas/config.schema.ts +11 -0
  130. package/src/src/config/schemas/index.ts +15 -1
  131. package/src/src/errors/error-registry-helpers.ts +23 -0
  132. package/src/src/errors/error-registry.ts +8 -3
  133. package/src/src/extensions/factory-loader.ts +76 -0
  134. package/src/src/extensions/index.ts +20 -0
  135. package/src/src/extensions/orchestrate.ts +184 -0
  136. package/src/src/html/styles-builder/plugin-loader.ts +32 -0
  137. package/src/src/html/styles-builder/tailwind-plugin-allowlist.ts +51 -0
  138. package/src/src/modules/react-loader/ssr-module-loader/loader-helpers.ts +37 -0
  139. package/src/src/modules/react-loader/ssr-module-loader/loader.ts +8 -39
  140. package/src/src/platform/adapters/fs/veryfront/adapter-helpers.ts +43 -0
  141. package/src/src/platform/adapters/fs/veryfront/adapter.ts +8 -22
  142. package/src/src/platform/adapters/fs/veryfront/read-operations-helpers.ts +4 -0
  143. package/src/src/platform/adapters/fs/veryfront/read-operations.ts +4 -7
  144. package/src/src/platform/adapters/fs/veryfront/websocket-manager-helpers.ts +73 -0
  145. package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +29 -44
  146. package/src/src/provider/runtime-loader/tool-input-status.ts +210 -0
  147. package/src/src/provider/runtime-loader.ts +6 -203
  148. package/src/src/rendering/orchestrator/pipeline-helpers.ts +35 -0
  149. package/src/src/rendering/orchestrator/pipeline.ts +8 -35
  150. package/src/src/routing/api/module-loader/loader-helpers.ts +68 -0
  151. package/src/src/routing/api/module-loader/loader.ts +8 -65
  152. package/src/src/server/bootstrap.ts +88 -7
  153. package/src/src/server/dev-ui/manifest.js +3 -1
  154. package/src/src/server/handlers/request/api/project-discovery.ts +19 -8
  155. package/src/src/server/production-server.ts +1 -1
  156. package/src/src/server/services/rsc/endpoints/rsc-bundles.generated.ts +2 -2
  157. package/src/src/studio/bridge/bridge-bundle.generated.ts +1 -1
  158. package/src/src/tool/index.ts +1 -0
  159. package/src/src/tool/types.ts +21 -0
  160. package/src/src/utils/version-constant.ts +1 -1
@@ -2,7 +2,7 @@ import * as dntShim from "../../../_dnt.shims.js";
2
2
  import { z } from "zod";
3
3
  type SafeParseResult<T> = { success: true; data: T } | { success: false; error: z.ZodError };
4
4
  import { type CommandResult, createFileSystem, getEnv, runCommand } from "../../../src/platform/index.js";
5
- import { basename, extname, join, normalize, relative } from "../../../src/platform/compat/path/index.js";
5
+ import { basename } from "../../../src/platform/compat/path/index.js";
6
6
  import { withSpan } from "../../../src/observability/tracing/otlp-setup.js";
7
7
  import { cliLogger } from "../../utils/index.js";
8
8
  import { type ApiClient, createApiClient, resolveConfigWithAuth } from "../../shared/config.js";
@@ -11,9 +11,10 @@ import { getStringArg } from "../../shared/parsed-args.js";
11
11
  import { downloadUploadToFile, listAllUploads, type UploadItem } from "../uploads/command.js";
12
12
  import { putRemoteFileFromLocal } from "../files/command.js";
13
13
  import { knowledgeIngestPythonSource } from "./parser-source.js";
14
+ import * as commandHelpers from "./command-helpers.js";
14
15
  import { createJobUserLogger, type Logger, serverLogger } from "../../../src/utils/index.js";
15
16
  import { writeJobResultIfConfigured } from "../../utils/write-job-result.js";
16
- import { classifyKnowledgeDirectoryPath, classifyKnowledgeSourcePath } from "./source-policy.js";
17
+ import { classifyKnowledgeSourcePath } from "./source-policy.js";
17
18
  import {
18
19
  buildKnowledgeIngestJobResult,
19
20
  type KnowledgeIngestFailedFileResult,
@@ -172,108 +173,10 @@ export function parseKnowledgeIngestArgs(
172
173
  });
173
174
  }
174
175
 
175
- export function normalizeKnowledgeInputPath(inputPath: string): string {
176
- const normalizedPath = normalize(inputPath).replace(/^\/+/, "").replace(/\\/g, "/");
177
- if (!normalizedPath || normalizedPath.startsWith("..") || normalizedPath.startsWith("/")) {
178
- throw new Error(`Invalid knowledge input path: ${inputPath}`);
179
- }
180
- return normalizedPath;
181
- }
182
-
183
- export function normalizeProjectUploadPath(inputPath: string): string {
184
- return normalizeKnowledgeInputPath(inputPath);
185
- }
186
-
187
- export function formatKnowledgeUploadSource(uploadPath: string): string {
188
- const normalizedPath = normalizeKnowledgeInputPath(uploadPath);
189
- return normalizedPath === "uploads" || normalizedPath.startsWith("uploads/")
190
- ? normalizedPath
191
- : `uploads/${normalizedPath}`;
192
- }
193
-
194
- function resolveExplicitUploadPath(inputPath: string): string {
195
- const normalizedInput = normalizeKnowledgeInputPath(inputPath);
196
- const displayInput = inputPath.replace(/\\/g, "/");
197
- const uploadPath = normalizeProjectUploadPath(inputPath);
198
- if (
199
- !uploadPath || uploadPath === "uploads" || normalizedInput === "uploads" ||
200
- normalizedInput.endsWith("/")
201
- ) {
202
- throw new Error(
203
- `Directory upload references require --path <prefix> --all: ${displayInput}`,
204
- );
205
- }
206
- return uploadPath;
207
- }
208
-
209
- export function isLikelyLocalPath(value: string): boolean {
210
- return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") ||
211
- /^[A-Za-z]:[\\/]/.test(value);
212
- }
213
-
214
- function isProjectUploadReference(value: string): boolean {
215
- if (isLikelyLocalPath(value)) return false;
216
- const normalizedValue = normalize(value).replace(/\\/g, "/").replace(/^\/+/, "");
217
- return normalizedValue === "uploads" || normalizedValue.startsWith("uploads/");
218
- }
219
-
220
- function slugify(value: string): string {
221
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "document";
222
- }
223
-
224
- /**
225
- * Strip the chat-upload prefix that Studio prepends to uploaded files.
226
- *
227
- * Studio stores uploads with a generated prefix like:
228
- * chat-<uuid>-<timestamp>-<shortid>-<original-filename>
229
- *
230
- * This function removes the prefix so knowledge files use the clean
231
- * original filename (e.g. "agents" instead of
232
- * "chat-909d3dbc-5a9a-4156-97e4-bcceb5c2ec0d-1773942180291-fv1qg5-agents").
233
- */
234
- const CHAT_UPLOAD_PREFIX_RE =
235
- /^chat-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-\d+-[a-z0-9]+-/i;
236
-
237
- export function stripChatUploadPrefix(name: string): string {
238
- return name.replace(CHAT_UPLOAD_PREFIX_RE, "");
239
- }
240
-
241
176
  function defaultOutputRoot(): Promise<string> {
242
177
  return dntShim.Deno.makeTempDir({ prefix: "veryfront-knowledge-" });
243
178
  }
244
179
 
245
- export function resolveKnowledgeDownloadOutputDir(outputDir: string): string {
246
- return join(outputDir, ".uploads");
247
- }
248
-
249
- function createSkippedKnowledgeSource(input: {
250
- source: string;
251
- localSourcePath?: string | null;
252
- message: string;
253
- reason: KnowledgeIngestSkippedFileResult["reason"];
254
- }): KnowledgeIngestSkippedFileResult {
255
- return {
256
- source: input.source,
257
- localSourcePath: input.localSourcePath ?? null,
258
- message: input.message,
259
- reason: input.reason,
260
- };
261
- }
262
-
263
- function createFailedKnowledgeSource(input: {
264
- source: string;
265
- localSourcePath: string;
266
- message: string;
267
- reason: KnowledgeIngestFailedFileResult["reason"];
268
- }): KnowledgeIngestFailedFileResult {
269
- return {
270
- source: input.source,
271
- localSourcePath: input.localSourcePath,
272
- message: input.message,
273
- reason: input.reason,
274
- };
275
- }
276
-
277
180
  function classifySourceOrSkip(input: {
278
181
  source: string;
279
182
  localSourcePath?: string | null;
@@ -283,180 +186,51 @@ function classifySourceOrSkip(input: {
283
186
  return null;
284
187
  }
285
188
 
286
- return createSkippedKnowledgeSource({
189
+ return {
287
190
  source: input.source,
288
- localSourcePath: input.localSourcePath,
191
+ localSourcePath: input.localSourcePath ?? null,
289
192
  message: decision.message,
290
193
  reason: decision.reason,
291
- });
194
+ };
292
195
  }
293
196
 
294
- function classifyDirectoryOrSkip(input: {
295
- source: string;
296
- }): KnowledgeIngestSkippedFileResult | null {
297
- const decision = classifyKnowledgeDirectoryPath(input.source);
298
- if (decision.kind === "ingest") {
299
- return null;
300
- }
197
+ export const normalizeKnowledgeInputPath = commandHelpers.normalizeKnowledgeInputPath;
198
+ export const normalizeProjectUploadPath = commandHelpers.normalizeProjectUploadPath;
199
+ export const formatKnowledgeUploadSource = commandHelpers.formatKnowledgeUploadSource;
200
+ export const isLikelyLocalPath = commandHelpers.isLikelyLocalPath;
201
+ export const stripChatUploadPrefix = commandHelpers.stripChatUploadPrefix;
202
+ export const resolveKnowledgeDownloadOutputDir = commandHelpers.resolveKnowledgeDownloadOutputDir;
203
+ export const buildSuggestedSlug = commandHelpers.buildSuggestedSlug;
204
+ export const ensureUniqueSlugs = commandHelpers.ensureUniqueSlugs;
205
+ export const deriveKnowledgeRemotePath = commandHelpers.deriveKnowledgeRemotePath;
206
+ export const createKnowledgeIngestResult = commandHelpers.createKnowledgeIngestResult;
301
207
 
302
- return createSkippedKnowledgeSource({
303
- source: input.source,
304
- localSourcePath: null,
305
- message: decision.message,
306
- reason: decision.reason,
307
- });
208
+ function resolveExplicitUploadPath(inputPath: string): string {
209
+ return commandHelpers.resolveExplicitUploadPath(inputPath);
210
+ }
211
+
212
+ function isProjectUploadReference(value: string): boolean {
213
+ return commandHelpers.isProjectUploadReference(value);
214
+ }
215
+
216
+ function createFailedKnowledgeSource(input: {
217
+ source: string;
218
+ localSourcePath: string;
219
+ message: string;
220
+ reason: KnowledgeIngestFailedFileResult["reason"];
221
+ }): KnowledgeIngestFailedFileResult {
222
+ return commandHelpers.createFailedKnowledgeSource(input);
308
223
  }
309
224
 
310
225
  async function collectLocalFiles(
311
226
  root: string,
312
227
  recursive: boolean,
313
228
  ): Promise<KnowledgeSourceCollection> {
314
- const fs = createFileSystem();
315
- const stat = await fs.stat(root);
316
- if (stat.isFile) {
317
- const skipped = classifySourceOrSkip({ source: root, localSourcePath: root });
318
- return skipped == null
319
- ? {
320
- sources: [{ kind: "local", input: root, localPath: root }],
321
- skipped: [],
322
- }
323
- : {
324
- sources: [],
325
- skipped: [skipped],
326
- };
327
- }
328
- if (!stat.isDirectory) {
329
- return { sources: [], skipped: [] };
330
- }
331
-
332
- const skippedRootDirectory = classifyDirectoryOrSkip({ source: root });
333
- if (skippedRootDirectory != null) {
334
- return {
335
- sources: [],
336
- skipped: [skippedRootDirectory],
337
- };
338
- }
339
-
340
- const collection: KnowledgeSourceCollection = {
341
- sources: [],
342
- skipped: [],
343
- };
344
- async function walk(dir: string): Promise<void> {
345
- for await (const entry of fs.readDir(dir)) {
346
- const entryPath = join(dir, entry.name);
347
- if (entry.isDirectory) {
348
- const skipped = classifyDirectoryOrSkip({ source: entryPath });
349
- if (skipped != null) {
350
- collection.skipped.push(skipped);
351
- continue;
352
- }
353
- if (recursive) await walk(entryPath);
354
- continue;
355
- }
356
-
357
- if (!entry.isFile) {
358
- continue;
359
- }
360
-
361
- const skipped = classifySourceOrSkip({ source: entryPath, localSourcePath: entryPath });
362
- if (skipped != null) {
363
- collection.skipped.push(skipped);
364
- continue;
365
- }
366
-
367
- collection.sources.push({ kind: "local", input: root, localPath: entryPath });
368
- }
369
- }
370
-
371
- await walk(root);
372
- collection.sources.sort((left, right) => left.localPath.localeCompare(right.localPath));
373
- collection.skipped.sort((left, right) => left.source.localeCompare(right.source));
374
- return collection;
229
+ return commandHelpers.collectLocalFiles(root, recursive);
375
230
  }
376
231
 
377
232
  function buildSourceReference(source: KnowledgeSource): string {
378
- return source.kind === "upload"
379
- ? formatKnowledgeUploadSource(source.uploadPath)
380
- : source.localPath;
381
- }
382
-
383
- export function buildSuggestedSlug(source: KnowledgeSource, index: number): string {
384
- const normalized = normalize(
385
- source.kind === "upload" ? source.uploadPath : source.localPath,
386
- ).replace(/\\/g, "/");
387
-
388
- let stripped: string;
389
- if (source.kind === "upload") {
390
- stripped = normalized
391
- .replace(/^\/workspace\/uploads\//, "")
392
- .replace(/^\/workspace\//, "")
393
- .replace(/^uploads\//, "")
394
- .replace(/\.[^.]+$/, "");
395
- } else if (normalized.startsWith("/workspace/uploads/")) {
396
- stripped = normalized.replace(/^\/workspace\/uploads\//, "").replace(/\.[^.]+$/, "");
397
- } else if (normalized.startsWith("/workspace/")) {
398
- stripped = normalized.replace(/^\/workspace\//, "").replace(/\.[^.]+$/, "");
399
- } else if (normalized.startsWith("/")) {
400
- stripped = basename(normalized, extname(normalized));
401
- } else {
402
- stripped = normalized.replace(/\.[^.]+$/, "");
403
- }
404
-
405
- // Strip Studio's chat-upload prefix from the filename portion so that
406
- // knowledge files use the original clean filename.
407
- const lastSlash = stripped.lastIndexOf("/");
408
- if (lastSlash >= 0) {
409
- const dir = stripped.slice(0, lastSlash + 1);
410
- const file = stripChatUploadPrefix(stripped.slice(lastSlash + 1));
411
- stripped = file ? `${dir}${file}` : stripped;
412
- } else {
413
- stripped = stripChatUploadPrefix(stripped) || stripped;
414
- }
415
-
416
- return slugify(stripped || basename(normalized, extname(normalized)) || `document-${index + 1}`);
417
- }
418
- export function ensureUniqueSlugs(sources: KnowledgeSource[]): string[] {
419
- const counts = new Map<string, number>();
420
- return sources.map((source, index) => {
421
- const baseSlug = buildSuggestedSlug(source, index);
422
- const nextCount = (counts.get(baseSlug) ?? 0) + 1;
423
- counts.set(baseSlug, nextCount);
424
- return nextCount === 1 ? baseSlug : `${baseSlug}-${nextCount}`;
425
- });
426
- }
427
-
428
- export function deriveKnowledgeRemotePath(
429
- outputPath: string,
430
- outputDir: string,
431
- knowledgePath: string,
432
- ): string {
433
- const relativeOutputPath = relative(outputDir, outputPath).replace(/\\/g, "/");
434
- if (!relativeOutputPath || relativeOutputPath.startsWith("..")) {
435
- throw new Error(`Output path is outside output directory: ${outputPath}`);
436
- }
437
- const prefix = normalizeKnowledgeInputPath(knowledgePath);
438
- const normalizedRelative = normalize(relativeOutputPath).replace(/^\/+/, "");
439
- return `${prefix}/${normalizedRelative}`.replace(/\\/g, "/");
440
- }
441
-
442
- export function createKnowledgeIngestResult(input: {
443
- source: string;
444
- localSourcePath: string;
445
- outputPath: string;
446
- remotePath: string;
447
- parser: Pick<KnowledgeParserResult, "slug" | "stats" | "warnings" | "source_type" | "summary">;
448
- }): KnowledgeIngestFileResult {
449
- return {
450
- source: input.source,
451
- localSourcePath: input.localSourcePath,
452
- outputPath: input.outputPath,
453
- remotePath: input.remotePath,
454
- slug: input.parser.slug,
455
- sourceType: input.parser.source_type,
456
- summary: input.parser.summary,
457
- stats: input.parser.stats,
458
- warnings: input.parser.warnings,
459
- };
233
+ return commandHelpers.buildSourceReference(source);
460
234
  }
461
235
 
462
236
  export async function runKnowledgeParser(input: {
@@ -0,0 +1,27 @@
1
+ function encodeFilePath(path: string): string {
2
+ return path.split("/").map(encodeURIComponent).join("/");
3
+ }
4
+
5
+ function getBranchPath(branch?: string): string {
6
+ return branch ? `/branches/${branch}` : "";
7
+ }
8
+
9
+ export function getBranchParam(branch?: string): string {
10
+ return branch ? `?branch_id=${branch}` : "";
11
+ }
12
+
13
+ export function buildProjectApiPath(project: string, resource: string, branch?: string): string {
14
+ const normalizedResource = resource.startsWith("/") ? resource.slice(1) : resource;
15
+ return `/${project}${getBranchPath(branch)}/${normalizedResource}`;
16
+ }
17
+
18
+ export function buildProjectFilePath(project: string, filePath: string, branch?: string): string {
19
+ return buildProjectApiPath(project, `files/${encodeFilePath(filePath)}`, branch);
20
+ }
21
+
22
+ export function slugToName(slug: string): string {
23
+ return slug
24
+ .split("-")
25
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
26
+ .join(" ");
27
+ }
@@ -16,6 +16,12 @@ import { withSpan } from "../../src/observability/tracing/otlp-setup.js";
16
16
  import { randomSuffix } from "../shared/slug.js";
17
17
 
18
18
  import { DEFAULT_LOCAL_API_URL } from "../shared/constants.js";
19
+ import {
20
+ buildProjectApiPath,
21
+ buildProjectFilePath,
22
+ getBranchParam,
23
+ slugToName,
24
+ } from "./remote-file-tool-helpers.js";
19
25
 
20
26
  function getApiBaseUrl(): string {
21
27
  return getEnvironmentConfig().apiBaseUrl || DEFAULT_LOCAL_API_URL;
@@ -73,27 +79,6 @@ async function apiRequest<T>(
73
79
  }
74
80
  }
75
81
 
76
- function encodeFilePath(path: string): string {
77
- return path.split("/").map(encodeURIComponent).join("/");
78
- }
79
-
80
- function getBranchPath(branch?: string): string {
81
- return branch ? `/branches/${branch}` : "";
82
- }
83
-
84
- function getBranchParam(branch?: string): string {
85
- return branch ? `?branch_id=${branch}` : "";
86
- }
87
-
88
- function buildProjectApiPath(project: string, resource: string, branch?: string): string {
89
- const normalizedResource = resource.startsWith("/") ? resource.slice(1) : resource;
90
- return `/${project}${getBranchPath(branch)}/${normalizedResource}`;
91
- }
92
-
93
- function buildProjectFilePath(project: string, filePath: string, branch?: string): string {
94
- return buildProjectApiPath(project, `files/${encodeFilePath(filePath)}`, branch);
95
- }
96
-
97
82
  interface RemoteFile {
98
83
  id?: string;
99
84
  path: string;
@@ -145,13 +130,6 @@ interface Project {
145
130
 
146
131
  const MAX_SLUG_ATTEMPTS = 10;
147
132
 
148
- function slugToName(slug: string): string {
149
- return slug
150
- .split("-")
151
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
152
- .join(" ");
153
- }
154
-
155
133
  /**
156
134
  * Create a project with slug conflict retry.
157
135
  * On 409, appends a random suffix and retries (matches reserveProjectSlug behavior).
@@ -0,0 +1,49 @@
1
+ import { join } from "../../src/fs/index.js";
2
+ import type { IntegrationName, TemplateFile } from "./types.js";
3
+
4
+ export function resolveIntegrationModuleDir(
5
+ moduleUrl: string,
6
+ platform = typeof process !== "undefined" ? process.platform : undefined,
7
+ ): string {
8
+ const normalizedModuleUrl = new URL(".", moduleUrl);
9
+
10
+ if (normalizedModuleUrl.protocol !== "file:") return normalizedModuleUrl.href;
11
+
12
+ let moduleDir = normalizedModuleUrl.pathname;
13
+ if (platform === "win32" && moduleDir.startsWith("/")) {
14
+ moduleDir = moduleDir.slice(1);
15
+ }
16
+
17
+ return moduleDir;
18
+ }
19
+
20
+ export function buildIntegrationDirectory(
21
+ moduleDir: string,
22
+ integrationName: string,
23
+ ): string {
24
+ return join(moduleDir, "integrations", integrationName);
25
+ }
26
+
27
+ export function buildUnknownIntegrationErrors(
28
+ integrations: IntegrationName[],
29
+ availableIntegrations: readonly IntegrationName[],
30
+ ): string[] {
31
+ const availableList = availableIntegrations.join(", ");
32
+ return integrations
33
+ .filter((integration) => !availableIntegrations.includes(integration))
34
+ .map((integration) => `Unknown integration: ${integration}. Available: ${availableList}`);
35
+ }
36
+
37
+ export function mergeIntegrationFiles(
38
+ integrations: Array<{ files: TemplateFile[] }>,
39
+ ): TemplateFile[] {
40
+ const fileMap = new Map<string, TemplateFile>();
41
+
42
+ for (const integration of integrations) {
43
+ for (const file of integration.files) {
44
+ fileMap.set(file.path, file);
45
+ }
46
+ }
47
+
48
+ return [...fileMap.values()].sort((a, b) => a.path.localeCompare(b.path));
49
+ }
@@ -10,6 +10,12 @@
10
10
 
11
11
  import { createFileSystem, join } from "../../src/fs/index.js";
12
12
  import { loadTemplateFromDirectory } from "./loader.js";
13
+ import {
14
+ buildIntegrationDirectory,
15
+ buildUnknownIntegrationErrors,
16
+ mergeIntegrationFiles,
17
+ resolveIntegrationModuleDir,
18
+ } from "./integration-loader-helpers.js";
13
19
  import type {
14
20
  IntegrationConfig,
15
21
  IntegrationName,
@@ -140,23 +146,14 @@ export const USE_CASE_CONFIGS: Record<UseCaseName, UseCaseConfig> = {
140
146
  };
141
147
 
142
148
  function getModuleDir(): string {
143
- const moduleUrl = new URL(".", import.meta.url);
144
-
145
- if (moduleUrl.protocol !== "file:") return moduleUrl.href;
146
-
147
- let moduleDir = moduleUrl.pathname;
148
- if (typeof process !== "undefined" && process.platform === "win32" && moduleDir.startsWith("/")) {
149
- moduleDir = moduleDir.slice(1);
150
- }
151
-
152
- return moduleDir;
149
+ return resolveIntegrationModuleDir(import.meta.url);
153
150
  }
154
151
 
155
152
  /**
156
153
  * Get the directory path for an integration
157
154
  */
158
155
  export function getIntegrationDirectory(integrationName: string): string {
159
- return join(getModuleDir(), "integrations", integrationName);
156
+ return buildIntegrationDirectory(getModuleDir(), integrationName);
160
157
  }
161
158
 
162
159
  /**
@@ -200,15 +197,7 @@ export function validateIntegrations(integrations: IntegrationName[]): {
200
197
  valid: boolean;
201
198
  errors: string[];
202
199
  } {
203
- const errors: string[] = [];
204
-
205
- for (const integration of integrations) {
206
- if (AVAILABLE_INTEGRATIONS.includes(integration)) continue;
207
-
208
- errors.push(
209
- `Unknown integration: ${integration}. Available: ${AVAILABLE_INTEGRATIONS.join(", ")}`,
210
- );
211
- }
200
+ const errors = buildUnknownIntegrationErrors(integrations, AVAILABLE_INTEGRATIONS);
212
201
 
213
202
  return { valid: errors.length === 0, errors };
214
203
  }
@@ -225,8 +214,6 @@ export async function loadIntegrations(
225
214
  }> {
226
215
  const integrations: ResolvedIntegration[] = [];
227
216
  const errors: string[] = [];
228
- const fileMap = new Map<string, TemplateFile>();
229
-
230
217
  for (const name of integrationNames) {
231
218
  const integration = await loadIntegration(name);
232
219
  if (!integration) {
@@ -235,16 +222,11 @@ export async function loadIntegrations(
235
222
  }
236
223
 
237
224
  integrations.push(integration);
238
-
239
- for (const file of integration.files) {
240
- // Later integrations override earlier ones
241
- fileMap.set(file.path, file);
242
- }
243
225
  }
244
226
 
245
227
  return {
246
228
  integrations,
247
- files: [...fileMap.values()].sort((a, b) => a.path.localeCompare(b.path)),
229
+ files: mergeIntegrationFiles(integrations),
248
230
  errors,
249
231
  };
250
232
  }