@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/asteroid.js +675 -328
- package/dist/core.js +287 -270
- package/package.json +1 -1
- package/src/account.ts +287 -10
- package/src/auth/credentials.ts +45 -7
- package/src/auth/store.ts +115 -7
- package/src/generated/commands.ts +199 -182
- package/src/program.ts +13 -1
- package/src/reference.ts +25 -1
- package/src/run.test.ts +289 -5
- package/src/run.ts +14 -4
- package/src/sync/commands.ts +0 -48
- package/src/sync/run.ts +60 -26
- package/src/sync/sync.test.ts +55 -16
- package/src/sync/workflow-pull.ts +21 -18
- package/src/sync/workflow-push.ts +6 -0
- package/src/types.ts +2 -0
- package/src/ui.ts +5 -0
package/dist/asteroid.js
CHANGED
|
@@ -124,7 +124,8 @@ var COMMANDS = [
|
|
|
124
124
|
query: [],
|
|
125
125
|
binary: false,
|
|
126
126
|
readOnly: false,
|
|
127
|
-
destructive: false
|
|
127
|
+
destructive: false,
|
|
128
|
+
capability: "run_execution"
|
|
128
129
|
},
|
|
129
130
|
{
|
|
130
131
|
command: "context",
|
|
@@ -1030,7 +1031,8 @@ var COMMANDS = [
|
|
|
1030
1031
|
],
|
|
1031
1032
|
files: [],
|
|
1032
1033
|
credentialOrganization: false
|
|
1033
|
-
}
|
|
1034
|
+
},
|
|
1035
|
+
capability: "run_execution"
|
|
1034
1036
|
},
|
|
1035
1037
|
{
|
|
1036
1038
|
command: "execution resume",
|
|
@@ -1050,7 +1052,8 @@ var COMMANDS = [
|
|
|
1050
1052
|
query: [],
|
|
1051
1053
|
binary: false,
|
|
1052
1054
|
readOnly: false,
|
|
1053
|
-
destructive: false
|
|
1055
|
+
destructive: false,
|
|
1056
|
+
capability: "run_execution"
|
|
1054
1057
|
},
|
|
1055
1058
|
{
|
|
1056
1059
|
command: "insight get",
|
|
@@ -1898,7 +1901,8 @@ var COMMANDS = [
|
|
|
1898
1901
|
],
|
|
1899
1902
|
files: [],
|
|
1900
1903
|
credentialOrganization: false
|
|
1901
|
-
}
|
|
1904
|
+
},
|
|
1905
|
+
capability: "run_execution"
|
|
1902
1906
|
},
|
|
1903
1907
|
{
|
|
1904
1908
|
command: "schedule delete",
|
|
@@ -1924,7 +1928,8 @@ var COMMANDS = [
|
|
|
1924
1928
|
query: [],
|
|
1925
1929
|
binary: false,
|
|
1926
1930
|
readOnly: false,
|
|
1927
|
-
destructive: true
|
|
1931
|
+
destructive: true,
|
|
1932
|
+
capability: "run_execution"
|
|
1928
1933
|
},
|
|
1929
1934
|
{
|
|
1930
1935
|
command: "schedule get",
|
|
@@ -2105,7 +2110,8 @@ var COMMANDS = [
|
|
|
2105
2110
|
],
|
|
2106
2111
|
files: [],
|
|
2107
2112
|
credentialOrganization: false
|
|
2108
|
-
}
|
|
2113
|
+
},
|
|
2114
|
+
capability: "run_execution"
|
|
2109
2115
|
},
|
|
2110
2116
|
{
|
|
2111
2117
|
command: "scheduled-execution cancel",
|
|
@@ -2125,7 +2131,8 @@ var COMMANDS = [
|
|
|
2125
2131
|
query: [],
|
|
2126
2132
|
binary: false,
|
|
2127
2133
|
readOnly: false,
|
|
2128
|
-
destructive: false
|
|
2134
|
+
destructive: false,
|
|
2135
|
+
capability: "run_execution"
|
|
2129
2136
|
},
|
|
2130
2137
|
{
|
|
2131
2138
|
command: "scheduled-execution create",
|
|
@@ -2193,7 +2200,8 @@ var COMMANDS = [
|
|
|
2193
2200
|
],
|
|
2194
2201
|
files: [],
|
|
2195
2202
|
credentialOrganization: false
|
|
2196
|
-
}
|
|
2203
|
+
},
|
|
2204
|
+
capability: "run_execution"
|
|
2197
2205
|
},
|
|
2198
2206
|
{
|
|
2199
2207
|
command: "scheduled-execution list",
|
|
@@ -2295,7 +2303,8 @@ var COMMANDS = [
|
|
|
2295
2303
|
],
|
|
2296
2304
|
files: [],
|
|
2297
2305
|
credentialOrganization: false
|
|
2298
|
-
}
|
|
2306
|
+
},
|
|
2307
|
+
capability: "run_execution"
|
|
2299
2308
|
},
|
|
2300
2309
|
{
|
|
2301
2310
|
command: "schema validate",
|
|
@@ -3435,7 +3444,8 @@ var COMMANDS = [
|
|
|
3435
3444
|
],
|
|
3436
3445
|
files: [],
|
|
3437
3446
|
credentialOrganization: false
|
|
3438
|
-
}
|
|
3447
|
+
},
|
|
3448
|
+
capability: "run_execution"
|
|
3439
3449
|
},
|
|
3440
3450
|
{
|
|
3441
3451
|
command: "workflow execute-sync",
|
|
@@ -3534,112 +3544,8 @@ var COMMANDS = [
|
|
|
3534
3544
|
],
|
|
3535
3545
|
files: [],
|
|
3536
3546
|
credentialOrganization: false
|
|
3537
|
-
}
|
|
3538
|
-
|
|
3539
|
-
{
|
|
3540
|
-
command: "workflow file list",
|
|
3541
|
-
tool: "workflow_files_list",
|
|
3542
|
-
title: "List workflow files",
|
|
3543
|
-
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.",
|
|
3544
|
-
method: "GET",
|
|
3545
|
-
path: "/agents/v2/workflows/{workflowId}/head/files",
|
|
3546
|
-
args: [
|
|
3547
|
-
{
|
|
3548
|
-
name: "workflowId",
|
|
3549
|
-
flag: "workflow-id",
|
|
3550
|
-
type: "string",
|
|
3551
|
-
required: true
|
|
3552
|
-
}
|
|
3553
|
-
],
|
|
3554
|
-
query: [
|
|
3555
|
-
{
|
|
3556
|
-
name: "contents",
|
|
3557
|
-
flag: "contents",
|
|
3558
|
-
type: "string",
|
|
3559
|
-
required: false,
|
|
3560
|
-
description: "Which contents come inline: none (a pure manifest), structural (the default), or all (structural plus file bytes within the response budget).",
|
|
3561
|
-
enum: ["none", "structural", "all"]
|
|
3562
|
-
},
|
|
3563
|
-
{
|
|
3564
|
-
name: "paths",
|
|
3565
|
-
flag: "paths",
|
|
3566
|
-
type: "string[]",
|
|
3567
|
-
required: false,
|
|
3568
|
-
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."
|
|
3569
|
-
},
|
|
3570
|
-
{
|
|
3571
|
-
name: "variants",
|
|
3572
|
-
flag: "variants",
|
|
3573
|
-
type: "string",
|
|
3574
|
-
required: false,
|
|
3575
|
-
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.`
|
|
3576
|
-
},
|
|
3577
|
-
{
|
|
3578
|
-
name: "maxFileBytes",
|
|
3579
|
-
flag: "max-file-bytes",
|
|
3580
|
-
type: "integer",
|
|
3581
|
-
required: false,
|
|
3582
|
-
description: "Per-file ceiling, in bytes, for inlined file contents. Clamped to the server maximum. Files above the ceiling stay references."
|
|
3583
|
-
},
|
|
3584
|
-
{
|
|
3585
|
-
name: "maxTotalBytes",
|
|
3586
|
-
flag: "max-total-bytes",
|
|
3587
|
-
type: "integer",
|
|
3588
|
-
required: false,
|
|
3589
|
-
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."
|
|
3590
|
-
}
|
|
3591
|
-
],
|
|
3592
|
-
binary: false,
|
|
3593
|
-
readOnly: true,
|
|
3594
|
-
destructive: false
|
|
3595
|
-
},
|
|
3596
|
-
{
|
|
3597
|
-
command: "workflow file patch",
|
|
3598
|
-
tool: "workflow_files_patch",
|
|
3599
|
-
title: "Write workflow files",
|
|
3600
|
-
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.",
|
|
3601
|
-
method: "PATCH",
|
|
3602
|
-
path: "/agents/v2/workflows/{workflowId}/head/files",
|
|
3603
|
-
args: [
|
|
3604
|
-
{
|
|
3605
|
-
name: "workflowId",
|
|
3606
|
-
flag: "workflow-id",
|
|
3607
|
-
type: "string",
|
|
3608
|
-
required: true
|
|
3609
|
-
}
|
|
3610
|
-
],
|
|
3611
|
-
query: [],
|
|
3612
|
-
binary: false,
|
|
3613
|
-
readOnly: false,
|
|
3614
|
-
destructive: true,
|
|
3615
|
-
body: {
|
|
3616
|
-
required: true,
|
|
3617
|
-
contentType: "application/json",
|
|
3618
|
-
fields: [
|
|
3619
|
-
{
|
|
3620
|
-
name: "baseRev",
|
|
3621
|
-
flag: "base-rev",
|
|
3622
|
-
type: "integer",
|
|
3623
|
-
required: true,
|
|
3624
|
-
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."
|
|
3625
|
-
},
|
|
3626
|
-
{
|
|
3627
|
-
name: "writes",
|
|
3628
|
-
flag: "writes",
|
|
3629
|
-
type: "json",
|
|
3630
|
-
required: true,
|
|
3631
|
-
description: "Files to upsert."
|
|
3632
|
-
},
|
|
3633
|
-
{
|
|
3634
|
-
name: "deletes",
|
|
3635
|
-
flag: "deletes",
|
|
3636
|
-
type: "string[]",
|
|
3637
|
-
required: true
|
|
3638
|
-
}
|
|
3639
|
-
],
|
|
3640
|
-
files: [],
|
|
3641
|
-
credentialOrganization: false
|
|
3642
|
-
}
|
|
3547
|
+
},
|
|
3548
|
+
capability: "run_execution"
|
|
3643
3549
|
},
|
|
3644
3550
|
{
|
|
3645
3551
|
command: "workflow get",
|
|
@@ -3687,7 +3593,8 @@ var COMMANDS = [
|
|
|
3687
3593
|
query: [],
|
|
3688
3594
|
binary: false,
|
|
3689
3595
|
readOnly: false,
|
|
3690
|
-
destructive: false
|
|
3596
|
+
destructive: false,
|
|
3597
|
+
capability: "publish"
|
|
3691
3598
|
},
|
|
3692
3599
|
{
|
|
3693
3600
|
command: "workflow head revert",
|
|
@@ -3813,6 +3720,113 @@ var COMMANDS = [
|
|
|
3813
3720
|
readOnly: true,
|
|
3814
3721
|
destructive: false
|
|
3815
3722
|
},
|
|
3723
|
+
{
|
|
3724
|
+
command: "workflow pull",
|
|
3725
|
+
tool: "workflow_files_list",
|
|
3726
|
+
title: "List workflow files",
|
|
3727
|
+
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.",
|
|
3728
|
+
method: "GET",
|
|
3729
|
+
path: "/agents/v2/workflows/{workflowId}/head/files",
|
|
3730
|
+
args: [
|
|
3731
|
+
{
|
|
3732
|
+
name: "workflowId",
|
|
3733
|
+
flag: "workflow-id",
|
|
3734
|
+
type: "string",
|
|
3735
|
+
required: true
|
|
3736
|
+
}
|
|
3737
|
+
],
|
|
3738
|
+
query: [
|
|
3739
|
+
{
|
|
3740
|
+
name: "contents",
|
|
3741
|
+
flag: "contents",
|
|
3742
|
+
type: "string",
|
|
3743
|
+
required: false,
|
|
3744
|
+
description: "Which contents come inline: none (a pure manifest), structural (the default), or all (structural plus file bytes within the response budget).",
|
|
3745
|
+
enum: ["none", "structural", "all"]
|
|
3746
|
+
},
|
|
3747
|
+
{
|
|
3748
|
+
name: "paths",
|
|
3749
|
+
flag: "paths",
|
|
3750
|
+
type: "string[]",
|
|
3751
|
+
required: false,
|
|
3752
|
+
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."
|
|
3753
|
+
},
|
|
3754
|
+
{
|
|
3755
|
+
name: "variants",
|
|
3756
|
+
flag: "variants",
|
|
3757
|
+
type: "string",
|
|
3758
|
+
required: false,
|
|
3759
|
+
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.`
|
|
3760
|
+
},
|
|
3761
|
+
{
|
|
3762
|
+
name: "maxFileBytes",
|
|
3763
|
+
flag: "max-file-bytes",
|
|
3764
|
+
type: "integer",
|
|
3765
|
+
required: false,
|
|
3766
|
+
description: "Per-file ceiling, in bytes, for inlined file contents. Clamped to the server maximum. Files above the ceiling stay references."
|
|
3767
|
+
},
|
|
3768
|
+
{
|
|
3769
|
+
name: "maxTotalBytes",
|
|
3770
|
+
flag: "max-total-bytes",
|
|
3771
|
+
type: "integer",
|
|
3772
|
+
required: false,
|
|
3773
|
+
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."
|
|
3774
|
+
}
|
|
3775
|
+
],
|
|
3776
|
+
binary: false,
|
|
3777
|
+
readOnly: true,
|
|
3778
|
+
destructive: false,
|
|
3779
|
+
files: "pull"
|
|
3780
|
+
},
|
|
3781
|
+
{
|
|
3782
|
+
command: "workflow push",
|
|
3783
|
+
tool: "workflow_files_patch",
|
|
3784
|
+
title: "Write workflow files",
|
|
3785
|
+
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.",
|
|
3786
|
+
method: "PATCH",
|
|
3787
|
+
path: "/agents/v2/workflows/{workflowId}/head/files",
|
|
3788
|
+
args: [
|
|
3789
|
+
{
|
|
3790
|
+
name: "workflowId",
|
|
3791
|
+
flag: "workflow-id",
|
|
3792
|
+
type: "string",
|
|
3793
|
+
required: true
|
|
3794
|
+
}
|
|
3795
|
+
],
|
|
3796
|
+
query: [],
|
|
3797
|
+
binary: false,
|
|
3798
|
+
readOnly: false,
|
|
3799
|
+
destructive: true,
|
|
3800
|
+
body: {
|
|
3801
|
+
required: true,
|
|
3802
|
+
contentType: "application/json",
|
|
3803
|
+
fields: [
|
|
3804
|
+
{
|
|
3805
|
+
name: "baseRev",
|
|
3806
|
+
flag: "base-rev",
|
|
3807
|
+
type: "integer",
|
|
3808
|
+
required: true,
|
|
3809
|
+
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."
|
|
3810
|
+
},
|
|
3811
|
+
{
|
|
3812
|
+
name: "writes",
|
|
3813
|
+
flag: "writes",
|
|
3814
|
+
type: "json",
|
|
3815
|
+
required: true,
|
|
3816
|
+
description: "Files to upsert."
|
|
3817
|
+
},
|
|
3818
|
+
{
|
|
3819
|
+
name: "deletes",
|
|
3820
|
+
flag: "deletes",
|
|
3821
|
+
type: "string[]",
|
|
3822
|
+
required: true
|
|
3823
|
+
}
|
|
3824
|
+
],
|
|
3825
|
+
files: [],
|
|
3826
|
+
credentialOrganization: false
|
|
3827
|
+
},
|
|
3828
|
+
files: "push"
|
|
3829
|
+
},
|
|
3816
3830
|
{
|
|
3817
3831
|
command: "workflow rename",
|
|
3818
3832
|
tool: "workflow_rename",
|
|
@@ -4215,78 +4229,16 @@ var COMMANDS = [
|
|
|
4215
4229
|
],
|
|
4216
4230
|
files: [],
|
|
4217
4231
|
credentialOrganization: false
|
|
4218
|
-
}
|
|
4232
|
+
},
|
|
4233
|
+
capability: "run_execution"
|
|
4219
4234
|
},
|
|
4220
4235
|
{
|
|
4221
|
-
command: "workflow version file
|
|
4222
|
-
tool: "
|
|
4223
|
-
title: "
|
|
4224
|
-
description: "
|
|
4236
|
+
command: "workflow version file url",
|
|
4237
|
+
tool: "workflow_version_file_signed_url",
|
|
4238
|
+
title: "Get workflow version file signed URL",
|
|
4239
|
+
description: "Get a short-lived signed URL for downloading one of a workflow version's files directly from storage. Prefer this over a redirect-based download when reading a file's contents.",
|
|
4225
4240
|
method: "GET",
|
|
4226
|
-
path: "/agents/v2/workflows/{workflowId}/versions/{versionId}/files",
|
|
4227
|
-
args: [
|
|
4228
|
-
{
|
|
4229
|
-
name: "workflowId",
|
|
4230
|
-
flag: "workflow-id",
|
|
4231
|
-
type: "string",
|
|
4232
|
-
required: true
|
|
4233
|
-
},
|
|
4234
|
-
{
|
|
4235
|
-
name: "versionId",
|
|
4236
|
-
flag: "version-id",
|
|
4237
|
-
type: "string",
|
|
4238
|
-
required: true
|
|
4239
|
-
}
|
|
4240
|
-
],
|
|
4241
|
-
query: [
|
|
4242
|
-
{
|
|
4243
|
-
name: "contents",
|
|
4244
|
-
flag: "contents",
|
|
4245
|
-
type: "string",
|
|
4246
|
-
required: false,
|
|
4247
|
-
description: "Which contents come inline: none (a pure manifest), structural (the default), or all (structural plus file bytes within the response budget).",
|
|
4248
|
-
enum: ["none", "structural", "all"]
|
|
4249
|
-
},
|
|
4250
|
-
{
|
|
4251
|
-
name: "paths",
|
|
4252
|
-
flag: "paths",
|
|
4253
|
-
type: "string[]",
|
|
4254
|
-
required: false,
|
|
4255
|
-
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."
|
|
4256
|
-
},
|
|
4257
|
-
{
|
|
4258
|
-
name: "variants",
|
|
4259
|
-
flag: "variants",
|
|
4260
|
-
type: "string",
|
|
4261
|
-
required: false,
|
|
4262
|
-
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.`
|
|
4263
|
-
},
|
|
4264
|
-
{
|
|
4265
|
-
name: "maxFileBytes",
|
|
4266
|
-
flag: "max-file-bytes",
|
|
4267
|
-
type: "integer",
|
|
4268
|
-
required: false,
|
|
4269
|
-
description: "Per-file ceiling, in bytes, for inlined file contents. Clamped to the server maximum. Files above the ceiling stay references."
|
|
4270
|
-
},
|
|
4271
|
-
{
|
|
4272
|
-
name: "maxTotalBytes",
|
|
4273
|
-
flag: "max-total-bytes",
|
|
4274
|
-
type: "integer",
|
|
4275
|
-
required: false,
|
|
4276
|
-
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."
|
|
4277
|
-
}
|
|
4278
|
-
],
|
|
4279
|
-
binary: false,
|
|
4280
|
-
readOnly: true,
|
|
4281
|
-
destructive: false
|
|
4282
|
-
},
|
|
4283
|
-
{
|
|
4284
|
-
command: "workflow version file url",
|
|
4285
|
-
tool: "workflow_version_file_signed_url",
|
|
4286
|
-
title: "Get workflow version file signed URL",
|
|
4287
|
-
description: "Get a short-lived signed URL for downloading one of a workflow version's files directly from storage. Prefer this over a redirect-based download when reading a file's contents.",
|
|
4288
|
-
method: "GET",
|
|
4289
|
-
path: "/agents/v2/workflows/{workflowId}/versions/{versionId}/files/{fileId}/signed-url",
|
|
4241
|
+
path: "/agents/v2/workflows/{workflowId}/versions/{versionId}/files/{fileId}/signed-url",
|
|
4290
4242
|
args: [
|
|
4291
4243
|
{
|
|
4292
4244
|
name: "workflowId",
|
|
@@ -4434,7 +4386,72 @@ var COMMANDS = [
|
|
|
4434
4386
|
query: [],
|
|
4435
4387
|
binary: false,
|
|
4436
4388
|
readOnly: false,
|
|
4437
|
-
destructive: false
|
|
4389
|
+
destructive: false,
|
|
4390
|
+
capability: "publish"
|
|
4391
|
+
},
|
|
4392
|
+
{
|
|
4393
|
+
command: "workflow version pull",
|
|
4394
|
+
tool: "workflow_version_files_list",
|
|
4395
|
+
title: "List workflow version files",
|
|
4396
|
+
description: "List a version's files, like `workflow_files_list` does for the editable head. Versions are immutable, so this is read-only.",
|
|
4397
|
+
method: "GET",
|
|
4398
|
+
path: "/agents/v2/workflows/{workflowId}/versions/{versionId}/files",
|
|
4399
|
+
args: [
|
|
4400
|
+
{
|
|
4401
|
+
name: "workflowId",
|
|
4402
|
+
flag: "workflow-id",
|
|
4403
|
+
type: "string",
|
|
4404
|
+
required: true
|
|
4405
|
+
},
|
|
4406
|
+
{
|
|
4407
|
+
name: "versionId",
|
|
4408
|
+
flag: "version-id",
|
|
4409
|
+
type: "string",
|
|
4410
|
+
required: true
|
|
4411
|
+
}
|
|
4412
|
+
],
|
|
4413
|
+
query: [
|
|
4414
|
+
{
|
|
4415
|
+
name: "contents",
|
|
4416
|
+
flag: "contents",
|
|
4417
|
+
type: "string",
|
|
4418
|
+
required: false,
|
|
4419
|
+
description: "Which contents come inline: none (a pure manifest), structural (the default), or all (structural plus file bytes within the response budget).",
|
|
4420
|
+
enum: ["none", "structural", "all"]
|
|
4421
|
+
},
|
|
4422
|
+
{
|
|
4423
|
+
name: "paths",
|
|
4424
|
+
flag: "paths",
|
|
4425
|
+
type: "string[]",
|
|
4426
|
+
required: false,
|
|
4427
|
+
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."
|
|
4428
|
+
},
|
|
4429
|
+
{
|
|
4430
|
+
name: "variants",
|
|
4431
|
+
flag: "variants",
|
|
4432
|
+
type: "string",
|
|
4433
|
+
required: false,
|
|
4434
|
+
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.`
|
|
4435
|
+
},
|
|
4436
|
+
{
|
|
4437
|
+
name: "maxFileBytes",
|
|
4438
|
+
flag: "max-file-bytes",
|
|
4439
|
+
type: "integer",
|
|
4440
|
+
required: false,
|
|
4441
|
+
description: "Per-file ceiling, in bytes, for inlined file contents. Clamped to the server maximum. Files above the ceiling stay references."
|
|
4442
|
+
},
|
|
4443
|
+
{
|
|
4444
|
+
name: "maxTotalBytes",
|
|
4445
|
+
flag: "max-total-bytes",
|
|
4446
|
+
type: "integer",
|
|
4447
|
+
required: false,
|
|
4448
|
+
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."
|
|
4449
|
+
}
|
|
4450
|
+
],
|
|
4451
|
+
binary: false,
|
|
4452
|
+
readOnly: true,
|
|
4453
|
+
destructive: false,
|
|
4454
|
+
files: "pull"
|
|
4438
4455
|
},
|
|
4439
4456
|
{
|
|
4440
4457
|
command: "workflow version validate",
|
|
@@ -4510,11 +4527,6 @@ var COMMANDS = [
|
|
|
4510
4527
|
function option(name, type, description, extra = {}) {
|
|
4511
4528
|
return { name, flag: name, type, required: false, description, ...extra };
|
|
4512
4529
|
}
|
|
4513
|
-
var DIR = option(
|
|
4514
|
-
"dir",
|
|
4515
|
-
"string",
|
|
4516
|
-
"Write here instead of `<workflow-name>/...` under the current directory."
|
|
4517
|
-
);
|
|
4518
4530
|
var local = {
|
|
4519
4531
|
method: "GET",
|
|
4520
4532
|
path: "",
|
|
@@ -4523,46 +4535,6 @@ var local = {
|
|
|
4523
4535
|
local: true
|
|
4524
4536
|
};
|
|
4525
4537
|
var SYNC_COMMANDS = [
|
|
4526
|
-
{
|
|
4527
|
-
...local,
|
|
4528
|
-
command: "workflow pull",
|
|
4529
|
-
tool: "workflow_pull",
|
|
4530
|
-
title: "Pull a workflow to disk",
|
|
4531
|
-
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.",
|
|
4532
|
-
readOnly: true,
|
|
4533
|
-
args: [
|
|
4534
|
-
{
|
|
4535
|
-
name: "workflowId",
|
|
4536
|
-
flag: "workflow-id",
|
|
4537
|
-
type: "string",
|
|
4538
|
-
required: true
|
|
4539
|
-
}
|
|
4540
|
-
],
|
|
4541
|
-
query: [
|
|
4542
|
-
option(
|
|
4543
|
-
"version-id",
|
|
4544
|
-
"string",
|
|
4545
|
-
"A workflow version ID. Omit for the editable head."
|
|
4546
|
-
),
|
|
4547
|
-
DIR
|
|
4548
|
-
]
|
|
4549
|
-
},
|
|
4550
|
-
{
|
|
4551
|
-
...local,
|
|
4552
|
-
command: "workflow push",
|
|
4553
|
-
tool: "workflow_push",
|
|
4554
|
-
title: "Push local edits to a workflow head",
|
|
4555
|
-
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.",
|
|
4556
|
-
readOnly: false,
|
|
4557
|
-
args: [],
|
|
4558
|
-
query: [
|
|
4559
|
-
option(
|
|
4560
|
-
"dir",
|
|
4561
|
-
"string",
|
|
4562
|
-
"The pulled head folder. Defaults to the current directory."
|
|
4563
|
-
)
|
|
4564
|
-
]
|
|
4565
|
-
},
|
|
4566
4538
|
{
|
|
4567
4539
|
...local,
|
|
4568
4540
|
command: "execution pull",
|
|
@@ -4644,8 +4616,8 @@ var HIDDEN = /^agent[A-Z]|Url$/;
|
|
|
4644
4616
|
var MAX_COLUMNS = 6;
|
|
4645
4617
|
var MAX_CELL = 60;
|
|
4646
4618
|
var CONTROL = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)?|\x1b[@-_]?|[\x00-\x08\x0b-\x1f\x7f-\x9f]/g;
|
|
4647
|
-
function clean(
|
|
4648
|
-
return
|
|
4619
|
+
function clean(text3) {
|
|
4620
|
+
return text3.replace(CONTROL, "");
|
|
4649
4621
|
}
|
|
4650
4622
|
function isObject(value2) {
|
|
4651
4623
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
@@ -4653,8 +4625,8 @@ function isObject(value2) {
|
|
|
4653
4625
|
function isScalar(value2) {
|
|
4654
4626
|
return value2 === null || ["string", "number", "boolean"].includes(typeof value2);
|
|
4655
4627
|
}
|
|
4656
|
-
function indent(
|
|
4657
|
-
return
|
|
4628
|
+
function indent(text3) {
|
|
4629
|
+
return text3.split("\n").map((line2) => ` ${line2}`).join("\n");
|
|
4658
4630
|
}
|
|
4659
4631
|
function text(value2) {
|
|
4660
4632
|
return clean(isScalar(value2) ? String(value2) : JSON.stringify(value2));
|
|
@@ -4835,7 +4807,7 @@ function orgLabel(org) {
|
|
|
4835
4807
|
}
|
|
4836
4808
|
|
|
4837
4809
|
// src/program.ts
|
|
4838
|
-
import { Command, CommanderError } from "commander";
|
|
4810
|
+
import { Command, CommanderError, Option as Flag } from "commander";
|
|
4839
4811
|
|
|
4840
4812
|
// src/args.ts
|
|
4841
4813
|
var UsageError = class extends Error {
|
|
@@ -4866,7 +4838,17 @@ var BUILTINS = [
|
|
|
4866
4838
|
usage: "asteroid use [org]",
|
|
4867
4839
|
arg: "org",
|
|
4868
4840
|
description: "Make an organization, by id or name, the default for later commands. Without one, pick from a list.",
|
|
4869
|
-
args: 1
|
|
4841
|
+
args: 1,
|
|
4842
|
+
hiddenOptions: [
|
|
4843
|
+
[
|
|
4844
|
+
"--reason <why>",
|
|
4845
|
+
"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."
|
|
4846
|
+
],
|
|
4847
|
+
[
|
|
4848
|
+
"--exit",
|
|
4849
|
+
"Staff only. Revoke every staff token now and go back to your own organization."
|
|
4850
|
+
]
|
|
4851
|
+
]
|
|
4870
4852
|
},
|
|
4871
4853
|
{
|
|
4872
4854
|
command: "complete",
|
|
@@ -4903,8 +4885,8 @@ function collect(value2, previous) {
|
|
|
4903
4885
|
return [...previous ?? [], value2];
|
|
4904
4886
|
}
|
|
4905
4887
|
function optionText(option2) {
|
|
4906
|
-
const
|
|
4907
|
-
const parts = [
|
|
4888
|
+
const text3 = option2.description?.replace(/\s+/g, " ").trim() ?? "";
|
|
4889
|
+
const parts = [text3 && !/[.!?]$/.test(text3) ? `${text3}.` : text3];
|
|
4908
4890
|
if (option2.enum) parts.push(`One of: ${option2.enum.join(", ")}.`);
|
|
4909
4891
|
if (option2.type.endsWith("[]")) parts.push("Repeatable.");
|
|
4910
4892
|
if (option2.required) parts.push("Required.");
|
|
@@ -4952,6 +4934,16 @@ function leafOptions(leaf, command) {
|
|
|
4952
4934
|
for (const file of command.body?.files ?? []) {
|
|
4953
4935
|
leaf.option(`--${file} <path>`, "File to upload. Repeatable.", collect);
|
|
4954
4936
|
}
|
|
4937
|
+
if (command.files === "pull")
|
|
4938
|
+
leaf.option(
|
|
4939
|
+
"--out <dir>",
|
|
4940
|
+
"Write here instead of ./<workflow-name>/head (a version: ./<workflow-name>/.versions/...). --json prints the file listing instead."
|
|
4941
|
+
);
|
|
4942
|
+
if (command.files === "push")
|
|
4943
|
+
leaf.option(
|
|
4944
|
+
"--from <dir>",
|
|
4945
|
+
"The pulled head folder to push. Defaults to the current folder."
|
|
4946
|
+
);
|
|
4955
4947
|
}
|
|
4956
4948
|
function toParsed(leaf, positionals, dotted) {
|
|
4957
4949
|
const globals = leaf.optsWithGlobals();
|
|
@@ -4979,12 +4971,14 @@ function defineCommands(root, options, dotted = /* @__PURE__ */ new Map(), onInv
|
|
|
4979
4971
|
"after",
|
|
4980
4972
|
"\nRun `asteroid --version` to print the CLI version."
|
|
4981
4973
|
);
|
|
4982
|
-
for (const [flags,
|
|
4983
|
-
if (!flags.startsWith("-h")) root.option(flags,
|
|
4974
|
+
for (const [flags, text3] of GLOBAL_FLAGS) {
|
|
4975
|
+
if (!flags.startsWith("-h")) root.option(flags, text3);
|
|
4984
4976
|
}
|
|
4985
4977
|
for (const builtin of options.builtins ?? []) {
|
|
4986
4978
|
const leaf = root.command(builtin.command).description(builtin.description);
|
|
4987
4979
|
if (builtin.arg) leaf.argument(`[${builtin.arg}]`);
|
|
4980
|
+
for (const [flags, text3] of builtin.hiddenOptions ?? [])
|
|
4981
|
+
leaf.addOption(new Flag(flags, text3).hideHelp());
|
|
4988
4982
|
leaf.action((...args) => {
|
|
4989
4983
|
const cmd = args.at(-1);
|
|
4990
4984
|
onInvocation({
|
|
@@ -5073,8 +5067,8 @@ async function parseInvocation(argv2, options) {
|
|
|
5073
5067
|
}
|
|
5074
5068
|
const { argv: rest, dotted } = splitDotted(argv2);
|
|
5075
5069
|
const root = new Command("asteroid").exitOverride().helpCommand(false).configureOutput({
|
|
5076
|
-
writeOut: (
|
|
5077
|
-
writeErr: (
|
|
5070
|
+
writeOut: (text3) => stdout += text3,
|
|
5071
|
+
writeErr: (text3) => stderr += text3
|
|
5078
5072
|
});
|
|
5079
5073
|
defineCommands(root, options, dotted, (invocation) => {
|
|
5080
5074
|
result = invocation;
|
|
@@ -5228,11 +5222,12 @@ function tokenClaims(token) {
|
|
|
5228
5222
|
var SERVICE = "asteroid-cli";
|
|
5229
5223
|
var LOCK_STALE_MS = 3e4;
|
|
5230
5224
|
var ACCOUNT = "refresh-token";
|
|
5231
|
-
|
|
5225
|
+
var STAFF_ACCOUNT = "staff-tokens";
|
|
5226
|
+
async function keyring(account = ACCOUNT) {
|
|
5232
5227
|
if (process.env.ASTEROID_NO_KEYCHAIN) return void 0;
|
|
5233
5228
|
try {
|
|
5234
5229
|
const { Entry } = await import("@napi-rs/keyring");
|
|
5235
|
-
const entry = new Entry(SERVICE,
|
|
5230
|
+
const entry = new Entry(SERVICE, account);
|
|
5236
5231
|
entry.getPassword();
|
|
5237
5232
|
return {
|
|
5238
5233
|
get: () => entry.getPassword() ?? null,
|
|
@@ -5272,6 +5267,9 @@ var CredentialStore = class {
|
|
|
5272
5267
|
get tokenFile() {
|
|
5273
5268
|
return join2(this.env.configDir, "tokens.json");
|
|
5274
5269
|
}
|
|
5270
|
+
get staffFile() {
|
|
5271
|
+
return join2(this.env.configDir, "staff-tokens.json");
|
|
5272
|
+
}
|
|
5275
5273
|
async readLogin() {
|
|
5276
5274
|
const ring = await keyring();
|
|
5277
5275
|
const raw = ring ? tryGet(ring).value : null;
|
|
@@ -5300,22 +5298,95 @@ var CredentialStore = class {
|
|
|
5300
5298
|
const cleared = current.ok && (!current.value || trySet(ring, null));
|
|
5301
5299
|
rmSync(this.loginFile, { force: true });
|
|
5302
5300
|
rmSync(this.tokenFile, { force: true });
|
|
5303
|
-
|
|
5301
|
+
const staffCleared = await this.withStaffLock(
|
|
5302
|
+
() => this.writeStaffTokens({})
|
|
5303
|
+
);
|
|
5304
|
+
return staffCleared && cleared;
|
|
5305
|
+
}
|
|
5306
|
+
/** Every staff token held, expired ones included. */
|
|
5307
|
+
async staffTokens() {
|
|
5308
|
+
const ring = await keyring(STAFF_ACCOUNT);
|
|
5309
|
+
const raw = ring ? tryGet(ring).value : null;
|
|
5310
|
+
const tokens = raw ? JSON.parse(raw) : readJson(this.staffFile, {});
|
|
5311
|
+
return Object.values(tokens);
|
|
5312
|
+
}
|
|
5313
|
+
/** The staff token for an org, matched by WorkOS id, id or name. */
|
|
5314
|
+
async staffToken(ref) {
|
|
5315
|
+
const lower = ref.toLowerCase();
|
|
5316
|
+
return (await this.staffTokens()).find(
|
|
5317
|
+
({ org }) => org.workosId.toLowerCase() === lower || org.id?.toLowerCase() === lower || org.name?.toLowerCase() === lower
|
|
5318
|
+
);
|
|
5319
|
+
}
|
|
5320
|
+
/** Saves a staff token and drops expired ones. `replaces` is dropped too. */
|
|
5321
|
+
async saveStaffToken(token, replaces) {
|
|
5322
|
+
await this.withStaffLock(async () => {
|
|
5323
|
+
const tokens = {};
|
|
5324
|
+
for (const held of await this.staffTokens())
|
|
5325
|
+
if (held.expiresAt > Date.now() && held.grantId !== replaces)
|
|
5326
|
+
tokens[held.org.workosId] = held;
|
|
5327
|
+
tokens[token.org.workosId] = token;
|
|
5328
|
+
if (!await this.writeStaffTokens(tokens))
|
|
5329
|
+
throw new WorkosError(
|
|
5330
|
+
"keychain",
|
|
5331
|
+
"could not save the staff token in the OS keychain. Unlock it and try again."
|
|
5332
|
+
);
|
|
5333
|
+
});
|
|
5334
|
+
}
|
|
5335
|
+
async dropStaffToken(grantId) {
|
|
5336
|
+
await this.withStaffLock(async () => {
|
|
5337
|
+
const tokens = {};
|
|
5338
|
+
for (const held of await this.staffTokens())
|
|
5339
|
+
if (held.grantId !== grantId) tokens[held.org.workosId] = held;
|
|
5340
|
+
if (!await this.writeStaffTokens(tokens))
|
|
5341
|
+
throw new WorkosError(
|
|
5342
|
+
"keychain",
|
|
5343
|
+
"could not remove the staff token from the OS keychain. Unlock it and try again."
|
|
5344
|
+
);
|
|
5345
|
+
});
|
|
5346
|
+
}
|
|
5347
|
+
async writeStaffTokens(tokens) {
|
|
5348
|
+
const empty = Object.keys(tokens).length === 0;
|
|
5349
|
+
const ring = await keyring(STAFF_ACCOUNT);
|
|
5350
|
+
if (ring && trySet(ring, empty ? null : JSON.stringify(tokens))) {
|
|
5351
|
+
rmSync(this.staffFile, { force: true });
|
|
5352
|
+
return true;
|
|
5353
|
+
}
|
|
5354
|
+
const stale = ring ? tryGet(ring) : { ok: true, value: null };
|
|
5355
|
+
if (!stale.ok || stale.value && !trySet(ring, null)) return false;
|
|
5356
|
+
if (empty) rmSync(this.staffFile, { force: true });
|
|
5357
|
+
else writePrivateJson(this.staffFile, tokens);
|
|
5358
|
+
return true;
|
|
5304
5359
|
}
|
|
5305
5360
|
async withRefreshLock(work) {
|
|
5361
|
+
return this.withLock(
|
|
5362
|
+
"refresh",
|
|
5363
|
+
"another asteroid command is refreshing the sign-in.",
|
|
5364
|
+
work
|
|
5365
|
+
);
|
|
5366
|
+
}
|
|
5367
|
+
/** Two commands at once must not lose a grant they could still revoke. */
|
|
5368
|
+
async withStaffLock(work) {
|
|
5369
|
+
return this.withLock(
|
|
5370
|
+
"staff",
|
|
5371
|
+
"another asteroid command is saving staff access.",
|
|
5372
|
+
work
|
|
5373
|
+
);
|
|
5374
|
+
}
|
|
5375
|
+
async withLock(name, busy, work) {
|
|
5306
5376
|
mkdirSync2(this.env.configDir, { recursive: true, mode: 448 });
|
|
5307
|
-
const lockfilePath = join2(this.env.configDir,
|
|
5377
|
+
const lockfilePath = join2(this.env.configDir, `${name}.lock`);
|
|
5308
5378
|
let release;
|
|
5309
5379
|
try {
|
|
5310
|
-
release = await lockfile.lock(this.env.configDir, {
|
|
5380
|
+
release = await lockfile.lock(join2(this.env.configDir, name), {
|
|
5311
5381
|
lockfilePath,
|
|
5382
|
+
realpath: false,
|
|
5312
5383
|
stale: LOCK_STALE_MS,
|
|
5313
5384
|
retries: { retries: 60, minTimeout: 100, maxTimeout: 500 }
|
|
5314
5385
|
});
|
|
5315
5386
|
} catch {
|
|
5316
5387
|
throw new WorkosError(
|
|
5317
5388
|
"locked",
|
|
5318
|
-
|
|
5389
|
+
`${busy} Try again, or delete ${lockfilePath} if none is running.`
|
|
5319
5390
|
);
|
|
5320
5391
|
}
|
|
5321
5392
|
try {
|
|
@@ -5342,8 +5413,25 @@ var CredentialStore = class {
|
|
|
5342
5413
|
// src/auth/credentials.ts
|
|
5343
5414
|
var AuthError = class extends Error {
|
|
5344
5415
|
};
|
|
5416
|
+
var NotMemberError = class extends AuthError {
|
|
5417
|
+
};
|
|
5345
5418
|
var API_KEY_OVERRIDES = "ASTEROID_API_KEY is set, so the CLI uses that key and ignores `asteroid login`.";
|
|
5346
5419
|
var UNSET_API_KEY = "Run `unset ASTEROID_API_KEY` to use your login instead.";
|
|
5420
|
+
function isStaffLogin(login2) {
|
|
5421
|
+
return login2?.email?.toLowerCase().endsWith("@asteroid.ai") ?? false;
|
|
5422
|
+
}
|
|
5423
|
+
function staffHint(ref, staff) {
|
|
5424
|
+
return staff ? ` Asteroid staff: run \`asteroid use ${clean(ref)} --reason <why>\`.` : "";
|
|
5425
|
+
}
|
|
5426
|
+
function staffAuth(held) {
|
|
5427
|
+
return {
|
|
5428
|
+
kind: "bearer",
|
|
5429
|
+
headers: { Authorization: `Bearer ${held.token}` },
|
|
5430
|
+
org: held.org,
|
|
5431
|
+
expiresAt: held.expiresAt,
|
|
5432
|
+
staff: { grantId: held.grantId, reason: held.reason }
|
|
5433
|
+
};
|
|
5434
|
+
}
|
|
5347
5435
|
function orgFromToken(token) {
|
|
5348
5436
|
const claims = tokenClaims(token);
|
|
5349
5437
|
if (!claims.org_id) return void 0;
|
|
@@ -5370,7 +5458,7 @@ function bearer(token, expiresAt) {
|
|
|
5370
5458
|
};
|
|
5371
5459
|
}
|
|
5372
5460
|
var SSO_OR_MFA = /^(sso_required|mfa_.*|organization_authentication_methods_required)$/;
|
|
5373
|
-
function refreshError(error, org) {
|
|
5461
|
+
function refreshError(error, org, login2) {
|
|
5374
5462
|
if (error.code === "invalid_grant") {
|
|
5375
5463
|
return new AuthError("your sign-in has expired. Run `asteroid login`.");
|
|
5376
5464
|
}
|
|
@@ -5380,8 +5468,8 @@ function refreshError(error, org) {
|
|
|
5380
5468
|
);
|
|
5381
5469
|
}
|
|
5382
5470
|
if (error.code === "organization_membership_not_found") {
|
|
5383
|
-
return new
|
|
5384
|
-
`you are not a member of ${orgLabel(org)}.
|
|
5471
|
+
return new NotMemberError(
|
|
5472
|
+
`you are not a member of ${orgLabel(org)}.${staffHint(org.id ?? org.workosId, isStaffLogin(login2))}`
|
|
5385
5473
|
);
|
|
5386
5474
|
}
|
|
5387
5475
|
return new AuthError(
|
|
@@ -5392,15 +5480,24 @@ async function resolveAuth(env, config, orgRef, fetchImpl = fetch) {
|
|
|
5392
5480
|
if (env.apiKey)
|
|
5393
5481
|
return { kind: "api-key", headers: { "X-Api-Key": env.apiKey } };
|
|
5394
5482
|
const store = new CredentialStore(env);
|
|
5395
|
-
|
|
5483
|
+
const login2 = await store.readLogin();
|
|
5484
|
+
if (!login2) {
|
|
5396
5485
|
throw new AuthError(
|
|
5397
5486
|
"not signed in. Run `asteroid login`, or set ASTEROID_API_KEY."
|
|
5398
5487
|
);
|
|
5399
5488
|
}
|
|
5489
|
+
const target = orgRef ?? config.org?.workosId;
|
|
5490
|
+
const held = target ? await store.staffToken(target) : void 0;
|
|
5491
|
+
if (held) {
|
|
5492
|
+
if (held.expiresAt - 3e4 > Date.now()) return staffAuth(held);
|
|
5493
|
+
throw new AuthError(
|
|
5494
|
+
`your staff access to ${orgLabel(held.org)} has expired.${staffHint(held.org.id ?? held.org.workosId, true)}`
|
|
5495
|
+
);
|
|
5496
|
+
}
|
|
5400
5497
|
const org = orgRef ? findOrg(orgRef, config.orgs ?? []) : config.org;
|
|
5401
5498
|
if (orgRef && !org) {
|
|
5402
5499
|
throw new AuthError(
|
|
5403
|
-
`unknown organization ${orgRef}. Run \`asteroid orgs\` to list yours
|
|
5500
|
+
`unknown organization ${clean(orgRef)}. Run \`asteroid orgs\` to list yours.${staffHint(orgRef, isStaffLogin(login2))}`
|
|
5404
5501
|
);
|
|
5405
5502
|
}
|
|
5406
5503
|
if (!org) {
|
|
@@ -5413,8 +5510,8 @@ async function resolveAuth(env, config, orgRef, fetchImpl = fetch) {
|
|
|
5413
5510
|
return store.withRefreshLock(async () => {
|
|
5414
5511
|
const fresh = store.cachedToken(org.workosId);
|
|
5415
5512
|
if (fresh) return bearer(fresh.accessToken, fresh.expiresAt);
|
|
5416
|
-
const
|
|
5417
|
-
if (!
|
|
5513
|
+
const login3 = await store.readLogin();
|
|
5514
|
+
if (!login3) {
|
|
5418
5515
|
throw new AuthError("not signed in. Run `asteroid login`.");
|
|
5419
5516
|
}
|
|
5420
5517
|
try {
|
|
@@ -5424,10 +5521,10 @@ async function resolveAuth(env, config, orgRef, fetchImpl = fetch) {
|
|
|
5424
5521
|
apiUrl: env.workosApiUrl,
|
|
5425
5522
|
fetch: fetchImpl
|
|
5426
5523
|
},
|
|
5427
|
-
|
|
5524
|
+
login3.refreshToken,
|
|
5428
5525
|
org.workosId
|
|
5429
5526
|
);
|
|
5430
|
-
await store.writeLogin({ ...
|
|
5527
|
+
await store.writeLogin({ ...login3, refreshToken: session2.refreshToken });
|
|
5431
5528
|
const expiresAt = (tokenClaims(session2.accessToken).exp ?? 0) * 1e3 || Date.now() + 6e4;
|
|
5432
5529
|
const auth = bearer(session2.accessToken, expiresAt);
|
|
5433
5530
|
store.cacheToken(auth.org?.workosId ?? org.workosId, {
|
|
@@ -5437,7 +5534,7 @@ async function resolveAuth(env, config, orgRef, fetchImpl = fetch) {
|
|
|
5437
5534
|
rememberOrg(env, config, auth.org);
|
|
5438
5535
|
return auth;
|
|
5439
5536
|
} catch (error) {
|
|
5440
|
-
if (error instanceof WorkosError) throw refreshError(error, org);
|
|
5537
|
+
if (error instanceof WorkosError) throw refreshError(error, org, login3);
|
|
5441
5538
|
throw error;
|
|
5442
5539
|
}
|
|
5443
5540
|
});
|
|
@@ -5453,7 +5550,7 @@ var ApiError = class extends Error {
|
|
|
5453
5550
|
status;
|
|
5454
5551
|
body;
|
|
5455
5552
|
};
|
|
5456
|
-
var VERSION = "1.9.
|
|
5553
|
+
var VERSION = "1.9.27";
|
|
5457
5554
|
function problem(status, body, fallback) {
|
|
5458
5555
|
if (body && typeof body === "object") {
|
|
5459
5556
|
const record2 = body;
|
|
@@ -5513,14 +5610,14 @@ async function send(apiUrl, auth, request, binary, fetchImpl = fetch) {
|
|
|
5513
5610
|
}
|
|
5514
5611
|
const contentType = response.headers.get("content-type") ?? "";
|
|
5515
5612
|
if (!response.ok) {
|
|
5516
|
-
const
|
|
5517
|
-
let parsed =
|
|
5613
|
+
const text3 = await response.text();
|
|
5614
|
+
let parsed = text3;
|
|
5518
5615
|
try {
|
|
5519
|
-
parsed = JSON.parse(
|
|
5616
|
+
parsed = JSON.parse(text3);
|
|
5520
5617
|
} catch {
|
|
5521
|
-
parsed =
|
|
5618
|
+
parsed = text3;
|
|
5522
5619
|
}
|
|
5523
|
-
let message = problem(response.status, parsed,
|
|
5620
|
+
let message = problem(response.status, parsed, text3.slice(0, 200));
|
|
5524
5621
|
const sentKey = "X-Api-Key" in hopHeaders;
|
|
5525
5622
|
if (sentKey && response.status === 401)
|
|
5526
5623
|
message = `ASTEROID_API_KEY was rejected: ${message}. Check the key. ${UNSET_API_KEY}`;
|
|
@@ -5632,11 +5729,21 @@ async function orgs(deps, flagOrg, json) {
|
|
|
5632
5729
|
deps.fetch
|
|
5633
5730
|
);
|
|
5634
5731
|
const list = loadConfig(deps.env).orgs ?? [];
|
|
5732
|
+
const staff = (await new CredentialStore(deps.env).staffTokens()).filter(
|
|
5733
|
+
(held) => held.expiresAt > Date.now()
|
|
5734
|
+
);
|
|
5635
5735
|
const active = auth.org?.workosId;
|
|
5636
5736
|
if (json) {
|
|
5637
5737
|
deps.stdout(
|
|
5638
5738
|
`${JSON.stringify(
|
|
5639
|
-
|
|
5739
|
+
[
|
|
5740
|
+
...list.map((org) => ({ ...org, active: org.workosId === active })),
|
|
5741
|
+
...staff.map((held) => ({
|
|
5742
|
+
...held.org,
|
|
5743
|
+
active: held.org.workosId === active,
|
|
5744
|
+
staff: { reason: held.reason, expiresAt: held.expiresAt }
|
|
5745
|
+
}))
|
|
5746
|
+
],
|
|
5640
5747
|
null,
|
|
5641
5748
|
2
|
|
5642
5749
|
)}
|
|
@@ -5647,13 +5754,204 @@ async function orgs(deps, flagOrg, json) {
|
|
|
5647
5754
|
for (const org of list)
|
|
5648
5755
|
deps.stdout(`${org.workosId === active ? "*" : " "} ${orgLabel(org)}
|
|
5649
5756
|
`);
|
|
5757
|
+
for (const held of staff)
|
|
5758
|
+
deps.stdout(
|
|
5759
|
+
`${held.org.workosId === active ? "*" : " "} ${orgLabel(held.org)} staff access, ${timeLeft(held.expiresAt)} left
|
|
5760
|
+
`
|
|
5761
|
+
);
|
|
5650
5762
|
return 0;
|
|
5651
5763
|
}
|
|
5652
|
-
|
|
5764
|
+
function timeLeft(expiresAt) {
|
|
5765
|
+
const minutes = Math.max(0, Math.round((expiresAt - Date.now()) / 6e4));
|
|
5766
|
+
return minutes >= 60 ? `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, "0")}m` : `${minutes}m`;
|
|
5767
|
+
}
|
|
5768
|
+
function isMember(config, org) {
|
|
5769
|
+
return (config.orgs ?? []).some((o) => o.workosId === org.workosId);
|
|
5770
|
+
}
|
|
5771
|
+
function homeOrg(config) {
|
|
5772
|
+
const org = config.org && isMember(config, config.org) ? config.org : config.orgs?.[0];
|
|
5773
|
+
if (!org)
|
|
5774
|
+
throw new AuthError(
|
|
5775
|
+
"staff access needs a sign-in to one of your own organizations. Run `asteroid login`."
|
|
5776
|
+
);
|
|
5777
|
+
return org;
|
|
5778
|
+
}
|
|
5779
|
+
var STAFF_TOKEN_PATH = "/users/v2/staff-tokens";
|
|
5780
|
+
async function mintStaffToken(deps, config, ref, reason) {
|
|
5781
|
+
const home = await resolveAuth(
|
|
5782
|
+
deps.env,
|
|
5783
|
+
config,
|
|
5784
|
+
homeOrg(config).workosId,
|
|
5785
|
+
deps.fetch
|
|
5786
|
+
);
|
|
5787
|
+
let minted;
|
|
5788
|
+
try {
|
|
5789
|
+
const response = await send(
|
|
5790
|
+
deps.env.apiUrl,
|
|
5791
|
+
home,
|
|
5792
|
+
{
|
|
5793
|
+
method: "POST",
|
|
5794
|
+
path: STAFF_TOKEN_PATH,
|
|
5795
|
+
query: new URLSearchParams(),
|
|
5796
|
+
json: { organization: ref, reason }
|
|
5797
|
+
},
|
|
5798
|
+
false,
|
|
5799
|
+
deps.fetch
|
|
5800
|
+
);
|
|
5801
|
+
minted = response.json;
|
|
5802
|
+
} catch (error) {
|
|
5803
|
+
if (error instanceof ApiError && error.status === 403)
|
|
5804
|
+
throw new AuthError(
|
|
5805
|
+
`you are not a member of ${clean(ref)}, and staff access is for Asteroid staff only.`
|
|
5806
|
+
);
|
|
5807
|
+
if (error instanceof ApiError && error.status === 404)
|
|
5808
|
+
throw new UsageError(
|
|
5809
|
+
`unknown organization ${clean(ref)}. Pass its id or its WorkOS id (org_...).`
|
|
5810
|
+
);
|
|
5811
|
+
throw error;
|
|
5812
|
+
}
|
|
5813
|
+
return {
|
|
5814
|
+
token: minted.token,
|
|
5815
|
+
grantId: minted.grantId,
|
|
5816
|
+
org: {
|
|
5817
|
+
workosId: minted.workosOrganizationId ?? minted.organizationId,
|
|
5818
|
+
id: minted.organizationId,
|
|
5819
|
+
name: minted.organizationName
|
|
5820
|
+
},
|
|
5821
|
+
reason: minted.reason,
|
|
5822
|
+
expiresAt: Date.parse(minted.expiresAt)
|
|
5823
|
+
};
|
|
5824
|
+
}
|
|
5825
|
+
async function revokeStaffToken(deps, home, held) {
|
|
5826
|
+
try {
|
|
5827
|
+
await send(
|
|
5828
|
+
deps.env.apiUrl,
|
|
5829
|
+
home,
|
|
5830
|
+
{
|
|
5831
|
+
method: "DELETE",
|
|
5832
|
+
path: `${STAFF_TOKEN_PATH}/${encodeURIComponent(held.grantId)}`,
|
|
5833
|
+
query: new URLSearchParams()
|
|
5834
|
+
},
|
|
5835
|
+
false,
|
|
5836
|
+
deps.fetch
|
|
5837
|
+
);
|
|
5838
|
+
return true;
|
|
5839
|
+
} catch (error) {
|
|
5840
|
+
if (error instanceof ApiError && error.status === 404) return true;
|
|
5841
|
+
deps.stderr(
|
|
5842
|
+
`Could not revoke staff access to ${orgLabel(held.org)}: ${clean(error instanceof Error ? error.message : String(error))}. It ends at ${new Date(held.expiresAt).toISOString()}.
|
|
5843
|
+
`
|
|
5844
|
+
);
|
|
5845
|
+
return false;
|
|
5846
|
+
}
|
|
5847
|
+
}
|
|
5848
|
+
async function exitStaff(deps) {
|
|
5849
|
+
const store = new CredentialStore(deps.env);
|
|
5850
|
+
const held = await store.staffTokens();
|
|
5851
|
+
const config = loadConfig(deps.env);
|
|
5852
|
+
if (held.length === 0) {
|
|
5853
|
+
deps.stderr("No staff access to leave.\n");
|
|
5854
|
+
return 0;
|
|
5855
|
+
}
|
|
5856
|
+
const home = await resolveAuth(
|
|
5857
|
+
deps.env,
|
|
5858
|
+
config,
|
|
5859
|
+
homeOrg(config).workosId,
|
|
5860
|
+
deps.fetch
|
|
5861
|
+
);
|
|
5862
|
+
const left = [];
|
|
5863
|
+
const kept = [];
|
|
5864
|
+
for (const token of held) {
|
|
5865
|
+
const ended = token.expiresAt <= Date.now() || await revokeStaffToken(deps, home, token);
|
|
5866
|
+
if (!ended) {
|
|
5867
|
+
kept.push(token);
|
|
5868
|
+
continue;
|
|
5869
|
+
}
|
|
5870
|
+
await store.dropStaffToken(token.grantId);
|
|
5871
|
+
left.push(token);
|
|
5872
|
+
}
|
|
5873
|
+
const back = held.find((t) => t.from)?.from ?? homeOrg(config);
|
|
5874
|
+
saveConfig(deps.env, { ...loadConfig(deps.env), org: back });
|
|
5875
|
+
if (left.length > 0)
|
|
5876
|
+
deps.stderr(
|
|
5877
|
+
`Left staff access to ${left.map((t) => orgLabel(t.org)).join(", ")}.
|
|
5878
|
+
`
|
|
5879
|
+
);
|
|
5880
|
+
if (kept.length > 0)
|
|
5881
|
+
deps.stderr(
|
|
5882
|
+
`Still holding staff access to ${kept.map((t) => orgLabel(t.org)).join(", ")}. Run \`asteroid use --exit\` again to retry.
|
|
5883
|
+
`
|
|
5884
|
+
);
|
|
5885
|
+
deps.stderr(`Now using ${orgLabel(back)}.
|
|
5886
|
+
`);
|
|
5887
|
+
return kept.length === 0 ? 0 : 1;
|
|
5888
|
+
}
|
|
5889
|
+
async function askReason(deps, org) {
|
|
5890
|
+
if (!deps.ui) return void 0;
|
|
5891
|
+
const reason = await deps.ui.text(
|
|
5892
|
+
`You are not a member of ${clean(org)}. Why do you need staff access? This is audited.`
|
|
5893
|
+
);
|
|
5894
|
+
return reason?.trim() || void 0;
|
|
5895
|
+
}
|
|
5896
|
+
async function useAsStaff(deps, config, ref, reason, staff) {
|
|
5897
|
+
const store = new CredentialStore(deps.env);
|
|
5898
|
+
const held = await store.staffToken(ref);
|
|
5899
|
+
if (held && held.expiresAt - 3e4 > Date.now() && !reason) {
|
|
5900
|
+
saveConfig(deps.env, { ...config, org: held.org });
|
|
5901
|
+
deps.stderr(
|
|
5902
|
+
`Now using ${orgLabel(held.org)} with staff access, ${timeLeft(held.expiresAt)} left.
|
|
5903
|
+
`
|
|
5904
|
+
);
|
|
5905
|
+
return 0;
|
|
5906
|
+
}
|
|
5907
|
+
if (!reason && staff) reason = await askReason(deps, ref);
|
|
5908
|
+
if (!reason)
|
|
5909
|
+
throw new UsageError(
|
|
5910
|
+
`you are not a member of ${clean(ref)}.${staffHint(ref, staff)}${staff ? " The reason is audited." : ""}`
|
|
5911
|
+
);
|
|
5912
|
+
const minted = await mintStaffToken(
|
|
5913
|
+
deps,
|
|
5914
|
+
config,
|
|
5915
|
+
held?.org.id ?? ref,
|
|
5916
|
+
reason
|
|
5917
|
+
);
|
|
5918
|
+
const previous = config.org ? isMember(config, config.org) ? config.org : (await store.staffToken(config.org.workosId))?.from : void 0;
|
|
5919
|
+
try {
|
|
5920
|
+
await store.saveStaffToken(
|
|
5921
|
+
{ ...minted, from: previous ?? held?.from },
|
|
5922
|
+
held?.grantId
|
|
5923
|
+
);
|
|
5924
|
+
} catch (error) {
|
|
5925
|
+
const home = await resolveAuth(
|
|
5926
|
+
deps.env,
|
|
5927
|
+
config,
|
|
5928
|
+
homeOrg(config).workosId,
|
|
5929
|
+
deps.fetch
|
|
5930
|
+
);
|
|
5931
|
+
await revokeStaffToken(deps, home, minted);
|
|
5932
|
+
throw error;
|
|
5933
|
+
}
|
|
5934
|
+
saveConfig(deps.env, { ...loadConfig(deps.env), org: minted.org });
|
|
5935
|
+
deps.stderr(
|
|
5936
|
+
`Staff access to ${orgLabel(minted.org)} for ${timeLeft(minted.expiresAt)}, until ${new Date(minted.expiresAt).toLocaleTimeString()}. Reason: ${clean(minted.reason)}. This is audited. Run \`asteroid use --exit\` to leave.
|
|
5937
|
+
`
|
|
5938
|
+
);
|
|
5939
|
+
return 0;
|
|
5940
|
+
}
|
|
5941
|
+
async function use(deps, ref, options = {}) {
|
|
5653
5942
|
if (deps.env.apiKey)
|
|
5654
5943
|
throw new UsageError(
|
|
5655
5944
|
`${API_KEY_OVERRIDES} \`use\` only switches the login's organization. ${UNSET_API_KEY}`
|
|
5656
5945
|
);
|
|
5946
|
+
if (options.exit) {
|
|
5947
|
+
if (ref || options.reason)
|
|
5948
|
+
throw new UsageError("usage: asteroid use --exit");
|
|
5949
|
+
return exitStaff(deps);
|
|
5950
|
+
}
|
|
5951
|
+
if (options.reason?.startsWith("-"))
|
|
5952
|
+
throw new UsageError(
|
|
5953
|
+
`--reason needs a value, not ${clean(options.reason)}. Give the reason without a leading -.`
|
|
5954
|
+
);
|
|
5657
5955
|
const config = loadConfig(deps.env);
|
|
5658
5956
|
if (!ref && deps.ui && (config.orgs?.length ?? 0) > 0) {
|
|
5659
5957
|
ref = await deps.ui.select(
|
|
@@ -5666,12 +5964,20 @@ async function use(deps, ref) {
|
|
|
5666
5964
|
if (!ref) return 1;
|
|
5667
5965
|
}
|
|
5668
5966
|
if (!ref) throw new UsageError("usage: asteroid use <org>");
|
|
5967
|
+
const login2 = await new CredentialStore(deps.env).readLogin();
|
|
5968
|
+
if (!login2) throw new AuthError("not signed in. Run `asteroid login`.");
|
|
5969
|
+
const staff = isStaffLogin(login2);
|
|
5669
5970
|
const org = findOrg(ref, config.orgs ?? []);
|
|
5670
|
-
if (!org)
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5971
|
+
if (!org || !isMember(config, org) && options.reason)
|
|
5972
|
+
return useAsStaff(deps, config, ref, options.reason, staff);
|
|
5973
|
+
let auth;
|
|
5974
|
+
try {
|
|
5975
|
+
auth = await resolveAuth(deps.env, config, org.workosId, deps.fetch);
|
|
5976
|
+
} catch (error) {
|
|
5977
|
+
if (error instanceof NotMemberError)
|
|
5978
|
+
return useAsStaff(deps, config, ref, options.reason, staff);
|
|
5979
|
+
throw error;
|
|
5980
|
+
}
|
|
5675
5981
|
const updated = loadConfig(deps.env);
|
|
5676
5982
|
updated.org = updated.orgs?.find((o) => o.workosId === org.workosId) ?? auth.org ?? org;
|
|
5677
5983
|
saveConfig(deps.env, updated);
|
|
@@ -5703,6 +6009,7 @@ async function whoami(deps, flagOrg, json) {
|
|
|
5703
6009
|
);
|
|
5704
6010
|
return 0;
|
|
5705
6011
|
}
|
|
6012
|
+
const grant = body.staffGrant;
|
|
5706
6013
|
const person = body.person;
|
|
5707
6014
|
const orgId = body.organizationId;
|
|
5708
6015
|
const organization = auth.org ? orgLabel(auth.org) : orgId ?? "none";
|
|
@@ -5711,9 +6018,10 @@ async function whoami(deps, flagOrg, json) {
|
|
|
5711
6018
|
as: person?.email ?? body.id,
|
|
5712
6019
|
kind: body.kind,
|
|
5713
6020
|
credential: body.credential,
|
|
5714
|
-
source: auth.kind === "api-key" ? "ASTEROID_API_KEY" : "asteroid login",
|
|
6021
|
+
source: auth.kind === "api-key" ? "ASTEROID_API_KEY" : auth.staff ? "asteroid use (staff access)" : "asteroid login",
|
|
5715
6022
|
organization,
|
|
5716
|
-
...
|
|
6023
|
+
...grant ? { staffReason: grant.reason } : {},
|
|
6024
|
+
...auth.expiresAt === void 0 ? {} : { tokenExpiresIn: timeLeft(auth.expiresAt) }
|
|
5717
6025
|
})}
|
|
5718
6026
|
`
|
|
5719
6027
|
);
|
|
@@ -6107,8 +6415,8 @@ function executionDir(options, execution) {
|
|
|
6107
6415
|
);
|
|
6108
6416
|
return join3(root, "executions", executionDirName(execution.id));
|
|
6109
6417
|
}
|
|
6110
|
-
async function writeText(sync, path,
|
|
6111
|
-
await sync.files.write(path, Buffer.from(
|
|
6418
|
+
async function writeText(sync, path, text3) {
|
|
6419
|
+
await sync.files.write(path, Buffer.from(text3, "utf8"));
|
|
6112
6420
|
}
|
|
6113
6421
|
async function writeActivities(sync, dir, execution) {
|
|
6114
6422
|
try {
|
|
@@ -6308,16 +6616,10 @@ function nodeFiles(cwd) {
|
|
|
6308
6616
|
|
|
6309
6617
|
// src/sync/workflow-pull.ts
|
|
6310
6618
|
var CONCURRENCY = 16;
|
|
6311
|
-
function
|
|
6312
|
-
const base =
|
|
6313
|
-
|
|
6314
|
-
return
|
|
6315
|
-
}
|
|
6316
|
-
function pullDir(options, tree) {
|
|
6317
|
-
if (options.dir) return options.dir;
|
|
6318
|
-
const root = workflowRoot(options.root, tree.workflowName);
|
|
6319
|
-
if (!options.versionId) return join3(root, "head");
|
|
6320
|
-
return join3(root, ".versions", workflowDirName(tree.versionId, tree.version));
|
|
6619
|
+
function pullDir(root, tree, head) {
|
|
6620
|
+
const base = workflowRoot(root, tree.workflowName);
|
|
6621
|
+
if (head) return join3(base, "head");
|
|
6622
|
+
return join3(base, ".versions", workflowDirName(tree.versionId, tree.version));
|
|
6321
6623
|
}
|
|
6322
6624
|
async function agentFileBytes(sync, workflowId, tree, entry) {
|
|
6323
6625
|
const path = `/agents/v2/workflows/${workflowId}/versions/${tree.versionId}/files/${entry.fileId}/signed-url`;
|
|
@@ -6362,13 +6664,19 @@ async function removeStale(sync, dir, keep) {
|
|
|
6362
6664
|
}
|
|
6363
6665
|
}
|
|
6364
6666
|
async function writeManifest(sync, dir, manifest) {
|
|
6365
|
-
const
|
|
6667
|
+
const text3 = `${JSON.stringify(manifest, null, 2)}
|
|
6366
6668
|
`;
|
|
6367
|
-
await sync.files.write(join3(dir, MANIFEST), Buffer.from(
|
|
6669
|
+
await sync.files.write(join3(dir, MANIFEST), Buffer.from(text3, "utf8"));
|
|
6368
6670
|
}
|
|
6369
6671
|
async function pullWorkflow(sync, options) {
|
|
6370
|
-
const
|
|
6371
|
-
|
|
6672
|
+
const request = {
|
|
6673
|
+
...options.request,
|
|
6674
|
+
query: new URLSearchParams(options.request.query)
|
|
6675
|
+
};
|
|
6676
|
+
request.query.set("contents", "all");
|
|
6677
|
+
const tree = await sync.remote.json(request);
|
|
6678
|
+
const head = request.path.endsWith("/head/files");
|
|
6679
|
+
const dir = options.dir ?? pullDir(options.root ?? "", tree, head);
|
|
6372
6680
|
const { hashes, skipped, shared } = await writeEntries(
|
|
6373
6681
|
sync,
|
|
6374
6682
|
options,
|
|
@@ -6380,7 +6688,7 @@ async function pullWorkflow(sync, options) {
|
|
|
6380
6688
|
await writeManifest(sync, dir, {
|
|
6381
6689
|
workflowId: options.workflowId,
|
|
6382
6690
|
versionId: tree.versionId,
|
|
6383
|
-
head
|
|
6691
|
+
head,
|
|
6384
6692
|
rev: tree.rev,
|
|
6385
6693
|
files: hashes
|
|
6386
6694
|
});
|
|
@@ -6392,6 +6700,7 @@ async function pullWorkflow(sync, options) {
|
|
|
6392
6700
|
version: tree.version,
|
|
6393
6701
|
rev: tree.rev,
|
|
6394
6702
|
dir,
|
|
6703
|
+
head,
|
|
6395
6704
|
written: Object.keys(hashes).length,
|
|
6396
6705
|
shared,
|
|
6397
6706
|
skipped
|
|
@@ -6406,7 +6715,8 @@ function formatWorkflowPull(result) {
|
|
|
6406
6715
|
`Written to: ${result.dir}/`,
|
|
6407
6716
|
files,
|
|
6408
6717
|
result.skipped.length > 0 ? `Skipped:
|
|
6409
|
-
${result.skipped.map((s) => ` - ${s}`).join("\n")}` : null
|
|
6718
|
+
${result.skipped.map((s) => ` - ${s}`).join("\n")}` : null,
|
|
6719
|
+
result.head ? `Edit the files, then run \`asteroid workflow push\` from ${result.dir}/.` : "This is a pinned version. It is read-only."
|
|
6410
6720
|
].filter(Boolean).join("\n");
|
|
6411
6721
|
}
|
|
6412
6722
|
|
|
@@ -6488,8 +6798,13 @@ async function refresh2(sync, dir, manifest, pushed) {
|
|
|
6488
6798
|
await writeManifest(sync, dir, next);
|
|
6489
6799
|
return next;
|
|
6490
6800
|
}
|
|
6491
|
-
async function pushWorkflow(sync, dir) {
|
|
6801
|
+
async function pushWorkflow(sync, dir, workflowId) {
|
|
6492
6802
|
const manifest = await readManifest(sync, dir);
|
|
6803
|
+
if (workflowId !== void 0 && manifest.workflowId !== workflowId) {
|
|
6804
|
+
throw new UsageError(
|
|
6805
|
+
`${dir} holds workflow ${manifest.workflowId}, not ${workflowId}.`
|
|
6806
|
+
);
|
|
6807
|
+
}
|
|
6493
6808
|
const { writes, deletes } = await localChanges(sync, dir, manifest);
|
|
6494
6809
|
const result = {
|
|
6495
6810
|
workflowId: manifest.workflowId,
|
|
@@ -6550,42 +6865,64 @@ function executionOptions(parsed) {
|
|
|
6550
6865
|
dir: dir ? () => dir : void 0
|
|
6551
6866
|
};
|
|
6552
6867
|
}
|
|
6868
|
+
function withoutFlag(parsed, name) {
|
|
6869
|
+
const flags = new Map(parsed.flags);
|
|
6870
|
+
flags.delete(name);
|
|
6871
|
+
return { ...parsed, flags };
|
|
6872
|
+
}
|
|
6873
|
+
function runsLocally(command, parsed) {
|
|
6874
|
+
if (command.local) return true;
|
|
6875
|
+
if (command.files === "pull") return !parsed.json;
|
|
6876
|
+
if (command.files === "push") return parsed.body === void 0;
|
|
6877
|
+
return false;
|
|
6878
|
+
}
|
|
6879
|
+
var PARTIAL = ["paths", "variants"];
|
|
6880
|
+
function rejectPartial(parsed) {
|
|
6881
|
+
const used = PARTIAL.filter((name) => parsed.flags.has(name));
|
|
6882
|
+
if (used.length === 0) return;
|
|
6883
|
+
throw new UsageError(
|
|
6884
|
+
`pull writes the whole tree, so it cannot take ${used.map((name) => `--${name}`).join(" or ")}. Add --json to list those files instead.`
|
|
6885
|
+
);
|
|
6886
|
+
}
|
|
6887
|
+
async function pull(command, parsed, sync, deps) {
|
|
6888
|
+
rejectPartial(parsed);
|
|
6889
|
+
const result = await pullWorkflow(sync, {
|
|
6890
|
+
workflowId: parsed.positionals[0],
|
|
6891
|
+
request: await buildRequest(command, withoutFlag(parsed, "out"), {
|
|
6892
|
+
io: deps.io
|
|
6893
|
+
}),
|
|
6894
|
+
dir: flag(parsed, "out"),
|
|
6895
|
+
root: deps.root ?? "",
|
|
6896
|
+
...deps.workflowPull
|
|
6897
|
+
});
|
|
6898
|
+
return { result, text: formatWorkflowPull(result) };
|
|
6899
|
+
}
|
|
6553
6900
|
async function execute(command, parsed, sync, deps) {
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
...deps.workflowPull
|
|
6563
|
-
});
|
|
6564
|
-
return { result, text: formatWorkflowPull(result) };
|
|
6565
|
-
}
|
|
6566
|
-
case "workflow push": {
|
|
6567
|
-
const result = await pushWorkflow(sync, flag(parsed, "dir") ?? ".");
|
|
6568
|
-
return { result, text: formatWorkflowPush(result) };
|
|
6569
|
-
}
|
|
6570
|
-
case "execution pull": {
|
|
6571
|
-
const result = await pullExecution(sync, {
|
|
6572
|
-
...executionOptions(parsed),
|
|
6573
|
-
root,
|
|
6574
|
-
...deps.executionPull
|
|
6575
|
-
});
|
|
6576
|
-
return { result, text: formatExecutionPull(result) };
|
|
6577
|
-
}
|
|
6578
|
-
default:
|
|
6579
|
-
throw new UsageError(`unknown command ${command.command}`);
|
|
6901
|
+
if (command.files === "pull") return pull(command, parsed, sync, deps);
|
|
6902
|
+
if (command.files === "push") {
|
|
6903
|
+
const result2 = await pushWorkflow(
|
|
6904
|
+
sync,
|
|
6905
|
+
flag(parsed, "from") ?? ".",
|
|
6906
|
+
parsed.positionals[0] || void 0
|
|
6907
|
+
);
|
|
6908
|
+
return { result: result2, text: formatWorkflowPush(result2) };
|
|
6580
6909
|
}
|
|
6910
|
+
if (command.command !== "execution pull")
|
|
6911
|
+
throw new UsageError(`unknown command ${command.command}`);
|
|
6912
|
+
const result = await pullExecution(sync, {
|
|
6913
|
+
...executionOptions(parsed),
|
|
6914
|
+
root: deps.root ?? "",
|
|
6915
|
+
...deps.executionPull
|
|
6916
|
+
});
|
|
6917
|
+
return { result, text: formatExecutionPull(result) };
|
|
6581
6918
|
}
|
|
6582
6919
|
async function runSync(command, parsed, deps) {
|
|
6583
6920
|
const sync = {
|
|
6584
6921
|
remote: deps.remote,
|
|
6585
6922
|
files: deps.files ?? nodeFiles(deps.cwd)
|
|
6586
6923
|
};
|
|
6587
|
-
const { result, text:
|
|
6588
|
-
deps.stdout(`${parsed.json ? JSON.stringify(result, null, 2) :
|
|
6924
|
+
const { result, text: text3 } = await execute(command, parsed, sync, deps);
|
|
6925
|
+
deps.stdout(`${parsed.json ? JSON.stringify(result, null, 2) : text3}
|
|
6589
6926
|
`);
|
|
6590
6927
|
return 0;
|
|
6591
6928
|
}
|
|
@@ -6613,7 +6950,10 @@ async function runBuiltin(name, parsed, deps) {
|
|
|
6613
6950
|
"run `asteroid complete <shell>` to print a completion script"
|
|
6614
6951
|
);
|
|
6615
6952
|
case "use":
|
|
6616
|
-
return use(deps, parsed.positionals[0]
|
|
6953
|
+
return use(deps, parsed.positionals[0], {
|
|
6954
|
+
reason: parsed.flags.get("reason")?.[0],
|
|
6955
|
+
exit: parsed.flags.has("exit")
|
|
6956
|
+
});
|
|
6617
6957
|
default:
|
|
6618
6958
|
return whoami(deps, parsed.org, parsed.json);
|
|
6619
6959
|
}
|
|
@@ -6642,7 +6982,8 @@ async function authFor(parsed, deps) {
|
|
|
6642
6982
|
function announce(command, auth, deps) {
|
|
6643
6983
|
if (command.readOnly) return;
|
|
6644
6984
|
const org = auth.kind === "api-key" ? "the API key's organization" : auth.org ? orgLabel(auth.org) : "your default organization";
|
|
6645
|
-
|
|
6985
|
+
const staff = auth.staff ? ` with staff access (${clean(auth.staff.reason)})` : "";
|
|
6986
|
+
deps.stderr(`Acting in ${org}${staff}.
|
|
6646
6987
|
`);
|
|
6647
6988
|
}
|
|
6648
6989
|
async function runCommand(command, parsed, deps) {
|
|
@@ -6710,7 +7051,9 @@ async function run(argv2, deps) {
|
|
|
6710
7051
|
commands: ALL_COMMANDS,
|
|
6711
7052
|
builtins: BUILTINS,
|
|
6712
7053
|
version: VERSION,
|
|
6713
|
-
description: DESCRIPTION
|
|
7054
|
+
description: DESCRIPTION,
|
|
7055
|
+
defaultArg: (command, arg) => command.files === "push" && arg.name === "workflowId" ? "" : void 0,
|
|
7056
|
+
defaultArgNote: "<workflow-id> defaults to the workflow the pushed folder was pulled from."
|
|
6714
7057
|
});
|
|
6715
7058
|
if (invocation.kind === "exit") {
|
|
6716
7059
|
if (invocation.stdout) deps.stdout(invocation.stdout);
|
|
@@ -6720,7 +7063,7 @@ async function run(argv2, deps) {
|
|
|
6720
7063
|
if (invocation.kind === "builtin") {
|
|
6721
7064
|
return await runBuiltin(invocation.name, invocation.parsed, deps);
|
|
6722
7065
|
}
|
|
6723
|
-
if (invocation.command.
|
|
7066
|
+
if (runsLocally(invocation.command, invocation.parsed)) {
|
|
6724
7067
|
const auth = await authFor(invocation.parsed, deps);
|
|
6725
7068
|
announce(invocation.command, auth, deps);
|
|
6726
7069
|
return await runSync(invocation.command, invocation.parsed, {
|
|
@@ -6756,6 +7099,10 @@ var clackUi = {
|
|
|
6756
7099
|
const value2 = await clack.select({ message, options: choices });
|
|
6757
7100
|
return clack.isCancel(value2) ? void 0 : value2;
|
|
6758
7101
|
},
|
|
7102
|
+
async text(message) {
|
|
7103
|
+
const value2 = await clack.text({ message });
|
|
7104
|
+
return clack.isCancel(value2) ? void 0 : value2;
|
|
7105
|
+
},
|
|
6759
7106
|
async wait(message, done, work) {
|
|
6760
7107
|
const spinner2 = clack.spinner();
|
|
6761
7108
|
spinner2.start(message);
|
|
@@ -6790,8 +7137,8 @@ var code = await run(argv, {
|
|
|
6790
7137
|
env: readEnv(),
|
|
6791
7138
|
cwd: process.cwd(),
|
|
6792
7139
|
fetch,
|
|
6793
|
-
stdout: (
|
|
6794
|
-
stderr: (
|
|
7140
|
+
stdout: (text3) => process.stdout.write(text3),
|
|
7141
|
+
stderr: (text3) => process.stderr.write(text3),
|
|
6795
7142
|
writeBytes: (bytes) => process.stdout.write(bytes),
|
|
6796
7143
|
ui: process.stdin.isTTY && process.stderr.isTTY ? clackUi : void 0,
|
|
6797
7144
|
stdoutIsTTY: process.stdout.isTTY === true,
|