@sidecar-ai/compiler 0.1.0-alpha.0 → 0.1.0-alpha.10

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,219 @@ 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
+ function widgetBuildOptionsFromConfig(rootDir, config) {
819
+ const esbuildConfig = config?.esbuild;
820
+ if (!esbuildConfig) {
821
+ return void 0;
822
+ }
823
+ return {
824
+ alias: normalizeAliases(rootDir, esbuildConfig.alias),
825
+ conditions: esbuildConfig.conditions,
826
+ define: esbuildConfig.define,
827
+ external: esbuildConfig.external,
828
+ jsx: esbuildConfig.jsx,
829
+ jsxImportSource: esbuildConfig.jsxImportSource,
830
+ loader: esbuildConfig.loader,
831
+ mainFields: esbuildConfig.mainFields
832
+ };
833
+ }
834
+ async function loadWidgetBundlerHook(rootDir, cacheDir, hookPath) {
835
+ if (!hookPath) {
836
+ return void 0;
837
+ }
838
+ const sourcePath = path4.resolve(rootDir, hookPath);
839
+ const relative = path4.relative(rootDir, sourcePath);
840
+ if (relative.startsWith("..") || path4.isAbsolute(relative)) {
841
+ throw new Error(`Widget bundler hook must stay inside the project root: ${hookPath}`);
842
+ }
843
+ if (!existsSync3(sourcePath)) {
844
+ throw new Error(`Widget bundler hook not found: ${hookPath}`);
845
+ }
846
+ const outputFile = path4.join(cacheDir, `widget-bundler.${contentHash(hookPath)}.mjs`);
847
+ await esbuild({
848
+ absWorkingDir: rootDir,
849
+ alias: devSidecarBundleAliases(rootDir),
850
+ bundle: true,
851
+ entryPoints: [sourcePath],
852
+ format: "esm",
853
+ nodePaths: [
854
+ path4.join(rootDir, "node_modules"),
855
+ path4.join(process.cwd(), "node_modules")
856
+ ],
857
+ outfile: outputFile,
858
+ packages: "external",
859
+ platform: "node",
860
+ target: "node20"
861
+ });
862
+ const module = await import(`${pathToFileURL2(outputFile).href}?t=${Date.now()}`);
863
+ if (typeof module.default !== "function") {
864
+ throw new Error(`Widget bundler hook must default-export a function: ${hookPath}`);
865
+ }
866
+ return module.default;
867
+ }
868
+ async function configureWidgetBuild(hook, input) {
869
+ const result = await hook(input);
870
+ if (!result) {
871
+ return void 0;
872
+ }
873
+ if (typeof result === "object" && "esbuildOptions" in result) {
874
+ return result.esbuildOptions;
875
+ }
876
+ return result;
877
+ }
878
+ function mergeWidgetBuildOptions(...options) {
879
+ const merged = {};
880
+ for (const option of options) {
881
+ if (!option) {
882
+ continue;
883
+ }
884
+ const previousAlias = merged.alias;
885
+ const previousDefine = merged.define;
886
+ const previousLoader = merged.loader;
887
+ const previousExternal = merged.external;
888
+ const previousNodePaths = merged.nodePaths;
889
+ const previousPlugins = merged.plugins;
890
+ Object.assign(merged, option);
891
+ merged.alias = { ...previousAlias ?? {}, ...option.alias ?? {} };
892
+ merged.define = { ...previousDefine ?? {}, ...option.define ?? {} };
893
+ merged.loader = { ...previousLoader ?? {}, ...option.loader ?? {} };
894
+ merged.external = uniqueStrings([...previousExternal ?? [], ...option.external ?? []]);
895
+ merged.nodePaths = uniqueStrings([...previousNodePaths ?? [], ...option.nodePaths ?? []]);
896
+ merged.plugins = [...previousPlugins ?? [], ...option.plugins ?? []];
897
+ }
898
+ return merged;
899
+ }
900
+ function enforceWidgetBuildInvariants(options, required) {
901
+ const aliases = required ? { ...options.alias ?? {}, ...reactSingletonAliases(required.rootDir) } : options.alias;
902
+ return {
903
+ ...options,
904
+ absWorkingDir: required?.rootDir ?? options.absWorkingDir,
905
+ alias: aliases,
906
+ bundle: true,
907
+ entryPoints: required ? [required.entryFile] : options.entryPoints,
908
+ format: "iife",
909
+ outfile: "widget.js",
910
+ platform: "browser",
911
+ write: false
912
+ };
913
+ }
914
+ function sidecarWidgetAliases(rootDir) {
915
+ return {
916
+ ...devSidecarBundleAliases(rootDir) ?? {},
917
+ ...reactSingletonAliases(rootDir)
918
+ };
919
+ }
920
+ function reactSingletonAliases(rootDir) {
921
+ return {
922
+ react: resolveProjectModule(rootDir, "react"),
923
+ "react/jsx-runtime": resolveProjectModule(rootDir, "react/jsx-runtime"),
924
+ "react/jsx-dev-runtime": resolveProjectModule(rootDir, "react/jsx-dev-runtime"),
925
+ "react-dom": resolveProjectModule(rootDir, "react-dom"),
926
+ "react-dom/client": resolveProjectModule(rootDir, "react-dom/client"),
927
+ "react-dom/server": resolveProjectModule(rootDir, "react-dom/server")
928
+ };
929
+ }
930
+ function resolveProjectModule(rootDir, specifier) {
931
+ return requireFromCompiler.resolve(specifier, {
932
+ paths: [rootDir, process.cwd()]
933
+ });
934
+ }
935
+ function normalizeAliases(rootDir, aliases) {
936
+ if (!aliases) {
937
+ return void 0;
938
+ }
939
+ return Object.fromEntries(
940
+ Object.entries(aliases).map(([key, value]) => [
941
+ key,
942
+ value.startsWith(".") ? path4.resolve(rootDir, value) : value
943
+ ])
944
+ );
945
+ }
946
+ function uniqueStrings(values) {
947
+ return [...new Set(values)];
948
+ }
949
+ function contentHash(value) {
950
+ return createHash("sha256").update(value).digest("hex").slice(0, 12);
951
+ }
743
952
  function devSidecarBundleAliases(rootDir) {
744
953
  const repoRoot = findSidecarRepoRoot(rootDir) ?? findSidecarRepoRoot(process.cwd());
745
954
  if (!repoRoot) {
746
955
  return void 0;
747
956
  }
748
- const reactEntry = path3.join(repoRoot, "packages", "react", "src", "index.ts");
749
- if (!existsSync2(reactEntry)) {
957
+ const reactEntry = path4.join(repoRoot, "packages", "react", "src", "index.ts");
958
+ if (!existsSync3(reactEntry)) {
750
959
  return void 0;
751
960
  }
752
961
  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"),
962
+ "sidecar-ai": path4.join(repoRoot, "packages", "sidecar-ai", "src", "index.ts"),
963
+ "@sidecar-ai/client": path4.join(repoRoot, "packages", "client", "src", "index.ts"),
964
+ "@sidecar-ai/core": path4.join(repoRoot, "packages", "core", "src", "index.ts"),
756
965
  "@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")
966
+ "@sidecar-ai/native": path4.join(repoRoot, "packages", "native", "src", "index.ts"),
967
+ "@sidecar-ai/native/components": path4.join(repoRoot, "packages", "native", "src", "components", "index.tsx"),
968
+ "@sidecar-ai/native/styles.css": path4.join(repoRoot, "packages", "native", "src", "styles.css")
760
969
  };
761
970
  }
