@erdoai/cli 0.4.0 → 0.5.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/index.js CHANGED
@@ -293,26 +293,32 @@ var ErdoClient = class {
293
293
  runHeartbeat(id) {
294
294
  return this.request("POST", `/v1/heartbeats/${encodeURIComponent(id)}/run`, {});
295
295
  }
296
- // --- collections ---
297
- listCollections() {
298
- return this.request("GET", "/v1/collections");
296
+ // --- kv (named key/value stores, a.k.a. collections) ---
297
+ listKVStores() {
298
+ return this.request("GET", "/v1/kv");
299
299
  }
300
- createCollection(slug) {
301
- return this.request("POST", "/v1/collections", { slug });
300
+ createKVStore(slug) {
301
+ return this.request("POST", "/v1/kv", { slug });
302
302
  }
303
- getCollectionItem(slug, key) {
303
+ getKVItem(slug, key) {
304
304
  return this.request(
305
305
  "GET",
306
- `/v1/collections/${encodeURIComponent(slug)}/items/${encodeURIComponent(key)}`
306
+ `/v1/kv/${encodeURIComponent(slug)}/items/${encodeURIComponent(key)}`
307
307
  );
308
308
  }
309
- setCollectionItem(slug, key, value) {
309
+ setKVItem(slug, key, value) {
310
310
  return this.request(
311
311
  "PUT",
312
- `/v1/collections/${encodeURIComponent(slug)}/items/${encodeURIComponent(key)}`,
312
+ `/v1/kv/${encodeURIComponent(slug)}/items/${encodeURIComponent(key)}`,
313
313
  { value }
314
314
  );
315
315
  }
316
+ deleteKVItem(slug, key) {
317
+ return this.request(
318
+ "DELETE",
319
+ `/v1/kv/${encodeURIComponent(slug)}/items/${encodeURIComponent(key)}`
320
+ );
321
+ }
316
322
  // --- agent runs ---
317
323
  listAgentRuns(opts) {
318
324
  const params = new URLSearchParams();
@@ -438,19 +444,30 @@ async function browserLogin() {
438
444
  import { spawnSync } from "child_process";
439
445
  var PKG = "@erdoai/cli";
440
446
  var MANUAL_HINT = `npm install -g ${PKG}@latest`;
447
+ function parseVersion(v) {
448
+ const [core, pre = ""] = v.split("-", 2);
449
+ const nums = core.split(".").map((s) => Number.parseInt(s, 10) || 0);
450
+ return { nums, pre };
451
+ }
441
452
  function isNewer(latest, current) {
442
- const a = latest.split(".").map(Number);
443
- const b = current.split(".").map(Number);
453
+ const a = parseVersion(latest);
454
+ const b = parseVersion(current);
444
455
  for (let i = 0; i < 3; i++) {
445
- if ((a[i] ?? 0) > (b[i] ?? 0)) return true;
446
- if ((a[i] ?? 0) < (b[i] ?? 0)) return false;
447
- }
448
- return false;
456
+ const x = a.nums[i] ?? 0;
457
+ const y = b.nums[i] ?? 0;
458
+ if (x > y) return true;
459
+ if (x < y) return false;
460
+ }
461
+ if (!a.pre && b.pre) return true;
462
+ if (a.pre && !b.pre) return false;
463
+ return a.pre > b.pre;
449
464
  }
450
465
  async function update(current) {
451
466
  let latest;
452
467
  try {
453
- const res = await fetch(`https://registry.npmjs.org/${PKG}/latest`);
468
+ const res = await fetch(`https://registry.npmjs.org/${PKG}/latest`, {
469
+ signal: AbortSignal.timeout(1e4)
470
+ });
454
471
  if (res.ok) latest = (await res.json()).version;
455
472
  } catch {
456
473
  }
@@ -468,6 +485,10 @@ async function update(current) {
468
485
  // npm is npm.cmd on Windows; needs a shell to resolve.
469
486
  shell: process.platform === "win32"
470
487
  });
488
+ if (result.error) {
489
+ console.error(`Update failed: ${result.error.message}. Try manually: ${MANUAL_HINT}`);
490
+ process.exit(1);
491
+ }
471
492
  if (result.status !== 0) {
472
493
  console.error(`Update failed. Try manually: ${MANUAL_HINT}`);
473
494
  process.exit(result.status ?? 1);
@@ -945,7 +966,7 @@ function readMaybeFile(v) {
945
966
  if (!v) return void 0;
946
967
  return v.startsWith("@") ? readFileSync2(v.slice(1), "utf8") : v;
947
968
  }
948
- pagesCmd.command("deploy").description("Deploy a page (HTML/React); --html/--js/--css accept @file").requiredOption("--title <title>", "page title").requiredOption("--html <htmlOr@file>", "HTML (or @path to a file)").option("--js <jsOr@file>", "JS/JSX (or @path)").option("--css <cssOr@file>", "CSS (or @path)").option("--runtime <runtime>", "react-tailwind (default) or none").option("--datasets <csv>", "dataset slugs the page reads").option("--writable-datasets <csv>", "dataset slugs the page may write via erdo.insertRows").option("--collections <csv>", "named collection slugs the page reads").option("--writable-collections <csv>", "named collection slugs the page may write").option("--public", "make the page publicly viewable").action(
969
+ pagesCmd.command("deploy").description("Deploy a page (HTML/React); --html/--js/--css accept @file").requiredOption("--title <title>", "page title").requiredOption("--html <htmlOr@file>", "HTML (or @path to a file)").option("--js <jsOr@file>", "JS/JSX (or @path)").option("--css <cssOr@file>", "CSS (or @path)").option("--runtime <runtime>", "react-tailwind (default) or none").option("--datasets <csv>", "dataset slugs the page reads").option("--writable-datasets <csv>", "dataset slugs the page may write via erdo.insertRows").option("--kv <csv>", "named KV-store slugs the page reads").option("--writable-kv <csv>", "named KV-store slugs the page may write").option("--public", "make the page publicly viewable").action(
949
970
  async (opts) => {
950
971
  try {
951
972
  const csv = (v) => v ? v.split(",").map((s) => s.trim()) : void 0;
@@ -957,8 +978,8 @@ pagesCmd.command("deploy").description("Deploy a page (HTML/React); --html/--js/
957
978
  runtime: opts.runtime,
958
979
  dataset_slugs: csv(opts.datasets),
959
980
  writable_dataset_slugs: csv(opts.writableDatasets),
960
- collection_slugs: csv(opts.collections),
961
- writable_collection_slugs: csv(opts.writableCollections),
981
+ kv_slugs: csv(opts.kv),
982
+ writable_kv_slugs: csv(opts.writableKv),
962
983
  public: !!opts.public
963
984
  });
964
985
  console.log(`${res.id} ${res.public_url || res.url}`);
@@ -1044,29 +1065,29 @@ autoCmd.command("run <id>").description("Trigger an automation now").action(asyn
1044
1065
  fail(e);
1045
1066
  }
1046
1067
  });
1047
- var collCmd = program.command("collections").description("Named key/value collections");
1048
- collCmd.command("list").description("List collections").action(async () => {
1068
+ var kvCmd = program.command("kv").description("Named key/value stores");
1069
+ kvCmd.command("list").description("List KV stores").action(async () => {
1049
1070
  try {
1050
- print(await new ErdoClient().listCollections());
1071
+ print(await new ErdoClient().listKVStores());
1051
1072
  } catch (e) {
1052
1073
  fail(e);
1053
1074
  }
1054
1075
  });
1055
- collCmd.command("create <slug>").description("Create a named collection (slug: lowercase letters, digits, hyphens)").action(async (slug) => {
1076
+ kvCmd.command("create <slug>").description("Create a named KV store (slug: lowercase letters, digits, hyphens)").action(async (slug) => {
1056
1077
  try {
1057
- print(await new ErdoClient().createCollection(slug));
1078
+ print(await new ErdoClient().createKVStore(slug));
1058
1079
  } catch (e) {
1059
1080
  fail(e);
1060
1081
  }
1061
1082
  });
1062
- collCmd.command("get <slug> <key>").description("Get a collection item").action(async (slug, key) => {
1083
+ kvCmd.command("get <slug> <key>").description("Get a KV item").action(async (slug, key) => {
1063
1084
  try {
1064
- print(await new ErdoClient().getCollectionItem(slug, key));
1085
+ print(await new ErdoClient().getKVItem(slug, key));
1065
1086
  } catch (e) {
1066
1087
  fail(e);
1067
1088
  }
1068
1089
  });
1069
- collCmd.command("set <slug> <key> <jsonValue>").description("Set a collection item (value is parsed as JSON, else stored as string)").action(async (slug, key, jsonValue) => {
1090
+ kvCmd.command("set <slug> <key> <jsonValue>").description("Set a KV item (value is parsed as JSON, else stored as string)").action(async (slug, key, jsonValue) => {
1070
1091
  try {
1071
1092
  let value;
1072
1093
  try {
@@ -1074,7 +1095,14 @@ collCmd.command("set <slug> <key> <jsonValue>").description("Set a collection it
1074
1095
  } catch {
1075
1096
  value = jsonValue;
1076
1097
  }
1077
- print(await new ErdoClient().setCollectionItem(slug, key, value));
1098
+ print(await new ErdoClient().setKVItem(slug, key, value));
1099
+ } catch (e) {
1100
+ fail(e);
1101
+ }
1102
+ });
1103
+ kvCmd.command("delete <slug> <key>").description("Delete a KV item").action(async (slug, key) => {
1104
+ try {
1105
+ print(await new ErdoClient().deleteKVItem(slug, key));
1078
1106
  } catch (e) {
1079
1107
  fail(e);
1080
1108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,19 +1,39 @@
1
1
  // Friendly getting-started hint, printed once after `npm install -g @erdoai/cli`.
2
2
  // Stays quiet in CI and non-interactive installs (and never fails the install).
3
- import { existsSync } from "node:fs";
3
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
4
6
 
5
7
  try {
6
8
  // In our own source checkout the package ships `dist` + this script but never
7
9
  // `src`, so a sibling `src/` means this is a dev `npm install` — stay quiet.
8
10
  const devCheckout = existsSync(new URL("../src", import.meta.url));
9
11
 
12
+ // Print once per machine: a sentinel under the CLI's config dir (same dir as
13
+ // config.json) suppresses the banner on later installs — notably the global
14
+ // reinstall `erdo update` performs, which would otherwise re-trigger it.
15
+ const configDir = join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "erdo");
16
+ const sentinel = join(configDir, ".welcomed");
17
+
10
18
  const quiet =
11
19
  devCheckout ||
20
+ // Only greet on a global install — a local/devDependency install must not
21
+ // print the banner or write the sentinel (which would silence the real
22
+ // `npm install -g` welcome later). npm sets this to "true" for `-g`.
23
+ process.env.npm_config_global !== "true" ||
12
24
  process.env.CI ||
13
25
  process.env.ERDO_NO_POSTINSTALL ||
14
- !process.stdout.isTTY;
26
+ !process.stdout.isTTY ||
27
+ existsSync(sentinel);
15
28
 
16
29
  if (!quiet) {
30
+ try {
31
+ mkdirSync(configDir, { recursive: true });
32
+ writeFileSync(sentinel, "");
33
+ } catch {
34
+ // a read-only home shouldn't suppress the hint or fail the install
35
+ }
36
+
17
37
  const c = (code, s) => `\x1b[${code}m${s}\x1b[0m`;
18
38
  const b = (s) => c("1", s);
19
39
  const dim = (s) => c("2", s);