@sidecar-ai/compiler 0.1.0-alpha.1 → 0.1.0-alpha.11

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.js CHANGED
@@ -1,6 +1,12 @@
1
1
  // packages/core/src/index.ts
2
+ var toolBrand = /* @__PURE__ */ Symbol.for("sidecar.tool");
2
3
  var toolResultBrand = /* @__PURE__ */ Symbol.for("sidecar.toolResult");
3
4
  var resourceResultBrand = /* @__PURE__ */ Symbol.for("sidecar.resourceResult");
5
+ function isSidecarTool(value) {
6
+ return Boolean(
7
+ value && typeof value === "object" && (value[toolBrand] || value.kind === "sidecar.tool")
8
+ );
9
+ }
4
10
  var toolResult = Object.assign(
5
11
  createToolResult,
6
12
  {
@@ -27,9 +33,9 @@ var resourceResult = Object.assign(
27
33
  );
28
34
  function createToolResult(input) {
29
35
  const resultEnvelope = stripUndefined({
30
- structuredContent: input.structuredContent,
36
+ structuredContent: stripJsonUndefined(input.structuredContent),
31
37
  content: normalizeRequiredContent(input.content),
32
- _meta: input.meta,
38
+ _meta: stripJsonUndefined(input.meta),
33
39
  isError: input.isError
34
40
  });
35
41
  Object.defineProperty(resultEnvelope, toolResultBrand, {
@@ -271,10 +277,24 @@ function isReadonlyArray(value) {
271
277
  function stripUndefined(value) {
272
278
  return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
273
279
  }
280
+ function stripJsonUndefined(value) {
281
+ if (value === void 0) {
282
+ return void 0;
283
+ }
284
+ if (Array.isArray(value)) {
285
+ return value.map((entry) => stripJsonUndefined(entry)).filter((entry) => entry !== void 0);
286
+ }
287
+ if (!value || typeof value !== "object") {
288
+ return value;
289
+ }
290
+ return Object.fromEntries(
291
+ Object.entries(value).map(([key, entry]) => [key, stripJsonUndefined(entry)]).filter(([, entry]) => entry !== void 0)
292
+ );
293
+ }
274
294
 
275
295
  // packages/compiler/src/analyze.ts
276
296
  import { readdir } from "fs/promises";
277
- import path4 from "path";
297
+ import path5 from "path";
278
298
  import {
279
299
  Node as Node4,
280
300
  SyntaxKind as SyntaxKind2
@@ -441,14 +461,7 @@ function devSidecarTypePaths() {
441
461
  import {
442
462
  Node as Node2
443
463
  } from "ts-morph";
444
- function getParamsSchema(definition, execute) {
445
- const explicitParams = definition.getProperty("params");
446
- if (explicitParams && Node2.isPropertyAssignment(explicitParams)) {
447
- const inferred = getSchemaFromZodProperty(explicitParams);
448
- if (inferred) {
449
- return inferred;
450
- }
451
- }
464
+ function getParamsSchema(_definition, execute) {
452
465
  const [params] = execute.getParameters();
453
466
  if (!params) {
454
467
  return emptyObjectSchema();
@@ -469,74 +482,6 @@ function getOutputSchema(definition, execute) {
469
482
  }
470
483
  return typeToJsonSchema(returnType);
471
484
  }
472
- function getSchemaFromZodProperty(property) {
473
- const initializer = property.getInitializer();
474
- if (!initializer) {
475
- return void 0;
476
- }
477
- if (Node2.isCallExpression(initializer) && initializer.getExpression().getText().endsWith(".object")) {
478
- const [shape] = initializer.getArguments();
479
- if (shape && Node2.isObjectLiteralExpression(shape)) {
480
- return zodObjectLiteralToSchema(shape);
481
- }
482
- }
483
- return void 0;
484
- }
485
- function zodObjectLiteralToSchema(shape) {
486
- const properties = {};
487
- const required = [];
488
- for (const property of shape.getProperties()) {
489
- if (!Node2.isPropertyAssignment(property)) {
490
- continue;
491
- }
492
- const name = property.getName().replace(/^["']|["']$/g, "");
493
- const initializer = property.getInitializer();
494
- if (!initializer) {
495
- continue;
496
- }
497
- const propertySchema = zodExpressionToSchema(initializer);
498
- properties[name] = propertySchema.schema;
499
- if (!propertySchema.optional) {
500
- required.push(name);
501
- }
502
- }
503
- return {
504
- type: "object",
505
- properties,
506
- required,
507
- additionalProperties: false
508
- };
509
- }
510
- function zodExpressionToSchema(expression) {
511
- const text = expression.getText();
512
- const optional = /\.optional\(\)/.test(text) || /\.default\(/.test(text);
513
- const schema = {};
514
- if (/z\.string\(\)/.test(text)) {
515
- schema.type = "string";
516
- } else if (/z\.number\(\)/.test(text)) {
517
- schema.type = "number";
518
- } else if (/z\.boolean\(\)/.test(text)) {
519
- schema.type = "boolean";
520
- } else if (/z\.array\(/.test(text)) {
521
- schema.type = "array";
522
- schema.items = {};
523
- } else {
524
- schema.type = "object";
525
- }
526
- const min = text.match(/\.min\((\d+)/);
527
- const max = text.match(/\.max\((\d+)/);
528
- if (schema.type === "string") {
529
- if (min) schema.minLength = Number(min[1]);
530
- if (max) schema.maxLength = Number(max[1]);
531
- } else {
532
- if (min) schema.minimum = Number(min[1]);
533
- if (max) schema.maximum = Number(max[1]);
534
- }
535
- if (/\.int\(\)/.test(text)) {
536
- schema.type = "integer";
537
- }
538
- return { schema, optional };
539
- }
540
485
  function typeToJsonSchema(type, description) {
541
486
  const withoutUndefined = removeUndefined(type);
542
487
  const schema = typeToJsonSchemaInner(withoutUndefined);
@@ -579,12 +524,19 @@ function typeToJsonSchemaInner(type) {
579
524
  return { anyOf: parts.map((part) => typeToJsonSchema(part)) };
580
525
  }
581
526
  const properties = type.getProperties();
527
+ const indexType = type.getStringIndexType() ?? type.getNumberIndexType();
582
528
  if (properties.length > 0) {
583
- return objectTypeToSchema(properties);
529
+ return objectTypeToSchema(properties, indexType);
530
+ }
531
+ if (indexType) {
532
+ return {
533
+ type: "object",
534
+ additionalProperties: indexTypeToAdditionalProperties(indexType)
535
+ };
584
536
  }
585
537
  return {};
586
538
  }
587
- function objectTypeToSchema(properties) {
539
+ function objectTypeToSchema(properties, indexType) {
588
540
  const schemaProperties = {};
589
541
  const required = [];
590
542
  for (const property of properties) {
@@ -608,9 +560,16 @@ function objectTypeToSchema(properties) {
608
560
  type: "object",
609
561
  properties: schemaProperties,
610
562
  required,
611
- additionalProperties: false
563
+ additionalProperties: indexType ? indexTypeToAdditionalProperties(indexType) : false
612
564
  };
613
565
  }
566
+ function indexTypeToAdditionalProperties(type) {
567
+ if (type.isAny() || type.isUnknown()) {
568
+ return true;
569
+ }
570
+ const schema = typeToJsonSchema(type);
571
+ return Object.keys(schema).length ? schema : true;
572
+ }
614
573
  function literalOrPrimitive(type, primitive) {
615
574
  if (isLiteralType(type)) {
616
575
  return { const: literalValue(type) };
@@ -668,39 +627,134 @@ function schemaDescription(symbol) {
668
627
  }).find((comment) => comment.trim().length > 0);
669
628
  }
670
629
 
630
+ // packages/compiler/src/runtime-schema.ts
631
+ import { existsSync as existsSync2 } from "fs";
632
+ import path3 from "path";
633
+ import { pathToFileURL } from "url";
634
+ import { tsImport } from "tsx/esm/api";
635
+ async function readRuntimeToolInputSchema(rootDir, sourcePath) {
636
+ let sidecarTool;
637
+ try {
638
+ sidecarTool = await importRuntimeTool(rootDir, sourcePath);
639
+ } catch (error) {
640
+ warnRuntimeSchemaFallback(sourcePath, error);
641
+ return void 0;
642
+ }
643
+ if (!sidecarTool.params) {
644
+ return void 0;
645
+ }
646
+ try {
647
+ return await paramsToJsonSchema(sidecarTool.params);
648
+ } catch (error) {
649
+ warnRuntimeSchemaFallback(sourcePath, error);
650
+ return void 0;
651
+ }
652
+ }
653
+ async function importRuntimeTool(rootDir, sourcePath) {
654
+ const module = await tsImport(pathToFileURL(sourcePath).href, {
655
+ parentURL: runtimeParentUrl(rootDir),
656
+ tsconfig: runtimeTsconfig(rootDir)
657
+ });
658
+ const defaultExport = unwrapRuntimeDefault(module.default);
659
+ if (!isSidecarTool(defaultExport)) {
660
+ throw new Error(`${path3.relative(rootDir, sourcePath)} must default-export tool({ ... }).`);
661
+ }
662
+ return defaultExport;
663
+ }
664
+ async function paramsToJsonSchema(params) {
665
+ const instanceConverter = readInstanceConverter(params);
666
+ if (instanceConverter) {
667
+ return ensureJsonSchema(instanceConverter({ io: "input" }));
668
+ }
669
+ if (isZodMiniSchema(params)) {
670
+ const zod = await import("zod/v4-mini");
671
+ if (typeof zod.toJSONSchema === "function") {
672
+ return ensureJsonSchema(zod.toJSONSchema(params, { io: "input" }));
673
+ }
674
+ }
675
+ return void 0;
676
+ }
677
+ function readInstanceConverter(params) {
678
+ if (!params || typeof params !== "object") {
679
+ return void 0;
680
+ }
681
+ const record = params;
682
+ const converter = record.toJSONSchema ?? record.toJsonSchema;
683
+ return typeof converter === "function" ? converter.bind(params) : void 0;
684
+ }
685
+ function isZodMiniSchema(params) {
686
+ return Boolean(params && typeof params === "object" && "_zod" in params);
687
+ }
688
+ function ensureJsonSchema(value) {
689
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
690
+ return void 0;
691
+ }
692
+ return value;
693
+ }
694
+ function runtimeParentUrl(rootDir) {
695
+ const configPath = path3.join(rootDir, "sidecar.config.ts");
696
+ return pathToFileURL(existsSync2(configPath) ? configPath : rootDir).href;
697
+ }
698
+ function runtimeTsconfig(rootDir) {
699
+ const projectTsconfig = path3.join(rootDir, "tsconfig.json");
700
+ if (existsSync2(projectTsconfig)) {
701
+ return projectTsconfig;
702
+ }
703
+ const repoTsconfig = path3.join(process.cwd(), "tsconfig.json");
704
+ const repoCore = path3.join(process.cwd(), "packages", "core", "src", "index.ts");
705
+ return existsSyncSafe(repoTsconfig) && existsSyncSafe(repoCore) ? repoTsconfig : false;
706
+ }
707
+ function unwrapRuntimeDefault(value) {
708
+ if (value && typeof value === "object" && "default" in value && Object.keys(value).every((key) => key === "default" || key === "__esModule")) {
709
+ return unwrapRuntimeDefault(value.default);
710
+ }
711
+ return value;
712
+ }
713
+ function warnRuntimeSchemaFallback(sourcePath, error) {
714
+ const message = error instanceof Error ? error.message : String(error);
715
+ console.warn(
716
+ `[sidecar] Could not convert runtime params schema for ${sourcePath}; falling back to TypeScript parameter inference. ${message}`
717
+ );
718
+ }
719
+
671
720
  // packages/compiler/src/widgets.ts
672
721
  import { createHash } from "crypto";
673
- import { existsSync as existsSync2 } from "fs";
722
+ import { existsSync as existsSync3 } from "fs";
674
723
  import { mkdir, readFile, writeFile } from "fs/promises";
675
- import path3 from "path";
724
+ import { createRequire } from "module";
725
+ import path4 from "path";
726
+ import { pathToFileURL as pathToFileURL2 } from "url";
676
727
  import { build as esbuild } from "esbuild";
677
728
  import postcss from "postcss";
678
729
  import {
679
730
  Node as Node3,
680
731
  SyntaxKind
681
732
  } from "ts-morph";
682
- async function buildWidgets(rootDir, outDir, tools) {
733
+ var CLAUDE_FONT_RESOURCE_DOMAIN = "https://assets.claude.ai";
734
+ var requireFromCompiler = createRequire(import.meta.url);
735
+ async function buildWidgets(rootDir, outDir, tools, config = void 0) {
683
736
  const widgets = tools.filter(
684
737
  (entry) => Boolean(entry.widget)
685
738
  );
686
739
  if (!widgets.length) {
687
740
  return;
688
741
  }
689
- const cacheDir = path3.join(rootDir, ".sidecar", "cache", "widgets");
742
+ const cacheDir = path4.join(rootDir, ".sidecar", "cache", "widgets");
690
743
  await mkdir(cacheDir, { recursive: true });
691
744
  const appStyle = await prepareAppStyle(rootDir, cacheDir);
745
+ const configure = await loadWidgetBundlerHook(rootDir, cacheDir, config?.configure);
692
746
  for (const entry of widgets) {
693
- const sourceFile = path3.join(rootDir, entry.widget.sourceFile);
747
+ const sourceFile = path4.join(rootDir, entry.widget.sourceFile);
694
748
  const safeId = safePathSegment(entry.id);
695
- const entryFile = path3.join(cacheDir, `${safeId}.entry.tsx`);
696
- const importPath = toImportSpecifier(path3.dirname(entryFile), sourceFile);
749
+ const entryFile = path4.join(cacheDir, `${safeId}.entry.tsx`);
750
+ const importPath = toImportSpecifier(path4.dirname(entryFile), sourceFile);
697
751
  await writeFile(
698
752
  entryFile,
699
753
  `import React from "react";
700
754
  import { createRoot } from "react-dom/client";
701
755
  import { SidecarWidgetRoot } from "@sidecar-ai/react";
702
756
  import "@sidecar-ai/native/styles.css";
703
- ${appStyle ? `import ${JSON.stringify(toImportSpecifier(path3.dirname(entryFile), appStyle))};` : ""}
757
+ ${appStyle ? `import ${JSON.stringify(toImportSpecifier(path4.dirname(entryFile), appStyle))};` : ""}
704
758
  import Component from ${JSON.stringify(importPath)};
705
759
 
706
760
  createRoot(document.getElementById("root")!).render(
@@ -708,64 +762,396 @@ createRoot(document.getElementById("root")!).render(
708
762
  );
709
763
  `
710
764
  );
711
- const bundled = await esbuild({
765
+ const baseOptions = enforceWidgetBuildInvariants({
712
766
  absWorkingDir: rootDir,
713
- alias: devSidecarBundleAliases(rootDir),
767
+ alias: sidecarWidgetAliases(rootDir),
714
768
  bundle: true,
769
+ define: {
770
+ "process.env.NODE_ENV": JSON.stringify("production")
771
+ },
715
772
  entryPoints: [entryFile],
716
773
  format: "iife",
717
774
  jsx: "automatic",
718
- minify: false,
775
+ minify: true,
719
776
  nodePaths: [
720
- path3.join(rootDir, "node_modules"),
721
- path3.join(process.cwd(), "node_modules")
777
+ path4.join(rootDir, "node_modules"),
778
+ path4.join(process.cwd(), "node_modules")
722
779
  ],
723
780
  outfile: "widget.js",
724
781
  platform: "browser",
725
782
  sourcemap: false,
726
783
  write: false
727
784
  });
728
- const javascript = bundled.outputFiles.find((file) => file.path.endsWith(".js"))?.text ?? "";
729
- const css = bundled.outputFiles.filter((file) => file.path.endsWith(".css")).map((file) => file.text).join("\n");
785
+ const staticOptions = widgetBuildOptionsFromConfig(rootDir, config);
786
+ const configuredOptions = configure ? await configureWidgetBuild(configure, {
787
+ rootDir,
788
+ outDir,
789
+ entryFile,
790
+ widget: {
791
+ toolId: entry.id,
792
+ toolName: entry.name,
793
+ target: entry.target,
794
+ sourceFile: entry.widget.sourceFile
795
+ },
796
+ esbuildOptions: mergeWidgetBuildOptions(baseOptions, staticOptions)
797
+ }) : void 0;
798
+ const bundled = await esbuild(enforceWidgetBuildInvariants(
799
+ mergeWidgetBuildOptions(baseOptions, staticOptions, configuredOptions),
800
+ { rootDir, entryFile }
801
+ ));
802
+ const outputFiles = bundled.outputFiles ?? [];
803
+ const javascript = outputFiles.find((file) => file.path.endsWith(".js"))?.text ?? "";
804
+ const css = outputFiles.filter((file) => file.path.endsWith(".css")).map((file) => file.text).join("\n");
730
805
  const html = renderWidgetHtml(entry.name, javascript, css);
731
806
  const hash = createHash("sha256").update(html).digest("hex").slice(0, 12);
732
- const outputDir = path3.join(outDir, "public", "widgets", safeId);
733
- const outputFile = path3.join(outputDir, `widget.${hash}.html`);
807
+ const outputDir = path4.join(outDir, "public", "widgets", safeId);
808
+ const outputFile = path4.join(outputDir, `widget.${hash}.html`);
734
809
  const resourceUri = `ui://${safeId}/widget.${hash}.html`;
735
810
  await mkdir(outputDir, { recursive: true });
736
811
  await writeFile(outputFile, html);
737
812
  entry.widget.resourceUri = resourceUri;
738
813
  entry.widget.resourceMeta = widgetResourceMeta(entry.widget.options, entry.target);
739
- entry.widget.outputFile = path3.relative(outDir, outputFile);
814
+ entry.widget.outputFile = path4.relative(outDir, outputFile);
740
815
  entry.descriptor._meta = mergeWidgetMeta(entry.descriptor._meta, widgetMeta(resourceUri, entry.widget.options, entry.target));
741
816
  }
742
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
+ }
895
+ function widgetBuildOptionsFromConfig(rootDir, config) {
896
+ const esbuildConfig = config?.esbuild;
897
+ if (!esbuildConfig) {
898
+ return void 0;
899
+ }
900
+ return {
901
+ alias: normalizeAliases(rootDir, esbuildConfig.alias),
902
+ conditions: esbuildConfig.conditions,
903
+ define: esbuildConfig.define,
904
+ external: esbuildConfig.external,
905
+ jsx: esbuildConfig.jsx,
906
+ jsxImportSource: esbuildConfig.jsxImportSource,
907
+ loader: esbuildConfig.loader,
908
+ mainFields: esbuildConfig.mainFields
909
+ };
910
+ }
911
+ async function loadWidgetBundlerHook(rootDir, cacheDir, hookPath) {
912
+ if (!hookPath) {
913
+ return void 0;
914
+ }
915
+ const sourcePath = path4.resolve(rootDir, hookPath);
916
+ const relative = path4.relative(rootDir, sourcePath);
917
+ if (relative.startsWith("..") || path4.isAbsolute(relative)) {
918
+ throw new Error(`Widget bundler hook must stay inside the project root: ${hookPath}`);
919
+ }
920
+ if (!existsSync3(sourcePath)) {
921
+ throw new Error(`Widget bundler hook not found: ${hookPath}`);
922
+ }
923
+ const outputFile = path4.join(cacheDir, `widget-bundler.${contentHash(hookPath)}.mjs`);
924
+ await esbuild({
925
+ absWorkingDir: rootDir,
926
+ alias: devSidecarBundleAliases(rootDir),
927
+ bundle: true,
928
+ entryPoints: [sourcePath],
929
+ format: "esm",
930
+ nodePaths: [
931
+ path4.join(rootDir, "node_modules"),
932
+ path4.join(process.cwd(), "node_modules")
933
+ ],
934
+ outfile: outputFile,
935
+ packages: "external",
936
+ platform: "node",
937
+ target: "node20"
938
+ });
939
+ const module = await import(`${pathToFileURL2(outputFile).href}?t=${Date.now()}`);
940
+ if (typeof module.default !== "function") {
941
+ throw new Error(`Widget bundler hook must default-export a function: ${hookPath}`);
942
+ }
943
+ return module.default;
944
+ }
945
+ async function configureWidgetBuild(hook, input) {
946
+ const result = await hook(input);
947
+ if (!result) {
948
+ return void 0;
949
+ }
950
+ if (typeof result === "object" && "esbuildOptions" in result) {
951
+ return result.esbuildOptions;
952
+ }
953
+ return result;
954
+ }
955
+ function mergeWidgetBuildOptions(...options) {
956
+ const merged = {};
957
+ for (const option of options) {
958
+ if (!option) {
959
+ continue;
960
+ }
961
+ const previousAlias = merged.alias;
962
+ const previousDefine = merged.define;
963
+ const previousLoader = merged.loader;
964
+ const previousExternal = merged.external;
965
+ const previousNodePaths = merged.nodePaths;
966
+ const previousPlugins = merged.plugins;
967
+ Object.assign(merged, option);
968
+ merged.alias = { ...previousAlias ?? {}, ...option.alias ?? {} };
969
+ merged.define = { ...previousDefine ?? {}, ...option.define ?? {} };
970
+ merged.loader = { ...previousLoader ?? {}, ...option.loader ?? {} };
971
+ merged.external = uniqueStrings([...previousExternal ?? [], ...option.external ?? []]);
972
+ merged.nodePaths = uniqueStrings([...previousNodePaths ?? [], ...option.nodePaths ?? []]);
973
+ merged.plugins = [...previousPlugins ?? [], ...option.plugins ?? []];
974
+ }
975
+ return merged;
976
+ }
977
+ function enforceWidgetBuildInvariants(options, required) {
978
+ const aliases = required ? { ...options.alias ?? {}, ...reactSingletonAliases(required.rootDir) } : options.alias;
979
+ return {
980
+ ...options,
981
+ absWorkingDir: required?.rootDir ?? options.absWorkingDir,
982
+ alias: aliases,
983
+ bundle: true,
984
+ entryPoints: required ? [required.entryFile] : options.entryPoints,
985
+ format: "iife",
986
+ outfile: "widget.js",
987
+ platform: "browser",
988
+ write: false
989
+ };
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
+ }
1090
+ function sidecarWidgetAliases(rootDir) {
1091
+ return {
1092
+ ...devSidecarBundleAliases(rootDir) ?? {},
1093
+ ...reactSingletonAliases(rootDir)
1094
+ };
1095
+ }
1096
+ function reactSingletonAliases(rootDir) {
1097
+ return {
1098
+ react: resolveProjectModule(rootDir, "react"),
1099
+ "react/jsx-runtime": resolveProjectModule(rootDir, "react/jsx-runtime"),
1100
+ "react/jsx-dev-runtime": resolveProjectModule(rootDir, "react/jsx-dev-runtime"),
1101
+ "react-dom": resolveProjectModule(rootDir, "react-dom"),
1102
+ "react-dom/client": resolveProjectModule(rootDir, "react-dom/client"),
1103
+ "react-dom/server": resolveProjectModule(rootDir, "react-dom/server")
1104
+ };
1105
+ }
1106
+ function resolveProjectModule(rootDir, specifier) {
1107
+ return requireFromCompiler.resolve(specifier, {
1108
+ paths: [rootDir, process.cwd()]
1109
+ });
1110
+ }
1111
+ function normalizeAliases(rootDir, aliases) {
1112
+ if (!aliases) {
1113
+ return void 0;
1114
+ }
1115
+ return Object.fromEntries(
1116
+ Object.entries(aliases).map(([key, value]) => [
1117
+ key,
1118
+ value.startsWith(".") ? path4.resolve(rootDir, value) : value
1119
+ ])
1120
+ );
1121
+ }
1122
+ function uniqueStrings(values) {
1123
+ return [...new Set(values)];
1124
+ }
1125
+ function contentHash(value) {
1126
+ return createHash("sha256").update(value).digest("hex").slice(0, 12);
1127
+ }
743
1128
  function devSidecarBundleAliases(rootDir) {
744
1129
  const repoRoot = findSidecarRepoRoot(rootDir) ?? findSidecarRepoRoot(process.cwd());
745
1130
  if (!repoRoot) {
746
1131
  return void 0;
747
1132
  }
748
- const reactEntry = path3.join(repoRoot, "packages", "react", "src", "index.ts");
749
- if (!existsSync2(reactEntry)) {
1133
+ const reactEntry = path4.join(repoRoot, "packages", "react", "src", "index.ts");
1134
+ if (!existsSync3(reactEntry)) {
750
1135
  return void 0;
751
1136
  }
752
1137
  return {
753
- "sidecar-ai": path3.join(repoRoot, "packages", "sidecar-ai", "src", "index.ts"),
754
- "@sidecar-ai/client": path3.join(repoRoot, "packages", "client", "src", "index.ts"),
755
- "@sidecar-ai/core": path3.join(repoRoot, "packages", "core", "src", "index.ts"),
1138
+ "sidecar-ai/remote": path4.join(repoRoot, "packages", "sidecar-ai", "src", "remote.ts"),
1139
+ "sidecar-ai": path4.join(repoRoot, "packages", "sidecar-ai", "src", "index.ts"),
1140
+ "@sidecar-ai/client": path4.join(repoRoot, "packages", "client", "src", "index.ts"),
1141
+ "@sidecar-ai/core": path4.join(repoRoot, "packages", "core", "src", "index.ts"),
756
1142
  "@sidecar-ai/react": reactEntry,
757
- "@sidecar-ai/native": path3.join(repoRoot, "packages", "native", "src", "index.ts"),
758
- "@sidecar-ai/native/components": path3.join(repoRoot, "packages", "native", "src", "components", "index.tsx"),
759
- "@sidecar-ai/native/styles.css": path3.join(repoRoot, "packages", "native", "src", "styles.css")
1143
+ "@sidecar-ai/native": path4.join(repoRoot, "packages", "native", "src", "index.ts"),
1144
+ "@sidecar-ai/native/components": path4.join(repoRoot, "packages", "native", "src", "components", "index.tsx"),
1145
+ "@sidecar-ai/native/styles.css": path4.join(repoRoot, "packages", "native", "src", "styles.css")
760
1146
  };
761
1147
  }
762
1148
  function findSidecarRepoRoot(startDir) {
763
- let current = path3.resolve(startDir);
1149
+ let current = path4.resolve(startDir);
764
1150
  while (true) {
765
- if (existsSync2(path3.join(current, "packages", "core", "src", "index.ts"))) {
1151
+ if (existsSync3(path4.join(current, "packages", "core", "src", "index.ts"))) {
766
1152
  return current;
767
1153
  }
768
- const parent = path3.dirname(current);
1154
+ const parent = path4.dirname(current);
769
1155
  if (parent === current) {
770
1156
  return void 0;
771
1157
  }
@@ -773,13 +1159,13 @@ function findSidecarRepoRoot(startDir) {
773
1159
  }
774
1160
  }
775
1161
  async function prepareAppStyle(rootDir, cacheDir) {
776
- const sourceFile = path3.join(rootDir, "style.css");
777
- if (!existsSync2(sourceFile)) {
1162
+ const sourceFile = path4.join(rootDir, "style.css");
1163
+ if (!existsSync3(sourceFile)) {
778
1164
  return void 0;
779
1165
  }
780
1166
  const source = await readFile(sourceFile, "utf8");
781
1167
  const css = await processAppStyle(source, sourceFile);
782
- const outputFile = path3.join(cacheDir, "style.css");
1168
+ const outputFile = path4.join(cacheDir, "style.css");
783
1169
  await writeFile(outputFile, css);
784
1170
  return outputFile;
785
1171
  }
@@ -791,7 +1177,7 @@ async function processAppStyle(source, from) {
791
1177
  const plugins = [];
792
1178
  if (cssSource.includes("@tailwind")) {
793
1179
  const tailwind = await import("@tailwindcss/postcss");
794
- plugins.push(tailwind.default({ base: path3.dirname(from) }));
1180
+ plugins.push(tailwind.default({ base: path4.dirname(from) }));
795
1181
  }
796
1182
  const autoprefixer = await import("autoprefixer");
797
1183
  plugins.push(autoprefixer.default());
@@ -805,13 +1191,13 @@ function needsPostcss(source) {
805
1191
  return /@import\s+["']tailwindcss["']|@tailwind|@source|@theme|@plugin/.test(source);
806
1192
  }
807
1193
  function findWidget(rootDir, toolFile, id, target = "mcp", toolVariant = "shared") {
808
- const selected = selectWidgetFile(path3.dirname(toolFile), target, toolVariant);
1194
+ const selected = selectWidgetFile(path4.dirname(toolFile), target, toolVariant);
809
1195
  if (!selected) {
810
1196
  return void 0;
811
1197
  }
812
1198
  const safeId = safePathSegment(id);
813
1199
  return {
814
- sourceFile: path3.relative(rootDir, selected.filePath),
1200
+ sourceFile: path4.relative(rootDir, selected.filePath),
815
1201
  variant: selected.variant,
816
1202
  resourceUri: `ui://${safeId}/widget.html`
817
1203
  };
@@ -846,7 +1232,8 @@ function widgetMeta(resourceUri, options = {}, target = "mcp") {
846
1232
  const standard = {
847
1233
  ui: {
848
1234
  resourceUri
849
- }
1235
+ },
1236
+ "ui/resourceUri": resourceUri
850
1237
  };
851
1238
  if (target !== "chatgpt") {
852
1239
  return standard;
@@ -862,7 +1249,7 @@ function widgetMeta(resourceUri, options = {}, target = "mcp") {
862
1249
  function widgetResourceMeta(options = {}, target = "mcp") {
863
1250
  const csp = stripUndefined3({
864
1251
  connectDomains: options.csp?.connectDomains ? [...options.csp.connectDomains] : [],
865
- resourceDomains: options.csp?.resourceDomains ? [...options.csp.resourceDomains] : [],
1252
+ resourceDomains: widgetResourceDomains(options, target),
866
1253
  frameDomains: options.csp?.frameDomains ? [...options.csp.frameDomains] : void 0,
867
1254
  baseUriDomains: options.csp?.baseUriDomains ? [...options.csp.baseUriDomains] : void 0
868
1255
  });
@@ -875,6 +1262,13 @@ function widgetResourceMeta(options = {}, target = "mcp") {
875
1262
  });
876
1263
  return Object.keys(ui).length ? { ui } : void 0;
877
1264
  }
1265
+ function widgetResourceDomains(options, target) {
1266
+ const domains = [...options.csp?.resourceDomains ?? []];
1267
+ if (target === "claude" && !domains.includes(CLAUDE_FONT_RESOURCE_DOMAIN)) {
1268
+ domains.push(CLAUDE_FONT_RESOURCE_DOMAIN);
1269
+ }
1270
+ return domains;
1271
+ }
878
1272
  function findWidgetDefinition(sourceFile) {
879
1273
  const call = resolveDefaultExportCall(sourceFile, "widget");
880
1274
  if (!call) {
@@ -986,17 +1380,17 @@ function readStringArrayProperty(definition, propertyName) {
986
1380
  }
987
1381
  function selectWidgetFile(directory, target, toolVariant) {
988
1382
  if (toolVariant === "openai") {
989
- const filePath = path3.join(directory, "widget.openai.tsx");
990
- return existsSync2(filePath) ? { filePath, variant: "openai" } : void 0;
1383
+ const filePath = path4.join(directory, "widget.openai.tsx");
1384
+ return existsSync3(filePath) ? { filePath, variant: "openai" } : void 0;
991
1385
  }
992
1386
  if (toolVariant === "anthropic") {
993
- const filePath = path3.join(directory, "widget.anthropic.tsx");
994
- return existsSync2(filePath) ? { filePath, variant: "anthropic" } : void 0;
1387
+ const filePath = path4.join(directory, "widget.anthropic.tsx");
1388
+ return existsSync3(filePath) ? { filePath, variant: "anthropic" } : void 0;
995
1389
  }
996
1390
  const candidates = target === "chatgpt" || target === "claude" || target === "mcp" ? [{ name: "widget.tsx", variant: "shared" }] : [];
997
1391
  for (const candidate of candidates) {
998
- const filePath = path3.join(directory, candidate.name);
999
- if (existsSync2(filePath)) {
1392
+ const filePath = path4.join(directory, candidate.name);
1393
+ if (existsSync3(filePath)) {
1000
1394
  return { filePath, variant: candidate.variant };
1001
1395
  }
1002
1396
  }
@@ -1047,17 +1441,23 @@ function stripUndefined3(value) {
1047
1441
  // packages/compiler/src/analyze.ts
1048
1442
  async function analyzeProjectTools(rootDir, options = {}) {
1049
1443
  const target = options.target ?? "mcp";
1050
- const toolFiles = await findToolFiles(path4.join(rootDir, "server"), target);
1444
+ const toolFiles = await findToolFiles(path5.join(rootDir, "server"), target);
1051
1445
  if (toolFiles.length === 0) {
1052
1446
  return [];
1053
1447
  }
1054
1448
  const project = createProject(rootDir);
1055
1449
  const authScopes = readAuthScopeCatalog(project, rootDir);
1056
- return toolFiles.map(
1057
- (candidate) => analyzeToolFile(project.addSourceFileAtPath(candidate.filePath), rootDir, {
1450
+ const sources = toolFiles.map((candidate) => ({
1451
+ candidate,
1452
+ sourceFile: project.addSourceFileAtPath(candidate.filePath)
1453
+ }));
1454
+ const runtimeInputSchemas = await readRuntimeInputSchemas(rootDir, sources);
1455
+ return sources.map(
1456
+ ({ candidate, sourceFile }) => analyzeToolFile(sourceFile, rootDir, {
1058
1457
  target,
1059
1458
  variant: candidate.variant,
1060
- authScopes
1459
+ authScopes,
1460
+ inputSchema: runtimeInputSchemas.get(candidate.filePath)
1061
1461
  })
1062
1462
  );
1063
1463
  }
@@ -1066,7 +1466,7 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1066
1466
  const variant = options.variant ?? "shared";
1067
1467
  const definition = findToolDefinition(sourceFile);
1068
1468
  const absoluteFile = sourceFile.getFilePath();
1069
- const directory = path4.basename(path4.dirname(absoluteFile));
1469
+ const directory = path5.basename(path5.dirname(absoluteFile));
1070
1470
  const name = getRequiredStringProperty(definition, "name", sourceFile);
1071
1471
  const id = getOptionalStringProperty(definition, "id") ?? toMachineName(directory);
1072
1472
  const description = getRequiredStringProperty(
@@ -1079,7 +1479,7 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1079
1479
  const hosts = readHosts(definition);
1080
1480
  const auth = readAuthPolicy(definition, options.authScopes ?? {});
1081
1481
  const execute = getExecuteFunction(definition, sourceFile);
1082
- const inputSchema = getParamsSchema(definition, execute);
1482
+ const inputSchema = options.inputSchema ?? getParamsSchema(definition, execute);
1083
1483
  const outputSchema = getOutputSchema(definition, execute);
1084
1484
  const descriptor = createToolDescriptor({
1085
1485
  name,
@@ -1097,7 +1497,7 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1097
1497
  validateWidgetHierarchy(sourceFile, absoluteFile, target, variant);
1098
1498
  const widget = findWidget(rootDir, absoluteFile, id, target, variant);
1099
1499
  if (widget) {
1100
- const widgetFile = path4.join(rootDir, widget.sourceFile);
1500
+ const widgetFile = path5.join(rootDir, widget.sourceFile);
1101
1501
  const project = sourceFile.getProject();
1102
1502
  const widgetSourceFile = project.getSourceFile(widgetFile) ?? project.addSourceFileAtPath(widgetFile);
1103
1503
  widget.options = {
@@ -1108,7 +1508,7 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1108
1508
  descriptor._meta = mergeWidgetMeta(descriptor._meta, widgetMeta(widget.resourceUri, widget.options, target));
1109
1509
  }
1110
1510
  return {
1111
- sourceFile: path4.relative(rootDir, absoluteFile),
1511
+ sourceFile: path5.relative(rootDir, absoluteFile),
1112
1512
  variant,
1113
1513
  target,
1114
1514
  directory,
@@ -1123,6 +1523,26 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1123
1523
  descriptor
1124
1524
  };
1125
1525
  }
1526
+ async function readRuntimeInputSchemas(rootDir, sources) {
1527
+ const schemas = /* @__PURE__ */ new Map();
1528
+ await Promise.all(sources.map(async ({ candidate, sourceFile }) => {
1529
+ const definition = findToolDefinition(sourceFile);
1530
+ if (!definitionHasRuntimeParams(definition)) {
1531
+ return;
1532
+ }
1533
+ const schema = await readRuntimeToolInputSchema(rootDir, candidate.filePath);
1534
+ if (schema) {
1535
+ schemas.set(candidate.filePath, schema);
1536
+ }
1537
+ }));
1538
+ return schemas;
1539
+ }
1540
+ function definitionHasRuntimeParams(definition) {
1541
+ if (definition.getProperty("params")) {
1542
+ return true;
1543
+ }
1544
+ return Boolean(getWithParamsCall(definition));
1545
+ }
1126
1546
  function readAuthPolicy(definition, authScopes) {
1127
1547
  const initializer = readObjectProperty2(definition, "auth");
1128
1548
  if (!initializer) {
@@ -1139,7 +1559,7 @@ function readAuthPolicy(definition, authScopes) {
1139
1559
  return { authenticated: true };
1140
1560
  }
1141
1561
  function readAuthScopeCatalog(project, rootDir) {
1142
- const authPath = path4.join(rootDir, "auth.ts");
1562
+ const authPath = path5.join(rootDir, "auth.ts");
1143
1563
  if (!existsSyncSafe(authPath)) {
1144
1564
  return {};
1145
1565
  }
@@ -1221,12 +1641,12 @@ function isNamedCall(call, name) {
1221
1641
  return callee === name || callee.endsWith(`.${name}`);
1222
1642
  }
1223
1643
  function validateWidgetHierarchy(sourceFile, toolFile, target, variant) {
1224
- const directory = path4.dirname(toolFile);
1644
+ const directory = path5.dirname(toolFile);
1225
1645
  const platformWidget = target === "chatgpt" ? "widget.openai.tsx" : target === "claude" ? "widget.anthropic.tsx" : void 0;
1226
1646
  if (!platformWidget || variant !== "shared") {
1227
1647
  return;
1228
1648
  }
1229
- if (existsSyncSafe(path4.join(directory, platformWidget))) {
1649
+ if (existsSyncSafe(path5.join(directory, platformWidget))) {
1230
1650
  const expectedTool = target === "chatgpt" ? "tool.openai.ts" : "tool.anthropic.ts";
1231
1651
  throw new CompilerError(
1232
1652
  sourceFile,
@@ -1241,7 +1661,7 @@ async function findToolFiles(serverDir, target) {
1241
1661
  const entries = await readdir(serverDir, { withFileTypes: true });
1242
1662
  const files = [];
1243
1663
  for (const entry of entries) {
1244
- const entryPath = path4.join(serverDir, entry.name);
1664
+ const entryPath = path5.join(serverDir, entry.name);
1245
1665
  if (entry.isDirectory()) {
1246
1666
  const candidate = selectToolFile(entryPath, target);
1247
1667
  if (candidate) {
@@ -1260,7 +1680,7 @@ function selectToolFile(directory, target) {
1260
1680
  { name: "tool.ts", variant: "shared" }
1261
1681
  ] : [{ name: "tool.ts", variant: "shared" }];
1262
1682
  for (const candidate of candidates) {
1263
- const filePath = path4.join(directory, candidate.name);
1683
+ const filePath = path5.join(directory, candidate.name);
1264
1684
  if (existsSyncSafe(filePath)) {
1265
1685
  return { filePath, variant: candidate.variant };
1266
1686
  }
@@ -1296,16 +1716,32 @@ function getExecuteFunction(definition, sourceFile) {
1296
1716
  return property;
1297
1717
  }
1298
1718
  if (Node4.isPropertyAssignment(property)) {
1299
- const initializer = property.getInitializer();
1719
+ const initializer = unwrapExpression(property.getInitializer());
1300
1720
  if (initializer && (Node4.isArrowFunction(initializer) || Node4.isFunctionExpression(initializer))) {
1301
1721
  return initializer;
1302
1722
  }
1723
+ const withParamsCall = getWithParamsCall(definition);
1724
+ const wrappedExecute = unwrapExpression(withParamsCall?.getArguments()[1]);
1725
+ if (wrappedExecute && (Node4.isArrowFunction(wrappedExecute) || Node4.isFunctionExpression(wrappedExecute))) {
1726
+ return wrappedExecute;
1727
+ }
1303
1728
  }
1304
1729
  throw new CompilerError(
1305
1730
  sourceFile,
1306
1731
  "execute must be a method, function expression, or arrow function."
1307
1732
  );
1308
1733
  }
1734
+ function getWithParamsCall(definition) {
1735
+ const property = definition.getProperty("execute");
1736
+ if (!property || !Node4.isPropertyAssignment(property)) {
1737
+ return void 0;
1738
+ }
1739
+ const initializer = unwrapExpression(property.getInitializer());
1740
+ if (!initializer || !Node4.isCallExpression(initializer)) {
1741
+ return void 0;
1742
+ }
1743
+ return isNamedCall(initializer, "withParams") ? initializer : void 0;
1744
+ }
1309
1745
  function getRequiredStringProperty(definition, propertyName, sourceFile) {
1310
1746
  const value = getOptionalStringProperty(definition, propertyName);
1311
1747
  if (value === void 0 || !value.trim()) {
@@ -1460,17 +1896,18 @@ function readStringArrayProperty2(definition, propertyName) {
1460
1896
  }
1461
1897
 
1462
1898
  // packages/compiler/src/build.ts
1463
- import { mkdir as mkdir8, writeFile as writeFile8 } from "fs/promises";
1464
- import path17 from "path";
1899
+ import { mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
1900
+ import { existsSync as existsSync7 } from "fs";
1901
+ import path19 from "path";
1465
1902
 
1466
1903
  // packages/compiler/src/config.ts
1467
- import path5 from "path";
1904
+ import path6 from "path";
1468
1905
  import {
1469
1906
  Node as Node5,
1470
1907
  SyntaxKind as SyntaxKind3
1471
1908
  } from "ts-morph";
1472
1909
  function analyzeProjectConfig(rootDir) {
1473
- const configPath = path5.join(rootDir, "sidecar.config.ts");
1910
+ const configPath = path6.join(rootDir, "sidecar.config.ts");
1474
1911
  if (!existsSyncSafe(configPath)) {
1475
1912
  return defaultCompilerConfig();
1476
1913
  }
@@ -1482,6 +1919,13 @@ function analyzeProjectConfig(rootDir) {
1482
1919
  return defaultCompilerConfig();
1483
1920
  }
1484
1921
  return {
1922
+ build: {
1923
+ target: readTargetNested(definition, "build", "target"),
1924
+ host: readHostNested(definition, "build", "host"),
1925
+ outDir: readStringNested(definition, "build", "outDir"),
1926
+ plugins: readBooleanNested(definition, "build", "plugins"),
1927
+ widgets: readWidgetBuildConfig(definition)
1928
+ },
1485
1929
  resources: {
1486
1930
  subscribe: readBooleanNested(definition, "resources", "subscribe") ?? false,
1487
1931
  listChanged: readBooleanNested(definition, "resources", "listChanged") ?? false
@@ -1493,13 +1937,56 @@ function analyzeProjectConfig(rootDir) {
1493
1937
  listChanged: readBooleanNested(definition, "tools", "listChanged") ?? false
1494
1938
  },
1495
1939
  pagination: {
1496
- pageSize: readNumberNested(definition, "pagination", "pageSize") ?? 10,
1940
+ pageSize: readNumberNested(definition, "pagination", "pageSize") ?? 50,
1497
1941
  hasOverride: hasProperty(readObjectProperty3(definition, "pagination"), "override")
1942
+ },
1943
+ codeMode: readCodeModeConfig(definition),
1944
+ remoteExecution: {
1945
+ enabled: readBooleanProperty3(definition, "remoteExecution") ?? false
1498
1946
  }
1499
1947
  };
1500
1948
  }
1949
+ function readWidgetBuildConfig(definition) {
1950
+ const widgets = readObjectProperty3(readObjectProperty3(definition, "build"), "widgets");
1951
+ if (!widgets) {
1952
+ return void 0;
1953
+ }
1954
+ const config = {};
1955
+ const configure = readStringProperty3(widgets, "configure");
1956
+ const esbuild3 = readWidgetEsbuildConfig(widgets);
1957
+ if (configure) config.configure = configure;
1958
+ if (esbuild3) config.esbuild = esbuild3;
1959
+ return Object.keys(config).length ? config : void 0;
1960
+ }
1961
+ function readWidgetEsbuildConfig(widgets) {
1962
+ const esbuild3 = readObjectProperty3(widgets, "esbuild");
1963
+ if (!esbuild3) {
1964
+ return void 0;
1965
+ }
1966
+ const config = {};
1967
+ const alias = readStringRecordProperty(esbuild3, "alias");
1968
+ const define = readStringRecordProperty(esbuild3, "define");
1969
+ const external = readStringArrayProperty3(esbuild3, "external");
1970
+ const loader = readStringRecordProperty(esbuild3, "loader");
1971
+ const conditions = readStringArrayProperty3(esbuild3, "conditions");
1972
+ const mainFields = readStringArrayProperty3(esbuild3, "mainFields");
1973
+ const jsx = readStringProperty3(esbuild3, "jsx");
1974
+ const jsxImportSource = readStringProperty3(esbuild3, "jsxImportSource");
1975
+ if (alias) config.alias = alias;
1976
+ if (define) config.define = define;
1977
+ if (external) config.external = external;
1978
+ if (loader) config.loader = loader;
1979
+ if (conditions) config.conditions = conditions;
1980
+ if (mainFields) config.mainFields = mainFields;
1981
+ if (jsx === "automatic" || jsx === "transform" || jsx === "preserve") {
1982
+ config.jsx = jsx;
1983
+ }
1984
+ if (jsxImportSource) config.jsxImportSource = jsxImportSource;
1985
+ return Object.keys(config).length ? config : void 0;
1986
+ }
1501
1987
  function defaultCompilerConfig() {
1502
1988
  return {
1989
+ build: {},
1503
1990
  resources: {
1504
1991
  subscribe: false,
1505
1992
  listChanged: false
@@ -1511,17 +1998,101 @@ function defaultCompilerConfig() {
1511
1998
  listChanged: false
1512
1999
  },
1513
2000
  pagination: {
1514
- pageSize: 10,
2001
+ pageSize: 50,
1515
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
1516
2014
  }
1517
2015
  };
1518
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
+ }
2072
+ function readTargetNested(definition, section, propertyName) {
2073
+ const value = readStringNested(definition, section, propertyName);
2074
+ return value === "mcp" || value === "chatgpt" || value === "claude" ? value : void 0;
2075
+ }
2076
+ function readHostNested(definition, section, propertyName) {
2077
+ const value = readStringNested(definition, section, propertyName);
2078
+ return value === "node" || value === "vercel" ? value : void 0;
2079
+ }
2080
+ function readStringNested(definition, section, propertyName) {
2081
+ const object = readObjectProperty3(definition, section);
2082
+ if (!object) {
2083
+ return void 0;
2084
+ }
2085
+ return readStringProperty3(object, propertyName);
2086
+ }
1519
2087
  function readBooleanNested(definition, section, propertyName) {
1520
2088
  const object = readObjectProperty3(definition, section);
1521
2089
  if (!object) {
1522
2090
  return void 0;
1523
2091
  }
1524
- const property = object.getProperty(propertyName);
2092
+ return readBooleanProperty3(object, propertyName);
2093
+ }
2094
+ function readBooleanProperty3(definition, propertyName) {
2095
+ const property = definition.getProperty(propertyName);
1525
2096
  if (!property || !Node5.isPropertyAssignment(property)) {
1526
2097
  return void 0;
1527
2098
  }
@@ -1546,6 +2117,9 @@ function readNumberNested(definition, section, propertyName) {
1546
2117
  return initializer && Node5.isNumericLiteral(initializer) ? Number(initializer.getLiteralText()) : void 0;
1547
2118
  }
1548
2119
  function readObjectProperty3(definition, propertyName) {
2120
+ if (!definition) {
2121
+ return void 0;
2122
+ }
1549
2123
  const property = definition.getProperty(propertyName);
1550
2124
  if (!property || !Node5.isPropertyAssignment(property)) {
1551
2125
  return void 0;
@@ -1553,38 +2127,79 @@ function readObjectProperty3(definition, propertyName) {
1553
2127
  const initializer = unwrapExpression(property.getInitializer());
1554
2128
  return initializer && Node5.isObjectLiteralExpression(initializer) ? initializer : void 0;
1555
2129
  }
2130
+ function readStringProperty3(definition, propertyName) {
2131
+ const property = definition.getProperty(propertyName);
2132
+ if (!property || !Node5.isPropertyAssignment(property)) {
2133
+ return void 0;
2134
+ }
2135
+ const initializer = unwrapExpression(property.getInitializer());
2136
+ return initializer && Node5.isStringLiteral(initializer) ? initializer.getLiteralText() : void 0;
2137
+ }
2138
+ function readStringArrayProperty3(definition, propertyName) {
2139
+ const property = definition.getProperty(propertyName);
2140
+ if (!property || !Node5.isPropertyAssignment(property)) {
2141
+ return void 0;
2142
+ }
2143
+ const initializer = unwrapExpression(property.getInitializer());
2144
+ if (!initializer || !Node5.isArrayLiteralExpression(initializer)) {
2145
+ return void 0;
2146
+ }
2147
+ const values = initializer.getElements().map((element) => {
2148
+ const unwrapped = unwrapExpression(element);
2149
+ return unwrapped && Node5.isStringLiteral(unwrapped) ? unwrapped.getLiteralText() : void 0;
2150
+ });
2151
+ return values.every((value) => value !== void 0) ? values : void 0;
2152
+ }
2153
+ function readStringRecordProperty(definition, propertyName) {
2154
+ const object = readObjectProperty3(definition, propertyName);
2155
+ if (!object) {
2156
+ return void 0;
2157
+ }
2158
+ const record = {};
2159
+ for (const property of object.getProperties()) {
2160
+ if (!Node5.isPropertyAssignment(property)) {
2161
+ return void 0;
2162
+ }
2163
+ const initializer = unwrapExpression(property.getInitializer());
2164
+ if (!initializer || !Node5.isStringLiteral(initializer)) {
2165
+ return void 0;
2166
+ }
2167
+ record[property.getName().replace(/^["']|["']$/g, "")] = initializer.getLiteralText();
2168
+ }
2169
+ return record;
2170
+ }
1556
2171
  function hasProperty(definition, propertyName) {
1557
2172
  return Boolean(definition?.getProperty(propertyName));
1558
2173
  }
1559
2174
 
1560
2175
  // packages/compiler/src/diagnostics.ts
1561
- import { existsSync as existsSync3 } from "fs";
2176
+ import { existsSync as existsSync4 } from "fs";
1562
2177
  import { readFile as readFile2 } from "fs/promises";
1563
- import path6 from "path";
2178
+ import path7 from "path";
1564
2179
  async function collectProjectDiagnostics(rootDir, input) {
1565
2180
  const diagnostics = [];
1566
2181
  const tools = Array.isArray(input) ? input : input.tools;
1567
2182
  const resources = Array.isArray(input) ? [] : input.resources ?? [];
1568
2183
  const prompts = Array.isArray(input) ? [] : input.prompts ?? [];
1569
2184
  const config = Array.isArray(input) ? void 0 : input.config;
1570
- const hasAuthConfig = existsSync3(path6.join(rootDir, "auth.ts"));
2185
+ const hasAuthConfig = existsSync4(path7.join(rootDir, "auth.ts"));
1571
2186
  for (const entry of tools) {
1572
- const toolPath = path6.join(rootDir, entry.sourceFile);
2187
+ const toolPath = path7.join(rootDir, entry.sourceFile);
1573
2188
  const toolSource = await readFile2(toolPath, "utf8");
1574
- diagnostics.push(...diagnoseToolSource(rootDir, entry, toolSource, hasAuthConfig));
2189
+ diagnostics.push(...diagnoseToolSource(entry, toolSource, hasAuthConfig));
1575
2190
  if (entry.widget) {
1576
- const widgetPath = path6.join(rootDir, entry.widget.sourceFile);
2191
+ const widgetPath = path7.join(rootDir, entry.widget.sourceFile);
1577
2192
  const widgetSource = await readFile2(widgetPath, "utf8");
1578
2193
  diagnostics.push(...diagnoseWidgetSource(rootDir, entry, widgetSource));
1579
2194
  }
1580
2195
  }
1581
2196
  for (const entry of resources) {
1582
- const resourcePath = path6.join(rootDir, entry.sourceFile);
2197
+ const resourcePath = path7.join(rootDir, entry.sourceFile);
1583
2198
  const resourceSource = await readFile2(resourcePath, "utf8");
1584
2199
  diagnostics.push(...diagnoseResourceSource(entry, resourceSource));
1585
2200
  }
1586
2201
  for (const entry of prompts) {
1587
- const promptPath = path6.join(rootDir, entry.sourceFile);
2202
+ const promptPath = path7.join(rootDir, entry.sourceFile);
1588
2203
  const promptSource = await readFile2(promptPath, "utf8");
1589
2204
  diagnostics.push(...diagnosePromptSource(entry, promptSource));
1590
2205
  }
@@ -1599,7 +2214,7 @@ function formatDiagnostic(diagnostic) {
1599
2214
  hint: ${diagnostic.hint}` : "";
1600
2215
  return `${location} - ${diagnostic.severity} ${diagnostic.code}: ${diagnostic.message}${hint}`;
1601
2216
  }
1602
- function diagnoseToolSource(rootDir, entry, source, hasAuthConfig) {
2217
+ function diagnoseToolSource(entry, source, hasAuthConfig) {
1603
2218
  const diagnostics = [];
1604
2219
  const toolLocation = locate(source, "tool({");
1605
2220
  if (!entry.description.trim().startsWith("Use this when")) {
@@ -1816,21 +2431,21 @@ function isIgnored(source, code) {
1816
2431
 
1817
2432
  // packages/compiler/src/generated.ts
1818
2433
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
1819
- import path7 from "path";
2434
+ import path8 from "path";
1820
2435
  async function writeGeneratedTypes(rootDir, tools) {
1821
- const generatedDir = path7.join(rootDir, ".sidecar", "generated");
2436
+ const generatedDir = path8.join(rootDir, ".sidecar", "generated");
1822
2437
  await mkdir2(generatedDir, { recursive: true });
1823
2438
  const appCallableTools = tools.filter(isAppCallable);
1824
2439
  const methodNames = uniqueMethodNames(appCallableTools.map((entry) => toIdentifier(entry.id)));
1825
2440
  const imports = appCallableTools.map(
1826
- (entry, index) => `import type tool${index} from ${JSON.stringify(toImportSpecifier(generatedDir, path7.join(rootDir, entry.sourceFile), { extension: "js" }))};`
2441
+ (entry, index) => `import type tool${index} from ${JSON.stringify(toImportSpecifier(generatedDir, path8.join(rootDir, entry.sourceFile), { extension: "js" }))};`
1827
2442
  ).join("\n");
1828
2443
  const ids = appCallableTools.map((entry, index) => ` ${methodNames[index]}: ${JSON.stringify(entry.id)}`).join(",\n");
1829
2444
  const toolTypes = appCallableTools.map(
1830
- (entry, index) => ` ${methodNames[index]}(params: ToolParams<typeof tool${index}>): Promise<ToolOutput<typeof tool${index}>>;`
2445
+ (_entry, index) => ` ${methodNames[index]}(params: ToolParams<typeof tool${index}>): Promise<ToolOutput<typeof tool${index}>>;`
1831
2446
  ).join("\n");
1832
2447
  await writeFile2(
1833
- path7.join(generatedDir, "tools.ts"),
2448
+ path8.join(generatedDir, "tools.ts"),
1834
2449
  `/**
1835
2450
  * Generated Sidecar widget tool client.
1836
2451
  *
@@ -1878,19 +2493,15 @@ function uniqueMethodNames(names) {
1878
2493
  });
1879
2494
  }
1880
2495
 
1881
- // packages/compiler/src/plugins.ts
1882
- import { mkdir as mkdir7, writeFile as writeFile7 } from "fs/promises";
1883
- import path14 from "path";
1884
-
1885
2496
  // packages/compiler/src/identity.ts
1886
2497
  import { readFile as readFile3 } from "fs/promises";
1887
- import path8 from "path";
2498
+ import path9 from "path";
1888
2499
  async function loadProjectIdentity(rootDir) {
1889
- const packageJson = await readOptionalJson(path8.join(rootDir, "package.json"));
2500
+ const packageJson = await readOptionalJson(path9.join(rootDir, "package.json"));
1890
2501
  const configText = await readOptionalText(
1891
- path8.join(rootDir, "sidecar.config.ts")
2502
+ path9.join(rootDir, "sidecar.config.ts")
1892
2503
  );
1893
- const name = readConfigString(configText, "name") ?? packageJson?.name ?? path8.basename(rootDir);
2504
+ const name = readConfigString(configText, "name") ?? packageJson?.name ?? path9.basename(rootDir);
1894
2505
  const version = readConfigString(configText, "version") ?? packageJson?.version ?? "0.0.0-dev";
1895
2506
  const description = readConfigString(configText, "description") ?? packageJson?.description ?? `${name} Sidecar app.`;
1896
2507
  return {
@@ -1922,30 +2533,34 @@ function readConfigString(configText, key) {
1922
2533
  return match?.[1];
1923
2534
  }
1924
2535
 
2536
+ // packages/compiler/src/plugins.ts
2537
+ import { mkdir as mkdir7, writeFile as writeFile7 } from "fs/promises";
2538
+ import path15 from "path";
2539
+
1925
2540
  // packages/compiler/src/plugin-components/agents.ts
1926
2541
  import { mkdir as mkdir3, readdir as readdir2, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
1927
- import path9 from "path";
2542
+ import path10 from "path";
1928
2543
  async function emitClaudeAgents(rootDir, destination) {
1929
- const source = path9.join(rootDir, "agents");
2544
+ const source = path10.join(rootDir, "agents");
1930
2545
  if (!existsSyncSafe(source)) {
1931
2546
  return;
1932
2547
  }
1933
2548
  const entries = await readdir2(source, { withFileTypes: true });
1934
2549
  const agentDirs = entries.filter(
1935
- (entry) => entry.isDirectory() && existsSyncSafe(path9.join(source, entry.name, "agent.ts"))
2550
+ (entry) => entry.isDirectory() && existsSyncSafe(path10.join(source, entry.name, "agent.ts"))
1936
2551
  );
1937
2552
  if (!agentDirs.length) {
1938
2553
  return;
1939
2554
  }
1940
2555
  await mkdir3(destination, { recursive: true });
1941
2556
  for (const entry of agentDirs) {
1942
- const sourceText = await readFile4(path9.join(source, entry.name, "agent.ts"), "utf8");
2557
+ const sourceText = await readFile4(path10.join(source, entry.name, "agent.ts"), "utf8");
1943
2558
  const markdown = parseClaudeAgent(
1944
2559
  sourceText,
1945
2560
  entry.name
1946
2561
  );
1947
2562
  const agentName = readObjectString(sourceText, "name") ?? entry.name;
1948
- await writeFile3(path9.join(destination, `${safeFileStem(agentName)}.md`), markdown);
2563
+ await writeFile3(path10.join(destination, `${safeFileStem(agentName)}.md`), markdown);
1949
2564
  }
1950
2565
  }
1951
2566
  function parseClaudeAgent(source, fallbackName) {
@@ -1974,41 +2589,41 @@ ${prompt.trim()}
1974
2589
 
1975
2590
  // packages/compiler/src/plugin-components/commands.ts
1976
2591
  import { cp, lstat, mkdir as mkdir4, readdir as readdir3, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
1977
- import path10 from "path";
2592
+ import path11 from "path";
1978
2593
  async function copyCommands(rootDir, destination) {
1979
- const source = path10.join(rootDir, "commands");
2594
+ const source = path11.join(rootDir, "commands");
1980
2595
  if (!existsSyncSafe(source)) {
1981
2596
  return;
1982
2597
  }
1983
2598
  await mkdir4(destination, { recursive: true });
1984
2599
  const entries = await readdir3(source, { withFileTypes: true });
1985
2600
  for (const entry of entries) {
1986
- const sourcePath = path10.join(source, entry.name);
2601
+ const sourcePath = path11.join(source, entry.name);
1987
2602
  if (entry.isFile() && entry.name.endsWith(".md")) {
1988
2603
  if (await safeCommandCopyFilter(sourcePath)) {
1989
- await cp(sourcePath, path10.join(destination, safeFileStem(entry.name.replace(/\.md$/, "")) + ".md"));
2604
+ await cp(sourcePath, path11.join(destination, safeFileStem(entry.name.replace(/\.md$/, "")) + ".md"));
1990
2605
  }
1991
2606
  continue;
1992
2607
  }
1993
2608
  if (!entry.isDirectory()) {
1994
2609
  continue;
1995
2610
  }
1996
- const dynamicCommand = path10.join(sourcePath, "command.ts");
2611
+ const dynamicCommand = path11.join(sourcePath, "command.ts");
1997
2612
  if (existsSyncSafe(dynamicCommand)) {
1998
2613
  const sourceText = await readFile5(dynamicCommand, "utf8");
1999
2614
  const markdown = parseDynamicCommand(sourceText, entry.name);
2000
2615
  const commandName = readObjectString(sourceText, "name") ?? entry.name;
2001
- await writeFile4(path10.join(destination, `${safeFileStem(commandName)}.md`), markdown);
2616
+ await writeFile4(path11.join(destination, `${safeFileStem(commandName)}.md`), markdown);
2002
2617
  continue;
2003
2618
  }
2004
- await cp(sourcePath, path10.join(destination, entry.name), {
2619
+ await cp(sourcePath, path11.join(destination, entry.name), {
2005
2620
  recursive: true,
2006
2621
  filter: safeCommandCopyFilter
2007
2622
  });
2008
2623
  }
2009
2624
  }
2010
2625
  async function safeCommandCopyFilter(sourcePath) {
2011
- const basename = path10.basename(sourcePath);
2626
+ const basename = path11.basename(sourcePath);
2012
2627
  if (basename === ".env" || basename.startsWith(".env.") || basename === "node_modules" || basename === ".git") {
2013
2628
  return false;
2014
2629
  }
@@ -2037,32 +2652,32 @@ ${Object.entries(frontmatter).map(([key, value]) => `${key}: ${yamlScalar(value)
2037
2652
 
2038
2653
  // packages/compiler/src/plugin-components/hooks.ts
2039
2654
  import { mkdir as mkdir5, readdir as readdir4, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
2040
- import path11 from "path";
2655
+ import path12 from "path";
2041
2656
  import {
2042
2657
  Node as Node6,
2043
2658
  Project as Project2,
2044
2659
  SyntaxKind as SyntaxKind4
2045
2660
  } from "ts-morph";
2046
2661
  async function copyHooks(rootDir, destination) {
2047
- const source = path11.join(rootDir, "hooks");
2662
+ const source = path12.join(rootDir, "hooks");
2048
2663
  if (!existsSyncSafe(source)) {
2049
2664
  return;
2050
2665
  }
2051
2666
  const entries = await readdir4(source, { withFileTypes: true });
2052
2667
  const hookDirs = entries.filter(
2053
- (entry) => entry.isDirectory() && existsSyncSafe(path11.join(source, entry.name, "hook.ts"))
2668
+ (entry) => entry.isDirectory() && existsSyncSafe(path12.join(source, entry.name, "hook.ts"))
2054
2669
  );
2055
2670
  if (!hookDirs.length) {
2056
2671
  return;
2057
2672
  }
2058
2673
  const config = {};
2059
2674
  for (const entry of hookDirs) {
2060
- const filePath = path11.join(source, entry.name, "hook.ts");
2675
+ const filePath = path12.join(source, entry.name, "hook.ts");
2061
2676
  mergeHooks(config, parseHookFile(await readFile6(filePath, "utf8"), entry.name));
2062
2677
  }
2063
2678
  await mkdir5(destination, { recursive: true });
2064
2679
  await writeFile5(
2065
- path11.join(destination, "hooks.json"),
2680
+ path12.join(destination, "hooks.json"),
2066
2681
  `${JSON.stringify(config, null, 2)}
2067
2682
  `
2068
2683
  );
@@ -2089,7 +2704,7 @@ function parseHookFile(source, fallbackName) {
2089
2704
  throw new Error(`hooks/${fallbackName}/hook.ts must default-export hook({ ... }) or hooks({ ... }).`);
2090
2705
  }
2091
2706
  function parseHookDefinition(definition, fallbackName) {
2092
- const event = readStringProperty3(definition, "event");
2707
+ const event = readStringProperty4(definition, "event");
2093
2708
  if (!event) {
2094
2709
  throw new Error(`hooks/${fallbackName}/hook.ts must declare an event string.`);
2095
2710
  }
@@ -2099,7 +2714,7 @@ function parseHookDefinition(definition, fallbackName) {
2099
2714
  }
2100
2715
  return {
2101
2716
  event,
2102
- matcher: readStringProperty3(definition, "matcher"),
2717
+ matcher: readStringProperty4(definition, "matcher"),
2103
2718
  run: hooks
2104
2719
  };
2105
2720
  }
@@ -2119,7 +2734,7 @@ function parseHooksDefinition(definition, fallbackName) {
2119
2734
  throw new Error(`hooks/${fallbackName}/hook.ts event "${event}" entries must be objects.`);
2120
2735
  }
2121
2736
  return stripUndefined2({
2122
- matcher: readStringProperty3(entry, "matcher"),
2737
+ matcher: readStringProperty4(entry, "matcher"),
2123
2738
  hooks: readHookArray(entry, "run", fallbackName)
2124
2739
  });
2125
2740
  });
@@ -2142,7 +2757,7 @@ function parseHookHandlers(handlers, fallbackName) {
2142
2757
  }
2143
2758
  function parseHookHandler(handler, fallbackName) {
2144
2759
  if (Node6.isObjectLiteralExpression(handler)) {
2145
- const type = readStringProperty3(handler, "type");
2760
+ const type = readStringProperty4(handler, "type");
2146
2761
  if (type !== "command" && type !== "http") {
2147
2762
  throw new Error(`hooks/${fallbackName}/hook.ts hook handlers need type "command" or "http".`);
2148
2763
  }
@@ -2192,7 +2807,7 @@ function objectLiteralToRecord(object) {
2192
2807
  }
2193
2808
  return stripUndefined2(record);
2194
2809
  }
2195
- function readStringProperty3(object, propertyName) {
2810
+ function readStringProperty4(object, propertyName) {
2196
2811
  const property = object.getProperty(propertyName);
2197
2812
  if (!property || !Node6.isPropertyAssignment(property)) {
2198
2813
  return void 0;
@@ -2236,7 +2851,7 @@ function mergeHooks(target, source) {
2236
2851
 
2237
2852
  // packages/compiler/src/plugin-components/passthrough.ts
2238
2853
  import { cp as cp2, lstat as lstat2 } from "fs/promises";
2239
- import path12 from "path";
2854
+ import path13 from "path";
2240
2855
  async function copyClaudePassthroughDirectories(rootDir, pluginDir) {
2241
2856
  for (const directory of [
2242
2857
  "assets",
@@ -2245,18 +2860,18 @@ async function copyClaudePassthroughDirectories(rootDir, pluginDir) {
2245
2860
  "output-styles",
2246
2861
  "themes"
2247
2862
  ]) {
2248
- const source = path12.join(rootDir, directory);
2863
+ const source = path13.join(rootDir, directory);
2249
2864
  if (!existsSyncSafe(source)) {
2250
2865
  continue;
2251
2866
  }
2252
- await cp2(source, path12.join(pluginDir, directory), {
2867
+ await cp2(source, path13.join(pluginDir, directory), {
2253
2868
  recursive: true,
2254
2869
  filter: safePluginCopyFilter
2255
2870
  });
2256
2871
  }
2257
2872
  }
2258
2873
  async function safePluginCopyFilter(sourcePath) {
2259
- const basename = path12.basename(sourcePath);
2874
+ const basename = path13.basename(sourcePath);
2260
2875
  if (basename === "node_modules" || basename === ".git" || basename === ".env" || basename.startsWith(".env.")) {
2261
2876
  return false;
2262
2877
  }
@@ -2265,11 +2880,11 @@ async function safePluginCopyFilter(sourcePath) {
2265
2880
  }
2266
2881
 
2267
2882
  // packages/compiler/src/plugin-components/skills.ts
2268
- import { existsSync as existsSync4 } from "fs";
2883
+ import { existsSync as existsSync5 } from "fs";
2269
2884
  import { cp as cp3, lstat as lstat3, mkdir as mkdir6, readdir as readdir5, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
2270
- import path13 from "path";
2885
+ import path14 from "path";
2271
2886
  async function copySkills(rootDir, destination) {
2272
- const source = path13.join(rootDir, "skills");
2887
+ const source = path14.join(rootDir, "skills");
2273
2888
  if (!existsSyncSafe(source)) {
2274
2889
  return;
2275
2890
  }
@@ -2279,27 +2894,27 @@ async function copySkills(rootDir, destination) {
2279
2894
  if (!entry.isDirectory()) {
2280
2895
  continue;
2281
2896
  }
2282
- const sourceDir = path13.join(source, entry.name);
2283
- const destinationDir = path13.join(destination, entry.name);
2284
- const staticSkill = path13.join(sourceDir, "SKILL.md");
2285
- const dynamicSkill = path13.join(sourceDir, "skill.ts");
2897
+ const sourceDir = path14.join(source, entry.name);
2898
+ const destinationDir = path14.join(destination, entry.name);
2899
+ const staticSkill = path14.join(sourceDir, "SKILL.md");
2900
+ const dynamicSkill = path14.join(sourceDir, "skill.ts");
2286
2901
  await mkdir6(destinationDir, { recursive: true });
2287
- if (existsSync4(staticSkill)) {
2902
+ if (existsSync5(staticSkill)) {
2288
2903
  await cp3(sourceDir, destinationDir, {
2289
2904
  recursive: true,
2290
- filter: async (sourcePath) => !sourcePath.endsWith(`${path13.sep}skill.ts`) && await safeSkillCopyFilter(sourcePath)
2905
+ filter: async (sourcePath) => !sourcePath.endsWith(`${path14.sep}skill.ts`) && await safeSkillCopyFilter(sourcePath)
2291
2906
  });
2292
- } else if (existsSync4(dynamicSkill)) {
2907
+ } else if (existsSync5(dynamicSkill)) {
2293
2908
  const generated = parseDynamicSkill(
2294
2909
  await readFile7(dynamicSkill, "utf8"),
2295
2910
  entry.name
2296
2911
  );
2297
- await writeFile6(path13.join(destinationDir, "SKILL.md"), generated);
2912
+ await writeFile6(path14.join(destinationDir, "SKILL.md"), generated);
2298
2913
  }
2299
2914
  }
2300
2915
  }
2301
2916
  async function safeSkillCopyFilter(sourcePath) {
2302
- const basename = path13.basename(sourcePath);
2917
+ const basename = path14.basename(sourcePath);
2303
2918
  if (basename === ".env" || basename.startsWith(".env.") || basename === "node_modules" || basename === ".git") {
2304
2919
  return false;
2305
2920
  }
@@ -2325,25 +2940,25 @@ async function buildPluginPackages(rootDir, outRoot, manifest) {
2325
2940
  await buildClaudePlugin(rootDir, outRoot, identity, manifest);
2326
2941
  }
2327
2942
  async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2328
- const pluginDir = path14.join(outRoot, "claude-plugin");
2329
- await mkdir7(path14.join(pluginDir, ".claude-plugin"), { recursive: true });
2330
- await copySkills(rootDir, path14.join(pluginDir, "skills"));
2331
- await copyCommands(rootDir, path14.join(pluginDir, "commands"));
2332
- await copyHooks(rootDir, path14.join(pluginDir, "hooks"));
2333
- await emitClaudeAgents(rootDir, path14.join(pluginDir, "agents"));
2943
+ const pluginDir = path15.join(outRoot, "claude-plugin");
2944
+ await mkdir7(path15.join(pluginDir, ".claude-plugin"), { recursive: true });
2945
+ await copySkills(rootDir, path15.join(pluginDir, "skills"));
2946
+ await copyCommands(rootDir, path15.join(pluginDir, "commands"));
2947
+ await copyHooks(rootDir, path15.join(pluginDir, "hooks"));
2948
+ await emitClaudeAgents(rootDir, path15.join(pluginDir, "agents"));
2334
2949
  await copyClaudePassthroughDirectories(rootDir, pluginDir);
2335
- await writeJson(path14.join(pluginDir, ".claude-plugin", "plugin.json"), {
2950
+ await writeJson(path15.join(pluginDir, ".claude-plugin", "plugin.json"), {
2336
2951
  name: identity.slug,
2337
2952
  version: identity.version,
2338
2953
  description: identity.description,
2339
2954
  displayName: identity.name,
2340
2955
  installationPreference: "available"
2341
2956
  });
2342
- await writeJson(path14.join(pluginDir, "version.json"), {
2957
+ await writeJson(path15.join(pluginDir, "version.json"), {
2343
2958
  version: identity.version,
2344
2959
  generatedBy: "sidecar"
2345
2960
  });
2346
- await writeJson(path14.join(pluginDir, ".mcp.json"), {
2961
+ await writeJson(path15.join(pluginDir, ".mcp.json"), {
2347
2962
  mcpServers: {
2348
2963
  [identity.slug]: {
2349
2964
  type: "http",
@@ -2351,7 +2966,7 @@ async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2351
2966
  }
2352
2967
  }
2353
2968
  });
2354
- await writeJson(path14.join(pluginDir, "settings.json"), {
2969
+ await writeJson(path15.join(pluginDir, "settings.json"), {
2355
2970
  sidecar: {
2356
2971
  tools: manifest.tools.map((entry) => entry.id),
2357
2972
  resources: manifest.resources.map((entry) => entry.uri),
@@ -2359,21 +2974,21 @@ async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2359
2974
  }
2360
2975
  });
2361
2976
  await writeFile7(
2362
- path14.join(pluginDir, "README.md"),
2977
+ path15.join(pluginDir, "README.md"),
2363
2978
  `# ${identity.name} Claude Plugin
2364
2979
 
2365
2980
  This package was generated by Sidecar.
2366
2981
 
2367
2982
  Set \`SIDECAR_MCP_URL\` to the hosted MCP endpoint for this app.
2368
2983
 
2369
- For local testing, run \`sidecar dev --tunnel\` and use the printed HTTPS MCP URL.
2984
+ For local testing, run \`sidecar dev --tunnel\` and use the validated HTTPS MCP URL it prints. Temporary quick tunnels are public and best-effort; use a configured tunnel/domain or deployed preview for repeatable testing.
2370
2985
 
2371
2986
  Claude/Cowork plugin-specific components such as skills, slash commands, agents, hooks, output styles, themes, monitors, assets, and bin scripts will be emitted here when the source project defines them.
2372
2987
  `
2373
2988
  );
2374
2989
  }
2375
2990
  async function writeJson(filePath, value) {
2376
- await mkdir7(path14.dirname(filePath), { recursive: true });
2991
+ await mkdir7(path15.dirname(filePath), { recursive: true });
2377
2992
  await writeFile7(
2378
2993
  filePath,
2379
2994
  `${JSON.stringify(stripUndefined2(value), null, 2)}
@@ -2383,13 +2998,13 @@ async function writeJson(filePath, value) {
2383
2998
 
2384
2999
  // packages/compiler/src/prompts.ts
2385
3000
  import { readdir as readdir6 } from "fs/promises";
2386
- import path15 from "path";
3001
+ import path16 from "path";
2387
3002
  import {
2388
3003
  Node as Node7,
2389
3004
  SyntaxKind as SyntaxKind5
2390
3005
  } from "ts-morph";
2391
3006
  async function analyzeProjectPrompts(rootDir) {
2392
- const promptFiles = await findPromptFiles(path15.join(rootDir, "prompts"));
3007
+ const promptFiles = await findPromptFiles(path16.join(rootDir, "prompts"));
2393
3008
  if (promptFiles.length === 0) {
2394
3009
  return [];
2395
3010
  }
@@ -2401,7 +3016,7 @@ async function analyzeProjectPrompts(rootDir) {
2401
3016
  function analyzePromptFile(sourceFile, rootDir) {
2402
3017
  const definition = findPromptDefinition(sourceFile);
2403
3018
  const absoluteFile = sourceFile.getFilePath();
2404
- const directory = path15.basename(path15.dirname(absoluteFile));
3019
+ const directory = path16.basename(path16.dirname(absoluteFile));
2405
3020
  const name = getOptionalStringProperty2(definition, "name") ?? safePathSegment(directory);
2406
3021
  const title = getRequiredStringProperty2(definition, "title", sourceFile);
2407
3022
  const description = getOptionalStringProperty2(definition, "description");
@@ -2417,7 +3032,7 @@ function analyzePromptFile(sourceFile, rootDir) {
2417
3032
  throw new CompilerError(sourceFile, "prompt({ ... }) must include a run method.");
2418
3033
  }
2419
3034
  return {
2420
- sourceFile: path15.relative(rootDir, absoluteFile),
3035
+ sourceFile: path16.relative(rootDir, absoluteFile),
2421
3036
  directory,
2422
3037
  name,
2423
3038
  title,
@@ -2436,7 +3051,7 @@ async function findPromptFiles(promptsDir) {
2436
3051
  if (!entry.isDirectory()) {
2437
3052
  continue;
2438
3053
  }
2439
- const filePath = path15.join(promptsDir, entry.name, "prompt.ts");
3054
+ const filePath = path16.join(promptsDir, entry.name, "prompt.ts");
2440
3055
  if (existsSyncSafe(filePath)) {
2441
3056
  files.push(filePath);
2442
3057
  }
@@ -2506,12 +3121,12 @@ function readArgInput(node) {
2506
3121
  if (Node7.isObjectLiteralExpression(initializer)) {
2507
3122
  return {
2508
3123
  description: getOptionalStringProperty2(initializer, "description"),
2509
- required: readBooleanProperty3(initializer, "required")
3124
+ required: readBooleanProperty4(initializer, "required")
2510
3125
  };
2511
3126
  }
2512
3127
  return void 0;
2513
3128
  }
2514
- function readBooleanProperty3(definition, propertyName) {
3129
+ function readBooleanProperty4(definition, propertyName) {
2515
3130
  const property = definition.getProperty(propertyName);
2516
3131
  if (!property || !Node7.isPropertyAssignment(property)) {
2517
3132
  return void 0;
@@ -2558,7 +3173,7 @@ function readIcons(definition) {
2558
3173
  return [{
2559
3174
  src,
2560
3175
  mimeType: getOptionalStringProperty2(object, "mimeType"),
2561
- sizes: readStringArrayProperty3(object, "sizes")
3176
+ sizes: readStringArrayProperty4(object, "sizes")
2562
3177
  }];
2563
3178
  });
2564
3179
  return icons.length ? icons : void 0;
@@ -2571,7 +3186,7 @@ function readObjectProperty4(definition, propertyName) {
2571
3186
  const initializer = unwrapExpression(property.getInitializer());
2572
3187
  return initializer && Node7.isObjectLiteralExpression(initializer) ? initializer : void 0;
2573
3188
  }
2574
- function readStringArrayProperty3(definition, propertyName) {
3189
+ function readStringArrayProperty4(definition, propertyName) {
2575
3190
  const property = definition.getProperty(propertyName);
2576
3191
  if (!property || !Node7.isPropertyAssignment(property)) {
2577
3192
  return void 0;
@@ -2585,13 +3200,13 @@ function readStringArrayProperty3(definition, propertyName) {
2585
3200
 
2586
3201
  // packages/compiler/src/resources.ts
2587
3202
  import { readdir as readdir7 } from "fs/promises";
2588
- import path16 from "path";
3203
+ import path17 from "path";
2589
3204
  import {
2590
3205
  Node as Node8,
2591
3206
  SyntaxKind as SyntaxKind6
2592
3207
  } from "ts-morph";
2593
3208
  async function analyzeProjectResources(rootDir) {
2594
- const resourceFiles = await findResourceFiles(path16.join(rootDir, "resources"));
3209
+ const resourceFiles = await findResourceFiles(path17.join(rootDir, "resources"));
2595
3210
  if (resourceFiles.length === 0) {
2596
3211
  return [];
2597
3212
  }
@@ -2603,7 +3218,7 @@ async function analyzeProjectResources(rootDir) {
2603
3218
  function analyzeResourceFile(sourceFile, rootDir) {
2604
3219
  const definition = findResourceDefinition(sourceFile);
2605
3220
  const absoluteFile = sourceFile.getFilePath();
2606
- const directory = path16.basename(path16.dirname(absoluteFile));
3221
+ const directory = path17.basename(path17.dirname(absoluteFile));
2607
3222
  const defaultUri = `sidecar://resources/${safePathSegment(directory)}`;
2608
3223
  const name = getRequiredStringProperty3(definition, "name", sourceFile);
2609
3224
  const uri = getOptionalStringProperty3(definition, "uri") ?? defaultUri;
@@ -2621,7 +3236,7 @@ function analyzeResourceFile(sourceFile, rootDir) {
2621
3236
  throw new CompilerError(sourceFile, "resource({ ... }) must include a read method.");
2622
3237
  }
2623
3238
  return {
2624
- sourceFile: path16.relative(rootDir, absoluteFile),
3239
+ sourceFile: path17.relative(rootDir, absoluteFile),
2625
3240
  directory,
2626
3241
  uri,
2627
3242
  name,
@@ -2630,7 +3245,7 @@ function analyzeResourceFile(sourceFile, rootDir) {
2630
3245
  mimeType: descriptor.mimeType,
2631
3246
  size: descriptor.size,
2632
3247
  annotations: descriptor.annotations,
2633
- subscribe: readBooleanProperty4(definition, "subscribe"),
3248
+ subscribe: readBooleanProperty5(definition, "subscribe"),
2634
3249
  descriptor
2635
3250
  };
2636
3251
  }
@@ -2644,7 +3259,7 @@ async function findResourceFiles(resourcesDir) {
2644
3259
  if (!entry.isDirectory()) {
2645
3260
  continue;
2646
3261
  }
2647
- const filePath = path16.join(resourcesDir, entry.name, "resource.ts");
3262
+ const filePath = path17.join(resourcesDir, entry.name, "resource.ts");
2648
3263
  if (existsSyncSafe(filePath)) {
2649
3264
  files.push(filePath);
2650
3265
  }
@@ -2691,7 +3306,7 @@ function getOptionalNumberProperty(definition, propertyName) {
2691
3306
  const initializer = unwrapExpression(property.getInitializer());
2692
3307
  return initializer && Node8.isNumericLiteral(initializer) ? Number(initializer.getLiteralText()) : void 0;
2693
3308
  }
2694
- function readBooleanProperty4(definition, propertyName) {
3309
+ function readBooleanProperty5(definition, propertyName) {
2695
3310
  const property = definition.getProperty(propertyName);
2696
3311
  if (!property || !Node8.isPropertyAssignment(property)) {
2697
3312
  return void 0;
@@ -2723,7 +3338,7 @@ function readAnnotations2(definition) {
2723
3338
  return void 0;
2724
3339
  }
2725
3340
  const annotations = {};
2726
- const audience = readStringArrayProperty4(initializer, "audience")?.filter((value) => value === "user" || value === "assistant");
3341
+ const audience = readStringArrayProperty5(initializer, "audience")?.filter((value) => value === "user" || value === "assistant");
2727
3342
  const priority = getOptionalNumberProperty(initializer, "priority");
2728
3343
  const lastModified = getOptionalStringProperty3(initializer, "lastModified");
2729
3344
  if (audience?.length) annotations.audience = audience;
@@ -2752,7 +3367,7 @@ function readIcons2(definition) {
2752
3367
  return [{
2753
3368
  src,
2754
3369
  mimeType: getOptionalStringProperty3(object, "mimeType"),
2755
- sizes: readStringArrayProperty4(object, "sizes")
3370
+ sizes: readStringArrayProperty5(object, "sizes")
2756
3371
  }];
2757
3372
  });
2758
3373
  return icons.length ? icons : void 0;
@@ -2765,7 +3380,7 @@ function readObjectProperty5(definition, propertyName) {
2765
3380
  const initializer = unwrapExpression(property.getInitializer());
2766
3381
  return initializer && Node8.isObjectLiteralExpression(initializer) ? initializer : void 0;
2767
3382
  }
2768
- function readStringArrayProperty4(definition, propertyName) {
3383
+ function readStringArrayProperty5(definition, propertyName) {
2769
3384
  const property = definition.getProperty(propertyName);
2770
3385
  if (!property || !Node8.isPropertyAssignment(property)) {
2771
3386
  return void 0;
@@ -2777,15 +3392,386 @@ function readStringArrayProperty4(definition, propertyName) {
2777
3392
  return initializer.getElements().filter(Node8.isStringLiteral).map((element) => element.getLiteralText());
2778
3393
  }
2779
3394
 
3395
+ // packages/compiler/src/server-output.ts
3396
+ import { existsSync as existsSync6 } from "fs";
3397
+ import { mkdir as mkdir8, rm, writeFile as writeFile8 } from "fs/promises";
3398
+ import path18 from "path";
3399
+ import { build as esbuild2 } from "esbuild";
3400
+ var SERVER_ENTRYPOINT = "server/index.js";
3401
+ var VERCEL_FUNCTION_DIR = "functions/api/sidecar.func";
3402
+ var VERCEL_ENTRYPOINT = `${VERCEL_FUNCTION_DIR}/index.js`;
3403
+ var VERCEL_HANDLER_FILE = "index.js";
3404
+ async function buildServerOutput(rootDir, outDir, manifest, identity, host = "node", options = {}) {
3405
+ const cacheDir = path18.join(rootDir, ".sidecar", "cache", "server");
3406
+ await mkdir8(cacheDir, { recursive: true });
3407
+ const entryFile = path18.join(cacheDir, "index.ts");
3408
+ await writeFile8(entryFile, renderServerEntry(rootDir, entryFile, manifest, identity));
3409
+ const serverFile = path18.join(outDir, SERVER_ENTRYPOINT);
3410
+ await mkdir8(path18.dirname(serverFile), { recursive: true });
3411
+ await esbuild2({
3412
+ absWorkingDir: rootDir,
3413
+ alias: sidecarBundleAliases(rootDir),
3414
+ banner: {
3415
+ js: `import { createRequire as __sidecarCreateRequire } from "node:module";
3416
+ const require = __sidecarCreateRequire(import.meta.url);`
3417
+ },
3418
+ bundle: true,
3419
+ entryPoints: [entryFile],
3420
+ format: "esm",
3421
+ legalComments: "none",
3422
+ minify: false,
3423
+ nodePaths: [
3424
+ path18.join(rootDir, "node_modules"),
3425
+ path18.join(process.cwd(), "node_modules")
3426
+ ],
3427
+ outfile: serverFile,
3428
+ platform: "node",
3429
+ sourcemap: false,
3430
+ target: "node20"
3431
+ });
3432
+ await writeFile8(path18.join(outDir, "package.json"), renderServerPackage(identity));
3433
+ if (host === "vercel") {
3434
+ const vercelOutputDir = options.vercelOutputDir ?? outDir;
3435
+ await rm(path18.join(vercelOutputDir, "api"), { recursive: true, force: true });
3436
+ await rm(path18.join(vercelOutputDir, "vercel.json"), { force: true });
3437
+ await writeFile8(path18.join(outDir, VERCEL_HANDLER_FILE), renderVercelEntrypoint());
3438
+ await writeFile8(path18.join(outDir, ".vc-config.json"), renderVercelFunctionConfig());
3439
+ await mkdir8(vercelOutputDir, { recursive: true });
3440
+ await writeFile8(path18.join(vercelOutputDir, "config.json"), renderVercelOutputConfig());
3441
+ } else {
3442
+ await rm(path18.join(outDir, "api"), { recursive: true, force: true });
3443
+ await rm(path18.join(outDir, "vercel.json"), { force: true });
3444
+ }
3445
+ }
3446
+ function renderServerEntry(rootDir, entryFile, manifest, identity) {
3447
+ const entryDir = path18.dirname(entryFile);
3448
+ const tools = manifest.tools;
3449
+ const resources = manifest.resources;
3450
+ const prompts = manifest.prompts;
3451
+ const imports = [
3452
+ `import { readFileSync, realpathSync } from "node:fs";`,
3453
+ `import { createServer } from "node:http";`,
3454
+ `import { pathToFileURL } from "node:url";`,
3455
+ `import { createSidecarHttpHandler } from "@sidecar-ai/server";`,
3456
+ `import { isSidecarAuth } from "@sidecar-ai/auth";`,
3457
+ `import { isSidecarPrompt, isSidecarRemote, isSidecarResource, isSidecarTool } from "@sidecar-ai/core";`,
3458
+ `import { isSidecarProxy } from "@sidecar-ai/server/proxy";`,
3459
+ ...tools.map(
3460
+ (entry, index) => `import tool${index} from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, entry.sourceFile)))};`
3461
+ ),
3462
+ ...resources.map(
3463
+ (entry, index) => `import resource${index} from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, entry.sourceFile)))};`
3464
+ ),
3465
+ ...prompts.map(
3466
+ (entry, index) => `import prompt${index} from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, entry.sourceFile)))};`
3467
+ ),
3468
+ existsSync6(path18.join(rootDir, "auth.ts")) ? `import authExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "auth.ts")))};` : `const authExport = undefined;`,
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;`,
3471
+ existsSync6(path18.join(rootDir, "sidecar.config.ts")) ? `import configExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "sidecar.config.ts")))};` : `const configExport = undefined;`
3472
+ ].join("\n");
3473
+ return `${imports}
3474
+
3475
+ const manifest = ${JSON.stringify(manifest, null, 2)};
3476
+ const identity = ${JSON.stringify(identity, null, 2)};
3477
+
3478
+ const loadedAuth = authExport === undefined ? undefined : assertAuth(authExport);
3479
+ const auth = loadedAuth && process.env.SIDECAR_MCP_URL
3480
+ ? loadedAuth.withResource(process.env.SIDECAR_MCP_URL)
3481
+ : loadedAuth;
3482
+ const proxy = proxyExport === undefined ? undefined : assertProxy(proxyExport);
3483
+ const remoteExecution = manifest.codeMode?.remoteExecution
3484
+ ? assertRemote(remoteExport)
3485
+ : undefined;
3486
+ const runtimeConfig = configExport && typeof configExport === "object" ? configExport : undefined;
3487
+
3488
+ if (auth && !process.env.SIDECAR_MCP_URL) {
3489
+ console.warn("Sidecar auth is enabled. Set SIDECAR_MCP_URL to the public https://.../mcp URL before hosting.");
3490
+ }
3491
+
3492
+ export const handler = createSidecarHttpHandler({
3493
+ name: identity.name,
3494
+ version: identity.version,
3495
+ path: process.env.SIDECAR_MCP_PATH ?? "/mcp",
3496
+ publicUrl: process.env.SIDECAR_PUBLIC_URL ?? process.env.SIDECAR_MCP_URL,
3497
+ auth,
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,
3514
+ tools: [
3515
+ ${tools.map((entry, index) => ` loadTool(${JSON.stringify(entry.sourceFile)}, tool${index}, manifest.tools[${index}].descriptor),`).join("\n")}
3516
+ ],
3517
+ resources: [
3518
+ ${renderWidgetResources(manifest)}
3519
+ ${resources.map((entry, index) => ` loadResource(${JSON.stringify(entry.sourceFile)}, resource${index}, manifest.resources[${index}]),`).join("\n")}
3520
+ ],
3521
+ resourceTemplates: manifest.resourceTemplates.map((entry) => ({ descriptor: entry.descriptor })),
3522
+ prompts: [
3523
+ ${prompts.map((entry, index) => ` loadPrompt(${JSON.stringify(entry.sourceFile)}, prompt${index}, manifest.prompts[${index}].descriptor),`).join("\n")}
3524
+ ],
3525
+ capabilities: {
3526
+ tools: runtimeConfig?.tools ?? manifest.config.tools,
3527
+ resources: runtimeConfig?.resources ?? manifest.config.resources,
3528
+ prompts: runtimeConfig?.prompts ?? manifest.config.prompts,
3529
+ },
3530
+ pagination: runtimeConfig?.pagination ?? {
3531
+ pageSize: manifest.config.pagination.pageSize,
3532
+ },
3533
+ });
3534
+
3535
+ export default handler;
3536
+
3537
+ export const server = createServer(handler);
3538
+
3539
+ if (isDirectRun()) {
3540
+ const port = readPort();
3541
+ const host = process.env.SIDECAR_HOST ?? process.env.HOST ?? "0.0.0.0";
3542
+ server.listen(port, host, () => {
3543
+ const localHost = host === "0.0.0.0" ? "127.0.0.1" : host;
3544
+ console.log(\`MCP running on Streamable HTTP at http://\${localHost}:\${port}\${process.env.SIDECAR_MCP_PATH ?? "/mcp"}\`);
3545
+ printRuntimeUrls(localHost, port);
3546
+ });
3547
+
3548
+ process.on("SIGTERM", () => shutdown());
3549
+ process.on("SIGINT", () => shutdown());
3550
+ }
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
+
3562
+ function loadTool(sourceFile, value, descriptor) {
3563
+ const tool = unwrapRuntimeDefault(value);
3564
+ if (!isSidecarTool(tool)) {
3565
+ throw new Error(\`\${sourceFile} did not default-export a Sidecar tool.\`);
3566
+ }
3567
+ return { tool, descriptor };
3568
+ }
3569
+
3570
+ function loadResource(sourceFile, value, entry) {
3571
+ const resource = unwrapRuntimeDefault(value);
3572
+ if (!isSidecarResource(resource)) {
3573
+ throw new Error(\`\${sourceFile} did not default-export a Sidecar resource.\`);
3574
+ }
3575
+ return {
3576
+ uri: entry.uri,
3577
+ descriptor: entry.descriptor,
3578
+ resource,
3579
+ };
3580
+ }
3581
+
3582
+ function loadPrompt(sourceFile, value, descriptor) {
3583
+ const prompt = unwrapRuntimeDefault(value);
3584
+ if (!isSidecarPrompt(prompt)) {
3585
+ throw new Error(\`\${sourceFile} did not default-export a Sidecar prompt.\`);
3586
+ }
3587
+ return { prompt, descriptor };
3588
+ }
3589
+
3590
+ function assertAuth(value) {
3591
+ const authValue = unwrapRuntimeDefault(value);
3592
+ if (!isSidecarAuth(authValue)) {
3593
+ throw new Error("auth.ts must default-export auth({ ... }) from sidecar-ai.");
3594
+ }
3595
+ return authValue;
3596
+ }
3597
+
3598
+ function assertProxy(value) {
3599
+ const proxyValue = unwrapRuntimeDefault(value);
3600
+ if (!isSidecarProxy(proxyValue)) {
3601
+ throw new Error("proxy.ts must default-export proxy({ ... }) from @sidecar-ai/server/proxy.");
3602
+ }
3603
+ return proxyValue;
3604
+ }
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
+
3614
+ function unwrapRuntimeDefault(value) {
3615
+ if (
3616
+ value &&
3617
+ typeof value === "object" &&
3618
+ "default" in value &&
3619
+ Object.keys(value).every((key) => key === "default" || key === "__esModule")
3620
+ ) {
3621
+ return unwrapRuntimeDefault(value.default);
3622
+ }
3623
+ return value;
3624
+ }
3625
+
3626
+ function readWidget(outputFile) {
3627
+ return readFileSync(new URL(\`../\${outputFile}\`, import.meta.url), "utf8");
3628
+ }
3629
+
3630
+ function readPort() {
3631
+ const raw = process.env.PORT ?? process.env.SIDECAR_PORT ?? "3001";
3632
+ const port = Number(raw);
3633
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
3634
+ throw new Error(\`Invalid PORT/SIDECAR_PORT value: \${raw}\`);
3635
+ }
3636
+ return port;
3637
+ }
3638
+
3639
+ function isDirectRun() {
3640
+ const entry = process.argv[1];
3641
+ if (!entry) {
3642
+ return false;
3643
+ }
3644
+
3645
+ return import.meta.url === pathToFileURL(realpathSync(entry)).href;
3646
+ }
3647
+
3648
+ function shutdown() {
3649
+ server.close(() => process.exit(0));
3650
+ }
3651
+ `;
3652
+ }
3653
+ function renderWidgetResources(manifest) {
3654
+ const toolWidgets = manifest.tools.filter((entry) => entry.widget?.outputFile).map((entry) => ` {
3655
+ uri: ${JSON.stringify(entry.widget?.resourceUri)},
3656
+ name: ${JSON.stringify(entry.name)},
3657
+ description: ${JSON.stringify(entry.widget?.options?.description)},
3658
+ mimeType: "text/html;profile=mcp-app",
3659
+ text: readWidget(${JSON.stringify(entry.widget?.outputFile)}),
3660
+ _meta: ${JSON.stringify(entry.widget?.resourceMeta ?? void 0)},
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");
3671
+ }
3672
+ function renderServerPackage(identity) {
3673
+ return `${JSON.stringify({
3674
+ name: `${identity.slug}-sidecar-server`,
3675
+ version: identity.version,
3676
+ private: true,
3677
+ type: "module",
3678
+ scripts: {
3679
+ start: "node server/index.js"
3680
+ },
3681
+ engines: {
3682
+ node: ">=20"
3683
+ }
3684
+ }, null, 2)}
3685
+ `;
3686
+ }
3687
+ function renderVercelEntrypoint() {
3688
+ return `export { default } from "./server/index.js";
3689
+ `;
3690
+ }
3691
+ function renderVercelFunctionConfig() {
3692
+ return `${JSON.stringify({
3693
+ runtime: "nodejs22.x",
3694
+ handler: VERCEL_HANDLER_FILE,
3695
+ launcherType: "Nodejs",
3696
+ shouldAddHelpers: true,
3697
+ supportsResponseStreaming: true,
3698
+ maxDuration: 300
3699
+ }, null, 2)}
3700
+ `;
3701
+ }
3702
+ function renderVercelOutputConfig() {
3703
+ return `${JSON.stringify({
3704
+ version: 3,
3705
+ routes: [
3706
+ {
3707
+ src: "/(.*)",
3708
+ dest: "/api/sidecar"
3709
+ }
3710
+ ]
3711
+ }, null, 2)}
3712
+ `;
3713
+ }
3714
+ function sidecarBundleAliases(rootDir) {
3715
+ const repoRoot = findSidecarRepoRoot2(rootDir) ?? findSidecarRepoRoot2(process.cwd());
3716
+ if (!repoRoot) {
3717
+ return void 0;
3718
+ }
3719
+ return {
3720
+ "sidecar-ai/remote": path18.join(repoRoot, "packages", "sidecar-ai", "src", "remote.ts"),
3721
+ "sidecar-ai": path18.join(repoRoot, "packages", "sidecar-ai", "src", "index.ts"),
3722
+ "@sidecar-ai/auth": path18.join(repoRoot, "packages", "auth", "src", "index.ts"),
3723
+ "@sidecar-ai/core": path18.join(repoRoot, "packages", "core", "src", "index.ts"),
3724
+ "@sidecar-ai/server": path18.join(repoRoot, "packages", "server", "src", "index.ts"),
3725
+ "@sidecar-ai/server/proxy": path18.join(repoRoot, "packages", "server", "src", "proxy.ts"),
3726
+ "@sidecar-ai/client": path18.join(repoRoot, "packages", "client", "src", "index.ts"),
3727
+ "@sidecar-ai/react": path18.join(repoRoot, "packages", "react", "src", "index.ts"),
3728
+ "@sidecar-ai/native": path18.join(repoRoot, "packages", "native", "src", "index.ts"),
3729
+ "@sidecar-ai/native/components": path18.join(repoRoot, "packages", "native", "src", "components", "index.tsx"),
3730
+ "@sidecar-ai/openai": path18.join(repoRoot, "packages", "openai", "src", "index.ts"),
3731
+ "@sidecar-ai/openai/components": path18.join(repoRoot, "packages", "openai", "src", "components.tsx"),
3732
+ "@sidecar-ai/openai/official": path18.join(repoRoot, "packages", "openai", "src", "official.ts"),
3733
+ "@sidecar-ai/anthropic": path18.join(repoRoot, "packages", "anthropic", "src", "index.ts"),
3734
+ "@sidecar-ai/anthropic/agent": path18.join(repoRoot, "packages", "anthropic", "src", "agent.ts"),
3735
+ "@sidecar-ai/anthropic/command": path18.join(repoRoot, "packages", "anthropic", "src", "command.ts"),
3736
+ "@sidecar-ai/anthropic/components": path18.join(repoRoot, "packages", "anthropic", "src", "components.tsx"),
3737
+ "@sidecar-ai/anthropic/hooks": path18.join(repoRoot, "packages", "anthropic", "src", "hooks.ts"),
3738
+ "@sidecar-ai/anthropic/mcp": path18.join(repoRoot, "packages", "anthropic", "src", "mcp.ts"),
3739
+ "@sidecar-ai/anthropic/plugin": path18.join(repoRoot, "packages", "anthropic", "src", "plugin.ts"),
3740
+ "@sidecar-ai/anthropic/skill": path18.join(repoRoot, "packages", "anthropic", "src", "skill.ts")
3741
+ };
3742
+ }
3743
+ function findSidecarRepoRoot2(startDir) {
3744
+ let current = path18.resolve(startDir);
3745
+ while (true) {
3746
+ if (existsSync6(path18.join(current, "packages", "core", "src", "index.ts"))) {
3747
+ return current;
3748
+ }
3749
+ const parent = path18.dirname(current);
3750
+ if (parent === current) {
3751
+ return void 0;
3752
+ }
3753
+ current = parent;
3754
+ }
3755
+ }
3756
+
2780
3757
  // packages/compiler/src/build.ts
2781
3758
  async function buildProject(options) {
2782
- const rootDir = path17.resolve(options.rootDir);
2783
- const target = options.target ?? "mcp";
3759
+ const rootDir = path19.resolve(options.rootDir);
2784
3760
  const config = analyzeProjectConfig(rootDir);
3761
+ const target = options.target ?? config.build.target ?? "mcp";
3762
+ const host = options.host ?? config.build.host ?? "node";
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
+ }
2785
3770
  const tools = await analyzeProjectTools(rootDir, { target });
2786
3771
  const resources = await analyzeProjectResources(rootDir);
2787
3772
  const resourceTemplates = [];
2788
3773
  const prompts = await analyzeProjectPrompts(rootDir);
3774
+ const identity = await loadProjectIdentity(rootDir);
2789
3775
  const diagnostics = await collectProjectDiagnostics(rootDir, {
2790
3776
  tools,
2791
3777
  resources,
@@ -2796,37 +3782,71 @@ async function buildProject(options) {
2796
3782
  if (errors.length) {
2797
3783
  throw new Error(errors.map(formatDiagnostic).join("\n"));
2798
3784
  }
2799
- const outDir = resolveInsideRoot(rootDir, options.outDir ?? "out/mcp");
2800
- await buildWidgets(rootDir, outDir, tools);
3785
+ const outDir = resolveInsideRoot(rootDir, options.outDir ?? config.build.outDir ?? defaultBuildOutDir(host, target));
3786
+ const runtimeOutDir = resolveRuntimeOutputDir(outDir, host);
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;
2801
3789
  const manifest = {
2802
3790
  version: 1,
2803
3791
  target,
3792
+ host,
2804
3793
  rootDir: ".",
2805
3794
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2806
3795
  config,
2807
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,
2808
3804
  resources,
2809
3805
  resourceTemplates,
2810
3806
  prompts,
2811
3807
  diagnostics
2812
3808
  };
2813
- await mkdir8(outDir, { recursive: true });
2814
- await writeFile8(
2815
- path17.join(outDir, "manifest.sidecar.json"),
3809
+ await mkdir9(runtimeOutDir, { recursive: true });
3810
+ await writeFile9(
3811
+ path19.join(runtimeOutDir, "manifest.sidecar.json"),
2816
3812
  `${JSON.stringify(manifest, null, 2)}
2817
3813
  `
2818
3814
  );
2819
- await writeFile8(path17.join(outDir, "README.md"), renderMcpReadme(manifest));
3815
+ await writeFile9(path19.join(runtimeOutDir, "README.md"), renderMcpReadme(manifest));
2820
3816
  await writeGeneratedTypes(rootDir, tools);
2821
- if (options.plugins) {
2822
- await buildPluginPackages(rootDir, path17.dirname(outDir), manifest);
3817
+ await buildServerOutput(rootDir, runtimeOutDir, manifest, identity, host, {
3818
+ vercelOutputDir: outDir
3819
+ });
3820
+ if (plugins) {
3821
+ await buildPluginPackages(rootDir, resolvePluginOutputBase(rootDir, outDir, host), manifest);
2823
3822
  }
2824
3823
  return manifest;
2825
3824
  }
3825
+ function defaultBuildOutDir(host, target) {
3826
+ if (host === "vercel") {
3827
+ return ".vercel/output";
3828
+ }
3829
+ return `out/${target}`;
3830
+ }
3831
+ function resolveRuntimeOutputDir(outDir, host) {
3832
+ if (host === "vercel") {
3833
+ return path19.join(outDir, VERCEL_FUNCTION_DIR);
3834
+ }
3835
+ return outDir;
3836
+ }
3837
+ function resolvePluginOutputBase(rootDir, outDir, host) {
3838
+ if (host === "vercel" && isVercelBuildOutputDir(outDir)) {
3839
+ return path19.join(rootDir, "out");
3840
+ }
3841
+ return path19.dirname(outDir);
3842
+ }
3843
+ function isVercelBuildOutputDir(outDir) {
3844
+ return path19.basename(outDir) === "output" && path19.basename(path19.dirname(outDir)) === ".vercel";
3845
+ }
2826
3846
  function resolveInsideRoot(rootDir, output) {
2827
- const resolved = path17.resolve(rootDir, output);
2828
- const relative = path17.relative(rootDir, resolved);
2829
- if (relative.startsWith("..") || path17.isAbsolute(relative)) {
3847
+ const resolved = path19.resolve(rootDir, output);
3848
+ const relative = path19.relative(rootDir, resolved);
3849
+ if (relative.startsWith("..") || path19.isAbsolute(relative)) {
2830
3850
  throw new Error(`Build output must stay inside the project root: ${output}`);
2831
3851
  }
2832
3852
  return resolved;
@@ -2851,13 +3871,41 @@ ${resources || "No resources detected."}
2851
3871
 
2852
3872
  ${prompts || "No prompts detected."}
2853
3873
 
3874
+ ${manifest.host === "vercel" ? renderVercelReadmeSection() : renderNodeReadmeSection()}
3875
+
2854
3876
  ## Local HTTPS Testing
2855
3877
 
2856
- Run \`sidecar dev --tunnel\` from the project root to start the local MCP server on Streamable HTTP and print an HTTPS MCP URL that can be added to ChatGPT, Claude, or a Claude plugin install.
3878
+ Run \`sidecar dev --tunnel\` from the project root to start the local MCP server on Streamable HTTP and print a validated HTTPS MCP URL that can be added to ChatGPT, Claude, or a Claude plugin install. Temporary quick tunnels are public and best-effort; use a configured tunnel/domain or deployed preview for repeatable testing.
3879
+ `;
3880
+ }
3881
+ function renderNodeReadmeSection() {
3882
+ return `## Run The Server
3883
+
3884
+ This output includes a standalone Node MCP server. Start it with:
3885
+
3886
+ \`\`\`sh
3887
+ npm start
3888
+ \`\`\`
3889
+
3890
+ or:
3891
+
3892
+ \`\`\`sh
3893
+ node server/index.js
3894
+ \`\`\`
3895
+
3896
+ Set \`PORT\` or \`SIDECAR_PORT\` to choose the listen port. Hosted/authenticated MCPs should set \`SIDECAR_MCP_URL\` to the public \`https://.../mcp\` URL before starting.
3897
+ `;
3898
+ }
3899
+ function renderVercelReadmeSection() {
3900
+ return `## Deploy To Vercel
3901
+
3902
+ This output uses Vercel's Build Output API. The MCP function is emitted at \`${VERCEL_FUNCTION_DIR}\`, and \`config.json\` routes Streamable HTTP traffic to it. Set \`SIDECAR_MCP_URL\` to the public \`https://.../mcp\` URL in Vercel.
2857
3903
  `;
2858
3904
  }
2859
3905
  export {
2860
3906
  CompilerError,
3907
+ SERVER_ENTRYPOINT,
3908
+ VERCEL_ENTRYPOINT,
2861
3909
  analyzeProjectConfig,
2862
3910
  analyzeProjectPrompts,
2863
3911
  analyzeProjectResources,