@thingd/cli 0.52.10 → 0.53.1

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.
@@ -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;AAk6BD,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,CA6CxE;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"}
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;AAk6BD,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,CAqDxE;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
@@ -959,6 +959,10 @@ export function resolveConnection(context) {
959
959
  }
960
960
  // Fall back to saved cloud config when no explicit URL/token is provided
961
961
  const cloudCfg = cloud && !url ? readCloudConfig() : null;
962
+ if (driver === "cloud" && !url && !cloudCfg) {
963
+ context.stderr.write(`${pc.red("Not logged in.")} Run ${pc.cyan("thingd cloud login")} first, or use ${pc.cyan("--url")} and ${pc.cyan("--auth-token")} to connect directly.\n`);
964
+ throw new Error("not_logged_in");
965
+ }
962
966
  const resolvedAuthToken = stringFlag(context.parsed, "auth-token") ??
963
967
  context.env.THINGD_AUTH_TOKEN ??
964
968
  cloudCfg?.apiKey ??
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AAwmEA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA+DvD"}
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AAolEA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA0FvD"}
@@ -92,6 +92,7 @@ let viewerScroll = 0;
92
92
  let loadedItemId = "";
93
93
  let loadTimer = null;
94
94
  let pollTimer = null;
95
+ let polling = false;
95
96
  let keypressHandler = null;
96
97
  let formState = null;
