@alva-ai/toolkit 0.2.1 → 0.4.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/cli.js CHANGED
@@ -28,9 +28,20 @@ var FsResource = class {
28
28
  client;
29
29
  /** Returns `ArrayBuffer` for binary files, or parsed JSON for time-series virtual paths. */
30
30
  async read(params) {
31
- return this.client._request("GET", "/api/v1/fs/read", {
31
+ const result = await this.client._request("GET", "/api/v1/fs/read", {
32
32
  query: { path: params.path, offset: params.offset, size: params.size }
33
33
  });
34
+ if (!(result instanceof ArrayBuffer)) return result;
35
+ try {
36
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(result);
37
+ try {
38
+ return JSON.parse(text);
39
+ } catch {
40
+ return text;
41
+ }
42
+ } catch {
43
+ return result;
44
+ }
34
45
  }
35
46
  /** Write file using JSON body (Mode 2). For text content. */
36
47
  async write(params) {
@@ -241,7 +252,8 @@ var ReleaseResource = class {
241
252
  version: params.version,
242
253
  cronjob_id: params.cronjob_id,
243
254
  view_json: params.view_json,
244
- description: params.description
255
+ description: params.description,
256
+ changelog: params.changelog
245
257
  }
246
258
  });
247
259
  }
@@ -253,8 +265,7 @@ var ReleaseResource = class {
253
265
  display_name: params.display_name,
254
266
  description: params.description,
255
267
  feeds: params.feeds,
256
- trading_symbols: params.trading_symbols,
257
- changelog: params.changelog
268
+ trading_symbols: params.trading_symbols
258
269
  }
259
270
  });
260
271
  }
@@ -338,6 +349,55 @@ var SdkDocsResource = class {
338
349
  }
339
350
  };
340
351
 
352
+ // src/resources/skills.ts
353
+ var SkillsResource = class {
354
+ constructor(client) {
355
+ this.client = client;
356
+ }
357
+ client;
358
+ async list() {
359
+ const res = await this.client._request("GET", "/api/v1/skills", {
360
+ baseUrl: this.client.arraysBaseUrl,
361
+ noAuth: true
362
+ });
363
+ return { skills: res.data ?? [] };
364
+ }
365
+ async summary(params) {
366
+ const encoded = encodeURIComponent(params.name);
367
+ const res = await this.client._request(
368
+ "GET",
369
+ `/api/v1/skills/${encoded}`,
370
+ {
371
+ baseUrl: this.client.arraysBaseUrl,
372
+ noAuth: true
373
+ }
374
+ );
375
+ const doc = res.data?.[0];
376
+ if (!doc)
377
+ throw new Error(`empty skills summary response for "${params.name}"`);
378
+ return doc;
379
+ }
380
+ async endpoint(params) {
381
+ const encoded = encodeURIComponent(params.name);
382
+ const res = await this.client._request(
383
+ "GET",
384
+ `/api/v1/skills/${encoded}`,
385
+ {
386
+ baseUrl: this.client.arraysBaseUrl,
387
+ noAuth: true,
388
+ query: { endpoint: params.file }
389
+ }
390
+ );
391
+ const doc = res.data?.[0];
392
+ if (!doc) {
393
+ throw new Error(
394
+ `empty skills endpoint response for "${params.name}" file "${params.file}"`
395
+ );
396
+ }
397
+ return doc;
398
+ }
399
+ };
400
+
341
401
  // src/resources/comments.ts
