@poncho-ai/cli 0.8.3 → 0.9.0
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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +14 -0
- package/dist/chunk-GFGEMANG.js +5820 -0
- package/dist/chunk-J2MTY7EY.js +5780 -0
- package/dist/chunk-OTOMFL3L.js +5773 -0
- package/dist/chunk-PHVOJ2R5.js +5781 -0
- package/dist/chunk-SWPCETEB.js +5772 -0
- package/dist/chunk-VP4ABFQK.js +5795 -0
- package/dist/chunk-ZHHKZDHY.js +5795 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/run-interactive-ink-54UJ6WGA.js +535 -0
- package/dist/run-interactive-ink-64XY2KJD.js +535 -0
- package/dist/run-interactive-ink-BU4ZKI3Z.js +535 -0
- package/dist/run-interactive-ink-EU3DN4MJ.js +535 -0
- package/dist/run-interactive-ink-MQTTMSSO.js +535 -0
- package/dist/run-interactive-ink-NT66KRS5.js +535 -0
- package/dist/run-interactive-ink-YWJ5OBNI.js +535 -0
- package/package.json +3 -4
- package/src/index.ts +224 -241
- package/src/init-onboarding.ts +12 -0
- package/test/cli.test.ts +129 -84
package/src/index.ts
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
import { createInterface } from "node:readline/promises";
|
|
43
43
|
import {
|
|
44
44
|
runInitOnboarding,
|
|
45
|
+
type DeployTarget,
|
|
45
46
|
type InitOnboardingOptions,
|
|
46
47
|
} from "./init-onboarding.js";
|
|
47
48
|
import {
|
|
@@ -493,7 +494,7 @@ ${name}/
|
|
|
493
494
|
\`\`\`bash
|
|
494
495
|
# Build for Vercel
|
|
495
496
|
poncho build vercel
|
|
496
|
-
|
|
497
|
+
vercel deploy --prod
|
|
497
498
|
|
|
498
499
|
# Build for Docker
|
|
499
500
|
poncho build docker
|
|
@@ -506,19 +507,7 @@ https://github.com/cesr/poncho-ai
|
|
|
506
507
|
|
|
507
508
|
const ENV_TEMPLATE = "ANTHROPIC_API_KEY=sk-ant-...\n";
|
|
508
509
|
const GITIGNORE_TEMPLATE =
|
|
509
|
-
".env\nnode_modules\ndist\n.poncho
|
|
510
|
-
const VERCEL_RUNTIME_DEPENDENCIES: Record<string, string> = {
|
|
511
|
-
"@anthropic-ai/sdk": "^0.74.0",
|
|
512
|
-
"@aws-sdk/client-dynamodb": "^3.988.0",
|
|
513
|
-
"@latitude-data/telemetry": "^2.0.2",
|
|
514
|
-
commander: "^12.0.0",
|
|
515
|
-
dotenv: "^16.4.0",
|
|
516
|
-
jiti: "^2.6.1",
|
|
517
|
-
mustache: "^4.2.0",
|
|
518
|
-
openai: "^6.3.0",
|
|
519
|
-
redis: "^5.10.0",
|
|
520
|
-
yaml: "^2.8.1",
|
|
521
|
-
};
|
|
510
|
+
".env\nnode_modules\ndist\n.poncho/\ninteractive-session.json\n.vercel\n";
|
|
522
511
|
const TEST_TEMPLATE = `tests:
|
|
523
512
|
- name: "Basic sanity"
|
|
524
513
|
task: "What is 2 + 2?"
|
|
@@ -617,35 +606,106 @@ const ensureFile = async (path: string, content: string): Promise<void> => {
|
|
|
617
606
|
await writeFile(path, content, { encoding: "utf8", flag: "wx" });
|
|
618
607
|
};
|
|
619
608
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
609
|
+
type DeployScaffoldTarget = Exclude<DeployTarget, "none">;
|
|
610
|
+
|
|
611
|
+
const normalizeDeployTarget = (target: string): DeployScaffoldTarget => {
|
|
612
|
+
const normalized = target.toLowerCase();
|
|
613
|
+
if (
|
|
614
|
+
normalized === "vercel" ||
|
|
615
|
+
normalized === "docker" ||
|
|
616
|
+
normalized === "lambda" ||
|
|
617
|
+
normalized === "fly"
|
|
618
|
+
) {
|
|
619
|
+
return normalized;
|
|
625
620
|
}
|
|
626
|
-
|
|
627
|
-
// Build outputs should contain materialized files, not symlinks to paths
|
|
628
|
-
// that may not exist inside deployment artifacts (e.g. .agents/skills/*).
|
|
629
|
-
await cp(sourcePath, destinationPath, { recursive: true, dereference: true });
|
|
621
|
+
throw new Error(`Unsupported build target: ${target}`);
|
|
630
622
|
};
|
|
631
623
|
|
|
632
|
-
const
|
|
633
|
-
const
|
|
624
|
+
const readCliVersion = async (): Promise<string> => {
|
|
625
|
+
const fallback = "0.1.0";
|
|
634
626
|
try {
|
|
635
|
-
|
|
636
|
-
|
|
627
|
+
const packageJsonPath = resolve(packageRoot, "package.json");
|
|
628
|
+
const content = await readFile(packageJsonPath, "utf8");
|
|
629
|
+
const parsed = JSON.parse(content) as { version?: unknown };
|
|
630
|
+
if (typeof parsed.version === "string" && parsed.version.trim().length > 0) {
|
|
631
|
+
return parsed.version;
|
|
632
|
+
}
|
|
637
633
|
} catch {
|
|
638
|
-
|
|
634
|
+
// Use fallback when package metadata cannot be read.
|
|
639
635
|
}
|
|
636
|
+
return fallback;
|
|
640
637
|
};
|
|
641
638
|
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
639
|
+
const writeScaffoldFile = async (
|
|
640
|
+
filePath: string,
|
|
641
|
+
content: string,
|
|
642
|
+
options: { force?: boolean; writtenPaths: string[]; baseDir: string },
|
|
643
|
+
): Promise<void> => {
|
|
644
|
+
if (!options.force) {
|
|
645
|
+
try {
|
|
646
|
+
await access(filePath);
|
|
647
|
+
throw new Error(
|
|
648
|
+
`Refusing to overwrite existing file: ${relative(options.baseDir, filePath)}. Re-run with --force to overwrite.`,
|
|
649
|
+
);
|
|
650
|
+
} catch (error) {
|
|
651
|
+
if (!(error instanceof Error) || !error.message.includes("Refusing to overwrite")) {
|
|
652
|
+
// File does not exist, safe to continue.
|
|
653
|
+
} else {
|
|
654
|
+
throw error;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
659
|
+
await writeFile(filePath, content, "utf8");
|
|
660
|
+
options.writtenPaths.push(relative(options.baseDir, filePath));
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
const ensureRuntimeCliDependency = async (
|
|
664
|
+
projectDir: string,
|
|
665
|
+
cliVersion: string,
|
|
666
|
+
): Promise<string[]> => {
|
|
667
|
+
const packageJsonPath = resolve(projectDir, "package.json");
|
|
668
|
+
const content = await readFile(packageJsonPath, "utf8");
|
|
669
|
+
const parsed = JSON.parse(content) as {
|
|
670
|
+
dependencies?: Record<string, string>;
|
|
671
|
+
devDependencies?: Record<string, string>;
|
|
672
|
+
};
|
|
673
|
+
const dependencies = { ...(parsed.dependencies ?? {}) };
|
|
674
|
+
const isLocalOnlySpecifier = (value: string | undefined): boolean =>
|
|
675
|
+
typeof value === "string" &&
|
|
676
|
+
(value.startsWith("link:") || value.startsWith("workspace:") || value.startsWith("file:"));
|
|
677
|
+
|
|
678
|
+
// Deployment projects should not depend on local monorepo paths.
|
|
679
|
+
if (isLocalOnlySpecifier(dependencies["@poncho-ai/harness"])) {
|
|
680
|
+
delete dependencies["@poncho-ai/harness"];
|
|
681
|
+
}
|
|
682
|
+
if (isLocalOnlySpecifier(dependencies["@poncho-ai/sdk"])) {
|
|
683
|
+
delete dependencies["@poncho-ai/sdk"];
|
|
684
|
+
}
|
|
685
|
+
dependencies["@poncho-ai/cli"] = `^${cliVersion}`;
|
|
686
|
+
parsed.dependencies = dependencies;
|
|
687
|
+
await writeFile(packageJsonPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
|
|
688
|
+
return [relative(projectDir, packageJsonPath)];
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
const scaffoldDeployTarget = async (
|
|
692
|
+
projectDir: string,
|
|
693
|
+
target: DeployScaffoldTarget,
|
|
694
|
+
options?: { force?: boolean },
|
|
695
|
+
): Promise<string[]> => {
|
|
696
|
+
const writtenPaths: string[] = [];
|
|
697
|
+
const cliVersion = await readCliVersion();
|
|
698
|
+
const sharedServerEntrypoint = `import { startDevServer } from "@poncho-ai/cli";
|
|
699
|
+
|
|
700
|
+
const port = Number.parseInt(process.env.PORT ?? "3000", 10);
|
|
701
|
+
await startDevServer(Number.isNaN(port) ? 3000 : port, { workingDir: process.cwd() });
|
|
702
|
+
`;
|
|
703
|
+
|
|
704
|
+
if (target === "vercel") {
|
|
705
|
+
const entryPath = resolve(projectDir, "api", "index.mjs");
|
|
706
|
+
await writeScaffoldFile(
|
|
707
|
+
entryPath,
|
|
708
|
+
`import { createRequestHandler } from "@poncho-ai/cli";
|
|
649
709
|
let handlerPromise;
|
|
650
710
|
export default async function handler(req, res) {
|
|
651
711
|
try {
|
|
@@ -663,68 +723,114 @@ export default async function handler(req, res) {
|
|
|
663
723
|
}
|
|
664
724
|
}
|
|
665
725
|
`,
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
"
|
|
713
|
-
"
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
"node:assert",
|
|
723
|
-
"node:buffer",
|
|
724
|
-
"node:timers",
|
|
725
|
-
"node:timers/promises",
|
|
726
|
-
],
|
|
726
|
+
{ force: options?.force, writtenPaths, baseDir: projectDir },
|
|
727
|
+
);
|
|
728
|
+
const vercelConfigPath = resolve(projectDir, "vercel.json");
|
|
729
|
+
await writeScaffoldFile(
|
|
730
|
+
vercelConfigPath,
|
|
731
|
+
`${JSON.stringify(
|
|
732
|
+
{
|
|
733
|
+
version: 2,
|
|
734
|
+
functions: {
|
|
735
|
+
"api/index.mjs": {
|
|
736
|
+
includeFiles: "{AGENT.md,poncho.config.js,skills/**,tests/**}",
|
|
737
|
+
},
|
|
738
|
+
},
|
|
739
|
+
routes: [{ src: "/(.*)", dest: "/api/index.mjs" }],
|
|
740
|
+
},
|
|
741
|
+
null,
|
|
742
|
+
2,
|
|
743
|
+
)}\n`,
|
|
744
|
+
{ force: options?.force, writtenPaths, baseDir: projectDir },
|
|
745
|
+
);
|
|
746
|
+
} else if (target === "docker") {
|
|
747
|
+
const dockerfilePath = resolve(projectDir, "Dockerfile");
|
|
748
|
+
await writeScaffoldFile(
|
|
749
|
+
dockerfilePath,
|
|
750
|
+
`FROM node:20-slim
|
|
751
|
+
WORKDIR /app
|
|
752
|
+
COPY package.json package.json
|
|
753
|
+
COPY AGENT.md AGENT.md
|
|
754
|
+
COPY poncho.config.js poncho.config.js
|
|
755
|
+
COPY skills skills
|
|
756
|
+
COPY tests tests
|
|
757
|
+
COPY .env.example .env.example
|
|
758
|
+
RUN corepack enable && npm install -g @poncho-ai/cli@^${cliVersion}
|
|
759
|
+
COPY server.js server.js
|
|
760
|
+
EXPOSE 3000
|
|
761
|
+
CMD ["node","server.js"]
|
|
762
|
+
`,
|
|
763
|
+
{ force: options?.force, writtenPaths, baseDir: projectDir },
|
|
764
|
+
);
|
|
765
|
+
await writeScaffoldFile(resolve(projectDir, "server.js"), sharedServerEntrypoint, {
|
|
766
|
+
force: options?.force,
|
|
767
|
+
writtenPaths,
|
|
768
|
+
baseDir: projectDir,
|
|
769
|
+
});
|
|
770
|
+
} else if (target === "lambda") {
|
|
771
|
+
await writeScaffoldFile(
|
|
772
|
+
resolve(projectDir, "lambda-handler.js"),
|
|
773
|
+
`import { startDevServer } from "@poncho-ai/cli";
|
|
774
|
+
let serverPromise;
|
|
775
|
+
export const handler = async (event = {}) => {
|
|
776
|
+
if (!serverPromise) {
|
|
777
|
+
serverPromise = startDevServer(0, { workingDir: process.cwd() });
|
|
778
|
+
}
|
|
779
|
+
const body = JSON.stringify({
|
|
780
|
+
status: "ready",
|
|
781
|
+
route: event.rawPath ?? event.path ?? "/",
|
|
727
782
|
});
|
|
783
|
+
return { statusCode: 200, headers: { "content-type": "application/json" }, body };
|
|
784
|
+
};
|
|
785
|
+
`,
|
|
786
|
+
{ force: options?.force, writtenPaths, baseDir: projectDir },
|
|
787
|
+
);
|
|
788
|
+
} else if (target === "fly") {
|
|
789
|
+
await writeScaffoldFile(
|
|
790
|
+
resolve(projectDir, "fly.toml"),
|
|
791
|
+
`app = "poncho-app"
|
|
792
|
+
[env]
|
|
793
|
+
PORT = "3000"
|
|
794
|
+
[http_service]
|
|
795
|
+
internal_port = 3000
|
|
796
|
+
force_https = true
|
|
797
|
+
auto_start_machines = true
|
|
798
|
+
auto_stop_machines = "stop"
|
|
799
|
+
min_machines_running = 0
|
|
800
|
+
`,
|
|
801
|
+
{ force: options?.force, writtenPaths, baseDir: projectDir },
|
|
802
|
+
);
|
|
803
|
+
await writeScaffoldFile(
|
|
804
|
+
resolve(projectDir, "Dockerfile"),
|
|
805
|
+
`FROM node:20-slim
|
|
806
|
+
WORKDIR /app
|
|
807
|
+
COPY package.json package.json
|
|
808
|
+
COPY AGENT.md AGENT.md
|
|
809
|
+
COPY poncho.config.js poncho.config.js
|
|
810
|
+
COPY skills skills
|
|
811
|
+
COPY tests tests
|
|
812
|
+
RUN npm install -g @poncho-ai/cli@^${cliVersion}
|
|
813
|
+
COPY server.js server.js
|
|
814
|
+
EXPOSE 3000
|
|
815
|
+
CMD ["node","server.js"]
|
|
816
|
+
`,
|
|
817
|
+
{ force: options?.force, writtenPaths, baseDir: projectDir },
|
|
818
|
+
);
|
|
819
|
+
await writeScaffoldFile(resolve(projectDir, "server.js"), sharedServerEntrypoint, {
|
|
820
|
+
force: options?.force,
|
|
821
|
+
writtenPaths,
|
|
822
|
+
baseDir: projectDir,
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const packagePaths = await ensureRuntimeCliDependency(projectDir, cliVersion);
|
|
827
|
+
for (const path of packagePaths) {
|
|
828
|
+
if (!writtenPaths.includes(path)) {
|
|
829
|
+
writtenPaths.push(path);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return writtenPaths;
|
|
728
834
|
};
|
|
729
835
|
|
|
730
836
|
const renderConfigFile = (config: PonchoConfig): string =>
|
|
@@ -844,6 +950,13 @@ export const initProject = async (
|
|
|
844
950
|
process.stdout.write(` ${D}+${R} ${D}${file.path}${R}\n`);
|
|
845
951
|
}
|
|
846
952
|
|
|
953
|
+
if (onboarding.deployTarget !== "none") {
|
|
954
|
+
const deployFiles = await scaffoldDeployTarget(projectDir, onboarding.deployTarget);
|
|
955
|
+
for (const filePath of deployFiles) {
|
|
956
|
+
process.stdout.write(` ${D}+${R} ${D}${filePath}${R}\n`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
847
960
|
await initializeOnboardingMarker(projectDir, {
|
|
848
961
|
allowIntro: !(onboardingOptions.yes ?? false),
|
|
849
962
|
});
|
|
@@ -2415,150 +2528,19 @@ export const runTests = async (
|
|
|
2415
2528
|
return { passed, failed };
|
|
2416
2529
|
};
|
|
2417
2530
|
|
|
2418
|
-
export const buildTarget = async (
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
const
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
const runtimePackageJson = JSON.stringify(
|
|
2427
|
-
{
|
|
2428
|
-
name: "poncho-runtime-bundle",
|
|
2429
|
-
private: true,
|
|
2430
|
-
type: "module",
|
|
2431
|
-
scripts: {
|
|
2432
|
-
start: "node server.js",
|
|
2433
|
-
},
|
|
2434
|
-
dependencies: {
|
|
2435
|
-
"@poncho-ai/cli": "^0.1.0",
|
|
2436
|
-
},
|
|
2437
|
-
},
|
|
2438
|
-
null,
|
|
2439
|
-
2,
|
|
2440
|
-
);
|
|
2441
|
-
|
|
2442
|
-
if (target === "vercel") {
|
|
2443
|
-
await mkdir(resolve(outDir, "api"), { recursive: true });
|
|
2444
|
-
await copyIfExists(resolve(workingDir, "AGENT.md"), resolve(outDir, "AGENT.md"));
|
|
2445
|
-
await copyIfExists(
|
|
2446
|
-
resolve(workingDir, "poncho.config.js"),
|
|
2447
|
-
resolve(outDir, "poncho.config.js"),
|
|
2448
|
-
);
|
|
2449
|
-
await copyIfExists(resolve(workingDir, "skills"), resolve(outDir, "skills"));
|
|
2450
|
-
await copyIfExists(resolve(workingDir, "tests"), resolve(outDir, "tests"));
|
|
2451
|
-
await writeFile(
|
|
2452
|
-
resolve(outDir, "vercel.json"),
|
|
2453
|
-
JSON.stringify(
|
|
2454
|
-
{
|
|
2455
|
-
version: 2,
|
|
2456
|
-
functions: {
|
|
2457
|
-
"api/index.js": {
|
|
2458
|
-
includeFiles: "{AGENT.md,poncho.config.js,skills/**,tests/**}",
|
|
2459
|
-
},
|
|
2460
|
-
},
|
|
2461
|
-
routes: [{ src: "/(.*)", dest: "/api/index.js" }],
|
|
2462
|
-
},
|
|
2463
|
-
null,
|
|
2464
|
-
2,
|
|
2465
|
-
),
|
|
2466
|
-
"utf8",
|
|
2467
|
-
);
|
|
2468
|
-
await buildVercelHandlerBundle(outDir);
|
|
2469
|
-
await writeFile(
|
|
2470
|
-
resolve(outDir, "package.json"),
|
|
2471
|
-
JSON.stringify(
|
|
2472
|
-
{
|
|
2473
|
-
private: true,
|
|
2474
|
-
type: "module",
|
|
2475
|
-
engines: {
|
|
2476
|
-
node: "20.x",
|
|
2477
|
-
},
|
|
2478
|
-
dependencies: VERCEL_RUNTIME_DEPENDENCIES,
|
|
2479
|
-
},
|
|
2480
|
-
null,
|
|
2481
|
-
2,
|
|
2482
|
-
),
|
|
2483
|
-
"utf8",
|
|
2484
|
-
);
|
|
2485
|
-
} else if (target === "docker") {
|
|
2486
|
-
await writeFile(
|
|
2487
|
-
resolve(outDir, "Dockerfile"),
|
|
2488
|
-
`FROM node:20-slim
|
|
2489
|
-
WORKDIR /app
|
|
2490
|
-
COPY package.json package.json
|
|
2491
|
-
COPY AGENT.md AGENT.md
|
|
2492
|
-
COPY poncho.config.js poncho.config.js
|
|
2493
|
-
COPY skills skills
|
|
2494
|
-
COPY tests tests
|
|
2495
|
-
COPY .env.example .env.example
|
|
2496
|
-
RUN corepack enable && npm install -g @poncho-ai/cli
|
|
2497
|
-
COPY server.js server.js
|
|
2498
|
-
EXPOSE 3000
|
|
2499
|
-
CMD ["node","server.js"]
|
|
2500
|
-
`,
|
|
2501
|
-
"utf8",
|
|
2502
|
-
);
|
|
2503
|
-
await writeFile(resolve(outDir, "server.js"), serverEntrypoint, "utf8");
|
|
2504
|
-
await writeFile(resolve(outDir, "package.json"), runtimePackageJson, "utf8");
|
|
2505
|
-
} else if (target === "lambda") {
|
|
2506
|
-
await writeFile(
|
|
2507
|
-
resolve(outDir, "lambda-handler.js"),
|
|
2508
|
-
`import { startDevServer } from "@poncho-ai/cli";
|
|
2509
|
-
let serverPromise;
|
|
2510
|
-
export const handler = async (event = {}) => {
|
|
2511
|
-
if (!serverPromise) {
|
|
2512
|
-
serverPromise = startDevServer(0, { workingDir: process.cwd() });
|
|
2513
|
-
}
|
|
2514
|
-
const body = JSON.stringify({
|
|
2515
|
-
status: "ready",
|
|
2516
|
-
route: event.rawPath ?? event.path ?? "/",
|
|
2531
|
+
export const buildTarget = async (
|
|
2532
|
+
workingDir: string,
|
|
2533
|
+
target: string,
|
|
2534
|
+
options?: { force?: boolean },
|
|
2535
|
+
): Promise<void> => {
|
|
2536
|
+
const normalizedTarget = normalizeDeployTarget(target);
|
|
2537
|
+
const writtenPaths = await scaffoldDeployTarget(workingDir, normalizedTarget, {
|
|
2538
|
+
force: options?.force,
|
|
2517
2539
|
});
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
"utf8",
|
|
2522
|
-
);
|
|
2523
|
-
await writeFile(resolve(outDir, "package.json"), runtimePackageJson, "utf8");
|
|
2524
|
-
} else if (target === "fly") {
|
|
2525
|
-
await writeFile(
|
|
2526
|
-
resolve(outDir, "fly.toml"),
|
|
2527
|
-
`app = "poncho-app"
|
|
2528
|
-
[env]
|
|
2529
|
-
PORT = "3000"
|
|
2530
|
-
[http_service]
|
|
2531
|
-
internal_port = 3000
|
|
2532
|
-
force_https = true
|
|
2533
|
-
auto_start_machines = true
|
|
2534
|
-
auto_stop_machines = "stop"
|
|
2535
|
-
min_machines_running = 0
|
|
2536
|
-
`,
|
|
2537
|
-
"utf8",
|
|
2538
|
-
);
|
|
2539
|
-
await writeFile(
|
|
2540
|
-
resolve(outDir, "Dockerfile"),
|
|
2541
|
-
`FROM node:20-slim
|
|
2542
|
-
WORKDIR /app
|
|
2543
|
-
COPY package.json package.json
|
|
2544
|
-
COPY AGENT.md AGENT.md
|
|
2545
|
-
COPY poncho.config.js poncho.config.js
|
|
2546
|
-
COPY skills skills
|
|
2547
|
-
COPY tests tests
|
|
2548
|
-
RUN npm install -g @poncho-ai/cli
|
|
2549
|
-
COPY server.js server.js
|
|
2550
|
-
EXPOSE 3000
|
|
2551
|
-
CMD ["node","server.js"]
|
|
2552
|
-
`,
|
|
2553
|
-
"utf8",
|
|
2554
|
-
);
|
|
2555
|
-
await writeFile(resolve(outDir, "server.js"), serverEntrypoint, "utf8");
|
|
2556
|
-
await writeFile(resolve(outDir, "package.json"), runtimePackageJson, "utf8");
|
|
2557
|
-
} else {
|
|
2558
|
-
throw new Error(`Unsupported build target: ${target}`);
|
|
2540
|
+
process.stdout.write(`Scaffolded deploy files for ${normalizedTarget}:\n`);
|
|
2541
|
+
for (const filePath of writtenPaths) {
|
|
2542
|
+
process.stdout.write(` - ${filePath}\n`);
|
|
2559
2543
|
}
|
|
2560
|
-
|
|
2561
|
-
process.stdout.write(`Build artifacts generated at ${outDir}\n`);
|
|
2562
2544
|
};
|
|
2563
2545
|
|
|
2564
2546
|
const normalizeMcpName = (entry: { url?: string; name?: string }): string =>
|
|
@@ -2950,9 +2932,10 @@ export const buildCli = (): Command => {
|
|
|
2950
2932
|
program
|
|
2951
2933
|
.command("build")
|
|
2952
2934
|
.argument("<target>", "vercel|docker|lambda|fly")
|
|
2953
|
-
.
|
|
2954
|
-
.
|
|
2955
|
-
|
|
2935
|
+
.option("--force", "overwrite existing deployment files")
|
|
2936
|
+
.description("Scaffold deployment files for a target")
|
|
2937
|
+
.action(async (target: string, options: { force?: boolean }) => {
|
|
2938
|
+
await buildTarget(process.cwd(), target, { force: options.force });
|
|
2956
2939
|
});
|
|
2957
2940
|
|
|
2958
2941
|
const mcpCommand = program.command("mcp").description("Manage MCP servers");
|
package/src/init-onboarding.ts
CHANGED
|
@@ -21,6 +21,7 @@ const bold = (s: string): string => `${C.bold}${s}${C.reset}`;
|
|
|
21
21
|
const INPUT_CARET = "»";
|
|
22
22
|
|
|
23
23
|
type OnboardingAnswers = Record<string, string | number | boolean>;
|
|
24
|
+
export type DeployTarget = "none" | "vercel" | "docker" | "fly" | "lambda";
|
|
24
25
|
|
|
25
26
|
export type InitOnboardingOptions = {
|
|
26
27
|
yes?: boolean;
|
|
@@ -33,6 +34,7 @@ export type InitOnboardingResult = {
|
|
|
33
34
|
envExample: string;
|
|
34
35
|
envFile: string;
|
|
35
36
|
envNeedsUserInput: boolean;
|
|
37
|
+
deployTarget: DeployTarget;
|
|
36
38
|
agentModel: {
|
|
37
39
|
provider: "anthropic" | "openai";
|
|
38
40
|
name: string;
|
|
@@ -276,6 +278,14 @@ const askOnboardingQuestions = async (options: InitOnboardingOptions): Promise<O
|
|
|
276
278
|
const getProviderModelName = (provider: string): string =>
|
|
277
279
|
provider === "openai" ? "gpt-4.1" : "claude-opus-4-5";
|
|
278
280
|
|
|
281
|
+
const normalizeDeployTarget = (value: unknown): DeployTarget => {
|
|
282
|
+
const target = typeof value === "string" ? value.toLowerCase() : "";
|
|
283
|
+
if (target === "vercel" || target === "docker" || target === "fly" || target === "lambda") {
|
|
284
|
+
return target;
|
|
285
|
+
}
|
|
286
|
+
return "none";
|
|
287
|
+
};
|
|
288
|
+
|
|
279
289
|
const maybeSet = (
|
|
280
290
|
target: object,
|
|
281
291
|
key: string,
|
|
@@ -506,6 +516,7 @@ export const runInitOnboarding = async (
|
|
|
506
516
|
): Promise<InitOnboardingResult> => {
|
|
507
517
|
const answers = await askOnboardingQuestions(options);
|
|
508
518
|
const provider = String(answers["model.provider"] ?? "anthropic");
|
|
519
|
+
const deployTarget = normalizeDeployTarget(answers["deploy.target"]);
|
|
509
520
|
const config = buildConfigFromOnboardingAnswers(answers);
|
|
510
521
|
const envExampleLines = collectEnvVars(answers);
|
|
511
522
|
const envFileLines = collectEnvFileLines(answers);
|
|
@@ -522,6 +533,7 @@ export const runInitOnboarding = async (
|
|
|
522
533
|
envExample: `${envExampleLines.join("\n")}\n`,
|
|
523
534
|
envFile: envFileLines.length > 0 ? `${envFileLines.join("\n")}\n` : "",
|
|
524
535
|
envNeedsUserInput,
|
|
536
|
+
deployTarget,
|
|
525
537
|
agentModel: {
|
|
526
538
|
provider: provider === "openai" ? "openai" : "anthropic",
|
|
527
539
|
name: getProviderModelName(provider),
|