@buildinternet/uploads 0.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 +64 -0
- package/bin/uploads.js +9 -0
- package/dist/agent.d.ts +8 -0
- package/dist/agent.js +24 -0
- package/dist/cli-args.d.ts +39 -0
- package/dist/cli-args.js +129 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +158 -0
- package/dist/client.d.ts +55 -0
- package/dist/client.js +97 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +202 -0
- package/dist/commands/setup.d.ts +4 -0
- package/dist/commands/setup.js +223 -0
- package/dist/commands.d.ts +19 -0
- package/dist/commands.js +451 -0
- package/dist/config-file.d.ts +37 -0
- package/dist/config-file.js +204 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.js +157 -0
- package/dist/embed.d.ts +6 -0
- package/dist/embed.js +30 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +10 -0
- package/dist/github-gh.d.ts +19 -0
- package/dist/github-gh.js +93 -0
- package/dist/github.d.ts +24 -0
- package/dist/github.js +44 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/keys.d.ts +11 -0
- package/dist/keys.js +34 -0
- package/package.json +60 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { DEFAULT_API_URL, DEFAULT_WORKSPACE, UPLOADS_CONFIG_KEYS, describeConfigSources, redactToken, resolveConfig, resolveConfigPath, resolvePutDefaults, writeConfigKeys, } from "../config.js";
|
|
3
|
+
import { flagBool, flagString, parseCommandArgs, UsageError } from "../cli-args.js";
|
|
4
|
+
const CONFIG_HELP = `uploads config — manage shared buildinternet config
|
|
5
|
+
|
|
6
|
+
Shared file (with github-screenshots and other skills):
|
|
7
|
+
~/.config/buildinternet/config
|
|
8
|
+
or $XDG_CONFIG_HOME/buildinternet/config
|
|
9
|
+
or override with $BUILDINTERNET_CONFIG
|
|
10
|
+
|
|
11
|
+
Subcommands:
|
|
12
|
+
path Print the resolved config file path
|
|
13
|
+
show Show effective settings (token redacted)
|
|
14
|
+
init Create or update UPLOADS_* keys in the config file
|
|
15
|
+
set <key> <value> Set one UPLOADS_* key
|
|
16
|
+
|
|
17
|
+
Keys:
|
|
18
|
+
UPLOADS_API_URL API base URL (default: ${DEFAULT_API_URL})
|
|
19
|
+
UPLOADS_WORKSPACE Workspace / bucket tenant (default: ${DEFAULT_WORKSPACE})
|
|
20
|
+
UPLOADS_TOKEN Bearer token for the workspace
|
|
21
|
+
UPLOADS_DEFAULT_PREFIX Default key prefix for put/list
|
|
22
|
+
UPLOADS_DEFAULT_REPO Default repo segment for put
|
|
23
|
+
UPLOADS_DEFAULT_REF Default ref segment for put
|
|
24
|
+
UPLOADS_DEFAULT_WIDTH Default markdown image width
|
|
25
|
+
UPLOADS_NO_GIT Set to 1 to skip git remote for --repo
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
uploads config path
|
|
29
|
+
uploads config show
|
|
30
|
+
uploads config init --token up_default_… --workspace default
|
|
31
|
+
uploads config set UPLOADS_TOKEN up_default_…
|
|
32
|
+
uploads config init --api-url http://localhost:8787 --force
|
|
33
|
+
`;
|
|
34
|
+
const VALID_KEYS = new Set(UPLOADS_CONFIG_KEYS);
|
|
35
|
+
function writeStdout(text) {
|
|
36
|
+
process.stdout.write(text);
|
|
37
|
+
}
|
|
38
|
+
function writeJson(payload) {
|
|
39
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
40
|
+
}
|
|
41
|
+
export async function runConfig(args, opts, help = false) {
|
|
42
|
+
const parsed = parseCommandArgs(args);
|
|
43
|
+
if (help || parsed.help || parsed.positionals.length === 0) {
|
|
44
|
+
process.stderr.write(CONFIG_HELP);
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
const sub = parsed.positionals[0];
|
|
48
|
+
const rest = parsed.positionals.slice(1);
|
|
49
|
+
const subArgs = args.slice(args.indexOf(sub) + 1);
|
|
50
|
+
switch (sub) {
|
|
51
|
+
case "path":
|
|
52
|
+
return runConfigPath(subArgs, opts, help);
|
|
53
|
+
case "show":
|
|
54
|
+
return runConfigShow(subArgs, opts, help);
|
|
55
|
+
case "init":
|
|
56
|
+
return runConfigInit(subArgs, opts, help);
|
|
57
|
+
case "set":
|
|
58
|
+
return runConfigSet(rest, subArgs, opts, help);
|
|
59
|
+
default:
|
|
60
|
+
process.stderr.write(`unknown config subcommand: ${sub}\n\n${CONFIG_HELP}`);
|
|
61
|
+
return 2;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function runConfigPath(args, opts, help) {
|
|
65
|
+
if (help || parseCommandArgs(args).help) {
|
|
66
|
+
process.stderr.write(`uploads config path\n\nPrint the resolved config file path.\n`);
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
const path = resolveConfigPath({ envFile: opts.envFile });
|
|
70
|
+
const payload = { path, exists: existsSync(path) };
|
|
71
|
+
if (opts.json)
|
|
72
|
+
writeJson(payload);
|
|
73
|
+
else
|
|
74
|
+
writeStdout(`${path}\n`);
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
async function runConfigShow(args, opts, help) {
|
|
78
|
+
if (help || parseCommandArgs(args).help) {
|
|
79
|
+
process.stderr.write(`uploads config show\n\nShow effective settings (token redacted).\n`);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
const config = resolveConfig({ envFile: opts.envFile, requireToken: false });
|
|
83
|
+
const sources = describeConfigSources({ envFile: opts.envFile });
|
|
84
|
+
const defaults = resolvePutDefaults({ envFile: opts.envFile });
|
|
85
|
+
const payload = {
|
|
86
|
+
configPath: config.configPath,
|
|
87
|
+
configExists: config.configExists,
|
|
88
|
+
apiUrl: config.apiUrl,
|
|
89
|
+
workspace: config.workspace,
|
|
90
|
+
token: redactToken(config.token || undefined),
|
|
91
|
+
sources,
|
|
92
|
+
defaults,
|
|
93
|
+
};
|
|
94
|
+
if (opts.json) {
|
|
95
|
+
writeJson(payload);
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
const lines = [
|
|
99
|
+
`config: ${config.configPath}${config.configExists ? "" : " (missing)"}`,
|
|
100
|
+
`api: ${config.apiUrl} (${sources.apiUrl})`,
|
|
101
|
+
`workspace: ${config.workspace} (${sources.workspace})`,
|
|
102
|
+
`token: ${redactToken(config.token || undefined)} (${sources.token})`,
|
|
103
|
+
];
|
|
104
|
+
if (defaults.prefix)
|
|
105
|
+
lines.push(`prefix: ${defaults.prefix}`);
|
|
106
|
+
if (defaults.repo)
|
|
107
|
+
lines.push(`repo: ${defaults.repo}`);
|
|
108
|
+
if (defaults.ref)
|
|
109
|
+
lines.push(`ref: ${defaults.ref}`);
|
|
110
|
+
if (defaults.width != null)
|
|
111
|
+
lines.push(`width: ${defaults.width}`);
|
|
112
|
+
if (defaults.noGit)
|
|
113
|
+
lines.push("no-git: true");
|
|
114
|
+
writeStdout(lines.join("\n") + "\n");
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
async function runConfigInit(args, opts, help) {
|
|
118
|
+
const parsed = parseCommandArgs(args);
|
|
119
|
+
if (help || parsed.help) {
|
|
120
|
+
process.stderr.write(`uploads config init [options]
|
|
121
|
+
|
|
122
|
+
Create or update UPLOADS_* keys in the shared config file.
|
|
123
|
+
|
|
124
|
+
Options:
|
|
125
|
+
--api-url <url>
|
|
126
|
+
--workspace, -w <name>
|
|
127
|
+
--token <token>
|
|
128
|
+
--path <file> Write to this file instead of the default
|
|
129
|
+
--force Overwrite existing non-empty values
|
|
130
|
+
|
|
131
|
+
Examples:
|
|
132
|
+
uploads config init --token up_default_…
|
|
133
|
+
uploads config init --api-url http://localhost:8787 --workspace default --token up_default_…
|
|
134
|
+
`);
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
const apiUrl = flagString(parsed.flags, "--api-url");
|
|
138
|
+
const workspace = flagString(parsed.flags, "--workspace") ?? flagString(parsed.flags, "-w");
|
|
139
|
+
const token = flagString(parsed.flags, "--token");
|
|
140
|
+
const path = flagString(parsed.flags, "--path") ?? resolveConfigPath({ envFile: opts.envFile });
|
|
141
|
+
const force = flagBool(parsed.flags, "--force");
|
|
142
|
+
const keys = {};
|
|
143
|
+
if (apiUrl)
|
|
144
|
+
keys.UPLOADS_API_URL = apiUrl;
|
|
145
|
+
if (workspace)
|
|
146
|
+
keys.UPLOADS_WORKSPACE = workspace;
|
|
147
|
+
if (token)
|
|
148
|
+
keys.UPLOADS_TOKEN = token;
|
|
149
|
+
if (Object.keys(keys).length === 0) {
|
|
150
|
+
keys.UPLOADS_API_URL = DEFAULT_API_URL;
|
|
151
|
+
keys.UPLOADS_WORKSPACE = DEFAULT_WORKSPACE;
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const result = writeConfigKeys(path, keys, { force });
|
|
155
|
+
const payload = { ...result, keys: Object.keys(keys) };
|
|
156
|
+
if (opts.json)
|
|
157
|
+
writeJson(payload);
|
|
158
|
+
else {
|
|
159
|
+
const verb = result.created ? "created" : "updated";
|
|
160
|
+
process.stdout.write(`${verb} ${result.path}\n`);
|
|
161
|
+
if (result.updated.length)
|
|
162
|
+
process.stdout.write(`keys: ${result.updated.join(", ")}\n`);
|
|
163
|
+
if (!token) {
|
|
164
|
+
process.stderr.write("hint: add a token with uploads config set UPLOADS_TOKEN <token>\n");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
throw new UsageError(err instanceof Error ? err.message : String(err));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async function runConfigSet(positionals, args, opts, help) {
|
|
174
|
+
const parsed = parseCommandArgs(args);
|
|
175
|
+
if (help || parsed.help) {
|
|
176
|
+
process.stderr.write(`uploads config set <key> <value> [--path <file>] [--force]
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
uploads config set UPLOADS_TOKEN up_default_…
|
|
180
|
+
uploads config set UPLOADS_WORKSPACE acme
|
|
181
|
+
`);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
const key = positionals[0];
|
|
185
|
+
const value = positionals[1];
|
|
186
|
+
if (!key || !value) {
|
|
187
|
+
process.stderr.write(`uploads config set <key> <value>\n`);
|
|
188
|
+
return 2;
|
|
189
|
+
}
|
|
190
|
+
if (!VALID_KEYS.has(key)) {
|
|
191
|
+
throw new UsageError(`unknown key: ${key} (expected ${[...VALID_KEYS].join(", ")})`);
|
|
192
|
+
}
|
|
193
|
+
const path = flagString(parsed.flags, "--path") ?? resolveConfigPath({ envFile: opts.envFile });
|
|
194
|
+
const force = flagBool(parsed.flags, "--force");
|
|
195
|
+
const result = writeConfigKeys(path, { [key]: value }, { force });
|
|
196
|
+
const payload = { ...result, key, value: key === "UPLOADS_TOKEN" ? redactToken(value) : value };
|
|
197
|
+
if (opts.json)
|
|
198
|
+
writeJson(payload);
|
|
199
|
+
else
|
|
200
|
+
process.stdout.write(`set ${key} in ${result.path}\n`);
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { createUploadsClient } from "../client.js";
|
|
2
|
+
import { DEFAULT_API_URL, DEFAULT_WORKSPACE, describeConfigSources, putDefaultsToConfigValues, redactToken, resolveApiUrl, resolveConfig, resolveConfigPath, resolvePutDefaults, writeConfigKeys, workspaceFromToken, } from "../config.js";
|
|
3
|
+
import { flagBool, flagInt, flagString, parseCommandArgs, UsageError } from "../cli-args.js";
|
|
4
|
+
import { UploadsError } from "../errors.js";
|
|
5
|
+
const SETUP_HELP = `uploads setup — guided CLI configuration
|
|
6
|
+
|
|
7
|
+
Writes UPLOADS_* keys to the shared buildinternet config file and prints
|
|
8
|
+
step-by-step instructions for anything still missing.
|
|
9
|
+
|
|
10
|
+
Without flags: shows current status and the next steps to finish setup.
|
|
11
|
+
With flags: saves provided values, then optionally verifies with doctor.
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--api-url <url> API base (default: ${DEFAULT_API_URL})
|
|
15
|
+
--workspace, -w <name>
|
|
16
|
+
--token <token> Bearer token (mint via admin endpoint — see below)
|
|
17
|
+
--prefix <path> Default key prefix for put/list (default: screenshots)
|
|
18
|
+
--repo <owner/repo> Default repo segment for put
|
|
19
|
+
--ref <id> Default ref segment for put (PR/issue/branch/date)
|
|
20
|
+
--width <px> Default markdown image width
|
|
21
|
+
--no-git Don't derive --repo from git remote
|
|
22
|
+
--path <file> Config file (default: ~/.config/buildinternet/config)
|
|
23
|
+
--force Overwrite existing non-empty values
|
|
24
|
+
--check Run doctor after saving (default when --token is set)
|
|
25
|
+
--no-check Skip doctor after saving
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
uploads setup
|
|
29
|
+
uploads setup --token up_default_…
|
|
30
|
+
uploads setup --api-url http://localhost:8787 --workspace default --token up_default_…
|
|
31
|
+
uploads setup --prefix screenshots --repo myorg/myapp --check
|
|
32
|
+
`;
|
|
33
|
+
function buildStatus(envFile) {
|
|
34
|
+
const config = resolveConfig({ envFile, requireToken: false });
|
|
35
|
+
return {
|
|
36
|
+
configPath: config.configPath,
|
|
37
|
+
apiUrl: config.apiUrl,
|
|
38
|
+
workspace: config.workspace,
|
|
39
|
+
token: config.token || undefined,
|
|
40
|
+
defaults: resolvePutDefaults({ envFile }),
|
|
41
|
+
sources: describeConfigSources({ envFile }),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function mintTokenCommand(apiUrl, workspace) {
|
|
45
|
+
const body = workspace && workspace !== DEFAULT_WORKSPACE
|
|
46
|
+
? ` \\\n -H "Content-Type: application/json" \\\n -d '{"workspace":"${workspace}","label":"cli"}'`
|
|
47
|
+
: "";
|
|
48
|
+
return [
|
|
49
|
+
`curl -XPOST ${apiUrl}/admin/tokens \\`,
|
|
50
|
+
` -H "Authorization: Bearer $ADMIN_TOKEN"${body}`,
|
|
51
|
+
].join("\n");
|
|
52
|
+
}
|
|
53
|
+
function formatWizard(status) {
|
|
54
|
+
const lines = ["uploads setup", ""];
|
|
55
|
+
lines.push(`Config file: ${status.configPath}`);
|
|
56
|
+
lines.push(`API: ${status.apiUrl} (${status.sources.apiUrl})`);
|
|
57
|
+
lines.push(`Workspace: ${status.workspace} (${status.sources.workspace})`);
|
|
58
|
+
lines.push(`Token: ${redactToken(status.token)} (${status.sources.token})`);
|
|
59
|
+
const d = status.defaults;
|
|
60
|
+
if (d.prefix || d.repo || d.ref || d.width != null || d.noGit) {
|
|
61
|
+
lines.push("Defaults:");
|
|
62
|
+
if (d.prefix)
|
|
63
|
+
lines.push(` prefix: ${d.prefix}`);
|
|
64
|
+
if (d.repo)
|
|
65
|
+
lines.push(` repo: ${d.repo}`);
|
|
66
|
+
if (d.ref)
|
|
67
|
+
lines.push(` ref: ${d.ref}`);
|
|
68
|
+
if (d.width != null)
|
|
69
|
+
lines.push(` width: ${d.width}`);
|
|
70
|
+
if (d.noGit)
|
|
71
|
+
lines.push(" no-git: true");
|
|
72
|
+
}
|
|
73
|
+
lines.push("");
|
|
74
|
+
if (!status.token) {
|
|
75
|
+
lines.push("Step 1 — Mint a token");
|
|
76
|
+
lines.push(" You need ADMIN_TOKEN for the API (ask your uploads.sh admin, or set it locally in apps/api/.dev.vars).");
|
|
77
|
+
lines.push(" Mint:");
|
|
78
|
+
lines.push(` ${mintTokenCommand(status.apiUrl, status.workspace)}`);
|
|
79
|
+
lines.push(" Save:");
|
|
80
|
+
lines.push(" uploads setup --token up_<workspace>_…");
|
|
81
|
+
lines.push("");
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
lines.push("Step 1 — Token: ok");
|
|
85
|
+
lines.push("");
|
|
86
|
+
}
|
|
87
|
+
if (status.apiUrl.includes("localhost") || status.apiUrl.includes("127.0.0.1")) {
|
|
88
|
+
lines.push("Note — local API");
|
|
89
|
+
lines.push(" Tokens minted with workspace:add --local only work against localhost.");
|
|
90
|
+
lines.push(" Prod tokens need UPLOADS_API_URL=https://api.uploads.sh");
|
|
91
|
+
lines.push("");
|
|
92
|
+
}
|
|
93
|
+
lines.push("Step 2 — Optional put defaults");
|
|
94
|
+
lines.push(" uploads setup --prefix screenshots --repo myorg/myapp");
|
|
95
|
+
lines.push(" uploads config set UPLOADS_DEFAULT_REPO myorg/myapp");
|
|
96
|
+
lines.push("");
|
|
97
|
+
lines.push("Step 3 — Verify");
|
|
98
|
+
lines.push(" uploads doctor");
|
|
99
|
+
lines.push(" uploads put ./shot.png");
|
|
100
|
+
lines.push("");
|
|
101
|
+
if (status.token) {
|
|
102
|
+
lines.push("Quick save (all-in-one):");
|
|
103
|
+
lines.push(` uploads setup --api-url ${status.apiUrl} --workspace ${status.workspace} --token <token> --check`);
|
|
104
|
+
}
|
|
105
|
+
return lines.join("\n");
|
|
106
|
+
}
|
|
107
|
+
export async function runSetup(args, opts, help = false) {
|
|
108
|
+
const parsed = parseCommandArgs(args);
|
|
109
|
+
if (help || parsed.help) {
|
|
110
|
+
process.stderr.write(SETUP_HELP);
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
const apiUrl = flagString(parsed.flags, "--api-url");
|
|
114
|
+
const workspace = flagString(parsed.flags, "--workspace") ?? flagString(parsed.flags, "-w");
|
|
115
|
+
const token = flagString(parsed.flags, "--token");
|
|
116
|
+
const prefix = flagString(parsed.flags, "--prefix");
|
|
117
|
+
const repo = flagString(parsed.flags, "--repo");
|
|
118
|
+
const ref = flagString(parsed.flags, "--ref");
|
|
119
|
+
const width = flagInt(parsed.flags, "--width", "--width");
|
|
120
|
+
const noGit = flagBool(parsed.flags, "--no-git");
|
|
121
|
+
const path = flagString(parsed.flags, "--path") ?? resolveConfigPath({ envFile: opts.envFile });
|
|
122
|
+
const force = flagBool(parsed.flags, "--force");
|
|
123
|
+
const checkExplicit = flagBool(parsed.flags, "--check");
|
|
124
|
+
const noCheck = flagBool(parsed.flags, "--no-check");
|
|
125
|
+
const hasWrites = apiUrl != null ||
|
|
126
|
+
workspace != null ||
|
|
127
|
+
token != null ||
|
|
128
|
+
prefix != null ||
|
|
129
|
+
repo != null ||
|
|
130
|
+
ref != null ||
|
|
131
|
+
width != null ||
|
|
132
|
+
noGit;
|
|
133
|
+
if (!hasWrites) {
|
|
134
|
+
const status = buildStatus(opts.envFile);
|
|
135
|
+
if (opts.json) {
|
|
136
|
+
process.stdout.write(JSON.stringify({
|
|
137
|
+
...status,
|
|
138
|
+
token: redactToken(status.token),
|
|
139
|
+
complete: Boolean(status.token),
|
|
140
|
+
next: status.token ? "doctor" : "mint-token",
|
|
141
|
+
}, null, 2) + "\n");
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
process.stdout.write(formatWizard(status));
|
|
145
|
+
}
|
|
146
|
+
return status.token ? 0 : 1;
|
|
147
|
+
}
|
|
148
|
+
const keys = {};
|
|
149
|
+
if (apiUrl)
|
|
150
|
+
keys.UPLOADS_API_URL = apiUrl;
|
|
151
|
+
if (workspace)
|
|
152
|
+
keys.UPLOADS_WORKSPACE = workspace;
|
|
153
|
+
if (token)
|
|
154
|
+
keys.UPLOADS_TOKEN = token;
|
|
155
|
+
Object.assign(keys, putDefaultsToConfigValues({
|
|
156
|
+
prefix: prefix ?? undefined,
|
|
157
|
+
repo: repo ?? undefined,
|
|
158
|
+
ref: ref ?? undefined,
|
|
159
|
+
width: width ?? undefined,
|
|
160
|
+
noGit: noGit || undefined,
|
|
161
|
+
}));
|
|
162
|
+
if (Object.keys(keys).length === 0) {
|
|
163
|
+
throw new UsageError("no setup values provided");
|
|
164
|
+
}
|
|
165
|
+
const result = writeConfigKeys(path, keys, { force });
|
|
166
|
+
const savedApiUrl = apiUrl ?? resolveApiUrl({ envFile: opts.envFile });
|
|
167
|
+
const savedWorkspace = workspace ?? (token ? workspaceFromToken(token) : undefined) ?? DEFAULT_WORKSPACE;
|
|
168
|
+
const shouldCheck = !noCheck && (checkExplicit || Boolean(token));
|
|
169
|
+
let doctorOk;
|
|
170
|
+
let doctorError;
|
|
171
|
+
if (shouldCheck) {
|
|
172
|
+
const cfg = resolveConfig({ envFile: opts.envFile, requireToken: false });
|
|
173
|
+
if (!cfg.token) {
|
|
174
|
+
doctorError = "no token configured — run uploads setup --token <token>";
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
try {
|
|
178
|
+
const client = createUploadsClient({
|
|
179
|
+
apiUrl: cfg.apiUrl,
|
|
180
|
+
workspace: cfg.workspace,
|
|
181
|
+
token: cfg.token,
|
|
182
|
+
});
|
|
183
|
+
const health = await client.health();
|
|
184
|
+
if (!health.ok)
|
|
185
|
+
throw new UploadsError("API unhealthy", "API_ERROR");
|
|
186
|
+
await client.list({ limit: 1 });
|
|
187
|
+
doctorOk = true;
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
doctorOk = false;
|
|
191
|
+
doctorError = err instanceof UploadsError ? err.message : String(err);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const payload = {
|
|
196
|
+
...result,
|
|
197
|
+
keys: Object.keys(keys),
|
|
198
|
+
apiUrl: savedApiUrl,
|
|
199
|
+
workspace: savedWorkspace,
|
|
200
|
+
doctor: shouldCheck ? { ok: doctorOk, error: doctorError } : undefined,
|
|
201
|
+
};
|
|
202
|
+
if (opts.json) {
|
|
203
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
204
|
+
return doctorOk === false ? 1 : 0;
|
|
205
|
+
}
|
|
206
|
+
const verb = result.created ? "created" : "updated";
|
|
207
|
+
process.stdout.write(`${verb} ${result.path}\n`);
|
|
208
|
+
if (result.updated.length)
|
|
209
|
+
process.stdout.write(`keys: ${result.updated.join(", ")}\n`);
|
|
210
|
+
if (shouldCheck) {
|
|
211
|
+
if (doctorOk)
|
|
212
|
+
process.stdout.write("doctor: ok\n");
|
|
213
|
+
else
|
|
214
|
+
process.stderr.write(`doctor: failed — ${doctorError}\n`);
|
|
215
|
+
}
|
|
216
|
+
else if (token || checkExplicit) {
|
|
217
|
+
process.stderr.write("hint: run uploads doctor to verify\n");
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
process.stderr.write("hint: run uploads setup to see minting instructions\n");
|
|
221
|
+
}
|
|
222
|
+
return doctorOk === false ? 1 : 0;
|
|
223
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type UploadsClient } from "./client.js";
|
|
2
|
+
import { type ResolvedConfig } from "./config.js";
|
|
3
|
+
import { type CommandRunner } from "./github-gh.js";
|
|
4
|
+
export interface CliContext {
|
|
5
|
+
config: ResolvedConfig;
|
|
6
|
+
client: UploadsClient;
|
|
7
|
+
json: boolean;
|
|
8
|
+
quiet: boolean;
|
|
9
|
+
envFile?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function runAttach(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
|
|
12
|
+
export declare function runPut(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
|
|
13
|
+
export declare function runList(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
|
|
14
|
+
export declare function runDelete(ctx: CliContext, args: string[], help?: boolean): Promise<number>;
|
|
15
|
+
export declare function runComment(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
|
|
16
|
+
export declare function runHealth(ctx: Pick<CliContext, "json"> & {
|
|
17
|
+
apiUrl: string;
|
|
18
|
+
}, args: string[], help?: boolean): Promise<number>;
|
|
19
|
+
export declare function runDoctor(ctx: CliContext, args: string[], help?: boolean): Promise<number>;
|