762
971
  function findSidecarRepoRoot(startDir) {
763
- let current = path3.resolve(startDir);
972
+ let current = path4.resolve(startDir);
764
973
  while (true) {
765
- if (existsSync2(path3.join(current, "packages", "core", "src", "index.ts"))) {
974
+ if (existsSync3(path4.join(current, "packages", "core", "src", "index.ts"))) {
766
975
  return current;
767
976
  }
768
- const parent = path3.dirname(current);
977
+ const parent = path4.dirname(current);
769
978
  if (parent === current) {
770
979
  return void 0;
771
980
  }
@@ -773,13 +982,13 @@ function findSidecarRepoRoot(startDir) {
773
982
  }
774
983
  }
775
984
  async function prepareAppStyle(rootDir, cacheDir) {
776
- const sourceFile = path3.join(rootDir, "style.css");
777
- if (!existsSync2(sourceFile)) {
985
+ const sourceFile = path4.join(rootDir, "style.css");
986
+ if (!existsSync3(sourceFile)) {
778
987
  return void 0;
779
988
  }
780
989
  const source = await readFile(sourceFile, "utf8");
781
990
  const css = await processAppStyle(source, sourceFile);
782
- const outputFile = path3.join(cacheDir, "style.css");
991
+ const outputFile = path4.join(cacheDir, "style.css");
783
992
  await writeFile(outputFile, css);
784
993
  return outputFile;
785
994
  }
@@ -791,7 +1000,7 @@ async function processAppStyle(source, from) {
791
1000
  const plugins = [];
792
1001
  if (cssSource.includes("@tailwind")) {
793
1002
  const tailwind = await import("@tailwindcss/postcss");
794
- plugins.push(tailwind.default({ base: path3.dirname(from) }));
1003
+ plugins.push(tailwind.default({ base: path4.dirname(from) }));
795
1004
  }
796
1005
  const autoprefixer = await import("autoprefixer");
797
1006
  plugins.push(autoprefixer.default());
@@ -805,13 +1014,13 @@ function needsPostcss(source) {
805
1014
  return /@import\s+["']tailwindcss["']|@tailwind|@source|@theme|@plugin/.test(source);
806
1015
  }
807
1016
  function findWidget(rootDir, toolFile, id, target = "mcp", toolVariant = "shared") {
808
- const selected = selectWidgetFile(path3.dirname(toolFile), target, toolVariant);
1017
+ const selected = selectWidgetFile(path4.dirname(toolFile), target, toolVariant);
809
1018
  if (!selected) {
810
1019
  return void 0;
811
1020
  }
812
1021
  const safeId = safePathSegment(id);
813
1022
  return {
814
- sourceFile: path3.relative(rootDir, selected.filePath),
1023
+ sourceFile: path4.relative(rootDir, selected.filePath),
815
1024
  variant: selected.variant,
816
1025
  resourceUri: `ui://${safeId}/widget.html`
817
1026
  };
@@ -846,7 +1055,8 @@ function widgetMeta(resourceUri, options = {}, target = "mcp") {
846
1055
  const standard = {
847
1056
  ui: {
848
1057
  resourceUri
849
- }
1058
+ },
1059
+ "ui/resourceUri": resourceUri
850
1060
  };
851
1061
  if (target !== "chatgpt") {
852
1062
  return standard;
@@ -862,7 +1072,7 @@ function widgetMeta(resourceUri, options = {}, target = "mcp") {
862
1072
  function widgetResourceMeta(options = {}, target = "mcp") {
863
1073
  const csp = stripUndefined3({
864
1074
  connectDomains: options.csp?.connectDomains ? [...options.csp.connectDomains] : [],
865
- resourceDomains: options.csp?.resourceDomains ? [...options.csp.resourceDomains] : [],
1075
+ resourceDomains: widgetResourceDomains(options, target),
866
1076
  frameDomains: options.csp?.frameDomains ? [...options.csp.frameDomains] : void 0,
867
1077
  baseUriDomains: options.csp?.baseUriDomains ? [...options.csp.baseUriDomains] : void 0
868
1078
  });
@@ -875,6 +1085,13 @@ function widgetResourceMeta(options = {}, target = "mcp") {
875
1085
  });
876
1086
  return Object.keys(ui).length ? { ui } : void 0;
877
1087
  }
1088
+ function widgetResourceDomains(options, target) {
1089
+ const domains = [...options.csp?.resourceDomains ?? []];
1090
+ if (target === "claude" && !domains.includes(CLAUDE_FONT_RESOURCE_DOMAIN)) {
1091
+ domains.push(CLAUDE_FONT_RESOURCE_DOMAIN);
1092
+ }
1093
+ return domains;
1094
+ }
878
1095
  function findWidgetDefinition(sourceFile) {
879
1096
  const call = resolveDefaultExportCall(sourceFile, "widget");
880
1097
  if (!call) {
@@ -986,17 +1203,17 @@ function readStringArrayProperty(definition, propertyName) {
986
1203
  }
987
1204
  function selectWidgetFile(directory, target, toolVariant) {
988
1205
  if (toolVariant === "openai") {
989
- const filePath = path3.join(directory, "widget.openai.tsx");
990
- return existsSync2(filePath) ? { filePath, variant: "openai" } : void 0;
1206
+ const filePath = path4.join(directory, "widget.openai.tsx");
1207
+ return existsSync3(filePath) ? { filePath, variant: "openai" } : void 0;
991
1208
  }
992
1209
  if (toolVariant === "anthropic") {
993
- const filePath = path3.join(directory, "widget.anthropic.tsx");
994
- return existsSync2(filePath) ? { filePath, variant: "anthropic" } : void 0;
1210
+ const filePath = path4.join(directory, "widget.anthropic.tsx");
1211
+ return existsSync3(filePath) ? { filePath, variant: "anthropic" } : void 0;
995
1212
  }
996
1213
  const candidates = target === "chatgpt" || target === "claude" || target === "mcp" ? [{ name: "widget.tsx", variant: "shared" }] : [];
997
1214
  for (const candidate of candidates) {
998
- const filePath = path3.join(directory, candidate.name);
999
- if (existsSync2(filePath)) {
1215
+ const filePath = path4.join(directory, candidate.name);
1216
+ if (existsSync3(filePath)) {
1000
1217
  return { filePath, variant: candidate.variant };
1001
1218
  }
1002
1219
  }
@@ -1047,17 +1264,23 @@ function stripUndefined3(value) {
1047
1264
  // packages/compiler/src/analyze.ts
1048
1265
  async function analyzeProjectTools(rootDir, options = {}) {
1049
1266
  const target = options.target ?? "mcp";
1050
- const toolFiles = await findToolFiles(path4.join(rootDir, "server"), target);
1267
+ const toolFiles = await findToolFiles(path5.join(rootDir, "server"), target);
1051
1268
  if (toolFiles.length === 0) {
1052
1269
  return [];
1053
1270
  }
1054
1271
  const project = createProject(rootDir);
1055
1272
  const authScopes = readAuthScopeCatalog(project, rootDir);
1056
- return toolFiles.map(
1057
- (candidate) => analyzeToolFile(project.addSourceFileAtPath(candidate.filePath), rootDir, {
1273
+ const sources = toolFiles.map((candidate) => ({
1274
+ candidate,
1275
+ sourceFile: project.addSourceFileAtPath(candidate.filePath)
1276
+ }));
1277
+ const runtimeInputSchemas = await readRuntimeInputSchemas(rootDir, sources);
1278
+ return sources.map(
1279
+ ({ candidate, sourceFile }) => analyzeToolFile(sourceFile, rootDir, {
1058
1280
  target,
1059
1281
  variant: candidate.variant,
1060
- authScopes
1282
+ authScopes,
1283
+ inputSchema: runtimeInputSchemas.get(candidate.filePath)
1061
1284
  })
1062
1285
  );
1063
1286
  }
@@ -1066,7 +1289,7 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1066
1289
  const variant = options.variant ?? "shared";
1067
1290
  const definition = findToolDefinition(sourceFile);
1068
1291
  const absoluteFile = sourceFile.getFilePath();
1069
- const directory = path4.basename(path4.dirname(absoluteFile));
1292
+ const directory = path5.basename(path5.dirname(absoluteFile));
1070
1293
  const name = getRequiredStringProperty(definition, "name", sourceFile);
1071
1294
  const id = getOptionalStringProperty(definition, "id") ?? toMachineName(directory);
1072
1295
  const description = getRequiredStringProperty(
@@ -1079,7 +1302,7 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1079
1302
  const hosts = readHosts(definition);
1080
1303
  const auth = readAuthPolicy(definition, options.authScopes ?? {});
1081
1304
  const execute = getExecuteFunction(definition, sourceFile);
1082
- const inputSchema = getParamsSchema(definition, execute);
1305
+ const inputSchema = options.inputSchema ?? getParamsSchema(definition, execute);
1083
1306
  const outputSchema = getOutputSchema(definition, execute);
1084
1307
  const descriptor = createToolDescriptor({
1085
1308
  name,
@@ -1097,7 +1320,7 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1097
1320
  validateWidgetHierarchy(sourceFile, absoluteFile, target, variant);
1098
1321
  const widget = findWidget(rootDir, absoluteFile, id, target, variant);
1099
1322
  if (widget) {
1100
- const widgetFile = path4.join(rootDir, widget.sourceFile);
1323
+ const widgetFile = path5.join(rootDir, widget.sourceFile);
1101
1324
  const project = sourceFile.getProject();
1102
1325
  const widgetSourceFile = project.getSourceFile(widgetFile) ?? project.addSourceFileAtPath(widgetFile);
1103
1326
  widget.options = {
@@ -1108,7 +1331,7 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1108
1331
  descriptor._meta = mergeWidgetMeta(descriptor._meta, widgetMeta(widget.resourceUri, widget.options, target));
1109
1332
  }
1110
1333
  return {
1111
- sourceFile: path4.relative(rootDir, absoluteFile),
1334
+ sourceFile: path5.relative(rootDir, absoluteFile),
1112
1335
  variant,
1113
1336
  target,
1114
1337
  directory,
@@ -1123,6 +1346,26 @@ function analyzeToolFile(sourceFile, rootDir, options = {}) {
1123
1346
  descriptor
1124
1347
  };
1125
1348
  }
1349
+ async function readRuntimeInputSchemas(rootDir, sources) {
1350
+ const schemas = /* @__PURE__ */ new Map();
1351
+ await Promise.all(sources.map(async ({ candidate, sourceFile }) => {
1352
+ const definition = findToolDefinition(sourceFile);
1353
+ if (!definitionHasRuntimeParams(definition)) {
1354
+ return;
1355
+ }
1356
+ const schema = await readRuntimeToolInputSchema(rootDir, candidate.filePath);
1357
+ if (schema) {
1358
+ schemas.set(candidate.filePath, schema);
1359
+ }
1360
+ }));
1361
+ return schemas;
1362
+ }
1363
+ function definitionHasRuntimeParams(definition) {
1364
+ if (definition.getProperty("params")) {
1365
+ return true;
1366
+ }
1367
+ return Boolean(getWithParamsCall(definition));
1368
+ }
1126
1369
  function readAuthPolicy(definition, authScopes) {
1127
1370
  const initializer = readObjectProperty2(definition, "auth");
1128
1371
  if (!initializer) {
@@ -1139,7 +1382,7 @@ function readAuthPolicy(definition, authScopes) {
1139
1382
  return { authenticated: true };
1140
1383
  }
1141
1384
  function readAuthScopeCatalog(project, rootDir) {
1142
- const authPath = path4.join(rootDir, "auth.ts");
1385
+ const authPath = path5.join(rootDir, "auth.ts");
1143
1386
  if (!existsSyncSafe(authPath)) {
1144
1387
  return {};
1145
1388
  }
@@ -1221,12 +1464,12 @@ function isNamedCall(call, name) {
1221
1464
  return callee === name || callee.endsWith(`.${name}`);
1222
1465
  }
1223
1466
  function validateWidgetHierarchy(sourceFile, toolFile, target, variant) {
1224
- const directory = path4.dirname(toolFile);
1467
+ const directory = path5.dirname(toolFile);
1225
1468
  const platformWidget = target === "chatgpt" ? "widget.openai.tsx" : target === "claude" ? "widget.anthropic.tsx" : void 0;
1226
1469
  if (!platformWidget || variant !== "shared") {
1227
1470
  return;
1228
1471
  }
1229
- if (existsSyncSafe(path4.join(directory, platformWidget))) {
1472
+ if (existsSyncSafe(path5.join(directory, platformWidget))) {
1230
1473
  const expectedTool = target === "chatgpt" ? "tool.openai.ts" : "tool.anthropic.ts";
1231
1474
  throw new CompilerError(
1232
1475
  sourceFile,
@@ -1241,7 +1484,7 @@ async function findToolFiles(serverDir, target) {
1241
1484
  const entries = await readdir(serverDir, { withFileTypes: true });
1242
1485
  const files = [];
1243
1486
  for (const entry of entries) {
1244
- const entryPath = path4.join(serverDir, entry.name);
1487
+ const entryPath = path5.join(serverDir, entry.name);
1245
1488
  if (entry.isDirectory()) {
1246
1489
  const candidate = selectToolFile(entryPath, target);
1247
1490
  if (candidate) {
@@ -1260,7 +1503,7 @@ function selectToolFile(directory, target) {
1260
1503
  { name: "tool.ts", variant: "shared" }
1261
1504
  ] : [{ name: "tool.ts", variant: "shared" }];
1262
1505
  for (const candidate of candidates) {
1263
- const filePath = path4.join(directory, candidate.name);
1506
+ const filePath = path5.join(directory, candidate.name);
1264
1507
  if (existsSyncSafe(filePath)) {
1265
1508
  return { filePath, variant: candidate.variant };
1266
1509
  }
@@ -1296,16 +1539,32 @@ function getExecuteFunction(definition, sourceFile) {
1296
1539
  return property;
1297
1540
  }
1298
1541
  if (Node4.isPropertyAssignment(property)) {
1299
- const initializer = property.getInitializer();
1542
+ const initializer = unwrapExpression(property.getInitializer());
1300
1543
  if (initializer && (Node4.isArrowFunction(initializer) || Node4.isFunctionExpression(initializer))) {
1301
1544
  return initializer;
1302
1545
  }
1546
+ const withParamsCall = getWithParamsCall(definition);
1547
+ const wrappedExecute = unwrapExpression(withParamsCall?.getArguments()[1]);
1548
+ if (wrappedExecute && (Node4.isArrowFunction(wrappedExecute) || Node4.isFunctionExpression(wrappedExecute))) {
1549
+ return wrappedExecute;
1550
+ }
1303
1551
  }
1304
1552
  throw new CompilerError(
1305
1553
  sourceFile,
1306
1554
  "execute must be a method, function expression, or arrow function."
1307
1555
  );
1308
1556
  }
1557
+ function getWithParamsCall(definition) {
1558
+ const property = definition.getProperty("execute");
1559
+ if (!property || !Node4.isPropertyAssignment(property)) {
1560
+ return void 0;
1561
+ }
1562
+ const initializer = unwrapExpression(property.getInitializer());
1563
+ if (!initializer || !Node4.isCallExpression(initializer)) {
1564
+ return void 0;
1565
+ }
1566
+ return isNamedCall(initializer, "withParams") ? initializer : void 0;
1567
+ }
1309
1568
  function getRequiredStringProperty(definition, propertyName, sourceFile) {
1310
1569
  const value = getOptionalStringProperty(definition, propertyName);
1311
1570
  if (value === void 0 || !value.trim()) {
@@ -1460,17 +1719,17 @@ function readStringArrayProperty2(definition, propertyName) {
1460
1719
  }
1461
1720
 
1462
1721
  // packages/compiler/src/build.ts
1463
- import { mkdir as mkdir8, writeFile as writeFile8 } from "fs/promises";
1464
- import path17 from "path";
1722
+ import { mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
1723
+ import path19 from "path";
1465
1724
 
1466
1725
  // packages/compiler/src/config.ts
1467
- import path5 from "path";
1726
+ import path6 from "path";
1468
1727
  import {
1469
1728
  Node as Node5,
1470
1729
  SyntaxKind as SyntaxKind3
1471
1730
  } from "ts-morph";
1472
1731
  function analyzeProjectConfig(rootDir) {
1473
- const configPath = path5.join(rootDir, "sidecar.config.ts");
1732
+ const configPath = path6.join(rootDir, "sidecar.config.ts");
1474
1733
  if (!existsSyncSafe(configPath)) {
1475
1734
  return defaultCompilerConfig();
1476
1735
  }
@@ -1482,6 +1741,13 @@ function analyzeProjectConfig(rootDir) {
1482
1741
  return defaultCompilerConfig();
1483
1742
  }
1484
1743
  return {
1744
+ build: {
1745
+ target: readTargetNested(definition, "build", "target"),
1746
+ host: readHostNested(definition, "build", "host"),
1747
+ outDir: readStringNested(definition, "build", "outDir"),
1748
+ plugins: readBooleanNested(definition, "build", "plugins"),
1749
+ widgets: readWidgetBuildConfig(definition)
1750
+ },
1485
1751
  resources: {
1486
1752
  subscribe: readBooleanNested(definition, "resources", "subscribe") ?? false,
1487
1753
  listChanged: readBooleanNested(definition, "resources", "listChanged") ?? false
@@ -1493,13 +1759,52 @@ function analyzeProjectConfig(rootDir) {
1493
1759
  listChanged: readBooleanNested(definition, "tools", "listChanged") ?? false
1494
1760
  },
1495
1761
  pagination: {
1496
- pageSize: readNumberNested(definition, "pagination", "pageSize") ?? 10,
1762
+ pageSize: readNumberNested(definition, "pagination", "pageSize") ?? 50,
1497
1763
  hasOverride: hasProperty(readObjectProperty3(definition, "pagination"), "override")
1498
1764
  }
1499
1765
  };
1500
1766
  }
1767
+ function readWidgetBuildConfig(definition) {
1768
+ const widgets = readObjectProperty3(readObjectProperty3(definition, "build"), "widgets");
1769
+ if (!widgets) {
1770
+ return void 0;
1771
+ }
1772
+ const config = {};
1773
+ const configure = readStringProperty3(widgets, "configure");
1774
+ const esbuild3 = readWidgetEsbuildConfig(widgets);
1775
+ if (configure) config.configure = configure;
1776
+ if (esbuild3) config.esbuild = esbuild3;
1777
+ return Object.keys(config).length ? config : void 0;
1778
+ }
1779
+ function readWidgetEsbuildConfig(widgets) {
1780
+ const esbuild3 = readObjectProperty3(widgets, "esbuild");
1781
+ if (!esbuild3) {
1782
+ return void 0;
1783
+ }
1784
+ const config = {};
1785
+ const alias = readStringRecordProperty(esbuild3, "alias");
1786
+ const define = readStringRecordProperty(esbuild3, "define");
1787
+ const external = readStringArrayProperty3(esbuild3, "external");
1788
+ const loader = readStringRecordProperty(esbuild3, "loader");
1789
+ const conditions = readStringArrayProperty3(esbuild3, "conditions");
1790
+ const mainFields = readStringArrayProperty3(esbuild3, "mainFields");
1791
+ const jsx = readStringProperty3(esbuild3, "jsx");
1792
+ const jsxImportSource = readStringProperty3(esbuild3, "jsxImportSource");
1793
+ if (alias) config.alias = alias;
1794
+ if (define) config.define = define;
1795
+ if (external) config.external = external;
1796
+ if (loader) config.loader = loader;
1797
+ if (conditions) config.conditions = conditions;
1798
+ if (mainFields) config.mainFields = mainFields;
1799
+ if (jsx === "automatic" || jsx === "transform" || jsx === "preserve") {
1800
+ config.jsx = jsx;
1801
+ }
1802
+ if (jsxImportSource) config.jsxImportSource = jsxImportSource;
1803
+ return Object.keys(config).length ? config : void 0;
1804
+ }
1501
1805
  function defaultCompilerConfig() {
1502
1806
  return {
1807
+ build: {},
1503
1808
  resources: {
1504
1809
  subscribe: false,
1505
1810
  listChanged: false
@@ -1511,11 +1816,26 @@ function defaultCompilerConfig() {
1511
1816
  listChanged: false
1512
1817
  },
1513
1818
  pagination: {
1514
- pageSize: 10,
1819
+ pageSize: 50,
1515
1820
  hasOverride: false
1516
1821
  }
1517
1822
  };
1518
1823
  }
1824
+ function readTargetNested(definition, section, propertyName) {
1825
+ const value = readStringNested(definition, section, propertyName);
1826
+ return value === "mcp" || value === "chatgpt" || value === "claude" ? value : void 0;
1827
+ }
1828
+ function readHostNested(definition, section, propertyName) {
1829
+ const value = readStringNested(definition, section, propertyName);
1830
+ return value === "node" || value === "vercel" ? value : void 0;
1831
+ }
1832
+ function readStringNested(definition, section, propertyName) {
1833
+ const object = readObjectProperty3(definition, section);
1834
+ if (!object) {
1835
+ return void 0;
1836
+ }
1837
+ return readStringProperty3(object, propertyName);
1838
+ }
1519
1839
  function readBooleanNested(definition, section, propertyName) {
1520
1840
  const object = readObjectProperty3(definition, section);
1521
1841
  if (!object) {
@@ -1546,6 +1866,9 @@ function readNumberNested(definition, section, propertyName) {
1546
1866
  return initializer && Node5.isNumericLiteral(initializer) ? Number(initializer.getLiteralText()) : void 0;
1547
1867
  }
1548
1868
  function readObjectProperty3(definition, propertyName) {
1869
+ if (!definition) {
1870
+ return void 0;
1871
+ }
1549
1872
  const property = definition.getProperty(propertyName);
1550
1873
  if (!property || !Node5.isPropertyAssignment(property)) {
1551
1874
  return void 0;
@@ -1553,40 +1876,79 @@ function readObjectProperty3(definition, propertyName) {
1553
1876
  const initializer = unwrapExpression(property.getInitializer());
1554
1877
  return initializer && Node5.isObjectLiteralExpression(initializer) ? initializer : void 0;
1555
1878
  }
1879
+ function readStringProperty3(definition, propertyName) {
1880
+ const property = definition.getProperty(propertyName);
1881
+ if (!property || !Node5.isPropertyAssignment(property)) {
1882
+ return void 0;
1883
+ }
1884
+ const initializer = unwrapExpression(property.getInitializer());
1885
+ return initializer && Node5.isStringLiteral(initializer) ? initializer.getLiteralText() : void 0;
1886
+ }
1887
+ function readStringArrayProperty3(definition, propertyName) {
1888
+ const property = definition.getProperty(propertyName);
1889
+ if (!property || !Node5.isPropertyAssignment(property)) {
1890
+ return void 0;
1891
+ }
1892
+ const initializer = unwrapExpression(property.getInitializer());
1893
+ if (!initializer || !Node5.isArrayLiteralExpression(initializer)) {
1894
+ return void 0;
1895
+ }
1896
+ const values = initializer.getElements().map((element) => {
1897
+ const unwrapped = unwrapExpression(element);
1898
+ return unwrapped && Node5.isStringLiteral(unwrapped) ? unwrapped.getLiteralText() : void 0;
1899
+ });
1900
+ return values.every((value) => value !== void 0) ? values : void 0;
1901
+ }
1902
+ function readStringRecordProperty(definition, propertyName) {
1903
+ const object = readObjectProperty3(definition, propertyName);
1904
+ if (!object) {
1905
+ return void 0;
1906
+ }
1907
+ const record = {};
1908
+ for (const property of object.getProperties()) {
1909
+ if (!Node5.isPropertyAssignment(property)) {
1910
+ return void 0;
1911
+ }
1912
+ const initializer = unwrapExpression(property.getInitializer());
1913
+ if (!initializer || !Node5.isStringLiteral(initializer)) {
1914
+ return void 0;
1915
+ }
1916
+ record[property.getName().replace(/^["']|["']$/g, "")] = initializer.getLiteralText();
1917
+ }
1918
+ return record;
1919
+ }
1556
1920
  function hasProperty(definition, propertyName) {
1557
1921
  return Boolean(definition?.getProperty(propertyName));
1558
1922
  }
1559
1923
 
1560
1924
  // packages/compiler/src/diagnostics.ts
1561
- import { existsSync as existsSync3 } from "fs";
1925
+ import { existsSync as existsSync4 } from "fs";
1562
1926
  import { readFile as readFile2 } from "fs/promises";
1563
- import { createRequire } from "module";
1564
- import path6 from "path";
1927
+ import path7 from "path";
1565
1928
  async function collectProjectDiagnostics(rootDir, input) {
1566
1929
  const diagnostics = [];
1567
1930
  const tools = Array.isArray(input) ? input : input.tools;
1568
1931
  const resources = Array.isArray(input) ? [] : input.resources ?? [];
1569
1932
  const prompts = Array.isArray(input) ? [] : input.prompts ?? [];
1570
1933
  const config = Array.isArray(input) ? void 0 : input.config;
1571
- const hasAuthConfig = existsSync3(path6.join(rootDir, "auth.ts"));
1572
- const hasOpenAiAppsSdkUi = canResolveOpenAiAppsSdkUi(rootDir);
1934
+ const hasAuthConfig = existsSync4(path7.join(rootDir, "auth.ts"));
1573
1935
  for (const entry of tools) {
1574
- const toolPath = path6.join(rootDir, entry.sourceFile);
1936
+ const toolPath = path7.join(rootDir, entry.sourceFile);
1575
1937
  const toolSource = await readFile2(toolPath, "utf8");
1576
- diagnostics.push(...diagnoseToolSource(rootDir, entry, toolSource, hasAuthConfig));
1938
+ diagnostics.push(...diagnoseToolSource(entry, toolSource, hasAuthConfig));
1577
1939
  if (entry.widget) {
1578
- const widgetPath = path6.join(rootDir, entry.widget.sourceFile);
1940
+ const widgetPath = path7.join(rootDir, entry.widget.sourceFile);
1579
1941
  const widgetSource = await readFile2(widgetPath, "utf8");
1580
- diagnostics.push(...diagnoseWidgetSource(rootDir, entry, widgetSource, hasOpenAiAppsSdkUi));
1942
+ diagnostics.push(...diagnoseWidgetSource(rootDir, entry, widgetSource));
1581
1943
  }
1582
1944
  }
1583
1945
  for (const entry of resources) {
1584
- const resourcePath = path6.join(rootDir, entry.sourceFile);
1946
+ const resourcePath = path7.join(rootDir, entry.sourceFile);
1585
1947
  const resourceSource = await readFile2(resourcePath, "utf8");
1586
1948
  diagnostics.push(...diagnoseResourceSource(entry, resourceSource));
1587
1949
  }
1588
1950
  for (const entry of prompts) {
1589
- const promptPath = path6.join(rootDir, entry.sourceFile);
1951
+ const promptPath = path7.join(rootDir, entry.sourceFile);
1590
1952
  const promptSource = await readFile2(promptPath, "utf8");
1591
1953
  diagnostics.push(...diagnosePromptSource(entry, promptSource));
1592
1954
  }
@@ -1601,7 +1963,7 @@ function formatDiagnostic(diagnostic) {
1601
1963
  hint: ${diagnostic.hint}` : "";
1602
1964
  return `${location} - ${diagnostic.severity} ${diagnostic.code}: ${diagnostic.message}${hint}`;
1603
1965
  }
1604
- function diagnoseToolSource(rootDir, entry, source, hasAuthConfig) {
1966
+ function diagnoseToolSource(entry, source, hasAuthConfig) {
1605
1967
  const diagnostics = [];
1606
1968
  const toolLocation = locate(source, "tool({");
1607
1969
  if (!entry.description.trim().startsWith("Use this when")) {
@@ -1678,7 +2040,7 @@ function diagnoseToolSource(rootDir, entry, source, hasAuthConfig) {
1678
2040
  }
1679
2041
  return diagnostics.filter((diagnostic) => !isIgnored(source, diagnostic.code));
1680
2042
  }
1681
- function diagnoseWidgetSource(_rootDir, entry, source, hasOpenAiAppsSdkUi) {
2043
+ function diagnoseWidgetSource(_rootDir, entry, source) {
1682
2044
  const diagnostics = [];
1683
2045
  const widgetFile = entry.widget?.sourceFile;
1684
2046
  if (!widgetFile) {
@@ -1714,16 +2076,6 @@ function diagnoseWidgetSource(_rootDir, entry, source, hasOpenAiAppsSdkUi) {
1714
2076
  hint: "Use @sidecar-ai/native for portable primitives. Keep @sidecar-ai/openai/components for widgets intentionally targeted to ChatGPT."
1715
2077
  });
1716
2078
  }
1717
- if (/@sidecar-ai\/openai\/components/.test(source) && !hasOpenAiAppsSdkUi && !isIgnored(source, "SIDECAR_OPENAI_UI_SDK_MISSING")) {
1718
- diagnostics.push({
1719
- severity: "error",
1720
- code: "SIDECAR_OPENAI_UI_SDK_MISSING",
1721
- message: `Widget for "${entry.id}" imports @sidecar-ai/openai/components but @openai/apps-sdk-ui is not installed.`,
1722
- filePath: widgetFile,
1723
- ...locate(source, "@sidecar-ai/openai/components"),
1724
- hint: "Install it with: npm install @openai/apps-sdk-ui"
1725
- });
1726
- }
1727
2079
  if (entry.widget?.variant !== "anthropic" && /@sidecar-ai\/anthropic\/components/.test(source) && !isIgnored(source, "SIDECAR_ANTHROPIC_COMPONENT_CROSS_HOST")) {
1728
2080
  diagnostics.push({
1729
2081
  severity: "warning",
@@ -1813,14 +2165,6 @@ function hasCompanyKnowledgeShape(schema) {
1813
2165
  const id = properties.id;
1814
2166
  return query?.type === "string" || url?.type === "string" || id?.type === "string";
1815
2167
  }
1816
- function canResolveOpenAiAppsSdkUi(rootDir) {
1817
- try {
1818
- createRequire(path6.join(rootDir, "package.json")).resolve("@openai/apps-sdk-ui/css");
1819
- return true;
1820
- } catch {
1821
- return false;
1822
- }
1823
- }
1824
2168
  function locate(source, needle) {
1825
2169
  const index = Math.max(0, source.indexOf(needle));
1826
2170
  const prefix = source.slice(0, index);
@@ -1836,21 +2180,21 @@ function isIgnored(source, code) {
1836
2180
 
1837
2181
  // packages/compiler/src/generated.ts
1838
2182
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
1839
- import path7 from "path";
2183
+ import path8 from "path";
1840
2184
  async function writeGeneratedTypes(rootDir, tools) {
1841
- const generatedDir = path7.join(rootDir, ".sidecar", "generated");
2185
+ const generatedDir = path8.join(rootDir, ".sidecar", "generated");
1842
2186
  await mkdir2(generatedDir, { recursive: true });
1843
2187
  const appCallableTools = tools.filter(isAppCallable);
1844
2188
  const methodNames = uniqueMethodNames(appCallableTools.map((entry) => toIdentifier(entry.id)));
1845
2189
  const imports = appCallableTools.map(
1846
- (entry, index) => `import type tool${index} from ${JSON.stringify(toImportSpecifier(generatedDir, path7.join(rootDir, entry.sourceFile), { extension: "js" }))};`
2190
+ (entry, index) => `import type tool${index} from ${JSON.stringify(toImportSpecifier(generatedDir, path8.join(rootDir, entry.sourceFile), { extension: "js" }))};`
1847
2191
  ).join("\n");
1848
2192
  const ids = appCallableTools.map((entry, index) => ` ${methodNames[index]}: ${JSON.stringify(entry.id)}`).join(",\n");
1849
2193
  const toolTypes = appCallableTools.map(
1850
- (entry, index) => ` ${methodNames[index]}(params: ToolParams<typeof tool${index}>): Promise<ToolOutput<typeof tool${index}>>;`
2194
+ (_entry, index) => ` ${methodNames[index]}(params: ToolParams<typeof tool${index}>): Promise<ToolOutput<typeof tool${index}>>;`
1851
2195
  ).join("\n");
1852
2196
  await writeFile2(
1853
- path7.join(generatedDir, "tools.ts"),
2197
+ path8.join(generatedDir, "tools.ts"),
1854
2198
  `/**
1855
2199
  * Generated Sidecar widget tool client.
1856
2200
  *
@@ -1898,19 +2242,15 @@ function uniqueMethodNames(names) {
1898
2242
  });
1899
2243
  }
1900
2244
 
1901
- // packages/compiler/src/plugins.ts
1902
- import { mkdir as mkdir7, writeFile as writeFile7 } from "fs/promises";
1903
- import path14 from "path";
1904
-
1905
2245
  // packages/compiler/src/identity.ts
1906
2246
  import { readFile as readFile3 } from "fs/promises";
1907
- import path8 from "path";
2247
+ import path9 from "path";
1908
2248
  async function loadProjectIdentity(rootDir) {
1909
- const packageJson = await readOptionalJson(path8.join(rootDir, "package.json"));
2249
+ const packageJson = await readOptionalJson(path9.join(rootDir, "package.json"));
1910
2250
  const configText = await readOptionalText(
1911
- path8.join(rootDir, "sidecar.config.ts")
2251
+ path9.join(rootDir, "sidecar.config.ts")
1912
2252
  );
1913
- const name = readConfigString(configText, "name") ?? packageJson?.name ?? path8.basename(rootDir);
2253
+ const name = readConfigString(configText, "name") ?? packageJson?.name ?? path9.basename(rootDir);
1914
2254
  const version = readConfigString(configText, "version") ?? packageJson?.version ?? "0.0.0-dev";
1915
2255
  const description = readConfigString(configText, "description") ?? packageJson?.description ?? `${name} Sidecar app.`;
1916
2256
  return {
@@ -1942,30 +2282,34 @@ function readConfigString(configText, key) {
1942
2282
  return match?.[1];
1943
2283
  }
1944
2284
 
2285
+ // packages/compiler/src/plugins.ts
2286
+ import { mkdir as mkdir7, writeFile as writeFile7 } from "fs/promises";
2287
+ import path15 from "path";
2288
+
1945
2289
  // packages/compiler/src/plugin-components/agents.ts
1946
2290
  import { mkdir as mkdir3, readdir as readdir2, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
1947
- import path9 from "path";
2291
+ import path10 from "path";
1948
2292
  async function emitClaudeAgents(rootDir, destination) {
1949
- const source = path9.join(rootDir, "agents");
2293
+ const source = path10.join(rootDir, "agents");
1950
2294
  if (!existsSyncSafe(source)) {
1951
2295
  return;
1952
2296
  }
1953
2297
  const entries = await readdir2(source, { withFileTypes: true });
1954
2298
  const agentDirs = entries.filter(
1955
- (entry) => entry.isDirectory() && existsSyncSafe(path9.join(source, entry.name, "agent.ts"))
2299
+ (entry) => entry.isDirectory() && existsSyncSafe(path10.join(source, entry.name, "agent.ts"))
1956
2300
  );
1957
2301
  if (!agentDirs.length) {
1958
2302
  return;
1959
2303
  }
1960
2304
  await mkdir3(destination, { recursive: true });
1961
2305
  for (const entry of agentDirs) {
1962
- const sourceText = await readFile4(path9.join(source, entry.name, "agent.ts"), "utf8");
2306
+ const sourceText = await readFile4(path10.join(source, entry.name, "agent.ts"), "utf8");
1963
2307
  const markdown = parseClaudeAgent(
1964
2308
  sourceText,
1965
2309
  entry.name
1966
2310
  );
1967
2311
  const agentName = readObjectString(sourceText, "name") ?? entry.name;
1968
- await writeFile3(path9.join(destination, `${safeFileStem(agentName)}.md`), markdown);
2312
+ await writeFile3(path10.join(destination, `${safeFileStem(agentName)}.md`), markdown);
1969
2313
  }
1970
2314
  }
1971
2315
  function parseClaudeAgent(source, fallbackName) {
@@ -1994,41 +2338,41 @@ ${prompt.trim()}
1994
2338
 
1995
2339
  // packages/compiler/src/plugin-components/commands.ts
1996
2340
  import { cp, lstat, mkdir as mkdir4, readdir as readdir3, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
1997
- import path10 from "path";
2341
+ import path11 from "path";
1998
2342
  async function copyCommands(rootDir, destination) {
1999
- const source = path10.join(rootDir, "commands");
2343
+ const source = path11.join(rootDir, "commands");
2000
2344
  if (!existsSyncSafe(source)) {
2001
2345
  return;
2002
2346
  }
2003
2347
  await mkdir4(destination, { recursive: true });
2004
2348
  const entries = await readdir3(source, { withFileTypes: true });
2005
2349
  for (const entry of entries) {
2006
- const sourcePath = path10.join(source, entry.name);
2350
+ const sourcePath = path11.join(source, entry.name);
2007
2351
  if (entry.isFile() && entry.name.endsWith(".md")) {
2008
2352
  if (await safeCommandCopyFilter(sourcePath)) {
2009
- await cp(sourcePath, path10.join(destination, safeFileStem(entry.name.replace(/\.md$/, "")) + ".md"));
2353
+ await cp(sourcePath, path11.join(destination, safeFileStem(entry.name.replace(/\.md$/, "")) + ".md"));
2010
2354
  }
2011
2355
  continue;
2012
2356
  }
2013
2357
  if (!entry.isDirectory()) {
2014
2358
  continue;
2015
2359
  }
2016
- const dynamicCommand = path10.join(sourcePath, "command.ts");
2360
+ const dynamicCommand = path11.join(sourcePath, "command.ts");
2017
2361
  if (existsSyncSafe(dynamicCommand)) {
2018
2362
  const sourceText = await readFile5(dynamicCommand, "utf8");
2019
2363
  const markdown = parseDynamicCommand(sourceText, entry.name);
2020
2364
  const commandName = readObjectString(sourceText, "name") ?? entry.name;
2021
- await writeFile4(path10.join(destination, `${safeFileStem(commandName)}.md`), markdown);
2365
+ await writeFile4(path11.join(destination, `${safeFileStem(commandName)}.md`), markdown);
2022
2366
  continue;
2023
2367
  }
2024
- await cp(sourcePath, path10.join(destination, entry.name), {
2368
+ await cp(sourcePath, path11.join(destination, entry.name), {
2025
2369
  recursive: true,
2026
2370
  filter: safeCommandCopyFilter
2027
2371
  });
2028
2372
  }
2029
2373
  }
2030
2374
  async function safeCommandCopyFilter(sourcePath) {
2031
- const basename = path10.basename(sourcePath);
2375
+ const basename = path11.basename(sourcePath);
2032
2376
  if (basename === ".env" || basename.startsWith(".env.") || basename === "node_modules" || basename === ".git") {
2033
2377
  return false;
2034
2378
  }
@@ -2057,32 +2401,32 @@ ${Object.entries(frontmatter).map(([key, value]) => `${key}: ${yamlScalar(value)
2057
2401
 
2058
2402
  // packages/compiler/src/plugin-components/hooks.ts
2059
2403
  import { mkdir as mkdir5, readdir as readdir4, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
2060
- import path11 from "path";
2404
+ import path12 from "path";
2061
2405
  import {
2062
2406
  Node as Node6,
2063
2407
  Project as Project2,
2064
2408
  SyntaxKind as SyntaxKind4
2065
2409
  } from "ts-morph";
2066
2410
  async function copyHooks(rootDir, destination) {
2067
- const source = path11.join(rootDir, "hooks");
2411
+ const source = path12.join(rootDir, "hooks");
2068
2412
  if (!existsSyncSafe(source)) {
2069
2413
  return;
2070
2414
  }
2071
2415
  const entries = await readdir4(source, { withFileTypes: true });
2072
2416
  const hookDirs = entries.filter(
2073
- (entry) => entry.isDirectory() && existsSyncSafe(path11.join(source, entry.name, "hook.ts"))
2417
+ (entry) => entry.isDirectory() && existsSyncSafe(path12.join(source, entry.name, "hook.ts"))
2074
2418
  );
2075
2419
  if (!hookDirs.length) {
2076
2420
  return;
2077
2421
  }
2078
2422
  const config = {};
2079
2423
  for (const entry of hookDirs) {
2080
- const filePath = path11.join(source, entry.name, "hook.ts");
2424
+ const filePath = path12.join(source, entry.name, "hook.ts");
2081
2425
  mergeHooks(config, parseHookFile(await readFile6(filePath, "utf8"), entry.name));
2082
2426
  }
2083
2427
  await mkdir5(destination, { recursive: true });
2084
2428
  await writeFile5(
2085
- path11.join(destination, "hooks.json"),
2429
+ path12.join(destination, "hooks.json"),
2086
2430
  `${JSON.stringify(config, null, 2)}
2087
2431
  `
2088
2432
  );
@@ -2109,7 +2453,7 @@ function parseHookFile(source, fallbackName) {
2109
2453
  throw new Error(`hooks/${fallbackName}/hook.ts must default-export hook({ ... }) or hooks({ ... }).`);
2110
2454
  }
2111
2455
  function parseHookDefinition(definition, fallbackName) {
2112
- const event = readStringProperty3(definition, "event");
2456
+ const event = readStringProperty4(definition, "event");
2113
2457
  if (!event) {
2114
2458
  throw new Error(`hooks/${fallbackName}/hook.ts must declare an event string.`);
2115
2459
  }
@@ -2119,7 +2463,7 @@ function parseHookDefinition(definition, fallbackName) {
2119
2463
  }
2120
2464
  return {
2121
2465
  event,
2122
- matcher: readStringProperty3(definition, "matcher"),
2466
+ matcher: readStringProperty4(definition, "matcher"),
2123
2467
  run: hooks
2124
2468
  };
2125
2469
  }
@@ -2139,7 +2483,7 @@ function parseHooksDefinition(definition, fallbackName) {
2139
2483
  throw new Error(`hooks/${fallbackName}/hook.ts event "${event}" entries must be objects.`);
2140
2484
  }
2141
2485
  return stripUndefined2({
2142
- matcher: readStringProperty3(entry, "matcher"),
2486
+ matcher: readStringProperty4(entry, "matcher"),
2143
2487
  hooks: readHookArray(entry, "run", fallbackName)
2144
2488
  });
2145
2489
  });
@@ -2162,7 +2506,7 @@ function parseHookHandlers(handlers, fallbackName) {
2162
2506
  }
2163
2507
  function parseHookHandler(handler, fallbackName) {
2164
2508
  if (Node6.isObjectLiteralExpression(handler)) {
2165
- const type = readStringProperty3(handler, "type");
2509
+ const type = readStringProperty4(handler, "type");
2166
2510
  if (type !== "command" && type !== "http") {
2167
2511
  throw new Error(`hooks/${fallbackName}/hook.ts hook handlers need type "command" or "http".`);
2168
2512
  }
@@ -2212,7 +2556,7 @@ function objectLiteralToRecord(object) {
2212
2556
  }
2213
2557
  return stripUndefined2(record);
2214
2558
  }
2215
- function readStringProperty3(object, propertyName) {
2559
+ function readStringProperty4(object, propertyName) {
2216
2560
  const property = object.getProperty(propertyName);
2217
2561
  if (!property || !Node6.isPropertyAssignment(property)) {
2218
2562
  return void 0;
@@ -2256,7 +2600,7 @@ function mergeHooks(target, source) {
2256
2600
 
2257
2601
  // packages/compiler/src/plugin-components/passthrough.ts
2258
2602
  import { cp as cp2, lstat as lstat2 } from "fs/promises";
2259
- import path12 from "path";
2603
+ import path13 from "path";
2260
2604
  async function copyClaudePassthroughDirectories(rootDir, pluginDir) {
2261
2605
  for (const directory of [
2262
2606
  "assets",
@@ -2265,18 +2609,18 @@ async function copyClaudePassthroughDirectories(rootDir, pluginDir) {
2265
2609
  "output-styles",
2266
2610
  "themes"
2267
2611
  ]) {
2268
- const source = path12.join(rootDir, directory);
2612
+ const source = path13.join(rootDir, directory);
2269
2613
  if (!existsSyncSafe(source)) {
2270
2614
  continue;
2271
2615
  }
2272
- await cp2(source, path12.join(pluginDir, directory), {
2616
+ await cp2(source, path13.join(pluginDir, directory), {
2273
2617
  recursive: true,
2274
2618
  filter: safePluginCopyFilter
2275
2619
  });
2276
2620
  }
2277
2621
  }
2278
2622
  async function safePluginCopyFilter(sourcePath) {
2279
- const basename = path12.basename(sourcePath);
2623
+ const basename = path13.basename(sourcePath);
2280
2624
  if (basename === "node_modules" || basename === ".git" || basename === ".env" || basename.startsWith(".env.")) {
2281
2625
  return false;
2282
2626
  }
@@ -2285,11 +2629,11 @@ async function safePluginCopyFilter(sourcePath) {
2285
2629
  }
2286
2630
 
2287
2631
  // packages/compiler/src/plugin-components/skills.ts
2288
- import { existsSync as existsSync4 } from "fs";
2632
+ import { existsSync as existsSync5 } from "fs";
2289
2633
  import { cp as cp3, lstat as lstat3, mkdir as mkdir6, readdir as readdir5, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
2290
- import path13 from "path";
2634
+ import path14 from "path";
2291
2635
  async function copySkills(rootDir, destination) {
2292
- const source = path13.join(rootDir, "skills");
2636
+ const source = path14.join(rootDir, "skills");
2293
2637
  if (!existsSyncSafe(source)) {
2294
2638
  return;
2295
2639
  }
@@ -2299,27 +2643,27 @@ async function copySkills(rootDir, destination) {
2299
2643
  if (!entry.isDirectory()) {
2300
2644
  continue;
2301
2645
  }
2302
- const sourceDir = path13.join(source, entry.name);
2303
- const destinationDir = path13.join(destination, entry.name);
2304
- const staticSkill = path13.join(sourceDir, "SKILL.md");
2305
- const dynamicSkill = path13.join(sourceDir, "skill.ts");
2646
+ const sourceDir = path14.join(source, entry.name);
2647
+ const destinationDir = path14.join(destination, entry.name);
2648
+ const staticSkill = path14.join(sourceDir, "SKILL.md");
2649
+ const dynamicSkill = path14.join(sourceDir, "skill.ts");
2306
2650
  await mkdir6(destinationDir, { recursive: true });
2307
- if (existsSync4(staticSkill)) {
2651
+ if (existsSync5(staticSkill)) {
2308
2652
  await cp3(sourceDir, destinationDir, {
2309
2653
  recursive: true,
2310
- filter: async (sourcePath) => !sourcePath.endsWith(`${path13.sep}skill.ts`) && await safeSkillCopyFilter(sourcePath)
2654
+ filter: async (sourcePath) => !sourcePath.endsWith(`${path14.sep}skill.ts`) && await safeSkillCopyFilter(sourcePath)
2311
2655
  });
2312
- } else if (existsSync4(dynamicSkill)) {
2656
+ } else if (existsSync5(dynamicSkill)) {
2313
2657
  const generated = parseDynamicSkill(
2314
2658
  await readFile7(dynamicSkill, "utf8"),
2315
2659
  entry.name
2316
2660
  );
2317
- await writeFile6(path13.join(destinationDir, "SKILL.md"), generated);
2661
+ await writeFile6(path14.join(destinationDir, "SKILL.md"), generated);
2318
2662
  }
2319
2663
  }
2320
2664
  }
2321
2665
  async function safeSkillCopyFilter(sourcePath) {
2322
- const basename = path13.basename(sourcePath);
2666
+ const basename = path14.basename(sourcePath);
2323
2667
  if (basename === ".env" || basename.startsWith(".env.") || basename === "node_modules" || basename === ".git") {
2324
2668
  return false;
2325
2669
  }
@@ -2345,25 +2689,25 @@ async function buildPluginPackages(rootDir, outRoot, manifest) {
2345
2689
  await buildClaudePlugin(rootDir, outRoot, identity, manifest);
2346
2690
  }
2347
2691
  async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2348
- const pluginDir = path14.join(outRoot, "claude-plugin");
2349
- await mkdir7(path14.join(pluginDir, ".claude-plugin"), { recursive: true });
2350
- await copySkills(rootDir, path14.join(pluginDir, "skills"));
2351
- await copyCommands(rootDir, path14.join(pluginDir, "commands"));
2352
- await copyHooks(rootDir, path14.join(pluginDir, "hooks"));
2353
- await emitClaudeAgents(rootDir, path14.join(pluginDir, "agents"));
2692
+ const pluginDir = path15.join(outRoot, "claude-plugin");
2693
+ await mkdir7(path15.join(pluginDir, ".claude-plugin"), { recursive: true });
2694
+ await copySkills(rootDir, path15.join(pluginDir, "skills"));
2695
+ await copyCommands(rootDir, path15.join(pluginDir, "commands"));
2696
+ await copyHooks(rootDir, path15.join(pluginDir, "hooks"));
2697
+ await emitClaudeAgents(rootDir, path15.join(pluginDir, "agents"));
2354
2698
  await copyClaudePassthroughDirectories(rootDir, pluginDir);
2355
- await writeJson(path14.join(pluginDir, ".claude-plugin", "plugin.json"), {
2699
+ await writeJson(path15.join(pluginDir, ".claude-plugin", "plugin.json"), {
2356
2700
  name: identity.slug,
2357
2701
  version: identity.version,
2358
2702
  description: identity.description,
2359
2703
  displayName: identity.name,
2360
2704
  installationPreference: "available"
2361
2705
  });
2362
- await writeJson(path14.join(pluginDir, "version.json"), {
2706
+ await writeJson(path15.join(pluginDir, "version.json"), {
2363
2707
  version: identity.version,
2364
2708
  generatedBy: "sidecar"
2365
2709
  });
2366
- await writeJson(path14.join(pluginDir, ".mcp.json"), {
2710
+ await writeJson(path15.join(pluginDir, ".mcp.json"), {
2367
2711
  mcpServers: {
2368
2712
  [identity.slug]: {
2369
2713
  type: "http",
@@ -2371,7 +2715,7 @@ async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2371
2715
  }
2372
2716
  }
2373
2717
  });
2374
- await writeJson(path14.join(pluginDir, "settings.json"), {
2718
+ await writeJson(path15.join(pluginDir, "settings.json"), {
2375
2719
  sidecar: {
2376
2720
  tools: manifest.tools.map((entry) => entry.id),
2377
2721
  resources: manifest.resources.map((entry) => entry.uri),
@@ -2379,21 +2723,21 @@ async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2379
2723
  }
2380
2724
  });
2381
2725
  await writeFile7(
2382
- path14.join(pluginDir, "README.md"),
2726
+ path15.join(pluginDir, "README.md"),
2383
2727
  `# ${identity.name} Claude Plugin
2384
2728
 
2385
2729
  This package was generated by Sidecar.
2386
2730
 
2387
2731
  Set \`SIDECAR_MCP_URL\` to the hosted MCP endpoint for this app.
2388
2732
 
2389
- For local testing, run \`sidecar dev --tunnel\` and use the printed HTTPS MCP URL.
2733
+ 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.
2390
2734
 
2391
2735
  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.
2392
2736
  `
2393
2737
  );
2394
2738
  }
2395
2739
  async function writeJson(filePath, value) {
2396
- await mkdir7(path14.dirname(filePath), { recursive: true });
2740
+ await mkdir7(path15.dirname(filePath), { recursive: true });
2397
2741
  await writeFile7(
2398
2742
  filePath,
2399
2743
  `${JSON.stringify(stripUndefined2(value), null, 2)}
@@ -2403,13 +2747,13 @@ async function writeJson(filePath, value) {
2403
2747
 
2404
2748
  // packages/compiler/src/prompts.ts
2405
2749
  import { readdir as readdir6 } from "fs/promises";
2406
- import path15 from "path";
2750
+ import path16 from "path";
2407
2751
  import {
2408
2752
  Node as Node7,
2409
2753
  SyntaxKind as SyntaxKind5
2410
2754
  } from "ts-morph";
2411
2755
  async function analyzeProjectPrompts(rootDir) {
2412
- const promptFiles = await findPromptFiles(path15.join(rootDir, "prompts"));
2756
+ const promptFiles = await findPromptFiles(path16.join(rootDir, "prompts"));
2413
2757
  if (promptFiles.length === 0) {
2414
2758
  return [];
2415
2759
  }
@@ -2421,7 +2765,7 @@ async function analyzeProjectPrompts(rootDir) {
2421
2765
  function analyzePromptFile(sourceFile, rootDir) {
2422
2766
  const definition = findPromptDefinition(sourceFile);
2423
2767
  const absoluteFile = sourceFile.getFilePath();
2424
- const directory = path15.basename(path15.dirname(absoluteFile));
2768
+ const directory = path16.basename(path16.dirname(absoluteFile));
2425
2769
  const name = getOptionalStringProperty2(definition, "name") ?? safePathSegment(directory);
2426
2770
  const title = getRequiredStringProperty2(definition, "title", sourceFile);
2427
2771
  const description = getOptionalStringProperty2(definition, "description");
@@ -2437,7 +2781,7 @@ function analyzePromptFile(sourceFile, rootDir) {
2437
2781
  throw new CompilerError(sourceFile, "prompt({ ... }) must include a run method.");
2438
2782
  }
2439
2783
  return {
2440
- sourceFile: path15.relative(rootDir, absoluteFile),
2784
+ sourceFile: path16.relative(rootDir, absoluteFile),
2441
2785
  directory,
2442
2786
  name,
2443
2787
  title,
@@ -2456,7 +2800,7 @@ async function findPromptFiles(promptsDir) {
2456
2800
  if (!entry.isDirectory()) {
2457
2801
  continue;
2458
2802
  }
2459
- const filePath = path15.join(promptsDir, entry.name, "prompt.ts");
2803
+ const filePath = path16.join(promptsDir, entry.name, "prompt.ts");
2460
2804
  if (existsSyncSafe(filePath)) {
2461
2805
  files.push(filePath);
2462
2806
  }
@@ -2578,7 +2922,7 @@ function readIcons(definition) {
2578
2922
  return [{
2579
2923
  src,
2580
2924
  mimeType: getOptionalStringProperty2(object, "mimeType"),
2581
- sizes: readStringArrayProperty3(object, "sizes")
2925
+ sizes: readStringArrayProperty4(object, "sizes")
2582
2926
  }];
2583
2927
  });
2584
2928
  return icons.length ? icons : void 0;
@@ -2591,7 +2935,7 @@ function readObjectProperty4(definition, propertyName) {
2591
2935
  const initializer = unwrapExpression(property.getInitializer());
2592
2936
  return initializer && Node7.isObjectLiteralExpression(initializer) ? initializer : void 0;
2593
2937
  }
2594
- function readStringArrayProperty3(definition, propertyName) {
2938
+ function readStringArrayProperty4(definition, propertyName) {
2595
2939
  const property = definition.getProperty(propertyName);
2596
2940
  if (!property || !Node7.isPropertyAssignment(property)) {
2597
2941
  return void 0;
@@ -2605,13 +2949,13 @@ function readStringArrayProperty3(definition, propertyName) {
2605
2949
 
2606
2950
  // packages/compiler/src/resources.ts
2607
2951
  import { readdir as readdir7 } from "fs/promises";
2608
- import path16 from "path";
2952
+ import path17 from "path";
2609
2953
  import {
2610
2954
  Node as Node8,
2611
2955
  SyntaxKind as SyntaxKind6
2612
2956
  } from "ts-morph";
2613
2957
  async function analyzeProjectResources(rootDir) {
2614
- const resourceFiles = await findResourceFiles(path16.join(rootDir, "resources"));
2958
+ const resourceFiles = await findResourceFiles(path17.join(rootDir, "resources"));
2615
2959
  if (resourceFiles.length === 0) {
2616
2960
  return [];
2617
2961
  }
@@ -2623,7 +2967,7 @@ async function analyzeProjectResources(rootDir) {
2623
2967
  function analyzeResourceFile(sourceFile, rootDir) {
2624
2968
  const definition = findResourceDefinition(sourceFile);
2625
2969
  const absoluteFile = sourceFile.getFilePath();
2626
- const directory = path16.basename(path16.dirname(absoluteFile));
2970
+ const directory = path17.basename(path17.dirname(absoluteFile));
2627
2971
  const defaultUri = `sidecar://resources/${safePathSegment(directory)}`;
2628
2972
  const name = getRequiredStringProperty3(definition, "name", sourceFile);
2629
2973
  const uri = getOptionalStringProperty3(definition, "uri") ?? defaultUri;
@@ -2641,7 +2985,7 @@ function analyzeResourceFile(sourceFile, rootDir) {
2641
2985
  throw new CompilerError(sourceFile, "resource({ ... }) must include a read method.");
2642
2986
  }
2643
2987
  return {
2644
- sourceFile: path16.relative(rootDir, absoluteFile),
2988
+ sourceFile: path17.relative(rootDir, absoluteFile),
2645
2989
  directory,
2646
2990
  uri,
2647
2991
  name,
@@ -2664,7 +3008,7 @@ async function findResourceFiles(resourcesDir) {
2664
3008
  if (!entry.isDirectory()) {
2665
3009
  continue;
2666
3010
  }
2667
- const filePath = path16.join(resourcesDir, entry.name, "resource.ts");
3011
+ const filePath = path17.join(resourcesDir, entry.name, "resource.ts");
2668
3012
  if (existsSyncSafe(filePath)) {
2669
3013
  files.push(filePath);
2670
3014
  }
@@ -2743,7 +3087,7 @@ function readAnnotations2(definition) {
2743
3087
  return void 0;
2744
3088
  }
2745
3089
  const annotations = {};
2746
- const audience = readStringArrayProperty4(initializer, "audience")?.filter((value) => value === "user" || value === "assistant");
3090
+ const audience = readStringArrayProperty5(initializer, "audience")?.filter((value) => value === "user" || value === "assistant");
2747
3091
  const priority = getOptionalNumberProperty(initializer, "priority");
2748
3092
  const lastModified = getOptionalStringProperty3(initializer, "lastModified");
2749
3093
  if (audience?.length) annotations.audience = audience;
@@ -2772,7 +3116,7 @@ function readIcons2(definition) {
2772
3116
  return [{
2773
3117
  src,
2774
3118
  mimeType: getOptionalStringProperty3(object, "mimeType"),
2775
- sizes: readStringArrayProperty4(object, "sizes")
3119
+ sizes: readStringArrayProperty5(object, "sizes")
2776
3120
  }];
2777
3121
  });
2778
3122
  return icons.length ? icons : void 0;
@@ -2785,7 +3129,7 @@ function readObjectProperty5(definition, propertyName) {
2785
3129
  const initializer = unwrapExpression(property.getInitializer());
2786
3130
  return initializer && Node8.isObjectLiteralExpression(initializer) ? initializer : void 0;
2787
3131
  }
2788
- function readStringArrayProperty4(definition, propertyName) {
3132
+ function readStringArrayProperty5(definition, propertyName) {
2789
3133
  const property = definition.getProperty(propertyName);
2790
3134
  if (!property || !Node8.isPropertyAssignment(property)) {
2791
3135
  return void 0;
@@ -2797,15 +3141,332 @@ function readStringArrayProperty4(definition, propertyName) {
2797
3141
  return initializer.getElements().filter(Node8.isStringLiteral).map((element) => element.getLiteralText());
2798
3142
  }
2799
3143
 
3144
+ // packages/compiler/src/server-output.ts
3145
+ import { existsSync as existsSync6 } from "fs";
3146
+ import { mkdir as mkdir8, rm, writeFile as writeFile8 } from "fs/promises";
3147
+ import path18 from "path";
3148
+ import { build as esbuild2 } from "esbuild";
3149
+ var SERVER_ENTRYPOINT = "server/index.js";
3150
+ var VERCEL_FUNCTION_DIR = "functions/api/sidecar.func";
3151
+ var VERCEL_ENTRYPOINT = `${VERCEL_FUNCTION_DIR}/index.js`;
3152
+ var VERCEL_HANDLER_FILE = "index.js";
3153
+ async function buildServerOutput(rootDir, outDir, manifest, identity, host = "node", options = {}) {
3154
+ const cacheDir = path18.join(rootDir, ".sidecar", "cache", "server");
3155
+ await mkdir8(cacheDir, { recursive: true });
3156
+ const entryFile = path18.join(cacheDir, "index.ts");
3157
+ await writeFile8(entryFile, renderServerEntry(rootDir, entryFile, manifest, identity));
3158
+ const serverFile = path18.join(outDir, SERVER_ENTRYPOINT);
3159
+ await mkdir8(path18.dirname(serverFile), { recursive: true });
3160
+ await esbuild2({
3161
+ absWorkingDir: rootDir,
3162
+ alias: sidecarBundleAliases(rootDir),
3163
+ banner: {
3164
+ js: `import { createRequire as __sidecarCreateRequire } from "node:module";
3165
+ const require = __sidecarCreateRequire(import.meta.url);`
3166
+ },
3167
+ bundle: true,
3168
+ entryPoints: [entryFile],
3169
+ format: "esm",
3170
+ legalComments: "none",
3171
+ minify: false,
3172
+ nodePaths: [
3173
+ path18.join(rootDir, "node_modules"),
3174
+ path18.join(process.cwd(), "node_modules")
3175
+ ],
3176
+ outfile: serverFile,
3177
+ platform: "node",
3178
+ sourcemap: false,
3179
+ target: "node20"
3180
+ });
3181
+ await writeFile8(path18.join(outDir, "package.json"), renderServerPackage(identity));
3182
+ if (host === "vercel") {
3183
+ const vercelOutputDir = options.vercelOutputDir ?? outDir;
3184
+ await rm(path18.join(vercelOutputDir, "api"), { recursive: true, force: true });
3185
+ await rm(path18.join(vercelOutputDir, "vercel.json"), { force: true });
3186
+ await writeFile8(path18.join(outDir, VERCEL_HANDLER_FILE), renderVercelEntrypoint());
3187
+ await writeFile8(path18.join(outDir, ".vc-config.json"), renderVercelFunctionConfig());
3188
+ await mkdir8(vercelOutputDir, { recursive: true });
3189
+ await writeFile8(path18.join(vercelOutputDir, "config.json"), renderVercelOutputConfig());
3190
+ } else {
3191
+ await rm(path18.join(outDir, "api"), { recursive: true, force: true });
3192
+ await rm(path18.join(outDir, "vercel.json"), { force: true });
3193
+ }
3194
+ }
3195
+ function renderServerEntry(rootDir, entryFile, manifest, identity) {
3196
+ const entryDir = path18.dirname(entryFile);
3197
+ const tools = manifest.tools;
3198
+ const resources = manifest.resources;
3199
+ const prompts = manifest.prompts;
3200
+ const imports = [
3201
+ `import { readFileSync, realpathSync } from "node:fs";`,
3202
+ `import { createServer } from "node:http";`,
3203
+ `import { pathToFileURL } from "node:url";`,
3204
+ `import { createSidecarHttpHandler } from "@sidecar-ai/server";`,
3205
+ `import { isSidecarAuth } from "@sidecar-ai/auth";`,
3206
+ `import { isSidecarPrompt, isSidecarResource, isSidecarTool } from "@sidecar-ai/core";`,
3207
+ `import { isSidecarProxy } from "@sidecar-ai/server/proxy";`,
3208
+ ...tools.map(
3209
+ (entry, index) => `import tool${index} from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, entry.sourceFile)))};`
3210
+ ),
3211
+ ...resources.map(
3212
+ (entry, index) => `import resource${index} from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, entry.sourceFile)))};`
3213
+ ),
3214
+ ...prompts.map(
3215
+ (entry, index) => `import prompt${index} from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, entry.sourceFile)))};`
3216
+ ),
3217
+ existsSync6(path18.join(rootDir, "auth.ts")) ? `import authExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "auth.ts")))};` : `const authExport = undefined;`,
3218
+ existsSync6(path18.join(rootDir, "proxy.ts")) ? `import proxyExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "proxy.ts")))};` : `const proxyExport = undefined;`,
3219
+ existsSync6(path18.join(rootDir, "sidecar.config.ts")) ? `import configExport from ${JSON.stringify(toImportSpecifier(entryDir, path18.join(rootDir, "sidecar.config.ts")))};` : `const configExport = undefined;`
3220
+ ].join("\n");
3221
+ return `${imports}
3222
+
3223
+ const manifest = ${JSON.stringify(manifest, null, 2)};
3224
+ const identity = ${JSON.stringify(identity, null, 2)};
3225
+
3226
+ const loadedAuth = authExport === undefined ? undefined : assertAuth(authExport);
3227
+ const auth = loadedAuth && process.env.SIDECAR_MCP_URL
3228
+ ? loadedAuth.withResource(process.env.SIDECAR_MCP_URL)
3229
+ : loadedAuth;
3230
+ const proxy = proxyExport === undefined ? undefined : assertProxy(proxyExport);
3231
+ const runtimeConfig = configExport && typeof configExport === "object" ? configExport : undefined;
3232
+
3233
+ if (auth && !process.env.SIDECAR_MCP_URL) {
3234
+ console.warn("Sidecar auth is enabled. Set SIDECAR_MCP_URL to the public https://.../mcp URL before hosting.");
3235
+ }
3236
+
3237
+ export const handler = createSidecarHttpHandler({
3238
+ name: identity.name,
3239
+ version: identity.version,
3240
+ path: process.env.SIDECAR_MCP_PATH ?? "/mcp",
3241
+ publicUrl: process.env.SIDECAR_PUBLIC_URL ?? process.env.SIDECAR_MCP_URL,
3242
+ auth,
3243
+ proxy,
3244
+ tools: [
3245
+ ${tools.map((entry, index) => ` loadTool(${JSON.stringify(entry.sourceFile)}, tool${index}, manifest.tools[${index}].descriptor),`).join("\n")}
3246
+ ],
3247
+ resources: [
3248
+ ${renderWidgetResources(manifest)}
3249
+ ${resources.map((entry, index) => ` loadResource(${JSON.stringify(entry.sourceFile)}, resource${index}, manifest.resources[${index}]),`).join("\n")}
3250
+ ],
3251
+ resourceTemplates: manifest.resourceTemplates.map((entry) => ({ descriptor: entry.descriptor })),
3252
+ prompts: [
3253
+ ${prompts.map((entry, index) => ` loadPrompt(${JSON.stringify(entry.sourceFile)}, prompt${index}, manifest.prompts[${index}].descriptor),`).join("\n")}
3254
+ ],
3255
+ capabilities: {
3256
+ tools: runtimeConfig?.tools ?? manifest.config.tools,
3257
+ resources: runtimeConfig?.resources ?? manifest.config.resources,
3258
+ prompts: runtimeConfig?.prompts ?? manifest.config.prompts,
3259
+ },
3260
+ pagination: runtimeConfig?.pagination ?? {
3261
+ pageSize: manifest.config.pagination.pageSize,
3262
+ },
3263
+ });
3264
+
3265
+ export default handler;
3266
+
3267
+ export const server = createServer(handler);
3268
+
3269
+ if (isDirectRun()) {
3270
+ const port = readPort();
3271
+ const host = process.env.SIDECAR_HOST ?? process.env.HOST ?? "0.0.0.0";
3272
+ server.listen(port, host, () => {
3273
+ const localHost = host === "0.0.0.0" ? "127.0.0.1" : host;
3274
+ console.log(\`MCP running on Streamable HTTP at http://\${localHost}:\${port}\${process.env.SIDECAR_MCP_PATH ?? "/mcp"}\`);
3275
+ });
3276
+
3277
+ process.on("SIGTERM", () => shutdown());
3278
+ process.on("SIGINT", () => shutdown());
3279
+ }
3280
+
3281
+ function loadTool(sourceFile, value, descriptor) {
3282
+ const tool = unwrapRuntimeDefault(value);
3283
+ if (!isSidecarTool(tool)) {
3284
+ throw new Error(\`\${sourceFile} did not default-export a Sidecar tool.\`);
3285
+ }
3286
+ return { tool, descriptor };
3287
+ }
3288
+
3289
+ function loadResource(sourceFile, value, entry) {
3290
+ const resource = unwrapRuntimeDefault(value);
3291
+ if (!isSidecarResource(resource)) {
3292
+ throw new Error(\`\${sourceFile} did not default-export a Sidecar resource.\`);
3293
+ }
3294
+ return {
3295
+ uri: entry.uri,
3296
+ descriptor: entry.descriptor,
3297
+ resource,
3298
+ };
3299
+ }
3300
+
3301
+ function loadPrompt(sourceFile, value, descriptor) {
3302
+ const prompt = unwrapRuntimeDefault(value);
3303
+ if (!isSidecarPrompt(prompt)) {
3304
+ throw new Error(\`\${sourceFile} did not default-export a Sidecar prompt.\`);
3305
+ }
3306
+ return { prompt, descriptor };
3307
+ }
3308
+
3309
+ function assertAuth(value) {
3310
+ const authValue = unwrapRuntimeDefault(value);
3311
+ if (!isSidecarAuth(authValue)) {
3312
+ throw new Error("auth.ts must default-export auth({ ... }) from sidecar-ai.");
3313
+ }
3314
+ return authValue;
3315
+ }
3316
+
3317
+ function assertProxy(value) {
3318
+ const proxyValue = unwrapRuntimeDefault(value);
3319
+ if (!isSidecarProxy(proxyValue)) {
3320
+ throw new Error("proxy.ts must default-export proxy({ ... }) from @sidecar-ai/server/proxy.");
3321
+ }
3322
+ return proxyValue;
3323
+ }
3324
+
3325
+ function unwrapRuntimeDefault(value) {
3326
+ if (
3327
+ value &&
3328
+ typeof value === "object" &&
3329
+ "default" in value &&
3330
+ Object.keys(value).every((key) => key === "default" || key === "__esModule")
3331
+ ) {
3332
+ return unwrapRuntimeDefault(value.default);
3333
+ }
3334
+ return value;
3335
+ }
3336
+
3337
+ function readWidget(outputFile) {
3338
+ return readFileSync(new URL(\`../\${outputFile}\`, import.meta.url), "utf8");
3339
+ }
3340
+
3341
+ function readPort() {
3342
+ const raw = process.env.PORT ?? process.env.SIDECAR_PORT ?? "3001";
3343
+ const port = Number(raw);
3344
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
3345
+ throw new Error(\`Invalid PORT/SIDECAR_PORT value: \${raw}\`);
3346
+ }
3347
+ return port;
3348
+ }
3349
+
3350
+ function isDirectRun() {
3351
+ const entry = process.argv[1];
3352
+ if (!entry) {
3353
+ return false;
3354
+ }
3355
+
3356
+ return import.meta.url === pathToFileURL(realpathSync(entry)).href;
3357
+ }
3358
+
3359
+ function shutdown() {
3360
+ server.close(() => process.exit(0));
3361
+ }
3362
+ `;
3363
+ }
3364
+ function renderWidgetResources(manifest) {
3365
+ return manifest.tools.filter((entry) => entry.widget?.outputFile).map((entry) => ` {
3366
+ uri: ${JSON.stringify(entry.widget?.resourceUri)},
3367
+ name: ${JSON.stringify(entry.name)},
3368
+ description: ${JSON.stringify(entry.widget?.options?.description)},
3369
+ mimeType: "text/html;profile=mcp-app",
3370
+ text: readWidget(${JSON.stringify(entry.widget?.outputFile)}),
3371
+ _meta: ${JSON.stringify(entry.widget?.resourceMeta ?? void 0)},
3372
+ },`).join("\n");
3373
+ }
3374
+ function renderServerPackage(identity) {
3375
+ return `${JSON.stringify({
3376
+ name: `${identity.slug}-sidecar-server`,
3377
+ version: identity.version,
3378
+ private: true,
3379
+ type: "module",
3380
+ scripts: {
3381
+ start: "node server/index.js"
3382
+ },
3383
+ engines: {
3384
+ node: ">=20"
3385
+ }
3386
+ }, null, 2)}
3387
+ `;
3388
+ }
3389
+ function renderVercelEntrypoint() {
3390
+ return `export { default } from "./server/index.js";
3391
+ `;
3392
+ }
3393
+ function renderVercelFunctionConfig() {
3394
+ return `${JSON.stringify({
3395
+ runtime: "nodejs22.x",
3396
+ handler: VERCEL_HANDLER_FILE,
3397
+ launcherType: "Nodejs",
3398
+ shouldAddHelpers: true,
3399
+ supportsResponseStreaming: true,
3400
+ maxDuration: 300
3401
+ }, null, 2)}
3402
+ `;
3403
+ }
3404
+ function renderVercelOutputConfig() {
3405
+ return `${JSON.stringify({
3406
+ version: 3,
3407
+ routes: [
3408
+ {
3409
+ src: "/(.*)",
3410
+ dest: "/api/sidecar"
3411
+ }
3412
+ ]
3413
+ }, null, 2)}
3414
+ `;
3415
+ }
3416
+ function sidecarBundleAliases(rootDir) {
3417
+ const repoRoot = findSidecarRepoRoot2(rootDir) ?? findSidecarRepoRoot2(process.cwd());
3418
+ if (!repoRoot) {
3419
+ return void 0;
3420
+ }
3421
+ return {
3422
+ "sidecar-ai": path18.join(repoRoot, "packages", "sidecar-ai", "src", "index.ts"),
3423
+ "@sidecar-ai/auth": path18.join(repoRoot, "packages", "auth", "src", "index.ts"),
3424
+ "@sidecar-ai/core": path18.join(repoRoot, "packages", "core", "src", "index.ts"),
3425
+ "@sidecar-ai/server": path18.join(repoRoot, "packages", "server", "src", "index.ts"),
3426
+ "@sidecar-ai/server/proxy": path18.join(repoRoot, "packages", "server", "src", "proxy.ts"),
3427
+ "@sidecar-ai/client": path18.join(repoRoot, "packages", "client", "src", "index.ts"),
3428
+ "@sidecar-ai/react": path18.join(repoRoot, "packages", "react", "src", "index.ts"),
3429
+ "@sidecar-ai/native": path18.join(repoRoot, "packages", "native", "src", "index.ts"),
3430
+ "@sidecar-ai/native/components": path18.join(repoRoot, "packages", "native", "src", "components", "index.tsx"),
3431
+ "@sidecar-ai/openai": path18.join(repoRoot, "packages", "openai", "src", "index.ts"),
3432
+ "@sidecar-ai/openai/components": path18.join(repoRoot, "packages", "openai", "src", "components.tsx"),
3433
+ "@sidecar-ai/openai/official": path18.join(repoRoot, "packages", "openai", "src", "official.ts"),
3434
+ "@sidecar-ai/anthropic": path18.join(repoRoot, "packages", "anthropic", "src", "index.ts"),
3435
+ "@sidecar-ai/anthropic/agent": path18.join(repoRoot, "packages", "anthropic", "src", "agent.ts"),
3436
+ "@sidecar-ai/anthropic/command": path18.join(repoRoot, "packages", "anthropic", "src", "command.ts"),
3437
+ "@sidecar-ai/anthropic/components": path18.join(repoRoot, "packages", "anthropic", "src", "components.tsx"),
3438
+ "@sidecar-ai/anthropic/hooks": path18.join(repoRoot, "packages", "anthropic", "src", "hooks.ts"),
3439
+ "@sidecar-ai/anthropic/mcp": path18.join(repoRoot, "packages", "anthropic", "src", "mcp.ts"),
3440
+ "@sidecar-ai/anthropic/plugin": path18.join(repoRoot, "packages", "anthropic", "src", "plugin.ts"),
3441
+ "@sidecar-ai/anthropic/skill": path18.join(repoRoot, "packages", "anthropic", "src", "skill.ts")
3442
+ };
3443
+ }
3444
+ function findSidecarRepoRoot2(startDir) {
3445
+ let current = path18.resolve(startDir);
3446
+ while (true) {
3447
+ if (existsSync6(path18.join(current, "packages", "core", "src", "index.ts"))) {
3448
+ return current;
3449
+ }
3450
+ const parent = path18.dirname(current);
3451
+ if (parent === current) {
3452
+ return void 0;
3453
+ }
3454
+ current = parent;
3455
+ }
3456
+ }
3457
+
2800
3458
  // packages/compiler/src/build.ts
2801
3459
  async function buildProject(options) {
2802
- const rootDir = path17.resolve(options.rootDir);
2803
- const target = options.target ?? "mcp";
3460
+ const rootDir = path19.resolve(options.rootDir);
2804
3461
  const config = analyzeProjectConfig(rootDir);
3462
+ const target = options.target ?? config.build.target ?? "mcp";
3463
+ const host = options.host ?? config.build.host ?? "node";
3464
+ const plugins = options.plugins ?? config.build.plugins ?? false;
2805
3465
  const tools = await analyzeProjectTools(rootDir, { target });
2806
3466
  const resources = await analyzeProjectResources(rootDir);
2807
3467
  const resourceTemplates = [];
2808
3468
  const prompts = await analyzeProjectPrompts(rootDir);
3469
+ const identity = await loadProjectIdentity(rootDir);
2809
3470
  const diagnostics = await collectProjectDiagnostics(rootDir, {
2810
3471
  tools,
2811
3472
  resources,
@@ -2816,11 +3477,13 @@ async function buildProject(options) {
2816
3477
  if (errors.length) {
2817
3478
  throw new Error(errors.map(formatDiagnostic).join("\n"));
2818
3479
  }
2819
- const outDir = resolveInsideRoot(rootDir, options.outDir ?? "out/mcp");
2820
- await buildWidgets(rootDir, outDir, tools);
3480
+ const outDir = resolveInsideRoot(rootDir, options.outDir ?? config.build.outDir ?? defaultBuildOutDir(host, target));
3481
+ const runtimeOutDir = resolveRuntimeOutputDir(outDir, host);
3482
+ await buildWidgets(rootDir, runtimeOutDir, tools, config.build.widgets);
2821
3483
  const manifest = {
2822
3484
  version: 1,
2823
3485
  target,
3486
+ host,
2824
3487
  rootDir: ".",
2825
3488
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2826
3489
  config,
@@ -2830,23 +3493,47 @@ async function buildProject(options) {
2830
3493
  prompts,
2831
3494
  diagnostics
2832
3495
  };
2833
- await mkdir8(outDir, { recursive: true });
2834
- await writeFile8(
2835
- path17.join(outDir, "manifest.sidecar.json"),
3496
+ await mkdir9(runtimeOutDir, { recursive: true });
3497
+ await writeFile9(
3498
+ path19.join(runtimeOutDir, "manifest.sidecar.json"),
2836
3499
  `${JSON.stringify(manifest, null, 2)}
2837
3500
  `
2838
3501
  );
2839
- await writeFile8(path17.join(outDir, "README.md"), renderMcpReadme(manifest));
3502
+ await writeFile9(path19.join(runtimeOutDir, "README.md"), renderMcpReadme(manifest));
2840
3503
  await writeGeneratedTypes(rootDir, tools);
2841
- if (options.plugins) {
2842
- await buildPluginPackages(rootDir, path17.dirname(outDir), manifest);
3504
+ await buildServerOutput(rootDir, runtimeOutDir, manifest, identity, host, {
3505
+ vercelOutputDir: outDir
3506
+ });
3507
+ if (plugins) {
3508
+ await buildPluginPackages(rootDir, resolvePluginOutputBase(rootDir, outDir, host), manifest);
2843
3509
  }
2844
3510
  return manifest;
2845
3511
  }
3512
+ function defaultBuildOutDir(host, target) {
3513
+ if (host === "vercel") {
3514
+ return ".vercel/output";
3515
+ }
3516
+ return `out/${target}`;
3517
+ }
3518
+ function resolveRuntimeOutputDir(outDir, host) {
3519
+ if (host === "vercel") {
3520
+ return path19.join(outDir, VERCEL_FUNCTION_DIR);
3521
+ }
3522
+ return outDir;
3523
+ }
3524
+ function resolvePluginOutputBase(rootDir, outDir, host) {
3525
+ if (host === "vercel" && isVercelBuildOutputDir(outDir)) {
3526
+ return path19.join(rootDir, "out");
3527
+ }
3528
+ return path19.dirname(outDir);
3529
+ }
3530
+ function isVercelBuildOutputDir(outDir) {
3531
+ return path19.basename(outDir) === "output" && path19.basename(path19.dirname(outDir)) === ".vercel";
3532
+ }
2846
3533
  function resolveInsideRoot(rootDir, output) {
2847
- const resolved = path17.resolve(rootDir, output);
2848
- const relative = path17.relative(rootDir, resolved);
2849
- if (relative.startsWith("..") || path17.isAbsolute(relative)) {
3534
+ const resolved = path19.resolve(rootDir, output);
3535
+ const relative = path19.relative(rootDir, resolved);
3536
+ if (relative.startsWith("..") || path19.isAbsolute(relative)) {
2850
3537
  throw new Error(`Build output must stay inside the project root: ${output}`);
2851
3538
  }
2852
3539
  return resolved;
@@ -2871,13 +3558,41 @@ ${resources || "No resources detected."}
2871
3558
 
2872
3559
  ${prompts || "No prompts detected."}
2873
3560
 
3561
+ ${manifest.host === "vercel" ? renderVercelReadmeSection() : renderNodeReadmeSection()}
3562
+
2874
3563
  ## Local HTTPS Testing
2875
3564
 
2876
- 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.
3565
+ 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.
3566
+ `;
3567
+ }
3568
+ function renderNodeReadmeSection() {
3569
+ return `## Run The Server
3570
+
3571
+ This output includes a standalone Node MCP server. Start it with:
3572
+
3573
+ \`\`\`sh
3574
+ npm start
3575
+ \`\`\`
3576
+
3577
+ or:
3578
+
3579
+ \`\`\`sh
3580
+ node server/index.js
3581
+ \`\`\`
3582
+
3583
+ 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.
3584
+ `;
3585
+ }
3586
+ function renderVercelReadmeSection() {
3587
+ return `## Deploy To Vercel
3588
+
3589
+ 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.
2877
3590
  `;
2878
3591
  }
2879
3592
  export {
2880
3593
  CompilerError,
3594
+ SERVER_ENTRYPOINT,
3595
+ VERCEL_ENTRYPOINT,
2881
3596
  analyzeProjectConfig,
2882
3597
  analyzeProjectPrompts,
2883
3598
  analyzeProjectResources,