@sidecar-ai/compiler 0.1.0-alpha.1 → 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,38 +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 path6 from "path";
1927
+ import path7 from "path";
1564
1928
  async function collectProjectDiagnostics(rootDir, input) {
1565
1929
  const diagnostics = [];
1566
1930
  const tools = Array.isArray(input) ? input : input.tools;
1567
1931
  const resources = Array.isArray(input) ? [] : input.resources ?? [];
1568
1932
  const prompts = Array.isArray(input) ? [] : input.prompts ?? [];
1569
1933
  const config = Array.isArray(input) ? void 0 : input.config;
1570
- const hasAuthConfig = existsSync3(path6.join(rootDir, "auth.ts"));
1934
+ const hasAuthConfig = existsSync4(path7.join(rootDir, "auth.ts"));
1571
1935
  for (const entry of tools) {
1572
- const toolPath = path6.join(rootDir, entry.sourceFile);
1936
+ const toolPath = path7.join(rootDir, entry.sourceFile);
1573
1937
  const toolSource = await readFile2(toolPath, "utf8");
1574
- diagnostics.push(...diagnoseToolSource(rootDir, entry, toolSource, hasAuthConfig));
1938
+ diagnostics.push(...diagnoseToolSource(entry, toolSource, hasAuthConfig));
1575
1939
  if (entry.widget) {
1576
- const widgetPath = path6.join(rootDir, entry.widget.sourceFile);
1940
+ const widgetPath = path7.join(rootDir, entry.widget.sourceFile);
1577
1941
  const widgetSource = await readFile2(widgetPath, "utf8");
1578
1942
  diagnostics.push(...diagnoseWidgetSource(rootDir, entry, widgetSource));
1579
1943
  }
1580
1944
  }
1581
1945
  for (const entry of resources) {
1582
- const resourcePath = path6.join(rootDir, entry.sourceFile);
1946
+ const resourcePath = path7.join(rootDir, entry.sourceFile);
1583
1947
  const resourceSource = await readFile2(resourcePath, "utf8");
1584
1948
  diagnostics.push(...diagnoseResourceSource(entry, resourceSource));
1585
1949
  }
1586
1950
  for (const entry of prompts) {
1587
- const promptPath = path6.join(rootDir, entry.sourceFile);
1951
+ const promptPath = path7.join(rootDir, entry.sourceFile);
1588
1952
  const promptSource = await readFile2(promptPath, "utf8");
1589
1953
  diagnostics.push(...diagnosePromptSource(entry, promptSource));
1590
1954
  }
@@ -1599,7 +1963,7 @@ function formatDiagnostic(diagnostic) {
1599
1963
  hint: ${diagnostic.hint}` : "";
1600
1964
  return `${location} - ${diagnostic.severity} ${diagnostic.code}: ${diagnostic.message}${hint}`;
1601
1965
  }
1602
- function diagnoseToolSource(rootDir, entry, source, hasAuthConfig) {
1966
+ function diagnoseToolSource(entry, source, hasAuthConfig) {
1603
1967
  const diagnostics = [];
1604
1968
  const toolLocation = locate(source, "tool({");
1605
1969
  if (!entry.description.trim().startsWith("Use this when")) {
@@ -1816,21 +2180,21 @@ function isIgnored(source, code) {
1816
2180
 
1817
2181
  // packages/compiler/src/generated.ts
1818
2182
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
1819
- import path7 from "path";
2183
+ import path8 from "path";
1820
2184
  async function writeGeneratedTypes(rootDir, tools) {
1821
- const generatedDir = path7.join(rootDir, ".sidecar", "generated");
2185
+ const generatedDir = path8.join(rootDir, ".sidecar", "generated");
1822
2186
  await mkdir2(generatedDir, { recursive: true });
1823
2187
  const appCallableTools = tools.filter(isAppCallable);
1824
2188
  const methodNames = uniqueMethodNames(appCallableTools.map((entry) => toIdentifier(entry.id)));
1825
2189
  const imports = appCallableTools.map(
1826
- (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" }))};`
1827
2191
  ).join("\n");
1828
2192
  const ids = appCallableTools.map((entry, index) => ` ${methodNames[index]}: ${JSON.stringify(entry.id)}`).join(",\n");
1829
2193
  const toolTypes = appCallableTools.map(
1830
- (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}>>;`
1831
2195
  ).join("\n");
1832
2196
  await writeFile2(
1833
- path7.join(generatedDir, "tools.ts"),
2197
+ path8.join(generatedDir, "tools.ts"),
1834
2198
  `/**
1835
2199
  * Generated Sidecar widget tool client.
1836
2200
  *
@@ -1878,19 +2242,15 @@ function uniqueMethodNames(names) {
1878
2242
  });
1879
2243
  }
1880
2244
 
1881
- // packages/compiler/src/plugins.ts
1882
- import { mkdir as mkdir7, writeFile as writeFile7 } from "fs/promises";
1883
- import path14 from "path";
1884
-
1885
2245
  // packages/compiler/src/identity.ts
1886
2246
  import { readFile as readFile3 } from "fs/promises";
1887
- import path8 from "path";
2247
+ import path9 from "path";
1888
2248
  async function loadProjectIdentity(rootDir) {
1889
- const packageJson = await readOptionalJson(path8.join(rootDir, "package.json"));
2249
+ const packageJson = await readOptionalJson(path9.join(rootDir, "package.json"));
1890
2250
  const configText = await readOptionalText(
1891
- path8.join(rootDir, "sidecar.config.ts")
2251
+ path9.join(rootDir, "sidecar.config.ts")
1892
2252
  );
1893
- const name = readConfigString(configText, "name") ?? packageJson?.name ?? path8.basename(rootDir);
2253
+ const name = readConfigString(configText, "name") ?? packageJson?.name ?? path9.basename(rootDir);
1894
2254
  const version = readConfigString(configText, "version") ?? packageJson?.version ?? "0.0.0-dev";
1895
2255
  const description = readConfigString(configText, "description") ?? packageJson?.description ?? `${name} Sidecar app.`;
1896
2256
  return {
@@ -1922,30 +2282,34 @@ function readConfigString(configText, key) {
1922
2282
  return match?.[1];
1923
2283
  }
1924
2284
 
2285
+ // packages/compiler/src/plugins.ts
2286
+ import { mkdir as mkdir7, writeFile as writeFile7 } from "fs/promises";
2287
+ import path15 from "path";
2288
+
1925
2289
  // packages/compiler/src/plugin-components/agents.ts
1926
2290
  import { mkdir as mkdir3, readdir as readdir2, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
1927
- import path9 from "path";
2291
+ import path10 from "path";
1928
2292
  async function emitClaudeAgents(rootDir, destination) {
1929
- const source = path9.join(rootDir, "agents");
2293
+ const source = path10.join(rootDir, "agents");
1930
2294
  if (!existsSyncSafe(source)) {
1931
2295
  return;
1932
2296
  }
1933
2297
  const entries = await readdir2(source, { withFileTypes: true });
1934
2298
  const agentDirs = entries.filter(
1935
- (entry) => entry.isDirectory() && existsSyncSafe(path9.join(source, entry.name, "agent.ts"))
2299
+ (entry) => entry.isDirectory() && existsSyncSafe(path10.join(source, entry.name, "agent.ts"))
1936
2300
  );
1937
2301
  if (!agentDirs.length) {
1938
2302
  return;
1939
2303
  }
1940
2304
  await mkdir3(destination, { recursive: true });
1941
2305
  for (const entry of agentDirs) {
1942
- 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");
1943
2307
  const markdown = parseClaudeAgent(
1944
2308
  sourceText,
1945
2309
  entry.name
1946
2310
  );
1947
2311
  const agentName = readObjectString(sourceText, "name") ?? entry.name;
1948
- await writeFile3(path9.join(destination, `${safeFileStem(agentName)}.md`), markdown);
2312
+ await writeFile3(path10.join(destination, `${safeFileStem(agentName)}.md`), markdown);
1949
2313
  }
1950
2314
  }
1951
2315
  function parseClaudeAgent(source, fallbackName) {
@@ -1974,41 +2338,41 @@ ${prompt.trim()}
1974
2338
 
1975
2339
  // packages/compiler/src/plugin-components/commands.ts
1976
2340
  import { cp, lstat, mkdir as mkdir4, readdir as readdir3, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
1977
- import path10 from "path";
2341
+ import path11 from "path";
1978
2342
  async function copyCommands(rootDir, destination) {
1979
- const source = path10.join(rootDir, "commands");
2343
+ const source = path11.join(rootDir, "commands");
1980
2344
  if (!existsSyncSafe(source)) {
1981
2345
  return;
1982
2346
  }
1983
2347
  await mkdir4(destination, { recursive: true });
1984
2348
  const entries = await readdir3(source, { withFileTypes: true });
1985
2349
  for (const entry of entries) {
1986
- const sourcePath = path10.join(source, entry.name);
2350
+ const sourcePath = path11.join(source, entry.name);
1987
2351
  if (entry.isFile() && entry.name.endsWith(".md")) {
1988
2352
  if (await safeCommandCopyFilter(sourcePath)) {
1989
- 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"));
1990
2354
  }
1991
2355
  continue;
1992
2356
  }
1993
2357
  if (!entry.isDirectory()) {
1994
2358
  continue;
1995
2359
  }
1996
- const dynamicCommand = path10.join(sourcePath, "command.ts");
2360
+ const dynamicCommand = path11.join(sourcePath, "command.ts");
1997
2361
  if (existsSyncSafe(dynamicCommand)) {
1998
2362
  const sourceText = await readFile5(dynamicCommand, "utf8");
1999
2363
  const markdown = parseDynamicCommand(sourceText, entry.name);
2000
2364
  const commandName = readObjectString(sourceText, "name") ?? entry.name;
2001
- await writeFile4(path10.join(destination, `${safeFileStem(commandName)}.md`), markdown);
2365
+ await writeFile4(path11.join(destination, `${safeFileStem(commandName)}.md`), markdown);
2002
2366
  continue;
2003
2367
  }
2004
- await cp(sourcePath, path10.join(destination, entry.name), {
2368
+ await cp(sourcePath, path11.join(destination, entry.name), {
2005
2369
  recursive: true,
2006
2370
  filter: safeCommandCopyFilter
2007
2371
  });
2008
2372
  }
2009
2373
  }
2010
2374
  async function safeCommandCopyFilter(sourcePath) {
2011
- const basename = path10.basename(sourcePath);
2375
+ const basename = path11.basename(sourcePath);
2012
2376
  if (basename === ".env" || basename.startsWith(".env.") || basename === "node_modules" || basename === ".git") {
2013
2377
  return false;
2014
2378
  }
@@ -2037,32 +2401,32 @@ ${Object.entries(frontmatter).map(([key, value]) => `${key}: ${yamlScalar(value)
2037
2401
 
2038
2402
  // packages/compiler/src/plugin-components/hooks.ts
2039
2403
  import { mkdir as mkdir5, readdir as readdir4, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
2040
- import path11 from "path";
2404
+ import path12 from "path";
2041
2405
  import {
2042
2406
  Node as Node6,
2043
2407
  Project as Project2,
2044
2408
  SyntaxKind as SyntaxKind4
2045
2409
  } from "ts-morph";
2046
2410
  async function copyHooks(rootDir, destination) {
2047
- const source = path11.join(rootDir, "hooks");
2411
+ const source = path12.join(rootDir, "hooks");
2048
2412
  if (!existsSyncSafe(source)) {
2049
2413
  return;
2050
2414
  }
2051
2415
  const entries = await readdir4(source, { withFileTypes: true });
2052
2416
  const hookDirs = entries.filter(
2053
- (entry) => entry.isDirectory() && existsSyncSafe(path11.join(source, entry.name, "hook.ts"))
2417
+ (entry) => entry.isDirectory() && existsSyncSafe(path12.join(source, entry.name, "hook.ts"))
2054
2418
  );
2055
2419
  if (!hookDirs.length) {
2056
2420
  return;
2057
2421
  }
2058
2422
  const config = {};
2059
2423
  for (const entry of hookDirs) {
2060
- const filePath = path11.join(source, entry.name, "hook.ts");
2424
+ const filePath = path12.join(source, entry.name, "hook.ts");
2061
2425
  mergeHooks(config, parseHookFile(await readFile6(filePath, "utf8"), entry.name));
2062
2426
  }
2063
2427
  await mkdir5(destination, { recursive: true });
2064
2428
  await writeFile5(
2065
- path11.join(destination, "hooks.json"),
2429
+ path12.join(destination, "hooks.json"),
2066
2430
  `${JSON.stringify(config, null, 2)}
2067
2431
  `
2068
2432
  );
@@ -2089,7 +2453,7 @@ function parseHookFile(source, fallbackName) {
2089
2453
  throw new Error(`hooks/${fallbackName}/hook.ts must default-export hook({ ... }) or hooks({ ... }).`);
2090
2454
  }
2091
2455
  function parseHookDefinition(definition, fallbackName) {
2092
- const event = readStringProperty3(definition, "event");
2456
+ const event = readStringProperty4(definition, "event");
2093
2457
  if (!event) {
2094
2458
  throw new Error(`hooks/${fallbackName}/hook.ts must declare an event string.`);
2095
2459
  }
@@ -2099,7 +2463,7 @@ function parseHookDefinition(definition, fallbackName) {
2099
2463
  }
2100
2464
  return {
2101
2465
  event,
2102
- matcher: readStringProperty3(definition, "matcher"),
2466
+ matcher: readStringProperty4(definition, "matcher"),
2103
2467
  run: hooks
2104
2468
  };
2105
2469
  }
@@ -2119,7 +2483,7 @@ function parseHooksDefinition(definition, fallbackName) {
2119
2483
  throw new Error(`hooks/${fallbackName}/hook.ts event "${event}" entries must be objects.`);
2120
2484
  }
2121
2485
  return stripUndefined2({
2122
- matcher: readStringProperty3(entry, "matcher"),
2486
+ matcher: readStringProperty4(entry, "matcher"),
2123
2487
  hooks: readHookArray(entry, "run", fallbackName)
2124
2488
  });
2125
2489
  });
@@ -2142,7 +2506,7 @@ function parseHookHandlers(handlers, fallbackName) {
2142
2506
  }
2143
2507
  function parseHookHandler(handler, fallbackName) {
2144
2508
  if (Node6.isObjectLiteralExpression(handler)) {
2145
- const type = readStringProperty3(handler, "type");
2509
+ const type = readStringProperty4(handler, "type");
2146
2510
  if (type !== "command" && type !== "http") {
2147
2511
  throw new Error(`hooks/${fallbackName}/hook.ts hook handlers need type "command" or "http".`);
2148
2512
  }
@@ -2192,7 +2556,7 @@ function objectLiteralToRecord(object) {
2192
2556
  }
2193
2557
  return stripUndefined2(record);
2194
2558
  }
2195
- function readStringProperty3(object, propertyName) {
2559
+ function readStringProperty4(object, propertyName) {
2196
2560
  const property = object.getProperty(propertyName);
2197
2561
  if (!property || !Node6.isPropertyAssignment(property)) {
2198
2562
  return void 0;
@@ -2236,7 +2600,7 @@ function mergeHooks(target, source) {
2236
2600
 
2237
2601
  // packages/compiler/src/plugin-components/passthrough.ts
2238
2602
  import { cp as cp2, lstat as lstat2 } from "fs/promises";
2239
- import path12 from "path";
2603
+ import path13 from "path";
2240
2604
  async function copyClaudePassthroughDirectories(rootDir, pluginDir) {
2241
2605
  for (const directory of [
2242
2606
  "assets",
@@ -2245,18 +2609,18 @@ async function copyClaudePassthroughDirectories(rootDir, pluginDir) {
2245
2609
  "output-styles",
2246
2610
  "themes"
2247
2611
  ]) {
2248
- const source = path12.join(rootDir, directory);
2612
+ const source = path13.join(rootDir, directory);
2249
2613
  if (!existsSyncSafe(source)) {
2250
2614
  continue;
2251
2615
  }
2252
- await cp2(source, path12.join(pluginDir, directory), {
2616
+ await cp2(source, path13.join(pluginDir, directory), {
2253
2617
  recursive: true,
2254
2618
  filter: safePluginCopyFilter
2255
2619
  });
2256
2620
  }
2257
2621
  }
2258
2622
  async function safePluginCopyFilter(sourcePath) {
2259
- const basename = path12.basename(sourcePath);
2623
+ const basename = path13.basename(sourcePath);
2260
2624
  if (basename === "node_modules" || basename === ".git" || basename === ".env" || basename.startsWith(".env.")) {
2261
2625
  return false;
2262
2626
  }
@@ -2265,11 +2629,11 @@ async function safePluginCopyFilter(sourcePath) {
2265
2629
  }
2266
2630
 
2267
2631
  // packages/compiler/src/plugin-components/skills.ts
2268
- import { existsSync as existsSync4 } from "fs";
2632
+ import { existsSync as existsSync5 } from "fs";
2269
2633
  import { cp as cp3, lstat as lstat3, mkdir as mkdir6, readdir as readdir5, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
2270
- import path13 from "path";
2634
+ import path14 from "path";
2271
2635
  async function copySkills(rootDir, destination) {
2272
- const source = path13.join(rootDir, "skills");
2636
+ const source = path14.join(rootDir, "skills");
2273
2637
  if (!existsSyncSafe(source)) {
2274
2638
  return;
2275
2639
  }
@@ -2279,27 +2643,27 @@ async function copySkills(rootDir, destination) {
2279
2643
  if (!entry.isDirectory()) {
2280
2644
  continue;
2281
2645
  }
2282
- const sourceDir = path13.join(source, entry.name);
2283
- const destinationDir = path13.join(destination, entry.name);
2284
- const staticSkill = path13.join(sourceDir, "SKILL.md");
2285
- const dynamicSkill = path13.join(sourceDir, "skill.ts");
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");
2286
2650
  await mkdir6(destinationDir, { recursive: true });
2287
- if (existsSync4(staticSkill)) {
2651
+ if (existsSync5(staticSkill)) {
2288
2652
  await cp3(sourceDir, destinationDir, {
2289
2653
  recursive: true,
2290
- 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)
2291
2655
  });
2292
- } else if (existsSync4(dynamicSkill)) {
2656
+ } else if (existsSync5(dynamicSkill)) {
2293
2657
  const generated = parseDynamicSkill(
2294
2658
  await readFile7(dynamicSkill, "utf8"),
2295
2659
  entry.name
2296
2660
  );
2297
- await writeFile6(path13.join(destinationDir, "SKILL.md"), generated);
2661
+ await writeFile6(path14.join(destinationDir, "SKILL.md"), generated);
2298
2662
  }
2299
2663
  }
2300
2664
  }
2301
2665
  async function safeSkillCopyFilter(sourcePath) {
2302
- const basename = path13.basename(sourcePath);
2666
+ const basename = path14.basename(sourcePath);
2303
2667
  if (basename === ".env" || basename.startsWith(".env.") || basename === "node_modules" || basename === ".git") {
2304
2668
  return false;
2305
2669
  }
@@ -2325,25 +2689,25 @@ async function buildPluginPackages(rootDir, outRoot, manifest) {
2325
2689
  await buildClaudePlugin(rootDir, outRoot, identity, manifest);
2326
2690
  }
2327
2691
  async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2328
- const pluginDir = path14.join(outRoot, "claude-plugin");
2329
- await mkdir7(path14.join(pluginDir, ".claude-plugin"), { recursive: true });
2330
- await copySkills(rootDir, path14.join(pluginDir, "skills"));
2331
- await copyCommands(rootDir, path14.join(pluginDir, "commands"));
2332
- await copyHooks(rootDir, path14.join(pluginDir, "hooks"));
2333
- await emitClaudeAgents(rootDir, path14.join(pluginDir, "agents"));
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"));
2334
2698
  await copyClaudePassthroughDirectories(rootDir, pluginDir);
2335
- await writeJson(path14.join(pluginDir, ".claude-plugin", "plugin.json"), {
2699
+ await writeJson(path15.join(pluginDir, ".claude-plugin", "plugin.json"), {
2336
2700
  name: identity.slug,
2337
2701
  version: identity.version,
2338
2702
  description: identity.description,
2339
2703
  displayName: identity.name,
2340
2704
  installationPreference: "available"
2341
2705
  });
2342
- await writeJson(path14.join(pluginDir, "version.json"), {
2706
+ await writeJson(path15.join(pluginDir, "version.json"), {
2343
2707
  version: identity.version,
2344
2708
  generatedBy: "sidecar"
2345
2709
  });
2346
- await writeJson(path14.join(pluginDir, ".mcp.json"), {
2710
+ await writeJson(path15.join(pluginDir, ".mcp.json"), {
2347
2711
  mcpServers: {
2348
2712
  [identity.slug]: {
2349
2713
  type: "http",
@@ -2351,7 +2715,7 @@ async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2351
2715
  }
2352
2716
  }
2353
2717
  });
2354
- await writeJson(path14.join(pluginDir, "settings.json"), {
2718
+ await writeJson(path15.join(pluginDir, "settings.json"), {
2355
2719
  sidecar: {
2356
2720
  tools: manifest.tools.map((entry) => entry.id),
2357
2721
  resources: manifest.resources.map((entry) => entry.uri),
@@ -2359,21 +2723,21 @@ async function buildClaudePlugin(rootDir, outRoot, identity, manifest) {
2359
2723
  }
2360
2724
  });
2361
2725
  await writeFile7(
2362
- path14.join(pluginDir, "README.md"),
2726
+ path15.join(pluginDir, "README.md"),
2363
2727
  `# ${identity.name} Claude Plugin
2364
2728
 
2365
2729
  This package was generated by Sidecar.
2366
2730
 
2367
2731
  Set \`SIDECAR_MCP_URL\` to the hosted MCP endpoint for this app.
2368
2732
 
2369
- 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.
2370
2734
 
2371
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.
2372
2736
  `
2373
2737
  );
2374
2738
  }
2375
2739
  async function writeJson(filePath, value) {
2376
- await mkdir7(path14.dirname(filePath), { recursive: true });
2740
+ await mkdir7(path15.dirname(filePath), { recursive: true });
2377
2741
  await writeFile7(
2378
2742
  filePath,
2379
2743
  `${JSON.stringify(stripUndefined2(value), null, 2)}
@@ -2383,13 +2747,13 @@ async function writeJson(filePath, value) {
2383
2747
 
2384
2748
  // packages/compiler/src/prompts.ts
2385
2749
  import { readdir as readdir6 } from "fs/promises";
2386
- import path15 from "path";
2750
+ import path16 from "path";
2387
2751
  import {
2388
2752
  Node as Node7,
2389
2753
  SyntaxKind as SyntaxKind5
2390
2754
  } from "ts-morph";
2391
2755
  async function analyzeProjectPrompts(rootDir) {
2392
- const promptFiles = await findPromptFiles(path15.join(rootDir, "prompts"));
2756
+ const promptFiles = await findPromptFiles(path16.join(rootDir, "prompts"));
2393
2757
  if (promptFiles.length === 0) {
2394
2758
  return [];
2395
2759
  }
@@ -2401,7 +2765,7 @@ async function analyzeProjectPrompts(rootDir) {
2401
2765
  function analyzePromptFile(sourceFile, rootDir) {
2402
2766
  const definition = findPromptDefinition(sourceFile);
2403
2767
  const absoluteFile = sourceFile.getFilePath();
2404
- const directory = path15.basename(path15.dirname(absoluteFile));
2768
+ const directory = path16.basename(path16.dirname(absoluteFile));
2405
2769
  const name = getOptionalStringProperty2(definition, "name") ?? safePathSegment(directory);
2406
2770
  const title = getRequiredStringProperty2(definition, "title", sourceFile);
2407
2771
  const description = getOptionalStringProperty2(definition, "description");
@@ -2417,7 +2781,7 @@ function analyzePromptFile(sourceFile, rootDir) {
2417
2781
  throw new CompilerError(sourceFile, "prompt({ ... }) must include a run method.");
2418
2782
  }
2419
2783
  return {
2420
- sourceFile: path15.relative(rootDir, absoluteFile),
2784
+ sourceFile: path16.relative(rootDir, absoluteFile),
2421
2785
  directory,
2422
2786
  name,
2423
2787
  title,
@@ -2436,7 +2800,7 @@ async function findPromptFiles(promptsDir) {
2436
2800
  if (!entry.isDirectory()) {
2437
2801
  continue;
2438
2802
  }
2439
- const filePath = path15.join(promptsDir, entry.name, "prompt.ts");
2803
+ const filePath = path16.join(promptsDir, entry.name, "prompt.ts");
2440
2804
  if (existsSyncSafe(filePath)) {
2441
2805
  files.push(filePath);
2442
2806
  }
@@ -2558,7 +2922,7 @@ function readIcons(definition) {
2558
2922
  return [{
2559
2923
  src,
2560
2924
  mimeType: getOptionalStringProperty2(object, "mimeType"),
2561
- sizes: readStringArrayProperty3(object, "sizes")
2925
+ sizes: readStringArrayProperty4(object, "sizes")
2562
2926
  }];
2563
2927
  });
2564
2928
  return icons.length ? icons : void 0;
@@ -2571,7 +2935,7 @@ function readObjectProperty4(definition, propertyName) {
2571
2935
  const initializer = unwrapExpression(property.getInitializer());
2572
2936
  return initializer && Node7.isObjectLiteralExpression(initializer) ? initializer : void 0;
2573
2937
  }
2574
- function readStringArrayProperty3(definition, propertyName) {
2938
+ function readStringArrayProperty4(definition, propertyName) {
2575
2939
  const property = definition.getProperty(propertyName);
2576
2940
  if (!property || !Node7.isPropertyAssignment(property)) {
2577
2941
  return void 0;
@@ -2585,13 +2949,13 @@ function readStringArrayProperty3(definition, propertyName) {
2585
2949
 
2586
2950
  // packages/compiler/src/resources.ts
2587
2951
  import { readdir as readdir7 } from "fs/promises";
2588
- import path16 from "path";
2952
+ import path17 from "path";
2589
2953
  import {
2590
2954
  Node as Node8,
2591
2955
  SyntaxKind as SyntaxKind6
2592
2956
  } from "ts-morph";
2593
2957
  async function analyzeProjectResources(rootDir) {
2594
- const resourceFiles = await findResourceFiles(path16.join(rootDir, "resources"));
2958
+ const resourceFiles = await findResourceFiles(path17.join(rootDir, "resources"));
2595
2959
  if (resourceFiles.length === 0) {
2596
2960
  return [];
2597
2961
  }
@@ -2603,7 +2967,7 @@ async function analyzeProjectResources(rootDir) {
2603
2967
  function analyzeResourceFile(sourceFile, rootDir) {
2604
2968
  const definition = findResourceDefinition(sourceFile);
2605
2969
  const absoluteFile = sourceFile.getFilePath();
2606
- const directory = path16.basename(path16.dirname(absoluteFile));
2970
+ const directory = path17.basename(path17.dirname(absoluteFile));
2607
2971
  const defaultUri = `sidecar://resources/${safePathSegment(directory)}`;
2608
2972
  const name = getRequiredStringProperty3(definition, "name", sourceFile);
2609
2973
  const uri = getOptionalStringProperty3(definition, "uri") ?? defaultUri;
@@ -2621,7 +2985,7 @@ function analyzeResourceFile(sourceFile, rootDir) {
2621
2985
  throw new CompilerError(sourceFile, "resource({ ... }) must include a read method.");
2622
2986
  }
2623
2987
  return {
2624
- sourceFile: path16.relative(rootDir, absoluteFile),
2988
+ sourceFile: path17.relative(rootDir, absoluteFile),
2625
2989
  directory,
2626
2990
  uri,
2627
2991
  name,
@@ -2644,7 +3008,7 @@ async function findResourceFiles(resourcesDir) {
2644
3008
  if (!entry.isDirectory()) {
2645
3009
  continue;
2646
3010
  }
2647
- const filePath = path16.join(resourcesDir, entry.name, "resource.ts");
3011
+ const filePath = path17.join(resourcesDir, entry.name, "resource.ts");
2648
3012
  if (existsSyncSafe(filePath)) {
2649
3013
  files.push(filePath);
2650
3014
  }
@@ -2723,7 +3087,7 @@ function readAnnotations2(definition) {
2723
3087
  return void 0;
2724
3088
  }
2725
3089
  const annotations = {};
2726
- const audience = readStringArrayProperty4(initializer, "audience")?.filter((value) => value === "user" || value === "assistant");
3090
+ const audience = readStringArrayProperty5(initializer, "audience")?.filter((value) => value === "user" || value === "assistant");
2727
3091
  const priority = getOptionalNumberProperty(initializer, "priority");
2728
3092
  const lastModified = getOptionalStringProperty3(initializer, "lastModified");
2729
3093
  if (audience?.length) annotations.audience = audience;
@@ -2752,7 +3116,7 @@ function readIcons2(definition) {
2752
3116
  return [{
2753
3117
  src,
2754
3118
  mimeType: getOptionalStringProperty3(object, "mimeType"),
2755
- sizes: readStringArrayProperty4(object, "sizes")
3119
+ sizes: readStringArrayProperty5(object, "sizes")
2756
3120
  }];
2757
3121
  });
2758
3122
  return icons.length ? icons : void 0;
@@ -2765,7 +3129,7 @@ function readObjectProperty5(definition, propertyName) {
2765
3129
  const initializer = unwrapExpression(property.getInitializer());
2766
3130
  return initializer && Node8.isObjectLiteralExpression(initializer) ? initializer : void 0;
2767
3131
  }
2768
- function readStringArrayProperty4(definition, propertyName) {
3132
+ function readStringArrayProperty5(definition, propertyName) {
2769
3133
  const property = definition.getProperty(propertyName);
2770
3134
  if (!property || !Node8.isPropertyAssignment(property)) {
2771
3135
  return void 0;
@@ -2777,15 +3141,332 @@ function readStringArrayProperty4(definition, propertyName) {
2777
3141
  return initializer.getElements().filter(Node8.isStringLiteral).map((element) => element.getLiteralText());
2778
3142
  }
2779
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
+
2780
3458
  // packages/compiler/src/build.ts
2781
3459
  async function buildProject(options) {
2782
- const rootDir = path17.resolve(options.rootDir);
2783
- const target = options.target ?? "mcp";
3460
+ const rootDir = path19.resolve(options.rootDir);
2784
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;
2785
3465
  const tools = await analyzeProjectTools(rootDir, { target });
2786
3466
  const resources = await analyzeProjectResources(rootDir);
2787
3467
  const resourceTemplates = [];
2788
3468
  const prompts = await analyzeProjectPrompts(rootDir);
3469
+ const identity = await loadProjectIdentity(rootDir);
2789
3470
  const diagnostics = await collectProjectDiagnostics(rootDir, {
2790
3471
  tools,
2791
3472
  resources,
@@ -2796,11 +3477,13 @@ async function buildProject(options) {
2796
3477
  if (errors.length) {
2797
3478
  throw new Error(errors.map(formatDiagnostic).join("\n"));
2798
3479
  }
2799
- const outDir = resolveInsideRoot(rootDir, options.outDir ?? "out/mcp");
2800
- 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);
2801
3483
  const manifest = {
2802
3484
  version: 1,
2803
3485
  target,
3486
+ host,
2804
3487
  rootDir: ".",
2805
3488
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2806
3489
  config,
@@ -2810,23 +3493,47 @@ async function buildProject(options) {
2810
3493
  prompts,
2811
3494
  diagnostics
2812
3495
  };
2813
- await mkdir8(outDir, { recursive: true });
2814
- await writeFile8(
2815
- path17.join(outDir, "manifest.sidecar.json"),
3496
+ await mkdir9(runtimeOutDir, { recursive: true });
3497
+ await writeFile9(
3498
+ path19.join(runtimeOutDir, "manifest.sidecar.json"),
2816
3499
  `${JSON.stringify(manifest, null, 2)}
2817
3500
  `
2818
3501
  );
2819
- await writeFile8(path17.join(outDir, "README.md"), renderMcpReadme(manifest));
3502
+ await writeFile9(path19.join(runtimeOutDir, "README.md"), renderMcpReadme(manifest));
2820
3503
  await writeGeneratedTypes(rootDir, tools);
2821
- if (options.plugins) {
2822
- 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);
2823
3509
  }
2824
3510
  return manifest;
2825
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
+ }
2826
3533
  function resolveInsideRoot(rootDir, output) {
2827
- const resolved = path17.resolve(rootDir, output);
2828
- const relative = path17.relative(rootDir, resolved);
2829
- if (relative.startsWith("..") || path17.isAbsolute(relative)) {
3534
+ const resolved = path19.resolve(rootDir, output);
3535
+ const relative = path19.relative(rootDir, resolved);
3536
+ if (relative.startsWith("..") || path19.isAbsolute(relative)) {
2830
3537
  throw new Error(`Build output must stay inside the project root: ${output}`);
2831
3538
  }
2832
3539
  return resolved;
@@ -2851,13 +3558,41 @@ ${resources || "No resources detected."}
2851
3558
 
2852
3559
  ${prompts || "No prompts detected."}
2853
3560
 
3561
+ ${manifest.host === "vercel" ? renderVercelReadmeSection() : renderNodeReadmeSection()}
3562
+
2854
3563
  ## Local HTTPS Testing
2855
3564
 
2856
- Run \`sidecar dev --tunnel\` from the project root to start the local MCP server on Streamable HTTP and print an HTTPS MCP URL that can be added to ChatGPT, Claude, or a Claude plugin install.
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.
2857
3590
  `;
2858
3591
  }
2859
3592
  export {
2860
3593
  CompilerError,
3594
+ SERVER_ENTRYPOINT,
3595
+ VERCEL_ENTRYPOINT,
2861
3596
  analyzeProjectConfig,
2862
3597
  analyzeProjectPrompts,
2863
3598
  analyzeProjectResources,