@sidecar-ai/compiler 0.1.0-alpha.10 → 0.1.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -146
- package/dist/index.js +320 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,149 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JsonSchema } from '@sidecar-ai/core';
|
|
2
2
|
import { SourceFile } from 'ts-morph';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type DiagnosticSeverity = "warning" | "error";
|
|
6
|
-
/** Stable diagnostic emitted for a concrete project source location. */
|
|
7
|
-
type SidecarDiagnostic = {
|
|
8
|
-
severity: DiagnosticSeverity;
|
|
9
|
-
code: string;
|
|
10
|
-
message: string;
|
|
11
|
-
filePath: string;
|
|
12
|
-
line: number;
|
|
13
|
-
column: number;
|
|
14
|
-
hint?: string;
|
|
15
|
-
};
|
|
16
|
-
/** Collects project-level warnings that do not block compilation by default. */
|
|
17
|
-
declare function collectProjectDiagnostics(rootDir: string, input: SidecarToolManifestEntry[] | {
|
|
18
|
-
tools: SidecarToolManifestEntry[];
|
|
19
|
-
resources?: SidecarResourceManifestEntry[];
|
|
20
|
-
prompts?: SidecarPromptManifestEntry[];
|
|
21
|
-
config?: SidecarCompilerConfig;
|
|
22
|
-
}): Promise<SidecarDiagnostic[]>;
|
|
23
|
-
/** Formats diagnostics in a TypeScript-style shape understood by many editors. */
|
|
24
|
-
declare function formatDiagnostic(diagnostic: SidecarDiagnostic): string;
|
|
25
|
-
|
|
26
|
-
/** Manifest and option types shared across the Sidecar compiler modules. */
|
|
27
|
-
|
|
28
|
-
/** Build target profile selected by reserved platform file suffixes. */
|
|
29
|
-
type SidecarTarget = BuildTarget;
|
|
30
|
-
/** Host runtime artifact emitted for a build output. */
|
|
31
|
-
type SidecarHost = BuildHost;
|
|
32
|
-
/** Platform suffix chosen for a reserved tool or widget file. */
|
|
33
|
-
type SidecarSourceVariant = "shared" | "openai" | "anthropic";
|
|
34
|
-
/** Tool entry emitted into `manifest.sidecar.json`. */
|
|
35
|
-
type SidecarToolManifestEntry = {
|
|
36
|
-
sourceFile: string;
|
|
37
|
-
variant: SidecarSourceVariant;
|
|
38
|
-
target: SidecarTarget;
|
|
39
|
-
directory: string;
|
|
40
|
-
id: string;
|
|
41
|
-
name: string;
|
|
42
|
-
description: string;
|
|
43
|
-
inputSchema: JsonSchema;
|
|
44
|
-
outputSchema?: JsonSchema;
|
|
45
|
-
annotations?: ToolAnnotations;
|
|
46
|
-
visibility?: ToolVisibility;
|
|
47
|
-
widget?: SidecarWidgetManifestEntry;
|
|
48
|
-
descriptor: McpToolDescriptor;
|
|
49
|
-
};
|
|
50
|
-
/** Widget resource linked to a tool entry. */
|
|
51
|
-
type SidecarWidgetManifestEntry = {
|
|
52
|
-
sourceFile: string;
|
|
53
|
-
variant: SidecarSourceVariant;
|
|
54
|
-
resourceUri: string;
|
|
55
|
-
resourceMeta?: Record<string, unknown>;
|
|
56
|
-
outputFile?: string;
|
|
57
|
-
options?: ToolWidgetOptions;
|
|
58
|
-
};
|
|
59
|
-
/** Static resource entry emitted into `manifest.sidecar.json`. */
|
|
60
|
-
type SidecarResourceManifestEntry = {
|
|
61
|
-
sourceFile: string;
|
|
62
|
-
directory: string;
|
|
63
|
-
uri: string;
|
|
64
|
-
name: string;
|
|
65
|
-
title?: string;
|
|
66
|
-
description?: string;
|
|
67
|
-
mimeType?: string;
|
|
68
|
-
size?: number;
|
|
69
|
-
annotations?: ResourceAnnotations;
|
|
70
|
-
subscribe?: boolean;
|
|
71
|
-
descriptor: McpResourceDescriptor;
|
|
72
|
-
};
|
|
73
|
-
/** Static resource template entry emitted into `manifest.sidecar.json`. */
|
|
74
|
-
type SidecarResourceTemplateManifestEntry = {
|
|
75
|
-
uriTemplate: string;
|
|
76
|
-
name: string;
|
|
77
|
-
title?: string;
|
|
78
|
-
description?: string;
|
|
79
|
-
mimeType?: string;
|
|
80
|
-
annotations?: ResourceAnnotations;
|
|
81
|
-
descriptor: McpResourceTemplateDescriptor;
|
|
82
|
-
};
|
|
83
|
-
/** Static prompt entry emitted into `manifest.sidecar.json`. */
|
|
84
|
-
type SidecarPromptManifestEntry = {
|
|
85
|
-
sourceFile: string;
|
|
86
|
-
directory: string;
|
|
87
|
-
name: string;
|
|
88
|
-
title: string;
|
|
89
|
-
description?: string;
|
|
90
|
-
args?: PromptArgsDefinition;
|
|
91
|
-
descriptor: McpPromptDescriptor;
|
|
92
|
-
};
|
|
93
|
-
/** Serializable project config subset recorded in build manifests. */
|
|
94
|
-
type SidecarCompilerConfig = {
|
|
95
|
-
build: {
|
|
96
|
-
target?: SidecarTarget;
|
|
97
|
-
host?: SidecarHost;
|
|
98
|
-
outDir?: string;
|
|
99
|
-
plugins?: boolean;
|
|
100
|
-
widgets?: WidgetBuildConfig;
|
|
101
|
-
};
|
|
102
|
-
resources: {
|
|
103
|
-
subscribe: boolean;
|
|
104
|
-
listChanged: boolean;
|
|
105
|
-
};
|
|
106
|
-
prompts: {
|
|
107
|
-
listChanged: boolean;
|
|
108
|
-
};
|
|
109
|
-
tools: {
|
|
110
|
-
listChanged: boolean;
|
|
111
|
-
};
|
|
112
|
-
pagination: {
|
|
113
|
-
pageSize: number;
|
|
114
|
-
hasOverride: boolean;
|
|
115
|
-
};
|
|
116
|
-
};
|
|
117
|
-
/** Build manifest produced for a Sidecar MCP output. */
|
|
118
|
-
type SidecarManifest = {
|
|
119
|
-
version: 1;
|
|
120
|
-
target: SidecarTarget;
|
|
121
|
-
host: SidecarHost;
|
|
122
|
-
rootDir: string;
|
|
123
|
-
generatedAt: string;
|
|
124
|
-
config: SidecarCompilerConfig;
|
|
125
|
-
tools: SidecarToolManifestEntry[];
|
|
126
|
-
resources: SidecarResourceManifestEntry[];
|
|
127
|
-
resourceTemplates: SidecarResourceTemplateManifestEntry[];
|
|
128
|
-
prompts: SidecarPromptManifestEntry[];
|
|
129
|
-
diagnostics?: SidecarDiagnostic[];
|
|
130
|
-
};
|
|
131
|
-
/** Options accepted by `buildProject()`. */
|
|
132
|
-
type BuildProjectOptions = {
|
|
133
|
-
rootDir: string;
|
|
134
|
-
host?: SidecarHost;
|
|
135
|
-
outDir?: string;
|
|
136
|
-
plugins?: boolean;
|
|
137
|
-
strict?: boolean;
|
|
138
|
-
target?: SidecarTarget;
|
|
139
|
-
};
|
|
140
|
-
/** Project identity used by generated plugin packages. */
|
|
141
|
-
type ProjectIdentity = {
|
|
142
|
-
name: string;
|
|
143
|
-
slug: string;
|
|
144
|
-
version: string;
|
|
145
|
-
description: string;
|
|
146
|
-
};
|
|
3
|
+
import { h as SidecarTarget, i as SidecarToolManifestEntry, g as SidecarSourceVariant, B as BuildProjectOptions, c as SidecarManifest, S as SidecarCompilerConfig, d as SidecarPromptManifestEntry, e as SidecarResourceManifestEntry } from '../types-CWHwikML.js';
|
|
4
|
+
export { P as ProjectIdentity, a as SidecarDiagnostic, b as SidecarHost, f as SidecarResourceTemplateManifestEntry, j as SidecarWidgetManifestEntry, k as collectProjectDiagnostics, l as formatDiagnostic } from '../types-CWHwikML.js';
|
|
147
5
|
|
|
148
6
|
/** Static analysis for reserved server tool files. */
|
|
149
7
|
|
|
@@ -191,4 +49,4 @@ declare const SERVER_ENTRYPOINT = "server/index.js";
|
|
|
191
49
|
/** Relative location of the Vercel Build Output API function entrypoint. */
|
|
192
50
|
declare const VERCEL_ENTRYPOINT = "functions/api/sidecar.func/index.js";
|
|
193
51
|
|
|
194
|
-
export {
|
|
52
|
+
export { BuildProjectOptions, CompilerError, SERVER_ENTRYPOINT, SidecarCompilerConfig, SidecarManifest, SidecarPromptManifestEntry, SidecarResourceManifestEntry, SidecarSourceVariant, SidecarTarget, SidecarToolManifestEntry, VERCEL_ENTRYPOINT, analyzeProjectConfig, analyzeProjectPrompts, analyzeProjectResources, analyzeProjectTools, analyzePromptFile, analyzeResourceFile, analyzeToolFile, buildProject };
|
package/dist/index.js
CHANGED
|
@@ -815,6 +815,83 @@ createRoot(document.getElementById("root")!).render(
|
|
|
815
815
|
entry.descriptor._meta = mergeWidgetMeta(entry.descriptor._meta, widgetMeta(resourceUri, entry.widget.options, entry.target));
|
|
816
816
|
}
|
|
817
817
|
}
|
|
818
|
+
async function buildCodeModeWidget(rootDir, outDir, tools, target, config = void 0) {
|
|
819
|
+
const renderable = tools.filter(
|
|
820
|
+
(entry) => Boolean(entry.widget)
|
|
821
|
+
);
|
|
822
|
+
if (!renderable.length) {
|
|
823
|
+
return void 0;
|
|
824
|
+
}
|
|
825
|
+
const cacheDir = path4.join(rootDir, ".sidecar", "cache", "widgets");
|
|
826
|
+
await mkdir(cacheDir, { recursive: true });
|
|
827
|
+
const appStyle = await prepareAppStyle(rootDir, cacheDir);
|
|
828
|
+
const configure = await loadWidgetBundlerHook(rootDir, cacheDir, config?.configure);
|
|
829
|
+
const safeId = "code-mode";
|
|
830
|
+
const entryFile = path4.join(cacheDir, `${safeId}.entry.tsx`);
|
|
831
|
+
await writeFile(entryFile, renderCodeModeWidgetEntry(rootDir, entryFile, renderable, appStyle));
|
|
832
|
+
const baseOptions = enforceWidgetBuildInvariants({
|
|
833
|
+
absWorkingDir: rootDir,
|
|
834
|
+
alias: sidecarWidgetAliases(rootDir),
|
|
835
|
+
bundle: true,
|
|
836
|
+
define: {
|
|
837
|
+
"process.env.NODE_ENV": JSON.stringify("production")
|
|
838
|
+
},
|
|
839
|
+
entryPoints: [entryFile],
|
|
840
|
+
format: "iife",
|
|
841
|
+
jsx: "automatic",
|
|
842
|
+
minify: true,
|
|
843
|
+
nodePaths: [
|
|
844
|
+
path4.join(rootDir, "node_modules"),
|
|
845
|
+
path4.join(process.cwd(), "node_modules")
|
|
846
|
+
],
|
|
847
|
+
outfile: "widget.js",
|
|
848
|
+
platform: "browser",
|
|
849
|
+
sourcemap: false,
|
|
850
|
+
write: false
|
|
851
|
+
});
|
|
852
|
+
const staticOptions = widgetBuildOptionsFromConfig(rootDir, config);
|
|
853
|
+
const configuredOptions = configure ? await configureWidgetBuild(configure, {
|
|
854
|
+
rootDir,
|
|
855
|
+
outDir,
|
|
856
|
+
entryFile,
|
|
857
|
+
widget: {
|
|
858
|
+
toolId: "execute_code",
|
|
859
|
+
toolName: "Execute Code",
|
|
860
|
+
target,
|
|
861
|
+
sourceFile: path4.relative(rootDir, entryFile)
|
|
862
|
+
},
|
|
863
|
+
esbuildOptions: mergeWidgetBuildOptions(baseOptions, staticOptions)
|
|
864
|
+
}) : void 0;
|
|
865
|
+
const bundled = await esbuild(enforceWidgetBuildInvariants(
|
|
866
|
+
mergeWidgetBuildOptions(baseOptions, staticOptions, configuredOptions),
|
|
867
|
+
{ rootDir, entryFile }
|
|
868
|
+
));
|
|
869
|
+
const outputFiles = bundled.outputFiles ?? [];
|
|
870
|
+
const javascript = outputFiles.find((file) => file.path.endsWith(".js"))?.text ?? "";
|
|
871
|
+
const css = outputFiles.filter((file) => file.path.endsWith(".css")).map((file) => file.text).join("\n");
|
|
872
|
+
const html = renderWidgetHtml("Execute Code", javascript, css);
|
|
873
|
+
const hash = createHash("sha256").update(html).digest("hex").slice(0, 12);
|
|
874
|
+
const outputDir = path4.join(outDir, "public", "widgets", safeId);
|
|
875
|
+
const outputFile = path4.join(outputDir, `widget.${hash}.html`);
|
|
876
|
+
const resourceUri = `ui://${safeId}/widget.${hash}.html`;
|
|
877
|
+
const options = {
|
|
878
|
+
description: "Renders the selected result from a Sidecar code-mode execution.",
|
|
879
|
+
csp: {
|
|
880
|
+
connectDomains: [],
|
|
881
|
+
resourceDomains: []
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
await mkdir(outputDir, { recursive: true });
|
|
885
|
+
await writeFile(outputFile, html);
|
|
886
|
+
return {
|
|
887
|
+
sourceFile: path4.relative(rootDir, entryFile),
|
|
888
|
+
variant: "shared",
|
|
889
|
+
resourceUri,
|
|
890
|
+
resourceMeta: widgetResourceMeta(options, target),
|
|
891
|
+
outputFile: path4.relative(outDir, outputFile),
|
|
892
|
+
options
|
|
893
|
+
};
|
|
894
|
+
}
|
|
818
895
|
function widgetBuildOptionsFromConfig(rootDir, config) {
|
|
819
896
|
const esbuildConfig = config?.esbuild;
|
|
820
897
|
if (!esbuildConfig) {
|
|
@@ -911,6 +988,105 @@ function enforceWidgetBuildInvariants(options, required) {
|
|
|
911
988
|
write: false
|
|
912
989
|
};
|
|
913
990
|
}
|
|
991
|
+
function renderCodeModeWidgetEntry(rootDir, entryFile, entries, appStyle) {
|
|
992
|
+
const entryDir = path4.dirname(entryFile);
|
|
993
|
+
const imports = entries.map((entry, index) => {
|
|
994
|
+
const sourceFile = path4.join(rootDir, entry.widget.sourceFile);
|
|
995
|
+
return `import Widget${index} from ${JSON.stringify(toImportSpecifier(entryDir, sourceFile))};`;
|
|
996
|
+
}).join("\n");
|
|
997
|
+
const registry = entries.map((entry, index) => `${JSON.stringify(entry.id)}: Widget${index}`).join(",\n ");
|
|
998
|
+
return `import React, { useMemo } from "react";
|
|
999
|
+
import { createRoot } from "react-dom/client";
|
|
1000
|
+
import {
|
|
1001
|
+
SidecarWidgetProvider,
|
|
1002
|
+
SidecarWidgetRoot,
|
|
1003
|
+
browserBridge,
|
|
1004
|
+
useToolResult
|
|
1005
|
+
} from "@sidecar-ai/react";
|
|
1006
|
+
import { Heading, Stack, Surface, Text } from "@sidecar-ai/native/components";
|
|
1007
|
+
import "@sidecar-ai/native/styles.css";
|
|
1008
|
+
${appStyle ? `import ${JSON.stringify(toImportSpecifier(entryDir, appStyle))};` : ""}
|
|
1009
|
+
${imports}
|
|
1010
|
+
|
|
1011
|
+
const registry = {
|
|
1012
|
+
${registry}
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
function nestedResult(result, payload) {
|
|
1016
|
+
return {
|
|
1017
|
+
...result,
|
|
1018
|
+
structuredContent: payload?.result ?? payload?.value,
|
|
1019
|
+
structured: payload?.result ?? payload?.value,
|
|
1020
|
+
content: payload?.content ?? result.content ?? [],
|
|
1021
|
+
meta: payload?.meta ?? result.meta ?? {},
|
|
1022
|
+
_meta: payload?.meta ?? result._meta ?? {},
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function createNestedBridge(result, payload) {
|
|
1027
|
+
return {
|
|
1028
|
+
...browserBridge,
|
|
1029
|
+
getToolResult() {
|
|
1030
|
+
return nestedResult(result, payload);
|
|
1031
|
+
},
|
|
1032
|
+
subscribeToolResult(listener) {
|
|
1033
|
+
return browserBridge.subscribeToolResult((next) => {
|
|
1034
|
+
const nextPayload = next?.structuredContent?.codeMode;
|
|
1035
|
+
listener(nestedResult(next, nextPayload));
|
|
1036
|
+
});
|
|
1037
|
+
},
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function GenericCodeModeResult({ payload }) {
|
|
1042
|
+
return React.createElement("main", { className: "sidecar-code-mode" },
|
|
1043
|
+
React.createElement(Stack, { gap: "md" },
|
|
1044
|
+
React.createElement(Heading, { level: 1 }, "Code result"),
|
|
1045
|
+
React.createElement(Surface, { variant: "card" },
|
|
1046
|
+
React.createElement("pre", {
|
|
1047
|
+
style: {
|
|
1048
|
+
margin: 0,
|
|
1049
|
+
overflowWrap: "anywhere",
|
|
1050
|
+
whiteSpace: "pre-wrap"
|
|
1051
|
+
}
|
|
1052
|
+
}, JSON.stringify(payload?.value ?? payload ?? {}, null, 2))
|
|
1053
|
+
)
|
|
1054
|
+
)
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function CodeModeWidget() {
|
|
1059
|
+
const result = useToolResult();
|
|
1060
|
+
const payload = result.structuredContent?.codeMode;
|
|
1061
|
+
const renderer = payload?.renderer;
|
|
1062
|
+
const Component = renderer ? registry[renderer] : undefined;
|
|
1063
|
+
const bridge = useMemo(() => createNestedBridge(result, payload), [result, payload]);
|
|
1064
|
+
|
|
1065
|
+
if (Component) {
|
|
1066
|
+
return React.createElement(
|
|
1067
|
+
SidecarWidgetProvider,
|
|
1068
|
+
{ bridge },
|
|
1069
|
+
React.createElement(Component)
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
if (payload?.ok === false) {
|
|
1074
|
+
return React.createElement("main", { className: "sidecar-code-mode" },
|
|
1075
|
+
React.createElement(Stack, { gap: "sm" },
|
|
1076
|
+
React.createElement(Heading, { level: 1 }, "Code failed"),
|
|
1077
|
+
React.createElement(Text, { tone: "muted" }, "The generated code did not complete successfully.")
|
|
1078
|
+
)
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
return React.createElement(GenericCodeModeResult, { payload });
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
createRoot(document.getElementById("root")).render(
|
|
1086
|
+
React.createElement(SidecarWidgetRoot, null, React.createElement(CodeModeWidget))
|
|
1087
|
+
);
|
|
1088
|
+
`;
|
|
1089
|
+
}
|
|
914
1090
|
function sidecarWidgetAliases(rootDir) {
|
|
915
1091
|
return {
|
|
916
1092
|
...devSidecarBundleAliases(rootDir) ?? {},
|
|
@@ -959,6 +1135,7 @@ function devSidecarBundleAliases(rootDir) {
|
|
|
959
1135
|
return void 0;
|
|
960
1136
|
}
|
|
961
1137
|
return {
|
|
1138
|
+
"sidecar-ai/remote": path4.join(repoRoot, "packages", "sidecar-ai", "src", "remote.ts"),
|
|
962
1139
|
"sidecar-ai": path4.join(repoRoot, "packages", "sidecar-ai", "src", "index.ts"),
|
|
963
1140
|
"@sidecar-ai/client": path4.join(repoRoot, "packages", "client", "src", "index.ts"),
|
|
964
1141
|
"@sidecar-ai/core": path4.join(repoRoot, "packages", "core", "src", "index.ts"),
|
|
@@ -1720,6 +1897,7 @@ function readStringArrayProperty2(definition, propertyName) {
|
|
|
1720
1897
|
|
|
1721
1898
|
// packages/compiler/src/build.ts
|
|
1722
1899
|
import { mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
|
|
1900
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1723
1901
|
import path19 from "path";
|
|
1724
1902
|
|
|
1725
1903
|
// packages/compiler/src/config.ts
|
|
@@ -1761,6 +1939,10 @@ function analyzeProjectConfig(rootDir) {
|
|
|
1761
1939
|
pagination: {
|
|
1762
1940
|
pageSize: readNumberNested(definition, "pagination", "pageSize") ?? 50,
|
|
1763
1941
|
hasOverride: hasProperty(readObjectProperty3(definition, "pagination"), "override")
|
|
1942
|
+
},
|
|
1943
|
+
codeMode: readCodeModeConfig(definition),
|
|
1944
|
+
remoteExecution: {
|
|
1945
|
+
enabled: readBooleanProperty3(definition, "remoteExecution") ?? false
|
|
1764
1946
|
}
|
|
1765
1947
|
};
|
|
1766
1948
|
}
|
|
@@ -1818,9 +2000,75 @@ function defaultCompilerConfig() {
|
|
|
1818
2000
|
pagination: {
|
|
1819
2001
|
pageSize: 50,
|
|
1820
2002
|
hasOverride: false
|
|
2003
|
+
},
|
|
2004
|
+
codeMode: {
|
|
2005
|
+
enabled: false,
|
|
2006
|
+
unsafe: false,
|
|
2007
|
+
render: {
|
|
2008
|
+
enabled: true,
|
|
2009
|
+
strategy: "last-renderable"
|
|
2010
|
+
}
|
|
2011
|
+
},
|
|
2012
|
+
remoteExecution: {
|
|
2013
|
+
enabled: false
|
|
1821
2014
|
}
|
|
1822
2015
|
};
|
|
1823
2016
|
}
|
|
2017
|
+
function readCodeModeConfig(definition) {
|
|
2018
|
+
const defaults = defaultCompilerConfig().codeMode;
|
|
2019
|
+
const property = definition.getProperty("codeMode");
|
|
2020
|
+
if (!property || !Node5.isPropertyAssignment(property)) {
|
|
2021
|
+
return defaults;
|
|
2022
|
+
}
|
|
2023
|
+
const initializer = unwrapExpression(property.getInitializer());
|
|
2024
|
+
if (!initializer) {
|
|
2025
|
+
return defaults;
|
|
2026
|
+
}
|
|
2027
|
+
if (initializer.getKind() === SyntaxKind3.TrueKeyword) {
|
|
2028
|
+
return { ...defaults, enabled: true };
|
|
2029
|
+
}
|
|
2030
|
+
if (initializer.getKind() === SyntaxKind3.FalseKeyword) {
|
|
2031
|
+
return { ...defaults, enabled: false };
|
|
2032
|
+
}
|
|
2033
|
+
if (!Node5.isObjectLiteralExpression(initializer)) {
|
|
2034
|
+
return defaults;
|
|
2035
|
+
}
|
|
2036
|
+
return {
|
|
2037
|
+
enabled: true,
|
|
2038
|
+
unsafe: readBooleanProperty3(initializer, "unsafe") ?? false,
|
|
2039
|
+
render: readCodeModeRenderConfig(initializer) ?? defaults.render
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
function readCodeModeRenderConfig(definition) {
|
|
2043
|
+
const property = definition.getProperty("render");
|
|
2044
|
+
if (!property || !Node5.isPropertyAssignment(property)) {
|
|
2045
|
+
return void 0;
|
|
2046
|
+
}
|
|
2047
|
+
const initializer = unwrapExpression(property.getInitializer());
|
|
2048
|
+
if (!initializer) {
|
|
2049
|
+
return void 0;
|
|
2050
|
+
}
|
|
2051
|
+
if (initializer.getKind() === SyntaxKind3.TrueKeyword) {
|
|
2052
|
+
return { enabled: true, strategy: "last-renderable" };
|
|
2053
|
+
}
|
|
2054
|
+
if (initializer.getKind() === SyntaxKind3.FalseKeyword) {
|
|
2055
|
+
return { enabled: false, strategy: "last-renderable" };
|
|
2056
|
+
}
|
|
2057
|
+
if (!Node5.isObjectLiteralExpression(initializer)) {
|
|
2058
|
+
return void 0;
|
|
2059
|
+
}
|
|
2060
|
+
return {
|
|
2061
|
+
enabled: readBooleanProperty3(initializer, "enabled") ?? true,
|
|
2062
|
+
strategy: readRenderStrategy(initializer) ?? "last-renderable"
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
function readRenderStrategy(definition) {
|
|
2066
|
+
const value = readStringProperty3(definition, "strategy");
|
|
2067
|
+
if (value === "last-renderable" || value === "first-renderable" || value === "explicit") {
|
|
2068
|
+
return value;
|
|
2069
|
+
}
|
|
2070
|
+
return void 0;
|
|
2071
|
+
}
|
|
1824
2072
|
function readTargetNested(definition, section, propertyName) {
|
|
1825
2073
|
const value = readStringNested(definition, section, propertyName);
|
|
1826
2074
|
return value === "mcp" || value === "chatgpt" || value === "claude" ? value : void 0;
|
|
@@ -1841,7 +2089,10 @@ function readBooleanNested(definition, section, propertyName) {
|
|
|
1841
2089
|
if (!object) {
|
|
1842
2090
|
return void 0;
|
|
1843
2091
|
}
|
|
1844
|
-
|
|
2092
|
+
return readBooleanProperty3(object, propertyName);
|
|
2093
|
+
}
|
|
2094
|
+
function readBooleanProperty3(definition, propertyName) {
|
|
2095
|
+
const property = definition.getProperty(propertyName);
|
|
1845
2096
|
if (!property || !Node5.isPropertyAssignment(property)) {
|
|
1846
2097
|
return void 0;
|
|
1847
2098
|
}
|
|
@@ -2870,12 +3121,12 @@ function readArgInput(node) {
|
|
|
2870
3121
|
if (Node7.isObjectLiteralExpression(initializer)) {
|
|
2871
3122
|
return {
|
|
2872
3123
|
description: getOptionalStringProperty2(initializer, "description"),
|
|
2873
|
-
required:
|
|
3124
|
+
required: readBooleanProperty4(initializer, "required")
|
|
2874
3125
|
};
|
|
2875
3126
|
}
|
|
2876
3127
|
return void 0;
|
|
2877
3128
|
}
|
|
2878
|
-
function
|
|
3129
|
+
function readBooleanProperty4(definition, propertyName) {
|
|
2879
3130
|
const property = definition.getProperty(propertyName);
|
|
2880
3131
|
if (!property || !Node7.isPropertyAssignment(property)) {
|
|
2881
3132
|
return void 0;
|
|
@@ -2994,7 +3245,7 @@ function analyzeResourceFile(sourceFile, rootDir) {
|
|
|
2994
3245
|
mimeType: descriptor.mimeType,
|
|
2995
3246
|
size: descriptor.size,
|
|
2996
3247
|
annotations: descriptor.annotations,
|
|
2997
|
-
subscribe:
|
|
3248
|
+
subscribe: readBooleanProperty5(definition, "subscribe"),
|
|
2998
3249
|
descriptor
|
|
2999
3250
|
};
|
|
3000
3251
|
}
|
|
@@ -3055,7 +3306,7 @@ function getOptionalNumberProperty(definition, propertyName) {
|
|
|
3055
3306
|
const initializer = unwrapExpression(property.getInitializer());
|
|
3056
3307
|
return initializer && Node8.isNumericLiteral(initializer) ? Number(initializer.getLiteralText()) : void 0;
|
|
3057
3308
|
}
|
|
3058
|
-
function
|
|
3309
|
+
function readBooleanProperty5(definition, propertyName) {
|
|
3059
3310
|
const property = definition.getProperty(propertyName);
|
|
3060
3311
|
if (!property || !Node8.isPropertyAssignment(property)) {
|
|
3061
3312
|
return void 0;
|
|
@@ -3203,7 +3454,7 @@ function renderServerEntry(rootDir, entryFile, manifest, identity) {
|
|
|
3203
3454
|
`import { pathToFileURL } from "node:url";`,
|
|
3204
3455
|
`import { createSidecarHttpHandler } from "@sidecar-ai/server";`,
|
|
3205
3456
|
`import { isSidecarAuth } from "@sidecar-ai/auth";`,
|
|
3206
|
-
`import { isSidecarPrompt, isSidecarResource, isSidecarTool } from "@sidecar-ai/core";`,
|
|
3457
|
+
`import { isSidecarPrompt, isSidecarRemote, isSidecarResource, isSidecarTool } from "@sidecar-ai/core";`,
|
|
3207
3458
|
`import { isSidecarProxy } from "@sidecar-ai/server/proxy";`,
|
|
3208
3459
|
...tools.map(
|
|
3209
3460
|
(entry, index) => `import tool${index} from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, entry.sourceFile)))};`
|
|
@@ -3216,6 +3467,7 @@ function renderServerEntry(rootDir, entryFile, manifest, identity) {
|
|
|
3216
3467
|
),
|
|
3217
3468
|
existsSync6(path18.join(rootDir, "auth.ts")) ? `import authExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "auth.ts")))};` : `const authExport = undefined;`,
|
|
3218
3469
|
existsSync6(path18.join(rootDir, "proxy.ts")) ? `import proxyExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "proxy.ts")))};` : `const proxyExport = undefined;`,
|
|
3470
|
+
existsSync6(path18.join(rootDir, "remote.ts")) ? `import remoteExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "remote.ts")))};` : `const remoteExport = undefined;`,
|
|
3219
3471
|
existsSync6(path18.join(rootDir, "sidecar.config.ts")) ? `import configExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "sidecar.config.ts")))};` : `const configExport = undefined;`
|
|
3220
3472
|
].join("\n");
|
|
3221
3473
|
return `${imports}
|
|
@@ -3228,6 +3480,9 @@ const auth = loadedAuth && process.env.SIDECAR_MCP_URL
|
|
|
3228
3480
|
? loadedAuth.withResource(process.env.SIDECAR_MCP_URL)
|
|
3229
3481
|
: loadedAuth;
|
|
3230
3482
|
const proxy = proxyExport === undefined ? undefined : assertProxy(proxyExport);
|
|
3483
|
+
const remoteExecution = manifest.codeMode?.remoteExecution
|
|
3484
|
+
? assertRemote(remoteExport)
|
|
3485
|
+
: undefined;
|
|
3231
3486
|
const runtimeConfig = configExport && typeof configExport === "object" ? configExport : undefined;
|
|
3232
3487
|
|
|
3233
3488
|
if (auth && !process.env.SIDECAR_MCP_URL) {
|
|
@@ -3241,6 +3496,21 @@ export const handler = createSidecarHttpHandler({
|
|
|
3241
3496
|
publicUrl: process.env.SIDECAR_PUBLIC_URL ?? process.env.SIDECAR_MCP_URL,
|
|
3242
3497
|
auth,
|
|
3243
3498
|
proxy,
|
|
3499
|
+
codeMode: manifest.codeMode
|
|
3500
|
+
? {
|
|
3501
|
+
enabled: true,
|
|
3502
|
+
unsafe: manifest.codeMode.unsafe,
|
|
3503
|
+
render: manifest.codeMode.render,
|
|
3504
|
+
widgetMeta: manifest.codeMode.widget?.resourceUri
|
|
3505
|
+
? {
|
|
3506
|
+
ui: { resourceUri: manifest.codeMode.widget.resourceUri },
|
|
3507
|
+
"ui/resourceUri": manifest.codeMode.widget.resourceUri,
|
|
3508
|
+
...(manifest.target === "chatgpt" ? { "openai/outputTemplate": manifest.codeMode.widget.resourceUri } : {}),
|
|
3509
|
+
}
|
|
3510
|
+
: undefined,
|
|
3511
|
+
}
|
|
3512
|
+
: undefined,
|
|
3513
|
+
remoteExecution,
|
|
3244
3514
|
tools: [
|
|
3245
3515
|
${tools.map((entry, index) => ` loadTool(${JSON.stringify(entry.sourceFile)}, tool${index}, manifest.tools[${index}].descriptor),`).join("\n")}
|
|
3246
3516
|
],
|
|
@@ -3272,12 +3542,23 @@ if (isDirectRun()) {
|
|
|
3272
3542
|
server.listen(port, host, () => {
|
|
3273
3543
|
const localHost = host === "0.0.0.0" ? "127.0.0.1" : host;
|
|
3274
3544
|
console.log(\`MCP running on Streamable HTTP at http://\${localHost}:\${port}\${process.env.SIDECAR_MCP_PATH ?? "/mcp"}\`);
|
|
3545
|
+
printRuntimeUrls(localHost, port);
|
|
3275
3546
|
});
|
|
3276
3547
|
|
|
3277
3548
|
process.on("SIGTERM", () => shutdown());
|
|
3278
3549
|
process.on("SIGINT", () => shutdown());
|
|
3279
3550
|
}
|
|
3280
3551
|
|
|
3552
|
+
function printRuntimeUrls(localHost, port) {
|
|
3553
|
+
const mcpPath = process.env.SIDECAR_MCP_PATH ?? "/mcp";
|
|
3554
|
+
console.log("Sidecar URLs:");
|
|
3555
|
+
console.log(\` Local MCP: http://\${localHost}:\${port}\${mcpPath}\`);
|
|
3556
|
+
console.log(\` Public MCP: \${process.env.SIDECAR_MCP_URL ?? "set SIDECAR_MCP_URL=https://your-host.example.com" + mcpPath}\`);
|
|
3557
|
+
if (process.env.SIDECAR_PUBLIC_URL) {
|
|
3558
|
+
console.log(\` Public base: \${process.env.SIDECAR_PUBLIC_URL}\`);
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3281
3562
|
function loadTool(sourceFile, value, descriptor) {
|
|
3282
3563
|
const tool = unwrapRuntimeDefault(value);
|
|
3283
3564
|
if (!isSidecarTool(tool)) {
|
|
@@ -3322,6 +3603,14 @@ function assertProxy(value) {
|
|
|
3322
3603
|
return proxyValue;
|
|
3323
3604
|
}
|
|
3324
3605
|
|
|
3606
|
+
function assertRemote(value) {
|
|
3607
|
+
const remoteValue = unwrapRuntimeDefault(value);
|
|
3608
|
+
if (!isSidecarRemote(remoteValue)) {
|
|
3609
|
+
throw new Error("remote.ts must default-export remote({ execute }) from sidecar-ai/remote.");
|
|
3610
|
+
}
|
|
3611
|
+
return remoteValue;
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3325
3614
|
function unwrapRuntimeDefault(value) {
|
|
3326
3615
|
if (
|
|
3327
3616
|
value &&
|
|
@@ -3362,7 +3651,7 @@ function shutdown() {
|
|
|
3362
3651
|
`;
|
|
3363
3652
|
}
|
|
3364
3653
|
function renderWidgetResources(manifest) {
|
|
3365
|
-
|
|
3654
|
+
const toolWidgets = manifest.tools.filter((entry) => entry.widget?.outputFile).map((entry) => ` {
|
|
3366
3655
|
uri: ${JSON.stringify(entry.widget?.resourceUri)},
|
|
3367
3656
|
name: ${JSON.stringify(entry.name)},
|
|
3368
3657
|
description: ${JSON.stringify(entry.widget?.options?.description)},
|
|
@@ -3370,6 +3659,15 @@ function renderWidgetResources(manifest) {
|
|
|
3370
3659
|
text: readWidget(${JSON.stringify(entry.widget?.outputFile)}),
|
|
3371
3660
|
_meta: ${JSON.stringify(entry.widget?.resourceMeta ?? void 0)},
|
|
3372
3661
|
},`).join("\n");
|
|
3662
|
+
const codeModeWidget = manifest.codeMode?.widget?.outputFile ? ` {
|
|
3663
|
+
uri: ${JSON.stringify(manifest.codeMode.widget.resourceUri)},
|
|
3664
|
+
name: "Execute Code",
|
|
3665
|
+
description: ${JSON.stringify(manifest.codeMode.widget.options?.description)},
|
|
3666
|
+
mimeType: "text/html;profile=mcp-app",
|
|
3667
|
+
text: readWidget(${JSON.stringify(manifest.codeMode.widget.outputFile)}),
|
|
3668
|
+
_meta: ${JSON.stringify(manifest.codeMode.widget.resourceMeta ?? void 0)},
|
|
3669
|
+
},` : "";
|
|
3670
|
+
return [codeModeWidget, toolWidgets].filter(Boolean).join("\n");
|
|
3373
3671
|
}
|
|
3374
3672
|
function renderServerPackage(identity) {
|
|
3375
3673
|
return `${JSON.stringify({
|
|
@@ -3419,6 +3717,7 @@ function sidecarBundleAliases(rootDir) {
|
|
|
3419
3717
|
return void 0;
|
|
3420
3718
|
}
|
|
3421
3719
|
return {
|
|
3720
|
+
"sidecar-ai/remote": path18.join(repoRoot, "packages", "sidecar-ai", "src", "remote.ts"),
|
|
3422
3721
|
"sidecar-ai": path18.join(repoRoot, "packages", "sidecar-ai", "src", "index.ts"),
|
|
3423
3722
|
"@sidecar-ai/auth": path18.join(repoRoot, "packages", "auth", "src", "index.ts"),
|
|
3424
3723
|
"@sidecar-ai/core": path18.join(repoRoot, "packages", "core", "src", "index.ts"),
|
|
@@ -3462,6 +3761,12 @@ async function buildProject(options) {
|
|
|
3462
3761
|
const target = options.target ?? config.build.target ?? "mcp";
|
|
3463
3762
|
const host = options.host ?? config.build.host ?? "node";
|
|
3464
3763
|
const plugins = options.plugins ?? config.build.plugins ?? false;
|
|
3764
|
+
if (config.codeMode.enabled && !config.codeMode.unsafe && !config.remoteExecution.enabled) {
|
|
3765
|
+
throw new Error("Code mode requires remoteExecution: true, or codeMode: { unsafe: true } for trusted local use.");
|
|
3766
|
+
}
|
|
3767
|
+
if (config.codeMode.enabled && config.remoteExecution.enabled && !existsSync7(path19.join(rootDir, "remote.ts"))) {
|
|
3768
|
+
throw new Error("remoteExecution: true requires a reserved remote.ts file at the project root.");
|
|
3769
|
+
}
|
|
3465
3770
|
const tools = await analyzeProjectTools(rootDir, { target });
|
|
3466
3771
|
const resources = await analyzeProjectResources(rootDir);
|
|
3467
3772
|
const resourceTemplates = [];
|
|
@@ -3480,6 +3785,7 @@ async function buildProject(options) {
|
|
|
3480
3785
|
const outDir = resolveInsideRoot(rootDir, options.outDir ?? config.build.outDir ?? defaultBuildOutDir(host, target));
|
|
3481
3786
|
const runtimeOutDir = resolveRuntimeOutputDir(outDir, host);
|
|
3482
3787
|
await buildWidgets(rootDir, runtimeOutDir, tools, config.build.widgets);
|
|
3788
|
+
const codeModeWidget = config.codeMode.enabled && config.codeMode.render.enabled ? await buildCodeModeWidget(rootDir, runtimeOutDir, tools, target, config.build.widgets) : void 0;
|
|
3483
3789
|
const manifest = {
|
|
3484
3790
|
version: 1,
|
|
3485
3791
|
target,
|
|
@@ -3488,6 +3794,13 @@ async function buildProject(options) {
|
|
|
3488
3794
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3489
3795
|
config,
|
|
3490
3796
|
tools,
|
|
3797
|
+
codeMode: config.codeMode.enabled ? {
|
|
3798
|
+
enabled: true,
|
|
3799
|
+
unsafe: config.codeMode.unsafe,
|
|
3800
|
+
remoteExecution: config.remoteExecution.enabled,
|
|
3801
|
+
render: config.codeMode.render,
|
|
3802
|
+
widget: codeModeWidget
|
|
3803
|
+
} : void 0,
|
|
3491
3804
|
resources,
|
|
3492
3805
|
resourceTemplates,
|
|
3493
3806
|
prompts,
|