@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.
@@ -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, Options } from "@effect/cli";
4
- import * as Terminal from "@effect/platform/Terminal";
5
- import { DEFAULT_PUTIO_API_BASE_URL, DEFAULT_PUTIO_WEB_APP_URL, DownloadLinksCreateInputSchema, TransferAddInputSchema, createPutioSdkEffectClient, makePutioSdkLayer } from "@putdotio/sdk";
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 { FetchHttpClient } from "@effect/platform";
12
- import * as FileSystem from "@effect/platform/FileSystem";
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.12";
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: { cleared: "cleared persisted auth state at {{configPath}}" },
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.Literal("machineReadableOutput", "rawPayloadInput", "schemaIntrospection", "contextWindowDiscipline", "inputHardening", "safetyRails", "agentKnowledgePackaging");
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.Tag("@putdotio/cli/CliRuntime")() {};
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$4 = Schema.String.pipe(Schema.filter((value) => value.length > 0, { message: () => "Expected a non-empty string" }));
536
- const UrlStringSchema = NonEmptyStringSchema$4.pipe(Schema.filter((value) => {
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 true;
577
+ return;
540
578
  } catch {
541
- return false;
579
+ return "Expected a valid absolute URL";
542
580
  }
543
- }, { message: () => "Expected a valid absolute URL" }));
581
+ })));
544
582
  const PutioCliAuthFlowConfigSchema = Schema.Struct({
545
- appId: NonEmptyStringSchema$4,
546
- clientName: NonEmptyStringSchema$4,
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$4,
552
- token: Schema.optional(NonEmptyStringSchema$4)
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.Tag("@putdotio/cli/CliConfig")() {};
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$3 = Schema.String.pipe(Schema.filter((value) => value.length > 0, { message: () => "Expected a non-empty string" }));
625
- const OutputModeSchema = Schema.Literal("json", "text", "ndjson");
626
- const InternalRendererSchema = Schema.Literal("json", "terminal", "ndjson");
627
- const CommandKindSchema = Schema.Literal("utility", "auth", "read", "write");
628
- const CommandOptionTypeSchema = Schema.Literal("string", "integer", "boolean", "enum");
629
- const JsonPrimitiveKindSchema = Schema.Literal("string", "integer", "boolean", "null");
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(Schema.String, Schema.Number, Schema.Boolean, Schema.Null);
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$3,
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$3))
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(JsonScalarSchema, JsonEnumSchema, JsonObjectSchema, JsonArraySchema);
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$3)),
653
- defaultValue: Schema.optional(Schema.Union(NonEmptyStringSchema$3, Schema.Number, Schema.Boolean)),
654
- description: Schema.optional(NonEmptyStringSchema$3),
655
- name: NonEmptyStringSchema$3,
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$3,
768
+ command: NonEmptyStringSchema$2,
675
769
  input: CommandInputSchema,
676
770
  kind: CommandKindSchema,
