veryfront 0.1.129 → 0.1.131
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/auth/login.d.ts.map +1 -1
- package/esm/cli/auth/login.js +11 -0
- package/esm/cli/auth/provider-store.d.ts +20 -0
- package/esm/cli/auth/provider-store.d.ts.map +1 -0
- package/esm/cli/auth/provider-store.js +62 -0
- package/esm/cli/auth/providers/anthropic.d.ts +2 -0
- package/esm/cli/auth/providers/anthropic.d.ts.map +1 -0
- package/esm/cli/auth/providers/anthropic.js +37 -0
- package/esm/cli/auth/providers/openai.d.ts +2 -0
- package/esm/cli/auth/providers/openai.d.ts.map +1 -0
- package/esm/cli/auth/providers/openai.js +35 -0
- package/esm/cli/auth/utils.d.ts +5 -0
- package/esm/cli/auth/utils.d.ts.map +1 -1
- package/esm/cli/auth/utils.js +9 -0
- package/esm/cli/commands/config/command-help.d.ts +3 -0
- package/esm/cli/commands/config/command-help.d.ts.map +1 -0
- package/esm/cli/commands/config/command-help.js +13 -0
- package/esm/cli/commands/config/handler.d.ts +5 -0
- package/esm/cli/commands/config/handler.d.ts.map +1 -0
- package/esm/cli/commands/config/handler.js +70 -0
- package/esm/cli/commands/open/command-help.d.ts +3 -0
- package/esm/cli/commands/open/command-help.d.ts.map +1 -0
- package/esm/cli/commands/open/command-help.js +17 -0
- package/esm/cli/commands/open/command.d.ts +14 -0
- package/esm/cli/commands/open/command.d.ts.map +1 -0
- package/esm/cli/commands/open/command.js +22 -0
- package/esm/cli/commands/open/handler.d.ts +3 -0
- package/esm/cli/commands/open/handler.d.ts.map +1 -0
- package/esm/cli/commands/open/handler.js +29 -0
- package/esm/cli/help/command-definitions.d.ts.map +1 -1
- package/esm/cli/help/command-definitions.js +4 -0
- package/esm/cli/router.d.ts.map +1 -1
- package/esm/cli/router.js +26 -1
- package/esm/deno.js +1 -1
- package/esm/src/channels/control-plane.js +6 -6
- package/esm/src/discovery/handlers/agent-handler.d.ts.map +1 -1
- package/esm/src/discovery/handlers/agent-handler.js +10 -1
- package/esm/src/platform/compat/framework-source-resolver.d.ts +8 -0
- package/esm/src/platform/compat/framework-source-resolver.d.ts.map +1 -1
- package/esm/src/platform/compat/framework-source-resolver.js +77 -1
- package/esm/src/rendering/rsc/client-boot.ts +18 -1
- package/esm/src/server/handlers/preview/markdown-html-generator.d.ts +2 -0
- package/esm/src/server/handlers/preview/markdown-html-generator.d.ts.map +1 -1
- package/esm/src/server/handlers/preview/markdown-html-generator.js +10 -7
- package/esm/src/server/handlers/preview/markdown-preview.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/preview/markdown-preview.handler.js +6 -3
- package/esm/src/server/handlers/request/api/project-discovery.d.ts.map +1 -1
- package/esm/src/server/handlers/request/api/project-discovery.js +16 -5
- package/esm/src/server/handlers/request/api/security-headers.d.ts +1 -0
- package/esm/src/server/handlers/request/api/security-headers.d.ts.map +1 -1
- package/esm/src/server/handlers/request/api/security-headers.js +4 -1
- package/esm/src/server/handlers/request/openapi-docs.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/openapi-docs.handler.js +10 -6
- package/esm/src/server/handlers/request/rsc/index.d.ts.map +1 -1
- package/esm/src/server/handlers/request/rsc/index.js +5 -2
- package/esm/src/server/handlers/request/ssr/ssr-response-builder.d.ts.map +1 -1
- package/esm/src/server/handlers/request/ssr/ssr-response-builder.js +12 -2
- package/esm/src/server/handlers/response/not-found.d.ts.map +1 -1
- package/esm/src/server/handlers/response/not-found.js +14 -15
- package/esm/src/server/services/rsc/endpoints/endpoint-router.d.ts +1 -1
- package/esm/src/server/services/rsc/endpoints/endpoint-router.d.ts.map +1 -1
- package/esm/src/server/services/rsc/endpoints/endpoint-router.js +3 -3
- 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 +1 -1
- package/esm/src/server/services/rsc/endpoints/types.d.ts +1 -0
- package/esm/src/server/services/rsc/endpoints/types.d.ts.map +1 -1
- package/esm/src/server/services/rsc/orchestrators/handler.d.ts +1 -1
- package/esm/src/server/services/rsc/orchestrators/handler.d.ts.map +1 -1
- package/esm/src/server/services/rsc/orchestrators/handler.js +2 -2
- package/esm/src/server/services/rsc/orchestrators/page-handler.d.ts +1 -1
- package/esm/src/server/services/rsc/orchestrators/page-handler.d.ts.map +1 -1
- package/esm/src/server/services/rsc/orchestrators/page-handler.js +7 -5
- package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
- package/esm/src/transforms/esm/import-parser.js +6 -0
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/path-resolver.d.ts +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/path-resolver.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/path-resolver.js +10 -66
- 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/auth/login.ts +12 -0
- package/src/cli/auth/provider-store.ts +82 -0
- package/src/cli/auth/providers/anthropic.ts +46 -0
- package/src/cli/auth/providers/openai.ts +45 -0
- package/src/cli/auth/utils.ts +10 -0
- package/src/cli/commands/config/command-help.ts +15 -0
- package/src/cli/commands/config/handler.ts +90 -0
- package/src/cli/commands/open/command-help.ts +19 -0
- package/src/cli/commands/open/command.ts +28 -0
- package/src/cli/commands/open/handler.ts +38 -0
- package/src/cli/help/command-definitions.ts +4 -0
- package/src/cli/router.ts +28 -1
- package/src/deno.js +1 -1
- package/src/src/channels/control-plane.ts +6 -6
- package/src/src/discovery/handlers/agent-handler.ts +10 -1
- package/src/src/platform/compat/framework-source-resolver.ts +101 -1
- package/src/src/server/handlers/preview/markdown-html-generator.ts +12 -6
- package/src/src/server/handlers/preview/markdown-preview.handler.ts +6 -3
- package/src/src/server/handlers/request/api/project-discovery.ts +18 -5
- package/src/src/server/handlers/request/api/security-headers.ts +10 -1
- package/src/src/server/handlers/request/openapi-docs.handler.ts +10 -6
- package/src/src/server/handlers/request/rsc/index.ts +5 -2
- package/src/src/server/handlers/request/ssr/ssr-response-builder.ts +16 -2
- package/src/src/server/handlers/response/not-found.ts +14 -15
- package/src/src/server/services/rsc/endpoints/endpoint-router.ts +3 -3
- package/src/src/server/services/rsc/endpoints/rsc-bundles.generated.ts +1 -1
- package/src/src/server/services/rsc/endpoints/types.ts +1 -0
- package/src/src/server/services/rsc/orchestrators/handler.ts +2 -2
- package/src/src/server/services/rsc/orchestrators/page-handler.ts +8 -5
- package/src/src/transforms/esm/import-parser.ts +12 -0
- package/src/src/transforms/pipeline/stages/ssr-vf-modules/path-resolver.ts +10 -69
- package/src/src/utils/version-constant.ts +1 -1
package/src/cli/auth/utils.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { AuthMethod } from "./login.js";
|
|
8
8
|
import type { ParsedArgs } from "../shared/types.js";
|
|
9
|
+
import type { ProviderName } from "./provider-store.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Parse login method from CLI arguments
|
|
@@ -19,3 +20,12 @@ export function parseLoginMethod(
|
|
|
19
20
|
if (args.token) return "token";
|
|
20
21
|
return undefined;
|
|
21
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parse --provider flag from CLI arguments
|
|
26
|
+
*/
|
|
27
|
+
export function parseProvider(args: ParsedArgs): ProviderName | undefined {
|
|
28
|
+
const provider = args.provider as string | undefined;
|
|
29
|
+
if (provider === "anthropic" || provider === "openai") return provider;
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CommandHelp } from "../../help/types.js";
|
|
2
|
+
|
|
3
|
+
export const configHelp: CommandHelp = {
|
|
4
|
+
name: "config",
|
|
5
|
+
category: "project",
|
|
6
|
+
description: "Show effective project configuration",
|
|
7
|
+
usage: "veryfront config [options]",
|
|
8
|
+
options: [
|
|
9
|
+
{ flag: "--json", description: "Output as JSON" },
|
|
10
|
+
],
|
|
11
|
+
examples: [
|
|
12
|
+
"veryfront config",
|
|
13
|
+
"veryfront config --json",
|
|
14
|
+
],
|
|
15
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ParsedArgs } from "../../shared/types.js";
|
|
2
|
+
import { cliLogger } from "../../utils/index.js";
|
|
3
|
+
import { getEnv } from "../../../src/platform/index.js";
|
|
4
|
+
import { createSuccessEnvelope, isJsonMode, outputJson } from "../../shared/json-output.js";
|
|
5
|
+
import { bold, dim } from "../../ui/colors.js";
|
|
6
|
+
|
|
7
|
+
const ENV_OVERRIDES: Record<string, string> = {
|
|
8
|
+
projectSlug: "VERYFRONT_PROJECT_SLUG",
|
|
9
|
+
apiBaseUrl: "VERYFRONT_API_BASE_URL",
|
|
10
|
+
apiToken: "VERYFRONT_API_TOKEN",
|
|
11
|
+
nodeEnv: "NODE_ENV",
|
|
12
|
+
veryfrontEnv: "VERYFRONT_ENV",
|
|
13
|
+
debug: "VERYFRONT_DEBUG",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function detectConfigSource(
|
|
17
|
+
projectDir: string,
|
|
18
|
+
): Promise<string | null> {
|
|
19
|
+
const { createFileSystem } = await import("../../../src/platform/index.js");
|
|
20
|
+
const { join } = await import("../../../src/platform/compat/path/index.js");
|
|
21
|
+
const fs = createFileSystem();
|
|
22
|
+
|
|
23
|
+
for (
|
|
24
|
+
const name of [
|
|
25
|
+
"veryfront.config.ts",
|
|
26
|
+
"veryfront.config.js",
|
|
27
|
+
"veryfront.json",
|
|
28
|
+
]
|
|
29
|
+
) {
|
|
30
|
+
if (await fs.exists(join(projectDir, name))) return name;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getEnvOverrides(): string[] {
|
|
36
|
+
const overrides: string[] = [];
|
|
37
|
+
for (const [field, envVar] of Object.entries(ENV_OVERRIDES)) {
|
|
38
|
+
if (getEnv(envVar)) overrides.push(`${field} (${envVar})`);
|
|
39
|
+
}
|
|
40
|
+
return overrides;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function handleConfigCommand(_args: ParsedArgs): Promise<void> {
|
|
44
|
+
const { getEnvironmentConfig } = await import("../../../src/config/index.js");
|
|
45
|
+
const { cwd } = await import("../../../src/platform/index.js");
|
|
46
|
+
const config = getEnvironmentConfig();
|
|
47
|
+
|
|
48
|
+
const projectDir = cwd();
|
|
49
|
+
const configSource = await detectConfigSource(projectDir);
|
|
50
|
+
const envOverrides = getEnvOverrides();
|
|
51
|
+
|
|
52
|
+
const configData = {
|
|
53
|
+
projectSlug: config.projectSlug ?? null,
|
|
54
|
+
nodeEnv: config.nodeEnv,
|
|
55
|
+
veryfrontEnv: config.veryfrontEnv || null,
|
|
56
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
57
|
+
debug: config.debug,
|
|
58
|
+
ci: config.ci,
|
|
59
|
+
hasApiToken: !!config.apiToken,
|
|
60
|
+
configSource,
|
|
61
|
+
envOverrides,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (isJsonMode()) {
|
|
65
|
+
await outputJson(createSuccessEnvelope("config", configData));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
cliLogger.info(`\n ${bold("Project Configuration")}\n`);
|
|
70
|
+
cliLogger.info(
|
|
71
|
+
` ${dim("Project slug:")} ${configData.projectSlug ?? "(not set)"}`,
|
|
72
|
+
);
|
|
73
|
+
cliLogger.info(` ${dim("Environment:")} ${configData.nodeEnv}`);
|
|
74
|
+
cliLogger.info(
|
|
75
|
+
` ${dim("VF Environment:")} ${configData.veryfrontEnv ?? "(not set)"}`,
|
|
76
|
+
);
|
|
77
|
+
cliLogger.info(` ${dim("API endpoint:")} ${configData.apiBaseUrl}`);
|
|
78
|
+
cliLogger.info(` ${dim("Debug:")} ${configData.debug}`);
|
|
79
|
+
cliLogger.info(` ${dim("CI:")} ${configData.ci}`);
|
|
80
|
+
cliLogger.info(
|
|
81
|
+
` ${dim("Authenticated:")} ${configData.hasApiToken ? "yes" : "no"}`,
|
|
82
|
+
);
|
|
83
|
+
cliLogger.info(
|
|
84
|
+
` ${dim("Config file:")} ${configData.configSource ?? "(none)"}`,
|
|
85
|
+
);
|
|
86
|
+
if (envOverrides.length > 0) {
|
|
87
|
+
cliLogger.info(` ${dim("Env overrides:")} ${envOverrides.join(", ")}`);
|
|
88
|
+
}
|
|
89
|
+
cliLogger.info("");
|
|
90
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CommandHelp } from "../../help/types.js";
|
|
2
|
+
|
|
3
|
+
export const openHelp: CommandHelp = {
|
|
4
|
+
name: "open",
|
|
5
|
+
category: "project",
|
|
6
|
+
description: "Open project URLs in the browser",
|
|
7
|
+
usage: "veryfront open [options]",
|
|
8
|
+
options: [
|
|
9
|
+
{ flag: "--env <name>", description: "Open a specific environment URL" },
|
|
10
|
+
{ flag: "--studio", description: "Open Veryfront Studio" },
|
|
11
|
+
{ flag: "--json", description: "Output URL as JSON instead of opening" },
|
|
12
|
+
],
|
|
13
|
+
examples: [
|
|
14
|
+
"veryfront open",
|
|
15
|
+
"veryfront open --env staging",
|
|
16
|
+
"veryfront open --studio",
|
|
17
|
+
"veryfront open --json",
|
|
18
|
+
],
|
|
19
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createArgParser } from "../../shared/args.js";
|
|
3
|
+
|
|
4
|
+
export const OpenArgsSchema = z.object({
|
|
5
|
+
env: z.string().optional(),
|
|
6
|
+
studio: z.boolean().default(false),
|
|
7
|
+
projectSlug: z.string().optional(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type OpenOptions = z.infer<typeof OpenArgsSchema>;
|
|
11
|
+
|
|
12
|
+
export const parseOpenArgs = createArgParser(OpenArgsSchema, {
|
|
13
|
+
env: { keys: ["env"], type: "string" },
|
|
14
|
+
studio: { keys: ["studio"], type: "boolean" },
|
|
15
|
+
projectSlug: { keys: ["project-slug", "project", "p"], type: "string" },
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const DASHBOARD_BASE = "https://veryfront.com";
|
|
19
|
+
|
|
20
|
+
export function buildUrl(projectSlug: string, options: OpenOptions): string {
|
|
21
|
+
if (options.studio) {
|
|
22
|
+
return `${DASHBOARD_BASE}/studio/${projectSlug}`;
|
|
23
|
+
}
|
|
24
|
+
if (options.env) {
|
|
25
|
+
return `${DASHBOARD_BASE}/projects/${projectSlug}/environments/${options.env}`;
|
|
26
|
+
}
|
|
27
|
+
return `${DASHBOARD_BASE}/projects/${projectSlug}`;
|
|
28
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ParsedArgs } from "../../shared/types.js";
|
|
2
|
+
import { parseArgsOrThrow } from "../../shared/args.js";
|
|
3
|
+
import { cliLogger, exitProcess } from "../../utils/index.js";
|
|
4
|
+
import { createSuccessEnvelope, isJsonMode, outputJson } from "../../shared/json-output.js";
|
|
5
|
+
import { buildUrl, parseOpenArgs } from "./command.js";
|
|
6
|
+
|
|
7
|
+
export async function handleOpenCommand(args: ParsedArgs): Promise<void> {
|
|
8
|
+
const opts = parseArgsOrThrow(parseOpenArgs, "open", args);
|
|
9
|
+
|
|
10
|
+
let projectSlug = opts.projectSlug;
|
|
11
|
+
if (!projectSlug) {
|
|
12
|
+
const { cwd } = await import("../../../src/platform/index.js");
|
|
13
|
+
const { getEnvironmentConfig } = await import("../../../src/config/index.js");
|
|
14
|
+
const { readConfigFile } = await import("../../shared/config.js");
|
|
15
|
+
projectSlug = getEnvironmentConfig().projectSlug ??
|
|
16
|
+
(await readConfigFile(cwd()))?.projectSlug ??
|
|
17
|
+
undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!projectSlug) {
|
|
21
|
+
cliLogger.error(
|
|
22
|
+
"No project found. Run from a project directory or use --project-slug",
|
|
23
|
+
);
|
|
24
|
+
exitProcess(1);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const url = buildUrl(projectSlug, opts);
|
|
29
|
+
|
|
30
|
+
if (isJsonMode()) {
|
|
31
|
+
await outputJson(createSuccessEnvelope("open", { url }));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { openBrowser } = await import("../../auth/browser.js");
|
|
36
|
+
await openBrowser(url);
|
|
37
|
+
console.log(` Opening ${url}`);
|
|
38
|
+
}
|
|
@@ -42,6 +42,8 @@ import { schemaHelp } from "../commands/schema/command-help.js";
|
|
|
42
42
|
import { testHelp } from "../commands/test/command-help.js";
|
|
43
43
|
import { lintHelp } from "../commands/lint/command-help.js";
|
|
44
44
|
import { skillsHelp } from "../commands/skills/command-help.js";
|
|
45
|
+
import { configHelp } from "../commands/config/command-help.js";
|
|
46
|
+
import { openHelp } from "../commands/open/command-help.js";
|
|
45
47
|
import { completionsHelp } from "../commands/completions/command-help.js";
|
|
46
48
|
|
|
47
49
|
/**
|
|
@@ -84,5 +86,7 @@ export const COMMANDS: CommandRegistry = {
|
|
|
84
86
|
test: testHelp,
|
|
85
87
|
lint: lintHelp,
|
|
86
88
|
skills: skillsHelp,
|
|
89
|
+
config: configHelp,
|
|
90
|
+
open: openHelp,
|
|
87
91
|
completions: completionsHelp,
|
|
88
92
|
};
|
package/src/cli/router.ts
CHANGED
|
@@ -40,6 +40,8 @@ import { handleSchemaCommand } from "./commands/schema/handler.js";
|
|
|
40
40
|
import { handleTestCommand } from "./commands/test/handler.js";
|
|
41
41
|
import { handleLintCommand } from "./commands/lint/handler.js";
|
|
42
42
|
import { handleSkillsCommand } from "./commands/skills/handler.js";
|
|
43
|
+
import { handleConfigCommand } from "./commands/config/handler.js";
|
|
44
|
+
import { handleOpenCommand } from "./commands/open/handler.js";
|
|
43
45
|
import { handleCompletionsCommand } from "./commands/completions/handler.js";
|
|
44
46
|
import { login, logout, whoami } from "./auth/index.js";
|
|
45
47
|
import { parseLoginMethod } from "./auth/utils.js";
|
|
@@ -84,9 +86,32 @@ const commands: Record<string, (args: ParsedArgs) => Promise<void>> = {
|
|
|
84
86
|
"deploy": handleDeployCommand,
|
|
85
87
|
"up": handleUpCommand,
|
|
86
88
|
"login": async (args) => {
|
|
89
|
+
const { parseProvider } = await import("./auth/utils.js");
|
|
90
|
+
const provider = parseProvider(args);
|
|
91
|
+
if (provider === "anthropic") {
|
|
92
|
+
const { loginAnthropic } = await import("./auth/providers/anthropic.js");
|
|
93
|
+
await loginAnthropic();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (provider === "openai") {
|
|
97
|
+
const { loginOpenAI } = await import("./auth/providers/openai.js");
|
|
98
|
+
await loginOpenAI(args["base-url"] as string | undefined);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
87
101
|
await login(parseLoginMethod(args));
|
|
88
102
|
},
|
|
89
|
-
"logout": async () => {
|
|
103
|
+
"logout": async (args) => {
|
|
104
|
+
const { parseProvider } = await import("./auth/utils.js");
|
|
105
|
+
const provider = parseProvider(args);
|
|
106
|
+
if (provider) {
|
|
107
|
+
const { deleteProviderToken } = await import(
|
|
108
|
+
"./auth/provider-store.js"
|
|
109
|
+
);
|
|
110
|
+
await deleteProviderToken(provider);
|
|
111
|
+
const { logSuccess } = await import("./utils/index.js");
|
|
112
|
+
logSuccess(`${provider} API key removed`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
90
115
|
await logout();
|
|
91
116
|
},
|
|
92
117
|
"whoami": async () => {
|
|
@@ -105,6 +130,8 @@ const commands: Record<string, (args: ParsedArgs) => Promise<void>> = {
|
|
|
105
130
|
"test": handleTestCommand,
|
|
106
131
|
"lint": handleLintCommand,
|
|
107
132
|
"skills": handleSkillsCommand,
|
|
133
|
+
"config": handleConfigCommand,
|
|
134
|
+
"open": handleOpenCommand,
|
|
108
135
|
"completions": handleCompletionsCommand,
|
|
109
136
|
};
|
|
110
137
|
|
package/src/deno.js
CHANGED
|
@@ -233,14 +233,14 @@ function resolveAgentSkills(agent: Agent): RuntimeAgentSkill[] {
|
|
|
233
233
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
function getRuntimeAgentMetadata(agent: Agent): RuntimeAgent {
|
|
236
|
+
function getRuntimeAgentMetadata(id: string, agent: Agent): RuntimeAgent {
|
|
237
237
|
const rawConfig = agent.config as unknown as Record<string, unknown>;
|
|
238
238
|
|
|
239
239
|
return RuntimeAgentSchema.parse({
|
|
240
|
-
id
|
|
240
|
+
id,
|
|
241
241
|
name: typeof rawConfig.name === "string" && rawConfig.name.trim().length > 0
|
|
242
242
|
? rawConfig.name
|
|
243
|
-
:
|
|
243
|
+
: id,
|
|
244
244
|
description: typeof rawConfig.description === "string" ? rawConfig.description : null,
|
|
245
245
|
model: agent.config.model ?? null,
|
|
246
246
|
version: typeof rawConfig.version === "string" ? rawConfig.version : null,
|
|
@@ -255,9 +255,9 @@ export async function listRuntimeAgents(
|
|
|
255
255
|
await deps.ensureProjectDiscovery(ctx);
|
|
256
256
|
|
|
257
257
|
const agents = deps.getAllAgentIds()
|
|
258
|
-
.map((id) => deps.getAgent(id))
|
|
259
|
-
.filter((
|
|
260
|
-
.map(getRuntimeAgentMetadata)
|
|
258
|
+
.map((id) => ({ id, agent: deps.getAgent(id) }))
|
|
259
|
+
.filter((entry): entry is { id: string; agent: Agent } => Boolean(entry.agent))
|
|
260
|
+
.map(({ id, agent }) => getRuntimeAgentMetadata(id, agent))
|
|
261
261
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
262
262
|
|
|
263
263
|
return RuntimeAgentListResponseSchema.parse({ agents });
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { Agent } from "../../agent/index.js";
|
|
6
6
|
import { registerAgent } from "../../agent/index.js";
|
|
7
|
+
import { agentRegistry } from "../../agent/composition/index.js";
|
|
7
8
|
import type { DiscoveryHandler } from "../types.js";
|
|
8
9
|
import { filenameToId, trackAgentPath } from "../discovery-utils.js";
|
|
9
10
|
|
|
@@ -11,8 +12,16 @@ export const agentHandler: DiscoveryHandler<Agent> = {
|
|
|
11
12
|
typeName: "agent",
|
|
12
13
|
validate: (item): item is Agent =>
|
|
13
14
|
item !== null && typeof item === "object" && typeof (item as Agent).generate === "function",
|
|
14
|
-
getId: (agent, file) =>
|
|
15
|
+
getId: (agent, file) => {
|
|
16
|
+
const configuredId = agent.config.id;
|
|
17
|
+
return typeof configuredId === "string" && configuredId.trim().length > 0
|
|
18
|
+
? configuredId
|
|
19
|
+
: filenameToId(file);
|
|
20
|
+
},
|
|
15
21
|
register: (id, agent, file) => {
|
|
22
|
+
if (agent.id !== id) {
|
|
23
|
+
agentRegistry.delete(agent.id);
|
|
24
|
+
}
|
|
16
25
|
registerAgent(id, agent);
|
|
17
26
|
trackAgentPath(id, file);
|
|
18
27
|
return agent;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { join } from "./path/index.js";
|
|
2
2
|
import type { FileInfo } from "../adapters/base.js";
|
|
3
3
|
import { createFileSystem } from "./fs.js";
|
|
4
|
-
import { getFrameworkRootFromMeta } from "./vfs-paths.js";
|
|
4
|
+
import { getFrameworkRoot, getFrameworkRootFromMeta } from "./vfs-paths.js";
|
|
5
5
|
|
|
6
6
|
export const FRAMEWORK_ROOT = getFrameworkRootFromMeta(import.meta.url);
|
|
7
|
+
export const FRAMEWORK_SRC_DIR = join(FRAMEWORK_ROOT, "src");
|
|
7
8
|
export const FRAMEWORK_EMBEDDED_SRC_DIR = join(FRAMEWORK_ROOT, "dist", "framework-src");
|
|
8
9
|
|
|
9
10
|
export const DEFAULT_FRAMEWORK_SOURCE_EXTENSIONS = [
|
|
@@ -37,6 +38,12 @@ export interface ResolveFrameworkSourcePathOptions {
|
|
|
37
38
|
includeIndexFallback?: boolean;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
export interface ResolveRelativeFrameworkSourceImportOptions {
|
|
42
|
+
fileSystem?: FrameworkSourceFileSystem;
|
|
43
|
+
exists?: (path: string) => Promise<boolean>;
|
|
44
|
+
extensions?: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
export function getFrameworkSourceLookupDirs(extraLookupDirs: string[] = []): string[] {
|
|
41
48
|
const seen = new Set<string>();
|
|
42
49
|
const ordered = [
|
|
@@ -52,6 +59,48 @@ export function getFrameworkSourceLookupDirs(extraLookupDirs: string[] = []): st
|
|
|
52
59
|
});
|
|
53
60
|
}
|
|
54
61
|
|
|
62
|
+
export function isFrameworkSourcePath(path: string): boolean {
|
|
63
|
+
return path.startsWith(`${FRAMEWORK_SRC_DIR}/`) ||
|
|
64
|
+
path.startsWith(`${FRAMEWORK_EMBEDDED_SRC_DIR}/`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function expandFrameworkCandidatePaths(candidatePath: string): string[] {
|
|
68
|
+
const candidates = [candidatePath];
|
|
69
|
+
const candidateRoot = getFrameworkRoot(candidatePath);
|
|
70
|
+
const candidateSrcDir = candidateRoot ? join(candidateRoot, "src") : FRAMEWORK_SRC_DIR;
|
|
71
|
+
const candidateEmbeddedDir = candidateRoot
|
|
72
|
+
? join(candidateRoot, "dist", "framework-src")
|
|
73
|
+
: FRAMEWORK_EMBEDDED_SRC_DIR;
|
|
74
|
+
|
|
75
|
+
if (candidatePath.startsWith(`${candidateSrcDir}/`)) {
|
|
76
|
+
const relativePath = candidatePath.slice(candidateSrcDir.length + 1);
|
|
77
|
+
candidates.push(join(candidateEmbeddedDir, relativePath));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return [...new Set(candidates)];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function findExistingFrameworkCandidate(
|
|
84
|
+
candidatePath: string,
|
|
85
|
+
options: ResolveRelativeFrameworkSourceImportOptions = {},
|
|
86
|
+
): Promise<string | null> {
|
|
87
|
+
const fs = options.fileSystem ?? createFileSystem();
|
|
88
|
+
const exists = options.exists ?? (async (path: string) => {
|
|
89
|
+
try {
|
|
90
|
+
const stat = await fs.stat(path);
|
|
91
|
+
return stat.isFile;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
for (const candidate of expandFrameworkCandidatePaths(candidatePath)) {
|
|
98
|
+
if (await exists(candidate)) return candidate;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
55
104
|
export async function resolveFrameworkSourcePath(
|
|
56
105
|
relativePathWithoutExt: string,
|
|
57
106
|
options: ResolveFrameworkSourcePathOptions = {},
|
|
@@ -87,3 +136,54 @@ export async function resolveFrameworkSourcePath(
|
|
|
87
136
|
|
|
88
137
|
return null;
|
|
89
138
|
}
|
|
139
|
+
|
|
140
|
+
export async function resolveRelativeFrameworkSourceImport(
|
|
141
|
+
specifier: string,
|
|
142
|
+
fromSourcePath: string,
|
|
143
|
+
options: ResolveRelativeFrameworkSourceImportOptions = {},
|
|
144
|
+
): Promise<string | null> {
|
|
145
|
+
const extensions = options.extensions ?? DEFAULT_FRAMEWORK_SOURCE_EXTENSIONS;
|
|
146
|
+
const fromDir = fromSourcePath.substring(0, fromSourcePath.lastIndexOf("/"));
|
|
147
|
+
const parts = fromDir.split("/").filter(Boolean);
|
|
148
|
+
const importParts = specifier.split("/").filter(Boolean);
|
|
149
|
+
|
|
150
|
+
for (const part of importParts) {
|
|
151
|
+
if (part === "..") {
|
|
152
|
+
parts.pop();
|
|
153
|
+
} else if (part !== ".") {
|
|
154
|
+
parts.push(part);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const basePath = "/" + parts.join("/");
|
|
159
|
+
|
|
160
|
+
if (/\.(tsx?|jsx?|mjs)$/.test(specifier)) {
|
|
161
|
+
const explicitCandidates = [basePath, `${basePath}.src`];
|
|
162
|
+
|
|
163
|
+
if (basePath.endsWith(".js") || basePath.endsWith(".mjs")) {
|
|
164
|
+
const stem = basePath.replace(/\.(?:m?js)$/, "");
|
|
165
|
+
for (const ext of [".ts", ".tsx", ".jsx", ".js", ".mjs"]) {
|
|
166
|
+
explicitCandidates.push(`${stem}${ext}.src`, `${stem}${ext}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
for (const candidate of explicitCandidates) {
|
|
171
|
+
const resolved = await findExistingFrameworkCandidate(candidate, options);
|
|
172
|
+
if (resolved) return resolved;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for (const ext of extensions) {
|
|
179
|
+
const candidate = await findExistingFrameworkCandidate(basePath + ext, options);
|
|
180
|
+
if (candidate) return candidate;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for (const ext of extensions) {
|
|
184
|
+
const candidate = await findExistingFrameworkCandidate(join(basePath, "index" + ext), options);
|
|
185
|
+
if (candidate) return candidate;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
@@ -11,6 +11,7 @@ import * as dntShim from "../../../../_dnt.shims.js";
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
import { escapeHtml } from "../../../utils/html-escape.js";
|
|
14
|
+
import { buildNonceAttribute } from "../../../html/html-escape.js";
|
|
14
15
|
|
|
15
16
|
/** Options for generating markdown preview HTML. */
|
|
16
17
|
interface MarkdownHtmlOptions {
|
|
@@ -28,6 +29,8 @@ interface MarkdownHtmlOptions {
|
|
|
28
29
|
projectId: string;
|
|
29
30
|
/** File path of the markdown file. */
|
|
30
31
|
filePath: string;
|
|
32
|
+
/** CSP nonce for inline scripts. */
|
|
33
|
+
nonce?: string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
/**
|
|
@@ -60,9 +63,11 @@ function buildStudioScript(
|
|
|
60
63
|
url: URL,
|
|
61
64
|
projectId: string,
|
|
62
65
|
filePath: string,
|
|
66
|
+
nonce?: string,
|
|
63
67
|
): string {
|
|
64
68
|
const studioEmbed = url.searchParams.get("studio_embed") === "true";
|
|
65
69
|
if (!studioEmbed) return "";
|
|
70
|
+
const nonceAttr = buildNonceAttribute(nonce);
|
|
66
71
|
|
|
67
72
|
const rawQueryProjectId = url.searchParams.get("vf_project_id")?.trim() || "";
|
|
68
73
|
// Validate query param before using it in bridge config.
|
|
@@ -79,8 +84,8 @@ function buildStudioScript(
|
|
|
79
84
|
|
|
80
85
|
// Escape </script> sequences to prevent XSS breakout from inline JSON
|
|
81
86
|
const safeJson = JSON.stringify(bridgeConfig).replace(/</g, "\\u003c");
|
|
82
|
-
return `<script>window.__VF_BRIDGE_CONFIG__=${safeJson};</script>
|
|
83
|
-
<script type="module" src="/_veryfront/studio-bridge.js"></script>`;
|
|
87
|
+
return `<script${nonceAttr}>window.__VF_BRIDGE_CONFIG__=${safeJson};</script>
|
|
88
|
+
<script type="module" src="/_veryfront/studio-bridge.js"${nonceAttr}></script>`;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
/**
|
|
@@ -91,11 +96,12 @@ function buildStudioScript(
|
|
|
91
96
|
* studio bridge integration.
|
|
92
97
|
*/
|
|
93
98
|
export function generateMarkdownHtml(options: MarkdownHtmlOptions): string {
|
|
94
|
-
const { rawHtml, title, description, request, url, projectId, filePath } = options;
|
|
99
|
+
const { rawHtml, title, description, request, url, projectId, filePath, nonce } = options;
|
|
95
100
|
|
|
96
101
|
const theme = detectTheme(request, url);
|
|
97
|
-
const studioScript = buildStudioScript(url, projectId, filePath);
|
|
102
|
+
const studioScript = buildStudioScript(url, projectId, filePath, nonce);
|
|
98
103
|
const themeAttrs = theme ? ` data-theme="${theme}" style="color-scheme: ${theme};"` : "";
|
|
104
|
+
const nonceAttr = buildNonceAttribute(nonce);
|
|
99
105
|
|
|
100
106
|
return `<!DOCTYPE html>
|
|
101
107
|
<html lang="en"${themeAttrs}>
|
|
@@ -117,7 +123,7 @@ export function generateMarkdownHtml(options: MarkdownHtmlOptions): string {
|
|
|
117
123
|
|
|
118
124
|
${studioScript}
|
|
119
125
|
|
|
120
|
-
<script type="module">
|
|
126
|
+
<script type="module"${nonceAttr}>
|
|
121
127
|
import mermaid from 'https://esm.sh/mermaid@11.4.1?pin=v135';
|
|
122
128
|
|
|
123
129
|
function getMermaidTheme() {
|
|
@@ -177,7 +183,7 @@ export function generateMarkdownHtml(options: MarkdownHtmlOptions): string {
|
|
|
177
183
|
</script>
|
|
178
184
|
|
|
179
185
|
<!-- Preview HMR -->
|
|
180
|
-
<script src="/_veryfront/preview-hmr.js"></script>
|
|
186
|
+
<script src="/_veryfront/preview-hmr.js"${nonceAttr}></script>
|
|
181
187
|
</body>
|
|
182
188
|
</html>`;
|
|
183
189
|
}
|
|
@@ -154,6 +154,7 @@ export class MarkdownPreviewHandler extends BaseHandler {
|
|
|
154
154
|
"server",
|
|
155
155
|
);
|
|
156
156
|
|
|
157
|
+
const responseBuilder = this.createResponseBuilder(ctx);
|
|
157
158
|
const html = generateMarkdownHtml({
|
|
158
159
|
rawHtml: bundle.rawHtml || "",
|
|
159
160
|
title: frontmatter.title != null ? String(frontmatter.title) : filePath,
|
|
@@ -162,18 +163,20 @@ export class MarkdownPreviewHandler extends BaseHandler {
|
|
|
162
163
|
url,
|
|
163
164
|
projectId: ctx.projectSlug || ctx.projectId || "markdown-preview",
|
|
164
165
|
filePath,
|
|
166
|
+
nonce: responseBuilder.nonce,
|
|
165
167
|
});
|
|
166
168
|
|
|
167
|
-
|
|
169
|
+
responseBuilder
|
|
168
170
|
.withCache("no-cache")
|
|
169
|
-
.
|
|
171
|
+
.withSecurity(ctx.securityConfig ?? undefined, req);
|
|
172
|
+
const response = responseBuilder.withContentType("text/html; charset=utf-8", html, HTTP_OK);
|
|
170
173
|
|
|
171
174
|
logger.debug("Serving markdown preview", {
|
|
172
175
|
filePath,
|
|
173
176
|
htmlLength: html.length,
|
|
174
177
|
});
|
|
175
178
|
|
|
176
|
-
return this.respond(
|
|
179
|
+
return this.respond(response);
|
|
177
180
|
} catch (error) {
|
|
178
181
|
logger.error("Error rendering markdown", {
|
|
179
182
|
filePath,
|
|
@@ -16,6 +16,23 @@ const logger = serverLogger.component("api-wrapper");
|
|
|
16
16
|
*/
|
|
17
17
|
const discoveredProjects = new Map<string, Promise<void>>();
|
|
18
18
|
|
|
19
|
+
function buildDiscoveryConfig(ctx: HandlerContext) {
|
|
20
|
+
const ai = ctx.config?.ai;
|
|
21
|
+
const skillDiscoveryEnabled = ai?.skills?.discovery?.enabled ?? true;
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
baseDir: ctx.projectDir,
|
|
25
|
+
toolDirs: ai?.tools?.discovery?.paths ?? ["tools"],
|
|
26
|
+
agentDirs: ai?.agents?.discovery?.paths ?? ["agents"],
|
|
27
|
+
skillDirs: skillDiscoveryEnabled ? (ai?.skills?.discovery?.paths ?? ["skills"]) : [],
|
|
28
|
+
resourceDirs: ["resources"],
|
|
29
|
+
promptDirs: ["prompts"],
|
|
30
|
+
workflowDirs: ["workflows"],
|
|
31
|
+
fsAdapter: ctx.adapter.fs,
|
|
32
|
+
verbose: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
19
36
|
/** Build a discovery cache key that incorporates the release/version. */
|
|
20
37
|
function discoveryKey(ctx: HandlerContext): string {
|
|
21
38
|
const cacheContext = tryGetCacheKeyContext();
|
|
@@ -74,11 +91,7 @@ export async function ensureProjectDiscovery(ctx: HandlerContext): Promise<void>
|
|
|
74
91
|
agentRegistry.clear();
|
|
75
92
|
toolRegistry.clear();
|
|
76
93
|
|
|
77
|
-
const result = await discoverAll(
|
|
78
|
-
baseDir: ctx.projectDir,
|
|
79
|
-
fsAdapter: ctx.adapter.fs,
|
|
80
|
-
verbose: false,
|
|
81
|
-
});
|
|
94
|
+
const result = await discoverAll(buildDiscoveryConfig(ctx));
|
|
82
95
|
|
|
83
96
|
const logData = {
|
|
84
97
|
projectSlug: ctx.projectSlug,
|
|
@@ -31,10 +31,19 @@ export function getSecurityHeader(
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function applySecurityHeaders(headers: dntShim.Headers, ctx: HandlerContext, req?: dntShim.Request): void {
|
|
34
|
+
applySecurityHeadersWithNonce(headers, ctx, generateNonce(), req);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function applySecurityHeadersWithNonce(
|
|
38
|
+
headers: dntShim.Headers,
|
|
39
|
+
ctx: HandlerContext,
|
|
40
|
+
nonce: string,
|
|
41
|
+
req?: dntShim.Request,
|
|
42
|
+
): void {
|
|
34
43
|
coreApplySecurityHeaders(
|
|
35
44
|
headers,
|
|
36
45
|
isDev(ctx),
|
|
37
|
-
|
|
46
|
+
nonce,
|
|
38
47
|
ctx.cspUserHeader ?? null,
|
|
39
48
|
ctx.securityConfig,
|
|
40
49
|
ctx.adapter,
|