@asteroidai/cli 1.9.11 → 1.9.27

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/core.js CHANGED
@@ -145,7 +145,8 @@ var COMMANDS = [
145
145
  query: [],
146
146
  binary: false,
147
147
  readOnly: false,
148
- destructive: false
148
+ destructive: false,
149
+ capability: "run_execution"
149
150
  },
150
151
  {
151
152
  command: "context",
@@ -1051,7 +1052,8 @@ var COMMANDS = [
1051
1052
  ],
1052
1053
  files: [],
1053
1054
  credentialOrganization: false
1054
- }
1055
+ },
1056
+ capability: "run_execution"
1055
1057
  },
1056
1058
  {
1057
1059
  command: "execution resume",
@@ -1071,7 +1073,8 @@ var COMMANDS = [
1071
1073
  query: [],
1072
1074
  binary: false,
1073
1075
  readOnly: false,
1074
- destructive: false
1076
+ destructive: false,
1077
+ capability: "run_execution"
1075
1078
  },
1076
1079
  {
1077
1080
  command: "insight get",
@@ -1919,7 +1922,8 @@ var COMMANDS = [
1919
1922
  ],
1920
1923
  files: [],
1921
1924
  credentialOrganization: false
1922
- }
1925
+ },
1926
+ capability: "run_execution"
1923
1927
  },
1924
1928
  {
1925
1929
  command: "schedule delete",
@@ -1945,7 +1949,8 @@ var COMMANDS = [
1945
1949
  query: [],
1946
1950
  binary: false,
1947
1951
  readOnly: false,
1948
- destructive: true
1952
+ destructive: true,
1953
+ capability: "run_execution"
1949
1954
  },
1950
1955
  {
1951
1956
  command: "schedule get",
@@ -2126,7 +2131,8 @@ var COMMANDS = [
2126
2131
  ],
2127
2132
  files: [],
2128
2133
  credentialOrganization: false
2129
- }
2134
+ },
2135
+ capability: "run_execution"
2130
2136
  },
2131
2137
  {
2132
2138
  command: "scheduled-execution cancel",
@@ -2146,7 +2152,8 @@ var COMMANDS = [
2146
2152
  query: [],
2147
2153
  binary: false,
2148
2154
  readOnly: false,
2149
- destructive: false
2155
+ destructive: false,
2156
+ capability: "run_execution"
2150
2157
  },
2151
2158
  {
2152
2159
  command: "scheduled-execution create",
@@ -2214,7 +2221,8 @@ var COMMANDS = [
2214
2221
  ],
2215
2222
  files: [],
2216
2223
  credentialOrganization: false
2217
- }
2224
+ },
2225
+ capability: "run_execution"
2218
2226
  },
2219
2227
  {
2220
2228
  command: "scheduled-execution list",
@@ -2316,7 +2324,8 @@ var COMMANDS = [
2316
2324
  ],
2317
2325
  files: [],
2318
2326
  credentialOrganization: false
2319
- }
2327
+ },
2328
+ capability: "run_execution"
2320
2329
  },
2321
2330
  {
2322
2331
  command: "schema validate",
@@ -3456,7 +3465,8 @@ var COMMANDS = [
3456
3465
  ],
3457
3466
  files: [],
3458
3467
  credentialOrganization: false
3459
- }
3468
+ },
3469
+ capability: "run_execution"
3460
3470
  },