342
402
  var CommentsResource = class {
343
403
  constructor(client) {
@@ -493,10 +553,130 @@ var TradingResource = class {
493
553
  }
494
554
  };
495
555
 
556
+ // src/resources/arraysJwt.ts
557
+ var ArraysJwtResource = class {
558
+ constructor(client) {
559
+ this.client = client;
560
+ }
561
+ client;
562
+ /** Idempotently sign-or-renew the Arrays JWT server-side. */
563
+ async ensure() {
564
+ this.client._requireAuth();
565
+ return this.client._request(
566
+ "POST",
567
+ "/api/v1/arrays-jwt/ensure"
568
+ );
569
+ }
570
+ /** Report the current Arrays JWT state for the authenticated user. */
571
+ async status() {
572
+ this.client._requireAuth();
573
+ return this.client._request(
574
+ "GET",
575
+ "/api/v1/arrays-jwt/status"
576
+ );
577
+ }
578
+ };
579
+
580
+ // src/resources/notifications.ts
581
+ var NotificationsResource = class {
582
+ constructor(client) {
583
+ this.client = client;
584
+ }
585
+ client;
586
+ /**
587
+ * List the caller's notification history for one playbook
588
+ * `(username, name)`. Returns `NOT_FOUND` when the playbook is
589
+ * private or does not exist (the two cases are deliberately
590
+ * indistinguishable to prevent namespace enumeration).
591
+ */
592
+ async listPlaybook(params) {
593
+ this.client._requireAuth();
594
+ const path = `/api/v1/playbook/${encodeURIComponent(params.username)}/${encodeURIComponent(params.name)}/notifications`;
595
+ return this.client._request("GET", path, {
596
+ query: buildQuery(params)
597
+ });
598
+ }
599
+ /**
600
+ * List the caller's notification history for one feed
601
+ * `(username, name)`. Authorization is alfs read on
602
+ * `/alva/home/<username>/feeds/<name>`.
603
+ */
604
+ async listFeed(params) {
605
+ this.client._requireAuth();
606
+ const path = `/api/v1/feed/${encodeURIComponent(params.username)}/${encodeURIComponent(params.name)}/notifications`;
607
+ return this.client._request("GET", path, {
608
+ query: buildQuery(params)
609
+ });
610
+ }
611
+ };
612
+ function buildQuery(params) {
613
+ const q = {};
614
+ if (params.channel) q.channel = params.channel;
615
+ if (params.status) q.status = params.status;
616
+ if (params.since_time !== void 0 && params.since_time > 0) {
617
+ q.since_time = String(params.since_time);
618
+ }
619
+ if (params.first !== void 0 && params.first > 0) {
620
+ q.first = String(params.first);
621
+ }
622
+ if (params.cursor) q.cursor = params.cursor;
623
+ return q;
624
+ }
625
+
626
+ // src/resources/pushSubscriptions.ts
627
+ var PushSubscriptionsResource = class {
628
+ constructor(client) {
629
+ this.client = client;
630
+ }
631
+ client;
632
+ /**
633
+ * Opt into personal push for one playbook `(username, name)`.
634
+ * Idempotent. Auth: callers must be able to read the playbook
635
+ * (public/paid pass; private requires explicit alfs grant).
636
+ */
637
+ async subscribePlaybook(params) {
638
+ this.client._requireAuth();
639
+ const path = `/api/v1/playbook/${encodeURIComponent(params.username)}/${encodeURIComponent(params.name)}/push-subscription`;
640
+ return this.client._request(
641
+ "POST",
642
+ path
643
+ );
644
+ }
645
+ /**
646
+ * Soft-disable personal push for one playbook `(username, name)`.
647
+ * Does NOT remove any social follow. Idempotent.
648
+ */
649
+ async unsubscribePlaybook(params) {
650
+ this.client._requireAuth();
651
+ const path = `/api/v1/playbook/${encodeURIComponent(params.username)}/${encodeURIComponent(params.name)}/push-subscription`;
652
+ return this.client._request(
653
+ "DELETE",
654
+ path
655
+ );
656
+ }
657
+ /**
658
+ * List the caller's personal push subscriptions across all targets.
659
+ * Defaults to currently-active rows only; pass `include_history=true`
660
+ * to also return previously-unsubscribed rows.
661
+ */
662
+ async list(params = {}) {
663
+ this.client._requireAuth();
664
+ const query = {};
665
+ if (params.include_history !== void 0) {
666
+ query.include_history = String(params.include_history);
667
+ }
668
+ return this.client._request("GET", "/api/v1/me/push-subscriptions", {
669
+ query
670
+ });
671
+ }
672
+ };
673
+
496
674
  // src/client.ts
497
675
  var DEFAULT_BASE_URL = "https://api-llm.prd.alva.ai";
676
+ var DEFAULT_ARRAYS_BASE_URL = "https://data-tools.prd.space.id";
498
677
  var AlvaClient = class {
499
678
  baseUrl;
679
+ arraysBaseUrl;
500
680
  viewer_token;
501
681
  apiKey;
502
682
  _fs;
@@ -505,13 +685,18 @@ var AlvaClient = class {
505
685
  _release;
506
686
  _secrets;
507
687
  _sdk;
688
+ _skills;
508
689
  _comments;
509
690
  _remix;
510
691
  _screenshot;
511
692
  _user;
512
693
  _trading;
694
+ _arraysJwt;
695
+ _notifications;
696
+ _pushSubscriptions;
513
697
  constructor(config) {
514
698
  this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
699
+ this.arraysBaseUrl = config.arraysBaseUrl ?? DEFAULT_ARRAYS_BASE_URL;
515
700
  this.viewer_token = config.viewer_token;
516
701
  this.apiKey = config.apiKey;
517
702
  }
@@ -533,6 +718,9 @@ var AlvaClient = class {
533
718
  get sdk() {
534
719
  return this._sdk ??= new SdkDocsResource(this);
535
720
  }
721
+ get skills() {
722
+ return this._skills ??= new SkillsResource(this);
723
+ }
536
724
  get comments() {
537
725
  return this._comments ??= new CommentsResource(this);
538
726
  }
@@ -548,6 +736,15 @@ var AlvaClient = class {
548
736
  get trading() {
549
737
  return this._trading ??= new TradingResource(this);
550
738
  }
739
+ get arraysJwt() {
740
+ return this._arraysJwt ??= new ArraysJwtResource(this);
741
+ }
742
+ get notifications() {
743
+ return this._notifications ??= new NotificationsResource(this);
744
+ }
745
+ get pushSubscriptions() {
746
+ return this._pushSubscriptions ??= new PushSubscriptionsResource(this);
747
+ }
551
748
  _requireAuth() {
552
749
  if (!this.viewer_token && !this.apiKey) {
553
750
  throw new AlvaError(
@@ -558,7 +755,8 @@ var AlvaClient = class {
558
755
  }
559
756
  }
560
757
  async _request(method, path, options) {
561
- let url = `${this.baseUrl}${path}`;
758
+ const baseUrl = options?.baseUrl ?? this.baseUrl;
759
+ let url = `${baseUrl}${path}`;
562
760
  if (options?.query) {
563
761
  const params = new URLSearchParams();
564
762
  for (const [key, value] of Object.entries(options.query)) {
@@ -572,10 +770,12 @@ var AlvaClient = class {
572
770
  }
573
771
  }
574
772
  const headers = {};
575
- if (this.viewer_token) {
576
- headers["x-Playbook-Viewer"] = this.viewer_token;
577
- } else if (this.apiKey) {
578
- headers["X-Alva-Api-Key"] = this.apiKey;
773
+ if (!options?.noAuth) {
774
+ if (this.viewer_token) {
775
+ headers["x-Playbook-Viewer"] = this.viewer_token;
776
+ } else if (this.apiKey) {
777
+ headers["X-Alva-Api-Key"] = this.apiKey;
778
+ }
579
779
  }
580
780
  let fetchBody;
581
781
  if (options?.rawBody !== void 0) {
@@ -691,6 +891,7 @@ async function writeConfig(config, deps, profileName = "default") {
691
891
  return {
692
892
  apiKey: profileData.apiKey,
693
893
  baseUrl: profileData.baseUrl,
894
+ arraysBaseUrl: DEFAULT_ARRAYS_BASE_URL,
694
895
  profile: profileName
695
896
  };
696
897
  }
@@ -710,6 +911,8 @@ function loadConfig(deps) {
710
911
  const profileName = parseFlag(argv, "--profile") || env.ALVA_PROFILE || "default";
711
912
  const baseUrlFlag = parseFlag(argv, "--base-url");
712
913
  const baseUrlEnv = env.ALVA_ENDPOINT;
914
+ const arraysBaseUrlFlag = parseFlag(argv, "--arrays-endpoint");
915
+ const arraysBaseUrlEnv = env.ARRAYS_ENDPOINT;
713
916
  const apiKeyFlag = parseFlag(argv, "--api-key");
714
917
  const apiKeyEnv = env.ALVA_API_KEY;
715
918
  let fileProfile = {};
@@ -731,6 +934,7 @@ function loadConfig(deps) {
731
934
  return {
732
935
  apiKey: apiKeyFlag ?? apiKeyEnv ?? fileProfile.apiKey,
733
936
  baseUrl: baseUrlFlag ?? baseUrlEnv ?? fileProfile.baseUrl,
937
+ arraysBaseUrl: arraysBaseUrlFlag ?? arraysBaseUrlEnv ?? DEFAULT_ARRAYS_BASE_URL,
734
938
  profile: profileName
735
939
  };
736
940
  }
@@ -859,11 +1063,48 @@ Waiting for login callback...
859
1063
  });
860
1064
  }
861
1065
 
1066
+ // src/cli/postConfigureHooks.ts
1067
+ function defaultStderr(s) {
1068
+ process.stderr.write(s);
1069
+ }
1070
+ function formatTier(t) {
1071
+ const stripped = typeof t === "string" && t.startsWith("SUBSCRIPTION_TIER_") ? t.slice("SUBSCRIPTION_TIER_".length) : String(t);
1072
+ return stripped.toLowerCase();
1073
+ }
1074
+ function formatExpiry(expiresAt) {
1075
+ return new Date(expiresAt * 1e3).toISOString().slice(0, 10);
1076
+ }
1077
+ var ensureArraysJwtHook = {
1078
+ name: "ensureArraysJwt",
1079
+ async run(client) {
1080
+ const res = await client.arraysJwt.ensure();
1081
+ const date = formatExpiry(res.expires_at);
1082
+ const tier = formatTier(res.tier);
1083
+ const verb = res.renewed ? "Arrays JWT provisioned" : "Arrays JWT already current";
1084
+ process.stderr.write(`${verb} (expires ${date}, tier: ${tier})
1085
+ `);
1086
+ }
1087
+ };
1088
+ var POST_CONFIGURE_HOOKS = [ensureArraysJwtHook];
1089
+ async function runPostConfigureHooks(client, deps) {
1090
+ const hooks = deps?.hooks ?? POST_CONFIGURE_HOOKS;
1091
+ const stderr = deps?.stderr ?? defaultStderr;
1092
+ for (const hook of hooks) {
1093
+ try {
1094
+ await hook.run(client);
1095
+ } catch (err) {
1096
+ const msg = err instanceof Error ? err.message : String(err);
1097
+ stderr(`warning: post-configure hook "${hook.name}" failed: ${msg}
1098
+ `);
1099
+ }
1100
+ }
1101
+ }
1102
+
862
1103
  // src/cli/index.ts
863
1104
  import * as fs from "fs";
864
1105
  import * as os2 from "os";
865
1106
  import * as fsPromises2 from "fs/promises";
866
- var CLI_VERSION = true ? "0.2.1" : "dev";
1107
+ var CLI_VERSION = true ? "0.4.0" : "dev";
867
1108
  function isVersionOlderThan(a, b) {
868
1109
  const parse = (v) => {
869
1110
  if (!v) return null;
@@ -893,16 +1134,19 @@ Commands:
893
1134
  release Feed and playbook releases (feed, playbook-draft, playbook)
894
1135
  secrets Secret management (create, list, get, update, delete)
895
1136
  sdk SDK documentation (doc, partitions, partition-summary)
1137
+ skills Data-skill documentation from the Arrays backend (list, summary, endpoint)
896
1138
  comments Playbook comments (create, pin, unpin)
897
1139
  remix Save playbook remix lineage
898
1140
  trading Trading operations (accounts, portfolio, orders, subscriptions, equity-history, risk-rules, subscribe, unsubscribe, execute, update-risk-rules)
899
1141
  auth Authentication (login)
900
1142
  screenshot Capture a web screenshot as PNG
1143
+ arrays Arrays backend operations (token ensure, token status)
901
1144
 
902
1145
  Global options:
903
- --api-key <key> API key (overrides env and config file)
904
- --base-url <url> API base URL (overrides env and config file)
905
- --profile <name> Named profile to use (default: "default")
1146
+ --api-key <key> API key (overrides env and config file)
1147
+ --base-url <url> API base URL (overrides env and config file)
1148
+ --profile <name> Named profile to use (default: "default")
1149
+ --arrays-endpoint <url> Arrays backend URL (or ARRAYS_ENDPOINT env; default: https://data-tools.prd.space.id)
906
1150
  -v, --version Show CLI version
907
1151
  --help Show help (use 'alva <command> --help' for command details)
908
1152
 
@@ -919,6 +1163,8 @@ var COMMAND_HELP = {
919
1163
  Save API credentials to ~/.config/alva/config.json (mode 0600).
920
1164
  After configuring, subsequent commands use the saved key automatically.
921
1165
  Multiple profiles allow switching between environments (production, staging, etc.).
1166
+ Also auto-runs 'alva arrays token ensure' to provision the server-side Arrays JWT
1167
+ (soft-fail: a network/auth failure prints a stderr warning but exit stays 0).
922
1168
 
923
1169
  Required:
924
1170
  --api-key <key> Your Alva API key (starts with "alva_")
@@ -954,6 +1200,9 @@ Verify that your credentials are valid by calling the Alva API. Shows your
954
1200
  username, subscription tier, and which profile/endpoint is being used.
955
1201
  Use this after 'alva configure' to confirm everything works.
956
1202
 
1203
+ Output also includes _meta.arrays_jwt (exists, expires_at, renewal_needed,
1204
+ tier) when the backend is reachable; the field is omitted on RPC failure.
1205
+
957
1206
  Examples:
958
1207
  alva whoami
959
1208
  alva --profile staging whoami`,
@@ -1165,12 +1414,12 @@ Feed flags:
1165
1414
  --cronjob-id <id> ID of the backing cronjob (required)
1166
1415
  --view-json <json> View configuration JSON
1167
1416
  --description <text> Feed description
1417
+ --changelog <text> Per-major changelog summary
1168
1418
 
1169
1419
  Playbook-draft flags:
1170
1420
  --name <name> URL-safe playbook name, unique per user (required)
1171
1421
  --display-name <name> Human-readable title, max 40 chars (required)
1172
1422
  --feeds <json> JSON array of {feed_id, feed_major?} (required)
1173
- --changelog <text> Release changelog (required)
1174
1423
  --description <text> Playbook description
1175
1424
  --trading-symbols <json> JSON array of tickers, e.g. '["BTC","ETH"]' (max 50)
1176
1425
 
@@ -1189,7 +1438,7 @@ Display name conventions:
1189
1438
  Examples:
1190
1439
  alva release feed --name btc-ema --version 1.0.0 --cronjob-id 42
1191
1440
  alva release feed --name nvda-insiders --version 1.0.0 --cronjob-id 43 --description "NVDA insider trading activity"
1192
- alva release playbook-draft --name btc-dashboard --display-name "BTC Trend Dashboard" --feeds '[{"feed_id":100}]' --changelog "Initial release" --trading-symbols '["BTC"]'
1441
+ alva release playbook-draft --name btc-dashboard --display-name "BTC Trend Dashboard" --feeds '[{"feed_id":100}]' --trading-symbols '["BTC"]'
1193
1442
  alva release playbook --name btc-dashboard --version v1.0.0 --feeds '[{"feed_id":100}]' --changelog "Initial release"`,
1194
1443
  secrets: `Usage: alva secrets <subcommand> [options]
1195
1444
 
@@ -1238,20 +1487,37 @@ Flags:
1238
1487
  --partition <name> Partition name for 'partition-summary' (required)
1239
1488
 
1240
1489
  Key partitions:
1241
- spot_market_price_and_volume Spot OHLCV for crypto and equities
1242
- crypto_futures_data Perpetual futures, funding rates, OI
1243
- crypto_technical_metrics MA, RSI, MACD, MVRV, SOPR, NUPL (20 modules)
1244
- equity_fundamentals Income, balance sheet, PE, ROE (31 modules)
1245
- equity_estimates_and_targets Analyst targets, consensus estimates
1246
- equity_ownership_and_flow Insider trades, senator trading, institutions
1247
- macro_and_economics_data CPI, GDP, Treasury rates, VIX (20 modules)
1490
+ feed_widgets Per-handle/channel rolling subscriptions
1491
+ unified_search Web search and URL scraping tools (X/Grok, Google, Brave, serper, decodo)
1248
1492
  technical_indicator_calculation_helpers 50+ pure calculators (RSI, MACD, Bollinger)
1249
1493
 
1250
1494
  Examples:
1251
1495
  alva sdk partitions
1252
- alva sdk partition-summary --partition spot_market_price_and_volume
1253
- alva sdk doc --name "@arrays/crypto/ohlcv:v1.0.0"
1254
- alva sdk doc --name "@arrays/data/stock/ohlcv:v1.0.0"`,
1496
+ alva sdk partition-summary --partition feed_widgets
1497
+ alva sdk doc --name "@arrays/data/widget-scrap/twitter:v1.0.0"
1498
+ alva sdk doc --name "@arrays/data/search/search-grok-x:v1.0.0"`,
1499
+ skills: `Usage: alva skills <subcommand> [options]
1500
+
1501
+ Browse the Arrays backend's data-skill documentation. These endpoints are
1502
+ public \u2014 no Alva credentials required.
1503
+
1504
+ Subcommands:
1505
+ list List all available data skills
1506
+ summary Get the endpoints table for a skill (requires --name)
1507
+ endpoint Get full documentation for a specific endpoint (requires --name and --file)
1508
+
1509
+ Flags:
1510
+ --name <name> Skill name (required for summary and endpoint)
1511
+ --file <file> Endpoint file name from the "File" column of 'skills summary' (required for endpoint)
1512
+
1513
+ Global override:
1514
+ --arrays-endpoint <url> Arrays backend URL (or ARRAYS_ENDPOINT env)
1515
+ Default: https://data-tools.prd.space.id
1516
+
1517
+ Examples:
1518
+ alva skills list
1519
+ alva skills summary --name <skill>
1520
+ alva skills endpoint --name <skill> --file <endpoint-file>`,
1255
1521
  comments: `Usage: alva comments <subcommand> [options]
1256
1522
 
1257
1523
  Manage comments on Alva playbooks. Supports top-level comments and threaded
@@ -1371,7 +1637,20 @@ Examples:
1371
1637
  alva trading subscribe --account-id acc_123 --source-username alice --source-feed btc-signals --playbook-id pb_1 --playbook-version v1.0.0
1372
1638
  alva trading unsubscribe --subscription-id sub_456
1373
1639
  alva trading execute --account-id acc_123 --signal '{"symbol":"BTC","side":"buy","qty":0.1}' --dry-run
1374
- alva trading update-risk-rules --max-single-order-value 10000 --max-single-order-enabled true --max-daily-turnover-value 50000 --max-daily-turnover-enabled true --max-daily-orders-value 100 --max-daily-orders-enabled true`
1640
+ alva trading update-risk-rules --max-single-order-value 10000 --max-single-order-enabled true --max-daily-turnover-value 50000 --max-daily-turnover-enabled true --max-daily-orders-value 100 --max-daily-orders-enabled true`,
1641
+ arrays: `Usage: alva arrays token <subcommand>
1642
+
1643
+ Manage the Arrays JWT used by sandbox scripts (secret.loadPlaintext('ARRAYS_JWT')).
1644
+ The JWT is stored server-side as a jagent secret; the CLI never receives the
1645
+ token itself. 'alva configure' auto-runs 'token ensure' after saving credentials.
1646
+
1647
+ Subcommands:
1648
+ token ensure Provision or refresh the Arrays JWT (idempotent)
1649
+ token status Show Arrays JWT presence, expiry, tier, and renewal hint
1650
+
1651
+ Examples:
1652
+ alva arrays token ensure
1653
+ alva arrays token status`
1375
1654
  };
1376
1655
  async function handleConfigure(args, deps) {
1377
1656
  const flags = parseFlags2(args.slice(1));
@@ -1396,6 +1675,15 @@ async function handleConfigure(args, deps) {
1396
1675
  readFile: (path) => fsPromises2.readFile(path, "utf-8")
1397
1676
  };
1398
1677
  const result = await writeConfig(configInput, writeDeps, profileName);
1678
+ const client = new AlvaClient(baseUrl ? { apiKey, baseUrl } : { apiKey });
1679
+ const runHooks = writeDeps.runHooks ?? ((c) => runPostConfigureHooks(c));
1680
+ try {
1681
+ await runHooks(client);
1682
+ } catch (err) {
1683
+ const msg = err instanceof Error ? err.message : String(err);
1684
+ process.stderr?.write?.(`warning: post-configure hooks crashed: ${msg}
1685
+ `);
1686
+ }
1399
1687
  return {
1400
1688
  status: "configured",
1401
1689
  apiKey: result.apiKey,
@@ -1481,12 +1769,21 @@ async function dispatch(client, args, meta) {
1481
1769
  const user = await client.user.me();
1482
1770
  const record = user;
1483
1771
  const version = meta?.cliVersion ?? CLI_VERSION;
1772
+ let arraysJwtStatus;
1773
+ try {
1774
+ arraysJwtStatus = await client.arraysJwt.status();
1775
+ } catch {
1776
+ }
1777
+ const metaBlock = {
1778
+ profile: meta?.profile ?? "default",
1779
+ endpoint: meta?.baseUrl ?? client.baseUrl
1780
+ };
1781
+ if (arraysJwtStatus !== void 0) {
1782
+ metaBlock.arrays_jwt = arraysJwtStatus;
1783
+ }
1484
1784
  const result = {
1485
1785
  ...record,
1486
- _meta: {
1487
- profile: meta?.profile ?? "default",
1488
- endpoint: meta?.baseUrl ?? client.baseUrl
1489
- }
1786
+ _meta: metaBlock
1490
1787
  };
1491
1788
  const minVersion = record.toolkit_min_version;
1492
1789
  if (typeof minVersion === "string" && version && version !== "dev" && isVersionOlderThan(version, minVersion)) {
@@ -1682,7 +1979,8 @@ async function dispatch(client, args, meta) {
1682
1979
  version: requireFlag(flags, "version", "release feed"),
1683
1980
  cronjob_id: requireNumericFlag(flags, "cronjob-id", "release feed"),
1684
1981
  view_json: jsonParse(flags["view-json"]),
1685
- description: flags["description"]
1982
+ description: flags["description"],
1983
+ changelog: flags["changelog"]
1686
1984
  });
1687
1985
  case "playbook-draft":
1688
1986
  return client.release.playbookDraft({
@@ -1696,12 +1994,7 @@ async function dispatch(client, args, meta) {
1696
1994
  feeds: jsonParse(
1697
1995
  requireFlag(flags, "feeds", "release playbook-draft")
1698
1996
  ),
1699
- trading_symbols: flags["trading-symbols"] ? jsonParse(flags["trading-symbols"]) : void 0,
1700
- changelog: requireFlag(
1701
- flags,
1702
- "changelog",
1703
- "release playbook-draft"
1704
- )
1997
+ trading_symbols: flags["trading-symbols"] ? jsonParse(flags["trading-symbols"]) : void 0
1705
1998
  });
1706
1999
  case "playbook":
1707
2000
  return client.release.playbook({
@@ -1771,6 +2064,28 @@ async function dispatch(client, args, meta) {
1771
2064
  );
1772
2065
  }
1773
2066
  }
2067
+ case "skills": {
2068
+ if (!subcommand)
2069
+ throw new CliUsageError("Missing subcommand for skills", "skills");
2070
+ switch (subcommand) {
2071
+ case "list":
2072
+ return client.skills.list();
2073
+ case "summary":
2074
+ return client.skills.summary({
2075
+ name: requireFlag(flags, "name", "skills summary")
2076
+ });
2077
+ case "endpoint":
2078
+ return client.skills.endpoint({
2079
+ name: requireFlag(flags, "name", "skills endpoint"),
2080
+ file: requireFlag(flags, "file", "skills endpoint")
2081
+ });
2082
+ default:
2083
+ throw new CliUsageError(
2084
+ `Unknown subcommand: skills ${subcommand}`,
2085
+ "skills"
2086
+ );
2087
+ }
2088
+ }
1774
2089
  case "comments": {
1775
2090
  if (!subcommand)
1776
2091
  throw new CliUsageError("Missing subcommand for comments", "comments");
@@ -1809,6 +2124,30 @@ async function dispatch(client, args, meta) {
1809
2124
  },
1810
2125
  parents: jsonParse(requireFlag(flags, "parents", "remix"))
1811
2126
  });
2127
+ case "arrays": {
2128
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
2129
+ return { _help: true, text: COMMAND_HELP.arrays };
2130
+ }
2131
+ if (subcommand === "token") {
2132
+ const leaf = args[2];
2133
+ if (!leaf || leaf === "--help" || leaf === "-h") {
2134
+ return { _help: true, text: COMMAND_HELP.arrays };
2135
+ }
2136
+ switch (leaf) {
2137
+ case "ensure":
2138
+ return client.arraysJwt.ensure();
2139
+ case "status":
2140
+ return client.arraysJwt.status();
2141
+ default:
2142
+ throw new Error(
2143
+ `Unknown subcommand 'arrays token ${leaf}'. Use 'alva arrays --help' for usage.`
2144
+ );
2145
+ }
2146
+ }
2147
+ throw new Error(
2148
+ `Unknown subcommand 'arrays ${subcommand}'. Use 'alva arrays --help' for usage.`
2149
+ );
2150
+ }
1812
2151
  case "screenshot": {
1813
2152
  const outFile = requireFlag(flags, "out", "screenshot");
1814
2153
  const result = await client.screenshot.capture({
@@ -1817,6 +2156,12 @@ async function dispatch(client, args, meta) {
1817
2156
  xpath: flags["xpath"]
1818
2157
  });
1819
2158
  const buf = Buffer.from(result);
2159
+ if (buf.length === 0) {
2160
+ throw new CliUsageError(
2161
+ "Screenshot service returned empty response (0 bytes). The service may be overloaded \u2014 retry in a few seconds.",
2162
+ "screenshot"
2163
+ );
2164
+ }
1820
2165
  fs.writeFileSync(outFile, buf);
1821
2166
  return { written: outFile, bytes: buf.length };
1822
2167
  }
@@ -1940,6 +2285,27 @@ async function dispatch(client, args, meta) {
1940
2285
  throw new CliUsageError(`Unknown command: '${group}'`);
1941
2286
  }
1942
2287
  }
2288
+ function stripGlobalFlags(argv) {
2289
+ const GLOBAL_FLAGS = [
2290
+ "--api-key",
2291
+ "--base-url",
2292
+ "--profile",
2293
+ "--arrays-endpoint"
2294
+ ];
2295
+ const result = [];
2296
+ for (let i = 0; i < argv.length; i++) {
2297
+ const a = argv[i];
2298
+ if (GLOBAL_FLAGS.includes(a)) {
2299
+ i++;
2300
+ continue;
2301
+ }
2302
+ if (GLOBAL_FLAGS.some((f) => a.startsWith(`${f}=`))) {
2303
+ continue;
2304
+ }
2305
+ result.push(a);
2306
+ }
2307
+ return result;
2308
+ }
1943
2309
  async function main() {
1944
2310
  try {
1945
2311
  const rawArgs = process.argv.slice(2);
@@ -1982,20 +2348,10 @@ async function main() {
1982
2348
  });
1983
2349
  const client = new AlvaClient({
1984
2350
  apiKey: config.apiKey,
1985
- baseUrl: config.baseUrl
2351
+ baseUrl: config.baseUrl,
2352
+ arraysBaseUrl: config.arraysBaseUrl
1986
2353
  });
1987
- const cleanArgs = [];
1988
- for (let i = 0; i < rawArgs.length; i++) {
1989
- const a = rawArgs[i];
1990
- if (a === "--api-key" || a === "--base-url" || a === "--profile") {
1991
- i++;
1992
- continue;
1993
- }
1994
- if (a.startsWith("--api-key=") || a.startsWith("--base-url=") || a.startsWith("--profile=")) {
1995
- continue;
1996
- }
1997
- cleanArgs.push(a);
1998
- }
2354
+ const cleanArgs = stripGlobalFlags(rawArgs);
1999
2355
  const result = await dispatch(client, cleanArgs, {
2000
2356
  profile: config.profile,
2001
2357
  baseUrl: config.baseUrl,
@@ -2016,6 +2372,10 @@ async function main() {
2016
2372
  process.stdout.write(Buffer.from(result));
2017
2373
  return;
2018
2374
  }
2375
+ if (typeof result === "string") {
2376
+ process.stdout.write(result);
2377
+ return;
2378
+ }
2019
2379
  if (result !== void 0) {
2020
2380
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2021
2381
  }
@@ -2053,6 +2413,7 @@ export {
2053
2413
  CLI_VERSION,
2054
2414
  dispatch,
2055
2415
  handleConfigure,
2056
- isVersionOlderThan
2416
+ isVersionOlderThan,
2417
+ stripGlobalFlags
2057
2418
  };
2058
2419
  //# sourceMappingURL=cli.js.map