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.
- package/esm/cli/commands/generate/integration-generator-helpers.d.ts +35 -0
- package/esm/cli/commands/generate/integration-generator-helpers.d.ts.map +1 -0
- package/esm/cli/commands/generate/integration-generator-helpers.js +123 -0
- package/esm/cli/commands/generate/integration-generator.d.ts +1 -2
- package/esm/cli/commands/generate/integration-generator.d.ts.map +1 -1
- package/esm/cli/commands/generate/integration-generator.js +1 -123
- package/esm/cli/commands/knowledge/command-helpers.d.ts +48 -0
- package/esm/cli/commands/knowledge/command-helpers.d.ts.map +1 -0
- package/esm/cli/commands/knowledge/command-helpers.js +216 -0
- package/esm/cli/commands/knowledge/command.d.ts +11 -16
- package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
- package/esm/cli/commands/knowledge/command.js +25 -212
- package/esm/cli/mcp/remote-file-tool-helpers.d.ts +5 -0
- package/esm/cli/mcp/remote-file-tool-helpers.d.ts.map +1 -0
- package/esm/cli/mcp/remote-file-tool-helpers.js +22 -0
- package/esm/cli/mcp/remote-file-tools.d.ts.map +1 -1
- package/esm/cli/mcp/remote-file-tools.js +1 -22
- package/esm/cli/templates/integration-loader-helpers.d.ts +8 -0
- package/esm/cli/templates/integration-loader-helpers.d.ts.map +1 -0
- package/esm/cli/templates/integration-loader-helpers.js +29 -0
- package/esm/cli/templates/integration-loader.d.ts.map +1 -1
- package/esm/cli/templates/integration-loader.js +5 -21
- package/esm/cli/templates/manifest.d.ts +1 -0
- package/esm/cli/templates/manifest.js +2 -1
- package/esm/deno.js +2 -2
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +48 -90
- package/esm/src/chat/ag-ui-helpers.d.ts +17 -0
- package/esm/src/chat/ag-ui-helpers.d.ts.map +1 -0
- package/esm/src/chat/ag-ui-helpers.js +111 -0
- package/esm/src/chat/ag-ui.d.ts.map +1 -1
- package/esm/src/chat/ag-ui.js +1 -111
- package/esm/src/config/schemas/config.schema.d.ts +1 -0
- package/esm/src/config/schemas/config.schema.d.ts.map +1 -1
- package/esm/src/config/schemas/config.schema.js +11 -0
- package/esm/src/config/schemas/index.d.ts +11 -1
- package/esm/src/config/schemas/index.d.ts.map +1 -1
- package/esm/src/errors/error-registry-helpers.d.ts +6 -0
- package/esm/src/errors/error-registry-helpers.d.ts.map +1 -0
- package/esm/src/errors/error-registry-helpers.js +9 -0
- package/esm/src/errors/error-registry.d.ts.map +1 -1
- package/esm/src/errors/error-registry.js +4 -3
- package/esm/src/extensions/factory-loader.d.ts +29 -0
- package/esm/src/extensions/factory-loader.d.ts.map +1 -0
- package/esm/src/extensions/factory-loader.js +63 -0
- package/esm/src/extensions/index.d.ts +16 -0
- package/esm/src/extensions/index.d.ts.map +1 -1
- package/esm/src/extensions/index.js +16 -0
- package/esm/src/extensions/orchestrate.d.ts +54 -0
- package/esm/src/extensions/orchestrate.d.ts.map +1 -0
- package/esm/src/extensions/orchestrate.js +116 -0
- package/esm/src/html/styles-builder/plugin-loader.d.ts.map +1 -1
- package/esm/src/html/styles-builder/plugin-loader.js +23 -0
- package/esm/src/html/styles-builder/tailwind-plugin-allowlist.d.ts +29 -0
- package/esm/src/html/styles-builder/tailwind-plugin-allowlist.d.ts.map +1 -0
- package/esm/src/html/styles-builder/tailwind-plugin-allowlist.js +48 -0
- package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.d.ts +15 -0
- package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.d.ts.map +1 -0
- package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.js +22 -0
- package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts +0 -2
- package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/loader.js +4 -24
- package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.d.ts +19 -0
- package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.d.ts.map +1 -0
- package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.js +26 -0
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/adapter.js +4 -20
- package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts +1 -0
- package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.js +3 -0
- package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/read-operations.js +4 -7
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.d.ts +28 -0
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.d.ts.map +1 -0
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.js +41 -0
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +1 -4
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +7 -40
- package/esm/src/provider/runtime-loader/tool-input-status.d.ts +17 -0
- package/esm/src/provider/runtime-loader/tool-input-status.d.ts.map +1 -0
- package/esm/src/provider/runtime-loader/tool-input-status.js +155 -0
- package/esm/src/provider/runtime-loader.d.ts +2 -3
- package/esm/src/provider/runtime-loader.d.ts.map +1 -1
- package/esm/src/provider/runtime-loader.js +2 -155
- package/esm/src/rendering/orchestrator/pipeline-helpers.d.ts +8 -0
- package/esm/src/rendering/orchestrator/pipeline-helpers.d.ts.map +1 -0
- package/esm/src/rendering/orchestrator/pipeline-helpers.js +20 -0
- package/esm/src/rendering/orchestrator/pipeline.d.ts +0 -3
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +4 -22
- package/esm/src/routing/api/module-loader/loader-helpers.d.ts +10 -0
- package/esm/src/routing/api/module-loader/loader-helpers.d.ts.map +1 -0
- package/esm/src/routing/api/module-loader/loader-helpers.js +62 -0
- package/esm/src/routing/api/module-loader/loader.d.ts +1 -1
- package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/loader.js +2 -60
- package/esm/src/server/bootstrap.d.ts +22 -2
- package/esm/src/server/bootstrap.d.ts.map +1 -1
- package/esm/src/server/bootstrap.js +67 -5
- package/esm/src/server/dev-ui/manifest.d.ts +2 -0
- package/esm/src/server/dev-ui/manifest.js +3 -1
- package/esm/src/server/handlers/request/api/project-discovery.d.ts.map +1 -1
- package/esm/src/server/handlers/request/api/project-discovery.js +14 -8
- package/esm/src/server/production-server.js +1 -1
- package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.d.ts.map +1 -1
- package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.js +2 -2
- package/esm/src/studio/bridge/bridge-bundle.generated.d.ts.map +1 -1
- package/esm/src/studio/bridge/bridge-bundle.generated.js +1 -1
- package/esm/src/tool/index.d.ts +1 -1
- package/esm/src/tool/index.d.ts.map +1 -1
- package/esm/src/tool/types.d.ts +20 -0
- package/esm/src/tool/types.d.ts.map +1 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/cli/commands/generate/integration-generator-helpers.ts +185 -0
- package/src/cli/commands/generate/integration-generator.ts +12 -168
- package/src/cli/commands/knowledge/command-helpers.ts +295 -0
- package/src/cli/commands/knowledge/command.ts +33 -259
- package/src/cli/mcp/remote-file-tool-helpers.ts +27 -0
- package/src/cli/mcp/remote-file-tools.ts +6 -28
- package/src/cli/templates/integration-loader-helpers.ts +49 -0
- package/src/cli/templates/integration-loader.ts +10 -28
- package/src/cli/templates/manifest.js +2 -1
- package/src/deno.js +2 -2
- package/src/src/agent/runtime/index.ts +77 -94
- package/src/src/chat/ag-ui-helpers.ts +139 -0
- package/src/src/chat/ag-ui.ts +11 -139
- package/src/src/config/schemas/config.schema.ts +11 -0
- package/src/src/config/schemas/index.ts +15 -1
- package/src/src/errors/error-registry-helpers.ts +23 -0
- package/src/src/errors/error-registry.ts +8 -3
- package/src/src/extensions/factory-loader.ts +76 -0
- package/src/src/extensions/index.ts +20 -0
- package/src/src/extensions/orchestrate.ts +184 -0
- package/src/src/html/styles-builder/plugin-loader.ts +32 -0
- package/src/src/html/styles-builder/tailwind-plugin-allowlist.ts +51 -0
- package/src/src/modules/react-loader/ssr-module-loader/loader-helpers.ts +37 -0
- package/src/src/modules/react-loader/ssr-module-loader/loader.ts +8 -39
- package/src/src/platform/adapters/fs/veryfront/adapter-helpers.ts +43 -0
- package/src/src/platform/adapters/fs/veryfront/adapter.ts +8 -22
- package/src/src/platform/adapters/fs/veryfront/read-operations-helpers.ts +4 -0
- package/src/src/platform/adapters/fs/veryfront/read-operations.ts +4 -7
- package/src/src/platform/adapters/fs/veryfront/websocket-manager-helpers.ts +73 -0
- package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +29 -44
- package/src/src/provider/runtime-loader/tool-input-status.ts +210 -0
- package/src/src/provider/runtime-loader.ts +6 -203
- package/src/src/rendering/orchestrator/pipeline-helpers.ts +35 -0
- package/src/src/rendering/orchestrator/pipeline.ts +8 -35
- package/src/src/routing/api/module-loader/loader-helpers.ts +68 -0
- package/src/src/routing/api/module-loader/loader.ts +8 -65
- package/src/src/server/bootstrap.ts +88 -7
- package/src/src/server/dev-ui/manifest.js +3 -1
- package/src/src/server/handlers/request/api/project-discovery.ts +19 -8
- package/src/src/server/production-server.ts +1 -1
- package/src/src/server/services/rsc/endpoints/rsc-bundles.generated.ts +2 -2
- package/src/src/studio/bridge/bridge-bundle.generated.ts +1 -1
- package/src/src/tool/index.ts +1 -0
- package/src/src/tool/types.ts +21 -0
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -12,6 +12,18 @@ import { createFileSystem, type FileSystem } from "../../../src/platform/index.j
|
|
|
12
12
|
import { ensureDir } from "../../utils/fs.js";
|
|
13
13
|
import { isInteractive as checkIsInteractive, promptSync } from "../../../src/platform/index.js";
|
|
14
14
|
import { isCiEnv, isDenoTestingEnv } from "../../../src/config/index.js";
|
|
15
|
+
import {
|
|
16
|
+
getNonInteractiveConfig,
|
|
17
|
+
getToolExecuteBody,
|
|
18
|
+
getToolInputSchema,
|
|
19
|
+
type IntegrationConfig,
|
|
20
|
+
normalizeTokenAuthMethod,
|
|
21
|
+
parseAdditionalAuthParams,
|
|
22
|
+
parseBooleanOption,
|
|
23
|
+
parseScopes,
|
|
24
|
+
type TokenAuthMethod,
|
|
25
|
+
validateIntegrationName,
|
|
26
|
+
} from "./integration-generator-helpers.js";
|
|
15
27
|
import { select } from "../../utils/terminal-select.js";
|
|
16
28
|
|
|
17
29
|
let fs: FileSystem;
|
|
@@ -41,27 +53,6 @@ export interface IntegrationGeneratorOptions {
|
|
|
41
53
|
skipPrompts?: boolean;
|
|
42
54
|
}
|
|
43
55
|
|
|
44
|
-
type TokenAuthMethod =
|
|
45
|
-
| "basic"
|
|
46
|
-
| "body"
|
|
47
|
-
| "client_secret_basic"
|
|
48
|
-
| "client_secret_post"
|
|
49
|
-
| "request_body";
|
|
50
|
-
|
|
51
|
-
interface IntegrationConfig {
|
|
52
|
-
name: string;
|
|
53
|
-
displayName: string;
|
|
54
|
-
authType: "oauth2" | "api-key";
|
|
55
|
-
apiBaseUrl: string;
|
|
56
|
-
authorizationUrl?: string;
|
|
57
|
-
tokenUrl?: string;
|
|
58
|
-
scopes: string[];
|
|
59
|
-
tokenAuthMethod: TokenAuthMethod;
|
|
60
|
-
additionalAuthParams: Record<string, string>;
|
|
61
|
-
usePKCE: boolean;
|
|
62
|
-
envVarPrefix: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
56
|
function canRunPrompts(): boolean {
|
|
66
57
|
return !(isCiEnv() || isDenoTestingEnv()) && checkIsInteractive();
|
|
67
58
|
}
|
|
@@ -73,86 +64,6 @@ function promptText(question: string, defaultValue?: string): Promise<string> {
|
|
|
73
64
|
return Promise.resolve(input?.trim() || defaultValue || "");
|
|
74
65
|
}
|
|
75
66
|
|
|
76
|
-
function parseScopes(scopes?: string): string[] {
|
|
77
|
-
return scopes?.split(",").map((s) => s.trim()) || [];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function parseAdditionalAuthParams(params?: string): Record<string, string> {
|
|
81
|
-
if (!params?.trim()) return {};
|
|
82
|
-
|
|
83
|
-
return Object.fromEntries(
|
|
84
|
-
params.split(",")
|
|
85
|
-
.map((entry) => entry.trim())
|
|
86
|
-
.filter(Boolean)
|
|
87
|
-
.map((entry) => {
|
|
88
|
-
const separatorIndex = entry.indexOf("=");
|
|
89
|
-
if (separatorIndex === -1) {
|
|
90
|
-
throw new Error(
|
|
91
|
-
"Additional auth params must use key=value pairs separated by commas",
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const key = entry.slice(0, separatorIndex).trim();
|
|
96
|
-
const value = entry.slice(separatorIndex + 1).trim();
|
|
97
|
-
|
|
98
|
-
if (!key || !value) {
|
|
99
|
-
throw new Error(
|
|
100
|
-
"Additional auth params must use non-empty key=value pairs",
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return [key, value];
|
|
105
|
-
}),
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function normalizeTokenAuthMethod(method?: string): TokenAuthMethod {
|
|
110
|
-
switch (method?.trim().toLowerCase()) {
|
|
111
|
-
case "basic":
|
|
112
|
-
return "basic";
|
|
113
|
-
case "body":
|
|
114
|
-
return "body";
|
|
115
|
-
case "client_secret_basic":
|
|
116
|
-
return "client_secret_basic";
|
|
117
|
-
case "client_secret_post":
|
|
118
|
-
return "client_secret_post";
|
|
119
|
-
case "request_body":
|
|
120
|
-
case undefined:
|
|
121
|
-
case "":
|
|
122
|
-
return "request_body";
|
|
123
|
-
default:
|
|
124
|
-
throw new Error(
|
|
125
|
-
"OAuth token auth method must be one of: request_body, body, basic, client_secret_basic, client_secret_post",
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function parseBooleanOption(value: string | boolean | undefined, defaultValue = false): boolean {
|
|
131
|
-
if (typeof value === "boolean") return value;
|
|
132
|
-
if (!value?.trim()) return defaultValue;
|
|
133
|
-
|
|
134
|
-
switch (value.trim().toLowerCase()) {
|
|
135
|
-
case "y":
|
|
136
|
-
case "yes":
|
|
137
|
-
case "true":
|
|
138
|
-
case "1":
|
|
139
|
-
return true;
|
|
140
|
-
case "n":
|
|
141
|
-
case "no":
|
|
142
|
-
case "false":
|
|
143
|
-
case "0":
|
|
144
|
-
return false;
|
|
145
|
-
default:
|
|
146
|
-
throw new Error("Boolean option must be yes/no, true/false, or 1/0");
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function validateIntegrationName(name: string): void {
|
|
151
|
-
if (!name || !/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
152
|
-
throw new Error("Integration name must be lowercase letters, numbers, and hyphens");
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
67
|
export async function generateIntegration(
|
|
157
68
|
projectDir: string,
|
|
158
69
|
options: IntegrationGeneratorOptions = {},
|
|
@@ -182,32 +93,6 @@ export async function generateIntegration(
|
|
|
182
93
|
console.log("");
|
|
183
94
|
}
|
|
184
95
|
|
|
185
|
-
function getNonInteractiveConfig(options: IntegrationGeneratorOptions): IntegrationConfig {
|
|
186
|
-
const { name, displayName, authType } = options;
|
|
187
|
-
|
|
188
|
-
if (!name || !displayName || !authType) {
|
|
189
|
-
throw new Error(
|
|
190
|
-
"Non-interactive mode requires --name, --display-name, and --auth-type options",
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const normalizedName = name.toLowerCase();
|
|
195
|
-
|
|
196
|
-
return {
|
|
197
|
-
name: normalizedName,
|
|
198
|
-
displayName,
|
|
199
|
-
authType,
|
|
200
|
-
apiBaseUrl: options.apiBaseUrl ?? `https://api.${name}.com`,
|
|
201
|
-
authorizationUrl: options.authorizationUrl,
|
|
202
|
-
tokenUrl: options.tokenUrl,
|
|
203
|
-
scopes: parseScopes(options.scopes),
|
|
204
|
-
tokenAuthMethod: normalizeTokenAuthMethod(options.tokenAuthMethod),
|
|
205
|
-
additionalAuthParams: parseAdditionalAuthParams(options.additionalAuthParams),
|
|
206
|
-
usePKCE: parseBooleanOption(options.usePKCE, false),
|
|
207
|
-
envVarPrefix: name.toUpperCase().replace(/-/g, "_"),
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
96
|
async function getInteractiveConfig(
|
|
212
97
|
options: IntegrationGeneratorOptions,
|
|
213
98
|
): Promise<IntegrationConfig> {
|
|
@@ -809,47 +694,6 @@ export async function searchItems(query: string): Promise<unknown[]> {
|
|
|
809
694
|
cliLogger.debug("Created API client");
|
|
810
695
|
}
|
|
811
696
|
|
|
812
|
-
function getToolInputSchema(toolFile: string): string {
|
|
813
|
-
return TOOL_FILE_CONTENTS[toolFile]?.inputSchema ?? "";
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
function getToolExecuteBody(toolFile: string): string {
|
|
817
|
-
return TOOL_FILE_CONTENTS[toolFile]?.executeBody ?? "";
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
const TOOL_FILE_CONTENTS: Record<string, { inputSchema: string; executeBody: string }> = {
|
|
821
|
-
"list-items.ts": {
|
|
822
|
-
inputSchema: `limit: z.number().optional().describe("Maximum number of items to return"),
|
|
823
|
-
offset: z.number().optional().describe("Number of items to skip"),`,
|
|
824
|
-
executeBody: `const items = await listItems({
|
|
825
|
-
limit: input.limit,
|
|
826
|
-
offset: input.offset,
|
|
827
|
-
});
|
|
828
|
-
return {
|
|
829
|
-
success: true,
|
|
830
|
-
items,
|
|
831
|
-
count: items.length,
|
|
832
|
-
};`,
|
|
833
|
-
},
|
|
834
|
-
"get-item.ts": {
|
|
835
|
-
inputSchema: `id: z.string().describe("The ID of the item to retrieve"),`,
|
|
836
|
-
executeBody: `const item = await getItem(input.id);
|
|
837
|
-
return {
|
|
838
|
-
success: true,
|
|
839
|
-
item,
|
|
840
|
-
};`,
|
|
841
|
-
},
|
|
842
|
-
"search.ts": {
|
|
843
|
-
inputSchema: `query: z.string().describe("Search query"),`,
|
|
844
|
-
executeBody: `const results = await searchItems(input.query);
|
|
845
|
-
return {
|
|
846
|
-
success: true,
|
|
847
|
-
results,
|
|
848
|
-
count: results.length,
|
|
849
|
-
};`,
|
|
850
|
-
},
|
|
851
|
-
};
|
|
852
|
-
|
|
853
697
|
async function createToolSkeletons(baseDir: string, config: IntegrationConfig): Promise<void> {
|
|
854
698
|
const tools = [
|
|
855
699
|
{
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { createFileSystem } from "../../../src/platform/index.js";
|
|
2
|
+
import { basename, extname, join, normalize, relative } from "../../../src/platform/compat/path/index.js";
|
|
3
|
+
import { classifyKnowledgeDirectoryPath, classifyKnowledgeSourcePath } from "./source-policy.js";
|
|
4
|
+
import type {
|
|
5
|
+
KnowledgeIngestFailedFileResult,
|
|
6
|
+
KnowledgeIngestFileResult,
|
|
7
|
+
KnowledgeIngestSkippedFileResult,
|
|
8
|
+
} from "./result.js";
|
|
9
|
+
|
|
10
|
+
export type KnowledgeSource =
|
|
11
|
+
| { kind: "local"; input: string; localPath: string }
|
|
12
|
+
| { kind: "upload"; input: string; uploadPath: string; localPath: string };
|
|
13
|
+
|
|
14
|
+
export interface KnowledgeSourceCollection {
|
|
15
|
+
sources: KnowledgeSource[];
|
|
16
|
+
skipped: KnowledgeIngestSkippedFileResult[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function slugify(value: string): string {
|
|
20
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "document";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const CHAT_UPLOAD_PREFIX_RE =
|
|
24
|
+
/^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;
|
|
25
|
+
|
|
26
|
+
export function normalizeKnowledgeInputPath(inputPath: string): string {
|
|
27
|
+
const normalizedPath = normalize(inputPath).replace(/^\/+/, "").replace(/\\/g, "/");
|
|
28
|
+
if (!normalizedPath || normalizedPath.startsWith("..") || normalizedPath.startsWith("/")) {
|
|
29
|
+
throw new Error(`Invalid knowledge input path: ${inputPath}`);
|
|
30
|
+
}
|
|
31
|
+
return normalizedPath;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function normalizeProjectUploadPath(inputPath: string): string {
|
|
35
|
+
return normalizeKnowledgeInputPath(inputPath);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function formatKnowledgeUploadSource(uploadPath: string): string {
|
|
39
|
+
const normalizedPath = normalizeKnowledgeInputPath(uploadPath);
|
|
40
|
+
return normalizedPath === "uploads" || normalizedPath.startsWith("uploads/")
|
|
41
|
+
? normalizedPath
|
|
42
|
+
: `uploads/${normalizedPath}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolveExplicitUploadPath(inputPath: string): string {
|
|
46
|
+
const normalizedInput = normalizeKnowledgeInputPath(inputPath);
|
|
47
|
+
const displayInput = inputPath.replace(/\\/g, "/");
|
|
48
|
+
const uploadPath = normalizeProjectUploadPath(inputPath);
|
|
49
|
+
if (
|
|
50
|
+
!uploadPath || uploadPath === "uploads" || normalizedInput === "uploads" ||
|
|
51
|
+
normalizedInput.endsWith("/")
|
|
52
|
+
) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Directory upload references require --path <prefix> --all: ${displayInput}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return uploadPath;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isLikelyLocalPath(value: string): boolean {
|
|
61
|
+
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") ||
|
|
62
|
+
/^[A-Za-z]:[\\/]/.test(value);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isProjectUploadReference(value: string): boolean {
|
|
66
|
+
if (isLikelyLocalPath(value)) return false;
|
|
67
|
+
const normalizedValue = normalize(value).replace(/\\/g, "/").replace(/^\/+/, "");
|
|
68
|
+
return normalizedValue === "uploads" || normalizedValue.startsWith("uploads/");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function stripChatUploadPrefix(name: string): string {
|
|
72
|
+
return name.replace(CHAT_UPLOAD_PREFIX_RE, "");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function resolveKnowledgeDownloadOutputDir(outputDir: string): string {
|
|
76
|
+
return join(outputDir, ".uploads");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function createSkippedKnowledgeSource(input: {
|
|
80
|
+
source: string;
|
|
81
|
+
localSourcePath?: string | null;
|
|
82
|
+
message: string;
|
|
83
|
+
reason: KnowledgeIngestSkippedFileResult["reason"];
|
|
84
|
+
}): KnowledgeIngestSkippedFileResult {
|
|
85
|
+
return {
|
|
86
|
+
source: input.source,
|
|
87
|
+
localSourcePath: input.localSourcePath ?? null,
|
|
88
|
+
message: input.message,
|
|
89
|
+
reason: input.reason,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function createFailedKnowledgeSource(input: {
|
|
94
|
+
source: string;
|
|
95
|
+
localSourcePath: string;
|
|
96
|
+
message: string;
|
|
97
|
+
reason: KnowledgeIngestFailedFileResult["reason"];
|
|
98
|
+
}): KnowledgeIngestFailedFileResult {
|
|
99
|
+
return {
|
|
100
|
+
source: input.source,
|
|
101
|
+
localSourcePath: input.localSourcePath,
|
|
102
|
+
message: input.message,
|
|
103
|
+
reason: input.reason,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function classifySourceOrSkip(input: {
|
|
108
|
+
source: string;
|
|
109
|
+
localSourcePath?: string | null;
|
|
110
|
+
}): KnowledgeIngestSkippedFileResult | null {
|
|
111
|
+
const decision = classifyKnowledgeSourcePath(input.source);
|
|
112
|
+
if (decision.kind === "ingest") {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return createSkippedKnowledgeSource({
|
|
117
|
+
source: input.source,
|
|
118
|
+
localSourcePath: input.localSourcePath,
|
|
119
|
+
message: decision.message,
|
|
120
|
+
reason: decision.reason,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function classifyDirectoryOrSkip(
|
|
125
|
+
input: { source: string },
|
|
126
|
+
): KnowledgeIngestSkippedFileResult | null {
|
|
127
|
+
const decision = classifyKnowledgeDirectoryPath(input.source);
|
|
128
|
+
if (decision.kind === "ingest") {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return createSkippedKnowledgeSource({
|
|
133
|
+
source: input.source,
|
|
134
|
+
localSourcePath: null,
|
|
135
|
+
message: decision.message,
|
|
136
|
+
reason: decision.reason,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function collectLocalFiles(
|
|
141
|
+
root: string,
|
|
142
|
+
recursive: boolean,
|
|
143
|
+
): Promise<KnowledgeSourceCollection> {
|
|
144
|
+
const fs = createFileSystem();
|
|
145
|
+
const stat = await fs.stat(root);
|
|
146
|
+
if (stat.isFile) {
|
|
147
|
+
const skipped = classifySourceOrSkip({ source: root, localSourcePath: root });
|
|
148
|
+
return skipped == null
|
|
149
|
+
? {
|
|
150
|
+
sources: [{ kind: "local", input: root, localPath: root }],
|
|
151
|
+
skipped: [],
|
|
152
|
+
}
|
|
153
|
+
: {
|
|
154
|
+
sources: [],
|
|
155
|
+
skipped: [skipped],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (!stat.isDirectory) {
|
|
159
|
+
return { sources: [], skipped: [] };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const skippedRootDirectory = classifyDirectoryOrSkip({ source: root });
|
|
163
|
+
if (skippedRootDirectory != null) {
|
|
164
|
+
return {
|
|
165
|
+
sources: [],
|
|
166
|
+
skipped: [skippedRootDirectory],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const collection: KnowledgeSourceCollection = {
|
|
171
|
+
sources: [],
|
|
172
|
+
skipped: [],
|
|
173
|
+
};
|
|
174
|
+
async function walk(dir: string): Promise<void> {
|
|
175
|
+
for await (const entry of fs.readDir(dir)) {
|
|
176
|
+
const entryPath = join(dir, entry.name);
|
|
177
|
+
if (entry.isDirectory) {
|
|
178
|
+
const skipped = classifyDirectoryOrSkip({ source: entryPath });
|
|
179
|
+
if (skipped != null) {
|
|
180
|
+
collection.skipped.push(skipped);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (recursive) await walk(entryPath);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!entry.isFile) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const skipped = classifySourceOrSkip({ source: entryPath, localSourcePath: entryPath });
|
|
192
|
+
if (skipped != null) {
|
|
193
|
+
collection.skipped.push(skipped);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
collection.sources.push({ kind: "local", input: root, localPath: entryPath });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
await walk(root);
|
|
202
|
+
collection.sources.sort((left, right) => left.localPath.localeCompare(right.localPath));
|
|
203
|
+
collection.skipped.sort((left, right) => left.source.localeCompare(right.source));
|
|
204
|
+
return collection;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function buildSourceReference(source: KnowledgeSource): string {
|
|
208
|
+
return source.kind === "upload"
|
|
209
|
+
? formatKnowledgeUploadSource(source.uploadPath)
|
|
210
|
+
: source.localPath;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function buildSuggestedSlug(source: KnowledgeSource, index: number): string {
|
|
214
|
+
const normalized = normalize(
|
|
215
|
+
source.kind === "upload" ? source.uploadPath : source.localPath,
|
|
216
|
+
).replace(/\\/g, "/");
|
|
217
|
+
|
|
218
|
+
let stripped: string;
|
|
219
|
+
if (source.kind === "upload") {
|
|
220
|
+
stripped = normalized
|
|
221
|
+
.replace(/^\/workspace\/uploads\//, "")
|
|
222
|
+
.replace(/^\/workspace\//, "")
|
|
223
|
+
.replace(/^uploads\//, "")
|
|
224
|
+
.replace(/\.[^.]+$/, "");
|
|
225
|
+
} else if (normalized.startsWith("/workspace/uploads/")) {
|
|
226
|
+
stripped = normalized.replace(/^\/workspace\/uploads\//, "").replace(/\.[^.]+$/, "");
|
|
227
|
+
} else if (normalized.startsWith("/workspace/")) {
|
|
228
|
+
stripped = normalized.replace(/^\/workspace\//, "").replace(/\.[^.]+$/, "");
|
|
229
|
+
} else if (normalized.startsWith("/")) {
|
|
230
|
+
stripped = basename(normalized, extname(normalized));
|
|
231
|
+
} else {
|
|
232
|
+
stripped = normalized.replace(/\.[^.]+$/, "");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const lastSlash = stripped.lastIndexOf("/");
|
|
236
|
+
if (lastSlash >= 0) {
|
|
237
|
+
const dir = stripped.slice(0, lastSlash + 1);
|
|
238
|
+
const file = stripChatUploadPrefix(stripped.slice(lastSlash + 1));
|
|
239
|
+
stripped = file ? `${dir}${file}` : stripped;
|
|
240
|
+
} else {
|
|
241
|
+
stripped = stripChatUploadPrefix(stripped) || stripped;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return slugify(stripped || basename(normalized, extname(normalized)) || `document-${index + 1}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function ensureUniqueSlugs(sources: KnowledgeSource[]): string[] {
|
|
248
|
+
const counts = new Map<string, number>();
|
|
249
|
+
return sources.map((source, index) => {
|
|
250
|
+
const baseSlug = buildSuggestedSlug(source, index);
|
|
251
|
+
const nextCount = (counts.get(baseSlug) ?? 0) + 1;
|
|
252
|
+
counts.set(baseSlug, nextCount);
|
|
253
|
+
return nextCount === 1 ? baseSlug : `${baseSlug}-${nextCount}`;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function deriveKnowledgeRemotePath(
|
|
258
|
+
outputPath: string,
|
|
259
|
+
outputDir: string,
|
|
260
|
+
knowledgePath: string,
|
|
261
|
+
): string {
|
|
262
|
+
const relativeOutputPath = relative(outputDir, outputPath).replace(/\\/g, "/");
|
|
263
|
+
if (!relativeOutputPath || relativeOutputPath.startsWith("..")) {
|
|
264
|
+
throw new Error(`Output path is outside output directory: ${outputPath}`);
|
|
265
|
+
}
|
|
266
|
+
const prefix = normalizeKnowledgeInputPath(knowledgePath);
|
|
267
|
+
const normalizedRelative = normalize(relativeOutputPath).replace(/^\/+/, "");
|
|
268
|
+
return `${prefix}/${normalizedRelative}`.replace(/\\/g, "/");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function createKnowledgeIngestResult(input: {
|
|
272
|
+
source: string;
|
|
273
|
+
localSourcePath: string;
|
|
274
|
+
outputPath: string;
|
|
275
|
+
remotePath: string;
|
|
276
|
+
parser: {
|
|
277
|
+
slug: string;
|
|
278
|
+
stats: Record<string, unknown>;
|
|
279
|
+
warnings: string[];
|
|
280
|
+
source_type: string;
|
|
281
|
+
summary: string;
|
|
282
|
+
};
|
|
283
|
+
}): KnowledgeIngestFileResult {
|
|
284
|
+
return {
|
|
285
|
+
source: input.source,
|
|
286
|
+
localSourcePath: input.localSourcePath,
|
|
287
|
+
outputPath: input.outputPath,
|
|
288
|
+
remotePath: input.remotePath,
|
|
289
|
+
slug: input.parser.slug,
|
|
290
|
+
sourceType: input.parser.source_type,
|
|
291
|
+
summary: input.parser.summary,
|
|
292
|
+
stats: input.parser.stats,
|
|
293
|
+
warnings: input.parser.warnings,
|
|
294
|
+
};
|
|
295
|
+
}
|