@@ -9,13 +9,13 @@ import { bundleAgent } from "../utils/bundle";
99
1010interface DeployOptions {
1111 dir : string ;
12- spec ?: string ;
1312 id ?: string ;
1413 deployToken ?: string ;
1514 apiUrl : string ;
1615 name ?: string ;
1716 description ?: string ;
1817 environment ?: string ;
18+ dryRun ?: boolean ;
1919}
2020
2121interface PackageJson {
@@ -32,40 +32,19 @@ interface PackageJson {
3232 env ?: Record < string , any > ;
3333}
3434
35+ interface AgentSource {
36+ dependencies : Record < string , string > ;
37+ files : Record < string , string > ;
38+ }
39+
3540export async function deployCommand ( options : DeployOptions ) {
3641 const agentPath = path . resolve ( process . cwd ( ) , options . dir ) ;
3742
38- // Read spec file if provided
39- let specContent : string | undefined ;
40- if ( options . spec ) {
41- const specPath = path . resolve ( process . cwd ( ) , options . spec ) ;
42- if ( ! fs . existsSync ( specPath ) ) {
43- out . error ( `Spec file not found: ${ options . spec } ` ) ;
44- process . exit ( 1 ) ;
45- }
46- try {
47- specContent = fs . readFileSync ( specPath , "utf-8" ) ;
48- } catch ( error ) {
49- out . error ( "Failed to read spec file" , String ( error ) ) ;
50- process . exit ( 1 ) ;
51- }
52- }
53-
54- // Verify we're in an agent directory by checking for package.json
55- // Package.json is required when deploying source code, optional for spec
43+ // Check for package.json
5644 const packageJsonPath = path . join ( agentPath , "package.json" ) ;
5745 let packageJson : PackageJson | undefined ;
5846
59- if ( ! options . spec ) {
60- // Source code deployment requires package.json
61- if ( ! fs . existsSync ( packageJsonPath ) ) {
62- out . error (
63- "package.json not found. Are you in an agent directory?" ,
64- "Run this command from your agent's root directory, or use --spec to deploy from a spec file"
65- ) ;
66- process . exit ( 1 ) ;
67- }
68-
47+ if ( fs . existsSync ( packageJsonPath ) ) {
6948 // Read and validate package.json
7049 try {
7150 const packageJsonContent = fs . readFileSync ( packageJsonPath , "utf-8" ) ;
@@ -74,13 +53,41 @@ export async function deployCommand(options: DeployOptions) {
7453 out . error ( "Failed to parse package.json" , String ( error ) ) ;
7554 process . exit ( 1 ) ;
7655 }
77- } else if ( fs . existsSync ( packageJsonPath ) ) {
78- // Optional: read package.json for defaults when using spec
79- try {
80- const packageJsonContent = fs . readFileSync ( packageJsonPath , "utf-8" ) ;
81- packageJson = JSON . parse ( packageJsonContent ) ;
82- } catch {
83- // Ignore errors, package.json is optional for spec deployments
56+ } else {
57+ // No package.json - check for plot-agent.md as fallback
58+ const specPath = path . join ( agentPath , "plot-agent.md" ) ;
59+ if ( fs . existsSync ( specPath ) ) {
60+ out . info (
61+ "No package.json found, but plot-agent.md exists" ,
62+ [ "Generating agent from spec first..." ]
63+ ) ;
64+
65+ // Import and run generate command
66+ const { generateCommand } = await import ( "./generate" ) ;
67+ await generateCommand ( {
68+ dir : options . dir ,
69+ id : options . id ,
70+ deployToken : options . deployToken ,
71+ apiUrl : options . apiUrl ,
72+ } ) ;
73+
74+ // Re-read the generated package.json
75+ try {
76+ const packageJsonContent = fs . readFileSync ( packageJsonPath , "utf-8" ) ;
77+ packageJson = JSON . parse ( packageJsonContent ) ;
78+ } catch ( error ) {
79+ out . error ( "Failed to read generated package.json" , String ( error ) ) ;
80+ process . exit ( 1 ) ;
81+ }
82+
83+ out . blank ( ) ;
84+ out . progress ( "Continuing with deployment..." ) ;
85+ } else {
86+ out . error (
87+ "Neither package.json nor plot-agent.md found" ,
88+ "Run 'plot agent create' to create a new agent, or create a plot-agent.md spec file"
89+ ) ;
90+ process . exit ( 1 ) ;
8491 }
8592 }
8693
@@ -91,26 +98,8 @@ export async function deployCommand(options: DeployOptions) {
9198
9299 const environment = options . environment || "personal" ;
93100
94- // For spec deployments without package.json, require CLI options
95- if ( options . spec && ! packageJson ) {
96- if ( ! options . id ) {
97- out . error (
98- "Agent ID is required when deploying spec without package.json" ,
99- "Provide --id flag"
100- ) ;
101- process . exit ( 1 ) ;
102- }
103- if ( ! options . name ) {
104- out . error (
105- "Agent name is required when deploying spec without package.json" ,
106- "Provide --name flag"
107- ) ;
108- process . exit ( 1 ) ;
109- }
110- }
111-
112- // Validate required fields for source code deployments
113- if ( ! options . spec && ! agentName ) {
101+ // Validate required fields
102+ if ( ! agentName ) {
114103 out . error (
115104 "package.json is missing displayName" ,
116105 'Add "displayName": "Your Agent Name" to package.json'
@@ -200,55 +189,47 @@ export async function deployCommand(options: DeployOptions) {
200189 }
201190 }
202191
203- // Prepare request body based on spec or source code
192+ // Build the agent
204193 let requestBody : {
205- module ?: string ;
206- spec ?: string ;
194+ module : string ;
207195 name : string ;
208196 description ?: string ;
209197 environment : string ;
198+ dryRun ?: boolean ;
210199 } ;
211200
212201 try {
213- if ( options . spec && specContent ) {
214- // Deploying from spec
215- out . progress ( `Deploying ${ deploymentName } from spec...` ) ;
216-
217- requestBody = {
218- spec : specContent ,
219- name : deploymentName ! ,
220- description : deploymentDescription ,
221- environment : environment ,
222- } ;
223- } else {
224- // Deploying from source code - build the agent
225- out . progress ( `Building ${ deploymentName } ...` ) ;
202+ out . progress (
203+ options . dryRun
204+ ? `Validating ${ deploymentName } ...`
205+ : `Building ${ deploymentName } ...`
206+ ) ;
226207
227- const result = await bundleAgent ( agentPath , {
228- minify : false ,
229- sourcemap : true ,
230- } ) ;
208+ const result = await bundleAgent ( agentPath , {
209+ minify : false ,
210+ sourcemap : true ,
211+ } ) ;
231212
232- const moduleContent = result . code ;
213+ const moduleContent = result . code ;
233214
234- if ( result . warnings . length > 0 ) {
235- out . warning ( "Build completed with warnings" ) ;
236- for ( const warning of result . warnings . slice ( 0 , 5 ) ) {
237- console . warn ( ` ${ warning } ` ) ;
238- }
239- if ( result . warnings . length > 5 ) {
240- console . warn ( ` ... and ${ result . warnings . length - 5 } more warnings` ) ;
241- }
215+ if ( result . warnings . length > 0 ) {
216+ out . warning ( "Build completed with warnings" ) ;
217+ for ( const warning of result . warnings . slice ( 0 , 5 ) ) {
218+ console . warn ( ` ${ warning } ` ) ;
219+ }
220+ if ( result . warnings . length > 5 ) {
221+ console . warn ( ` ... and ${ result . warnings . length - 5 } more warnings` ) ;
242222 }
243-
244- requestBody = {
245- module : moduleContent ,
246- name : deploymentName ! ,
247- description : deploymentDescription ,
248- environment : environment ,
249- } ;
250223 }
251224
225+ requestBody = {
226+ module : moduleContent ,
227+ name : deploymentName ! ,
228+ description : deploymentDescription ,
229+ environment : environment ,
230+ dryRun : options . dryRun ,
231+ } ;
232+
252233 // Validate all required deployment fields
253234 if ( ! deploymentName ) {
254235 out . error (
@@ -291,6 +272,24 @@ export async function deployCommand(options: DeployOptions) {
291272
292273 const result = ( await response . json ( ) ) as any ;
293274
275+ // Handle dryRun response
276+ if ( options . dryRun ) {
277+ if ( result . errors && result . errors . length > 0 ) {
278+ out . error ( "Validation failed" ) ;
279+ for ( const error of result . errors ) {
280+ console . error ( ` ${ error } ` ) ;
281+ }
282+ process . exit ( 1 ) ;
283+ } else {
284+ out . success ( "Validation passed - agent is ready to deploy" ) ;
285+ out . info (
286+ "Run without --dry-run to deploy" ,
287+ [ `plot agent deploy` ]
288+ ) ;
289+ }
290+ return ;
291+ }
292+
294293 // Show dependencies from API response
295294 const dependencies = result . dependencies ;
296295 if ( dependencies && dependencies . length > 0 ) {
0 commit comments