3461
3471
  {
3462
3472
  command: "workflow execute-sync",
@@ -3555,112 +3565,8 @@ var COMMANDS = [
3555
3565
  ],
3556
3566
  files: [],
3557
3567
  credentialOrganization: false
3558
- }
3559
- },
3560
- {
3561
- command: "workflow file list",
3562
- tool: "workflow_files_list",
3563
- title: "List workflow files",
3564
- description: "List the workflow's editable head as a directory of files: the graph rendered as structural files (settings.yaml, per-node instructions.md, scripts) alongside the workflow's own files. Those files are referenced by fileId unless contents=all is set. Pass paths to read named files only, or contents=none for a manifest with no bodies. The returned rev is what you pass as baseRev to write.",
3565
- method: "GET",
3566
- path: "/agents/v2/workflows/{workflowId}/head/files",
3567
- args: [
3568
- {
3569
- name: "workflowId",
3570
- flag: "workflow-id",
3571
- type: "string",
3572
- required: true
3573
- }
3574
- ],
3575
- query: [
3576
- {
3577
- name: "contents",
3578
- flag: "contents",
3579
- type: "string",
3580
- required: false,
3581
- description: "Which contents come inline: none (a pure manifest), structural (the default), or all (structural plus file bytes within the response budget).",
3582
- enum: ["none", "structural", "all"]
3583
- },
3584
- {
3585
- name: "paths",
3586
- flag: "paths",
3587
- type: "string[]",
3588
- required: false,
3589
- description: "Return only the files at these exact tree paths. A path that names nothing yields no entry. The inline budget applies to the filtered set, so a narrow read can inline files a whole-tree read would have to skip."
3590
- },
3591
- {
3592
- name: "variants",
3593
- flag: "variants",
3594
- type: "string",
3595
- required: false,
3596
- description: `Narrow which variants/<key>/ files the tree carries: "none" strips every variant, a comma-separated list keeps only those variant keys. Files outside variants/ are always kept, and the response's variantKeys still names every variant. Ignored unless the workflow has variant mode enabled; absent keeps every variant.`
3597
- },
3598
- {
3599
- name: "maxFileBytes",
3600
- flag: "max-file-bytes",
3601
- type: "integer",
3602
- required: false,
3603
- description: "Per-file ceiling, in bytes, for inlined file contents. Clamped to the server maximum. Files above the ceiling stay references."
3604
- },
3605
- {
3606
- name: "maxTotalBytes",
3607
- flag: "max-total-bytes",
3608
- type: "integer",
3609
- required: false,
3610
- description: "Ceiling, in bytes, on the total inlined file contents in one response. Clamped to the server maximum. Files are admitted in path order until the ceiling is reached; the rest stay references."
3611
- }
3612
- ],
3613
- binary: false,
3614
- readOnly: true,
3615
- destructive: false
3616
- },
3617
- {
3618
- command: "workflow file patch",
3619
- tool: "workflow_files_patch",
3620
- title: "Write workflow files",
3621
- description: "Write and delete files on the workflow's editable head in one patch. Structural files re-derive the graph; the workflow's own files go to its file store, you just write paths. Pass the rev from `workflow_files_list` as baseRev; a stale value is rejected with 409, so re-read and retry. Editing a published or already-executed version forks a new one instead of rewriting it. Each write carries `contentBase64`: base64-encode the file content, plain text is rejected with 400.",
3622
- method: "PATCH",
3623
- path: "/agents/v2/workflows/{workflowId}/head/files",
3624
- args: [
3625
- {
3626
- name: "workflowId",
3627
- flag: "workflow-id",
3628
- type: "string",
3629
- required: true
3630
- }
3631
- ],
3632
- query: [],
3633
- binary: false,
3634
- readOnly: false,
3635
- destructive: true,
3636
- body: {
3637
- required: true,
3638
- contentType: "application/json",
3639
- fields: [
3640
- {
3641
- name: "baseRev",
3642
- flag: "base-rev",
3643
- type: "integer",
3644
- required: true,
3645
- description: "The revision this edit is based on. Agent-file-only patches overlay the current head even when this is stale. A structural-file patch is rejected with 409 when it does not match the stored head revision."
3646
- },
3647
- {
3648
- name: "writes",
3649
- flag: "writes",
3650
- type: "json",
3651
- required: true,
3652
- description: "Files to upsert."
3653
- },
3654
- {
3655
- name: "deletes",
3656
- flag: "deletes",
3657
- type: "string[]",
3658
- required: true
3659
- }
3660
- ],
3661
- files: [],
3662
- credentialOrganization: false
3663
- }
3568
+ },
3569
+ capability: "run_execution"
3664
3570
  },
