@putdotio/cli 1.0.12 → 1.2.0
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/README.md +32 -5
- package/dist/bin.mjs +116 -11
- package/dist/index.mjs +2 -2
- package/dist/{metadata-Bf6lftdl.mjs → metadata-DHsl0QOK.mjs} +712 -190
- package/package.json +8 -8
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { Clock, Config, Console, Context, Data, Duration, Effect, Fiber, Layer, Option, Schema } from "effect";
|
|
1
|
+
import { Cause, Clock, Config, Console, Context, Data, Duration, Effect, Fiber, Layer, Option, Queue, Schema } from "effect";
|
|
2
2
|
import i18next from "i18next";
|
|
3
|
-
import { Command,
|
|
4
|
-
import * as Terminal from "
|
|
5
|
-
import { DEFAULT_PUTIO_API_BASE_URL, DEFAULT_PUTIO_WEB_APP_URL, DownloadLinksCreateInputSchema, TransferAddInputSchema, createPutioSdkEffectClient,
|
|
3
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
4
|
+
import * as Terminal from "effect/Terminal";
|
|
5
|
+
import { DEFAULT_PUTIO_API_BASE_URL, DEFAULT_PUTIO_WEB_APP_URL, DownloadLinksCreateInputSchema, TransferAddInputSchema, createPutioSdkEffectClient, makePutioSdkLiveLayer } from "@putdotio/sdk";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
import { homedir, hostname } from "node:os";
|
|
8
8
|
import { dirname, join } from "node:path";
|
|
9
|
+
import * as SchemaAST from "effect/SchemaAST";
|
|
9
10
|
import { LocalizedError, createLocalizeError } from "@putdotio/sdk/utilities";
|
|
10
11
|
import Table from "cli-table3";
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import { SystemError } from "@effect/platform/Error";
|
|
12
|
+
import * as FileSystem from "effect/FileSystem";
|
|
13
|
+
import { PlatformError, SystemError } from "effect/PlatformError";
|
|
14
14
|
//#region package.json
|
|
15
15
|
var name = "@putdotio/cli";
|
|
16
|
-
var version = "1.0
|
|
16
|
+
var version = "1.2.0";
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/i18n/translate.ts
|
|
19
19
|
const resources = { en: { translation: {
|
|
@@ -59,13 +59,24 @@ const resources = { en: { translation: {
|
|
|
59
59
|
title: "┌( ಠ‿ಠ)┘ welcome!",
|
|
60
60
|
waiting: "Waiting for authorization..."
|
|
61
61
|
},
|
|
62
|
-
logout: {
|
|
62
|
+
logout: {
|
|
63
|
+
cleared: "cleared persisted auth state at {{configPath}}",
|
|
64
|
+
notFound: "no persisted auth state was configured at {{configPath}}"
|
|
65
|
+
},
|
|
63
66
|
preview: { browserOpened: "opened automatically in your browser" },
|
|
67
|
+
profiles: {
|
|
68
|
+
empty: "no auth profiles configured",
|
|
69
|
+
notFound: "auth profile {{profile}} was not configured",
|
|
70
|
+
removed: "removed auth profile {{profile}}",
|
|
71
|
+
used: "using auth profile {{profile}}"
|
|
72
|
+
},
|
|
64
73
|
status: {
|
|
65
74
|
apiBaseUrl: "api base url: {{value}}",
|
|
66
75
|
authenticatedNo: "authenticated: no",
|
|
67
76
|
authenticatedYes: "authenticated: yes",
|
|
68
77
|
configPath: "config path: {{value}}",
|
|
78
|
+
defaultProfile: "default profile: {{value}}",
|
|
79
|
+
profile: "profile: {{value}}",
|
|
69
80
|
source: "source: {{value}}",
|
|
70
81
|
unknown: "unknown"
|
|
71
82
|
},
|
|
@@ -73,6 +84,7 @@ const resources = { en: { translation: {
|
|
|
73
84
|
apiBaseUrl: "api base url {{value}}",
|
|
74
85
|
browserOpened: "browser opened {{value}}",
|
|
75
86
|
configPath: "config path {{value}}",
|
|
87
|
+
profile: "profile {{value}}",
|
|
76
88
|
savedToken: "authenticated and saved token"
|
|
77
89
|
}
|
|
78
90
|
},
|
|
@@ -229,6 +241,9 @@ const resources = { en: { translation: {
|
|
|
229
241
|
authLogin: "Authorize the CLI through the put.io device-link flow and persist the resulting token.",
|
|
230
242
|
authLogout: "Remove the persisted CLI auth state.",
|
|
231
243
|
authPreview: "Render the auth screen locally without requesting a real device code.",
|
|
244
|
+
authProfilesList: "List configured auth profiles without exposing token material.",
|
|
245
|
+
authProfilesRemove: "Remove a persisted auth profile.",
|
|
246
|
+
authProfilesUse: "Set the default persisted auth profile.",
|
|
232
247
|
authStatus: "Report the currently resolved auth state.",
|
|
233
248
|
brand: "Render the put.io CLI brand mark without making any API calls.",
|
|
234
249
|
describe: "Print machine-readable CLI metadata for agents and scripts.",
|
|
@@ -358,7 +373,15 @@ const createTranslator = (locale = "en") => {
|
|
|
358
373
|
const translate = createTranslator();
|
|
359
374
|
//#endregion
|
|
360
375
|
//#region src/internal/agent-dx.ts
|
|
361
|
-
const AgentDxCategoryNameSchema = Schema.
|
|
376
|
+
const AgentDxCategoryNameSchema = Schema.Literals([
|
|
377
|
+
"machineReadableOutput",
|
|
378
|
+
"rawPayloadInput",
|
|
379
|
+
"schemaIntrospection",
|
|
380
|
+
"contextWindowDiscipline",
|
|
381
|
+
"inputHardening",
|
|
382
|
+
"safetyRails",
|
|
383
|
+
"agentKnowledgePackaging"
|
|
384
|
+
]);
|
|
362
385
|
const AgentDxDimensionSchema = Schema.Struct({
|
|
363
386
|
maxScore: Schema.Literal(3),
|
|
364
387
|
name: AgentDxCategoryNameSchema,
|
|
@@ -450,12 +473,13 @@ const PUTIO_CLI_APP_ID = "8993";
|
|
|
450
473
|
const ENV_CLI_CLIENT_NAME = "PUTIO_CLI_CLIENT_NAME";
|
|
451
474
|
const ENV_CLI_WEB_APP_URL = "PUTIO_CLI_WEB_APP_URL";
|
|
452
475
|
const ENV_CLI_CONFIG_PATH = "PUTIO_CLI_CONFIG_PATH";
|
|
476
|
+
const ENV_CLI_PROFILE = "PUTIO_CLI_PROFILE";
|
|
453
477
|
const ENV_API_BASE_URL = "PUTIO_CLI_API_BASE_URL";
|
|
454
478
|
const ENV_CLI_TOKEN = "PUTIO_CLI_TOKEN";
|
|
455
479
|
const ENV_XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
|
|
456
480
|
//#endregion
|
|
457
481
|
//#region src/internal/runtime.ts
|
|
458
|
-
var CliRuntime = class extends Context.
|
|
482
|
+
var CliRuntime = class extends Context.Service()("@putdotio/cli/CliRuntime") {};
|
|
459
483
|
const openExternalWithPlatform = (platform, url) => {
|
|
460
484
|
const command = platform === "darwin" ? {
|
|
461
485
|
file: "open",
|
|
@@ -506,6 +530,20 @@ const makeCliRuntime = (options = {}) => {
|
|
|
506
530
|
setExitCode: (code) => Effect.sync(() => {
|
|
507
531
|
process.exitCode = code;
|
|
508
532
|
}),
|
|
533
|
+
writeStdout: (message) => Effect.sync(() => {
|
|
534
|
+
if (options.writeStdout) {
|
|
535
|
+
options.writeStdout(message);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
process.stdout.write(message);
|
|
539
|
+
}),
|
|
540
|
+
writeStderr: (message) => Effect.sync(() => {
|
|
541
|
+
if (options.writeStderr) {
|
|
542
|
+
options.writeStderr(message);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
process.stderr.write(message);
|
|
546
|
+
}),
|
|
509
547
|
openExternal: (url) => Effect.sync(() => openExternalWithPlatform(platform, url)),
|
|
510
548
|
startSpinner: (message) => Effect.sync(() => {
|
|
511
549
|
let frameIndex = 0;
|
|
@@ -532,27 +570,28 @@ const makeCliRuntime = (options = {}) => {
|
|
|
532
570
|
const CliRuntimeLive = Layer.sync(CliRuntime, () => makeCliRuntime());
|
|
533
571
|
//#endregion
|
|
534
572
|
//#region src/internal/config.ts
|
|
535
|
-
const NonEmptyStringSchema$
|
|
536
|
-
const UrlStringSchema = NonEmptyStringSchema$
|
|
573
|
+
const NonEmptyStringSchema$3 = Schema.String.check(Schema.isNonEmpty());
|
|
574
|
+
const UrlStringSchema = NonEmptyStringSchema$3.pipe(Schema.check(Schema.makeFilter((value) => {
|
|
537
575
|
try {
|
|
538
576
|
new URL(value);
|
|
539
|
-
return
|
|
577
|
+
return;
|
|
540
578
|
} catch {
|
|
541
|
-
return
|
|
579
|
+
return "Expected a valid absolute URL";
|
|
542
580
|
}
|
|
543
|
-
}
|
|
581
|
+
})));
|
|
544
582
|
const PutioCliAuthFlowConfigSchema = Schema.Struct({
|
|
545
|
-
appId: NonEmptyStringSchema$
|
|
546
|
-
clientName: NonEmptyStringSchema$
|
|
583
|
+
appId: NonEmptyStringSchema$3,
|
|
584
|
+
clientName: NonEmptyStringSchema$3,
|
|
547
585
|
webAppUrl: UrlStringSchema
|
|
548
586
|
});
|
|
549
587
|
const CliRuntimeConfigSchema = Schema.Struct({
|
|
550
588
|
apiBaseUrl: UrlStringSchema,
|
|
551
|
-
configPath: NonEmptyStringSchema$
|
|
552
|
-
|
|
589
|
+
configPath: NonEmptyStringSchema$3,
|
|
590
|
+
profile: Schema.optional(NonEmptyStringSchema$3),
|
|
591
|
+
token: Schema.optional(NonEmptyStringSchema$3)
|
|
553
592
|
});
|
|
554
593
|
var CliConfigError = class extends Data.TaggedError("CliConfigError") {};
|
|
555
|
-
var CliConfig = class extends Context.
|
|
594
|
+
var CliConfig = class extends Context.Service()("@putdotio/cli/CliConfig") {};
|
|
556
595
|
const optionalTrimmedString = (name) => Config.option(Config.string(name)).pipe(Config.map((value) => Option.flatMap(value, (raw) => {
|
|
557
596
|
const trimmed = raw.trim();
|
|
558
597
|
return trimmed.length > 0 ? Option.some(trimmed) : Option.none();
|
|
@@ -579,6 +618,7 @@ const makeCliConfig = (runtime) => ({
|
|
|
579
618
|
const homePath = yield* runtime.getHomeDirectory;
|
|
580
619
|
const apiBaseUrl = yield* optionalTrimmedString(ENV_API_BASE_URL).pipe(Config.map((value) => Option.getOrElse(value, () => DEFAULT_PUTIO_API_BASE_URL)));
|
|
581
620
|
const token = yield* optionalTrimmedString(ENV_CLI_TOKEN);
|
|
621
|
+
const profile = yield* optionalTrimmedString(ENV_CLI_PROFILE);
|
|
582
622
|
const explicitConfigPath = yield* optionalTrimmedString(ENV_CLI_CONFIG_PATH);
|
|
583
623
|
const xdgConfigHome = yield* optionalTrimmedString(ENV_XDG_CONFIG_HOME);
|
|
584
624
|
return yield* Effect.try({
|
|
@@ -590,6 +630,7 @@ const makeCliConfig = (runtime) => ({
|
|
|
590
630
|
homePath,
|
|
591
631
|
joinPath: runtime.joinPath
|
|
592
632
|
}),
|
|
633
|
+
profile: Option.getOrUndefined(profile),
|
|
593
634
|
token: Option.getOrUndefined(token)
|
|
594
635
|
}),
|
|
595
636
|
catch: mapCliConfigError("Unable to resolve the CLI runtime configuration.")
|
|
@@ -620,24 +661,60 @@ const waitForDeviceToken = (options) => Effect.gen(function* () {
|
|
|
620
661
|
}
|
|
621
662
|
});
|
|
622
663
|
//#endregion
|
|
664
|
+
//#region src/internal/auth-profile.ts
|
|
665
|
+
const AUTH_PROFILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
666
|
+
const AUTH_PROFILE_NAME_DESCRIPTION = "Profile names must start with a letter or number and may contain letters, numbers, dots, underscores, or hyphens.";
|
|
667
|
+
const normalizeAuthProfileName = (value) => {
|
|
668
|
+
const trimmed = value.trim();
|
|
669
|
+
return AUTH_PROFILE_NAME_PATTERN.test(trimmed) ? trimmed : null;
|
|
670
|
+
};
|
|
671
|
+
//#endregion
|
|
623
672
|
//#region src/internal/command-specs.ts
|
|
624
|
-
const NonEmptyStringSchema$
|
|
625
|
-
const OutputModeSchema = Schema.
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
673
|
+
const NonEmptyStringSchema$2 = Schema.String.check(Schema.isNonEmpty());
|
|
674
|
+
const OutputModeSchema = Schema.Literals([
|
|
675
|
+
"json",
|
|
676
|
+
"text",
|
|
677
|
+
"ndjson"
|
|
678
|
+
]);
|
|
679
|
+
const InternalRendererSchema = Schema.Literals([
|
|
680
|
+
"json",
|
|
681
|
+
"terminal",
|
|
682
|
+
"ndjson"
|
|
683
|
+
]);
|
|
684
|
+
const CommandKindSchema = Schema.Literals([
|
|
685
|
+
"utility",
|
|
686
|
+
"auth",
|
|
687
|
+
"read",
|
|
688
|
+
"write"
|
|
689
|
+
]);
|
|
690
|
+
const CommandOptionTypeSchema = Schema.Literals([
|
|
691
|
+
"string",
|
|
692
|
+
"integer",
|
|
693
|
+
"boolean",
|
|
694
|
+
"enum"
|
|
695
|
+
]);
|
|
696
|
+
const JsonPrimitiveKindSchema = Schema.Literals([
|
|
697
|
+
"string",
|
|
698
|
+
"integer",
|
|
699
|
+
"boolean",
|
|
700
|
+
"null"
|
|
701
|
+
]);
|
|
630
702
|
const JsonScalarSchema = Schema.Struct({ kind: JsonPrimitiveKindSchema });
|
|
631
|
-
const JsonEnumValueSchema = Schema.Union(
|
|
703
|
+
const JsonEnumValueSchema = Schema.Union([
|
|
704
|
+
Schema.String,
|
|
705
|
+
Schema.Number,
|
|
706
|
+
Schema.Boolean,
|
|
707
|
+
Schema.Null
|
|
708
|
+
]);
|
|
632
709
|
const JsonPropertySchema = Schema.Struct({
|
|
633
|
-
name: NonEmptyStringSchema$
|
|
710
|
+
name: NonEmptyStringSchema$2,
|
|
634
711
|
required: Schema.Boolean,
|
|
635
712
|
schema: Schema.suspend(() => CommandJsonShapeSchema)
|
|
636
713
|
});
|
|
637
714
|
const JsonObjectSchema = Schema.Struct({
|
|
638
715
|
kind: Schema.Literal("object"),
|
|
639
716
|
properties: Schema.Array(JsonPropertySchema),
|
|
640
|
-
rules: Schema.optional(Schema.Array(NonEmptyStringSchema$
|
|
717
|
+
rules: Schema.optional(Schema.Array(NonEmptyStringSchema$2))
|
|
641
718
|
});
|
|
642
719
|
const JsonArraySchema = Schema.Struct({
|
|
643
720
|
kind: Schema.Literal("array"),
|
|
@@ -647,17 +724,34 @@ const JsonEnumSchema = Schema.Struct({
|
|
|
647
724
|
kind: Schema.Literal("enum"),
|
|
648
725
|
values: Schema.Array(JsonEnumValueSchema)
|
|
649
726
|
});
|
|
650
|
-
const CommandJsonShapeSchema = Schema.Union(
|
|
727
|
+
const CommandJsonShapeSchema = Schema.Union([
|
|
728
|
+
JsonScalarSchema,
|
|
729
|
+
JsonEnumSchema,
|
|
730
|
+
JsonObjectSchema,
|
|
731
|
+
JsonArraySchema
|
|
732
|
+
]);
|
|
651
733
|
const CommandOptionSchema = Schema.Struct({
|
|
652
|
-
choices: Schema.optional(Schema.Array(NonEmptyStringSchema$
|
|
653
|
-
defaultValue: Schema.optional(Schema.Union(
|
|
654
|
-
|
|
655
|
-
|
|
734
|
+
choices: Schema.optional(Schema.Array(NonEmptyStringSchema$2)),
|
|
735
|
+
defaultValue: Schema.optional(Schema.Union([
|
|
736
|
+
NonEmptyStringSchema$2,
|
|
737
|
+
Schema.Number,
|
|
738
|
+
Schema.Boolean
|
|
739
|
+
])),
|
|
740
|
+
description: Schema.optional(NonEmptyStringSchema$2),
|
|
741
|
+
name: NonEmptyStringSchema$2,
|
|
656
742
|
repeated: Schema.Boolean,
|
|
657
743
|
required: Schema.Boolean,
|
|
658
744
|
type: CommandOptionTypeSchema
|
|
659
745
|
});
|
|
746
|
+
const CommandArgumentSchema = Schema.Struct({
|
|
747
|
+
choices: Schema.optional(Schema.Array(NonEmptyStringSchema$2)),
|
|
748
|
+
description: Schema.optional(NonEmptyStringSchema$2),
|
|
749
|
+
name: NonEmptyStringSchema$2,
|
|
750
|
+
required: Schema.Boolean,
|
|
751
|
+
type: CommandOptionTypeSchema
|
|
752
|
+
});
|
|
660
753
|
const CommandInputSchema = Schema.Struct({
|
|
754
|
+
arguments: Schema.optional(Schema.Array(CommandArgumentSchema)),
|
|
661
755
|
flags: Schema.Array(CommandOptionSchema),
|
|
662
756
|
json: Schema.optional(CommandJsonShapeSchema)
|
|
663
757
|
});
|
|
@@ -671,10 +765,10 @@ const CommandAuthSchema = Schema.Struct({ required: Schema.Boolean });
|
|
|
671
765
|
const CommandDescriptorSchema = Schema.Struct({
|
|
672
766
|
auth: CommandAuthSchema,
|
|
673
767
|
capabilities: CommandCapabilitiesSchema,
|
|
674
|
-
command: NonEmptyStringSchema$
|
|
768
|
+
command: NonEmptyStringSchema$2,
|
|
675
769
|
input: CommandInputSchema,
|
|
676
770
|
kind: CommandKindSchema,
|
|
677
|
-
purpose: NonEmptyStringSchema$
|
|
771
|
+
purpose: NonEmptyStringSchema$2
|
|
678
772
|
});
|
|
679
773
|
const CliOutputContractSchema = Schema.Struct({
|
|
680
774
|
defaultInteractive: Schema.Literal("text"),
|
|
@@ -791,6 +885,12 @@ const stringFlag = (name, options = {}) => ({
|
|
|
791
885
|
required: options.required ?? false,
|
|
792
886
|
type: "string"
|
|
793
887
|
});
|
|
888
|
+
const stringArgument = (name, options = {}) => ({
|
|
889
|
+
description: options.description,
|
|
890
|
+
name,
|
|
891
|
+
required: options.required ?? true,
|
|
892
|
+
type: "string"
|
|
893
|
+
});
|
|
794
894
|
const repeatedStringFlag = (name, options = {}) => ({
|
|
795
895
|
description: options.description,
|
|
796
896
|
name,
|
|
@@ -808,31 +908,38 @@ const enumFlag = (name, choices, options = {}) => ({
|
|
|
808
908
|
});
|
|
809
909
|
const unwrapSchemaAst = (ast) => {
|
|
810
910
|
let current = ast;
|
|
811
|
-
while (current &&
|
|
911
|
+
while (current && current._tag === "Suspend") current = current.thunk();
|
|
812
912
|
return current;
|
|
813
913
|
};
|
|
814
914
|
const schemaAstToJsonShape = (ast) => {
|
|
815
915
|
const current = unwrapSchemaAst(ast);
|
|
816
916
|
switch (current?._tag) {
|
|
817
|
-
case "
|
|
818
|
-
case "
|
|
819
|
-
case "
|
|
820
|
-
case "
|
|
821
|
-
case "
|
|
822
|
-
case "
|
|
823
|
-
case "Literal":
|
|
824
|
-
|
|
825
|
-
|
|
917
|
+
case "String": return stringShape();
|
|
918
|
+
case "Number": return integerShape();
|
|
919
|
+
case "Boolean": return booleanShape();
|
|
920
|
+
case "Undefined":
|
|
921
|
+
case "Void":
|
|
922
|
+
case "Null": return nullShape();
|
|
923
|
+
case "Literal": {
|
|
924
|
+
const literal = current.literal ?? null;
|
|
925
|
+
if (typeof literal === "bigint") throw new Error("BigInt literals are not supported in CLI json metadata.");
|
|
926
|
+
return enumShape([literal]);
|
|
927
|
+
}
|
|
928
|
+
case "Arrays": {
|
|
929
|
+
const item = current.rest[0] ?? current.elements[0];
|
|
826
930
|
if (!item) throw new Error("Unable to derive an array item schema from an empty tuple AST.");
|
|
827
931
|
return arrayShape(schemaAstToJsonShape(item));
|
|
828
932
|
}
|
|
829
|
-
case "
|
|
933
|
+
case "Objects": return objectShape(current.propertySignatures.map((propertySignature) => property(String(propertySignature.name), schemaAstToJsonShape(propertySignature.type), !SchemaAST.isOptional(propertySignature.type))));
|
|
830
934
|
case "Union": {
|
|
831
|
-
const definedTypes =
|
|
935
|
+
const definedTypes = current.types.filter((type) => unwrapSchemaAst(type)?._tag !== "Undefined");
|
|
832
936
|
const enumValues = definedTypes.flatMap((type) => {
|
|
833
937
|
const unwrapped = unwrapSchemaAst(type);
|
|
834
|
-
if (unwrapped?._tag === "Literal")
|
|
835
|
-
|
|
938
|
+
if (unwrapped?._tag === "Literal") {
|
|
939
|
+
const literal = unwrapped.literal ?? null;
|
|
940
|
+
return typeof literal === "bigint" ? [] : [literal];
|
|
941
|
+
}
|
|
942
|
+
if (unwrapped?._tag === "Null" || unwrapped?._tag === "Void") return [null];
|
|
836
943
|
return [];
|
|
837
944
|
});
|
|
838
945
|
if (enumValues.length === definedTypes.length && enumValues.length > 0) return enumShape(enumValues);
|
|
@@ -1272,6 +1379,7 @@ const normalizeOutputMode = (output, isInteractiveTerminal = true) => {
|
|
|
1272
1379
|
if (output === "text") return "terminal";
|
|
1273
1380
|
return isInteractiveTerminal ? "terminal" : "json";
|
|
1274
1381
|
};
|
|
1382
|
+
const normalizeRequestedOrResolvedOutputMode = (output, isInteractiveTerminal = true) => output === "terminal" ? "terminal" : normalizeOutputMode(output, isInteractiveTerminal);
|
|
1275
1383
|
const detectOutputModeFromArgv = (argv, isInteractiveTerminal = true) => {
|
|
1276
1384
|
for (let index = 0; index < argv.length; index += 1) {
|
|
1277
1385
|
const argument = argv[index];
|
|
@@ -1399,56 +1507,100 @@ const formatCliError = (error) => {
|
|
|
1399
1507
|
const formatCliErrorJson = (error) => {
|
|
1400
1508
|
return renderJson(toCliErrorJson(isLocalizedError(error) ? error : localizeCliError(error)));
|
|
1401
1509
|
};
|
|
1402
|
-
var CliOutput = class extends Context.
|
|
1510
|
+
var CliOutput = class extends Context.Service()("@putdotio/cli/CliOutput") {};
|
|
1403
1511
|
const makeCliOutput = (runtime) => ({
|
|
1404
|
-
formatError: (error, output) => isStructuredOutputMode(
|
|
1512
|
+
formatError: (error, output) => isStructuredOutputMode(normalizeRequestedOrResolvedOutputMode(output, runtime.isInteractiveTerminal)) ? formatCliErrorJson(error) : formatCliError(error),
|
|
1405
1513
|
error: (message) => Console.error(sanitizeTerminalText(message)),
|
|
1406
|
-
write: (value, output, renderTerminalValue) =>
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
}
|
|
1412
|
-
})())
|
|
1514
|
+
write: (value, output, renderTerminalValue) => {
|
|
1515
|
+
const outputMode = normalizeOutputMode(output, runtime.isInteractiveTerminal);
|
|
1516
|
+
const rendered = outputMode === "terminal" ? renderTerminal(value, renderTerminalValue) : outputMode === "ndjson" ? renderNdjson(value) : renderJson(value);
|
|
1517
|
+
return runtime.writeStdout(`${rendered}\n`);
|
|
1518
|
+
}
|
|
1413
1519
|
});
|
|
1414
1520
|
const CliOutputLive = Layer.effect(CliOutput, Effect.map(CliRuntime, makeCliOutput));
|
|
1415
1521
|
const writeOutput = (value, output, renderTerminalValue) => Effect.flatMap(CliOutput, (cliOutput) => cliOutput.write(value, output, renderTerminalValue));
|
|
1416
1522
|
//#endregion
|
|
1417
1523
|
//#region src/internal/sdk.ts
|
|
1418
1524
|
const sdk = createPutioSdkEffectClient();
|
|
1419
|
-
var CliSdk = class extends Context.
|
|
1525
|
+
var CliSdk = class extends Context.Service()("@putdotio/cli/CliSdk") {};
|
|
1420
1526
|
const makeCliSdk = () => ({
|
|
1421
1527
|
client: sdk,
|
|
1422
|
-
provide: (config, program) => program.pipe(Effect.provide(
|
|
1528
|
+
provide: (config, program) => program.pipe(Effect.provide(makePutioSdkLiveLayer({
|
|
1423
1529
|
accessToken: config.token,
|
|
1424
1530
|
baseUrl: config.apiBaseUrl
|
|
1425
1531
|
})))
|
|
1426
1532
|
});
|
|
1427
|
-
const CliSdkLive = Layer.
|
|
1533
|
+
const CliSdkLive = Layer.succeed(CliSdk, makeCliSdk());
|
|
1428
1534
|
const provideSdk = (config, program) => Effect.flatMap(CliSdk, (cliSdk) => cliSdk.provide(config, program));
|
|
1429
1535
|
//#endregion
|
|
1430
1536
|
//#region src/internal/state.ts
|
|
1431
|
-
const NonEmptyStringSchema$
|
|
1537
|
+
const NonEmptyStringSchema$1 = Schema.String.check(Schema.isNonEmpty());
|
|
1538
|
+
const PutioCliProfileConfigSchema = Schema.Struct({
|
|
1539
|
+
api_base_url: Schema.optional(NonEmptyStringSchema$1),
|
|
1540
|
+
auth_token: Schema.optional(NonEmptyStringSchema$1)
|
|
1541
|
+
});
|
|
1432
1542
|
const PutioCliConfigSchema = Schema.Struct({
|
|
1433
|
-
api_base_url: NonEmptyStringSchema$
|
|
1434
|
-
auth_token: Schema.optional(NonEmptyStringSchema$
|
|
1543
|
+
api_base_url: NonEmptyStringSchema$1,
|
|
1544
|
+
auth_token: Schema.optional(NonEmptyStringSchema$1),
|
|
1545
|
+
default_profile: Schema.optional(NonEmptyStringSchema$1),
|
|
1546
|
+
profiles: Schema.optional(Schema.Record(Schema.String, PutioCliProfileConfigSchema))
|
|
1435
1547
|
});
|
|
1436
1548
|
const ResolvedAuthStateSchema = Schema.Struct({
|
|
1437
|
-
apiBaseUrl: NonEmptyStringSchema$
|
|
1438
|
-
configPath: NonEmptyStringSchema$
|
|
1439
|
-
|
|
1440
|
-
|
|
1549
|
+
apiBaseUrl: NonEmptyStringSchema$1,
|
|
1550
|
+
configPath: NonEmptyStringSchema$1,
|
|
1551
|
+
profile: Schema.NullOr(NonEmptyStringSchema$1),
|
|
1552
|
+
source: Schema.Literals([
|
|
1553
|
+
"env",
|
|
1554
|
+
"config",
|
|
1555
|
+
"profile"
|
|
1556
|
+
]),
|
|
1557
|
+
token: NonEmptyStringSchema$1
|
|
1441
1558
|
});
|
|
1442
1559
|
const AuthStatusSchema = Schema.Struct({
|
|
1443
|
-
apiBaseUrl: NonEmptyStringSchema$
|
|
1560
|
+
apiBaseUrl: NonEmptyStringSchema$1,
|
|
1561
|
+
authenticated: Schema.Boolean,
|
|
1562
|
+
configPath: NonEmptyStringSchema$1,
|
|
1563
|
+
defaultProfile: Schema.NullOr(NonEmptyStringSchema$1),
|
|
1564
|
+
profile: Schema.NullOr(NonEmptyStringSchema$1),
|
|
1565
|
+
source: Schema.NullOr(Schema.Literals([
|
|
1566
|
+
"env",
|
|
1567
|
+
"config",
|
|
1568
|
+
"profile"
|
|
1569
|
+
]))
|
|
1570
|
+
});
|
|
1571
|
+
const AuthProfileSummarySchema = Schema.Struct({
|
|
1572
|
+
apiBaseUrl: NonEmptyStringSchema$1,
|
|
1444
1573
|
authenticated: Schema.Boolean,
|
|
1445
|
-
|
|
1446
|
-
|
|
1574
|
+
current: Schema.Boolean,
|
|
1575
|
+
name: NonEmptyStringSchema$1
|
|
1576
|
+
});
|
|
1577
|
+
const AuthProfileListSchema = Schema.Struct({
|
|
1578
|
+
configPath: NonEmptyStringSchema$1,
|
|
1579
|
+
defaultProfile: Schema.NullOr(NonEmptyStringSchema$1),
|
|
1580
|
+
profiles: Schema.Array(AuthProfileSummarySchema)
|
|
1447
1581
|
});
|
|
1448
1582
|
var AuthStateError = class extends Data.TaggedError("AuthStateError") {};
|
|
1449
|
-
var CliState = class extends Context.
|
|
1583
|
+
var CliState = class extends Context.Service()("@putdotio/cli/CliState") {};
|
|
1450
1584
|
const decodePersistedConfig = Schema.decodeUnknownSync(PutioCliConfigSchema);
|
|
1451
1585
|
const mapFileSystemError = (error, message) => error instanceof AuthStateError ? error : new AuthStateError({ message });
|
|
1586
|
+
const resolveAuthRuntimeConfig = () => resolveCliRuntimeConfig().pipe(Effect.mapError((error) => new AuthStateError({ message: error.message })));
|
|
1587
|
+
const profileErrorMessage = (profile) => `Invalid auth profile \`${profile}\`. Profile names must start with a letter or number and may contain letters, numbers, dots, underscores, or hyphens.`;
|
|
1588
|
+
const validateProfileName = (profile) => {
|
|
1589
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
1590
|
+
if (normalized === null) throw new AuthStateError({ message: profileErrorMessage(profile) });
|
|
1591
|
+
return normalized;
|
|
1592
|
+
};
|
|
1593
|
+
const validateOptionalProfileName = (profile) => profile === void 0 ? void 0 : validateProfileName(profile);
|
|
1594
|
+
const validateProfileNameEffect = (profile) => Effect.try({
|
|
1595
|
+
try: () => validateProfileName(profile),
|
|
1596
|
+
catch: (error) => error instanceof AuthStateError ? error : new AuthStateError({ message: profileErrorMessage(profile) })
|
|
1597
|
+
});
|
|
1598
|
+
const validateOptionalProfileNameEffect = (profile) => profile === void 0 ? Effect.succeed(void 0) : validateProfileNameEffect(profile);
|
|
1599
|
+
const validatePersistedConfig = (state) => {
|
|
1600
|
+
validateOptionalProfileName(state.default_profile);
|
|
1601
|
+
for (const name of Object.keys(state.profiles ?? {})) validateProfileName(name);
|
|
1602
|
+
return state;
|
|
1603
|
+
};
|
|
1452
1604
|
const parsePersistedConfig = (raw) => {
|
|
1453
1605
|
let value;
|
|
1454
1606
|
try {
|
|
@@ -1457,153 +1609,363 @@ const parsePersistedConfig = (raw) => {
|
|
|
1457
1609
|
throw new AuthStateError({ message: "Stored CLI config is not valid JSON." });
|
|
1458
1610
|
}
|
|
1459
1611
|
try {
|
|
1460
|
-
return decodePersistedConfig(value);
|
|
1461
|
-
} catch {
|
|
1612
|
+
return validatePersistedConfig(decodePersistedConfig(value));
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
if (error instanceof AuthStateError) throw error;
|
|
1462
1615
|
throw new AuthStateError({ message: "Stored CLI config does not match the expected schema." });
|
|
1463
1616
|
}
|
|
1464
1617
|
};
|
|
1618
|
+
const profileConfigApiBaseUrl = (state, profile) => profile.api_base_url ?? state.api_base_url;
|
|
1619
|
+
const selectProfileNameEffect = (input) => Effect.gen(function* () {
|
|
1620
|
+
const explicitProfile = yield* validateOptionalProfileNameEffect(input.explicitProfile);
|
|
1621
|
+
if (explicitProfile !== void 0) return explicitProfile;
|
|
1622
|
+
const runtimeProfile = yield* validateOptionalProfileNameEffect(input.runtimeProfile);
|
|
1623
|
+
if (runtimeProfile !== void 0) return runtimeProfile;
|
|
1624
|
+
return yield* validateOptionalProfileNameEffect(input.state?.default_profile);
|
|
1625
|
+
});
|
|
1626
|
+
const shouldRemoveConfigFile = (state) => state.api_base_url === DEFAULT_PUTIO_API_BASE_URL && state.auth_token === void 0 && state.default_profile === void 0 && Object.keys(state.profiles ?? {}).length === 0;
|
|
1627
|
+
const persistConfigEffect = (effectiveConfigPath, state, message) => Effect.gen(function* () {
|
|
1628
|
+
const fs = yield* FileSystem.FileSystem;
|
|
1629
|
+
const runtime = yield* CliRuntime;
|
|
1630
|
+
if (shouldRemoveConfigFile(state)) return yield* fs.remove(effectiveConfigPath, { force: true }).pipe(Effect.mapError((error) => mapFileSystemError(error, message)));
|
|
1631
|
+
yield* fs.makeDirectory(runtime.dirname(effectiveConfigPath), { recursive: true }).pipe(Effect.mapError((error) => mapFileSystemError(error, message)));
|
|
1632
|
+
yield* fs.writeFileString(effectiveConfigPath, `${JSON.stringify(state, null, 2)}\n`).pipe(Effect.mapError((error) => mapFileSystemError(error, message)));
|
|
1633
|
+
yield* fs.chmod(effectiveConfigPath, 384).pipe(Effect.mapError((error) => mapFileSystemError(error, message)));
|
|
1634
|
+
});
|
|
1635
|
+
const makeEmptyState = (apiBaseUrl = DEFAULT_PUTIO_API_BASE_URL) => ({ api_base_url: apiBaseUrl });
|
|
1465
1636
|
const loadPersistedStateEffect = (configPath) => Effect.gen(function* () {
|
|
1466
1637
|
const fs = yield* FileSystem.FileSystem;
|
|
1467
|
-
const effectiveConfigPath = configPath ?? (yield*
|
|
1468
|
-
const rawConfig = yield* fs.readFileString(effectiveConfigPath, "utf8").pipe(Effect.catchIf((error) => error instanceof SystemError && error.reason === "NotFound", () => Effect.succeed(null)), Effect.mapError((error) => mapFileSystemError(error, `Unable to read CLI config at ${effectiveConfigPath}.`)));
|
|
1638
|
+
const effectiveConfigPath = configPath ?? (yield* resolveAuthRuntimeConfig()).configPath;
|
|
1639
|
+
const rawConfig = yield* fs.readFileString(effectiveConfigPath, "utf8").pipe(Effect.catchIf((error) => error instanceof PlatformError && error.reason instanceof SystemError && error.reason._tag === "NotFound", () => Effect.succeed(null)), Effect.mapError((error) => mapFileSystemError(error, `Unable to read CLI config at ${effectiveConfigPath}.`)));
|
|
1469
1640
|
if (rawConfig === null) return null;
|
|
1470
1641
|
return yield* Effect.try({
|
|
1471
1642
|
try: () => parsePersistedConfig(rawConfig),
|
|
1472
1643
|
catch: (error) => mapFileSystemError(error, `Unable to read CLI config at ${effectiveConfigPath}.`)
|
|
1473
1644
|
});
|
|
1474
1645
|
});
|
|
1475
|
-
const savePersistedStateEffect = (state, configPath) => Effect.gen(function* () {
|
|
1476
|
-
const
|
|
1477
|
-
const
|
|
1478
|
-
const effectiveConfigPath = configPath ?? (yield* resolveCliRuntimeConfig()).configPath;
|
|
1646
|
+
const savePersistedStateEffect = (state, configPath, selection = {}) => Effect.gen(function* () {
|
|
1647
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1648
|
+
const effectiveConfigPath = configPath ?? runtime.configPath;
|
|
1479
1649
|
const existingConfig = yield* loadPersistedStateEffect(effectiveConfigPath);
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1650
|
+
const selectedProfile = yield* selectProfileNameEffect({
|
|
1651
|
+
explicitProfile: selection.profile,
|
|
1652
|
+
runtimeProfile: runtime.profile,
|
|
1653
|
+
state: existingConfig
|
|
1654
|
+
});
|
|
1655
|
+
const persistedState = existingConfig ?? makeEmptyState(state.apiBaseUrl);
|
|
1656
|
+
if (selectedProfile) {
|
|
1657
|
+
const existingProfiles = persistedState.profiles ?? {};
|
|
1658
|
+
const existingProfile = existingProfiles[selectedProfile];
|
|
1659
|
+
const nextProfile = {
|
|
1660
|
+
api_base_url: state.apiBaseUrl ?? existingProfile?.api_base_url ?? persistedState.api_base_url,
|
|
1661
|
+
auth_token: state.token
|
|
1662
|
+
};
|
|
1663
|
+
const nextState = {
|
|
1664
|
+
...persistedState,
|
|
1665
|
+
profiles: {
|
|
1666
|
+
...existingProfiles,
|
|
1667
|
+
[selectedProfile]: nextProfile
|
|
1668
|
+
}
|
|
1669
|
+
};
|
|
1670
|
+
yield* persistConfigEffect(effectiveConfigPath, nextState, `Unable to write CLI config to ${effectiveConfigPath}.`);
|
|
1671
|
+
return {
|
|
1672
|
+
configPath: effectiveConfigPath,
|
|
1673
|
+
profile: selectedProfile,
|
|
1674
|
+
state: nextState
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
const nextState = {
|
|
1678
|
+
...persistedState,
|
|
1679
|
+
api_base_url: state.apiBaseUrl ?? persistedState.api_base_url,
|
|
1482
1680
|
auth_token: state.token
|
|
1483
1681
|
};
|
|
1484
|
-
yield*
|
|
1485
|
-
yield* fs.writeFileString(effectiveConfigPath, `${JSON.stringify(persistedState, null, 2)}\n`).pipe(Effect.mapError((error) => mapFileSystemError(error, `Unable to write CLI config to ${effectiveConfigPath}.`)));
|
|
1486
|
-
yield* fs.chmod(effectiveConfigPath, 384).pipe(Effect.mapError((error) => mapFileSystemError(error, `Unable to write CLI config to ${effectiveConfigPath}.`)));
|
|
1682
|
+
yield* persistConfigEffect(effectiveConfigPath, nextState, `Unable to write CLI config to ${effectiveConfigPath}.`);
|
|
1487
1683
|
return {
|
|
1488
1684
|
configPath: effectiveConfigPath,
|
|
1489
|
-
|
|
1685
|
+
profile: null,
|
|
1686
|
+
state: nextState
|
|
1490
1687
|
};
|
|
1491
1688
|
});
|
|
1492
|
-
const clearPersistedStateEffect = (configPath) => Effect.gen(function* () {
|
|
1493
|
-
const
|
|
1494
|
-
const
|
|
1495
|
-
const effectiveConfigPath = configPath ?? (yield* resolveCliRuntimeConfig()).configPath;
|
|
1689
|
+
const clearPersistedStateEffect = (configPath, selection = {}) => Effect.gen(function* () {
|
|
1690
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1691
|
+
const effectiveConfigPath = configPath ?? runtime.configPath;
|
|
1496
1692
|
const existingConfig = yield* loadPersistedStateEffect(effectiveConfigPath);
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1693
|
+
const selectedProfile = yield* selectProfileNameEffect({
|
|
1694
|
+
explicitProfile: selection.profile,
|
|
1695
|
+
runtimeProfile: runtime.profile,
|
|
1696
|
+
state: existingConfig
|
|
1697
|
+
});
|
|
1698
|
+
if (existingConfig === null) return {
|
|
1699
|
+
cleared: false,
|
|
1700
|
+
configPath: effectiveConfigPath,
|
|
1701
|
+
profile: selectedProfile ?? null
|
|
1702
|
+
};
|
|
1703
|
+
if (selectedProfile) {
|
|
1704
|
+
const existingProfiles = existingConfig.profiles ?? {};
|
|
1705
|
+
const existingProfile = existingProfiles[selectedProfile];
|
|
1706
|
+
if (existingProfile === void 0) return {
|
|
1707
|
+
cleared: false,
|
|
1708
|
+
configPath: effectiveConfigPath,
|
|
1709
|
+
profile: selectedProfile
|
|
1710
|
+
};
|
|
1711
|
+
const nextProfile = { api_base_url: existingProfile.api_base_url ?? existingConfig.api_base_url };
|
|
1712
|
+
yield* persistConfigEffect(effectiveConfigPath, {
|
|
1713
|
+
...existingConfig,
|
|
1714
|
+
profiles: {
|
|
1715
|
+
...existingProfiles,
|
|
1716
|
+
[selectedProfile]: nextProfile
|
|
1717
|
+
}
|
|
1718
|
+
}, `Unable to clear CLI auth state at ${effectiveConfigPath}.`);
|
|
1719
|
+
return {
|
|
1720
|
+
cleared: typeof existingProfile.auth_token === "string",
|
|
1721
|
+
configPath: effectiveConfigPath,
|
|
1722
|
+
profile: selectedProfile
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
const hadLegacyToken = typeof existingConfig.auth_token === "string";
|
|
1726
|
+
yield* persistConfigEffect(effectiveConfigPath, {
|
|
1727
|
+
...existingConfig,
|
|
1728
|
+
auth_token: void 0
|
|
1729
|
+
}, `Unable to clear CLI auth state at ${effectiveConfigPath}.`);
|
|
1730
|
+
return {
|
|
1731
|
+
cleared: hadLegacyToken,
|
|
1732
|
+
configPath: effectiveConfigPath,
|
|
1733
|
+
profile: null
|
|
1734
|
+
};
|
|
1735
|
+
});
|
|
1736
|
+
const listProfilesEffect = () => Effect.gen(function* () {
|
|
1737
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1738
|
+
const state = yield* loadPersistedStateEffect(runtime.configPath);
|
|
1739
|
+
const defaultProfile = state?.default_profile ?? null;
|
|
1740
|
+
const currentProfile = yield* selectProfileNameEffect({
|
|
1741
|
+
runtimeProfile: runtime.profile,
|
|
1742
|
+
state
|
|
1743
|
+
});
|
|
1744
|
+
const profiles = Object.entries(state?.profiles ?? {}).map(([name, profile]) => ({
|
|
1745
|
+
apiBaseUrl: profileConfigApiBaseUrl(state ?? makeEmptyState(), profile),
|
|
1746
|
+
authenticated: typeof profile.auth_token === "string",
|
|
1747
|
+
current: currentProfile === name,
|
|
1748
|
+
name
|
|
1749
|
+
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
1750
|
+
return {
|
|
1751
|
+
configPath: runtime.configPath,
|
|
1752
|
+
defaultProfile,
|
|
1753
|
+
profiles
|
|
1754
|
+
};
|
|
1755
|
+
});
|
|
1756
|
+
const getAuthStatusEffect = (selection = {}) => Effect.gen(function* () {
|
|
1757
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1507
1758
|
if (runtime.token) return {
|
|
1508
1759
|
authenticated: true,
|
|
1509
1760
|
source: "env",
|
|
1510
1761
|
apiBaseUrl: runtime.apiBaseUrl,
|
|
1511
|
-
configPath: runtime.configPath
|
|
1762
|
+
configPath: runtime.configPath,
|
|
1763
|
+
defaultProfile: null,
|
|
1764
|
+
profile: (yield* validateOptionalProfileNameEffect(selection.profile)) ?? (yield* validateOptionalProfileNameEffect(runtime.profile)) ?? null
|
|
1512
1765
|
};
|
|
1513
1766
|
const state = yield* loadPersistedStateEffect(runtime.configPath);
|
|
1767
|
+
const selectedProfile = yield* selectProfileNameEffect({
|
|
1768
|
+
explicitProfile: selection.profile,
|
|
1769
|
+
runtimeProfile: runtime.profile,
|
|
1770
|
+
state
|
|
1771
|
+
});
|
|
1772
|
+
if (selectedProfile) {
|
|
1773
|
+
if (state === null) return {
|
|
1774
|
+
authenticated: false,
|
|
1775
|
+
source: null,
|
|
1776
|
+
apiBaseUrl: runtime.apiBaseUrl,
|
|
1777
|
+
configPath: runtime.configPath,
|
|
1778
|
+
defaultProfile: null,
|
|
1779
|
+
profile: selectedProfile
|
|
1780
|
+
};
|
|
1781
|
+
const profile = state?.profiles?.[selectedProfile];
|
|
1782
|
+
return profile === void 0 || typeof profile.auth_token !== "string" ? {
|
|
1783
|
+
authenticated: false,
|
|
1784
|
+
source: null,
|
|
1785
|
+
apiBaseUrl: runtime.apiBaseUrl,
|
|
1786
|
+
configPath: runtime.configPath,
|
|
1787
|
+
defaultProfile: state?.default_profile ?? null,
|
|
1788
|
+
profile: selectedProfile
|
|
1789
|
+
} : {
|
|
1790
|
+
authenticated: true,
|
|
1791
|
+
source: "profile",
|
|
1792
|
+
apiBaseUrl: profileConfigApiBaseUrl(state, profile),
|
|
1793
|
+
configPath: runtime.configPath,
|
|
1794
|
+
defaultProfile: state.default_profile ?? null,
|
|
1795
|
+
profile: selectedProfile
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1514
1798
|
return state === null ? {
|
|
1515
1799
|
authenticated: false,
|
|
1516
1800
|
source: null,
|
|
1517
1801
|
apiBaseUrl: runtime.apiBaseUrl,
|
|
1518
|
-
configPath: runtime.configPath
|
|
1802
|
+
configPath: runtime.configPath,
|
|
1803
|
+
defaultProfile: null,
|
|
1804
|
+
profile: null
|
|
1519
1805
|
} : {
|
|
1520
|
-
authenticated: typeof state.auth_token === "string"
|
|
1806
|
+
authenticated: typeof state.auth_token === "string",
|
|
1521
1807
|
source: typeof state.auth_token === "string" ? "config" : null,
|
|
1522
1808
|
apiBaseUrl: state.api_base_url,
|
|
1523
|
-
configPath: runtime.configPath
|
|
1809
|
+
configPath: runtime.configPath,
|
|
1810
|
+
defaultProfile: state.default_profile ?? null,
|
|
1811
|
+
profile: null
|
|
1812
|
+
};
|
|
1813
|
+
});
|
|
1814
|
+
const removeProfileEffect = (profile) => Effect.gen(function* () {
|
|
1815
|
+
const profileName = yield* validateProfileNameEffect(profile);
|
|
1816
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1817
|
+
const state = yield* loadPersistedStateEffect(runtime.configPath);
|
|
1818
|
+
if (state === null || state.profiles?.[profileName] === void 0) return {
|
|
1819
|
+
configPath: runtime.configPath,
|
|
1820
|
+
profile: profileName,
|
|
1821
|
+
removed: false
|
|
1822
|
+
};
|
|
1823
|
+
const { [profileName]: _removed, ...remainingProfiles } = state.profiles;
|
|
1824
|
+
const nextState = {
|
|
1825
|
+
...state,
|
|
1826
|
+
default_profile: state.default_profile === profileName ? void 0 : state.default_profile,
|
|
1827
|
+
profiles: Object.keys(remainingProfiles).length > 0 ? remainingProfiles : void 0
|
|
1828
|
+
};
|
|
1829
|
+
yield* persistConfigEffect(runtime.configPath, nextState, `Unable to remove auth profile \`${profileName}\` at ${runtime.configPath}.`);
|
|
1830
|
+
return {
|
|
1831
|
+
configPath: runtime.configPath,
|
|
1832
|
+
profile: profileName,
|
|
1833
|
+
removed: true
|
|
1524
1834
|
};
|
|
1525
1835
|
});
|
|
1526
|
-
const resolveAuthStateEffect = () => Effect.gen(function* () {
|
|
1527
|
-
const runtime = yield*
|
|
1836
|
+
const resolveAuthStateEffect = (selection = {}) => Effect.gen(function* () {
|
|
1837
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1838
|
+
const explicitOrEnvProfile = (yield* validateOptionalProfileNameEffect(selection.profile)) ?? (yield* validateOptionalProfileNameEffect(runtime.profile)) ?? null;
|
|
1528
1839
|
if (runtime.token) return {
|
|
1529
1840
|
token: runtime.token,
|
|
1530
1841
|
apiBaseUrl: runtime.apiBaseUrl,
|
|
1531
1842
|
source: "env",
|
|
1532
|
-
configPath: runtime.configPath
|
|
1843
|
+
configPath: runtime.configPath,
|
|
1844
|
+
profile: explicitOrEnvProfile
|
|
1533
1845
|
};
|
|
1534
1846
|
const state = yield* loadPersistedStateEffect(runtime.configPath);
|
|
1847
|
+
const selectedProfile = yield* selectProfileNameEffect({
|
|
1848
|
+
explicitProfile: selection.profile,
|
|
1849
|
+
runtimeProfile: runtime.profile,
|
|
1850
|
+
state
|
|
1851
|
+
});
|
|
1852
|
+
if (selectedProfile) {
|
|
1853
|
+
if (state === null) return yield* Effect.fail(new AuthStateError({ message: `No put.io token is configured for profile \`${selectedProfile}\`. Set PUTIO_CLI_TOKEN or run \`putio auth login --profile ${selectedProfile}\`.` }));
|
|
1854
|
+
const profile = state?.profiles?.[selectedProfile];
|
|
1855
|
+
if (profile === void 0 || typeof profile.auth_token !== "string") return yield* Effect.fail(new AuthStateError({ message: `No put.io token is configured for profile \`${selectedProfile}\`. Set PUTIO_CLI_TOKEN or run \`putio auth login --profile ${selectedProfile}\`.` }));
|
|
1856
|
+
return {
|
|
1857
|
+
token: profile.auth_token,
|
|
1858
|
+
apiBaseUrl: profileConfigApiBaseUrl(state, profile),
|
|
1859
|
+
source: "profile",
|
|
1860
|
+
configPath: runtime.configPath,
|
|
1861
|
+
profile: selectedProfile
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1535
1864
|
if (state === null || typeof state.auth_token !== "string") return yield* Effect.fail(new AuthStateError({ message: "No put.io token is configured. Set PUTIO_CLI_TOKEN or run `putio auth login`." }));
|
|
1536
1865
|
return {
|
|
1537
1866
|
token: state.auth_token,
|
|
1538
1867
|
apiBaseUrl: state.api_base_url,
|
|
1539
1868
|
source: "config",
|
|
1540
|
-
configPath: runtime.configPath
|
|
1869
|
+
configPath: runtime.configPath,
|
|
1870
|
+
profile: null
|
|
1871
|
+
};
|
|
1872
|
+
});
|
|
1873
|
+
const useProfileEffect = (profile) => Effect.gen(function* () {
|
|
1874
|
+
const profileName = yield* validateProfileNameEffect(profile);
|
|
1875
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1876
|
+
const state = yield* loadPersistedStateEffect(runtime.configPath);
|
|
1877
|
+
if (state?.profiles?.[profileName] === void 0) return yield* Effect.fail(new AuthStateError({ message: `Auth profile \`${profileName}\` does not exist. Run \`putio auth login --profile ${profileName}\` first.` }));
|
|
1878
|
+
const nextState = {
|
|
1879
|
+
...state,
|
|
1880
|
+
default_profile: profileName
|
|
1881
|
+
};
|
|
1882
|
+
yield* persistConfigEffect(runtime.configPath, nextState, `Unable to set the default auth profile at ${runtime.configPath}.`);
|
|
1883
|
+
return {
|
|
1884
|
+
configPath: runtime.configPath,
|
|
1885
|
+
profile: profileName
|
|
1541
1886
|
};
|
|
1542
1887
|
});
|
|
1543
1888
|
const makeCliState = () => ({
|
|
1544
1889
|
clearPersistedState: clearPersistedStateEffect,
|
|
1545
1890
|
getAuthStatus: getAuthStatusEffect,
|
|
1891
|
+
listProfiles: listProfilesEffect,
|
|
1546
1892
|
loadPersistedState: loadPersistedStateEffect,
|
|
1893
|
+
removeProfile: removeProfileEffect,
|
|
1547
1894
|
resolveAuthState: resolveAuthStateEffect,
|
|
1548
|
-
savePersistedState: savePersistedStateEffect
|
|
1895
|
+
savePersistedState: savePersistedStateEffect,
|
|
1896
|
+
useProfile: useProfileEffect
|
|
1549
1897
|
});
|
|
1550
1898
|
const CliStateLive = Layer.sync(CliState, makeCliState);
|
|
1551
1899
|
const loadPersistedState = (configPath) => Effect.flatMap(CliState, (state) => state.loadPersistedState(configPath));
|
|
1552
|
-
const savePersistedState = (state, configPath) => Effect.flatMap(CliState, (cliState) => cliState.savePersistedState(state, configPath));
|
|
1553
|
-
const clearPersistedState = (configPath) => Effect.flatMap(CliState, (state) => state.clearPersistedState(configPath));
|
|
1554
|
-
const getAuthStatus = () => Effect.flatMap(CliState, (state) => state.getAuthStatus());
|
|
1555
|
-
const
|
|
1900
|
+
const savePersistedState = (state, configPath, selection) => Effect.flatMap(CliState, (cliState) => cliState.savePersistedState(state, configPath, selection));
|
|
1901
|
+
const clearPersistedState = (configPath, selection) => Effect.flatMap(CliState, (state) => state.clearPersistedState(configPath, selection));
|
|
1902
|
+
const getAuthStatus = (selection) => Effect.flatMap(CliState, (state) => state.getAuthStatus(selection));
|
|
1903
|
+
const listProfiles = () => Effect.flatMap(CliState, (state) => state.listProfiles());
|
|
1904
|
+
const removeProfile = (profile) => Effect.flatMap(CliState, (state) => state.removeProfile(profile));
|
|
1905
|
+
const resolveAuthState = (selection) => Effect.flatMap(CliState, (state) => state.resolveAuthState(selection));
|
|
1906
|
+
const useProfile = (profile) => Effect.flatMap(CliState, (state) => state.useProfile(profile));
|
|
1556
1907
|
//#endregion
|
|
1557
1908
|
//#region src/internal/command.ts
|
|
1558
|
-
const outputOption =
|
|
1909
|
+
const outputOption = Flag.choice("output", [
|
|
1559
1910
|
"json",
|
|
1560
1911
|
"text",
|
|
1561
1912
|
"ndjson"
|
|
1562
|
-
]).pipe(
|
|
1563
|
-
const dryRunOption =
|
|
1564
|
-
const fieldsOption =
|
|
1565
|
-
const jsonOption =
|
|
1566
|
-
const pageAllOption =
|
|
1913
|
+
]).pipe(Flag.optional);
|
|
1914
|
+
const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withDefault(false));
|
|
1915
|
+
const fieldsOption = Flag.string("fields").pipe(Flag.optional);
|
|
1916
|
+
const jsonOption = Flag.string("json").pipe(Flag.optional);
|
|
1917
|
+
const pageAllOption = Flag.boolean("page-all").pipe(Flag.withDefault(false));
|
|
1567
1918
|
const defineBooleanOption = (name, options = {}) => {
|
|
1568
|
-
const option = options.defaultValue === void 0 ?
|
|
1919
|
+
const option = options.defaultValue === void 0 ? Flag.boolean(name) : Flag.boolean(name).pipe(Flag.withDefault(options.defaultValue));
|
|
1569
1920
|
return {
|
|
1570
1921
|
flag: booleanFlag(name, options),
|
|
1571
1922
|
option
|
|
1572
1923
|
};
|
|
1573
1924
|
};
|
|
1574
|
-
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
option
|
|
1925
|
+
function defineIntegerOption(name, options = {}) {
|
|
1926
|
+
const flag = integerFlag(name, {
|
|
1927
|
+
description: options.description,
|
|
1928
|
+
required: options.required ?? options.optional !== true
|
|
1929
|
+
});
|
|
1930
|
+
return options.optional === true ? {
|
|
1931
|
+
flag,
|
|
1932
|
+
option: Flag.integer(name).pipe(Flag.optional)
|
|
1933
|
+
} : {
|
|
1934
|
+
flag,
|
|
1935
|
+
option: Flag.integer(name)
|
|
1582
1936
|
};
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
}),
|
|
1594
|
-
option
|
|
1937
|
+
}
|
|
1938
|
+
function defineTextOption(name, options = {}) {
|
|
1939
|
+
const flag = stringFlag(name, {
|
|
1940
|
+
defaultValue: options.defaultValue,
|
|
1941
|
+
description: options.description,
|
|
1942
|
+
required: options.required ?? (options.defaultValue === void 0 && options.optional !== true)
|
|
1943
|
+
});
|
|
1944
|
+
if (options.defaultValue !== void 0) return {
|
|
1945
|
+
flag,
|
|
1946
|
+
option: Flag.string(name).pipe(Flag.withDefault(options.defaultValue))
|
|
1595
1947
|
};
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
flag
|
|
1601
|
-
|
|
1602
|
-
required: options.required ?? options.optional !== true
|
|
1603
|
-
}),
|
|
1604
|
-
option
|
|
1948
|
+
return options.optional === true ? {
|
|
1949
|
+
flag,
|
|
1950
|
+
option: Flag.string(name).pipe(Flag.optional)
|
|
1951
|
+
} : {
|
|
1952
|
+
flag,
|
|
1953
|
+
option: Flag.string(name)
|
|
1605
1954
|
};
|
|
1606
|
-
}
|
|
1955
|
+
}
|
|
1956
|
+
function defineChoiceOption(name, choices, options = {}) {
|
|
1957
|
+
const flag = enumFlag(name, choices, {
|
|
1958
|
+
description: options.description,
|
|
1959
|
+
required: options.required ?? options.optional !== true
|
|
1960
|
+
});
|
|
1961
|
+
return options.optional === true ? {
|
|
1962
|
+
flag,
|
|
1963
|
+
option: Flag.choice(name, choices).pipe(Flag.optional)
|
|
1964
|
+
} : {
|
|
1965
|
+
flag,
|
|
1966
|
+
option: Flag.choice(name, choices)
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1607
1969
|
const getOption = (option) => Option.getOrUndefined(option);
|
|
1608
1970
|
var CliCommandInputError = class extends Data.TaggedError("CliCommandInputError") {};
|
|
1609
1971
|
const PATH_TRAVERSAL_PATTERN = /(?:^|[\\/])\.\.(?:[\\/]|$)|%2e/iu;
|
|
@@ -1672,14 +2034,14 @@ const parseRepeatedIntegers = (values) => {
|
|
|
1672
2034
|
}
|
|
1673
2035
|
return Option.some(parsed);
|
|
1674
2036
|
};
|
|
1675
|
-
const parseRepeatedIntegerOption = (name) =>
|
|
2037
|
+
const parseRepeatedIntegerOption = (name) => Flag.string(name).pipe(Flag.atLeast(0), Flag.filterMap(parseRepeatedIntegers, () => `Expected \`--${name}\` values to be integers.`));
|
|
1676
2038
|
const defineRepeatedIntegerOption = (name, options = {}) => ({
|
|
1677
2039
|
flag: repeatedIntegerFlag(name, options),
|
|
1678
2040
|
option: parseRepeatedIntegerOption(name)
|
|
1679
2041
|
});
|
|
1680
2042
|
const defineRepeatedTextOption = (name, options = {}) => ({
|
|
1681
2043
|
flag: repeatedStringFlag(name, options),
|
|
1682
|
-
option:
|
|
2044
|
+
option: Flag.string(name).pipe(Flag.atLeast(0))
|
|
1683
2045
|
});
|
|
1684
2046
|
const mapInputError = (error, fallbackMessage) => error instanceof CliCommandInputError ? error : new CliCommandInputError({ message: fallbackMessage });
|
|
1685
2047
|
const decodeJsonOption = (schema, raw) => Effect.try({
|
|
@@ -1907,6 +2269,7 @@ const renderAuthLoginTerminal = (value) => [
|
|
|
1907
2269
|
].join("\n\n");
|
|
1908
2270
|
const renderAuthLoginSuccessTerminal = (value) => [renderPutioSignature(), renderPanel([
|
|
1909
2271
|
ansi.bold(translate("cli.auth.success.savedToken")),
|
|
2272
|
+
translate("cli.auth.success.profile", { value: value.profile ?? translate("cli.common.none") }),
|
|
1910
2273
|
translate("cli.auth.success.apiBaseUrl", { value: value.apiBaseUrl }),
|
|
1911
2274
|
translate("cli.auth.success.configPath", { value: value.configPath }),
|
|
1912
2275
|
translate("cli.auth.success.browserOpened", { value: value.browserOpened ? translate("cli.common.yes") : translate("cli.common.no") })
|
|
@@ -1919,42 +2282,78 @@ const renderAuthLoginSuccessTerminal = (value) => [renderPutioSignature(), rende
|
|
|
1919
2282
|
const openConfig = defineBooleanOption("open", { defaultValue: false });
|
|
1920
2283
|
const timeoutSecondsConfig$1 = defineIntegerOption("timeout-seconds", { optional: true });
|
|
1921
2284
|
const previewCodeConfig = defineTextOption("code", { defaultValue: "PUTIO1" });
|
|
2285
|
+
const profileConfig = defineTextOption("profile", {
|
|
2286
|
+
description: AUTH_PROFILE_NAME_DESCRIPTION,
|
|
2287
|
+
optional: true
|
|
2288
|
+
});
|
|
1922
2289
|
const openOption = openConfig.option;
|
|
1923
2290
|
const timeoutSecondsOption$1 = timeoutSecondsConfig$1.option;
|
|
1924
2291
|
const previewCodeOption = previewCodeConfig.option;
|
|
2292
|
+
const profileOption = profileConfig.option;
|
|
2293
|
+
const profileArgument = Argument.string("profile");
|
|
2294
|
+
const profileCommandArgument = stringArgument("profile", {
|
|
2295
|
+
description: AUTH_PROFILE_NAME_DESCRIPTION,
|
|
2296
|
+
required: true
|
|
2297
|
+
});
|
|
1925
2298
|
const waitForOpenShortcut = (url) => Effect.gen(function* () {
|
|
1926
2299
|
const runtimeService = yield* CliRuntime;
|
|
1927
2300
|
if (!runtimeService.isInteractiveTerminal) return false;
|
|
1928
|
-
const
|
|
1929
|
-
if (!(yield* terminal.isTTY)) return false;
|
|
1930
|
-
const input = yield* terminal.readInput;
|
|
2301
|
+
const input = yield* (yield* Terminal.Terminal).readInput;
|
|
1931
2302
|
while (true) {
|
|
1932
|
-
const event = yield*
|
|
2303
|
+
const event = yield* Queue.take(input);
|
|
1933
2304
|
const keyInput = Option.getOrElse(event.input, () => "").toLowerCase();
|
|
1934
2305
|
if (event.key.name === "o" || keyInput === "o") return yield* runtimeService.openExternal(url);
|
|
1935
2306
|
}
|
|
1936
|
-
}).pipe(Effect.
|
|
2307
|
+
}).pipe(Effect.catchIf(Cause.isDone, () => Effect.succeed(false)));
|
|
2308
|
+
const resolveProfileInput = (profile) => Option.match(profile, {
|
|
2309
|
+
onNone: () => void 0,
|
|
2310
|
+
onSome: (value) => {
|
|
2311
|
+
const normalized = normalizeAuthProfileName(value);
|
|
2312
|
+
if (normalized === null) throw new CliCommandInputError({ message: `Invalid auth profile \`${value}\`. ${AUTH_PROFILE_NAME_DESCRIPTION}` });
|
|
2313
|
+
return normalized;
|
|
2314
|
+
}
|
|
2315
|
+
});
|
|
2316
|
+
const validateProfileArgument = (profile) => {
|
|
2317
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
2318
|
+
if (normalized === null) throw new CliCommandInputError({ message: `Invalid auth profile \`${profile}\`. ${AUTH_PROFILE_NAME_DESCRIPTION}` });
|
|
2319
|
+
return normalized;
|
|
2320
|
+
};
|
|
1937
2321
|
const renderAuthStatus = (status) => status.authenticated ? [
|
|
1938
2322
|
translate("cli.auth.status.authenticatedYes"),
|
|
1939
2323
|
translate("cli.auth.status.source", { value: status.source ?? translate("cli.auth.status.unknown") }),
|
|
2324
|
+
translate("cli.auth.status.profile", { value: status.profile ?? translate("cli.common.none") }),
|
|
2325
|
+
translate("cli.auth.status.defaultProfile", { value: status.defaultProfile ?? translate("cli.common.none") }),
|
|
1940
2326
|
translate("cli.auth.status.apiBaseUrl", { value: status.apiBaseUrl }),
|
|
1941
2327
|
translate("cli.auth.status.configPath", { value: status.configPath })
|
|
1942
2328
|
].join("\n") : [
|
|
1943
2329
|
translate("cli.auth.status.authenticatedNo"),
|
|
2330
|
+
translate("cli.auth.status.profile", { value: status.profile ?? translate("cli.common.none") }),
|
|
2331
|
+
translate("cli.auth.status.defaultProfile", { value: status.defaultProfile ?? translate("cli.common.none") }),
|
|
1944
2332
|
translate("cli.auth.status.apiBaseUrl", { value: status.apiBaseUrl }),
|
|
1945
2333
|
translate("cli.auth.status.configPath", { value: status.configPath })
|
|
1946
2334
|
].join("\n");
|
|
1947
|
-
const authStatus = Command.make("status", {
|
|
1948
|
-
|
|
2335
|
+
const authStatus = Command.make("status", {
|
|
2336
|
+
output: outputOption,
|
|
2337
|
+
profile: profileOption
|
|
2338
|
+
}, ({ output, profile }) => Effect.gen(function* () {
|
|
2339
|
+
yield* writeOutput(yield* getAuthStatus({ profile: yield* Effect.try({
|
|
2340
|
+
try: () => resolveProfileInput(profile),
|
|
2341
|
+
catch: (error) => error
|
|
2342
|
+
}) }), getOption(output), renderAuthStatus);
|
|
1949
2343
|
}));
|
|
1950
2344
|
const authLogin = Command.make("login", {
|
|
1951
2345
|
open: openOption,
|
|
1952
2346
|
output: outputOption,
|
|
2347
|
+
profile: profileOption,
|
|
1953
2348
|
timeoutSeconds: timeoutSecondsOption$1
|
|
1954
|
-
}, ({ open, output, timeoutSeconds }) => Effect.gen(function* () {
|
|
2349
|
+
}, ({ open, output, profile, timeoutSeconds }) => Effect.gen(function* () {
|
|
1955
2350
|
const runtimeService = yield* CliRuntime;
|
|
1956
2351
|
const outputMode = normalizeOutputMode(getOption(output), runtimeService.isInteractiveTerminal);
|
|
1957
2352
|
const apiBaseUrl = (yield* resolveCliRuntimeConfig()).apiBaseUrl;
|
|
2353
|
+
const selectedProfile = yield* Effect.try({
|
|
2354
|
+
try: () => resolveProfileInput(profile),
|
|
2355
|
+
catch: (error) => error
|
|
2356
|
+
});
|
|
1958
2357
|
const timeoutMs = Option.getOrElse(timeoutSeconds, () => 120) * 1e3;
|
|
1959
2358
|
const authFlow = yield* resolveCliAuthFlowConfig();
|
|
1960
2359
|
const { code } = yield* provideSdk({ apiBaseUrl }, sdk.auth.getCode({
|
|
@@ -1973,10 +2372,10 @@ const authLogin = Command.make("login", {
|
|
|
1973
2372
|
`code: ${code}`,
|
|
1974
2373
|
translate("cli.auth.login.waiting")
|
|
1975
2374
|
].join("\n");
|
|
1976
|
-
yield* outputMode === "terminal" ? Console.log(instructionMessage) :
|
|
2375
|
+
yield* outputMode === "terminal" ? Console.log(instructionMessage) : runtimeService.writeStderr(`${instructionMessage}\n`);
|
|
1977
2376
|
const openShortcutFiber = outputMode === "terminal" && !browserOpened ? yield* Effect.forkScoped(waitForOpenShortcut(linkUrl)) : void 0;
|
|
1978
2377
|
yield* Effect.addFinalizer(() => openShortcutFiber ? Fiber.interrupt(openShortcutFiber) : Effect.void);
|
|
1979
|
-
const { configPath, state } = yield* savePersistedState({
|
|
2378
|
+
const { configPath, profile: savedProfile, state } = yield* savePersistedState({
|
|
1980
2379
|
apiBaseUrl,
|
|
1981
2380
|
token: yield* withTerminalLoader({
|
|
1982
2381
|
message: translate("cli.auth.login.waiting"),
|
|
@@ -1986,21 +2385,29 @@ const authLogin = Command.make("login", {
|
|
|
1986
2385
|
timeoutMs,
|
|
1987
2386
|
checkCodeMatch: (authCode) => provideSdk({ apiBaseUrl }, sdk.auth.checkCodeMatch(authCode))
|
|
1988
2387
|
}))
|
|
1989
|
-
});
|
|
2388
|
+
}, void 0, { profile: selectedProfile });
|
|
1990
2389
|
yield* writeOutput({
|
|
1991
|
-
apiBaseUrl: state.api_base_url,
|
|
2390
|
+
apiBaseUrl: savedProfile ? state.profiles?.[savedProfile]?.api_base_url ?? state.api_base_url : state.api_base_url,
|
|
1992
2391
|
authenticated: true,
|
|
1993
2392
|
browserOpened,
|
|
1994
2393
|
configPath,
|
|
2394
|
+
profile: savedProfile,
|
|
1995
2395
|
linkUrl
|
|
1996
2396
|
}, getOption(output), (value) => renderAuthLoginSuccessTerminal(value));
|
|
1997
2397
|
}));
|
|
1998
|
-
const authLogout = Command.make("logout", {
|
|
1999
|
-
|
|
2398
|
+
const authLogout = Command.make("logout", {
|
|
2399
|
+
output: outputOption,
|
|
2400
|
+
profile: profileOption
|
|
2401
|
+
}, ({ output, profile }) => Effect.gen(function* () {
|
|
2402
|
+
const { cleared, configPath, profile: clearedProfile } = yield* clearPersistedState(void 0, { profile: yield* Effect.try({
|
|
2403
|
+
try: () => resolveProfileInput(profile),
|
|
2404
|
+
catch: (error) => error
|
|
2405
|
+
}) });
|
|
2000
2406
|
yield* writeOutput({
|
|
2001
|
-
cleared
|
|
2002
|
-
configPath
|
|
2003
|
-
|
|
2407
|
+
cleared,
|
|
2408
|
+
configPath,
|
|
2409
|
+
profile: clearedProfile
|
|
2410
|
+
}, getOption(output), (value) => value.cleared ? translate("cli.auth.logout.cleared", { configPath: value.configPath }) : value.profile ? translate("cli.auth.profiles.notFound", { profile: value.profile }) : translate("cli.auth.logout.notFound", { configPath: value.configPath }));
|
|
2004
2411
|
}));
|
|
2005
2412
|
const authPreview = Command.make("preview", {
|
|
2006
2413
|
code: previewCodeOption,
|
|
@@ -2015,11 +2422,37 @@ const authPreview = Command.make("preview", {
|
|
|
2015
2422
|
linkUrl: buildDeviceLinkUrl(previewCode, authFlow.webAppUrl)
|
|
2016
2423
|
}, getOption(output), renderAuthLoginTerminal);
|
|
2017
2424
|
}));
|
|
2425
|
+
const authProfilesList = Command.make("list", { output: outputOption }, ({ output }) => Effect.gen(function* () {
|
|
2426
|
+
yield* writeOutput(yield* listProfiles(), getOption(output), (value) => value.profiles.length === 0 ? translate("cli.auth.profiles.empty") : value.profiles.map((profile) => [
|
|
2427
|
+
profile.current ? "*" : "-",
|
|
2428
|
+
profile.name,
|
|
2429
|
+
profile.authenticated ? translate("cli.common.yes") : translate("cli.common.no"),
|
|
2430
|
+
profile.apiBaseUrl
|
|
2431
|
+
].join(" ")).join("\n"));
|
|
2432
|
+
}));
|
|
2433
|
+
const authProfilesUse = Command.make("use", {
|
|
2434
|
+
output: outputOption,
|
|
2435
|
+
profile: profileArgument
|
|
2436
|
+
}, ({ output, profile }) => Effect.gen(function* () {
|
|
2437
|
+
yield* writeOutput(yield* useProfile(validateProfileArgument(profile)), getOption(output), (value) => translate("cli.auth.profiles.used", { profile: value.profile }));
|
|
2438
|
+
}));
|
|
2439
|
+
const authProfilesRemove = Command.make("remove", {
|
|
2440
|
+
output: outputOption,
|
|
2441
|
+
profile: profileArgument
|
|
2442
|
+
}, ({ output, profile }) => Effect.gen(function* () {
|
|
2443
|
+
yield* writeOutput(yield* removeProfile(validateProfileArgument(profile)), getOption(output), (value) => value.removed ? translate("cli.auth.profiles.removed", { profile: value.profile }) : translate("cli.auth.profiles.notFound", { profile: value.profile }));
|
|
2444
|
+
}));
|
|
2445
|
+
const authProfiles = Command.make("profiles", {}, () => Effect.void).pipe(Command.withSubcommands([
|
|
2446
|
+
authProfilesList,
|
|
2447
|
+
authProfilesUse,
|
|
2448
|
+
authProfilesRemove
|
|
2449
|
+
]));
|
|
2018
2450
|
const makeAuthCommand = () => Command.make("auth", {}, () => Console.log(translate("cli.root.chooseAuthSubcommand"))).pipe(Command.withSubcommands([
|
|
2019
2451
|
authStatus,
|
|
2020
2452
|
authLogin,
|
|
2021
2453
|
authLogout,
|
|
2022
|
-
authPreview
|
|
2454
|
+
authPreview,
|
|
2455
|
+
authProfiles
|
|
2023
2456
|
]));
|
|
2024
2457
|
const authCommandSpecs = [
|
|
2025
2458
|
{
|
|
@@ -2034,6 +2467,7 @@ const authCommandSpecs = [
|
|
|
2034
2467
|
input: { flags: [
|
|
2035
2468
|
openConfig.flag,
|
|
2036
2469
|
outputFlag(),
|
|
2470
|
+
profileConfig.flag,
|
|
2037
2471
|
timeoutSecondsConfig$1.flag
|
|
2038
2472
|
] },
|
|
2039
2473
|
kind: "auth",
|
|
@@ -2048,7 +2482,7 @@ const authCommandSpecs = [
|
|
|
2048
2482
|
streaming: false
|
|
2049
2483
|
},
|
|
2050
2484
|
command: "auth status",
|
|
2051
|
-
input: { flags: [outputFlag()] },
|
|
2485
|
+
input: { flags: [outputFlag(), profileConfig.flag] },
|
|
2052
2486
|
kind: "auth",
|
|
2053
2487
|
purpose: translate("cli.metadata.authStatus")
|
|
2054
2488
|
},
|
|
@@ -2061,7 +2495,7 @@ const authCommandSpecs = [
|
|
|
2061
2495
|
streaming: false
|
|
2062
2496
|
},
|
|
2063
2497
|
command: "auth logout",
|
|
2064
|
-
input: { flags: [outputFlag()] },
|
|
2498
|
+
input: { flags: [outputFlag(), profileConfig.flag] },
|
|
2065
2499
|
kind: "auth",
|
|
2066
2500
|
purpose: translate("cli.metadata.authLogout")
|
|
2067
2501
|
},
|
|
@@ -2081,6 +2515,51 @@ const authCommandSpecs = [
|
|
|
2081
2515
|
] },
|
|
2082
2516
|
kind: "auth",
|
|
2083
2517
|
purpose: translate("cli.metadata.authPreview")
|
|
2518
|
+
},
|
|
2519
|
+
{
|
|
2520
|
+
auth: { required: false },
|
|
2521
|
+
capabilities: {
|
|
2522
|
+
dryRun: false,
|
|
2523
|
+
fieldSelection: false,
|
|
2524
|
+
rawJsonInput: false,
|
|
2525
|
+
streaming: false
|
|
2526
|
+
},
|
|
2527
|
+
command: "auth profiles list",
|
|
2528
|
+
input: { flags: [outputFlag()] },
|
|
2529
|
+
kind: "auth",
|
|
2530
|
+
purpose: translate("cli.metadata.authProfilesList")
|
|
2531
|
+
},
|
|
2532
|
+
{
|
|
2533
|
+
auth: { required: false },
|
|
2534
|
+
capabilities: {
|
|
2535
|
+
dryRun: false,
|
|
2536
|
+
fieldSelection: false,
|
|
2537
|
+
rawJsonInput: false,
|
|
2538
|
+
streaming: false
|
|
2539
|
+
},
|
|
2540
|
+
command: "auth profiles use",
|
|
2541
|
+
input: {
|
|
2542
|
+
arguments: [profileCommandArgument],
|
|
2543
|
+
flags: [outputFlag()]
|
|
2544
|
+
},
|
|
2545
|
+
kind: "auth",
|
|
2546
|
+
purpose: translate("cli.metadata.authProfilesUse")
|
|
2547
|
+
},
|
|
2548
|
+
{
|
|
2549
|
+
auth: { required: false },
|
|
2550
|
+
capabilities: {
|
|
2551
|
+
dryRun: false,
|
|
2552
|
+
fieldSelection: false,
|
|
2553
|
+
rawJsonInput: false,
|
|
2554
|
+
streaming: false
|
|
2555
|
+
},
|
|
2556
|
+
command: "auth profiles remove",
|
|
2557
|
+
input: {
|
|
2558
|
+
arguments: [profileCommandArgument],
|
|
2559
|
+
flags: [outputFlag()]
|
|
2560
|
+
},
|
|
2561
|
+
kind: "auth",
|
|
2562
|
+
purpose: translate("cli.metadata.authProfilesRemove")
|
|
2084
2563
|
}
|
|
2085
2564
|
];
|
|
2086
2565
|
//#endregion
|
|
@@ -2435,15 +2914,15 @@ const fileTypeOption = fileTypeConfig.option;
|
|
|
2435
2914
|
const sortByOption = sortByConfig.option;
|
|
2436
2915
|
const optionalFileIdOption = optionalFileIdConfig.option;
|
|
2437
2916
|
const optionalFileNameOption = optionalFileNameConfig.option;
|
|
2438
|
-
const
|
|
2439
|
-
const NonEmptyIdsSchema$1 = Schema.Array(Schema.Number).
|
|
2917
|
+
const NonBlankStringSchema = Schema.String.check(Schema.makeFilter((value) => value.trim().length > 0 ? void 0 : "Expected a non-empty string"));
|
|
2918
|
+
const NonEmptyIdsSchema$1 = Schema.Array(Schema.Number).check(Schema.isNonEmpty());
|
|
2440
2919
|
const FilesMkdirInputSchema = Schema.Struct({
|
|
2441
|
-
name:
|
|
2920
|
+
name: NonBlankStringSchema,
|
|
2442
2921
|
parent_id: Schema.optional(Schema.Number)
|
|
2443
2922
|
});
|
|
2444
2923
|
const FilesRenameInputSchema = Schema.Struct({
|
|
2445
2924
|
file_id: Schema.Number,
|
|
2446
|
-
name:
|
|
2925
|
+
name: NonBlankStringSchema
|
|
2447
2926
|
});
|
|
2448
2927
|
const FilesDeleteInputSchema = Schema.Struct({
|
|
2449
2928
|
ids: NonEmptyIdsSchema$1,
|
|
@@ -2886,8 +3365,8 @@ const WATCH_TERMINAL_STATUSES = [
|
|
|
2886
3365
|
"ERROR",
|
|
2887
3366
|
"SEEDING"
|
|
2888
3367
|
];
|
|
2889
|
-
const NonEmptyIdsSchema = Schema.Array(Schema.Number).
|
|
2890
|
-
const TransfersAddInputSchema = Schema.Array(TransferAddInputSchema).
|
|
3368
|
+
const NonEmptyIdsSchema = Schema.Array(Schema.Number).check(Schema.isNonEmpty());
|
|
3369
|
+
const TransfersAddInputSchema = Schema.Array(TransferAddInputSchema).check(Schema.isNonEmpty());
|
|
2891
3370
|
const TransfersCancelInputSchema = Schema.Struct({ ids: NonEmptyIdsSchema });
|
|
2892
3371
|
const TransfersSingleIdInputSchema = Schema.Struct({ id: Schema.Number });
|
|
2893
3372
|
const TransfersCleanInputSchema = Schema.Struct({ ids: Schema.optional(NonEmptyIdsSchema) });
|
|
@@ -3002,12 +3481,15 @@ const transfersRetry = Command.make("retry", {
|
|
|
3002
3481
|
}));
|
|
3003
3482
|
const transfersClean = Command.make("clean", {
|
|
3004
3483
|
dryRun: dryRunOption,
|
|
3005
|
-
id: transferIdsOption.pipe(
|
|
3484
|
+
id: transferIdsOption.pipe(Flag.optional),
|
|
3006
3485
|
json: jsonOption,
|
|
3007
3486
|
output: outputOption
|
|
3008
3487
|
}, ({ dryRun, id, json, output }) => Effect.gen(function* () {
|
|
3009
3488
|
const input = yield* resolveMutationInput({
|
|
3010
|
-
buildFromFlags: () =>
|
|
3489
|
+
buildFromFlags: () => {
|
|
3490
|
+
const ids = Option.getOrUndefined(id);
|
|
3491
|
+
return ids === void 0 || ids.length === 0 ? {} : { ids };
|
|
3492
|
+
},
|
|
3011
3493
|
json,
|
|
3012
3494
|
schema: TransfersCleanInputSchema
|
|
3013
3495
|
});
|
|
@@ -3385,7 +3867,15 @@ const commandCatalog = decodeCommandSpecs([
|
|
|
3385
3867
|
]);
|
|
3386
3868
|
//#endregion
|
|
3387
3869
|
//#region src/internal/metadata.ts
|
|
3388
|
-
const NonEmptyStringSchema = Schema.String.
|
|
3870
|
+
const NonEmptyStringSchema = Schema.String.check(Schema.isNonEmpty());
|
|
3871
|
+
const ConfigStringFieldSchema = Schema.Struct({
|
|
3872
|
+
required: Schema.Boolean,
|
|
3873
|
+
type: Schema.Literal("string")
|
|
3874
|
+
});
|
|
3875
|
+
const PersistedProfileShapeSchema = Schema.Struct({
|
|
3876
|
+
api_base_url: ConfigStringFieldSchema,
|
|
3877
|
+
auth_token: ConfigStringFieldSchema
|
|
3878
|
+
});
|
|
3389
3879
|
const CliMetadataSchema = Schema.Struct({
|
|
3390
3880
|
agentDx: AgentDxScorecardSchema,
|
|
3391
3881
|
auth: Schema.Struct({
|
|
@@ -3397,9 +3887,16 @@ const CliMetadataSchema = Schema.Struct({
|
|
|
3397
3887
|
loginWebAppUrlEnv: NonEmptyStringSchema,
|
|
3398
3888
|
persistedConfigEnv: NonEmptyStringSchema,
|
|
3399
3889
|
persistedConfigShape: Schema.Struct({
|
|
3400
|
-
api_base_url:
|
|
3401
|
-
auth_token:
|
|
3402
|
-
|
|
3890
|
+
api_base_url: ConfigStringFieldSchema,
|
|
3891
|
+
auth_token: ConfigStringFieldSchema,
|
|
3892
|
+
default_profile: ConfigStringFieldSchema,
|
|
3893
|
+
profiles: Schema.Struct({
|
|
3894
|
+
required: Schema.Boolean,
|
|
3895
|
+
type: Schema.Literal("record"),
|
|
3896
|
+
values: PersistedProfileShapeSchema
|
|
3897
|
+
})
|
|
3898
|
+
}),
|
|
3899
|
+
profileEnv: NonEmptyStringSchema
|
|
3403
3900
|
}),
|
|
3404
3901
|
binary: NonEmptyStringSchema,
|
|
3405
3902
|
commands: Schema.Array(CommandDescriptorSchema),
|
|
@@ -3436,9 +3933,34 @@ const describeCli = () => decodeCliMetadata({
|
|
|
3436
3933
|
loginWebAppUrlEnv: ENV_CLI_WEB_APP_URL,
|
|
3437
3934
|
persistedConfigEnv: ENV_CLI_CONFIG_PATH,
|
|
3438
3935
|
persistedConfigShape: {
|
|
3439
|
-
api_base_url:
|
|
3440
|
-
|
|
3441
|
-
|
|
3936
|
+
api_base_url: {
|
|
3937
|
+
required: true,
|
|
3938
|
+
type: "string"
|
|
3939
|
+
},
|
|
3940
|
+
auth_token: {
|
|
3941
|
+
required: false,
|
|
3942
|
+
type: "string"
|
|
3943
|
+
},
|
|
3944
|
+
default_profile: {
|
|
3945
|
+
required: false,
|
|
3946
|
+
type: "string"
|
|
3947
|
+
},
|
|
3948
|
+
profiles: {
|
|
3949
|
+
required: false,
|
|
3950
|
+
type: "record",
|
|
3951
|
+
values: {
|
|
3952
|
+
api_base_url: {
|
|
3953
|
+
required: false,
|
|
3954
|
+
type: "string"
|
|
3955
|
+
},
|
|
3956
|
+
auth_token: {
|
|
3957
|
+
required: false,
|
|
3958
|
+
type: "string"
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
},
|
|
3963
|
+
profileEnv: ENV_CLI_PROFILE
|
|
3442
3964
|
},
|
|
3443
3965
|
binary: translate("cli.brand.binary"),
|
|
3444
3966
|
commands: commandCatalog,
|
|
@@ -3460,4 +3982,4 @@ const describeCli = () => decodeCliMetadata({
|
|
|
3460
3982
|
version
|
|
3461
3983
|
});
|
|
3462
3984
|
//#endregion
|
|
3463
|
-
export {
|
|
3985
|
+
export { CliOutputLive as A, loadPersistedState as C, useProfile as D, savePersistedState as E, CliRuntime as F, CliRuntimeLive as I, translate as L, isStructuredOutputMode as M, renderJson as N, CliSdkLive as O, CliConfigLive as P, version as R, listProfiles as S, resolveAuthState as T, PutioCliConfigSchema as _, searchCommand as a, clearPersistedState as b, brandCommand as c, AuthProfileListSchema as d, AuthProfileSummarySchema as f, CliStateLive as g, CliState as h, filesCommand as i, detectOutputModeFromArgv as j, CliOutput as k, versionCommand as l, AuthStatusSchema as m, whoamiCommand as n, eventsCommand as o, AuthStateError as p, transfersCommand as r, downloadLinksCommand as s, describeCli as t, makeAuthCommand as u, PutioCliProfileConfigSchema as v, removeProfile as w, getAuthStatus as x, ResolvedAuthStateSchema as y };
|