speculos-toolkit 1.1.1 → 1.2.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 +3 -3
- package/package.json +5 -4
- package/skill/SKILL.md +8 -8
- package/src/client.js +7 -5
- package/src/index.js +23 -11
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ host never runs your build.
|
|
|
17
17
|
|
|
18
18
|
> **Frontend hosting is free; every Speculos account includes one backend app free.** Sign in
|
|
19
19
|
> once with `speculos-toolkit login` and a detected backend deploys too (sign up at
|
|
20
|
-
> https://
|
|
20
|
+
> https://unified.speculos.ai). Without an account the backend is skipped and the frontend still
|
|
21
21
|
> ships free.
|
|
22
22
|
|
|
23
23
|
## Who can see your app
|
|
@@ -26,7 +26,7 @@ host never runs your build.
|
|
|
26
26
|
|---|---|
|
|
27
27
|
| **No account** | the app is **public** — anyone with the link. A private app checks who the visitor is when they open it, and there is no account to check against yet. |
|
|
28
28
|
| **Signed in** | a new app is **private** by default. `--public` shares it, `--org` opens it to your org. |
|
|
29
|
-
| **Already deployed anonymously?** | `speculos-toolkit login` links this machine and your existing apps come with it — then redeploy with `--private`, or flip it on the [dashboard](https://
|
|
29
|
+
| **Already deployed anonymously?** | `speculos-toolkit login` links this machine and your existing apps come with it — then redeploy with `--private`, or flip it on the [dashboard](https://unified.speculos.ai/?tab=deploys). |
|
|
30
30
|
|
|
31
31
|
`--private` or `--org` without an account fails the deploy (`LOGIN_REQUIRED`)
|
|
32
32
|
rather than publishing something public that you asked to keep private.
|
|
@@ -79,6 +79,6 @@ speculos-toolkit teardown --slug <s> remove a deployment
|
|
|
79
79
|
--env KEY=VAL --env-file <file> --api <url> --timeout <sec> --json
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
Docs: https://
|
|
82
|
+
Docs: https://unified.speculos.ai · Source: https://github.com/speculosai/unified_platform
|
|
83
83
|
|
|
84
84
|
MIT
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "speculos-toolkit",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "The Speculos toolkit for coding agents
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "The Speculos toolkit for coding agents \u2014 deploy any frontend/backend to a live URL and build against your linked data connectors (BigQuery, Postgres, Snowflake, Salesforce, \u2026). Built for Claude Code, Codex, Cursor, and friends.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"speculos-toolkit": "bin/speculos-toolkit.js"
|
|
7
7
|
},
|
|
@@ -28,9 +28,10 @@
|
|
|
28
28
|
"speculos"
|
|
29
29
|
],
|
|
30
30
|
"license": "MIT",
|
|
31
|
-
"homepage": "https://
|
|
31
|
+
"homepage": "https://unified.speculos.ai",
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/speculosai/
|
|
34
|
+
"url": "git+https://github.com/speculosai/unified_platform.git",
|
|
35
|
+
"directory": "deploy_plane/packages/cli"
|
|
35
36
|
}
|
|
36
37
|
}
|
package/skill/SKILL.md
CHANGED
|
@@ -29,7 +29,7 @@ with `--frontend`/`--backend` in step 4 (or force a build dir to serve as-is wit
|
|
|
29
29
|
### If the project has a backend, ASK the user how to deploy (before you build)
|
|
30
30
|
|
|
31
31
|
Frontends are **free** and need no account; **every Speculos account includes one backend
|
|
32
|
-
app free** (sign in at https://
|
|
32
|
+
app free** (sign in at https://unified.speculos.ai). So when `detect` finds a backend, DON'T silently
|
|
33
33
|
skip it — ask the user with your question UI (e.g. AskUserQuestion). Tailor the “what won't
|
|
34
34
|
work” line to THIS app (a counter button, a form, login, saved data…). For example:
|
|
35
35
|
|
|
@@ -151,7 +151,7 @@ The last stdout line is `{ ok, brokerUrl, connectors: [{ alias, name, kind, acco
|
|
|
151
151
|
drop the flag (the app ships public) and lock it down after linking.
|
|
152
152
|
- `ok:true` with empty `connectors` → nothing linked (or nothing granted to this user).
|
|
153
153
|
If the app clearly wants external data, tell the user to link a source (or ask their org
|
|
154
|
-
admin for access) at **https://
|
|
154
|
+
admin for access) at **https://unified.speculos.ai/?tab=deploys**, then **re-run the list** —
|
|
155
155
|
access is resolved server-side on every call, so a source linked or granted seconds ago
|
|
156
156
|
shows up immediately with no re-login and no session restart. Never block a deploy on this.
|
|
157
157
|
- A `403 NO_ACCESS` on execute means an org admin hasn't granted that source to this user —
|
|
@@ -306,7 +306,7 @@ Builds run locally (this machine already has the toolchain); only static output
|
|
|
306
306
|
`speculos-toolkit login`, then deploy again.
|
|
307
307
|
- Already deployed anonymously and now want it private? `speculos-toolkit login`
|
|
308
308
|
links this machine and its existing apps come with it — then redeploy with
|
|
309
|
-
`--private`, or flip it at https://
|
|
309
|
+
`--private`, or flip it at https://unified.speculos.ai/?tab=deploys.
|
|
310
310
|
- A redeploy never changes the visibility of an app that is already live.
|
|
311
311
|
|
|
312
312
|
The **last line of stdout is one JSON object**:
|
|
@@ -320,16 +320,16 @@ On `ok:false`, read `error`/`logTail`, fix the cause **once**, and re-run. Do no
|
|
|
320
320
|
> limit (one is included; more come with Team plans). In both cases the deploy still **ships
|
|
321
321
|
> the frontend** and returns `ok:true` with a `backendNote` — so the user already has a live
|
|
322
322
|
> URL. To ship the backend: for `BACKEND_DISABLED`, sign in; for `TOO_MANY`, take an app
|
|
323
|
-
> offline at https://
|
|
323
|
+
> offline at https://unified.speculos.ai/?tab=deploys, redeploy an existing backend app, or talk
|
|
324
324
|
> to our team at https://calendar.app.google/VMGTvK3FmyDMAsix6 about more capacity. Always write the URL as
|
|
325
|
-
> **https://
|
|
325
|
+
> **https://unified.speculos.ai** — never `speculos.ai`. Don't retry the backend; once signed
|
|
326
326
|
> in / under the limit, re-running `deploy` ships it. Redeploying an app that already has a
|
|
327
327
|
> backend never counts against the limit (it reuses its sandbox).
|
|
328
328
|
|
|
329
329
|
## 5. Report + verify
|
|
330
330
|
|
|
331
331
|
- Give the user `urls.frontend` (and `urls.backend` if deployed). They can manage their
|
|
332
|
-
published apps and take them offline anytime at https://
|
|
332
|
+
published apps and take them offline anytime at https://unified.speculos.ai/?tab=deploys. The public URL is
|
|
333
333
|
`user-deployed.speculos.ai/<username>/<app-slug>/`, and BOTH segments are renameable there:
|
|
334
334
|
"Edit link" changes an app's slug (the second segment), and the account's "your link name"
|
|
335
335
|
changes the first segment (default a random id) for every app on that device. Accounts on
|
|
@@ -349,10 +349,10 @@ On `ok:false`, read `error`/`logTail`, fix the cause **once**, and re-run. Do no
|
|
|
349
349
|
## Notes
|
|
350
350
|
|
|
351
351
|
- Until the user signs in, the backend is skipped (frontend-only). Run `speculos-toolkit login`,
|
|
352
|
-
or point users to https://
|
|
352
|
+
or point users to https://unified.speculos.ai.
|
|
353
353
|
- Permission is granted once at skill install, so the deploy command runs without prompting.
|
|
354
354
|
- To remove a deployment: `npx -y speculos-toolkit@latest teardown --slug <slug>` (or use the
|
|
355
|
-
dashboard at https://
|
|
355
|
+
dashboard at https://unified.speculos.ai/?tab=deploys).
|
|
356
356
|
- **Already have this skill from before connectors existed?** Re-run
|
|
357
357
|
`npx -y speculos-toolkit@latest install-skill` to refresh it (safe to run repeatedly — it
|
|
358
358
|
overwrites the skill file and re-grants the command). The CLI itself is always current
|
package/src/client.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
// HTTP client to the Speculos
|
|
2
|
-
// secrets. The
|
|
3
|
-
// deploy
|
|
4
|
-
//
|
|
5
|
-
|
|
1
|
+
// HTTP client to the Speculos platform gateway (management API). Holds no
|
|
2
|
+
// platform secrets. The gateway proxies these calls to the deploy orchestrator
|
|
3
|
+
// on the same box and files every deploy in the console's shared history, so a
|
|
4
|
+
// CLI deploy and a console publish sit in one list under one identity. The
|
|
5
|
+
// orchestrator's own hostname (deploy-orch-38hd4.speculos.ai) still answers and
|
|
6
|
+
// can be selected with --api; deployed apps keep calling the broker there.
|
|
7
|
+
const DEFAULT_API = process.env.SPECULOS_API || "https://unified-api.speculos.ai/toolkit";
|
|
6
8
|
|
|
7
9
|
function base(opts) { return (opts.api || DEFAULT_API).replace(/\/$/, ""); }
|
|
8
10
|
|
package/src/index.js
CHANGED
|
@@ -98,11 +98,11 @@ WHO CAN SEE IT
|
|
|
98
98
|
Already
|
|
99
99
|
deployed? run 'speculos-toolkit login' to link this machine — your existing
|
|
100
100
|
apps come with it — then deploy again with --private, or flip it
|
|
101
|
-
on https://
|
|
101
|
+
on https://unified.speculos.ai/?tab=deploys.
|
|
102
102
|
|
|
103
103
|
A redeploy never changes the visibility of an app that is already live.
|
|
104
104
|
Every Speculos account includes one backend app free (an isolated sandbox per app) —
|
|
105
|
-
sign up at https://
|
|
105
|
+
sign up at https://unified.speculos.ai and run 'speculos-toolkit login'.
|
|
106
106
|
Without an account a detected backend is skipped (frontend still ships free).
|
|
107
107
|
|
|
108
108
|
OPTIONS
|
|
@@ -129,7 +129,7 @@ OPTIONS
|
|
|
129
129
|
--args <json> connectors exec: tool arguments as inline JSON
|
|
130
130
|
--args-file <f> connectors exec: tool arguments from a JSON file (preferred
|
|
131
131
|
in agents — inline JSON with $ ( ) etc. may need approval)
|
|
132
|
-
--api <url>
|
|
132
|
+
--api <url> platform API base (default https://unified-api.speculos.ai/toolkit)
|
|
133
133
|
--timeout <sec> max seconds to wait for the backend (default 600)
|
|
134
134
|
--json machine-readable only (auto-on when non-TTY/CI)
|
|
135
135
|
|
|
@@ -144,6 +144,11 @@ VITE_API_URL / NEXT_PUBLIC_API_URL / API_URL (build) or window.SPECULOS_API_URL.
|
|
|
144
144
|
|
|
145
145
|
async function cmdDetect(root, opts) {
|
|
146
146
|
const d = detect(root, opts);
|
|
147
|
+
// `detect` derives a slug from the directory name; a project that has already
|
|
148
|
+
// been deployed has its real slug in .speculos.json, and deploy honours it.
|
|
149
|
+
// Reporting the derived one made `detect` disagree with what `deploy` would do.
|
|
150
|
+
const saved = creds.load(root);
|
|
151
|
+
if (!opts.slug && saved && saved.slug) d.slug = saved.slug;
|
|
147
152
|
if (!opts.json) {
|
|
148
153
|
log(opts, `slug: ${d.slug}`);
|
|
149
154
|
log(opts, `frontend: ${d.frontend ? `${path.relative(root, d.frontend.dir) || "."} (${d.frontend.kind}${d.frontend.framework ? "/" + d.frontend.framework : ""})` : "none"}`);
|
|
@@ -184,7 +189,7 @@ async function cmdDeploy(root, opts) {
|
|
|
184
189
|
d.backend = null;
|
|
185
190
|
}
|
|
186
191
|
if (!d.frontend && !d.backend) {
|
|
187
|
-
if (hadBackend) { emit({ ok: false, error: `nothing to deploy: Speculos hosts frontends free, but this project is backend-only — sign in to deploy the backend (every Speculos account includes one free backend app). Run \`speculos-toolkit login\` (https://
|
|
192
|
+
if (hadBackend) { emit({ ok: false, error: `nothing to deploy: Speculos hosts frontends free, but this project is backend-only — sign in to deploy the backend (every Speculos account includes one free backend app). Run \`speculos-toolkit login\` (https://unified.speculos.ai), or add a frontend.`, code: "BETA_BACKEND_ONLY" }); return 2; }
|
|
188
193
|
emit({ ok: false, error: `could not detect a frontend in ${root}. Pass --frontend <dir>.`, code: "DETECT" });
|
|
189
194
|
return 2;
|
|
190
195
|
}
|
|
@@ -305,6 +310,7 @@ async function cmdDeploy(root, opts) {
|
|
|
305
310
|
if (alloc && alloc.visibilityNote) log(opts, ` note: ${alloc.visibilityNote}`);
|
|
306
311
|
const out = { ok: true, slug: d.slug, userId, urls };
|
|
307
312
|
if (visibility) out.visibility = visibility;
|
|
313
|
+
if (alloc && alloc.visibilityNote) out.visibilityNote = alloc.visibilityNote;
|
|
308
314
|
if (backendNote) out.backendNote = backendNote;
|
|
309
315
|
emit({ ...out });
|
|
310
316
|
return 0;
|
|
@@ -343,7 +349,7 @@ async function cmdLogin(opts) {
|
|
|
343
349
|
// to a different account, incl. moving a personal-linked device onto an org).
|
|
344
350
|
const existing = creds.loadIdentity();
|
|
345
351
|
if (existing && existing.accountToken && !opts.relink && !opts.pasteToken) {
|
|
346
|
-
log(opts, `✓ this device is already linked to your Speculos account. Pass --relink to link a different account. Manage deployments at https://
|
|
352
|
+
log(opts, `✓ this device is already linked to your Speculos account. Pass --relink to link a different account. Manage deployments at https://unified.speculos.ai/?tab=deploys.`);
|
|
347
353
|
emit({ ok: true, alreadyLinked: true });
|
|
348
354
|
return 0;
|
|
349
355
|
}
|
|
@@ -369,7 +375,7 @@ async function cmdLogin(opts) {
|
|
|
369
375
|
} catch { /* links on next deploy via the token */ }
|
|
370
376
|
}
|
|
371
377
|
if (reparented) process.stderr.write(`\n⚠ This device's deployments moved from ${previousEmail || "another account"} to ${who.email || "this account"}.\n`);
|
|
372
|
-
log(opts, `✓ this device is linked${who.email ? ` to ${who.email}` : ""}${who.org ? ` (org: ${who.org})` : ""} — manage at https://
|
|
378
|
+
log(opts, `✓ this device is linked${who.email ? ` to ${who.email}` : ""}${who.org ? ` (org: ${who.org})` : ""} — manage at https://unified.speculos.ai/?tab=deploys.`);
|
|
373
379
|
emit({ ok: true, linked: true, account: who.email || null, org: who.org || null, reparented, previousAccount: previousEmail });
|
|
374
380
|
return 0;
|
|
375
381
|
}
|
|
@@ -383,7 +389,7 @@ async function cmdLogin(opts) {
|
|
|
383
389
|
// And on stderr (even in --json) for a human watching.
|
|
384
390
|
process.stderr.write(`\nLink this machine to your Speculos account to enable backend hosting:\n`);
|
|
385
391
|
process.stderr.write(`\n 1. Open: ${start.url}\n`);
|
|
386
|
-
process.stderr.write(` (or go to https://
|
|
392
|
+
process.stderr.write(` (or go to https://unified.speculos.ai/link and enter code ${start.code})\n`);
|
|
387
393
|
process.stderr.write(` 2. Sign in / sign up, then click Approve.\n\n`);
|
|
388
394
|
process.stderr.write(`Waiting for approval…\n`);
|
|
389
395
|
|
|
@@ -411,7 +417,7 @@ async function cmdLogin(opts) {
|
|
|
411
417
|
if (reparented) {
|
|
412
418
|
process.stderr.write(`\n⚠ This device's deployments moved from ${previousEmail || "another account"} to ${approvedEmail || "this account"}. They now appear only in the new account's dashboard.\n`);
|
|
413
419
|
}
|
|
414
|
-
log(opts, `✓ this device is linked${approvedEmail ? ` to ${approvedEmail}` : ""} — manage your deployments at https://
|
|
420
|
+
log(opts, `✓ this device is linked${approvedEmail ? ` to ${approvedEmail}` : ""} — manage your deployments at https://unified.speculos.ai/?tab=deploys.`);
|
|
415
421
|
emit({ ok: true, linked, account: approvedEmail, reparented, previousAccount: previousEmail });
|
|
416
422
|
return 0;
|
|
417
423
|
}
|
|
@@ -451,7 +457,7 @@ async function cmdConnectors(opts) {
|
|
|
451
457
|
const identity = creds.loadIdentity();
|
|
452
458
|
const token = identity && identity.accountToken;
|
|
453
459
|
if (!token) {
|
|
454
|
-
emit({ ok: false, code: "NO_TOKEN", error: "not signed in — run `npx -y speculos-toolkit@latest login`, then link data sources at https://
|
|
460
|
+
emit({ ok: false, code: "NO_TOKEN", error: "not signed in — run `npx -y speculos-toolkit@latest login`, then link data sources at https://unified.speculos.ai/?tab=deploys" });
|
|
455
461
|
return 1;
|
|
456
462
|
}
|
|
457
463
|
const sub = opts._[1] || "list";
|
|
@@ -462,8 +468,14 @@ async function cmdConnectors(opts) {
|
|
|
462
468
|
catch (e) { emit({ ok: false, error: e.message, code: e.code || "CONNECTORS" }); return 1; }
|
|
463
469
|
const conns = r.connectors || [];
|
|
464
470
|
if (!opts.json) {
|
|
465
|
-
if (!conns.length) log(opts, "no data sources linked — link one at https://
|
|
466
|
-
for (const c of conns)
|
|
471
|
+
if (!conns.length) log(opts, "no data sources linked — link one at https://unified.speculos.ai/?tab=deploys");
|
|
472
|
+
for (const c of conns) {
|
|
473
|
+
// Say which product a source came from: the deploy plane and the
|
|
474
|
+
// Cloud console hold separate connections, often to the same SaaS,
|
|
475
|
+
// and running a tool against the wrong one is silent and wrong.
|
|
476
|
+
const scope = c.orgWide ? " · org" : "";
|
|
477
|
+
log(opts, ` ${c.alias} (${c.name}${c.accountIdentifier ? " · " + c.accountIdentifier : ""}${scope}) — ${(c.tools || []).length} tools`);
|
|
478
|
+
}
|
|
467
479
|
}
|
|
468
480
|
emit({ ok: true, brokerUrl: client.base(opts) + "/api/connectors", connectors: conns });
|
|
469
481
|
return 0;
|