3665
3571
  {
3666
3572
  command: "workflow get",
@@ -3708,7 +3614,8 @@ var COMMANDS = [
3708
3614
  query: [],
3709
3615
  binary: false,
3710
3616
  readOnly: false,
3711
- destructive: false
3617
+ destructive: false,
3618
+ capability: "publish"
3712
3619
  },
3713
3620
  {
3714
3621
  command: "workflow head revert",
@@ -3834,6 +3741,113 @@ var COMMANDS = [
3834
3741
  readOnly: true,
3835
3742
  destructive: false
3836
3743
  },
3744
+ {
3745
+ command: "workflow pull",
3746
+ tool: "workflow_files_list",
3747
+ title: "List workflow files",
3748
+ description: "List the workflow's editable head as a directory of files: the graph rendered as structural files (settings.yaml, per-node instructions.md, scripts) alongside the workflow's own files. Those files are referenced by fileId unless contents=all is set. Pass paths to read named files only, or contents=none for a manifest with no bodies. The returned rev is what you pass as baseRev to write.",
3749
+ method: "GET",
3750
+ path: "/agents/v2/workflows/{workflowId}/head/files",
3751
+ args: [
3752
+ {
3753
+ name: "workflowId",
3754
+ flag: "workflow-id",
3755
+ type: "string",
3756
+ required: true
3757
+ }
3758
+ ],
3759
+ query: [
3760
+ {
3761
+ name: "contents",
3762
+ flag: "contents",
3763
+ type: "string",
3764
+ required: false,
3765
+ description: "Which contents come inline: none (a pure manifest), structural (the default), or all (structural plus file bytes within the response budget).",
3766
+ enum: ["none", "structural", "all"]
3767
+ },
3768
+ {
3769
+ name: "paths",
3770
+ flag: "paths",
3771
+ type: "string[]",
3772
+ required: false,
3773
+ description: "Return only the files at these exact tree paths. A path that names nothing yields no entry. The inline budget applies to the filtered set, so a narrow read can inline files a whole-tree read would have to skip."
3774
+ },
3775
+ {
3776
+ name: "variants",
3777
+ flag: "variants",
3778
+ type: "string",
3779
+ required: false,
3780
+ description: `Narrow which variants/<key>/ files the tree carries: "none" strips every variant, a comma-separated list keeps only those variant keys. Files outside variants/ are always kept, and the response's variantKeys still names every variant. Ignored unless the workflow has variant mode enabled; absent keeps every variant.`
3781
+ },
3782
+ {
3783
+ name: "maxFileBytes",
3784
+ flag: "max-file-bytes",
3785
+ type: "integer",
3786
+ required: false,
3787
+ description: "Per-file ceiling, in bytes, for inlined file contents. Clamped to the server maximum. Files above the ceiling stay references."
3788
+ },
3789
+ {
3790
+ name: "maxTotalBytes",
3791
+ flag: "max-total-bytes",
3792
+ type: "integer",
3793
+ required: false,
3794
+ description: "Ceiling, in bytes, on the total inlined file contents in one response. Clamped to the server maximum. Files are admitted in path order until the ceiling is reached; the rest stay references."
3795
+ }
3796
+ ],
3797
+ binary: false,
3798
+ readOnly: true,
3799
+ destructive: false,
3800
+ files: "pull"
3801
+ },
3802
+ {
3803
+ command: "workflow push",
3804
+ tool: "workflow_files_patch",
3805
+ title: "Write workflow files",
3806
+ description: "Write and delete files on the workflow's editable head in one patch. Structural files re-derive the graph; the workflow's own files go to its file store, you just write paths. Pass the rev from `workflow_files_list` as baseRev; a stale value is rejected with 409, so re-read and retry. Editing a published or already-executed version forks a new one instead of rewriting it. Each write carries `contentBase64`: base64-encode the file content, plain text is rejected with 400.",
3807
+ method: "PATCH",
3808
+ path: "/agents/v2/workflows/{workflowId}/head/files",
3809
+ args: [
3810
+ {
3811
+ name: "workflowId",
3812
+ flag: "workflow-id",
3813
+ type: "string",
3814
+ required: true
3815
+ }
3816
+ ],
3817
+ query: [],
3818
+ binary: false,
3819
+ readOnly: false,
3820
+ destructive: true,
3821
+ body: {
3822
+ required: true,
3823
+ contentType: "application/json",
3824
+ fields: [
3825
+ {
3826
+ name: "baseRev",
3827
+ flag: "base-rev",
3828
+ type: "integer",
3829
+ required: true,
3830
+ description: "The revision this edit is based on. Agent-file-only patches overlay the current head even when this is stale. A structural-file patch is rejected with 409 when it does not match the stored head revision."
3831
+ },
3832
+ {
3833
+ name: "writes",
3834
+ flag: "writes",
3835
+ type: "json",
3836
+ required: true,
3837
+ description: "Files to upsert."
3838
+ },
3839
+ {
3840
+ name: "deletes",
3841
+ flag: "deletes",
3842
+ type: "string[]",
3843
+ required: true
3844
+ }
3845
+ ],
3846
+ files: [],
3847
+ credentialOrganization: false
3848
+ },
3849
+ files: "push"
3850
+ },
3837
3851
  {
3838
3852
  command: "workflow rename",
3839
3853
  tool: "workflow_rename",
@@ -4236,70 +4250,8 @@ var COMMANDS = [
4236
4250
  ],
4237
4251
  files: [],
4238
4252
  credentialOrganization: false
4239
- }
4240
- },
4241
- {
4242
- command: "workflow version file list",
4243
- tool: "workflow_version_files_list",
4244
- title: "List workflow version files",
4245
- description: "List a version's files, like `workflow_files_list` does for the editable head. Versions are immutable, so this is read-only.",
4246
- method: "GET",
4247
- path: "/agents/v2/workflows/{workflowId}/versions/{versionId}/files",
4248
- args: [
4249
- {
4250
- name: "workflowId",
4251
- flag: "workflow-id",
4252
- type: "string",
4253
- required: true
4254
- },
4255
- {
4256
- name: "versionId",
4257
- flag: "version-id",
4258
- type: "string",
4259
- required: true
4260
- }
4261
- ],
4262
- query: [
4263
- {
4264
- name: "contents",
4265
- flag: "contents",
4266
- type: "string",
4267
- required: false,
4268
- description: "Which contents come inline: none (a pure manifest), structural (the default), or all (structural plus file bytes within the response budget).",
4269
- enum: ["none", "structural", "all"]
4270
- },
4271
- {
4272
- name: "paths",
4273
- flag: "paths",
4274
- type: "string[]",
4275
- required: false,
4276
- description: "Return only the files at these exact tree paths. A path that names nothing yields no entry. The inline budget applies to the filtered set, so a narrow read can inline files a whole-tree read would have to skip."
4277
- },
4278
- {
4279
- name: "variants",
4280
- flag: "variants",
4281
- type: "string",
4282
- required: false,
4283
- description: `Narrow which variants/<key>/ files the tree carries: "none" strips every variant, a comma-separated list keeps only those variant keys. Files outside variants/ are always kept, and the response's variantKeys still names every variant. Ignored unless the workflow has variant mode enabled; absent keeps every variant.`
4284
- },
4285
- {
4286
- name: "maxFileBytes",
4287
- flag: "max-file-bytes",
4288
- type: "integer",
4289
- required: false,
4290
- description: "Per-file ceiling, in bytes, for inlined file contents. Clamped to the server maximum. Files above the ceiling stay references."
4291
- },
4292
- {
4293
- name: "maxTotalBytes",
4294
- flag: "max-total-bytes",
4295
- type: "integer",
4296
- required: false,
4297
- description: "Ceiling, in bytes, on the total inlined file contents in one response. Clamped to the server maximum. Files are admitted in path order until the ceiling is reached; the rest stay references."
4298
- }
4299
- ],
4300
- binary: false,
4301
- readOnly: true,
4302
- destructive: false
4253
+ },
4254
+ capability: "run_execution"
4303
4255
  },
