stow-cli 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-2A2CFVBC.js → app-JPUDM4LJ.js} +1 -1
- package/dist/backfill-QZTVNJZM.js +61 -0
- package/dist/buckets-JQKZ3PH3.js +63 -0
- package/dist/{chunk-ZDVARBCV.js → chunk-ELSDWMEB.js} +8 -2
- package/dist/chunk-WAQSCS57.js +38 -0
- package/dist/cli.js +508 -15
- package/dist/drops-QXGTYB5E.js +39 -0
- package/dist/files-OCYICIAM.js +165 -0
- package/dist/health-YMZTZUFX.js +52 -0
- package/dist/jobs-6JY4SVV5.js +92 -0
- package/dist/jobs-OKKGKSIH.js +83 -0
- package/dist/maintenance-EZUHQK5Q.js +86 -0
- package/dist/profiles-GWLBEEIT.js +50 -0
- package/dist/queues-EQX7EJZF.js +51 -0
- package/dist/search-2PXM6XQ6.js +108 -0
- package/dist/tags-LSVEIIUF.js +46 -0
- package/dist/{upload-MI6VFGTC.js → upload-ESYKBYHA.js} +3 -3
- package/dist/{whoami-754O5IML.js → whoami-4UYMSRVP.js} +1 -1
- package/package.json +1 -1
- package/dist/app-2H4TLSN7.js +0 -239
- package/dist/app-HYCPA7GA.js +0 -239
- package/dist/app-IZGSPZPX.js +0 -239
- package/dist/app-UGUM75MC.js +0 -239
- package/dist/app-XPOEAZJC.js +0 -239
- package/dist/app-YITP5APT.js +0 -233
- package/dist/chunk-2AORPTQB.js +0 -23
- package/dist/chunk-FEMMZ4YZ.js +0 -125
- package/dist/chunk-LYCXXF2T.js +0 -79
- package/dist/chunk-MHRMBH4Y.js +0 -36
- package/dist/chunk-R5CCBTXZ.js +0 -79
- package/dist/chunk-YRHPOFJT.js +0 -115
- package/dist/cli.d.ts +0 -1
- package/dist/delete-AECEJX5W.js +0 -18
- package/dist/delete-KYOZEODD.js +0 -18
- package/dist/delete-OLOAJRRO.js +0 -18
- package/dist/delete-V4EY4UBG.js +0 -18
- package/dist/list-7A3VZA2T.js +0 -97
- package/dist/list-DHXVIMRI.js +0 -94
- package/dist/list-DJEAKEZJ.js +0 -106
- package/dist/list-DQRU6QHO.js +0 -106
- package/dist/list-I5A6LTHX.js +0 -106
- package/dist/list-KEQPJY7I.js +0 -109
- package/dist/list-Z3MPT6MI.js +0 -109
- package/dist/open-2YNHG3MA.js +0 -15
- package/dist/upload-3NS5O3UL.js +0 -120
- package/dist/upload-B2PGW3AN.js +0 -125
- package/dist/upload-DQDBDIDI.js +0 -92
- package/dist/upload-FGNGNPC3.js +0 -93
- package/dist/upload-HKUPWTK2.js +0 -173
- package/dist/upload-JDVSJVWK.js +0 -173
- package/dist/upload-K4H7ZVRW.js +0 -98
- package/dist/whoami-2SLCNVKP.js +0 -27
- package/dist/whoami-DOMX3Z5K.js +0 -28
- package/dist/whoami-IPMCUEUH.js +0 -27
- package/dist/whoami-JSQA2IDN.js +0 -27
- package/dist/whoami-RKH5HHPR.js +0 -27
package/dist/app-XPOEAZJC.js
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
apiRequest
|
|
3
|
-
} from "./chunk-LYCXXF2T.js";
|
|
4
|
-
import {
|
|
5
|
-
formatBytes
|
|
6
|
-
} from "./chunk-ZDVARBCV.js";
|
|
7
|
-
import "./chunk-OZ7QQTIZ.js";
|
|
8
|
-
|
|
9
|
-
// src/interactive/app.tsx
|
|
10
|
-
import { render } from "ink";
|
|
11
|
-
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
12
|
-
|
|
13
|
-
// src/interactive/components/bucket-list.tsx
|
|
14
|
-
import { Box as Box3, Text as Text3, useApp, useInput } from "ink";
|
|
15
|
-
import Spinner from "ink-spinner";
|
|
16
|
-
import { useEffect, useState } from "react";
|
|
17
|
-
|
|
18
|
-
// src/interactive/components/header.tsx
|
|
19
|
-
import { Box, Text } from "ink";
|
|
20
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
-
function Header({ path, email, size }) {
|
|
22
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
23
|
-
/* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", width: "100%", children: [
|
|
24
|
-
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
25
|
-
"STOW",
|
|
26
|
-
path ? ` > ${path}` : ""
|
|
27
|
-
] }),
|
|
28
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: size || email || "" })
|
|
29
|
-
] }),
|
|
30
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(50) })
|
|
31
|
-
] });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// src/interactive/components/status-bar.tsx
|
|
35
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
36
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
37
|
-
function StatusBar({ hints }) {
|
|
38
|
-
return /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: hints.map((h, i) => /* @__PURE__ */ jsx2(Box2, { marginRight: 2, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
39
|
-
"[",
|
|
40
|
-
h.key,
|
|
41
|
-
"]",
|
|
42
|
-
h.label,
|
|
43
|
-
i < hints.length - 1 ? "" : ""
|
|
44
|
-
] }) }, h.key)) });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// src/interactive/components/bucket-list.tsx
|
|
48
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
49
|
-
function BucketList({ onSelect, email }) {
|
|
50
|
-
const { exit } = useApp();
|
|
51
|
-
const [buckets, setBuckets] = useState([]);
|
|
52
|
-
const [loading, setLoading] = useState(true);
|
|
53
|
-
const [error, setError] = useState(null);
|
|
54
|
-
const [cursor, setCursor] = useState(0);
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
apiRequest("/api/buckets").then((data) => {
|
|
57
|
-
setBuckets(data.buckets);
|
|
58
|
-
setLoading(false);
|
|
59
|
-
}).catch((err) => {
|
|
60
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
61
|
-
setLoading(false);
|
|
62
|
-
});
|
|
63
|
-
}, []);
|
|
64
|
-
useInput((input, key) => {
|
|
65
|
-
if (input === "q") {
|
|
66
|
-
exit();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (key.upArrow) {
|
|
70
|
-
setCursor((c) => Math.max(0, c - 1));
|
|
71
|
-
} else if (key.downArrow) {
|
|
72
|
-
setCursor((c) => Math.min(buckets.length - 1, c + 1));
|
|
73
|
-
} else if (key.return && buckets.length > 0) {
|
|
74
|
-
onSelect(buckets[cursor]);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
if (loading) {
|
|
78
|
-
return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { children: [
|
|
79
|
-
/* @__PURE__ */ jsx3(Spinner, { type: "dots" }),
|
|
80
|
-
" Loading buckets..."
|
|
81
|
-
] }) });
|
|
82
|
-
}
|
|
83
|
-
if (error) {
|
|
84
|
-
return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
|
|
85
|
-
"Error: ",
|
|
86
|
-
error
|
|
87
|
-
] }) });
|
|
88
|
-
}
|
|
89
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
90
|
-
/* @__PURE__ */ jsx3(Header, { email }),
|
|
91
|
-
buckets.length === 0 ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "No buckets yet. Press 'n' to create one." }) : buckets.map((b, i) => /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: i === cursor ? "cyan" : void 0, children: [
|
|
92
|
-
i === cursor ? "\u25B8 " : " ",
|
|
93
|
-
b.name.padEnd(20),
|
|
94
|
-
`${b.fileCount} files`.padEnd(14),
|
|
95
|
-
formatBytes(b.usageBytes)
|
|
96
|
-
] }) }, b.id)),
|
|
97
|
-
/* @__PURE__ */ jsx3(
|
|
98
|
-
StatusBar,
|
|
99
|
-
{
|
|
100
|
-
hints: [
|
|
101
|
-
{ key: "\u2191\u2193", label: "navigate" },
|
|
102
|
-
{ key: "\u21B5", label: "open" },
|
|
103
|
-
{ key: "q", label: "uit" }
|
|
104
|
-
]
|
|
105
|
-
}
|
|
106
|
-
)
|
|
107
|
-
] });
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// src/interactive/components/file-list.tsx
|
|
111
|
-
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
112
|
-
import Spinner2 from "ink-spinner";
|
|
113
|
-
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
114
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
115
|
-
function FileList({ bucket, onBack }) {
|
|
116
|
-
const [files, setFiles] = useState2([]);
|
|
117
|
-
const [loading, setLoading] = useState2(true);
|
|
118
|
-
const [error, setError] = useState2(null);
|
|
119
|
-
const [cursor, setCursor] = useState2(0);
|
|
120
|
-
const [nextCursor, setNextCursor] = useState2(null);
|
|
121
|
-
const [copied, setCopied] = useState2(false);
|
|
122
|
-
useEffect2(() => {
|
|
123
|
-
loadFiles();
|
|
124
|
-
}, []);
|
|
125
|
-
function loadFiles(pageCursor) {
|
|
126
|
-
setLoading(true);
|
|
127
|
-
const params = new URLSearchParams({ bucket: bucket.name });
|
|
128
|
-
if (pageCursor) {
|
|
129
|
-
params.set("cursor", pageCursor);
|
|
130
|
-
}
|
|
131
|
-
apiRequest(
|
|
132
|
-
`/api/files?${params}`
|
|
133
|
-
).then((data) => {
|
|
134
|
-
setFiles(
|
|
135
|
-
(prev) => pageCursor ? [...prev, ...data.files] : data.files
|
|
136
|
-
);
|
|
137
|
-
setNextCursor(data.nextCursor);
|
|
138
|
-
setLoading(false);
|
|
139
|
-
}).catch((err) => {
|
|
140
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
141
|
-
setLoading(false);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
useInput2((input, key) => {
|
|
145
|
-
if (key.escape || key.backspace || key.leftArrow && !loading) {
|
|
146
|
-
onBack();
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
if (key.upArrow) {
|
|
150
|
-
setCursor((c) => Math.max(0, c - 1));
|
|
151
|
-
} else if (key.downArrow) {
|
|
152
|
-
setCursor((c) => {
|
|
153
|
-
const next = Math.min(files.length - 1, c + 1);
|
|
154
|
-
if (next >= files.length - 3 && nextCursor && !loading) {
|
|
155
|
-
loadFiles(nextCursor);
|
|
156
|
-
}
|
|
157
|
-
return next;
|
|
158
|
-
});
|
|
159
|
-
} else if (input === "c" && files.length > 0) {
|
|
160
|
-
import("clipboardy").then(({ default: clipboard }) => {
|
|
161
|
-
clipboard.write(files[cursor].url).then(() => {
|
|
162
|
-
setCopied(true);
|
|
163
|
-
setTimeout(() => setCopied(false), 2e3);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
if (error) {
|
|
169
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
170
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
171
|
-
"Error: ",
|
|
172
|
-
error
|
|
173
|
-
] }),
|
|
174
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Press Esc to go back." })
|
|
175
|
-
] });
|
|
176
|
-
}
|
|
177
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
178
|
-
/* @__PURE__ */ jsx4(Header, { path: bucket.name, size: formatBytes(bucket.usageBytes) }),
|
|
179
|
-
loading && files.length === 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
180
|
-
/* @__PURE__ */ jsx4(Spinner2, { type: "dots" }),
|
|
181
|
-
" Loading files..."
|
|
182
|
-
] }),
|
|
183
|
-
!loading && files.length === 0 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No files in this bucket." }),
|
|
184
|
-
files.length > 0 && files.map((f, i) => /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: i === cursor ? "cyan" : void 0, children: [
|
|
185
|
-
i === cursor ? "\u25B8 " : " ",
|
|
186
|
-
f.key.padEnd(28),
|
|
187
|
-
formatBytes(f.size).padEnd(10),
|
|
188
|
-
f.lastModified.split("T")[0]
|
|
189
|
-
] }) }, f.key)),
|
|
190
|
-
loading && files.length > 0 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
191
|
-
/* @__PURE__ */ jsx4(Spinner2, { type: "dots" }),
|
|
192
|
-
" Loading more..."
|
|
193
|
-
] }),
|
|
194
|
-
copied && /* @__PURE__ */ jsx4(Text4, { color: "green", children: "Copied URL to clipboard!" }),
|
|
195
|
-
/* @__PURE__ */ jsx4(
|
|
196
|
-
StatusBar,
|
|
197
|
-
{
|
|
198
|
-
hints: [
|
|
199
|
-
{ key: "\u2191\u2193", label: "navigate" },
|
|
200
|
-
{ key: "c", label: "opy url" },
|
|
201
|
-
{ key: "\u2190", label: "back" }
|
|
202
|
-
]
|
|
203
|
-
}
|
|
204
|
-
)
|
|
205
|
-
] });
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// src/interactive/app.tsx
|
|
209
|
-
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
210
|
-
function App() {
|
|
211
|
-
const [screen, setScreen] = useState3({ type: "buckets" });
|
|
212
|
-
const [email, setEmail] = useState3();
|
|
213
|
-
useEffect3(() => {
|
|
214
|
-
apiRequest("/api/whoami").then((data) => setEmail(data.user.email)).catch(() => {
|
|
215
|
-
});
|
|
216
|
-
}, []);
|
|
217
|
-
if (screen.type === "files") {
|
|
218
|
-
return /* @__PURE__ */ jsx5(
|
|
219
|
-
FileList,
|
|
220
|
-
{
|
|
221
|
-
bucket: screen.bucket,
|
|
222
|
-
onBack: () => setScreen({ type: "buckets" })
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
return /* @__PURE__ */ jsx5(
|
|
227
|
-
BucketList,
|
|
228
|
-
{
|
|
229
|
-
email,
|
|
230
|
-
onSelect: (bucket) => setScreen({ type: "files", bucket })
|
|
231
|
-
}
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
function startInteractive() {
|
|
235
|
-
render(/* @__PURE__ */ jsx5(App, {}));
|
|
236
|
-
}
|
|
237
|
-
export {
|
|
238
|
-
startInteractive
|
|
239
|
-
};
|
package/dist/app-YITP5APT.js
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
formatBytes
|
|
3
|
-
} from "./chunk-MHRMBH4Y.js";
|
|
4
|
-
import {
|
|
5
|
-
apiRequest
|
|
6
|
-
} from "./chunk-YRHPOFJT.js";
|
|
7
|
-
import "./chunk-2AORPTQB.js";
|
|
8
|
-
|
|
9
|
-
// src/interactive/app.tsx
|
|
10
|
-
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
11
|
-
import { render } from "ink";
|
|
12
|
-
|
|
13
|
-
// src/interactive/components/bucket-list.tsx
|
|
14
|
-
import { useState, useEffect } from "react";
|
|
15
|
-
import { Box as Box3, Text as Text3, useInput, useApp } from "ink";
|
|
16
|
-
import Spinner from "ink-spinner";
|
|
17
|
-
|
|
18
|
-
// src/interactive/components/header.tsx
|
|
19
|
-
import { Box, Text } from "ink";
|
|
20
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
-
function Header({ path, email, size }) {
|
|
22
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
23
|
-
/* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", width: "100%", children: [
|
|
24
|
-
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
25
|
-
"STOW",
|
|
26
|
-
path ? ` > ${path}` : ""
|
|
27
|
-
] }),
|
|
28
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: size || email || "" })
|
|
29
|
-
] }),
|
|
30
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(50) })
|
|
31
|
-
] });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// src/interactive/components/status-bar.tsx
|
|
35
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
36
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
37
|
-
function StatusBar({ hints }) {
|
|
38
|
-
return /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: hints.map((h, i) => /* @__PURE__ */ jsx2(Box2, { marginRight: 2, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
39
|
-
"[",
|
|
40
|
-
h.key,
|
|
41
|
-
"]",
|
|
42
|
-
h.label,
|
|
43
|
-
i < hints.length - 1 ? "" : ""
|
|
44
|
-
] }) }, h.key)) });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// src/interactive/components/bucket-list.tsx
|
|
48
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
49
|
-
function BucketList({ onSelect, email }) {
|
|
50
|
-
const { exit } = useApp();
|
|
51
|
-
const [buckets, setBuckets] = useState([]);
|
|
52
|
-
const [loading, setLoading] = useState(true);
|
|
53
|
-
const [error, setError] = useState(null);
|
|
54
|
-
const [cursor, setCursor] = useState(0);
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
apiRequest("/api/buckets").then((data) => {
|
|
57
|
-
setBuckets(data.buckets);
|
|
58
|
-
setLoading(false);
|
|
59
|
-
}).catch((err) => {
|
|
60
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
61
|
-
setLoading(false);
|
|
62
|
-
});
|
|
63
|
-
}, []);
|
|
64
|
-
useInput((input, key) => {
|
|
65
|
-
if (input === "q") {
|
|
66
|
-
exit();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (key.upArrow) {
|
|
70
|
-
setCursor((c) => Math.max(0, c - 1));
|
|
71
|
-
} else if (key.downArrow) {
|
|
72
|
-
setCursor((c) => Math.min(buckets.length - 1, c + 1));
|
|
73
|
-
} else if (key.return && buckets.length > 0) {
|
|
74
|
-
onSelect(buckets[cursor]);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
if (loading) {
|
|
78
|
-
return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { children: [
|
|
79
|
-
/* @__PURE__ */ jsx3(Spinner, { type: "dots" }),
|
|
80
|
-
" Loading buckets..."
|
|
81
|
-
] }) });
|
|
82
|
-
}
|
|
83
|
-
if (error) {
|
|
84
|
-
return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
|
|
85
|
-
"Error: ",
|
|
86
|
-
error
|
|
87
|
-
] }) });
|
|
88
|
-
}
|
|
89
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
90
|
-
/* @__PURE__ */ jsx3(Header, { email }),
|
|
91
|
-
buckets.length === 0 ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "No buckets yet. Press 'n' to create one." }) : buckets.map((b, i) => /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: i === cursor ? "cyan" : void 0, children: [
|
|
92
|
-
i === cursor ? "\u25B8 " : " ",
|
|
93
|
-
b.name.padEnd(20),
|
|
94
|
-
`${b.fileCount} files`.padEnd(14),
|
|
95
|
-
formatBytes(b.usageBytes)
|
|
96
|
-
] }) }, b.id)),
|
|
97
|
-
/* @__PURE__ */ jsx3(
|
|
98
|
-
StatusBar,
|
|
99
|
-
{
|
|
100
|
-
hints: [
|
|
101
|
-
{ key: "\u2191\u2193", label: "navigate" },
|
|
102
|
-
{ key: "\u21B5", label: "open" },
|
|
103
|
-
{ key: "q", label: "uit" }
|
|
104
|
-
]
|
|
105
|
-
}
|
|
106
|
-
)
|
|
107
|
-
] });
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// src/interactive/components/file-list.tsx
|
|
111
|
-
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
112
|
-
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
113
|
-
import Spinner2 from "ink-spinner";
|
|
114
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
115
|
-
function FileList({ bucket, onBack }) {
|
|
116
|
-
const [files, setFiles] = useState2([]);
|
|
117
|
-
const [loading, setLoading] = useState2(true);
|
|
118
|
-
const [error, setError] = useState2(null);
|
|
119
|
-
const [cursor, setCursor] = useState2(0);
|
|
120
|
-
const [nextCursor, setNextCursor] = useState2(null);
|
|
121
|
-
const [copied, setCopied] = useState2(false);
|
|
122
|
-
useEffect2(() => {
|
|
123
|
-
loadFiles();
|
|
124
|
-
}, []);
|
|
125
|
-
function loadFiles(pageCursor) {
|
|
126
|
-
setLoading(true);
|
|
127
|
-
const params = new URLSearchParams({ bucket: bucket.name });
|
|
128
|
-
if (pageCursor) params.set("cursor", pageCursor);
|
|
129
|
-
apiRequest(
|
|
130
|
-
`/api/files?${params}`
|
|
131
|
-
).then((data) => {
|
|
132
|
-
setFiles((prev) => pageCursor ? [...prev, ...data.files] : data.files);
|
|
133
|
-
setNextCursor(data.nextCursor);
|
|
134
|
-
setLoading(false);
|
|
135
|
-
}).catch((err) => {
|
|
136
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
137
|
-
setLoading(false);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
useInput2((input, key) => {
|
|
141
|
-
if (key.escape || key.backspace || key.leftArrow && !loading) {
|
|
142
|
-
onBack();
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
if (key.upArrow) {
|
|
146
|
-
setCursor((c) => Math.max(0, c - 1));
|
|
147
|
-
} else if (key.downArrow) {
|
|
148
|
-
setCursor((c) => {
|
|
149
|
-
const next = Math.min(files.length - 1, c + 1);
|
|
150
|
-
if (next >= files.length - 3 && nextCursor && !loading) {
|
|
151
|
-
loadFiles(nextCursor);
|
|
152
|
-
}
|
|
153
|
-
return next;
|
|
154
|
-
});
|
|
155
|
-
} else if (input === "c" && files.length > 0) {
|
|
156
|
-
import("clipboardy").then(({ default: clipboard }) => {
|
|
157
|
-
clipboard.write(files[cursor].url).then(() => {
|
|
158
|
-
setCopied(true);
|
|
159
|
-
setTimeout(() => setCopied(false), 2e3);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
if (error) {
|
|
165
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
166
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
167
|
-
"Error: ",
|
|
168
|
-
error
|
|
169
|
-
] }),
|
|
170
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Press Esc to go back." })
|
|
171
|
-
] });
|
|
172
|
-
}
|
|
173
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
174
|
-
/* @__PURE__ */ jsx4(Header, { path: bucket.name, size: formatBytes(bucket.usageBytes) }),
|
|
175
|
-
loading && files.length === 0 ? /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
176
|
-
/* @__PURE__ */ jsx4(Spinner2, { type: "dots" }),
|
|
177
|
-
" Loading files..."
|
|
178
|
-
] }) : files.length === 0 ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No files in this bucket." }) : files.map((f, i) => /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: i === cursor ? "cyan" : void 0, children: [
|
|
179
|
-
i === cursor ? "\u25B8 " : " ",
|
|
180
|
-
f.key.padEnd(28),
|
|
181
|
-
formatBytes(f.size).padEnd(10),
|
|
182
|
-
f.lastModified.split("T")[0]
|
|
183
|
-
] }) }, f.key)),
|
|
184
|
-
loading && files.length > 0 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
185
|
-
/* @__PURE__ */ jsx4(Spinner2, { type: "dots" }),
|
|
186
|
-
" Loading more..."
|
|
187
|
-
] }),
|
|
188
|
-
copied && /* @__PURE__ */ jsx4(Text4, { color: "green", children: "Copied URL to clipboard!" }),
|
|
189
|
-
/* @__PURE__ */ jsx4(
|
|
190
|
-
StatusBar,
|
|
191
|
-
{
|
|
192
|
-
hints: [
|
|
193
|
-
{ key: "\u2191\u2193", label: "navigate" },
|
|
194
|
-
{ key: "c", label: "opy url" },
|
|
195
|
-
{ key: "\u2190", label: "back" }
|
|
196
|
-
]
|
|
197
|
-
}
|
|
198
|
-
)
|
|
199
|
-
] });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// src/interactive/app.tsx
|
|
203
|
-
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
204
|
-
function App() {
|
|
205
|
-
const [screen, setScreen] = useState3({ type: "buckets" });
|
|
206
|
-
const [email, setEmail] = useState3();
|
|
207
|
-
useEffect3(() => {
|
|
208
|
-
apiRequest("/api/whoami").then((data) => setEmail(data.user.email)).catch(() => {
|
|
209
|
-
});
|
|
210
|
-
}, []);
|
|
211
|
-
if (screen.type === "files") {
|
|
212
|
-
return /* @__PURE__ */ jsx5(
|
|
213
|
-
FileList,
|
|
214
|
-
{
|
|
215
|
-
bucket: screen.bucket,
|
|
216
|
-
onBack: () => setScreen({ type: "buckets" })
|
|
217
|
-
}
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
return /* @__PURE__ */ jsx5(
|
|
221
|
-
BucketList,
|
|
222
|
-
{
|
|
223
|
-
email,
|
|
224
|
-
onSelect: (bucket) => setScreen({ type: "files", bucket })
|
|
225
|
-
}
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
function startInteractive() {
|
|
229
|
-
render(/* @__PURE__ */ jsx5(App, {}));
|
|
230
|
-
}
|
|
231
|
-
export {
|
|
232
|
-
startInteractive
|
|
233
|
-
};
|
package/dist/chunk-2AORPTQB.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// src/lib/config.ts
|
|
2
|
-
var DEFAULT_BASE_URL = "https://stow.sh";
|
|
3
|
-
function getApiKey() {
|
|
4
|
-
const key = process.env.STOW_API_KEY;
|
|
5
|
-
if (!key) {
|
|
6
|
-
console.error("Error: STOW_API_KEY environment variable not set");
|
|
7
|
-
console.error("");
|
|
8
|
-
console.error("Set your API key:");
|
|
9
|
-
console.error(" export STOW_API_KEY=stow_...");
|
|
10
|
-
console.error("");
|
|
11
|
-
console.error("Get an API key at https://stow.sh/dashboard/api-keys");
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
return key;
|
|
15
|
-
}
|
|
16
|
-
function getBaseUrl() {
|
|
17
|
-
return process.env.STOW_API_URL || DEFAULT_BASE_URL;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
getApiKey,
|
|
22
|
-
getBaseUrl
|
|
23
|
-
};
|
package/dist/chunk-FEMMZ4YZ.js
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getApiKey,
|
|
3
|
-
getBaseUrl
|
|
4
|
-
} from "./chunk-2AORPTQB.js";
|
|
5
|
-
|
|
6
|
-
// src/lib/api.ts
|
|
7
|
-
var StowApiError = class extends Error {
|
|
8
|
-
status;
|
|
9
|
-
code;
|
|
10
|
-
constructor(status, message, code) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.name = "StowApiError";
|
|
13
|
-
this.status = status;
|
|
14
|
-
this.code = code;
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
function formatApiError(status, message) {
|
|
18
|
-
switch (status) {
|
|
19
|
-
case 401:
|
|
20
|
-
return "Invalid API key. Set STOW_API_KEY or get one at stow.sh/dashboard/api-keys";
|
|
21
|
-
case 403:
|
|
22
|
-
return message || "Permission denied";
|
|
23
|
-
case 404:
|
|
24
|
-
return message || "Not found";
|
|
25
|
-
case 413:
|
|
26
|
-
return "File too large (max 50MB)";
|
|
27
|
-
case 429:
|
|
28
|
-
return "Rate limit exceeded. Try again shortly.";
|
|
29
|
-
default:
|
|
30
|
-
return message || `Request failed (${status})`;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
async function apiRequest(path, options = {}) {
|
|
34
|
-
const apiKey = getApiKey();
|
|
35
|
-
const baseUrl = getBaseUrl();
|
|
36
|
-
const url = `${baseUrl}${path}`;
|
|
37
|
-
const headers = {
|
|
38
|
-
"x-api-key": apiKey,
|
|
39
|
-
...options.headers || {}
|
|
40
|
-
};
|
|
41
|
-
if (options.body && typeof options.body === "string") {
|
|
42
|
-
headers["Content-Type"] = "application/json";
|
|
43
|
-
}
|
|
44
|
-
let response;
|
|
45
|
-
try {
|
|
46
|
-
response = await fetch(url, { ...options, headers });
|
|
47
|
-
} catch (err) {
|
|
48
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
49
|
-
if (msg.includes("ECONNREFUSED")) {
|
|
50
|
-
throw new StowApiError(
|
|
51
|
-
0,
|
|
52
|
-
`Cannot connect to ${baseUrl}. Is the server running?`
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
if (msg.includes("ENOTFOUND")) {
|
|
56
|
-
throw new StowApiError(
|
|
57
|
-
0,
|
|
58
|
-
`Cannot resolve ${baseUrl}. Check your STOW_API_URL or internet connection.`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
throw new StowApiError(0, `Network error: ${msg}`);
|
|
62
|
-
}
|
|
63
|
-
if (!response.ok) {
|
|
64
|
-
const data = await response.json().catch(() => ({}));
|
|
65
|
-
const serverMsg = data.error;
|
|
66
|
-
const code = data.code;
|
|
67
|
-
throw new StowApiError(
|
|
68
|
-
response.status,
|
|
69
|
-
formatApiError(response.status, serverMsg || ""),
|
|
70
|
-
code
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
return await response.json();
|
|
74
|
-
}
|
|
75
|
-
async function apiUpload(path, fileBuffer, filename, contentType) {
|
|
76
|
-
const apiKey = getApiKey();
|
|
77
|
-
const baseUrl = getBaseUrl();
|
|
78
|
-
const boundary = `----stow${Date.now()}`;
|
|
79
|
-
const parts = [];
|
|
80
|
-
parts.push(Buffer.from(`--${boundary}\r
|
|
81
|
-
`));
|
|
82
|
-
parts.push(
|
|
83
|
-
Buffer.from(
|
|
84
|
-
`Content-Disposition: form-data; name="file"; filename="${filename}"\r
|
|
85
|
-
`
|
|
86
|
-
)
|
|
87
|
-
);
|
|
88
|
-
parts.push(Buffer.from(`Content-Type: ${contentType}\r
|
|
89
|
-
\r
|
|
90
|
-
`));
|
|
91
|
-
parts.push(fileBuffer);
|
|
92
|
-
parts.push(Buffer.from(`\r
|
|
93
|
-
--${boundary}--\r
|
|
94
|
-
`));
|
|
95
|
-
const body = Buffer.concat(parts);
|
|
96
|
-
let response;
|
|
97
|
-
try {
|
|
98
|
-
response = await fetch(`${baseUrl}${path}`, {
|
|
99
|
-
method: "POST",
|
|
100
|
-
headers: {
|
|
101
|
-
"x-api-key": apiKey,
|
|
102
|
-
"Content-Type": `multipart/form-data; boundary=${boundary}`
|
|
103
|
-
},
|
|
104
|
-
body
|
|
105
|
-
});
|
|
106
|
-
} catch (err) {
|
|
107
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
108
|
-
throw new StowApiError(0, `Network error: ${msg}`);
|
|
109
|
-
}
|
|
110
|
-
if (!response.ok) {
|
|
111
|
-
const data = await response.json().catch(() => ({}));
|
|
112
|
-
const serverMsg = data.error;
|
|
113
|
-
throw new StowApiError(
|
|
114
|
-
response.status,
|
|
115
|
-
formatApiError(response.status, serverMsg || "")
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
return await response.json();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export {
|
|
122
|
-
StowApiError,
|
|
123
|
-
apiRequest,
|
|
124
|
-
apiUpload
|
|
125
|
-
};
|
package/dist/chunk-LYCXXF2T.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getApiKey,
|
|
3
|
-
getBaseUrl
|
|
4
|
-
} from "./chunk-OZ7QQTIZ.js";
|
|
5
|
-
|
|
6
|
-
// src/lib/api.ts
|
|
7
|
-
var StowApiError = class extends Error {
|
|
8
|
-
status;
|
|
9
|
-
code;
|
|
10
|
-
constructor(status, message, code) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.name = "StowApiError";
|
|
13
|
-
this.status = status;
|
|
14
|
-
this.code = code;
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
function formatApiError(status, message) {
|
|
18
|
-
switch (status) {
|
|
19
|
-
case 401:
|
|
20
|
-
return "Invalid API key. Set STOW_API_KEY or get one at stow.sh/dashboard/api-keys";
|
|
21
|
-
case 403:
|
|
22
|
-
return message || "Permission denied";
|
|
23
|
-
case 404:
|
|
24
|
-
return message || "Not found";
|
|
25
|
-
case 413:
|
|
26
|
-
return "File too large (max 50MB)";
|
|
27
|
-
case 429:
|
|
28
|
-
return "Rate limit exceeded. Try again shortly.";
|
|
29
|
-
default:
|
|
30
|
-
return message || `Request failed (${status})`;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
async function apiRequest(path, options = {}) {
|
|
34
|
-
const apiKey = getApiKey();
|
|
35
|
-
const baseUrl = getBaseUrl();
|
|
36
|
-
const url = `${baseUrl}${path}`;
|
|
37
|
-
const headers = {
|
|
38
|
-
"x-api-key": apiKey,
|
|
39
|
-
...options.headers || {}
|
|
40
|
-
};
|
|
41
|
-
if (options.body && typeof options.body === "string") {
|
|
42
|
-
headers["Content-Type"] = "application/json";
|
|
43
|
-
}
|
|
44
|
-
let response;
|
|
45
|
-
try {
|
|
46
|
-
response = await fetch(url, { ...options, headers });
|
|
47
|
-
} catch (err) {
|
|
48
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
49
|
-
if (msg.includes("ECONNREFUSED")) {
|
|
50
|
-
throw new StowApiError(
|
|
51
|
-
0,
|
|
52
|
-
`Cannot connect to ${baseUrl}. Is the server running?`
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
if (msg.includes("ENOTFOUND")) {
|
|
56
|
-
throw new StowApiError(
|
|
57
|
-
0,
|
|
58
|
-
`Cannot resolve ${baseUrl}. Check your STOW_API_URL or internet connection.`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
throw new StowApiError(0, `Network error: ${msg}`);
|
|
62
|
-
}
|
|
63
|
-
if (!response.ok) {
|
|
64
|
-
const data = await response.json().catch(() => ({}));
|
|
65
|
-
const serverMsg = data.error;
|
|
66
|
-
const code = data.code;
|
|
67
|
-
throw new StowApiError(
|
|
68
|
-
response.status,
|
|
69
|
-
formatApiError(response.status, serverMsg || ""),
|
|
70
|
-
code
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
return await response.json();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export {
|
|
77
|
-
StowApiError,
|
|
78
|
-
apiRequest
|
|
79
|
-
};
|