@buildinternet/uploads 0.31.0 → 0.33.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/dist/client.d.ts +1 -0
- package/dist/commands/admin-enrollment.js +10 -2
- package/dist/commands/config.js +5 -1
- package/dist/commands/install.d.ts +1 -1
- package/dist/commands/install.js +26 -3
- package/dist/commands/login.d.ts +7 -2
- package/dist/commands/login.js +8 -4
- package/dist/commands/update.js +4 -3
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -437,6 +437,7 @@ export interface MintWorkspaceSummary {
|
|
|
437
437
|
/** GET /v1/tokens — workspaces the signed-in user can mint tokens for. */
|
|
438
438
|
export declare function listMintWorkspaces(apiUrl: string, accessToken: string): Promise<{
|
|
439
439
|
workspaces: MintWorkspaceSummary[];
|
|
440
|
+
suggestedWorkspace?: string;
|
|
440
441
|
}>;
|
|
441
442
|
export interface CreateWorkspaceResult {
|
|
442
443
|
name: string;
|
|
@@ -11,7 +11,7 @@ non-secret page URL plus a code you share separately). The legacy
|
|
|
11
11
|
|
|
12
12
|
Options:
|
|
13
13
|
--admin-token <token> Or ADMIN_TOKEN (UPLOADS_ADMIN_TOKEN is a legacy alias)
|
|
14
|
-
--workspace <name>
|
|
14
|
+
--workspace <name> Required — the workspace the invite grants access to
|
|
15
15
|
--label <label>
|
|
16
16
|
--expires-in <seconds> Default: server policy
|
|
17
17
|
--token-expires-in <seconds> Upload token lifetime (default: server policy)
|
|
@@ -79,7 +79,15 @@ export async function runAdmin(args, opts, help = false) {
|
|
|
79
79
|
throw new UsageError("ADMIN_TOKEN is required for admin enrollment creation");
|
|
80
80
|
const apiUrl = flagString(parsed.flags, "--api-url") ?? opts.apiUrl ?? "https://api.uploads.sh";
|
|
81
81
|
const webUrl = flagString(parsed.flags, "--web-url");
|
|
82
|
-
|
|
82
|
+
// No default. An invite grants files:read/files:write on the workspace it
|
|
83
|
+
// names, so omitting the flag used to silently target the shared `default`
|
|
84
|
+
// workspace — and enrollment redemption mints a token without an org
|
|
85
|
+
// membership, so the recipient would not appear in any member list while
|
|
86
|
+
// still reading and writing its files.
|
|
87
|
+
const workspace = flagString(parsed.flags, "--workspace");
|
|
88
|
+
if (!workspace) {
|
|
89
|
+
throw new UsageError("--workspace is required (the workspace the invite grants access to)");
|
|
90
|
+
}
|
|
83
91
|
const label = flagString(parsed.flags, "--label");
|
|
84
92
|
const email = flagString(parsed.flags, "--email");
|
|
85
93
|
const result = await createEnrollment(apiUrl, adminToken, {
|
package/dist/commands/config.js
CHANGED
|
@@ -149,9 +149,13 @@ Examples:
|
|
|
149
149
|
keys.UPLOADS_WORKSPACE = workspace;
|
|
150
150
|
if (token)
|
|
151
151
|
keys.UPLOADS_TOKEN = token;
|
|
152
|
+
// Seed only the API URL. A `UPLOADS_WORKSPACE` written here outranks the
|
|
153
|
+
// workspace encoded in the token itself (see `resolveConfig`'s precedence),
|
|
154
|
+
// so seeding one would pin every later `uploads login` to whatever this
|
|
155
|
+
// wrote — historically `default` — no matter which workspace the user's
|
|
156
|
+
// token was actually minted for.
|
|
152
157
|
if (Object.keys(keys).length === 0) {
|
|
153
158
|
keys.UPLOADS_API_URL = DEFAULT_API_URL;
|
|
154
|
-
keys.UPLOADS_WORKSPACE = DEFAULT_WORKSPACE;
|
|
155
159
|
}
|
|
156
160
|
try {
|
|
157
161
|
const result = writeConfigKeys(path, keys, { force });
|
|
@@ -4,7 +4,7 @@ export declare const DEFAULT_MCP_URL = "https://agents.uploads.sh/mcp";
|
|
|
4
4
|
export interface StepResult {
|
|
5
5
|
command: string[];
|
|
6
6
|
ok: boolean;
|
|
7
|
-
skipped?: "dry-run" | "sign-in";
|
|
7
|
+
skipped?: "dry-run" | "sign-in" | "already-configured";
|
|
8
8
|
error?: string;
|
|
9
9
|
output?: string;
|
|
10
10
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -17,6 +17,10 @@ infers your workspace from the bearer token, so only the token is needed.
|
|
|
17
17
|
Claude Code and Codex ship the same reminder via their plugins (same command:
|
|
18
18
|
\`${HOOK_COMMAND}\`) — install those plugins instead of relying on this step.
|
|
19
19
|
|
|
20
|
+
Safe to re-run. An MCP server already registered under this name is reported
|
|
21
|
+
as \`already configured\` and left as-is — including the token it was created
|
|
22
|
+
with. To point it at a new token: \`claude mcp remove <name>\` first.
|
|
23
|
+
|
|
20
24
|
Usage:
|
|
21
25
|
uploads install [skill|mcp|hooks|all] (default: all)
|
|
22
26
|
|
|
@@ -73,6 +77,16 @@ function skillCommand(skill) {
|
|
|
73
77
|
// -g global, -y non-interactive, -a '*' every agent (skips the multi-select TUI)
|
|
74
78
|
return ["npx", "-y", "skills", "add", SKILL_SOURCE, "--skill", skill, "-g", "-y", "-a", "*"];
|
|
75
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* `claude mcp add` refuses to overwrite an existing entry and exits non-zero
|
|
82
|
+
* ("MCP server uploads already exists in local config"). That is the steady
|
|
83
|
+
* state for anyone re-running `uploads install`, not a failure — recognize it
|
|
84
|
+
* so the run stays green and the footer tells them how to re-add if they want
|
|
85
|
+
* a fresh token in the header.
|
|
86
|
+
*/
|
|
87
|
+
function alreadyConfigured(result) {
|
|
88
|
+
return !result.ok && /already exists/i.test(result.error ?? "");
|
|
89
|
+
}
|
|
76
90
|
function mcpCommand(name, url, bearer) {
|
|
77
91
|
return [
|
|
78
92
|
"claude",
|
|
@@ -101,7 +115,7 @@ function peekToken(globals) {
|
|
|
101
115
|
return undefined;
|
|
102
116
|
}
|
|
103
117
|
}
|
|
104
|
-
function printHumanSteps(results, redact, verbose) {
|
|
118
|
+
function printHumanSteps(results, redact, verbose, mcpName) {
|
|
105
119
|
for (const [step, r] of Object.entries(results)) {
|
|
106
120
|
const cmd = redact(r.command.join(" "));
|
|
107
121
|
if (r.skipped === "dry-run") {
|
|
@@ -110,6 +124,10 @@ function printHumanSteps(results, redact, verbose) {
|
|
|
110
124
|
else if (r.skipped === "sign-in") {
|
|
111
125
|
process.stdout.write(`${step}: skipped — ${redact(r.error ?? "needs sign-in")}\n`);
|
|
112
126
|
}
|
|
127
|
+
else if (r.skipped === "already-configured") {
|
|
128
|
+
process.stdout.write(`${step}: already configured — "${mcpName}" is registered in Claude Code (nothing to do)\n` +
|
|
129
|
+
` To re-register (e.g. with a new token): claude mcp remove ${mcpName} && uploads install mcp\n`);
|
|
130
|
+
}
|
|
113
131
|
else if (r.ok) {
|
|
114
132
|
process.stdout.write(`${step}: ok\n`);
|
|
115
133
|
if (verbose && r.output) {
|
|
@@ -189,7 +207,12 @@ export async function runInstall(args, opts, help = false) {
|
|
|
189
207
|
const command = mcpCommand(name, url, token || "<token>");
|
|
190
208
|
if (human)
|
|
191
209
|
process.stdout.write("Installing MCP server…\n");
|
|
192
|
-
|
|
210
|
+
const step = dryRun
|
|
211
|
+
? { command, ok: true, skipped: "dry-run" }
|
|
212
|
+
: runStep(run, command);
|
|
213
|
+
results.mcp = alreadyConfigured(step)
|
|
214
|
+
? { command, ok: true, skipped: "already-configured", output: step.error }
|
|
215
|
+
: step;
|
|
193
216
|
}
|
|
194
217
|
}
|
|
195
218
|
if (target === "hooks" || target === "all") {
|
|
@@ -223,7 +246,7 @@ export async function runInstall(args, opts, help = false) {
|
|
|
223
246
|
process.stdout.write(JSON.stringify({ ok: !failed, steps }, null, 2) + "\n");
|
|
224
247
|
return failed ? 1 : 0;
|
|
225
248
|
}
|
|
226
|
-
printHumanSteps(results, redact, verbose);
|
|
249
|
+
printHumanSteps(results, redact, verbose, name);
|
|
227
250
|
// Path-level detail for hooks (printHumanSteps only shows the synthetic step).
|
|
228
251
|
if ((target === "hooks" || target === "all") &&
|
|
229
252
|
(dryRun || (human && (verbose || hookWrites.some((w) => w.action !== "skipped"))))) {
|
package/dist/commands/login.d.ts
CHANGED
|
@@ -30,8 +30,13 @@ export interface DeviceLoginIo {
|
|
|
30
30
|
write: (text: string) => void;
|
|
31
31
|
/** Whether the CLI can prompt the user (a real TTY, not a script/CI pipe). */
|
|
32
32
|
isTTY: boolean;
|
|
33
|
-
/**
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Prompt for a new workspace name when the account has zero. `suggestion`
|
|
35
|
+
* is a server-derived default (the user's GitHub login, when it makes a
|
|
36
|
+
* valid and available slug) offered as a bracketed default that Enter
|
|
37
|
+
* accepts; it is always overridable and never auto-submitted.
|
|
38
|
+
*/
|
|
39
|
+
promptWorkspaceName: (suggestion?: string) => Promise<string>;
|
|
35
40
|
}
|
|
36
41
|
export declare const defaultDeviceIo: DeviceLoginIo;
|
|
37
42
|
/** A completed device authorization: the session bearer plus the (possibly rewritten) scope. */
|
package/dist/commands/login.js
CHANGED
|
@@ -185,10 +185,14 @@ function openUrl(url) {
|
|
|
185
185
|
// ignore — the URL is printed for manual navigation.
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
-
async function promptWorkspaceName() {
|
|
188
|
+
async function promptWorkspaceName(suggestion) {
|
|
189
189
|
const rl = createInterface({ input: stdin, output: process.stderr });
|
|
190
|
+
const hint = suggestion ? ` [${suggestion}]` : "";
|
|
190
191
|
try {
|
|
191
|
-
|
|
192
|
+
const answer = (await rl.question(`no workspaces yet — enter a name to create one (lowercase, hyphens)${hint}: `)).trim();
|
|
193
|
+
// Empty input accepts the offered default; with no suggestion it stays
|
|
194
|
+
// empty and the caller's "cancelled" guard fires as before.
|
|
195
|
+
return answer || (suggestion ?? "");
|
|
192
196
|
}
|
|
193
197
|
finally {
|
|
194
198
|
rl.close();
|
|
@@ -347,14 +351,14 @@ async function resolveMintWorkspace(apiUrl, accessToken, requested, io, create =
|
|
|
347
351
|
io.write(`created workspace "${created.name}" — files will get public URLs under ${created.publicBaseUrl}/\n`);
|
|
348
352
|
return created.name;
|
|
349
353
|
}
|
|
350
|
-
const { workspaces } = await listMintWorkspaces(apiUrl, accessToken);
|
|
354
|
+
const { workspaces, suggestedWorkspace } = await listMintWorkspaces(apiUrl, accessToken);
|
|
351
355
|
if (workspaces.length === 1)
|
|
352
356
|
return workspaces[0].workspace;
|
|
353
357
|
if (workspaces.length === 0) {
|
|
354
358
|
if (!io.isTTY) {
|
|
355
359
|
throw new UsageError("your account has no workspace access yet — pass `--workspace <name> --create` to provision one, run `uploads login` interactively, or ask an administrator for an invitation");
|
|
356
360
|
}
|
|
357
|
-
const name = (await io.promptWorkspaceName()).trim();
|
|
361
|
+
const name = (await io.promptWorkspaceName(suggestedWorkspace)).trim();
|
|
358
362
|
if (!name)
|
|
359
363
|
throw new UsageError("workspace creation cancelled");
|
|
360
364
|
const created = await createWorkspaceRequest(apiUrl, accessToken, name);
|
package/dist/commands/update.js
CHANGED
|
@@ -9,9 +9,10 @@ import { runInstall, runStep } from "./install.js";
|
|
|
9
9
|
const UPDATE_HELP = `uploads update — update the CLI and refresh agent integrations
|
|
10
10
|
|
|
11
11
|
Upgrades the globally installed npm package, then re-runs \`uploads install\` so
|
|
12
|
-
the agent skills
|
|
13
|
-
|
|
14
|
-
already
|
|
12
|
+
the agent skills match the new version. Skills drift on their own, so this
|
|
13
|
+
refreshes them even when the CLI is already current. An MCP server already
|
|
14
|
+
registered is left as-is (\`already configured\`) — \`claude mcp add\` never
|
|
15
|
+
overwrites an existing entry.
|
|
15
16
|
|
|
16
17
|
Usage:
|
|
17
18
|
uploads update [options]
|