4304
4256
  {
4305
4257
  command: "workflow version file url",
@@ -4455,7 +4407,72 @@ var COMMANDS = [
4455
4407
  query: [],
4456
4408
  binary: false,
4457
4409
  readOnly: false,
4458
- destructive: false
4410
+ destructive: false,
4411
+ capability: "publish"
4412
+ },
4413
+ {
4414
+ command: "workflow version pull",
4415
+ tool: "workflow_version_files_list",
4416
+ title: "List workflow version files",
4417
+ description: "List a version's files, like `workflow_files_list` does for the editable head. Versions are immutable, so this is read-only.",
4418
+ method: "GET",
4419
+ path: "/agents/v2/workflows/{workflowId}/versions/{versionId}/files",
4420
+ args: [
4421
+ {
4422
+ name: "workflowId",
4423
+ flag: "workflow-id",
4424
+ type: "string",
4425
+ required: true
4426
+ },
4427
+ {
4428
+ name: "versionId",
4429
+ flag: "version-id",
4430
+ type: "string",
4431
+ required: true
4432
+ }
4433
+ ],
4434
+ query: [
4435
+ {
4436
+ name: "contents",
4437
+ flag: "contents",
4438
+ type: "string",
4439
+ required: false,
4440
+ description: "Which contents come inline: none (a pure manifest), structural (the default), or all (structural plus file bytes within the response budget).",
4441
+ enum: ["none", "structural", "all"]
4442
+ },
4443
+ {
4444
+ name: "paths",
4445
+ flag: "paths",
4446
+ type: "string[]",
4447
+ required: false,
4448
+ description: "Return only the files at these exact tree paths. A path that names nothing yields no entry. The inline budget applies to the filtered set, so a narrow read can inline files a whole-tree read would have to skip."
4449
+ },
4450
+ {
4451
+ name: "variants",
4452
+ flag: "variants",
4453
+ type: "string",
4454
+ required: false,
4455
+ description: `Narrow which variants/<key>/ files the tree carries: "none" strips every variant, a comma-separated list keeps only those variant keys. Files outside variants/ are always kept, and the response's variantKeys still names every variant. Ignored unless the workflow has variant mode enabled; absent keeps every variant.`
4456
+ },
4457
+ {
4458
+ name: "maxFileBytes",
4459
+ flag: "max-file-bytes",
4460
+ type: "integer",
4461
+ required: false,
4462
+ description: "Per-file ceiling, in bytes, for inlined file contents. Clamped to the server maximum. Files above the ceiling stay references."
4463
+ },
4464
+ {
4465
+ name: "maxTotalBytes",
4466
+ flag: "max-total-bytes",
4467
+ type: "integer",
4468
+ required: false,
4469
+ description: "Ceiling, in bytes, on the total inlined file contents in one response. Clamped to the server maximum. Files are admitted in path order until the ceiling is reached; the rest stay references."
4470
+ }
4471
+ ],
4472
+ binary: false,
4473
+ readOnly: true,
4474
+ destructive: false,
4475
+ files: "pull"
4459
4476
  },
4460
4477
  {
4461
4478
  command: "workflow version validate",
@@ -4664,7 +4681,7 @@ ${style("dim", rest.map(([k, v]) => `${k}: ${text(v)}`).join(" "), options)}` :
4664
4681
  }
4665
4682
 
4666
4683
  // src/program.ts
4667
- import { Command, CommanderError } from "commander";
4684
+ import { Command, CommanderError, Option as Flag } from "commander";
4668
4685
 
4669
4686
  // src/reference.ts
4670
4687
  var BUILTINS = [
@@ -4691,7 +4708,17 @@ var BUILTINS = [
4691
4708
  usage: "asteroid use [org]",
4692
4709
  arg: "org",
4693
4710
  description: "Make an organization, by id or name, the default for later commands. Without one, pick from a list.",
4694
- args: 1
4711
+ args: 1,
4712
+ hiddenOptions: [
4713
+ [
4714
+ "--reason <why>",
4715
+ "Staff only. Why you need access to an organization you are not a member of, such as a ticket id. It is audited. Asked for in a terminal."
4716
+ ],
4717
+ [
4718
+ "--exit",
4719
+ "Staff only. Revoke every staff token now and go back to your own organization."
4720
+ ]
4721
+ ]
4695
4722
  },
4696
4723
  {
4697
4724
  command: "complete",
@@ -4777,6 +4804,16 @@ function leafOptions(leaf, command) {
4777
4804
  for (const file of command.body?.files ?? []) {
4778
4805
  leaf.option(`--${file} <path>`, "File to upload. Repeatable.", collect);
4779
4806
  }
4807
+ if (command.files === "pull")
4808
+ leaf.option(
4809
+ "--out <dir>",
4810
+ "Write here instead of ./<workflow-name>/head (a version: ./<workflow-name>/.versions/...). --json prints the file listing instead."
4811
+ );
4812
+ if (command.files === "push")
4813
+ leaf.option(
4814
+ "--from <dir>",
4815
+ "The pulled head folder to push. Defaults to the current folder."
4816
+ );
4780
4817
  }
4781
4818
  function toParsed(leaf, positionals, dotted) {
4782
4819
  const globals = leaf.optsWithGlobals();
@@ -4810,6 +4847,8 @@ function defineCommands(root, options, dotted = /* @__PURE__ */ new Map(), onInv
4810
4847
  for (const builtin of options.builtins ?? []) {
4811
4848
  const leaf = root.command(builtin.command).description(builtin.description);
4812
4849
  if (builtin.arg) leaf.argument(`[${builtin.arg}]`);
4850
+ for (const [flags, text2] of builtin.hiddenOptions ?? [])
4851
+ leaf.addOption(new Flag(flags, text2).hideHelp());
4813
4852
  leaf.action((...args) => {
4814
4853
  const cmd = args.at(-1);
4815
4854
  onInvocation({
@@ -5125,11 +5164,6 @@ function rejectUnknown(flags) {
5125
5164
  function option(name, type, description, extra = {}) {
5126
5165
  return { name, flag: name, type, required: false, description, ...extra };
5127
5166
  }
5128
- var DIR = option(
5129
- "dir",
5130
- "string",
5131
- "Write here instead of `<workflow-name>/...` under the current directory."
5132
- );
5133
5167
  var local = {
5134
5168
  method: "GET",
5135
5169
  path: "",
@@ -5138,46 +5172,6 @@ var local = {
5138
5172
  local: true
5139
5173
  };
5140
5174
  var SYNC_COMMANDS = [
5141
- {
5142
- ...local,
5143
- command: "workflow pull",
5144
- tool: "workflow_pull",
5145
- title: "Pull a workflow to disk",
5146
- description: "Write a workflow's editable head to `<workflow-name>/head/` as files: settings, per-node instructions and scripts, and the workflow's own files. Pass --version-id for a pinned version, written read-only to `<workflow-name>/.versions/`. Edit the head files, then run `asteroid workflow push` from that folder.",
5147
- readOnly: true,
5148
- args: [
5149
- {
5150
- name: "workflowId",
5151
- flag: "workflow-id",
5152
- type: "string",
5153
- required: true
5154
- }
5155
- ],
5156
- query: [
5157
- option(
5158
- "version-id",
5159
- "string",
5160
- "A workflow version ID. Omit for the editable head."
5161
- ),
5162
- DIR
5163
- ]
5164
- },
5165
- {
5166
- ...local,
5167
- command: "workflow push",
5168
- tool: "workflow_push",
5169
- title: "Push local edits to a workflow head",
5170
- description: "Send the edits in a pulled head folder to the workflow's editable head: changed and new files are written, removed files are deleted. Fails if the head changed since the pull; pull again and reapply. Push does not publish.",
5171
- readOnly: false,
5172
- args: [],
5173
- query: [
5174
- option(
5175
- "dir",
5176
- "string",
5177
- "The pulled head folder. Defaults to the current directory."
5178
- )
5179
- ]
5180
- },
5181
5175
  {
5182
5176
  ...local,
5183
5177
  command: "execution pull",
@@ -5620,16 +5614,10 @@ function nodeFiles(cwd) {
5620
5614
 
5621
5615
  // src/sync/workflow-pull.ts
5622
5616
  var CONCURRENCY = 16;
5623
- function treeRequest(options) {
5624
- const base = `/agents/v2/workflows/${options.workflowId}`;
5625
- const path = options.versionId ? `${base}/versions/${options.versionId}/files` : `${base}/head/files`;
5626
- return get(path, { contents: "all" });
5627
- }
5628
- function pullDir(options, tree) {
5629
- if (options.dir) return options.dir;
5630
- const root = workflowRoot(options.root, tree.workflowName);
5631
- if (!options.versionId) return join(root, "head");
5632
- return join(root, ".versions", workflowDirName(tree.versionId, tree.version));
5617
+ function pullDir(root, tree, head) {
5618
+ const base = workflowRoot(root, tree.workflowName);
5619
+ if (head) return join(base, "head");
5620
+ return join(base, ".versions", workflowDirName(tree.versionId, tree.version));
5633
5621
  }
5634
5622
  async function agentFileBytes(sync, workflowId, tree, entry) {
5635
5623
  const path = `/agents/v2/workflows/${workflowId}/versions/${tree.versionId}/files/${entry.fileId}/signed-url`;
@@ -5679,8 +5667,14 @@ async function writeManifest(sync, dir, manifest) {
5679
5667
  await sync.files.write(join(dir, MANIFEST), Buffer.from(text2, "utf8"));
5680
5668
  }
5681
5669
  async function pullWorkflow(sync, options) {
5682
- const tree = await sync.remote.json(treeRequest(options));
5683
- const dir = pullDir(options, tree);
5670
+ const request = {
5671
+ ...options.request,
5672
+ query: new URLSearchParams(options.request.query)
5673
+ };
5674
+ request.query.set("contents", "all");
5675
+ const tree = await sync.remote.json(request);
5676
+ const head = request.path.endsWith("/head/files");
5677
+ const dir = options.dir ?? pullDir(options.root ?? "", tree, head);
5684
5678
  const { hashes, skipped, shared } = await writeEntries(
5685
5679
  sync,
5686
5680
  options,
@@ -5692,7 +5686,7 @@ async function pullWorkflow(sync, options) {
5692
5686
  await writeManifest(sync, dir, {
5693
5687
  workflowId: options.workflowId,
5694
5688
  versionId: tree.versionId,
5695
- head: !options.versionId,
5689
+ head,
5696
5690
  rev: tree.rev,
5697
5691
  files: hashes
5698
5692
  });
@@ -5704,6 +5698,7 @@ async function pullWorkflow(sync, options) {
5704
5698
  version: tree.version,
5705
5699
  rev: tree.rev,
5706
5700
  dir,
5701
+ head,
5707
5702
  written: Object.keys(hashes).length,
5708
5703
  shared,
5709
5704
  skipped
@@ -5718,7 +5713,8 @@ function formatWorkflowPull(result) {
5718
5713
  `Written to: ${result.dir}/`,
5719
5714
  files,
5720
5715
  result.skipped.length > 0 ? `Skipped:
5721
- ${result.skipped.map((s) => ` - ${s}`).join("\n")}` : null
5716
+ ${result.skipped.map((s) => ` - ${s}`).join("\n")}` : null,
5717
+ result.head ? `Edit the files, then run \`asteroid workflow push\` from ${result.dir}/.` : "This is a pinned version. It is read-only."
5722
5718
  ].filter(Boolean).join("\n");
5723
5719
  }
