@seedvault/cli 0.1.0 → 0.1.1
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/sv.js +2705 -0
- package/package.json +8 -3
- package/src/client.ts +0 -164
- package/src/commands/add.ts +0 -52
- package/src/commands/cat.ts +0 -29
- package/src/commands/collections.ts +0 -24
- package/src/commands/contributors.ts +0 -28
- package/src/commands/init.ts +0 -153
- package/src/commands/invite.ts +0 -26
- package/src/commands/ls.ts +0 -37
- package/src/commands/remove.ts +0 -25
- package/src/commands/start.ts +0 -258
- package/src/commands/status.ts +0 -63
- package/src/commands/stop.ts +0 -51
- package/src/config.ts +0 -182
- package/src/daemon/queue.ts +0 -107
- package/src/daemon/syncer.ts +0 -254
- package/src/daemon/watcher.ts +0 -71
- package/src/index.ts +0 -93
- package/tsconfig.json +0 -19
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seedvault/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
|
-
"sv": "./
|
|
6
|
+
"sv": "./dist/sv.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
+
"build": "bun build src/index.ts --outfile dist/sv.js --target bun",
|
|
9
10
|
"dev": "bun run src/index.ts",
|
|
10
|
-
"check": "tsc --noEmit"
|
|
11
|
+
"check": "tsc --noEmit",
|
|
12
|
+
"prepublishOnly": "bun run build"
|
|
11
13
|
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
12
17
|
"dependencies": {
|
|
13
18
|
"chokidar": "^4"
|
|
14
19
|
},
|
package/src/client.ts
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP client for the Seedvault server API.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface SeedvaultClient {
|
|
6
|
-
/** POST /v1/signup */
|
|
7
|
-
signup(name: string, invite?: string): Promise<SignupResponse>;
|
|
8
|
-
/** POST /v1/invites */
|
|
9
|
-
createInvite(): Promise<InviteResponse>;
|
|
10
|
-
/** GET /v1/contributors */
|
|
11
|
-
listContributors(): Promise<ContributorsResponse>;
|
|
12
|
-
/** PUT /v1/contributors/:contributorId/files/* */
|
|
13
|
-
putFile(contributorId: string, path: string, content: string): Promise<FileWriteResponse>;
|
|
14
|
-
/** DELETE /v1/contributors/:contributorId/files/* */
|
|
15
|
-
deleteFile(contributorId: string, path: string): Promise<void>;
|
|
16
|
-
/** GET /v1/contributors/:contributorId/files */
|
|
17
|
-
listFiles(contributorId: string, prefix?: string): Promise<FilesResponse>;
|
|
18
|
-
/** GET /v1/contributors/:contributorId/files/* */
|
|
19
|
-
getFile(contributorId: string, path: string): Promise<string>;
|
|
20
|
-
/** GET /health */
|
|
21
|
-
health(): Promise<HealthResponse>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// --- Response types ---
|
|
25
|
-
|
|
26
|
-
export interface SignupResponse {
|
|
27
|
-
contributor: { id: string; name: string; createdAt: string };
|
|
28
|
-
token: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface InviteResponse {
|
|
32
|
-
invite: string;
|
|
33
|
-
createdAt: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface ContributorsResponse {
|
|
37
|
-
contributors: Array<{ id: string; name: string; createdAt: string }>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface FileWriteResponse {
|
|
41
|
-
path: string;
|
|
42
|
-
size: number;
|
|
43
|
-
modifiedAt: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface FileEntry {
|
|
47
|
-
path: string;
|
|
48
|
-
size: number;
|
|
49
|
-
modifiedAt: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface FilesResponse {
|
|
53
|
-
files: FileEntry[];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface HealthResponse {
|
|
57
|
-
status: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// --- Error ---
|
|
61
|
-
|
|
62
|
-
export class ApiError extends Error {
|
|
63
|
-
public status: number;
|
|
64
|
-
constructor(status: number, message: string) {
|
|
65
|
-
super(message);
|
|
66
|
-
this.name = "ApiError";
|
|
67
|
-
this.status = status;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// --- Implementation ---
|
|
72
|
-
|
|
73
|
-
/** Encode each path segment individually, preserving slashes */
|
|
74
|
-
function encodePath(path: string): string {
|
|
75
|
-
return path.split("/").map(encodeURIComponent).join("/");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function createClient(serverUrl: string, token?: string): SeedvaultClient {
|
|
79
|
-
const base = serverUrl.replace(/\/+$/, "");
|
|
80
|
-
|
|
81
|
-
async function request(
|
|
82
|
-
method: string,
|
|
83
|
-
path: string,
|
|
84
|
-
opts: { body?: string; contentType?: string; auth?: boolean } = {}
|
|
85
|
-
): Promise<Response> {
|
|
86
|
-
const headers: Record<string, string> = {};
|
|
87
|
-
if (opts.auth !== false && token) {
|
|
88
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
89
|
-
}
|
|
90
|
-
if (opts.contentType) {
|
|
91
|
-
headers["Content-Type"] = opts.contentType;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const res = await fetch(`${base}${path}`, {
|
|
95
|
-
method,
|
|
96
|
-
headers,
|
|
97
|
-
body: opts.body,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
if (!res.ok) {
|
|
101
|
-
let msg: string;
|
|
102
|
-
try {
|
|
103
|
-
const json = (await res.json()) as { error?: string };
|
|
104
|
-
msg = json.error || res.statusText;
|
|
105
|
-
} catch {
|
|
106
|
-
msg = res.statusText;
|
|
107
|
-
}
|
|
108
|
-
throw new ApiError(res.status, msg);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return res;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
async signup(name: string, invite?: string): Promise<SignupResponse> {
|
|
116
|
-
const body: Record<string, string> = { name };
|
|
117
|
-
if (invite) body.invite = invite;
|
|
118
|
-
const res = await request("POST", "/v1/signup", {
|
|
119
|
-
body: JSON.stringify(body),
|
|
120
|
-
contentType: "application/json",
|
|
121
|
-
auth: false,
|
|
122
|
-
});
|
|
123
|
-
return res.json();
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
async createInvite(): Promise<InviteResponse> {
|
|
127
|
-
const res = await request("POST", "/v1/invites");
|
|
128
|
-
return res.json();
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
async listContributors(): Promise<ContributorsResponse> {
|
|
132
|
-
const res = await request("GET", "/v1/contributors");
|
|
133
|
-
return res.json();
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
async putFile(contributorId: string, path: string, content: string): Promise<FileWriteResponse> {
|
|
137
|
-
const res = await request("PUT", `/v1/contributors/${contributorId}/files/${encodePath(path)}`, {
|
|
138
|
-
body: content,
|
|
139
|
-
contentType: "text/markdown",
|
|
140
|
-
});
|
|
141
|
-
return res.json();
|
|
142
|
-
},
|
|
143
|
-
|
|
144
|
-
async deleteFile(contributorId: string, path: string): Promise<void> {
|
|
145
|
-
await request("DELETE", `/v1/contributors/${contributorId}/files/${encodePath(path)}`);
|
|
146
|
-
},
|
|
147
|
-
|
|
148
|
-
async listFiles(contributorId: string, prefix?: string): Promise<FilesResponse> {
|
|
149
|
-
const qs = prefix ? `?prefix=${encodeURIComponent(prefix)}` : "";
|
|
150
|
-
const res = await request("GET", `/v1/contributors/${contributorId}/files${qs}`);
|
|
151
|
-
return res.json();
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
async getFile(contributorId: string, path: string): Promise<string> {
|
|
155
|
-
const res = await request("GET", `/v1/contributors/${contributorId}/files/${encodePath(path)}`);
|
|
156
|
-
return res.text();
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
async health(): Promise<HealthResponse> {
|
|
160
|
-
const res = await request("GET", "/health", { auth: false });
|
|
161
|
-
return res.json();
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
|
-
}
|
package/src/commands/add.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "fs";
|
|
2
|
-
import { resolve } from "path";
|
|
3
|
-
import { homedir } from "os";
|
|
4
|
-
import { loadConfig, saveConfig, addCollection, defaultCollectionName } from "../config.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* sv add <path> [--name <name>]
|
|
8
|
-
*
|
|
9
|
-
* Add a collection path to sync. Name defaults to the path basename.
|
|
10
|
-
*/
|
|
11
|
-
export async function add(args: string[]): Promise<void> {
|
|
12
|
-
if (args.length === 0 || args[0].startsWith("--")) {
|
|
13
|
-
console.error("Usage: sv add <path> [--name <name>]");
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const rawPath = args[0];
|
|
18
|
-
const nameIdx = args.indexOf("--name");
|
|
19
|
-
const name = nameIdx !== -1 && args[nameIdx + 1]
|
|
20
|
-
? args[nameIdx + 1]
|
|
21
|
-
: defaultCollectionName(rawPath);
|
|
22
|
-
|
|
23
|
-
// Resolve path
|
|
24
|
-
const absPath = rawPath.startsWith("~")
|
|
25
|
-
? resolve(homedir(), rawPath.slice(2)) // skip ~/
|
|
26
|
-
: resolve(rawPath);
|
|
27
|
-
|
|
28
|
-
if (!existsSync(absPath)) {
|
|
29
|
-
console.error(`Directory not found: ${absPath}`);
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const config = loadConfig();
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const result = addCollection(config, absPath, name);
|
|
37
|
-
const updated = result.config;
|
|
38
|
-
saveConfig(updated);
|
|
39
|
-
console.log(`Added collection: ${absPath}`);
|
|
40
|
-
console.log(` Name: ${name}`);
|
|
41
|
-
console.log(` Files will sync to: ${name}/<relative-path>`);
|
|
42
|
-
if (result.removedChildCollections.length > 0) {
|
|
43
|
-
console.log(" Removed overlapping child collections:");
|
|
44
|
-
for (const child of result.removedChildCollections) {
|
|
45
|
-
console.log(` - ${child.name} (${child.path})`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} catch (e: unknown) {
|
|
49
|
-
console.error((e as Error).message);
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
}
|
package/src/commands/cat.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from "../config.js";
|
|
2
|
-
import { createClient, ApiError } from "../client.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* sv cat <path>
|
|
6
|
-
*
|
|
7
|
-
* Read a file from the server and print its content.
|
|
8
|
-
*/
|
|
9
|
-
export async function cat(args: string[]): Promise<void> {
|
|
10
|
-
if (args.length === 0) {
|
|
11
|
-
console.error("Usage: sv cat <path>");
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const filePath = args[0];
|
|
16
|
-
const config = loadConfig();
|
|
17
|
-
const client = createClient(config.server, config.token);
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const content = await client.getFile(config.contributorId, filePath);
|
|
21
|
-
process.stdout.write(content);
|
|
22
|
-
} catch (e) {
|
|
23
|
-
if (e instanceof ApiError && e.status === 404) {
|
|
24
|
-
console.error(`File not found: ${filePath}`);
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
throw e;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from "../config.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* sv collections
|
|
5
|
-
*
|
|
6
|
-
* List all configured watched collections.
|
|
7
|
-
*/
|
|
8
|
-
export async function collections(): Promise<void> {
|
|
9
|
-
const config = loadConfig();
|
|
10
|
-
|
|
11
|
-
if (config.collections.length === 0) {
|
|
12
|
-
console.log("No collections configured.");
|
|
13
|
-
console.log(" Run 'sv add <path>' to add one.");
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
console.log("Configured collections:\n");
|
|
18
|
-
for (const f of config.collections) {
|
|
19
|
-
console.log(` ${f.name}`);
|
|
20
|
-
console.log(` Path: ${f.path}`);
|
|
21
|
-
console.log(` Syncs: ${f.name}/<relative-path>`);
|
|
22
|
-
console.log();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from "../config.js";
|
|
2
|
-
import { createClient } from "../client.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* sv contributors
|
|
6
|
-
*
|
|
7
|
-
* List all contributors in the vault.
|
|
8
|
-
*/
|
|
9
|
-
export async function contributors(): Promise<void> {
|
|
10
|
-
const config = loadConfig();
|
|
11
|
-
const client = createClient(config.server, config.token);
|
|
12
|
-
|
|
13
|
-
const { contributors } = await client.listContributors();
|
|
14
|
-
|
|
15
|
-
if (contributors.length === 0) {
|
|
16
|
-
console.log("No contributors in the vault.");
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
console.log("Contributors:\n");
|
|
21
|
-
for (const contributor of contributors) {
|
|
22
|
-
const you = contributor.id === config.contributorId ? " (you)" : "";
|
|
23
|
-
console.log(` ${contributor.name}${you}`);
|
|
24
|
-
console.log(` ID: ${contributor.id}`);
|
|
25
|
-
console.log(` Created: ${new Date(contributor.createdAt).toLocaleString()}`);
|
|
26
|
-
console.log();
|
|
27
|
-
}
|
|
28
|
-
}
|
package/src/commands/init.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import * as readline from "readline/promises";
|
|
2
|
-
import { stdin, stdout } from "process";
|
|
3
|
-
import { configExists, saveConfig, type Config } from "../config.js";
|
|
4
|
-
import { createClient } from "../client.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* sv init
|
|
8
|
-
* Interactive: prompts for server URL, name, optional invite code
|
|
9
|
-
* Non-interactive:
|
|
10
|
-
* sv init --server URL --token TOKEN (already have a token)
|
|
11
|
-
* sv init --server URL --name NAME [--invite C] (signup via API)
|
|
12
|
-
*/
|
|
13
|
-
export async function init(args: string[]): Promise<void> {
|
|
14
|
-
// Parse flags
|
|
15
|
-
const flags = parseFlags(args);
|
|
16
|
-
|
|
17
|
-
if (configExists() && !flags.force) {
|
|
18
|
-
console.log("Seedvault is already configured.");
|
|
19
|
-
console.log(" Run 'sv init --force' to overwrite.");
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Non-interactive: --server + --token
|
|
24
|
-
if (flags.server && flags.token) {
|
|
25
|
-
// Verify the token works by hitting the server
|
|
26
|
-
const client = createClient(flags.server, flags.token);
|
|
27
|
-
try {
|
|
28
|
-
await client.health();
|
|
29
|
-
} catch {
|
|
30
|
-
console.error(`Could not reach server at ${flags.server}`);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// We don't know which contributor this token belongs to without trying.
|
|
35
|
-
// For now, save without contributorId — the first PUT will tell us.
|
|
36
|
-
// Actually, let's require --contributor-id or try to infer it.
|
|
37
|
-
const contributorId = flags["contributor-id"] || "";
|
|
38
|
-
if (!contributorId) {
|
|
39
|
-
console.error("When using --token, also pass --contributor-id");
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const config: Config = {
|
|
44
|
-
server: flags.server,
|
|
45
|
-
token: flags.token,
|
|
46
|
-
contributorId,
|
|
47
|
-
collections: [],
|
|
48
|
-
};
|
|
49
|
-
saveConfig(config);
|
|
50
|
-
console.log("Seedvault configured.");
|
|
51
|
-
console.log(` Server: ${config.server}`);
|
|
52
|
-
console.log(` Contributor ID: ${config.contributorId}`);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Non-interactive: --server + --name (signup)
|
|
57
|
-
if (flags.server && flags.name) {
|
|
58
|
-
const client = createClient(flags.server);
|
|
59
|
-
try {
|
|
60
|
-
await client.health();
|
|
61
|
-
} catch {
|
|
62
|
-
console.error(`Could not reach server at ${flags.server}`);
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const result = await client.signup(flags.name, flags.invite);
|
|
67
|
-
const config: Config = {
|
|
68
|
-
server: flags.server,
|
|
69
|
-
token: result.token,
|
|
70
|
-
contributorId: result.contributor.id,
|
|
71
|
-
collections: [],
|
|
72
|
-
};
|
|
73
|
-
saveConfig(config);
|
|
74
|
-
console.log("Signed up and configured.");
|
|
75
|
-
console.log(` Server: ${config.server}`);
|
|
76
|
-
console.log(` Contributor: ${result.contributor.name} (${result.contributor.id})`);
|
|
77
|
-
console.log(` Token: ${result.token}`);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Interactive mode
|
|
82
|
-
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
console.log("Seedvault Setup\n");
|
|
86
|
-
|
|
87
|
-
const server = await rl.question("Server URL: ");
|
|
88
|
-
if (!server) {
|
|
89
|
-
console.error("Server URL is required.");
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Verify server is reachable
|
|
94
|
-
const client = createClient(server);
|
|
95
|
-
try {
|
|
96
|
-
await client.health();
|
|
97
|
-
console.log(" Server is reachable.\n");
|
|
98
|
-
} catch {
|
|
99
|
-
console.error(` Could not reach server at ${server}`);
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const hasToken = await rl.question("Do you already have a token? (y/N): ");
|
|
104
|
-
|
|
105
|
-
if (hasToken.toLowerCase() === "y") {
|
|
106
|
-
const token = await rl.question("Token: ");
|
|
107
|
-
const contributorId = await rl.question("Contributor ID: ");
|
|
108
|
-
const config: Config = { server, token: token.trim(), contributorId: contributorId.trim(), collections: [] };
|
|
109
|
-
saveConfig(config);
|
|
110
|
-
console.log("\nSeedvault configured.");
|
|
111
|
-
} else {
|
|
112
|
-
const name = await rl.question("Contributor name (e.g. your-name-notes): ");
|
|
113
|
-
const invite = await rl.question("Invite code (leave blank if first user): ");
|
|
114
|
-
|
|
115
|
-
const result = await client.signup(name.trim(), invite.trim() || undefined);
|
|
116
|
-
const config: Config = {
|
|
117
|
-
server,
|
|
118
|
-
token: result.token,
|
|
119
|
-
contributorId: result.contributor.id,
|
|
120
|
-
collections: [],
|
|
121
|
-
};
|
|
122
|
-
saveConfig(config);
|
|
123
|
-
console.log(`\nSigned up as '${result.contributor.name}'.`);
|
|
124
|
-
console.log(` Contributor ID: ${result.contributor.id}`);
|
|
125
|
-
console.log(` Token: ${result.token}`);
|
|
126
|
-
console.log("\nSave your token — it won't be shown again.");
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
console.log("\nNext steps:");
|
|
130
|
-
console.log(" sv add ~/notes # Add a collection to sync");
|
|
131
|
-
console.log(" sv start # Start the daemon");
|
|
132
|
-
} finally {
|
|
133
|
-
rl.close();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function parseFlags(args: string[]): Record<string, string> {
|
|
138
|
-
const flags: Record<string, string> = {};
|
|
139
|
-
for (let i = 0; i < args.length; i++) {
|
|
140
|
-
const arg = args[i];
|
|
141
|
-
if (arg.startsWith("--")) {
|
|
142
|
-
const key = arg.slice(2);
|
|
143
|
-
const next = args[i + 1];
|
|
144
|
-
if (next && !next.startsWith("--")) {
|
|
145
|
-
flags[key] = next;
|
|
146
|
-
i++;
|
|
147
|
-
} else {
|
|
148
|
-
flags[key] = "true";
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return flags;
|
|
153
|
-
}
|
package/src/commands/invite.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from "../config.js";
|
|
2
|
-
import { createClient, ApiError } from "../client.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* sv invite
|
|
6
|
-
*
|
|
7
|
-
* Generate an invite code (operator only).
|
|
8
|
-
*/
|
|
9
|
-
export async function invite(): Promise<void> {
|
|
10
|
-
const config = loadConfig();
|
|
11
|
-
const client = createClient(config.server, config.token);
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const result = await client.createInvite();
|
|
15
|
-
console.log(`Invite code: ${result.invite}`);
|
|
16
|
-
console.log(`\nShare this with the person you want to invite.`);
|
|
17
|
-
console.log(`They can sign up with:`);
|
|
18
|
-
console.log(` sv init --server ${config.server} --name <name> --invite ${result.invite}`);
|
|
19
|
-
} catch (e) {
|
|
20
|
-
if (e instanceof ApiError && e.status === 403) {
|
|
21
|
-
console.error("Only the operator can generate invite codes.");
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
throw e;
|
|
25
|
-
}
|
|
26
|
-
}
|
package/src/commands/ls.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from "../config.js";
|
|
2
|
-
import { createClient } from "../client.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* sv ls [prefix]
|
|
6
|
-
*
|
|
7
|
-
* List files in your contributor, optionally filtered by prefix.
|
|
8
|
-
*/
|
|
9
|
-
export async function ls(args: string[]): Promise<void> {
|
|
10
|
-
const config = loadConfig();
|
|
11
|
-
const client = createClient(config.server, config.token);
|
|
12
|
-
const prefix = args[0] || undefined;
|
|
13
|
-
|
|
14
|
-
const { files } = await client.listFiles(config.contributorId, prefix);
|
|
15
|
-
|
|
16
|
-
if (files.length === 0) {
|
|
17
|
-
console.log(prefix ? `No files matching '${prefix}'.` : "No files in your contributor.");
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Find widest path for alignment
|
|
22
|
-
const maxPath = Math.max(...files.map((f) => f.path.length));
|
|
23
|
-
|
|
24
|
-
for (const f of files) {
|
|
25
|
-
const size = formatSize(f.size);
|
|
26
|
-
const date = new Date(f.modifiedAt).toLocaleString();
|
|
27
|
-
console.log(` ${f.path.padEnd(maxPath + 2)} ${size.padStart(8)} ${date}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
console.log(`\n${files.length} file(s)`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function formatSize(bytes: number): string {
|
|
34
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
35
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
36
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
37
|
-
}
|
package/src/commands/remove.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { loadConfig, saveConfig, removeCollection } from "../config.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* sv remove <name>
|
|
5
|
-
*
|
|
6
|
-
* Stop syncing a collection by name.
|
|
7
|
-
*/
|
|
8
|
-
export async function remove(args: string[]): Promise<void> {
|
|
9
|
-
if (args.length === 0) {
|
|
10
|
-
console.error("Usage: sv remove <name>");
|
|
11
|
-
process.exit(1);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const name = args[0];
|
|
15
|
-
const config = loadConfig();
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
const updated = removeCollection(config, name);
|
|
19
|
-
saveConfig(updated);
|
|
20
|
-
console.log(`Removed collection '${name}'.`);
|
|
21
|
-
} catch (e: unknown) {
|
|
22
|
-
console.error((e as Error).message);
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
}
|