@@ -566,14 +566,6 @@ func (c *appContext) apiBase() string {
566566}
567567
568568func (c * appContext ) getClient (ctx context.Context ) (* deckops.Client , error ) {
569- if c .config .Token == "" {
570- if _ , err := c .ensureLoggedIn (ctx , defaultLoginPort , "explicit" ); err != nil {
571- return nil , err
572- }
573- }
574- if c .config .Token == "" {
575- return nil , fmt .Errorf ("Login did not provide a token. Please run `deckflow login` again." )
576- }
577569 if c .client != nil {
578570 return c .client , nil
579571 }
@@ -582,7 +574,13 @@ func (c *appContext) getClient(ctx context.Context) (*deckops.Client, error) {
582574 Token : c .config .Token ,
583575 SpaceID : c .config .SpaceID ,
584576 OnUnauthorized : func (ctx context.Context ) (deckops.AuthRefresh , error ) {
585- token , err := c .ensureLoggedIn (ctx , defaultLoginPort , "unauthorized" )
577+ // First-time visit (no token yet) feels like an explicit login;
578+ // an expired token reads as "auth expired".
579+ reason := "unauthorized"
580+ if c .config .Token == "" {
581+ reason = "explicit"
582+ }
583+ token , err := c .ensureLoggedIn (ctx , defaultLoginPort , reason )
586584 if err != nil {
587585 return deckops.AuthRefresh {}, err
588586 }
@@ -1045,6 +1043,10 @@ func (c *appContext) runTask(args []string) error {
10451043 return nil
10461044 }
10471045 }
1046+ task , err = c .attachDownloadResult (context .Background (), client , task )
1047+ if err != nil {
1048+ return err
1049+ }
10481050 c .output (task , func () string { return formatTaskDetails (task ) })
10491051 case "delete" :
10501052 if len (args ) != 2 {
@@ -1428,6 +1430,17 @@ func (c *appContext) runFileTask(options fileTaskOptions) error {
14281430 if err != nil {
14291431 return err
14301432 }
1433+
1434+ // Guest mode (no token): the backend parks the task in a pending state
1435+ // and waits for an explicit start signal before executing.
1436+ // If Create triggered a 401 → login → retry, config.Token is now set
1437+ // and we skip the start call (authenticated tasks auto-start).
1438+ if c .config .Token == "" {
1439+ if _ , err := client .Tasks .Start (context .Background (), task .ID ); err != nil {
1440+ return err
1441+ }
1442+ }
1443+
14311444 c .info ("Task created: " + task .ID )
14321445 if options .wait {
14331446 timeoutSec , err := positiveInt (options .timeout , "--timeout" )
@@ -1458,14 +1471,12 @@ func (c *appContext) runFileTask(options fileTaskOptions) error {
14581471 return nil
14591472 }
14601473 }
1461- if options .wait && task .Status == deckops .TaskStatusCompleted {
1462- var downloadResult any
1463- if err := client .Tasks .Down (context .Background (), task .ID , deckops.TaskDownloadOptions {}, & downloadResult ); err != nil {
1464- return err
1465- }
1466- printJSON (downloadResult )
1467- return nil
1474+
1475+ task , err = c .attachDownloadResult (context .Background (), client , task )
1476+ if err != nil {
1477+ return err
14681478 }
1479+
14691480 c .output (task , func () string {
14701481 lines := []string {
14711482 options .title + ":" ,
@@ -1710,9 +1721,12 @@ func (c *appContext) tryWriteTaskOutput(ctx context.Context, client *deckops.Cli
17101721 return result , true
17111722 }
17121723 lastErr = err
1713- if attempt < 3 {
1724+ // Only retry transient network/upstream failures — never 403/4xx business errors.
1725+ if attempt < 3 && deckops .IsRetriableError (err ) {
17141726 time .Sleep (10 * time .Second )
1727+ continue
17151728 }
1729+ break
17161730 }
17171731 message := fmt .Sprintf ("Task completed, but --out result could not be saved to %s after 3 attempts. The task result will be printed below; you can manually download the file from the target/result JSON." , outPath )
17181732 if c .json {
@@ -1725,6 +1739,20 @@ func (c *appContext) tryWriteTaskOutput(ctx context.Context, client *deckops.Cli
17251739 return outputWriteResult {}, false
17261740}
17271741
1742+ // attachDownloadResult loads task result from GET /tools/tasks/:id/download.
1743+ // Task detail/SSE only carries status/progress metadata now.
1744+ func (c * appContext ) attachDownloadResult (ctx context.Context , client * deckops.Client , task * deckops.Task ) (* deckops.Task , error ) {
1745+ if task .Status != deckops .TaskStatusCompleted {
1746+ return task , nil
1747+ }
1748+ var result any
1749+ if err := client .Tasks .Down (ctx , task .ID , deckops.TaskDownloadOptions {}, & result ); err != nil {
1750+ return nil , err
1751+ }
1752+ task .Result = result
1753+ return task , nil
1754+ }
1755+
17281756func (c * appContext ) writeTaskOutput (ctx context.Context , client * deckops.Client , task * deckops.Task , outPath string ) (outputWriteResult , error ) {
17291757 var downloadResult any
17301758 if err := client .Tasks .Down (ctx , task .ID , deckops.TaskDownloadOptions {}, & downloadResult ); err != nil {
@@ -1740,9 +1768,6 @@ func (c *appContext) writeTaskOutput(ctx context.Context, client *deckops.Client
17401768 return outputWriteResult {}, err
17411769 }
17421770 payload := downloadResult
1743- if payload == nil {
1744- payload = task .Result
1745- }
17461771 if payload == nil {
17471772 payload = task
17481773 }
0 commit comments