@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 CHANGED
@@ -1,149 +1,7 @@
1
- import { BuildHost, BuildTarget, WidgetBuildConfig, JsonSchema, ToolAnnotations, ToolVisibility, ToolWidgetOptions, McpToolDescriptor, ResourceAnnotations, McpResourceDescriptor, McpResourceTemplateDescriptor, PromptArgsDefinition, McpPromptDescriptor } from '@sidecar-ai/core';
1
+ import { JsonSchema } from '@sidecar-ai/core';
2
2
  import { SourceFile } from 'ts-morph';
3
-
4
- /** Diagnostic severity surfaced by the CLI and future editor integrations. */
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 { type BuildProjectOptions, CompilerError, type ProjectIdentity, SERVER_ENTRYPOINT, type SidecarCompilerConfig, type SidecarDiagnostic, type SidecarHost, type SidecarManifest, type SidecarPromptManifestEntry, type SidecarResourceManifestEntry, type SidecarResourceTemplateManifestEntry, type SidecarSourceVariant, type SidecarTarget, type SidecarToolManifestEntry, type SidecarWidgetManifestEntry, VERCEL_ENTRYPOINT, analyzeProjectConfig, analyzeProjectPrompts, analyzeProjectResources, analyzeProjectTools, analyzePromptFile, analyzeResourceFile, analyzeToolFile, buildProject, collectProjectDiagnostics, formatDiagnostic };
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
- const property = object.getProperty(propertyName);
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: readBooleanProperty3(initializer, "required")
3124
+ required: readBooleanProperty4(initializer, "required")
2874
3125
  };
2875
3126
  }
2876
3127
  return void 0;
2877
3128
  }
2878
- function readBooleanProperty3(definition, propertyName) {
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: readBooleanProperty4(definition, "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 readBooleanProperty4(definition, propertyName) {
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
- return manifest.tools.filter((entry) => entry.widget?.outputFile).map((entry) => ` {
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,