77 "context"
88 "errors"
99 "fmt"
10- "io"
1110 "os"
1211 "path/filepath"
1312
@@ -72,7 +71,7 @@ func (a *downloadAction) Run(ctx context.Context) error {
7271 // Pre-flight via Get so we can detect the "no associated package" case
7372 // before issuing the :download call. The service returns 404 with a
7473 // dedicated error code when the skill was created from inline JSON (or a
75- // SKILL.md file) rather than a gzip package, but the message is opaque —
74+ // SKILL.md file) rather than a ZIP package, but the message is opaque —
7675 // surfacing the HasBlob check up front gives the user a clearer answer.
7776 skill , err := skillCtx .client .Get (ctx , a .flags .name )
7877 if err != nil {
@@ -82,9 +81,9 @@ func (a *downloadAction) Run(ctx context.Context) error {
8281 return exterrors .Validation (
8382 exterrors .CodeSkillNoPackage ,
8483 fmt .Sprintf ("skill %q has no downloadable package" , a .flags .name ),
85- "only skills created from a `.tar.gz` / `.tgz ` archive have a downloadable " +
84+ "only skills created from a `.zip ` archive have a downloadable " +
8685 "package. Use `azd ai skill show <name>` to inspect metadata; " +
87- "re-create with `azd ai skill create <name> --file <archive>.tar.gz --force` " +
86+ "re-create with `azd ai skill create <name> --file <archive>.zip --force` " +
8887 "if you want a downloadable copy." ,
8988 )
9089 }
@@ -93,20 +92,19 @@ func (a *downloadAction) Run(ctx context.Context) error {
9392 if err != nil {
9493 return exterrors .ServiceFromAzure (err , exterrors .OpDownloadSkill )
9594 }
96- defer body .Close ()
9795
9896 if a .flags .raw {
9997 return a .writeRaw (body , absOut )
10098 }
10199 return a .writeExtracted (body , absOut )
102100}
103101
104- func (a * downloadAction ) writeRaw (body io. Reader , outputDir string ) error {
102+ func (a * downloadAction ) writeRaw (body [] byte , outputDir string ) error {
105103 if err := os .MkdirAll (outputDir , 0700 ); err != nil {
106104 return fmt .Errorf ("create output dir: %w" , err )
107105 }
108106
109- archivePath := filepath .Join (outputDir , a .flags .name + ".tar.gz " )
107+ archivePath := filepath .Join (outputDir , a .flags .name + ".zip " )
110108
111109 // Always Lstat (even with --force) so we never follow a symlink and so we
112110 // refuse to overwrite a non-regular file (directory, device, socket, ...).
@@ -151,7 +149,7 @@ func (a *downloadAction) writeRaw(body io.Reader, outputDir string) error {
151149 if err != nil {
152150 return fmt .Errorf ("create archive: %w" , err )
153151 }
154- if _ , copyErr := io . Copy ( f , body ); copyErr != nil {
152+ if _ , copyErr := f . Write ( body ); copyErr != nil {
155153 _ = f .Close ()
156154 return fmt .Errorf ("write archive: %w" , copyErr )
157155 }
@@ -162,13 +160,13 @@ func (a *downloadAction) writeRaw(body io.Reader, outputDir string) error {
162160 res := downloadResult {
163161 Skill : a .flags .name ,
164162 OutputDir : outputDir ,
165- Archive : a .flags .name + ".tar.gz " ,
163+ Archive : a .flags .name + ".zip " ,
166164 Raw : true ,
167165 }
168166 return a .printResult (res )
169167}
170168
171- func (a * downloadAction ) writeExtracted (body io. Reader , outputDir string ) error {
169+ func (a * downloadAction ) writeExtracted (body [] byte , outputDir string ) error {
172170 result , err := skill_api .SafeExtract (body , skill_api.ExtractOptions {
173171 OutputDir : outputDir ,
174172 Force : a .flags .force ,
@@ -224,11 +222,11 @@ func classifyExtractError(err error, outputDir string) error {
224222 err .Error (),
225223 fmt .Sprintf ("pass --force to overwrite existing files in %s" , outputDir ),
226224 )
227- case errors .Is (err , skill_api .ErrInvalidGzip ):
225+ case errors .Is (err , skill_api .ErrInvalidZip ):
228226 return exterrors .Validation (
229227 exterrors .CodeInvalidParameter ,
230228 err .Error (),
231- "the service did not return a gzip archive; retry or contact support" ,
229+ "the service did not return a valid zip archive; retry or contact support" ,
232230 )
233231 }
234232 return err
@@ -242,15 +240,15 @@ func newDownloadCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
242240 cmd := & cobra.Command {
243241 Use : "download <name>" ,
244242 Short : "Download a Foundry skill package." ,
245- Long : `Download a skill's gzip package.
243+ Long : `Download a skill's ZIP package.
246244
247245By default the CLI extracts the archive into --output-dir (which defaults to
248- './.agents/skills/<name>/'). Pass --raw to write the unmodified gzip archive
246+ './.agents/skills/<name>/'). Pass --raw to write the unmodified ZIP archive
249247into --output-dir instead.
250248
251249Extraction enforces strict safety rules: no absolute paths, no '..' segments,
252- no symlinks, no hard links, no non-regular files , and a 10,000-entry /
253- 512 MB cap on the total uncompressed size.` ,
250+ no symlinks / non-regular entries , and a 10,000-entry / 512 MB cap on the
251+ total uncompressed size.` ,
254252 Args : cobra .ExactArgs (1 ),
255253 RunE : func (cmd * cobra.Command , args []string ) error {
256254 flags .name = args [0 ]
@@ -266,7 +264,7 @@ no symlinks, no hard links, no non-regular files, and a 10,000-entry /
266264 cmd .Flags ().StringVar (& flags .outputDir , "output-dir" , "" ,
267265 "Directory to write the extracted skill (default: ./.agents/skills/<name>/)" )
268266 cmd .Flags ().BoolVar (& flags .raw , "raw" , false ,
269- "Skip extraction; write the gzip archive as-is to --output-dir" )
267+ "Skip extraction; write the ZIP archive as-is to --output-dir" )
270268 cmd .Flags ().BoolVar (& flags .force , "force" , false ,
271269 "Overwrite existing files in --output-dir" )
272270 azdext .RegisterFlagOptions (cmd , azdext.FlagOptions {
0 commit comments