677
- purpose: NonEmptyStringSchema$3
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 && (current._tag === "Refinement" || current._tag === "Transformation" || current._tag === "Suspend")) current = current._tag === "Suspend" ? current.type : current._tag === "Transformation" ? current.to : current.from;
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 "StringKeyword": return stringShape();
818
- case "NumberKeyword": return integerShape();
819
- case "BooleanKeyword": return booleanShape();
820
- case "UndefinedKeyword":
821
- case "VoidKeyword":
822
- case "NullKeyword": return nullShape();
823
- case "Literal": return enumShape([current.literal ?? null]);
824
- case "TupleType": {
825
- const item = current.rest?.[0]?.type;
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 "TypeLiteral": return objectShape((current.propertySignatures ?? []).map((propertySignature) => property(propertySignature.name, schemaAstToJsonShape(propertySignature.type), propertySignature.isOptional !== true)));
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 = (current.types ?? []).filter((type) => unwrapSchemaAst(type)?._tag !== "UndefinedKeyword");
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") return [unwrapped.literal ?? null];
835
- if (unwrapped?._tag === "NullKeyword" || unwrapped?._tag === "VoidKeyword") return [null];
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.Tag("@putdotio/cli/CliOutput")() {};
1510
+ var CliOutput = class extends Context.Service()("@putdotio/cli/CliOutput") {};
1403
1511
  const makeCliOutput = (runtime) => ({
1404
- formatError: (error, output) => isStructuredOutputMode(normalizeOutputMode(output, runtime.isInteractiveTerminal)) ? formatCliErrorJson(error) : formatCliError(error),
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) => Console.log((() => {
1407
- switch (normalizeOutputMode(output, runtime.isInteractiveTerminal)) {
1408
- case "terminal": return renderTerminal(value, renderTerminalValue);
1409
- case "ndjson": return renderNdjson(value);
1410
- case "json": return renderJson(value);
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.Tag("@putdotio/cli/CliSdk")() {};
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(makePutioSdkLayer({
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.mergeAll(FetchHttpClient.layer, Layer.succeed(CliSdk, makeCliSdk()));
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$2 = Schema.String.pipe(Schema.filter((value) => value.length > 0, { message: () => "Expected a non-empty string" }));
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$2,
1434
- auth_token: Schema.optional(NonEmptyStringSchema$2)
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$2,
1438
- configPath: NonEmptyStringSchema$2,
1439
- source: Schema.Literal("env", "config"),
1440
- token: NonEmptyStringSchema$2
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$2,
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
- configPath: NonEmptyStringSchema$2,
1446
- source: Schema.NullOr(Schema.Literal("env", "config"))
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.Tag("@putdotio/cli/CliState")() {};
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* resolveCliRuntimeConfig()).configPath;
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 fs = yield* FileSystem.FileSystem;
1477
- const runtime = yield* CliRuntime;
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 persistedState = {
1481
- api_base_url: state.apiBaseUrl ?? existingConfig?.api_base_url ?? DEFAULT_PUTIO_API_BASE_URL,
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* fs.makeDirectory(runtime.dirname(effectiveConfigPath), { recursive: true }).pipe(Effect.mapError((error) => mapFileSystemError(error, `Unable to write CLI config to ${effectiveConfigPath}.`)));
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
- state: persistedState
1685
+ profile: null,
1686
+ state: nextState
1490
1687
  };
1491
1688
  });
1492
- const clearPersistedStateEffect = (configPath) => Effect.gen(function* () {
1493
- const fs = yield* FileSystem.FileSystem;
1494
- const runtime = yield* CliRuntime;
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
- if (existingConfig && existingConfig.api_base_url !== DEFAULT_PUTIO_API_BASE_URL) {
1498
- const nextConfig = { api_base_url: existingConfig.api_base_url };
1499
- yield* fs.makeDirectory(runtime.dirname(effectiveConfigPath), { recursive: true }).pipe(Effect.mapError((error) => mapFileSystemError(error, `Unable to clear CLI auth state at ${effectiveConfigPath}.`)));
1500
- yield* fs.writeFileString(effectiveConfigPath, `${JSON.stringify(nextConfig, null, 2)}\n`).pipe(Effect.mapError((error) => mapFileSystemError(error, `Unable to clear CLI auth state at ${effectiveConfigPath}.`)));
1501
- yield* fs.chmod(effectiveConfigPath, 384).pipe(Effect.mapError((error) => mapFileSystemError(error, `Unable to clear CLI auth state at ${effectiveConfigPath}.`)));
1502
- } else yield* fs.remove(effectiveConfigPath, { force: true }).pipe(Effect.mapError((error) => mapFileSystemError(error, `Unable to clear CLI auth state at ${effectiveConfigPath}.`)));
1503
- return { configPath: effectiveConfigPath };
1504
- });
1505
- const getAuthStatusEffect = () => Effect.gen(function* () {
1506
- const runtime = yield* resolveCliRuntimeConfig();
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" ? true : false,
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* resolveCliRuntimeConfig();
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 resolveAuthState = () => Effect.flatMap(CliState, (state) => state.resolveAuthState());
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 = Options.choice("output", [
1909
+ const outputOption = Flag.choice("output", [
1559
1910
  "json",
1560
1911
  "text",
1561
1912
  "ndjson"
1562
- ]).pipe(Options.optional);
1563
- const dryRunOption = Options.boolean("dry-run").pipe(Options.withDefault(false));
1564
- const fieldsOption = Options.text("fields").pipe(Options.optional);
1565
- const jsonOption = Options.text("json").pipe(Options.optional);
1566
- const pageAllOption = Options.boolean("page-all").pipe(Options.withDefault(false));
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 ? Options.boolean(name) : Options.boolean(name).pipe(Options.withDefault(options.defaultValue));
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
- const defineIntegerOption = (name, options = {}) => {
1575
- const option = options.optional ? Options.integer(name).pipe(Options.optional) : Options.integer(name);
1576
- return {
1577
- flag: integerFlag(name, {
1578
- description: options.description,
1579
- required: options.required ?? options.optional !== true
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
- const defineTextOption = (name, options = {}) => {
1585
- let option = Options.text(name);
1586
- if (options.defaultValue !== void 0) option = option.pipe(Options.withDefault(options.defaultValue));
1587
- else if (options.optional) option = option.pipe(Options.optional);
1588
- return {
1589
- flag: stringFlag(name, {
1590
- defaultValue: options.defaultValue,
1591
- description: options.description,
1592
- required: options.required ?? (options.defaultValue === void 0 && options.optional !== true)
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
- const defineChoiceOption = (name, choices, options = {}) => {
1598
- const option = options.optional ? Options.choice(name, choices).pipe(Options.optional) : Options.choice(name, choices);
1599
- return {
1600
- flag: enumFlag(name, choices, {
1601
- description: options.description,
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) => Options.text(name).pipe(Options.repeated, Options.filterMap(parseRepeatedIntegers, `Expected \`--${name}\` values to be integers.`));
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: Options.text(name).pipe(Options.repeated)
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 terminal = yield* Terminal.Terminal;
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* input.take;
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.catchTag("NoSuchElementException", () => Effect.succeed(false)));
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", { output: outputOption }, ({ output }) => Effect.gen(function* () {
1948
- yield* writeOutput(yield* getAuthStatus(), getOption(output), renderAuthStatus);
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) : Console.error(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", { output: outputOption }, ({ output }) => Effect.gen(function* () {
1999
- const { configPath } = yield* clearPersistedState();
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: true,
2002
- configPath
2003
- }, getOption(output), (value) => translate("cli.auth.logout.cleared", { configPath: value.configPath }));
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 NonEmptyStringSchema$1 = Schema.String.pipe(Schema.filter((value) => value.trim().length > 0, { message: () => "Expected a non-empty string" }));
2439
- const NonEmptyIdsSchema$1 = Schema.Array(Schema.Number).pipe(Schema.filter((value) => value.length > 0, { message: () => "Expected at least one id" }));
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: NonEmptyStringSchema$1,
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: NonEmptyStringSchema$1
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).pipe(Schema.filter((value) => value.length > 0, { message: () => "Expected at least one id" }));
2890
- const TransfersAddInputSchema = Schema.Array(TransferAddInputSchema).pipe(Schema.filter((value) => value.length > 0, { message: () => "Expected at least one transfer input" }));
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(Options.optional),
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: () => ({ ids: Option.getOrUndefined(id) }),
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.pipe(Schema.filter((value) => value.length > 0, { message: () => "Expected a non-empty 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: Schema.Literal("string"),
3401
- auth_token: Schema.Literal("string")
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: "string",
3440
- auth_token: "string"
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 { translate as A, CliOutput as C, CliConfigLive as D, renderJson as E, CliRuntime as O, CliSdkLive as S, detectOutputModeFromArgv as T, clearPersistedState as _, searchCommand as a, resolveAuthState as b, brandCommand as c, AuthStateError as d, AuthStatusSchema as f, ResolvedAuthStateSchema as g, PutioCliConfigSchema as h, filesCommand as i, version as j, CliRuntimeLive as k, versionCommand as l, CliStateLive as m, whoamiCommand as n, eventsCommand as o, CliState as p, transfersCommand as r, downloadLinksCommand as s, describeCli as t, makeAuthCommand as u, getAuthStatus as v, CliOutputLive as w, savePersistedState as x, loadPersistedState as y };
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 };