97
98
  function openForm(title, fields, onSubmit, keepViewer) {
@@ -198,75 +199,47 @@ function formatUptime(ms) {
198
199
  }
199
200
  async function fetchResourcesFallback() {
200
201
  cloudError = null;
202
+ // Collections and streams — parallel fetch
203
+ let nativeCollections;
204
+ let nativeStreams;
201
205
  try {
202
- const nativeCollections = await db.listCollections();
203
- const nativeStreams = await db.listStreams();
204
- // Maintain default collections/streams for UI if none exist
205
- const defaultCols = new Set();
206
- const defaultStrs = new Set();
207
- for (const c of nativeCollections) {
208
- defaultCols.add(c);
209
- }
210
- for (const s of nativeStreams) {
211
- defaultStrs.add(s);
212
- }
213
- collections = Array.from(defaultCols).sort();
214
- streams = Array.from(defaultStrs).sort();
206
+ [nativeCollections, nativeStreams] = await Promise.all([
207
+ db.listCollections(),
208
+ db.listStreams(),
209
+ ]);
215
210
  }
216
211
  catch (err) {
217
212
  const msg = err instanceof Error ? err.message : String(err);
218
213
  cloudError = `Failed to load resources: ${msg}`;
219
- collections = [];
220
- streams = [];
214
+ nativeCollections = [];
215
+ nativeStreams = [];
221
216
  if (viewerLines.length === 1 && viewerLines[0] === "Select an item to view details.") {
222
217
  viewerLines = [pc.yellow(cloudError), pc.dim("Press 'r' to retry or 's' to switch driver.")];
223
218
  }
224
219
  }
225
- // Counts independent try/catch so one failure doesn't clear collections
226
- try {
227
- totalObjects = await db.countObjects();
228
- }
229
- catch {
230
- totalObjects = 0;
231
- }
232
- try {
233
- totalEventsCount = await db.countEvents();
234
- }
235
- catch {
236
- totalEventsCount = 0;
237
- }
238
- try {
239
- totalActiveJobsCount = await db.countActiveJobs();
240
- }
241
- catch {
242
- totalActiveJobsCount = 0;
243
- }
244
- try {
245
- totalDeadJobsCount = await db.countDeadJobs();
246
- }
247
- catch {
248
- totalDeadJobsCount = 0;
249
- }
250
- // Queues
251
- try {
252
- const listed = await db.listQueues();
253
- queues = (listed ?? []).sort();
254
- }
255
- catch (err) {
256
- const msg = err instanceof Error ? err.message : String(err);
257
- cloudError = cloudError ?? `Failed to load queues: ${msg}`;
258
- queues = [];
259
- }
260
- // Link count
261
- try {
262
- totalLinksCount = await db.countLinks();
263
- }
264
- catch {
265
- totalLinksCount = 0;
266
- }
267
- // Populate objectsByCollection from live data
220
+ collections = [...new Set(nativeCollections)].sort();
221
+ streams = [...new Set(nativeStreams)].sort();
222
+ // Counts, queues, links — all parallel, each with independent error handling
223
+ const [objCount, evtCount, activeCount, deadCount, listedQueues, linkCount] = await Promise.all([
224
+ db.countObjects().catch(() => 0),
225
+ db.countEvents().catch(() => 0),
226
+ db.countActiveJobs().catch(() => 0),
227
+ db.countDeadJobs().catch(() => 0),
228
+ db.listQueues().catch(() => {
229
+ cloudError = cloudError ?? "Failed to load queues";
230
+ return [];
231
+ }),
232
+ db.countLinks().catch(() => 0),
233
+ ]);
234
+ totalObjects = objCount;
235
+ totalEventsCount = evtCount;
236
+ totalActiveJobsCount = activeCount;
237
+ totalDeadJobsCount = deadCount;
238
+ queues = [...new Set(listedQueues ?? [])].sort();
239
+ totalLinksCount = linkCount;
240
+ // Objects per collection — parallel
268
241
  objectsByCollection.clear();
269
- for (const col of collections) {
242
+ await Promise.all(collections.map(async (col) => {
270
243
  try {
271
244
  const list = await db.listObjects(col);
272
245
  objectsByCollection.set(col, list.map((o) => o.id));
@@ -274,7 +247,7 @@ async function fetchResourcesFallback() {
274
247
  catch {
275
248
  objectsByCollection.set(col, []);
276
249
  }
277
- }
250
+ }));
278
251
  }
279
252
  async function fetchResources() {
280
253
  if (driver === "native" && dbPath) {
@@ -327,9 +300,8 @@ async function fetchResources() {
327
300
  const prevEvents = eventsHistory.length > 0
328
301
  ? (eventsHistory[eventsHistory.length - 1] ?? totalEventsCount)
329
302
  : totalEventsCount;
330
- // Polling is every 2000ms. Operations per second = delta / 2
331
- const objectWriteRate = Math.max(0, Math.round((totalObjects - prevObjects) / 2));
332
- const eventAppendRate = Math.max(0, Math.round((totalEventsCount - prevEvents) / 2));
303
+ const objectWriteRate = Math.max(0, Math.round((totalObjects - prevObjects) / 10));
304
+ const eventAppendRate = Math.max(0, Math.round((totalEventsCount - prevEvents) / 10));
333
305
  // Push Histories with Initial Pre-population to prevent misleading growth wiggles
334
306
  if (objectsHistory.length === 0) {
335
307
  objectsHistory = new Array(60).fill(totalObjects);
@@ -1761,7 +1733,9 @@ async function handleConnect(node) {
1761
1733
  const cloudCfg = selectedDriver === "cloud" ? readCloudConfig() : null;
1762
1734
  const baseUrl = cloudCfg?.url ?? "https://api.thingd.cloud";
1763
1735
  const isCloudWithConfig = selectedDriver === "cloud" && cloudCfg?.token;
1764
- openForm(`Connect to ${selectedDriver}`, [
1736
+ openForm(selectedDriver === "cloud" && !isCloudWithConfig
1737
+ ? `Connect to ${selectedDriver} — enter credentials below or run ${pc.cyan("thingd cloud login")} first`
1738
+ : `Connect to ${selectedDriver}`, [
1765
1739
  ...(selectedDriver === "cloud"
1766
1740
  ? isCloudWithConfig
1767
1741
  ? [
@@ -1974,24 +1948,52 @@ export async function runInteractiveCli() {
1974
1948
  }
1975
1949
  }
1976
1950
  }
1951
+ else {
1952
+ viewerLines = [
1953
+ pc.cyan("Not logged in to thingd Cloud."),
1954
+ pc.dim("Press 'c' to connect to a local database, or run thingd cloud login first."),
1955
+ ];
1956
+ draw();
1957
+ }
1977
1958
  setupKeypress();
1978
1959
  // Background polling loop for real-time updates
1979
1960
  pollTimer = setInterval(async () => {
1980
- if (connected && !formState?.active) {
1981
- try {
1982
- const snapItemId = loadedItemId;
1983
- await fetchResources();
1984
- draw();
1985
- const tree = buildTree();
1986
- const n = tree[cursorIndex];
1987
- // Silently reload content if the same node is still actively viewed
1988
- if (n && snapItemId === n.id && n.type !== "category") {
1989
- await loadContent(n).catch(() => { });
1990
- }
1961
+ if (!connected || formState?.active || polling) {
1962
+ return;
1963
+ }
1964
+ polling = true;
1965
+ try {
1966
+ const snapItemId = loadedItemId;
1967
+ const snapshot = JSON.stringify([
1968
+ totalObjects,
1969
+ totalEventsCount,
1970
+ totalActiveJobsCount,
1971
+ totalDeadJobsCount,
1972
+ totalLinksCount,
1973
+ ]);
1974
+ await fetchResources();
1975
+ const changed = snapshot !==
1976
+ JSON.stringify([
1977
+ totalObjects,
1978
+ totalEventsCount,
1979
+ totalActiveJobsCount,
1980
+ totalDeadJobsCount,
1981
+ totalLinksCount,
1982
+ ]);
1983
+ const tree = buildTree();
1984
+ const n = tree[cursorIndex];
1985
+ if (n && snapItemId === n.id && n.type !== "category") {
1986
+ await loadContent(n).catch(() => { });
1991
1987
  }
1992
- catch {
1993
- // Prevent unhandled rejection from killing the process
1988
+ if (changed) {
1989
+ draw();
1994
1990
  }
1995
1991
  }
1992
+ catch {
1993
+ // Prevent unhandled rejection from killing the process
1994
+ }
1995
+ finally {
1996
+ polling = false;
1997
+ }
1996
1998
  }, 10_000);
1997
1999
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thingd/cli",
3
- "version": "0.52.10",
3
+ "version": "0.53.1",
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.52.10"
48
+ "@thingd/sdk": "0.53.1"
49
49
  },
50
50
  "engines": {
51
51
  "node": ">=24.0.0"