@putdotio/cli 1.0.11 → 1.1.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 +2 -2
- package/dist/bin.mjs +116 -11
- package/dist/index.mjs +1 -1
- package/dist/{metadata-DzqePHlY.mjs → metadata-C_yXlqxj.mjs} +297 -145
- package/package.json +6 -6
package/README.md
CHANGED
package/dist/bin.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as
|
|
3
|
-
import { Cause, Console, Effect, Layer } from "effect";
|
|
4
|
-
import { Command } from "
|
|
5
|
-
import {
|
|
6
|
-
import * as NodeTerminal from "@effect/platform-node/NodeTerminal";
|
|
2
|
+
import { A as CliRuntimeLive, C as CliOutput, D as renderJson, E as isStructuredOutputMode, M as version, O as CliConfigLive, S as CliSdkLive, T as detectOutputModeFromArgv, a as searchCommand, c as brandCommand, i as filesCommand, j as translate, k as CliRuntime, l as versionCommand, m as CliStateLive, n as whoamiCommand, o as eventsCommand, r as transfersCommand, s as downloadLinksCommand, t as describeCli, u as makeAuthCommand, w as CliOutputLive } from "./metadata-C_yXlqxj.mjs";
|
|
3
|
+
import { Cause, Console, Effect, Layer, Result } from "effect";
|
|
4
|
+
import { CliError, Command } from "effect/unstable/cli";
|
|
5
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
7
6
|
//#region src/cli.ts
|
|
8
7
|
const authCommand = makeAuthCommand();
|
|
9
8
|
const describeCommand = Command.make("describe", {}, () => Console.log(renderJson(describeCli())));
|
|
@@ -19,21 +18,127 @@ const command = Command.make("putio", {}, () => Console.log(translate("cli.root.
|
|
|
19
18
|
searchCommand,
|
|
20
19
|
transfersCommand
|
|
21
20
|
]));
|
|
21
|
+
const makeBufferedConsole = (entries) => ({
|
|
22
|
+
assert: (condition, ...args) => entries.push({
|
|
23
|
+
args: [condition, ...args],
|
|
24
|
+
method: "assert"
|
|
25
|
+
}),
|
|
26
|
+
clear: () => entries.push({
|
|
27
|
+
args: [],
|
|
28
|
+
method: "clear"
|
|
29
|
+
}),
|
|
30
|
+
count: (label) => entries.push({
|
|
31
|
+
args: label === void 0 ? [] : [label],
|
|
32
|
+
method: "count"
|
|
33
|
+
}),
|
|
34
|
+
countReset: (label) => entries.push({
|
|
35
|
+
args: label === void 0 ? [] : [label],
|
|
36
|
+
method: "countReset"
|
|
37
|
+
}),
|
|
38
|
+
debug: (...args) => entries.push({
|
|
39
|
+
args,
|
|
40
|
+
method: "debug"
|
|
41
|
+
}),
|
|
42
|
+
dir: (item, options) => entries.push({
|
|
43
|
+
args: options === void 0 ? [item] : [item, options],
|
|
44
|
+
method: "dir"
|
|
45
|
+
}),
|
|
46
|
+
dirxml: (...args) => entries.push({
|
|
47
|
+
args,
|
|
48
|
+
method: "dirxml"
|
|
49
|
+
}),
|
|
50
|
+
error: (...args) => entries.push({
|
|
51
|
+
args,
|
|
52
|
+
method: "error"
|
|
53
|
+
}),
|
|
54
|
+
group: (...args) => entries.push({
|
|
55
|
+
args,
|
|
56
|
+
method: "group"
|
|
57
|
+
}),
|
|
58
|
+
groupCollapsed: (...args) => entries.push({
|
|
59
|
+
args,
|
|
60
|
+
method: "groupCollapsed"
|
|
61
|
+
}),
|
|
62
|
+
groupEnd: () => entries.push({
|
|
63
|
+
args: [],
|
|
64
|
+
method: "groupEnd"
|
|
65
|
+
}),
|
|
66
|
+
info: (...args) => entries.push({
|
|
67
|
+
args,
|
|
68
|
+
method: "info"
|
|
69
|
+
}),
|
|
70
|
+
log: (...args) => entries.push({
|
|
71
|
+
args,
|
|
72
|
+
method: "log"
|
|
73
|
+
}),
|
|
74
|
+
table: (tabularData, properties) => entries.push({
|
|
75
|
+
args: properties === void 0 ? [tabularData] : [tabularData, properties],
|
|
76
|
+
method: "table"
|
|
77
|
+
}),
|
|
78
|
+
time: (label) => entries.push({
|
|
79
|
+
args: label === void 0 ? [] : [label],
|
|
80
|
+
method: "time"
|
|
81
|
+
}),
|
|
82
|
+
timeEnd: (label) => entries.push({
|
|
83
|
+
args: label === void 0 ? [] : [label],
|
|
84
|
+
method: "timeEnd"
|
|
85
|
+
}),
|
|
86
|
+
timeLog: (label, ...args) => entries.push({
|
|
87
|
+
args: label === void 0 ? args : [label, ...args],
|
|
88
|
+
method: "timeLog"
|
|
89
|
+
}),
|
|
90
|
+
trace: (...args) => entries.push({
|
|
91
|
+
args,
|
|
92
|
+
method: "trace"
|
|
93
|
+
}),
|
|
94
|
+
warn: (...args) => entries.push({
|
|
95
|
+
args,
|
|
96
|
+
method: "warn"
|
|
97
|
+
})
|
|
98
|
+
});
|
|
99
|
+
const replayBufferedConsole = (console, entries) => Effect.sync(() => {
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
const method = console[entry.method];
|
|
102
|
+
method(...entry.args);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const formatCliParserError = (error) => error.errors.map((nestedError) => nestedError.message).join("\n");
|
|
106
|
+
const executableName = (value) => {
|
|
107
|
+
const normalized = value.replaceAll("\\", "/");
|
|
108
|
+
return normalized.slice(normalized.lastIndexOf("/") + 1).toLowerCase();
|
|
109
|
+
};
|
|
110
|
+
const commandArgsFromArgv = (args) => {
|
|
111
|
+
const [first] = args;
|
|
112
|
+
if (first === void 0) return args;
|
|
113
|
+
const firstName = executableName(first);
|
|
114
|
+
if (firstName === "node" || firstName === "node.exe") return args.slice(2);
|
|
115
|
+
if (firstName === "putio" || firstName === "putio.exe" || firstName === "bin.mjs") return args.slice(1);
|
|
116
|
+
return args;
|
|
117
|
+
};
|
|
22
118
|
function runCli(args) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
119
|
+
const run = Command.runWith(command, { version });
|
|
120
|
+
return Effect.flatMap(CliRuntime, (runtime) => {
|
|
121
|
+
const outputMode = detectOutputModeFromArgv(args, runtime.isInteractiveTerminal);
|
|
122
|
+
const commandArgs = commandArgsFromArgv(args);
|
|
123
|
+
if (!isStructuredOutputMode(outputMode)) return run(commandArgs);
|
|
124
|
+
return Console.consoleWith((currentConsole) => {
|
|
125
|
+
const entries = [];
|
|
126
|
+
return run(commandArgs).pipe(Effect.provideService(Console.Console, makeBufferedConsole(entries)), Effect.tap(() => replayBufferedConsole(currentConsole, entries)), Effect.catchFilter((error) => CliError.isCliError(error) && error._tag === "ShowHelp" ? Result.succeed(error) : Result.fail(error), (error) => {
|
|
127
|
+
if (error.errors.length === 0) return replayBufferedConsole(currentConsole, entries);
|
|
128
|
+
return Effect.fail(new Error(formatCliParserError(error)));
|
|
129
|
+
}, (error) => Effect.fail(error)));
|
|
130
|
+
});
|
|
131
|
+
});
|
|
27
132
|
}
|
|
28
133
|
//#endregion
|
|
29
134
|
//#region src/internal/app-layer.ts
|
|
30
135
|
const makeCliAppLayer = (runtime) => {
|
|
31
136
|
const runtimeLayer = runtime ? Layer.succeed(CliRuntime, runtime) : CliRuntimeLive;
|
|
32
|
-
return Layer.mergeAll(
|
|
137
|
+
return Layer.mergeAll(NodeServices.layer, runtimeLayer, CliOutputLive.pipe(Layer.provide(runtimeLayer)), CliConfigLive.pipe(Layer.provide(runtimeLayer)), CliSdkLive, CliStateLive);
|
|
33
138
|
};
|
|
34
139
|
//#endregion
|
|
35
140
|
//#region src/bin.ts
|
|
36
|
-
NodeRuntime.runMain(Effect.scoped(Effect.flatMap(CliRuntime, (runtime) => runCli(runtime.argv)).pipe(Effect.
|
|
141
|
+
NodeRuntime.runMain(Effect.scoped(Effect.flatMap(CliRuntime, (runtime) => runCli(runtime.argv)).pipe(Effect.catchCause((cause) => Effect.gen(function* () {
|
|
37
142
|
const cliOutput = yield* CliOutput;
|
|
38
143
|
const runtime = yield* CliRuntime;
|
|
39
144
|
const outputMode = detectOutputModeFromArgv(runtime.argv, runtime.isInteractiveTerminal);
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as clearPersistedState, b as resolveAuthState, d as AuthStateError, f as AuthStatusSchema, g as ResolvedAuthStateSchema, h as PutioCliConfigSchema, m as CliStateLive, p as CliState, t as describeCli, v as getAuthStatus, x as savePersistedState, y as loadPersistedState } from "./metadata-
|
|
1
|
+
import { _ as clearPersistedState, b as resolveAuthState, d as AuthStateError, f as AuthStatusSchema, g as ResolvedAuthStateSchema, h as PutioCliConfigSchema, m as CliStateLive, p as CliState, t as describeCli, v as getAuthStatus, x as savePersistedState, y as loadPersistedState } from "./metadata-C_yXlqxj.mjs";
|
|
2
2
|
export { AuthStateError, AuthStatusSchema, CliState, CliStateLive, PutioCliConfigSchema, ResolvedAuthStateSchema, clearPersistedState, describeCli, getAuthStatus, loadPersistedState, resolveAuthState, savePersistedState };
|
|
@@ -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 { 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.1.0";
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/i18n/translate.ts
|
|
19
19
|
const resources = { en: { translation: {
|
|
@@ -358,7 +358,15 @@ const createTranslator = (locale = "en") => {
|
|
|
358
358
|
const translate = createTranslator();
|
|
359
359
|
//#endregion
|
|
360
360
|
//#region src/internal/agent-dx.ts
|
|
361
|
-
const AgentDxCategoryNameSchema = Schema.
|
|
361
|
+
const AgentDxCategoryNameSchema = Schema.Literals([
|
|
362
|
+
"machineReadableOutput",
|
|
363
|
+
"rawPayloadInput",
|
|
364
|
+
"schemaIntrospection",
|
|
365
|
+
"contextWindowDiscipline",
|
|
366
|
+
"inputHardening",
|
|
367
|
+
"safetyRails",
|
|
368
|
+
"agentKnowledgePackaging"
|
|
369
|
+
]);
|
|
362
370
|
const AgentDxDimensionSchema = Schema.Struct({
|
|
363
371
|
maxScore: Schema.Literal(3),
|
|
364
372
|
name: AgentDxCategoryNameSchema,
|
|
@@ -455,7 +463,7 @@ const ENV_CLI_TOKEN = "PUTIO_CLI_TOKEN";
|
|
|
455
463
|
const ENV_XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
|
|
456
464
|
//#endregion
|
|
457
465
|
//#region src/internal/runtime.ts
|
|
458
|
-
var CliRuntime = class extends Context.
|
|
466
|
+
var CliRuntime = class extends Context.Service()("@putdotio/cli/CliRuntime") {};
|
|
459
467
|
const openExternalWithPlatform = (platform, url) => {
|
|
460
468
|
const command = platform === "darwin" ? {
|
|
461
469
|
file: "open",
|
|
@@ -506,6 +514,20 @@ const makeCliRuntime = (options = {}) => {
|
|
|
506
514
|
setExitCode: (code) => Effect.sync(() => {
|
|
507
515
|
process.exitCode = code;
|
|
508
516
|
}),
|
|
517
|
+
writeStdout: (message) => Effect.sync(() => {
|
|
518
|
+
if (options.writeStdout) {
|
|
519
|
+
options.writeStdout(message);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
process.stdout.write(message);
|
|
523
|
+
}),
|
|
524
|
+
writeStderr: (message) => Effect.sync(() => {
|
|
525
|
+
if (options.writeStderr) {
|
|
526
|
+
options.writeStderr(message);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
process.stderr.write(message);
|
|
530
|
+
}),
|
|
509
531
|
openExternal: (url) => Effect.sync(() => openExternalWithPlatform(platform, url)),
|
|
510
532
|
startSpinner: (message) => Effect.sync(() => {
|
|
511
533
|
let frameIndex = 0;
|
|
@@ -532,27 +554,27 @@ const makeCliRuntime = (options = {}) => {
|
|
|
532
554
|
const CliRuntimeLive = Layer.sync(CliRuntime, () => makeCliRuntime());
|
|
533
555
|
//#endregion
|
|
534
556
|
//#region src/internal/config.ts
|
|
535
|
-
const NonEmptyStringSchema$
|
|
536
|
-
const UrlStringSchema = NonEmptyStringSchema$
|
|
557
|
+
const NonEmptyStringSchema$3 = Schema.String.check(Schema.isNonEmpty());
|
|
558
|
+
const UrlStringSchema = NonEmptyStringSchema$3.pipe(Schema.check(Schema.makeFilter((value) => {
|
|
537
559
|
try {
|
|
538
560
|
new URL(value);
|
|
539
|
-
return
|
|
561
|
+
return;
|
|
540
562
|
} catch {
|
|
541
|
-
return
|
|
563
|
+
return "Expected a valid absolute URL";
|
|
542
564
|
}
|
|
543
|
-
}
|
|
565
|
+
})));
|
|
544
566
|
const PutioCliAuthFlowConfigSchema = Schema.Struct({
|
|
545
|
-
appId: NonEmptyStringSchema$
|
|
546
|
-
clientName: NonEmptyStringSchema$
|
|
567
|
+
appId: NonEmptyStringSchema$3,
|
|
568
|
+
clientName: NonEmptyStringSchema$3,
|
|
547
569
|
webAppUrl: UrlStringSchema
|
|
548
570
|
});
|
|
549
571
|
const CliRuntimeConfigSchema = Schema.Struct({
|
|
550
572
|
apiBaseUrl: UrlStringSchema,
|
|
551
|
-
configPath: NonEmptyStringSchema$
|
|
552
|
-
token: Schema.optional(NonEmptyStringSchema$
|
|
573
|
+
configPath: NonEmptyStringSchema$3,
|
|
574
|
+
token: Schema.optional(NonEmptyStringSchema$3)
|
|
553
575
|
});
|
|
554
576
|
var CliConfigError = class extends Data.TaggedError("CliConfigError") {};
|
|
555
|
-
var CliConfig = class extends Context.
|
|
577
|
+
var CliConfig = class extends Context.Service()("@putdotio/cli/CliConfig") {};
|
|
556
578
|
const optionalTrimmedString = (name) => Config.option(Config.string(name)).pipe(Config.map((value) => Option.flatMap(value, (raw) => {
|
|
557
579
|
const trimmed = raw.trim();
|
|
558
580
|
return trimmed.length > 0 ? Option.some(trimmed) : Option.none();
|
|
@@ -621,23 +643,51 @@ const waitForDeviceToken = (options) => Effect.gen(function* () {
|
|
|
621
643
|
});
|
|
622
644
|
//#endregion
|
|
623
645
|
//#region src/internal/command-specs.ts
|
|
624
|
-
const NonEmptyStringSchema$
|
|
625
|
-
const OutputModeSchema = Schema.
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
646
|
+
const NonEmptyStringSchema$2 = Schema.String.check(Schema.isNonEmpty());
|
|
647
|
+
const OutputModeSchema = Schema.Literals([
|
|
648
|
+
"json",
|
|
649
|
+
"text",
|
|
650
|
+
"ndjson"
|
|
651
|
+
]);
|
|
652
|
+
const InternalRendererSchema = Schema.Literals([
|
|
653
|
+
"json",
|
|
654
|
+
"terminal",
|
|
655
|
+
"ndjson"
|
|
656
|
+
]);
|
|
657
|
+
const CommandKindSchema = Schema.Literals([
|
|
658
|
+
"utility",
|
|
659
|
+
"auth",
|
|
660
|
+
"read",
|
|
661
|
+
"write"
|
|
662
|
+
]);
|
|
663
|
+
const CommandOptionTypeSchema = Schema.Literals([
|
|
664
|
+
"string",
|
|
665
|
+
"integer",
|
|
666
|
+
"boolean",
|
|
667
|
+
"enum"
|
|
668
|
+
]);
|
|
669
|
+
const JsonPrimitiveKindSchema = Schema.Literals([
|
|
670
|
+
"string",
|
|
671
|
+
"integer",
|
|
672
|
+
"boolean",
|
|
673
|
+
"null"
|
|
674
|
+
]);
|
|
630
675
|
const JsonScalarSchema = Schema.Struct({ kind: JsonPrimitiveKindSchema });
|
|
631
|
-
const JsonEnumValueSchema = Schema.Union(
|
|
676
|
+
const JsonEnumValueSchema = Schema.Union([
|
|
677
|
+
Schema.String,
|
|
678
|
+
Schema.Number,
|
|
679
|
+
Schema.Boolean,
|
|
680
|
+
Schema.Null
|
|
681
|
+
]);
|
|
632
682
|
const JsonPropertySchema = Schema.Struct({
|
|
633
|
-
name: NonEmptyStringSchema$
|
|
683
|
+
name: NonEmptyStringSchema$2,
|
|
634
684
|
required: Schema.Boolean,
|
|
635
685
|
schema: Schema.suspend(() => CommandJsonShapeSchema)
|
|
636
686
|
});
|
|
637
687
|
const JsonObjectSchema = Schema.Struct({
|
|
638
688
|
kind: Schema.Literal("object"),
|
|
639
689
|
properties: Schema.Array(JsonPropertySchema),
|
|
640
|
-
rules: Schema.optional(Schema.Array(NonEmptyStringSchema$
|
|
690
|
+
rules: Schema.optional(Schema.Array(NonEmptyStringSchema$2))
|
|
641
691
|
});
|
|
642
692
|
const JsonArraySchema = Schema.Struct({
|
|
643
693
|
kind: Schema.Literal("array"),
|
|
@@ -647,12 +697,21 @@ const JsonEnumSchema = Schema.Struct({
|
|
|
647
697
|
kind: Schema.Literal("enum"),
|
|
648
698
|
values: Schema.Array(JsonEnumValueSchema)
|
|
649
699
|
});
|
|
650
|
-
const CommandJsonShapeSchema = Schema.Union(
|
|
700
|
+
const CommandJsonShapeSchema = Schema.Union([
|
|
701
|
+
JsonScalarSchema,
|
|
702
|
+
JsonEnumSchema,
|
|
703
|
+
JsonObjectSchema,
|
|
704
|
+
JsonArraySchema
|
|
705
|
+
]);
|
|
651
706
|
const CommandOptionSchema = Schema.Struct({
|
|
652
|
-
choices: Schema.optional(Schema.Array(NonEmptyStringSchema$
|
|
653
|
-
defaultValue: Schema.optional(Schema.Union(
|
|
654
|
-
|
|
655
|
-
|
|
707
|
+
choices: Schema.optional(Schema.Array(NonEmptyStringSchema$2)),
|
|
708
|
+
defaultValue: Schema.optional(Schema.Union([
|
|
709
|
+
NonEmptyStringSchema$2,
|
|
710
|
+
Schema.Number,
|
|
711
|
+
Schema.Boolean
|
|
712
|
+
])),
|
|
713
|
+
description: Schema.optional(NonEmptyStringSchema$2),
|
|
714
|
+
name: NonEmptyStringSchema$2,
|
|
656
715
|
repeated: Schema.Boolean,
|
|
657
716
|
required: Schema.Boolean,
|
|
658
717
|
type: CommandOptionTypeSchema
|
|
@@ -671,10 +730,10 @@ const CommandAuthSchema = Schema.Struct({ required: Schema.Boolean });
|
|
|
671
730
|
const CommandDescriptorSchema = Schema.Struct({
|
|
672
731
|
auth: CommandAuthSchema,
|
|
673
732
|
capabilities: CommandCapabilitiesSchema,
|
|
674
|
-
command: NonEmptyStringSchema$
|
|
733
|
+
command: NonEmptyStringSchema$2,
|
|
675
734
|
input: CommandInputSchema,
|
|
676
735
|
kind: CommandKindSchema,
|
|
677
|
-
purpose: NonEmptyStringSchema$
|
|
736
|
+
purpose: NonEmptyStringSchema$2
|
|
678
737
|
});
|
|
679
738
|
const CliOutputContractSchema = Schema.Struct({
|
|
680
739
|
defaultInteractive: Schema.Literal("text"),
|
|
@@ -808,31 +867,38 @@ const enumFlag = (name, choices, options = {}) => ({
|
|
|
808
867
|
});
|
|
809
868
|
const unwrapSchemaAst = (ast) => {
|
|
810
869
|
let current = ast;
|
|
811
|
-
while (current &&
|
|
870
|
+
while (current && current._tag === "Suspend") current = current.thunk();
|
|
812
871
|
return current;
|
|
813
872
|
};
|
|
814
873
|
const schemaAstToJsonShape = (ast) => {
|
|
815
874
|
const current = unwrapSchemaAst(ast);
|
|
816
875
|
switch (current?._tag) {
|
|
817
|
-
case "
|
|
818
|
-
case "
|
|
819
|
-
case "
|
|
820
|
-
case "
|
|
821
|
-
case "
|
|
822
|
-
case "
|
|
823
|
-
case "Literal":
|
|
824
|
-
|
|
825
|
-
|
|
876
|
+
case "String": return stringShape();
|
|
877
|
+
case "Number": return integerShape();
|
|
878
|
+
case "Boolean": return booleanShape();
|
|
879
|
+
case "Undefined":
|
|
880
|
+
case "Void":
|
|
881
|
+
case "Null": return nullShape();
|
|
882
|
+
case "Literal": {
|
|
883
|
+
const literal = current.literal ?? null;
|
|
884
|
+
if (typeof literal === "bigint") throw new Error("BigInt literals are not supported in CLI json metadata.");
|
|
885
|
+
return enumShape([literal]);
|
|
886
|
+
}
|
|
887
|
+
case "Arrays": {
|
|
888
|
+
const item = current.rest[0] ?? current.elements[0];
|
|
826
889
|
if (!item) throw new Error("Unable to derive an array item schema from an empty tuple AST.");
|
|
827
890
|
return arrayShape(schemaAstToJsonShape(item));
|
|
828
891
|
}
|
|
829
|
-
case "
|
|
892
|
+
case "Objects": return objectShape(current.propertySignatures.map((propertySignature) => property(String(propertySignature.name), schemaAstToJsonShape(propertySignature.type), !SchemaAST.isOptional(propertySignature.type))));
|
|
830
893
|
case "Union": {
|
|
831
|
-
const definedTypes =
|
|
894
|
+
const definedTypes = current.types.filter((type) => unwrapSchemaAst(type)?._tag !== "Undefined");
|
|
832
895
|
const enumValues = definedTypes.flatMap((type) => {
|
|
833
896
|
const unwrapped = unwrapSchemaAst(type);
|
|
834
|
-
if (unwrapped?._tag === "Literal")
|
|
835
|
-
|
|
897
|
+
if (unwrapped?._tag === "Literal") {
|
|
898
|
+
const literal = unwrapped.literal ?? null;
|
|
899
|
+
return typeof literal === "bigint" ? [] : [literal];
|
|
900
|
+
}
|
|
901
|
+
if (unwrapped?._tag === "Null" || unwrapped?._tag === "Void") return [null];
|
|
836
902
|
return [];
|
|
837
903
|
});
|
|
838
904
|
if (enumValues.length === definedTypes.length && enumValues.length > 0) return enumShape(enumValues);
|
|
@@ -1272,6 +1338,7 @@ const normalizeOutputMode = (output, isInteractiveTerminal = true) => {
|
|
|
1272
1338
|
if (output === "text") return "terminal";
|
|
1273
1339
|
return isInteractiveTerminal ? "terminal" : "json";
|
|
1274
1340
|
};
|
|
1341
|
+
const normalizeRequestedOrResolvedOutputMode = (output, isInteractiveTerminal = true) => output === "terminal" ? "terminal" : normalizeOutputMode(output, isInteractiveTerminal);
|
|
1275
1342
|
const detectOutputModeFromArgv = (argv, isInteractiveTerminal = true) => {
|
|
1276
1343
|
for (let index = 0; index < argv.length; index += 1) {
|
|
1277
1344
|
const argument = argv[index];
|
|
@@ -1290,15 +1357,28 @@ const SAFE_SENSITIVE_SENTINEL_VALUES = new Set([
|
|
|
1290
1357
|
REDACTED_VALUE
|
|
1291
1358
|
]);
|
|
1292
1359
|
const PROMPT_INJECTION_PATTERN = /\b(ignore (?:all|any|previous|above)|system prompt|developer message|tool call|function call|follow these instructions|you are chatgpt|you are an ai)\b/iu;
|
|
1293
|
-
const
|
|
1294
|
-
const
|
|
1360
|
+
const TERMINAL_ESCAPE_PATTERN = new RegExp(String.raw`\u001B(?:\][^\u0007]*(?:\u0007|\u001B\\)|\[[0-?]*[ -/]*[@-~]|[@-Z\\-_])`, "gu");
|
|
1361
|
+
const TERMINAL_CONTROL_PATTERN = new RegExp(String.raw`[\u0000-\u0008\u000B-\u001F\u007F-\u009F]`, "gu");
|
|
1362
|
+
const isPlainObject = (value) => {
|
|
1363
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
1364
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1365
|
+
return prototype === Object.prototype || prototype === null;
|
|
1366
|
+
};
|
|
1367
|
+
const redactSensitiveText = (value) => value.replace(/(Authorization\s*:\s*Bearer\s+)([A-Za-z0-9._~+/=-]+)/gi, (_match, prefix) => `${prefix}${REDACTED_VALUE}`).replace(/((?:auth_?token|access_?token|refresh_?token|oauth_?token|token|password|secret|cookie)["']?\s*[:=]\s*["']?)([^"'&,\s}]+)/gi, (_match, prefix) => `${prefix}${REDACTED_VALUE}`).replace(/(Bearer\s+)([A-Za-z0-9._~+/=-]+)/gi, (_match, prefix) => `${prefix}${REDACTED_VALUE}`).replace(/([?&](?:auth_?token|access_?token|refresh_?token|oauth_?token|token)=)([^&\s]+)/gi, (_match, prefix) => `${prefix}${REDACTED_VALUE}`);
|
|
1368
|
+
const sanitizeTerminalText = (value) => redactSensitiveText(value).replace(TERMINAL_ESCAPE_PATTERN, "").replace(TERMINAL_CONTROL_PATTERN, "");
|
|
1369
|
+
const sanitizeTerminalValue = (value) => {
|
|
1370
|
+
if (typeof value === "string") return sanitizeTerminalText(value);
|
|
1371
|
+
if (Array.isArray(value)) return value.map(sanitizeTerminalValue);
|
|
1372
|
+
if (isPlainObject(value)) return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [key, SENSITIVE_KEY_PATTERN.test(key) && typeof nestedValue === "string" ? SAFE_SENSITIVE_SENTINEL_VALUES.has(nestedValue) ? nestedValue : REDACTED_VALUE : sanitizeTerminalValue(nestedValue)]));
|
|
1373
|
+
return value;
|
|
1374
|
+
};
|
|
1295
1375
|
const joinPath = (segments) => segments.reduce((path, segment) => {
|
|
1296
1376
|
if (segment.startsWith("[")) return `${path}${segment}`;
|
|
1297
1377
|
return path === "$" ? `$.${segment}` : `${path}.${segment}`;
|
|
1298
1378
|
}, "$");
|
|
1299
1379
|
const sanitizeStructuredValueInternal = (value, path) => {
|
|
1300
1380
|
if (typeof value === "string") {
|
|
1301
|
-
const sanitized =
|
|
1381
|
+
const sanitized = redactSensitiveText(value);
|
|
1302
1382
|
return {
|
|
1303
1383
|
untrustedTextPaths: PROMPT_INJECTION_PATTERN.test(sanitized) ? [joinPath(path)] : [],
|
|
1304
1384
|
value: sanitized
|
|
@@ -1356,7 +1436,7 @@ const sanitizeStructuredValueInternal = (value, path) => {
|
|
|
1356
1436
|
const sanitizeStructuredValue = (value) => sanitizeStructuredValueInternal(value, []).value;
|
|
1357
1437
|
const renderJson = (value) => JSON.stringify(sanitizeStructuredValue(value), null, 2);
|
|
1358
1438
|
const renderNdjson = (value) => JSON.stringify(sanitizeStructuredValue(value));
|
|
1359
|
-
const renderTerminal = (value, renderTerminalValue) =>
|
|
1439
|
+
const renderTerminal = (value, renderTerminalValue) => redactSensitiveText(renderTerminalValue(sanitizeTerminalValue(value)));
|
|
1360
1440
|
const toCliErrorView = (error) => {
|
|
1361
1441
|
const meta = error.meta;
|
|
1362
1442
|
return {
|
|
@@ -1381,61 +1461,60 @@ const toCliErrorJson = (error) => {
|
|
|
1381
1461
|
};
|
|
1382
1462
|
const localizeError = (error) => isLocalizedError(error) ? error : localizeCliError(error);
|
|
1383
1463
|
const formatCliError = (error) => {
|
|
1384
|
-
return
|
|
1464
|
+
return redactSensitiveText(renderCliErrorTerminal(sanitizeTerminalValue(toCliErrorView(localizeError(error)))));
|
|
1385
1465
|
};
|
|
1386
1466
|
const formatCliErrorJson = (error) => {
|
|
1387
1467
|
return renderJson(toCliErrorJson(isLocalizedError(error) ? error : localizeCliError(error)));
|
|
1388
1468
|
};
|
|
1389
|
-
var CliOutput = class extends Context.
|
|
1469
|
+
var CliOutput = class extends Context.Service()("@putdotio/cli/CliOutput") {};
|
|
1390
1470
|
const makeCliOutput = (runtime) => ({
|
|
1391
|
-
formatError: (error, output) => isStructuredOutputMode(
|
|
1471
|
+
formatError: (error, output) => isStructuredOutputMode(normalizeRequestedOrResolvedOutputMode(output, runtime.isInteractiveTerminal)) ? formatCliErrorJson(error) : formatCliError(error),
|
|
1392
1472
|
error: (message) => Console.error(sanitizeTerminalText(message)),
|
|
1393
|
-
write: (value, output, renderTerminalValue) =>
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
}
|
|
1399
|
-
})())
|
|
1473
|
+
write: (value, output, renderTerminalValue) => {
|
|
1474
|
+
const outputMode = normalizeOutputMode(output, runtime.isInteractiveTerminal);
|
|
1475
|
+
const rendered = outputMode === "terminal" ? renderTerminal(value, renderTerminalValue) : outputMode === "ndjson" ? renderNdjson(value) : renderJson(value);
|
|
1476
|
+
return runtime.writeStdout(`${rendered}\n`);
|
|
1477
|
+
}
|
|
1400
1478
|
});
|
|
1401
1479
|
const CliOutputLive = Layer.effect(CliOutput, Effect.map(CliRuntime, makeCliOutput));
|
|
1402
1480
|
const writeOutput = (value, output, renderTerminalValue) => Effect.flatMap(CliOutput, (cliOutput) => cliOutput.write(value, output, renderTerminalValue));
|
|
1403
1481
|
//#endregion
|
|
1404
1482
|
//#region src/internal/sdk.ts
|
|
1405
1483
|
const sdk = createPutioSdkEffectClient();
|
|
1406
|
-
var CliSdk = class extends Context.
|
|
1484
|
+
var CliSdk = class extends Context.Service()("@putdotio/cli/CliSdk") {};
|
|
1407
1485
|
const makeCliSdk = () => ({
|
|
1408
1486
|
client: sdk,
|
|
1409
|
-
provide: (config, program) => program.pipe(Effect.provide(
|
|
1487
|
+
provide: (config, program) => program.pipe(Effect.provide(makePutioSdkLiveLayer({
|
|
1410
1488
|
accessToken: config.token,
|
|
1411
1489
|
baseUrl: config.apiBaseUrl
|
|
1412
1490
|
})))
|
|
1413
1491
|
});
|
|
1414
|
-
const CliSdkLive = Layer.
|
|
1492
|
+
const CliSdkLive = Layer.succeed(CliSdk, makeCliSdk());
|
|
1415
1493
|
const provideSdk = (config, program) => Effect.flatMap(CliSdk, (cliSdk) => cliSdk.provide(config, program));
|
|
1416
1494
|
//#endregion
|
|
1417
1495
|
//#region src/internal/state.ts
|
|
1418
|
-
const NonEmptyStringSchema$
|
|
1496
|
+
const NonEmptyStringSchema$1 = Schema.String.check(Schema.isNonEmpty());
|
|
1419
1497
|
const PutioCliConfigSchema = Schema.Struct({
|
|
1420
|
-
api_base_url: NonEmptyStringSchema$
|
|
1421
|
-
auth_token: Schema.optional(NonEmptyStringSchema$
|
|
1498
|
+
api_base_url: NonEmptyStringSchema$1,
|
|
1499
|
+
auth_token: Schema.optional(NonEmptyStringSchema$1)
|
|
1422
1500
|
});
|
|
1423
1501
|
const ResolvedAuthStateSchema = Schema.Struct({
|
|
1424
|
-
apiBaseUrl: NonEmptyStringSchema$
|
|
1425
|
-
configPath: NonEmptyStringSchema$
|
|
1426
|
-
source: Schema.
|
|
1427
|
-
token: NonEmptyStringSchema$
|
|
1502
|
+
apiBaseUrl: NonEmptyStringSchema$1,
|
|
1503
|
+
configPath: NonEmptyStringSchema$1,
|
|
1504
|
+
source: Schema.Literals(["env", "config"]),
|
|
1505
|
+
token: NonEmptyStringSchema$1
|
|
1428
1506
|
});
|
|
1429
1507
|
const AuthStatusSchema = Schema.Struct({
|
|
1430
|
-
apiBaseUrl: NonEmptyStringSchema$
|
|
1508
|
+
apiBaseUrl: NonEmptyStringSchema$1,
|
|
1431
1509
|
authenticated: Schema.Boolean,
|
|
1432
|
-
configPath: NonEmptyStringSchema$
|
|
1433
|
-
source: Schema.NullOr(Schema.
|
|
1510
|
+
configPath: NonEmptyStringSchema$1,
|
|
1511
|
+
source: Schema.NullOr(Schema.Literals(["env", "config"]))
|
|
1434
1512
|
});
|
|
1435
1513
|
var AuthStateError = class extends Data.TaggedError("AuthStateError") {};
|
|
1436
|
-
var CliState = class extends Context.
|
|
1514
|
+
var CliState = class extends Context.Service()("@putdotio/cli/CliState") {};
|
|
1437
1515
|
const decodePersistedConfig = Schema.decodeUnknownSync(PutioCliConfigSchema);
|
|
1438
1516
|
const mapFileSystemError = (error, message) => error instanceof AuthStateError ? error : new AuthStateError({ message });
|
|
1517
|
+
const resolveAuthRuntimeConfig = () => resolveCliRuntimeConfig().pipe(Effect.mapError((error) => new AuthStateError({ message: error.message })));
|
|
1439
1518
|
const parsePersistedConfig = (raw) => {
|
|
1440
1519
|
let value;
|
|
1441
1520
|
try {
|
|
@@ -1451,8 +1530,8 @@ const parsePersistedConfig = (raw) => {
|
|
|
1451
1530
|
};
|
|
1452
1531
|
const loadPersistedStateEffect = (configPath) => Effect.gen(function* () {
|
|
1453
1532
|
const fs = yield* FileSystem.FileSystem;
|
|
1454
|
-
const effectiveConfigPath = configPath ?? (yield*
|
|
1455
|
-
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}.`)));
|
|
1533
|
+
const effectiveConfigPath = configPath ?? (yield* resolveAuthRuntimeConfig()).configPath;
|
|
1534
|
+
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}.`)));
|
|
1456
1535
|
if (rawConfig === null) return null;
|
|
1457
1536
|
return yield* Effect.try({
|
|
1458
1537
|
try: () => parsePersistedConfig(rawConfig),
|
|
@@ -1462,7 +1541,7 @@ const loadPersistedStateEffect = (configPath) => Effect.gen(function* () {
|
|
|
1462
1541
|
const savePersistedStateEffect = (state, configPath) => Effect.gen(function* () {
|
|
1463
1542
|
const fs = yield* FileSystem.FileSystem;
|
|
1464
1543
|
const runtime = yield* CliRuntime;
|
|
1465
|
-
const effectiveConfigPath = configPath ?? (yield*
|
|
1544
|
+
const effectiveConfigPath = configPath ?? (yield* resolveAuthRuntimeConfig()).configPath;
|
|
1466
1545
|
const existingConfig = yield* loadPersistedStateEffect(effectiveConfigPath);
|
|
1467
1546
|
const persistedState = {
|
|
1468
1547
|
api_base_url: state.apiBaseUrl ?? existingConfig?.api_base_url ?? DEFAULT_PUTIO_API_BASE_URL,
|
|
@@ -1479,7 +1558,7 @@ const savePersistedStateEffect = (state, configPath) => Effect.gen(function* ()
|
|
|
1479
1558
|
const clearPersistedStateEffect = (configPath) => Effect.gen(function* () {
|
|
1480
1559
|
const fs = yield* FileSystem.FileSystem;
|
|
1481
1560
|
const runtime = yield* CliRuntime;
|
|
1482
|
-
const effectiveConfigPath = configPath ?? (yield*
|
|
1561
|
+
const effectiveConfigPath = configPath ?? (yield* resolveAuthRuntimeConfig()).configPath;
|
|
1483
1562
|
const existingConfig = yield* loadPersistedStateEffect(effectiveConfigPath);
|
|
1484
1563
|
if (existingConfig && existingConfig.api_base_url !== DEFAULT_PUTIO_API_BASE_URL) {
|
|
1485
1564
|
const nextConfig = { api_base_url: existingConfig.api_base_url };
|
|
@@ -1490,7 +1569,7 @@ const clearPersistedStateEffect = (configPath) => Effect.gen(function* () {
|
|
|
1490
1569
|
return { configPath: effectiveConfigPath };
|
|
1491
1570
|
});
|
|
1492
1571
|
const getAuthStatusEffect = () => Effect.gen(function* () {
|
|
1493
|
-
const runtime = yield*
|
|
1572
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1494
1573
|
if (runtime.token) return {
|
|
1495
1574
|
authenticated: true,
|
|
1496
1575
|
source: "env",
|
|
@@ -1511,7 +1590,7 @@ const getAuthStatusEffect = () => Effect.gen(function* () {
|
|
|
1511
1590
|
};
|
|
1512
1591
|
});
|
|
1513
1592
|
const resolveAuthStateEffect = () => Effect.gen(function* () {
|
|
1514
|
-
const runtime = yield*
|
|
1593
|
+
const runtime = yield* resolveAuthRuntimeConfig();
|
|
1515
1594
|
if (runtime.token) return {
|
|
1516
1595
|
token: runtime.token,
|
|
1517
1596
|
apiBaseUrl: runtime.apiBaseUrl,
|
|
@@ -1542,60 +1621,73 @@ const getAuthStatus = () => Effect.flatMap(CliState, (state) => state.getAuthSta
|
|
|
1542
1621
|
const resolveAuthState = () => Effect.flatMap(CliState, (state) => state.resolveAuthState());
|
|
1543
1622
|
//#endregion
|
|
1544
1623
|
//#region src/internal/command.ts
|
|
1545
|
-
const outputOption =
|
|
1624
|
+
const outputOption = Flag.choice("output", [
|
|
1546
1625
|
"json",
|
|
1547
1626
|
"text",
|
|
1548
1627
|
"ndjson"
|
|
1549
|
-
]).pipe(
|
|
1550
|
-
const dryRunOption =
|
|
1551
|
-
const fieldsOption =
|
|
1552
|
-
const jsonOption =
|
|
1553
|
-
const pageAllOption =
|
|
1628
|
+
]).pipe(Flag.optional);
|
|
1629
|
+
const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withDefault(false));
|
|
1630
|
+
const fieldsOption = Flag.string("fields").pipe(Flag.optional);
|
|
1631
|
+
const jsonOption = Flag.string("json").pipe(Flag.optional);
|
|
1632
|
+
const pageAllOption = Flag.boolean("page-all").pipe(Flag.withDefault(false));
|
|
1554
1633
|
const defineBooleanOption = (name, options = {}) => {
|
|
1555
|
-
const option = options.defaultValue === void 0 ?
|
|
1634
|
+
const option = options.defaultValue === void 0 ? Flag.boolean(name) : Flag.boolean(name).pipe(Flag.withDefault(options.defaultValue));
|
|
1556
1635
|
return {
|
|
1557
1636
|
flag: booleanFlag(name, options),
|
|
1558
1637
|
option
|
|
1559
1638
|
};
|
|
1560
1639
|
};
|
|
1561
|
-
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
option
|
|
1640
|
+
function defineIntegerOption(name, options = {}) {
|
|
1641
|
+
const flag = integerFlag(name, {
|
|
1642
|
+
description: options.description,
|
|
1643
|
+
required: options.required ?? options.optional !== true
|
|
1644
|
+
});
|
|
1645
|
+
return options.optional === true ? {
|
|
1646
|
+
flag,
|
|
1647
|
+
option: Flag.integer(name).pipe(Flag.optional)
|
|
1648
|
+
} : {
|
|
1649
|
+
flag,
|
|
1650
|
+
option: Flag.integer(name)
|
|
1569
1651
|
};
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
}),
|
|
1581
|
-
option
|
|
1652
|
+
}
|
|
1653
|
+
function defineTextOption(name, options = {}) {
|
|
1654
|
+
const flag = stringFlag(name, {
|
|
1655
|
+
defaultValue: options.defaultValue,
|
|
1656
|
+
description: options.description,
|
|
1657
|
+
required: options.required ?? (options.defaultValue === void 0 && options.optional !== true)
|
|
1658
|
+
});
|
|
1659
|
+
if (options.defaultValue !== void 0) return {
|
|
1660
|
+
flag,
|
|
1661
|
+
option: Flag.string(name).pipe(Flag.withDefault(options.defaultValue))
|
|
1582
1662
|
};
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
flag
|
|
1588
|
-
|
|
1589
|
-
required: options.required ?? options.optional !== true
|
|
1590
|
-
}),
|
|
1591
|
-
option
|
|
1663
|
+
return options.optional === true ? {
|
|
1664
|
+
flag,
|
|
1665
|
+
option: Flag.string(name).pipe(Flag.optional)
|
|
1666
|
+
} : {
|
|
1667
|
+
flag,
|
|
1668
|
+
option: Flag.string(name)
|
|
1592
1669
|
};
|
|
1593
|
-
}
|
|
1670
|
+
}
|
|
1671
|
+
function defineChoiceOption(name, choices, options = {}) {
|
|
1672
|
+
const flag = enumFlag(name, choices, {
|
|
1673
|
+
description: options.description,
|
|
1674
|
+
required: options.required ?? options.optional !== true
|
|
1675
|
+
});
|
|
1676
|
+
return options.optional === true ? {
|
|
1677
|
+
flag,
|
|
1678
|
+
option: Flag.choice(name, choices).pipe(Flag.optional)
|
|
1679
|
+
} : {
|
|
1680
|
+
flag,
|
|
1681
|
+
option: Flag.choice(name, choices)
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1594
1684
|
const getOption = (option) => Option.getOrUndefined(option);
|
|
1595
1685
|
var CliCommandInputError = class extends Data.TaggedError("CliCommandInputError") {};
|
|
1596
1686
|
const PATH_TRAVERSAL_PATTERN = /(?:^|[\\/])\.\.(?:[\\/]|$)|%2e/iu;
|
|
1597
1687
|
const QUERY_OR_FRAGMENT_PATTERN = /[?#]/u;
|
|
1598
1688
|
const TOP_LEVEL_FIELD_PATTERN = /^[A-Za-z0-9_-]+$/u;
|
|
1689
|
+
const MAX_CURSOR_PAGES = 1e3;
|
|
1690
|
+
const MAX_CURSOR_ITEMS = 1e5;
|
|
1599
1691
|
const ownKeys = (value) => Object.keys(value);
|
|
1600
1692
|
const hasControlCharacters = (value) => [...value].some((character) => {
|
|
1601
1693
|
const codePoint = character.codePointAt(0);
|
|
@@ -1620,10 +1712,10 @@ const validateNameLikeInput = (label, value) => validateSafeString({
|
|
|
1620
1712
|
const parseRequestedFields = (raw) => {
|
|
1621
1713
|
const parts = raw.split(",").map((part) => part.trim());
|
|
1622
1714
|
if (parts.length === 0 || parts.some((part) => part.length === 0)) throw new CliCommandInputError({ message: "Expected `--fields` to be a comma-separated list of top-level field names." });
|
|
1623
|
-
return [...new Set(parts.map((part) => validateSafeString({
|
|
1624
|
-
label: `\`--fields\` selector
|
|
1715
|
+
return [...new Set(parts.map((part, index) => validateSafeString({
|
|
1716
|
+
label: `\`--fields\` selector #${index + 1}`,
|
|
1625
1717
|
pattern: TOP_LEVEL_FIELD_PATTERN,
|
|
1626
|
-
patternMessage:
|
|
1718
|
+
patternMessage: `\`--fields\` selector #${index + 1} only accepts top-level field names without dots, brackets, or slashes.`,
|
|
1627
1719
|
value: part
|
|
1628
1720
|
})))];
|
|
1629
1721
|
};
|
|
@@ -1637,6 +1729,17 @@ const readPageItems = (value, itemKey, command) => {
|
|
|
1637
1729
|
if (!Array.isArray(items)) throw new CliCommandInputError({ message: `Expected \`${command}\` responses to include an array at \`${itemKey}\`.` });
|
|
1638
1730
|
return items;
|
|
1639
1731
|
};
|
|
1732
|
+
const assertCursorPageBudget = (input) => {
|
|
1733
|
+
if (input.pageCount > MAX_CURSOR_PAGES) throw new CliCommandInputError({ message: `\`${input.command}\` pagination exceeded ${MAX_CURSOR_PAGES} pages.` });
|
|
1734
|
+
if (input.itemCount > MAX_CURSOR_ITEMS) throw new CliCommandInputError({ message: `\`${input.command}\` pagination exceeded ${MAX_CURSOR_ITEMS} items.` });
|
|
1735
|
+
};
|
|
1736
|
+
const assertCursorNotSeen = (input) => {
|
|
1737
|
+
if (input.seenCursors.has(input.cursor)) throw new CliCommandInputError({ message: `\`${input.command}\` pagination returned a repeated cursor.` });
|
|
1738
|
+
input.seenCursors.add(input.cursor);
|
|
1739
|
+
};
|
|
1740
|
+
const assertCursorNotRepeated = (input) => {
|
|
1741
|
+
if (input.cursor !== null && input.seenCursors.has(input.cursor)) throw new CliCommandInputError({ message: `\`${input.command}\` pagination returned a repeated cursor.` });
|
|
1742
|
+
};
|
|
1640
1743
|
const integerPattern = /^-?\d+$/;
|
|
1641
1744
|
const parseRepeatedIntegers = (values) => {
|
|
1642
1745
|
const parsed = [];
|
|
@@ -1646,14 +1749,14 @@ const parseRepeatedIntegers = (values) => {
|
|
|
1646
1749
|
}
|
|
1647
1750
|
return Option.some(parsed);
|
|
1648
1751
|
};
|
|
1649
|
-
const parseRepeatedIntegerOption = (name) =>
|
|
1752
|
+
const parseRepeatedIntegerOption = (name) => Flag.string(name).pipe(Flag.atLeast(0), Flag.filterMap(parseRepeatedIntegers, () => `Expected \`--${name}\` values to be integers.`));
|
|
1650
1753
|
const defineRepeatedIntegerOption = (name, options = {}) => ({
|
|
1651
1754
|
flag: repeatedIntegerFlag(name, options),
|
|
1652
1755
|
option: parseRepeatedIntegerOption(name)
|
|
1653
1756
|
});
|
|
1654
1757
|
const defineRepeatedTextOption = (name, options = {}) => ({
|
|
1655
1758
|
flag: repeatedStringFlag(name, options),
|
|
1656
|
-
option:
|
|
1759
|
+
option: Flag.string(name).pipe(Flag.atLeast(0))
|
|
1657
1760
|
});
|
|
1658
1761
|
const mapInputError = (error, fallbackMessage) => error instanceof CliCommandInputError ? error : new CliCommandInputError({ message: fallbackMessage });
|
|
1659
1762
|
const decodeJsonOption = (schema, raw) => Effect.try({
|
|
@@ -1701,11 +1804,36 @@ const selectTopLevelFields = (input) => Effect.try({
|
|
|
1701
1804
|
const collectAllCursorPages = (input) => Effect.gen(function* () {
|
|
1702
1805
|
if (!input.pageAll) return input.initial;
|
|
1703
1806
|
const collectedItems = [...readPageItems(input.initial, input.itemKey, input.command)];
|
|
1807
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
1704
1808
|
let cursor = readCursor(input.initial);
|
|
1809
|
+
let pageCount = 1;
|
|
1810
|
+
assertCursorPageBudget({
|
|
1811
|
+
command: input.command,
|
|
1812
|
+
itemCount: collectedItems.length,
|
|
1813
|
+
pageCount
|
|
1814
|
+
});
|
|
1705
1815
|
while (cursor !== null) {
|
|
1816
|
+
assertCursorNotSeen({
|
|
1817
|
+
command: input.command,
|
|
1818
|
+
cursor,
|
|
1819
|
+
seenCursors
|
|
1820
|
+
});
|
|
1706
1821
|
const nextPage = yield* input.continueWithCursor(cursor);
|
|
1707
|
-
|
|
1708
|
-
|
|
1822
|
+
const nextCursor = readCursor(nextPage);
|
|
1823
|
+
assertCursorNotRepeated({
|
|
1824
|
+
command: input.command,
|
|
1825
|
+
cursor: nextCursor,
|
|
1826
|
+
seenCursors
|
|
1827
|
+
});
|
|
1828
|
+
const pageItems = readPageItems(nextPage, input.itemKey, input.command);
|
|
1829
|
+
pageCount += 1;
|
|
1830
|
+
collectedItems.push(...pageItems);
|
|
1831
|
+
assertCursorPageBudget({
|
|
1832
|
+
command: input.command,
|
|
1833
|
+
itemCount: collectedItems.length,
|
|
1834
|
+
pageCount
|
|
1835
|
+
});
|
|
1836
|
+
cursor = nextCursor;
|
|
1709
1837
|
}
|
|
1710
1838
|
return {
|
|
1711
1839
|
...input.initial,
|
|
@@ -1740,7 +1868,18 @@ const writeReadPages = (input) => Effect.gen(function* () {
|
|
|
1740
1868
|
});
|
|
1741
1869
|
}
|
|
1742
1870
|
let current = input.initial;
|
|
1871
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
1872
|
+
let pageCount = 1;
|
|
1873
|
+
let streamedItemCount = 0;
|
|
1743
1874
|
while (true) {
|
|
1875
|
+
if (input.itemKey) {
|
|
1876
|
+
streamedItemCount += readPageItems(current, input.itemKey, input.command).length;
|
|
1877
|
+
assertCursorPageBudget({
|
|
1878
|
+
command: input.command,
|
|
1879
|
+
itemCount: streamedItemCount,
|
|
1880
|
+
pageCount
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1744
1883
|
yield* writeOutput(yield* selectTopLevelFields({
|
|
1745
1884
|
command: input.command,
|
|
1746
1885
|
requestedFields: input.controls.requestedFields,
|
|
@@ -1749,7 +1888,19 @@ const writeReadPages = (input) => Effect.gen(function* () {
|
|
|
1749
1888
|
if (!input.controls.pageAll || !input.continueWithCursor || !input.itemKey) return;
|
|
1750
1889
|
const cursor = readCursor(current);
|
|
1751
1890
|
if (cursor === null) return;
|
|
1752
|
-
|
|
1891
|
+
assertCursorNotSeen({
|
|
1892
|
+
command: input.command,
|
|
1893
|
+
cursor,
|
|
1894
|
+
seenCursors
|
|
1895
|
+
});
|
|
1896
|
+
const nextPage = yield* input.continueWithCursor(cursor);
|
|
1897
|
+
assertCursorNotRepeated({
|
|
1898
|
+
command: input.command,
|
|
1899
|
+
cursor: readCursor(nextPage),
|
|
1900
|
+
seenCursors
|
|
1901
|
+
});
|
|
1902
|
+
current = nextPage;
|
|
1903
|
+
pageCount += 1;
|
|
1753
1904
|
}
|
|
1754
1905
|
});
|
|
1755
1906
|
const renderDryRunPlanTerminal = (value) => [
|
|
@@ -1851,15 +2002,13 @@ const previewCodeOption = previewCodeConfig.option;
|
|
|
1851
2002
|
const waitForOpenShortcut = (url) => Effect.gen(function* () {
|
|
1852
2003
|
const runtimeService = yield* CliRuntime;
|
|
1853
2004
|
if (!runtimeService.isInteractiveTerminal) return false;
|
|
1854
|
-
const
|
|
1855
|
-
if (!(yield* terminal.isTTY)) return false;
|
|
1856
|
-
const input = yield* terminal.readInput;
|
|
2005
|
+
const input = yield* (yield* Terminal.Terminal).readInput;
|
|
1857
2006
|
while (true) {
|
|
1858
|
-
const event = yield*
|
|
2007
|
+
const event = yield* Queue.take(input);
|
|
1859
2008
|
const keyInput = Option.getOrElse(event.input, () => "").toLowerCase();
|
|
1860
2009
|
if (event.key.name === "o" || keyInput === "o") return yield* runtimeService.openExternal(url);
|
|
1861
2010
|
}
|
|
1862
|
-
}).pipe(Effect.
|
|
2011
|
+
}).pipe(Effect.catchIf(Cause.isDone, () => Effect.succeed(false)));
|
|
1863
2012
|
const renderAuthStatus = (status) => status.authenticated ? [
|
|
1864
2013
|
translate("cli.auth.status.authenticatedYes"),
|
|
1865
2014
|
translate("cli.auth.status.source", { value: status.source ?? translate("cli.auth.status.unknown") }),
|
|
@@ -1899,7 +2048,7 @@ const authLogin = Command.make("login", {
|
|
|
1899
2048
|
`code: ${code}`,
|
|
1900
2049
|
translate("cli.auth.login.waiting")
|
|
1901
2050
|
].join("\n");
|
|
1902
|
-
yield* outputMode === "terminal" ? Console.log(instructionMessage) :
|
|
2051
|
+
yield* outputMode === "terminal" ? Console.log(instructionMessage) : runtimeService.writeStderr(`${instructionMessage}\n`);
|
|
1903
2052
|
const openShortcutFiber = outputMode === "terminal" && !browserOpened ? yield* Effect.forkScoped(waitForOpenShortcut(linkUrl)) : void 0;
|
|
1904
2053
|
yield* Effect.addFinalizer(() => openShortcutFiber ? Fiber.interrupt(openShortcutFiber) : Effect.void);
|
|
1905
2054
|
const { configPath, state } = yield* savePersistedState({
|
|
@@ -2361,15 +2510,15 @@ const fileTypeOption = fileTypeConfig.option;
|
|
|
2361
2510
|
const sortByOption = sortByConfig.option;
|
|
2362
2511
|
const optionalFileIdOption = optionalFileIdConfig.option;
|
|
2363
2512
|
const optionalFileNameOption = optionalFileNameConfig.option;
|
|
2364
|
-
const
|
|
2365
|
-
const NonEmptyIdsSchema$1 = Schema.Array(Schema.Number).
|
|
2513
|
+
const NonBlankStringSchema = Schema.String.check(Schema.makeFilter((value) => value.trim().length > 0 ? void 0 : "Expected a non-empty string"));
|
|
2514
|
+
const NonEmptyIdsSchema$1 = Schema.Array(Schema.Number).check(Schema.isNonEmpty());
|
|
2366
2515
|
const FilesMkdirInputSchema = Schema.Struct({
|
|
2367
|
-
name:
|
|
2516
|
+
name: NonBlankStringSchema,
|
|
2368
2517
|
parent_id: Schema.optional(Schema.Number)
|
|
2369
2518
|
});
|
|
2370
2519
|
const FilesRenameInputSchema = Schema.Struct({
|
|
2371
2520
|
file_id: Schema.Number,
|
|
2372
|
-
name:
|
|
2521
|
+
name: NonBlankStringSchema
|
|
2373
2522
|
});
|
|
2374
2523
|
const FilesDeleteInputSchema = Schema.Struct({
|
|
2375
2524
|
ids: NonEmptyIdsSchema$1,
|
|
@@ -2812,8 +2961,8 @@ const WATCH_TERMINAL_STATUSES = [
|
|
|
2812
2961
|
"ERROR",
|
|
2813
2962
|
"SEEDING"
|
|
2814
2963
|
];
|
|
2815
|
-
const NonEmptyIdsSchema = Schema.Array(Schema.Number).
|
|
2816
|
-
const TransfersAddInputSchema = Schema.Array(TransferAddInputSchema).
|
|
2964
|
+
const NonEmptyIdsSchema = Schema.Array(Schema.Number).check(Schema.isNonEmpty());
|
|
2965
|
+
const TransfersAddInputSchema = Schema.Array(TransferAddInputSchema).check(Schema.isNonEmpty());
|
|
2817
2966
|
const TransfersCancelInputSchema = Schema.Struct({ ids: NonEmptyIdsSchema });
|
|
2818
2967
|
const TransfersSingleIdInputSchema = Schema.Struct({ id: Schema.Number });
|
|
2819
2968
|
const TransfersCleanInputSchema = Schema.Struct({ ids: Schema.optional(NonEmptyIdsSchema) });
|
|
@@ -2928,12 +3077,15 @@ const transfersRetry = Command.make("retry", {
|
|
|
2928
3077
|
}));
|
|
2929
3078
|
const transfersClean = Command.make("clean", {
|
|
2930
3079
|
dryRun: dryRunOption,
|
|
2931
|
-
id: transferIdsOption.pipe(
|
|
3080
|
+
id: transferIdsOption.pipe(Flag.optional),
|
|
2932
3081
|
json: jsonOption,
|
|
2933
3082
|
output: outputOption
|
|
2934
3083
|
}, ({ dryRun, id, json, output }) => Effect.gen(function* () {
|
|
2935
3084
|
const input = yield* resolveMutationInput({
|
|
2936
|
-
buildFromFlags: () =>
|
|
3085
|
+
buildFromFlags: () => {
|
|
3086
|
+
const ids = Option.getOrUndefined(id);
|
|
3087
|
+
return ids === void 0 || ids.length === 0 ? {} : { ids };
|
|
3088
|
+
},
|
|
2937
3089
|
json,
|
|
2938
3090
|
schema: TransfersCleanInputSchema
|
|
2939
3091
|
});
|
|
@@ -3311,7 +3463,7 @@ const commandCatalog = decodeCommandSpecs([
|
|
|
3311
3463
|
]);
|
|
3312
3464
|
//#endregion
|
|
3313
3465
|
//#region src/internal/metadata.ts
|
|
3314
|
-
const NonEmptyStringSchema = Schema.String.
|
|
3466
|
+
const NonEmptyStringSchema = Schema.String.check(Schema.isNonEmpty());
|
|
3315
3467
|
const CliMetadataSchema = Schema.Struct({
|
|
3316
3468
|
agentDx: AgentDxScorecardSchema,
|
|
3317
3469
|
auth: Schema.Struct({
|
|
@@ -3386,4 +3538,4 @@ const describeCli = () => decodeCliMetadata({
|
|
|
3386
3538
|
version
|
|
3387
3539
|
});
|
|
3388
3540
|
//#endregion
|
|
3389
|
-
export {
|
|
3541
|
+
export { CliRuntimeLive as A, CliOutput as C, renderJson as D, isStructuredOutputMode as E, version as M, CliConfigLive 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, translate as j, CliRuntime 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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@putdotio/cli",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Agent-first CLI for the put.io API.",
|
|
5
5
|
"homepage": "https://github.com/putdotio/putio-cli#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"check": "vp check .",
|
|
35
35
|
"coverage": "vp test --coverage",
|
|
36
36
|
"dev": "vp pack --watch",
|
|
37
|
+
"prepare": "./scripts/prepare-effect.sh",
|
|
37
38
|
"prepack": "vp pack",
|
|
38
39
|
"smoke:pack": "node ./scripts/smoke-packed-install.mjs",
|
|
39
40
|
"test": "vp test",
|
|
@@ -42,18 +43,17 @@
|
|
|
42
43
|
"verify": "vp check . && vp pack && vp test && vp test --coverage"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
|
-
"@effect/
|
|
46
|
-
"@
|
|
47
|
-
"@effect/platform-node": "0.104.1",
|
|
48
|
-
"@putdotio/sdk": "^9.1.0",
|
|
46
|
+
"@effect/platform-node": "4.0.0-beta.66",
|
|
47
|
+
"@putdotio/sdk": "^9.3.0",
|
|
49
48
|
"cli-table3": "^0.6.5",
|
|
50
|
-
"effect": "
|
|
49
|
+
"effect": "4.0.0-beta.66",
|
|
51
50
|
"i18next": "^25.5.2"
|
|
52
51
|
},
|
|
53
52
|
"devDependencies": {
|
|
54
53
|
"@types/node": "^24.0.0",
|
|
55
54
|
"@vitest/coverage-v8": "^4.1.5",
|
|
56
55
|
"esbuild": "^0.27.0",
|
|
56
|
+
"is-ci": "^4.1.0",
|
|
57
57
|
"postject": "^1.0.0-alpha.6",
|
|
58
58
|
"typescript": "^5.9.3",
|
|
59
59
|
"vite-plus": "0.1.20"
|