@thingd/cli 0.59.0 → 0.61.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/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +227 -46
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +7 -6
- package/dist/lib/cloud-api.d.ts +30 -0
- package/dist/lib/cloud-api.d.ts.map +1 -1
- package/dist/lib/cloud-api.js +37 -1
- package/dist/lib/cloud-config.d.ts +8 -3
- package/dist/lib/cloud-config.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/commands/cloud.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/commands/cloud.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,UAAU,EAA6B,MAAM,aAAa,CAAC;AAkHzE,wBAAsB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCjE"}
|
package/dist/commands/cloud.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
+
import * as os from "node:os";
|
|
2
3
|
import { createInterface } from "node:readline/promises";
|
|
3
4
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
4
5
|
import pc from "picocolors";
|
|
5
6
|
import { requiredToken, stringFlag } from "../index.js";
|
|
6
|
-
import { addOrganizationMember, CloudApiError, createApiKey, createInstance, createOrganization, createProject, getMe, getOrganization, listInstances, listOrganizationMembers, listOrganizations, listProjects, pollCliAuth, removeOrganizationMember, resolveAllInstances, startCliAuth, } from "../lib/cloud-api.js";
|
|
7
|
+
import { addOrganizationMember, CloudApiError, cleanupSessions, createApiKey, createInstance, createOrganization, createProject, createUserToken, getMe, getOrganization, listInstances, listOrganizationMembers, listOrganizations, listProjects, listUserTokens, parseUserTokenId, pollCliAuth, removeOrganizationMember, resolveAllInstances, revokeUserToken, startCliAuth, updateUserToken, } from "../lib/cloud-api.js";
|
|
7
8
|
import { readCloudConfig, removeCloudConfig, writeCloudConfig, } from "../lib/cloud-config.js";
|
|
8
9
|
const POLL_INTERVAL_MS = 2_000;
|
|
9
10
|
const POLL_TIMEOUT_MS = 5 * 60 * 1_000;
|
|
@@ -37,7 +38,7 @@ function openBrowser(url) {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
function makeBaseConfig(context) {
|
|
40
|
-
return {
|
|
41
|
+
return { url: cliApiUrl(context) };
|
|
41
42
|
}
|
|
42
43
|
async function askQuestion(rl, query) {
|
|
43
44
|
return rl.question(query);
|
|
@@ -77,35 +78,6 @@ async function pickAndSaveInstance(context, cloudConfig) {
|
|
|
77
78
|
rl.close();
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Ensure a data-plane API key exists in the cloud config.
|
|
82
|
-
* Best-effort — if creation fails, the JWT is still usable for management API.
|
|
83
|
-
*/
|
|
84
|
-
async function ensureApiKey(context, cloudConfig) {
|
|
85
|
-
if (cloudConfig.apiKey) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
try {
|
|
89
|
-
let projectId = cloudConfig.projectId;
|
|
90
|
-
if (!projectId) {
|
|
91
|
-
const { projects } = await listProjects(cloudConfig);
|
|
92
|
-
if (projects.length === 0) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
projectId = projects[0]?.id;
|
|
96
|
-
}
|
|
97
|
-
if (!projectId) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const result = await createApiKey(cloudConfig, projectId);
|
|
101
|
-
cloudConfig.apiKey = result.token;
|
|
102
|
-
writeCloudConfig(cloudConfig);
|
|
103
|
-
context.stderr.write(` ${pc.dim("API key created for data access\n")}`);
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
// API key creation is best-effort
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
81
|
export async function runCloud(context) {
|
|
110
82
|
const sub = requiredToken(context.parsed, 1, "subcommand");
|
|
111
83
|
switch (sub) {
|
|
@@ -118,6 +90,9 @@ export async function runCloud(context) {
|
|
|
118
90
|
case "status":
|
|
119
91
|
await runCloudStatus(context);
|
|
120
92
|
return;
|
|
93
|
+
case "token":
|
|
94
|
+
await runToken(context);
|
|
95
|
+
return;
|
|
121
96
|
case "project":
|
|
122
97
|
await runProject(context);
|
|
123
98
|
return;
|
|
@@ -125,6 +100,7 @@ export async function runCloud(context) {
|
|
|
125
100
|
await runInstance(context);
|
|
126
101
|
return;
|
|
127
102
|
case "api-key":
|
|
103
|
+
context.stderr.write(pc.yellow("Deprecated. Use `thingd cloud token create` for CLI tokens. Project API keys are managed in the dashboard.\n"));
|
|
128
104
|
await runApiKey(context);
|
|
129
105
|
return;
|
|
130
106
|
case "org":
|
|
@@ -132,7 +108,7 @@ export async function runCloud(context) {
|
|
|
132
108
|
return;
|
|
133
109
|
default:
|
|
134
110
|
context.stderr.write(`Unknown cloud subcommand: ${sub}\n` +
|
|
135
|
-
"Available: login, logout, status, org, project, instance, api-key\n");
|
|
111
|
+
"Available: login, logout, status, token, org, project, instance, api-key\n");
|
|
136
112
|
}
|
|
137
113
|
}
|
|
138
114
|
async function runLogin(context) {
|
|
@@ -140,17 +116,19 @@ async function runLogin(context) {
|
|
|
140
116
|
const token = context.parsed.tokens[3] ?? stringFlag(context.parsed, "token");
|
|
141
117
|
// ── Manual --code --token flow (fallback) ─────────────────────────
|
|
142
118
|
if (code && token) {
|
|
143
|
-
const config = {
|
|
119
|
+
const config = { url: cliApiUrl(context) };
|
|
144
120
|
try {
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
//
|
|
121
|
+
// Verify JWT and get user info
|
|
122
|
+
const { user } = await getMe({ ...config, token });
|
|
123
|
+
// Create a permanent user token
|
|
124
|
+
const hostname = os.hostname();
|
|
125
|
+
const { token: userToken } = await createUserToken({ ...config, token }, `cli-${hostname}`);
|
|
126
|
+
// Save config with user token (not JWT)
|
|
127
|
+
const cloudConfig = { userToken, email: user.email, ...config };
|
|
148
128
|
writeCloudConfig(cloudConfig);
|
|
149
129
|
context.stdout.write(pc.green(`✓ Logged in as ${user.email}\n`));
|
|
150
130
|
// Discover and select an instance (interactive if multiple)
|
|
151
131
|
await pickAndSaveInstance(context, cloudConfig);
|
|
152
|
-
// Create a data-plane API key for REST/MCP operations
|
|
153
|
-
await ensureApiKey(context, cloudConfig);
|
|
154
132
|
}
|
|
155
133
|
catch (err) {
|
|
156
134
|
if (err instanceof CloudApiError && err.status === 401) {
|
|
@@ -187,17 +165,20 @@ async function runLogin(context) {
|
|
|
187
165
|
try {
|
|
188
166
|
const result = await pollCliAuth(baseConfig, deviceCode);
|
|
189
167
|
if ("token" in result) {
|
|
190
|
-
const
|
|
168
|
+
const jwt = result.token;
|
|
169
|
+
const tokenConfig = { url: cliApiUrl(context) };
|
|
191
170
|
try {
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
//
|
|
171
|
+
// Verify temporary JWT
|
|
172
|
+
const { user } = await getMe({ ...tokenConfig, token: jwt });
|
|
173
|
+
// Create permanent user token
|
|
174
|
+
const hostname = os.hostname();
|
|
175
|
+
const { token: userToken } = await createUserToken({ ...tokenConfig, token: jwt }, `cli-${hostname}`);
|
|
176
|
+
// Save config with user token (not JWT)
|
|
177
|
+
const cloudConfig = { userToken, email: user.email, ...tokenConfig };
|
|
195
178
|
writeCloudConfig(cloudConfig);
|
|
196
179
|
context.stdout.write(pc.green(`\r✓ Logged in as ${user.email}\n`));
|
|
197
180
|
// Discover and select an instance (interactive if multiple)
|
|
198
181
|
await pickAndSaveInstance(context, cloudConfig);
|
|
199
|
-
// Create a data-plane API key for REST/MCP operations
|
|
200
|
-
await ensureApiKey(context, cloudConfig);
|
|
201
182
|
return;
|
|
202
183
|
}
|
|
203
184
|
catch {
|
|
@@ -219,6 +200,18 @@ async function runLogin(context) {
|
|
|
219
200
|
context.stderr.write(pc.red("\nTimed out. Run `thingd cloud login` again.\n"));
|
|
220
201
|
}
|
|
221
202
|
async function runLogout(context) {
|
|
203
|
+
const config = readCloudConfig();
|
|
204
|
+
if (config?.userToken) {
|
|
205
|
+
try {
|
|
206
|
+
const tokenId = parseUserTokenId(config.userToken);
|
|
207
|
+
if (tokenId) {
|
|
208
|
+
await revokeUserToken(config, tokenId);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Best-effort — token may already be revoked or API unreachable
|
|
213
|
+
}
|
|
214
|
+
}
|
|
222
215
|
removeCloudConfig();
|
|
223
216
|
context.stdout.write(pc.green("✓ Logged out\n"));
|
|
224
217
|
}
|
|
@@ -228,10 +221,31 @@ async function runCloudStatus(context) {
|
|
|
228
221
|
context.stdout.write(`Not logged in. Run ${pc.cyan("thingd cloud login")}\n`);
|
|
229
222
|
return;
|
|
230
223
|
}
|
|
224
|
+
// Warn about old config format
|
|
225
|
+
if (config.token && !config.userToken) {
|
|
226
|
+
context.stdout.write(`${pc.yellow("Your credentials use an older format.")} Run ${pc.cyan("thingd cloud login")} to upgrade to a persistent CLI token.\n\n`);
|
|
227
|
+
}
|
|
231
228
|
try {
|
|
232
229
|
const { user } = await getMe(config);
|
|
233
|
-
context.stdout.write(`Logged in as ${pc.green(user.email)} (${user.role})\n`
|
|
234
|
-
|
|
230
|
+
context.stdout.write(`Logged in as ${pc.green(user.email)} (${user.role})\n`);
|
|
231
|
+
// Show token info
|
|
232
|
+
if (config.userToken) {
|
|
233
|
+
try {
|
|
234
|
+
const { userTokens } = await listUserTokens(config);
|
|
235
|
+
const active = userTokens.find((t) => !t.revokedAt);
|
|
236
|
+
if (active) {
|
|
237
|
+
context.stdout.write(`CLI Token: ${pc.cyan(active.name)}\n` +
|
|
238
|
+
` Prefix: ${pc.dim(active.prefix)}\n` +
|
|
239
|
+
` Created: ${pc.dim(formatTimeAgo(active.createdAt))}\n` +
|
|
240
|
+
` Last used:${pc.dim(active.lastUsedAt ? formatTimeAgo(active.lastUsedAt) : "never")}\n` +
|
|
241
|
+
` Access: ${pc.dim(active.projectAccess === "all" ? "All projects" : active.projectAccess)}\n`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
context.stdout.write(` ${pc.dim("(token info unavailable)")}\n`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
context.stdout.write(`API: ${config.url ?? "https://api.thingd.cloud"}\n`);
|
|
235
249
|
if (config.projectSlug && config.instanceSlug && config.instanceUrl) {
|
|
236
250
|
context.stdout.write(`Instance: ${pc.cyan(config.projectSlug)}/${pc.cyan(config.instanceSlug)}\n` +
|
|
237
251
|
` ${pc.dim(config.instanceUrl)}\n`);
|
|
@@ -256,6 +270,173 @@ async function runCloudStatus(context) {
|
|
|
256
270
|
context.stdout.write(`Token expired. Run ${pc.cyan("thingd cloud login")}\n`);
|
|
257
271
|
}
|
|
258
272
|
}
|
|
273
|
+
function formatTimeAgo(iso) {
|
|
274
|
+
const ms = Date.now() - new Date(iso).getTime();
|
|
275
|
+
const s = Math.floor(ms / 1000);
|
|
276
|
+
if (s < 5) {
|
|
277
|
+
return "just now";
|
|
278
|
+
}
|
|
279
|
+
if (s < 60) {
|
|
280
|
+
return `${s}s ago`;
|
|
281
|
+
}
|
|
282
|
+
const m = Math.floor(s / 60);
|
|
283
|
+
if (m < 60) {
|
|
284
|
+
return `${m}m ago`;
|
|
285
|
+
}
|
|
286
|
+
const h = Math.floor(m / 60);
|
|
287
|
+
if (h < 24) {
|
|
288
|
+
return `${h}h ago`;
|
|
289
|
+
}
|
|
290
|
+
const d = Math.floor(h / 24);
|
|
291
|
+
return `${d}d ago`;
|
|
292
|
+
}
|
|
293
|
+
// ── Token subcommands ────────────────────────────────────────────────
|
|
294
|
+
function requireLoggedInConfig(context) {
|
|
295
|
+
const config = readCloudConfig();
|
|
296
|
+
if (!config?.userToken && !config?.token) {
|
|
297
|
+
context.stderr.write(pc.yellow("Not logged in. Run thingd cloud login first.\n"));
|
|
298
|
+
throw new Error("not_logged_in");
|
|
299
|
+
}
|
|
300
|
+
return config;
|
|
301
|
+
}
|
|
302
|
+
async function runToken(context) {
|
|
303
|
+
const sub = context.parsed.tokens[2];
|
|
304
|
+
if (!sub) {
|
|
305
|
+
context.stderr.write("Usage: thingd cloud token <list|create|revoke|restrict|unrestricted>\n");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
switch (sub) {
|
|
309
|
+
case "list":
|
|
310
|
+
await runTokenList(context);
|
|
311
|
+
return;
|
|
312
|
+
case "create":
|
|
313
|
+
await runTokenCreate(context);
|
|
314
|
+
return;
|
|
315
|
+
case "revoke":
|
|
316
|
+
await runTokenRevoke(context);
|
|
317
|
+
return;
|
|
318
|
+
case "restrict":
|
|
319
|
+
await runTokenRestrict(context);
|
|
320
|
+
return;
|
|
321
|
+
case "unrestricted":
|
|
322
|
+
await runTokenUnrestricted(context);
|
|
323
|
+
return;
|
|
324
|
+
case "cleanup":
|
|
325
|
+
await runTokenCleanup(context);
|
|
326
|
+
return;
|
|
327
|
+
default:
|
|
328
|
+
context.stderr.write(`Unknown token action: ${sub}. Available: list, create, revoke, restrict, unrestricted, cleanup\n`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async function runTokenCleanup(context) {
|
|
332
|
+
const config = requireLoggedInConfig(context);
|
|
333
|
+
try {
|
|
334
|
+
// Clean up expired sessions on the user's account
|
|
335
|
+
const { removed } = await cleanupSessions(config);
|
|
336
|
+
context.stdout.write(pc.green(`✓ Cleaned up ${removed} expired sessions\n`));
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
context.stderr.write(pc.red(`Failed to clean up sessions: ${err}\n`));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async function runTokenList(context) {
|
|
343
|
+
const config = requireLoggedInConfig(context);
|
|
344
|
+
try {
|
|
345
|
+
const { userTokens } = await listUserTokens(config);
|
|
346
|
+
if (userTokens.length === 0) {
|
|
347
|
+
context.stdout.write("No CLI tokens found. Create one with `thingd cloud token create <name>`\n");
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
// Header
|
|
351
|
+
const header = `${pc.bold("Name".padEnd(18))} ${pc.bold("Prefix".padEnd(22))} ${pc.bold("Created".padEnd(14))} ${pc.bold("Last Used".padEnd(14))} ${pc.bold("Access")}`;
|
|
352
|
+
context.stdout.write(`${header}\n`);
|
|
353
|
+
context.stdout.write(`${pc.dim("─".repeat(header.length))}\n`);
|
|
354
|
+
for (const t of userTokens) {
|
|
355
|
+
if (t.revokedAt) {
|
|
356
|
+
continue; // Skip revoked tokens
|
|
357
|
+
}
|
|
358
|
+
const access = t.projectAccess === "all" ? "All" : t.projectAccess;
|
|
359
|
+
context.stdout.write(`${t.name.padEnd(18)} ${pc.dim(t.prefix.padEnd(22))} ${formatTimeAgo(t.createdAt).padEnd(14)} ${(t.lastUsedAt ? formatTimeAgo(t.lastUsedAt) : "never").padEnd(14)} ${pc.cyan(access)}\n`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
context.stderr.write(pc.red(`Failed to list tokens: ${err}\n`));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async function runTokenCreate(context) {
|
|
367
|
+
const config = requireLoggedInConfig(context);
|
|
368
|
+
const name = context.parsed.tokens[3] ?? "cli-token";
|
|
369
|
+
try {
|
|
370
|
+
const { token: userToken } = await createUserToken(config, name);
|
|
371
|
+
context.stdout.write(`\n${pc.green("✓ Token created")}\n\n` +
|
|
372
|
+
`${pc.bold(userToken)}\n\n` +
|
|
373
|
+
`${pc.yellow("⚠ This token will only be shown once. Copy it now.\n")}` +
|
|
374
|
+
`${pc.dim("Press Enter to continue...")}\n`);
|
|
375
|
+
// Wait for user to acknowledge
|
|
376
|
+
const rl = createInterface({
|
|
377
|
+
input: context.stdin,
|
|
378
|
+
output: context.stderr,
|
|
379
|
+
});
|
|
380
|
+
await rl.question("");
|
|
381
|
+
rl.close();
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
context.stderr.write(pc.red(`Failed to create token: ${err}\n`));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async function runTokenRevoke(context) {
|
|
388
|
+
const config = requireLoggedInConfig(context);
|
|
389
|
+
const tokenId = requiredToken(context.parsed, 3, "token-id");
|
|
390
|
+
try {
|
|
391
|
+
await revokeUserToken(config, tokenId);
|
|
392
|
+
context.stdout.write(pc.green(`✓ Token ${tokenId} revoked\n`));
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
if (err instanceof CloudApiError && err.status === 404) {
|
|
396
|
+
context.stderr.write(pc.red("Token not found.\n"));
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
context.stderr.write(pc.red(`Failed to revoke token: ${err}\n`));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async function runTokenRestrict(context) {
|
|
404
|
+
const config = requireLoggedInConfig(context);
|
|
405
|
+
const tokenId = requiredToken(context.parsed, 3, "token-id");
|
|
406
|
+
const projectSlug = requiredToken(context.parsed, 4, "project");
|
|
407
|
+
try {
|
|
408
|
+
const { userToken } = await updateUserToken(config, tokenId, {
|
|
409
|
+
projectAccess: projectSlug,
|
|
410
|
+
});
|
|
411
|
+
context.stdout.write(pc.green(`✓ Token "${userToken.name}" restricted to: ${projectSlug}\n`));
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
if (err instanceof CloudApiError && err.status === 404) {
|
|
415
|
+
context.stderr.write(pc.red("Token not found.\n"));
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
context.stderr.write(pc.red(`Failed to update token: ${err}\n`));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async function runTokenUnrestricted(context) {
|
|
423
|
+
const config = requireLoggedInConfig(context);
|
|
424
|
+
const tokenId = requiredToken(context.parsed, 3, "token-id");
|
|
425
|
+
try {
|
|
426
|
+
const { userToken } = await updateUserToken(config, tokenId, {
|
|
427
|
+
projectAccess: "all",
|
|
428
|
+
});
|
|
429
|
+
context.stdout.write(pc.green(`✓ Token "${userToken.name}" now has access to all projects\n`));
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
if (err instanceof CloudApiError && err.status === 404) {
|
|
433
|
+
context.stderr.write(pc.red("Token not found.\n"));
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
context.stderr.write(pc.red(`Failed to update token: ${err}\n`));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
259
440
|
// ── Org helpers ──────────────────────────────────────────────────────
|
|
260
441
|
async function resolveOrg(config, slugOrId) {
|
|
261
442
|
// Try direct ID lookup first, then assume it's a slug and list
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAQA,OAAO,EAUL,MAAM,EACN,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AASrB,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAEjD,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,KAAK,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC;AAE1C,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAsCF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAwFF,wBAAsB,MAAM,CAC1B,IAAI,WAAwB,EAC5B,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CA8EjB;AA08BD,wBAAsB,MAAM,CAC1B,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAef;AA4BD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAQA,OAAO,EAUL,MAAM,EACN,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AASrB,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAEjD,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,KAAK,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC;AAE1C,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAsCF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAwFF,wBAAsB,MAAM,CAC1B,IAAI,WAAwB,EAC5B,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CA8EjB;AA08BD,wBAAsB,MAAM,CAC1B,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAef;AA4BD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,iBAAiB,CAsDxE;AAiDD,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/E;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAGlF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMrE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEnF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMrF;AAyFD,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAEpF;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAElE"}
|
package/dist/index.js
CHANGED
|
@@ -1002,6 +1002,7 @@ export function resolveConnection(context) {
|
|
|
1002
1002
|
}
|
|
1003
1003
|
const resolvedAuthToken = stringFlag(context.parsed, "auth-token") ??
|
|
1004
1004
|
context.env.THINGD_AUTH_TOKEN ??
|
|
1005
|
+
cloudCfg?.userToken ??
|
|
1005
1006
|
cloudCfg?.apiKey ??
|
|
1006
1007
|
cloudCfg?.token;
|
|
1007
1008
|
// Prefer instanceUrl (resolved MCP endpoint) over raw cloudCfg.url (API base)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AAqxGA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA0FvD"}
|
package/dist/interactive.js
CHANGED
|
@@ -824,7 +824,8 @@ function scheduleLoad(node) {
|
|
|
824
824
|
: d === "native"
|
|
825
825
|
? ` Persistent SQLite database.\n Data is stored on disk.\n\n ${pc.dim("Best for: local development, single-node")}\n\n ${pc.dim("Press")} ${pc.bold("Enter")} ${pc.dim("to connect.")}`
|
|
826
826
|
: (() => {
|
|
827
|
-
const
|
|
827
|
+
const cfg = readCloudConfig();
|
|
828
|
+
const hasCfg = !!(cfg?.userToken ?? cfg?.token);
|
|
828
829
|
return hasCfg
|
|
829
830
|
? ` Connect to a remote thingd instance.\n Requires a URL and optional auth token.\n\n ${pc.dim("Best for: production, multi-node")}\n\n ${pc.dim("Press")} ${pc.bold("Enter")} ${pc.dim("to connect.")}`
|
|
830
831
|
: ` ${pc.yellow("Not logged in to thingd Cloud.")}\n\n Run ${pc.cyan("thingd cloud login")} to authenticate, or\n press ${pc.bold("Enter")} to connect with a URL and token manually.`;
|
|
@@ -2731,14 +2732,14 @@ async function handleConnect(node) {
|
|
|
2731
2732
|
cloudCfg.projectSlug = selected.projectSlug;
|
|
2732
2733
|
cloudCfg.instanceSlug = selected.instanceSlug;
|
|
2733
2734
|
writeCloudConfig(cloudCfg);
|
|
2734
|
-
await connectToDriver("cloud", deriveRestUrl(selected.mcpUrl), deriveRestUrl(selected.mcpUrl), cloudCfg.apiKey ?? cloudCfg.token, selected.instanceSlug);
|
|
2735
|
+
await connectToDriver("cloud", deriveRestUrl(selected.mcpUrl), deriveRestUrl(selected.mcpUrl), cloudCfg.userToken ?? cloudCfg.apiKey ?? cloudCfg.token, selected.instanceSlug);
|
|
2735
2736
|
});
|
|
2736
2737
|
return;
|
|
2737
2738
|
}
|
|
2738
2739
|
// No instances found — try saved URL or show error
|
|
2739
2740
|
if (cloudCfg.instanceUrl) {
|
|
2740
2741
|
const restUrl = deriveRestUrl(cloudCfg.instanceUrl);
|
|
2741
|
-
await connectToDriver("cloud", restUrl, restUrl, cloudCfg.apiKey ?? cloudCfg.token, cloudCfg.instanceSlug);
|
|
2742
|
+
await connectToDriver("cloud", restUrl, restUrl, cloudCfg.userToken ?? cloudCfg.apiKey ?? cloudCfg.token, cloudCfg.instanceSlug);
|
|
2742
2743
|
return;
|
|
2743
2744
|
}
|
|
2744
2745
|
viewerLines = [
|
|
@@ -2751,7 +2752,7 @@ async function handleConnect(node) {
|
|
|
2751
2752
|
catch {
|
|
2752
2753
|
if (cloudCfg.instanceUrl) {
|
|
2753
2754
|
const restUrl = deriveRestUrl(cloudCfg.instanceUrl);
|
|
2754
|
-
await connectToDriver("cloud", restUrl, restUrl, cloudCfg.apiKey ?? cloudCfg.token, cloudCfg.instanceSlug);
|
|
2755
|
+
await connectToDriver("cloud", restUrl, restUrl, cloudCfg.userToken ?? cloudCfg.apiKey ?? cloudCfg.token, cloudCfg.instanceSlug);
|
|
2755
2756
|
return;
|
|
2756
2757
|
}
|
|
2757
2758
|
viewerLines = [
|
|
@@ -2967,12 +2968,12 @@ export async function runInteractiveCli() {
|
|
|
2967
2968
|
}
|
|
2968
2969
|
// Auto-connect to cloud if credentials exist
|
|
2969
2970
|
const cloudCfg = readCloudConfig();
|
|
2970
|
-
if (cloudCfg?.token) {
|
|
2971
|
+
if (cloudCfg?.userToken ?? cloudCfg?.token) {
|
|
2971
2972
|
const cloudUrl = resolveCloudUrl(cloudCfg);
|
|
2972
2973
|
if (cloudUrl) {
|
|
2973
2974
|
try {
|
|
2974
2975
|
const restUrl = deriveRestUrl(cloudUrl);
|
|
2975
|
-
await connectToDriver("cloud", restUrl, restUrl, cloudCfg.apiKey ?? cloudCfg.token, cloudCfg.instanceSlug);
|
|
2976
|
+
await connectToDriver("cloud", restUrl, restUrl, cloudCfg.userToken ?? cloudCfg.apiKey ?? cloudCfg.token, cloudCfg.instanceSlug);
|
|
2976
2977
|
}
|
|
2977
2978
|
catch (err) {
|
|
2978
2979
|
const msg = err instanceof Error ? err.message : String(err);
|
package/dist/lib/cloud-api.d.ts
CHANGED
|
@@ -19,6 +19,15 @@ export type CloudApiKey = {
|
|
|
19
19
|
token?: string;
|
|
20
20
|
createdAt: string;
|
|
21
21
|
};
|
|
22
|
+
export type UserTokenDto = {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
prefix: string;
|
|
26
|
+
projectAccess: string;
|
|
27
|
+
createdAt: string;
|
|
28
|
+
lastUsedAt: string | null;
|
|
29
|
+
revokedAt: string | null;
|
|
30
|
+
};
|
|
22
31
|
export type CloudOrganization = {
|
|
23
32
|
id: string;
|
|
24
33
|
name: string;
|
|
@@ -105,6 +114,27 @@ export declare function deriveRestUrl(mcpUrl: string): string;
|
|
|
105
114
|
* Returns null if no projects or instances exist.
|
|
106
115
|
*/
|
|
107
116
|
export declare function resolveFirstInstance(config: CloudConfig): Promise<ResolvedInstance | null>;
|
|
117
|
+
export declare function createUserToken(config: CloudConfig, name: string, projectAccess?: string): Promise<{
|
|
118
|
+
token: string;
|
|
119
|
+
userToken: UserTokenDto;
|
|
120
|
+
}>;
|
|
121
|
+
export declare function listUserTokens(config: CloudConfig): Promise<{
|
|
122
|
+
userTokens: UserTokenDto[];
|
|
123
|
+
}>;
|
|
124
|
+
export declare function revokeUserToken(config: CloudConfig, tokenId: string): Promise<void>;
|
|
125
|
+
export declare function updateUserToken(config: CloudConfig, tokenId: string, updates: {
|
|
126
|
+
name?: string;
|
|
127
|
+
projectAccess?: string;
|
|
128
|
+
}): Promise<{
|
|
129
|
+
userToken: UserTokenDto;
|
|
130
|
+
}>;
|
|
131
|
+
/**
|
|
132
|
+
* Extract the token ID from a full user token string (md_user_<hexId>_<secret> → utk_<hexId>).
|
|
133
|
+
*/
|
|
134
|
+
export declare function cleanupSessions(config: CloudConfig): Promise<{
|
|
135
|
+
removed: number;
|
|
136
|
+
}>;
|
|
137
|
+
export declare function parseUserTokenId(userToken: string): string | null;
|
|
108
138
|
/**
|
|
109
139
|
* Fetch all cloud instances across all projects for the logged-in user.
|
|
110
140
|
* Returns an empty array if no projects or instances exist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-api.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AASrD,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,qBAAa,aAAc,SAAQ,KAAK;IACtC,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAI1C;CACF;
|
|
1
|
+
{"version":3,"file":"cloud-api.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-api.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/cloud-api.js
CHANGED
|
@@ -7,10 +7,14 @@ export class CloudApiError extends Error {
|
|
|
7
7
|
this.name = "CloudApiError";
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
+
function resolveAuthToken(config) {
|
|
11
|
+
return config.userToken ?? config.token ?? config.apiKey ?? "";
|
|
12
|
+
}
|
|
10
13
|
async function request(config, path, opts = {}) {
|
|
11
14
|
const url = `${config.url ?? DEFAULT_API_URL}${path}`;
|
|
15
|
+
const authToken = resolveAuthToken(config);
|
|
12
16
|
const headers = {
|
|
13
|
-
authorization: `Bearer ${
|
|
17
|
+
authorization: `Bearer ${authToken}`,
|
|
14
18
|
"content-type": "application/json",
|
|
15
19
|
};
|
|
16
20
|
const res = await fetch(url, {
|
|
@@ -137,6 +141,38 @@ export async function resolveFirstInstance(config) {
|
|
|
137
141
|
}
|
|
138
142
|
return null;
|
|
139
143
|
}
|
|
144
|
+
// ── User Token API ──────────────────────────────────────────────────
|
|
145
|
+
export async function createUserToken(config, name, projectAccess) {
|
|
146
|
+
return request(config, "/auth/user-tokens", {
|
|
147
|
+
method: "POST",
|
|
148
|
+
body: { name, projectAccess },
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export async function listUserTokens(config) {
|
|
152
|
+
return request(config, "/auth/user-tokens");
|
|
153
|
+
}
|
|
154
|
+
export async function revokeUserToken(config, tokenId) {
|
|
155
|
+
await request(config, `/auth/user-tokens/${tokenId}`, { method: "DELETE" });
|
|
156
|
+
}
|
|
157
|
+
export async function updateUserToken(config, tokenId, updates) {
|
|
158
|
+
return request(config, `/auth/user-tokens/${tokenId}`, {
|
|
159
|
+
method: "PATCH",
|
|
160
|
+
body: updates,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Extract the token ID from a full user token string (md_user_<hexId>_<secret> → utk_<hexId>).
|
|
165
|
+
*/
|
|
166
|
+
export async function cleanupSessions(config) {
|
|
167
|
+
return request(config, "/auth/sessions/cleanup", { method: "DELETE" });
|
|
168
|
+
}
|
|
169
|
+
export function parseUserTokenId(userToken) {
|
|
170
|
+
const match = /^md_user_([a-f0-9]{20})_/.exec(userToken);
|
|
171
|
+
if (!match?.[1]) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return `utk_${match[1]}`;
|
|
175
|
+
}
|
|
140
176
|
/**
|
|
141
177
|
* Fetch all cloud instances across all projects for the logged-in user.
|
|
142
178
|
* Returns an empty array if no projects or instances exist.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export type CloudConfig = {
|
|
2
|
-
token
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/** Primary credential — user token (md_user_*) created on login. */
|
|
3
|
+
userToken?: string;
|
|
4
|
+
/** Email of the logged-in user. */
|
|
5
5
|
email?: string;
|
|
6
|
+
/** API base URL (defaults to https://api.thingd.cloud). */
|
|
6
7
|
url?: string;
|
|
7
8
|
/** Currently active organization context (set by `thingd cloud org use`). */
|
|
8
9
|
organizationId?: string;
|
|
@@ -14,6 +15,10 @@ export type CloudConfig = {
|
|
|
14
15
|
projectSlug?: string;
|
|
15
16
|
/** Active instance slug (set when instanceUrl is resolved). */
|
|
16
17
|
instanceSlug?: string;
|
|
18
|
+
/** @deprecated Old JWT — still read as fallback if no userToken. */
|
|
19
|
+
token?: string;
|
|
20
|
+
/** @deprecated Old project API key — still read as fallback if no userToken or token. */
|
|
21
|
+
apiKey?: string;
|
|
17
22
|
};
|
|
18
23
|
export declare function cloudConfigPath(): string;
|
|
19
24
|
export declare function readCloudConfig(): CloudConfig | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-config.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-config.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG;IACxB,
|
|
1
|
+
{"version":3,"file":"cloud-config.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-config.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG;IACxB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8GAA8G;IAC9G,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAUpD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAKvE;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thingd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.61.0",
|
|
4
4
|
"description": "CLI, Interactive TUI Dashboard, and MCP server for thingd — a fast object-first data engine for applications and AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://engine.thingd.cloud",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"cli-table3": "^0.6.5",
|
|
46
46
|
"picocolors": "^1.1.1",
|
|
47
47
|
"zod": "^4.4.3",
|
|
48
|
-
"@thingd/sdk": "0.
|
|
48
|
+
"@thingd/sdk": "0.61.0"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=24.0.0"
|