speculos-toolkit 1.2.0 → 1.2.2
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 +11 -1
- package/package.json +1 -1
- package/skill/SKILL.md +5 -0
- package/src/client.js +11 -7
- package/src/index.js +8 -5
package/README.md
CHANGED
|
@@ -73,12 +73,22 @@ speculos-toolkit [deploy] detect, pack, deploy ./
|
|
|
73
73
|
speculos-toolkit detect show what would deploy (no upload)
|
|
74
74
|
speculos-toolkit status <jobId> poll a deployment
|
|
75
75
|
speculos-toolkit teardown --slug <s> remove a deployment
|
|
76
|
+
speculos-toolkit login [--token …] link this machine (browser approval at
|
|
77
|
+
https://unified.speculos.ai/link)
|
|
78
|
+
speculos-toolkit logout unlink it (revokes this device's token)
|
|
79
|
+
speculos-toolkit connectors [list|exec] your linked data sources, and one tool call
|
|
80
|
+
speculos-toolkit install-skill install the Claude Code skill
|
|
76
81
|
|
|
77
82
|
--frontend <dir> --backend <dir> --slug <name>
|
|
78
|
-
--runtime node|python --start "<cmd>" --build --output <dir>
|
|
83
|
+
--runtime node|python|bun --start "<cmd>" --build --static --output <dir>
|
|
84
|
+
--private | --org | --public
|
|
79
85
|
--env KEY=VAL --env-file <file> --api <url> --timeout <sec> --json
|
|
80
86
|
```
|
|
81
87
|
|
|
88
|
+
`--api` defaults to `https://unified-api.speculos.ai/toolkit` (the platform gateway,
|
|
89
|
+
which proxies to the deploy orchestrator and records the deploy in the console's
|
|
90
|
+
history); `SPECULOS_API` overrides it.
|
|
91
|
+
|
|
82
92
|
Docs: https://unified.speculos.ai · Source: https://github.com/speculosai/unified_platform
|
|
83
93
|
|
|
84
94
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "speculos-toolkit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
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"
|
package/skill/SKILL.md
CHANGED
|
@@ -222,6 +222,11 @@ export async function connector(alias, tool, args) {
|
|
|
222
222
|
}
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
(The `x-speculos-gate` line is belt-and-braces: the deployed page also loads an
|
|
226
|
+
injected `speculos-env.js` that patches `fetch`/`XMLHttpRequest` to add that header
|
|
227
|
+
on every call to the backend or broker origin, so an app written WITHOUT this helper
|
|
228
|
+
still works when it is private. Keeping it costs nothing and makes the flow explicit.)
|
|
229
|
+
|
|
225
230
|
Rules (and state to the user):
|
|
226
231
|
- **Read-only only.** The broker rejects any non-read tool on this path (`WRITE_BLOCKED`).
|
|
227
232
|
Use `LIST_`/`GET_`/`SEARCH_`/`QUERY` tools; `POSTGRES_QUERY` runs in a read-only transaction.
|
package/src/client.js
CHANGED
|
@@ -16,8 +16,8 @@ async function post(url, body, token) {
|
|
|
16
16
|
if (!res.ok) { const e = new Error(data.error || `HTTP ${res.status}`); e.code = data.code; e.status = res.status; throw e; }
|
|
17
17
|
return data;
|
|
18
18
|
}
|
|
19
|
-
async function get(url, token) {
|
|
20
|
-
const headers = {};
|
|
19
|
+
async function get(url, token, extra) {
|
|
20
|
+
const headers = Object.assign({}, extra || {});
|
|
21
21
|
if (token) headers["Authorization"] = "Bearer " + token;
|
|
22
22
|
const res = await fetch(url, { headers });
|
|
23
23
|
const data = await res.json().catch(() => ({}));
|
|
@@ -38,14 +38,18 @@ async function whoami(token, opts = {}) { return get(base(opts) + "/api/account/
|
|
|
38
38
|
async function linkMachine(token, payload, opts = {}) { return post(base(opts) + "/api/account/link", payload, token); }
|
|
39
39
|
async function logout(token, opts = {}) { return post(base(opts) + "/api/account/logout", {}, token); }
|
|
40
40
|
// poll a backend job (owner-authenticated)
|
|
41
|
+
// The machine credentials travel as headers, not a query string: a query
|
|
42
|
+
// string is written to every access log between here and the orchestrator.
|
|
41
43
|
async function backendStatus(jobId, creds = {}, opts = {}) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
return get(base(opts) + "/api/backend/" + encodeURIComponent(jobId), null,
|
|
45
|
+
{ "x-speculos-user-id": creds.userId || "", "x-speculos-user-key": creds.userKey || "" });
|
|
44
46
|
}
|
|
45
|
-
// upload built/static frontend
|
|
46
|
-
|
|
47
|
+
// upload built/static frontend. The account token rides along so the gateway
|
|
48
|
+
// can file the deploy under the person, not the machine; the orchestrator
|
|
49
|
+
// itself authorizes on the machine credentials in the body as before.
|
|
50
|
+
async function putFrontend(payload, opts = {}) { return post(base(opts) + "/api/frontend", payload, opts.token); }
|
|
47
51
|
// remove a deployment
|
|
48
|
-
async function teardown(payload, opts = {}) { return post(base(opts) + "/api/teardown", payload); }
|
|
52
|
+
async function teardown(payload, opts = {}) { return post(base(opts) + "/api/teardown", payload, opts.token); }
|
|
49
53
|
// ---- connectors (data sources linked in the dashboard) ----
|
|
50
54
|
async function connectorsList(token, opts = {}) {
|
|
51
55
|
return get(base(opts) + "/api/connectors" + (opts.noTools ? "?tools=0" : ""), token);
|
package/src/index.js
CHANGED
|
@@ -107,7 +107,8 @@ Without an account a detected backend is skipped (frontend still ships free).
|
|
|
107
107
|
|
|
108
108
|
OPTIONS
|
|
109
109
|
--frontend <dir> frontend dir (auto-detected: frontend/web/client/… or ./ if index.html)
|
|
110
|
-
--backend <dir> backend dir (auto-detected: backend/api/server/…) — needs
|
|
110
|
+
--backend <dir> backend dir (auto-detected: backend/api/server/…) — needs a linked
|
|
111
|
+
account (one backend app is included) or --override
|
|
111
112
|
--slug <slug> deployment slug (default: <folder>-<hash>)
|
|
112
113
|
--runtime <r> backend runtime: node | python | bun (auto-detected)
|
|
113
114
|
--start <cmd> backend start command (auto-detected; must bind 0.0.0.0:$PORT)
|
|
@@ -130,7 +131,7 @@ OPTIONS
|
|
|
130
131
|
--args-file <f> connectors exec: tool arguments from a JSON file (preferred
|
|
131
132
|
in agents — inline JSON with $ ( ) etc. may need approval)
|
|
132
133
|
--api <url> platform API base (default https://unified-api.speculos.ai/toolkit)
|
|
133
|
-
--timeout <sec> max seconds to wait for the backend (default
|
|
134
|
+
--timeout <sec> max seconds to wait for the backend (default 960)
|
|
134
135
|
--json machine-readable only (auto-on when non-TTY/CI)
|
|
135
136
|
|
|
136
137
|
Identity: the first deploy mints a machine-global { userId, userKey } saved to
|
|
@@ -335,6 +336,7 @@ async function cmdTeardown(root, opts) {
|
|
|
335
336
|
if (!slug) { emit({ ok: false, error: "--slug required (or run from the project dir with a .speculos.json)" }); return 2; }
|
|
336
337
|
if (!identity || !identity.userId) { emit({ ok: false, error: "no identity found (~/.speculos/identity.json) — nothing to tear down from this machine" }); return 1; }
|
|
337
338
|
try {
|
|
339
|
+
if (identity.accountToken) opts.token = identity.accountToken;
|
|
338
340
|
const r = await client.teardown({ userId: identity.userId, userKey: identity.userKey, slug }, opts);
|
|
339
341
|
if (saved && saved.slug === slug) creds.remove(root);
|
|
340
342
|
emit({ ok: true, ...r });
|
|
@@ -470,9 +472,10 @@ async function cmdConnectors(opts) {
|
|
|
470
472
|
if (!opts.json) {
|
|
471
473
|
if (!conns.length) log(opts, "no data sources linked — link one at https://unified.speculos.ai/?tab=deploys");
|
|
472
474
|
for (const c of conns) {
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
//
|
|
475
|
+
// Mark the org-wide sources: a person in an org sees those plus their
|
|
476
|
+
// own, and which one a tool runs against matters. (Console and toolkit
|
|
477
|
+
// now share ONE Composio workspace, so there is no "which product"
|
|
478
|
+
// left to disambiguate — a source linked in the console is this source.)
|
|
476
479
|
const scope = c.orgWide ? " · org" : "";
|
|
477
480
|
log(opts, ` ${c.alias} (${c.name}${c.accountIdentifier ? " · " + c.accountIdentifier : ""}${scope}) — ${(c.tools || []).length} tools`);
|
|
478
481
|
}
|