5724
5720
 
@@ -5800,8 +5796,13 @@ async function refresh(sync, dir, manifest, pushed) {
5800
5796
  await writeManifest(sync, dir, next);
5801
5797
  return next;
5802
5798
  }
5803
- async function pushWorkflow(sync, dir) {
5799
+ async function pushWorkflow(sync, dir, workflowId) {
5804
5800
  const manifest = await readManifest(sync, dir);
5801
+ if (workflowId !== void 0 && manifest.workflowId !== workflowId) {
5802
+ throw new UsageError(
5803
+ `${dir} holds workflow ${manifest.workflowId}, not ${workflowId}.`
5804
+ );
5805
+ }
5805
5806
  const { writes, deletes } = await localChanges(sync, dir, manifest);
5806
5807
  const result = {
5807
5808
  workflowId: manifest.workflowId,
@@ -5862,34 +5863,50 @@ function executionOptions(parsed) {
5862
5863
  dir: dir ? () => dir : void 0
5863
5864
  };
5864
5865
  }
5866
+ function withoutFlag(parsed, name) {
5867
+ const flags = new Map(parsed.flags);
5868
+ flags.delete(name);
5869
+ return { ...parsed, flags };
5870
+ }
5871
+ var PARTIAL = ["paths", "variants"];
5872
+ function rejectPartial(parsed) {
5873
+ const used = PARTIAL.filter((name) => parsed.flags.has(name));
5874
+ if (used.length === 0) return;
5875
+ throw new UsageError(
5876
+ `pull writes the whole tree, so it cannot take ${used.map((name) => `--${name}`).join(" or ")}. Add --json to list those files instead.`
5877
+ );
5878
+ }
5879
+ async function pull(command, parsed, sync, deps) {
5880
+ rejectPartial(parsed);
5881
+ const result = await pullWorkflow(sync, {
5882
+ workflowId: parsed.positionals[0],
5883
+ request: await buildRequest(command, withoutFlag(parsed, "out"), {
5884
+ io: deps.io
5885
+ }),
5886
+ dir: flag(parsed, "out"),
5887
+ root: deps.root ?? "",
5888
+ ...deps.workflowPull
5889
+ });
5890
+ return { result, text: formatWorkflowPull(result) };
5891
+ }
5865
5892
  async function execute(command, parsed, sync, deps) {
5866
- const root = deps.root ?? "";
5867
- switch (command.command) {
5868
- case "workflow pull": {
5869
- const result = await pullWorkflow(sync, {
5870
- workflowId: parsed.positionals[0],
5871
- versionId: flag(parsed, "version-id"),
5872
- dir: flag(parsed, "dir"),
5873
- root,
5874
- ...deps.workflowPull
5875
- });
5876
- return { result, text: formatWorkflowPull(result) };
5877
- }
5878
- case "workflow push": {
5879
- const result = await pushWorkflow(sync, flag(parsed, "dir") ?? ".");
5880
- return { result, text: formatWorkflowPush(result) };
5881
- }
5882
- case "execution pull": {
5883
- const result = await pullExecution(sync, {
5884
- ...executionOptions(parsed),
5885
- root,
5886
- ...deps.executionPull
5887
- });
5888
- return { result, text: formatExecutionPull(result) };
5889
- }
5890
- default:
5891
- throw new UsageError(`unknown command ${command.command}`);
5893
+ if (command.files === "pull") return pull(command, parsed, sync, deps);
5894
+ if (command.files === "push") {
5895
+ const result2 = await pushWorkflow(
5896
+ sync,
5897
+ flag(parsed, "from") ?? ".",
5898
+ parsed.positionals[0] || void 0
5899
+ );
5900
+ return { result: result2, text: formatWorkflowPush(result2) };
5892
5901
  }
5902
+ if (command.command !== "execution pull")
5903
+ throw new UsageError(`unknown command ${command.command}`);
5904
+ const result = await pullExecution(sync, {
5905
+ ...executionOptions(parsed),
5906
+ root: deps.root ?? "",
5907
+ ...deps.executionPull
5908
+ });
5909
+ return { result, text: formatExecutionPull(result) };
5893
5910
  }
5894
5911
  async function runSync(command, parsed, deps) {
5895
5912
  const sync = {