@alva-ai/toolkit 0.2.0 → 0.3.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.
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) {
@@ -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,11 +553,37 @@ 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
+
496
580
  // src/client.ts
497
581
  var DEFAULT_BASE_URL = "https://api-llm.prd.alva.ai";
582
+ var DEFAULT_ARRAYS_BASE_URL = "https://data-tools.prd.space.id";
498
583
  var AlvaClient = class {
499
584
  baseUrl;
500
- token;
585
+ arraysBaseUrl;
586
+ viewer_token;
501
587
  apiKey;
502
588
  _fs;
503
589
  _run;
@@ -505,14 +591,17 @@ var AlvaClient = class {
505
591
  _release;
506
592
  _secrets;
507
593
  _sdk;
594
+ _skills;
508
595
  _comments;
509
596
  _remix;
510
597
  _screenshot;
511
598
  _user;
512
599
  _trading;
600
+ _arraysJwt;
513
601
  constructor(config) {
514
602
  this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
515
- this.token = config.token;
603
+ this.arraysBaseUrl = config.arraysBaseUrl ?? DEFAULT_ARRAYS_BASE_URL;
604
+ this.viewer_token = config.viewer_token;
516
605
  this.apiKey = config.apiKey;
517
606
  }
518
607
  get fs() {
@@ -533,6 +622,9 @@ var AlvaClient = class {
533
622
  get sdk() {
534
623
  return this._sdk ??= new SdkDocsResource(this);
535
624
  }
625
+ get skills() {
626
+ return this._skills ??= new SkillsResource(this);
627
+ }
536
628
  get comments() {
537
629
  return this._comments ??= new CommentsResource(this);
538
630
  }
@@ -548,17 +640,21 @@ var AlvaClient = class {
548
640
  get trading() {
549
641
  return this._trading ??= new TradingResource(this);
550
642
  }
643
+ get arraysJwt() {
644
+ return this._arraysJwt ??= new ArraysJwtResource(this);
645
+ }
551
646
  _requireAuth() {
552
- if (!this.token && !this.apiKey) {
647
+ if (!this.viewer_token && !this.apiKey) {
553
648
  throw new AlvaError(
554
649
  "UNAUTHENTICATED",
555
- "Authentication is required. Pass token or apiKey in the constructor.",
650
+ "Authentication is required. Pass viewer_token or apiKey in the constructor.",
556
651
  401
557
652
  );
558
653
  }
559
654
  }
560
655
  async _request(method, path, options) {
561
- let url = `${this.baseUrl}${path}`;
656
+ const baseUrl = options?.baseUrl ?? this.baseUrl;
657
+ let url = `${baseUrl}${path}`;
562
658
  if (options?.query) {
563
659
  const params = new URLSearchParams();
564
660
  for (const [key, value] of Object.entries(options.query)) {
@@ -572,10 +668,12 @@ var AlvaClient = class {
572
668
  }
573
669
  }
574
670
  const headers = {};
575
- if (this.token) {
576
- headers.Authorization = `${this.token}`;
577
- } else if (this.apiKey) {
578
- headers["X-Alva-Api-Key"] = this.apiKey;
671
+ if (!options?.noAuth) {
672
+ if (this.viewer_token) {
673
+ headers["x-Playbook-Viewer"] = this.viewer_token;
674
+ } else if (this.apiKey) {
675
+ headers["X-Alva-Api-Key"] = this.apiKey;
676
+ }
579
677
  }
580
678
  let fetchBody;
581
679
  if (options?.rawBody !== void 0) {
@@ -691,6 +789,7 @@ async function writeConfig(config, deps, profileName = "default") {
691
789
  return {
692
790
  apiKey: profileData.apiKey,
693
791
  baseUrl: profileData.baseUrl,
792
+ arraysBaseUrl: DEFAULT_ARRAYS_BASE_URL,
694
793
  profile: profileName
695
794
  };
696
795
  }
@@ -710,6 +809,8 @@ function loadConfig(deps) {
710
809
  const profileName = parseFlag(argv, "--profile") || env.ALVA_PROFILE || "default";
711
810
  const baseUrlFlag = parseFlag(argv, "--base-url");
712
811
  const baseUrlEnv = env.ALVA_ENDPOINT;
812
+ const arraysBaseUrlFlag = parseFlag(argv, "--arrays-endpoint");
813
+ const arraysBaseUrlEnv = env.ARRAYS_ENDPOINT;
713
814
  const apiKeyFlag = parseFlag(argv, "--api-key");
714
815
  const apiKeyEnv = env.ALVA_API_KEY;
715
816
  let fileProfile = {};
@@ -731,6 +832,7 @@ function loadConfig(deps) {
731
832
  return {
732
833
  apiKey: apiKeyFlag ?? apiKeyEnv ?? fileProfile.apiKey,
733
834
  baseUrl: baseUrlFlag ?? baseUrlEnv ?? fileProfile.baseUrl,
835
+ arraysBaseUrl: arraysBaseUrlFlag ?? arraysBaseUrlEnv ?? DEFAULT_ARRAYS_BASE_URL,
734
836
  profile: profileName
735
837
  };
736
838
  }
@@ -859,11 +961,48 @@ Waiting for login callback...
859
961
  });
860
962
  }
861
963
 
964
+ // src/cli/postConfigureHooks.ts
965
+ function defaultStderr(s) {
966
+ process.stderr.write(s);
967
+ }
968
+ function formatTier(t) {
969
+ const stripped = typeof t === "string" && t.startsWith("SUBSCRIPTION_TIER_") ? t.slice("SUBSCRIPTION_TIER_".length) : String(t);
970
+ return stripped.toLowerCase();
971
+ }
972
+ function formatExpiry(expiresAt) {
973
+ return new Date(expiresAt * 1e3).toISOString().slice(0, 10);
974
+ }
975
+ var ensureArraysJwtHook = {
976
+ name: "ensureArraysJwt",
977
+ async run(client) {
978
+ const res = await client.arraysJwt.ensure();
979
+ const date = formatExpiry(res.expires_at);
980
+ const tier = formatTier(res.tier);
981
+ const verb = res.renewed ? "Arrays JWT provisioned" : "Arrays JWT already current";
982
+ process.stderr.write(`${verb} (expires ${date}, tier: ${tier})
983
+ `);
984
+ }
985
+ };
986
+ var POST_CONFIGURE_HOOKS = [ensureArraysJwtHook];
987
+ async function runPostConfigureHooks(client, deps) {
988
+ const hooks = deps?.hooks ?? POST_CONFIGURE_HOOKS;
989
+ const stderr = deps?.stderr ?? defaultStderr;
990
+ for (const hook of hooks) {
991
+ try {
992
+ await hook.run(client);
993
+ } catch (err) {
994
+ const msg = err instanceof Error ? err.message : String(err);
995
+ stderr(`warning: post-configure hook "${hook.name}" failed: ${msg}
996
+ `);
997
+ }
998
+ }
999
+ }
1000
+
862
1001
  // src/cli/index.ts
863
1002
  import * as fs from "fs";
864
1003
  import * as os2 from "os";
865
1004
  import * as fsPromises2 from "fs/promises";
866
- var CLI_VERSION = true ? "0.2.0" : "dev";
1005
+ var CLI_VERSION = true ? "0.3.1" : "dev";
867
1006
  function isVersionOlderThan(a, b) {
868
1007
  const parse = (v) => {
869
1008
  if (!v) return null;
@@ -885,24 +1024,27 @@ var HELP_TEXT = `Usage: alva <command> [options]
885
1024
 
886
1025
  Commands:
887
1026
  configure Save API key and endpoint to a named profile
888
- whoami Verify credentials and show current user info
889
- user User profile operations
1027
+ whoami Verify credentials and show current identity
1028
+ user User profile operations (me)
890
1029
  fs Filesystem operations (read, write, stat, readdir, mkdir, remove, rename, copy, symlink, readlink, chmod, grant, revoke)
891
1030
  run Execute code in the Alva runtime
892
- deploy Cronjob management (create, list, get, update, delete, pause, resume)
1031
+ deploy Cronjob management (create, list, get, update, delete, pause, resume, runs, run-logs)
893
1032
  release Feed and playbook releases (feed, playbook-draft, playbook)
894
1033
  secrets Secret management (create, list, get, update, delete)
895
1034
  sdk SDK documentation (doc, partitions, partition-summary)
1035
+ skills Data-skill documentation from the Arrays backend (list, summary, endpoint)
896
1036
  comments Playbook comments (create, pin, unpin)
897
1037
  remix Save playbook remix lineage
898
1038
  trading Trading operations (accounts, portfolio, orders, subscriptions, equity-history, risk-rules, subscribe, unsubscribe, execute, update-risk-rules)
899
- auth Authentication (login via browser)
1039
+ auth Authentication (login)
900
1040
  screenshot Capture a web screenshot as PNG
1041
+ arrays Arrays backend operations (token ensure, token status)
901
1042
 
902
1043
  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")
1044
+ --api-key <key> API key (overrides env and config file)
1045
+ --base-url <url> API base URL (overrides env and config file)
1046
+ --profile <name> Named profile to use (default: "default")
1047
+ --arrays-endpoint <url> Arrays backend URL (or ARRAYS_ENDPOINT env; default: https://data-tools.prd.space.id)
906
1048
  -v, --version Show CLI version
907
1049
  --help Show help (use 'alva <command> --help' for command details)
908
1050
 
@@ -919,6 +1061,8 @@ var COMMAND_HELP = {
919
1061
  Save API credentials to ~/.config/alva/config.json (mode 0600).
920
1062
  After configuring, subsequent commands use the saved key automatically.
921
1063
  Multiple profiles allow switching between environments (production, staging, etc.).
1064
+ Also auto-runs 'alva arrays token ensure' to provision the server-side Arrays JWT
1065
+ (soft-fail: a network/auth failure prints a stderr warning but exit stays 0).
922
1066
 
923
1067
  Required:
924
1068
  --api-key <key> Your Alva API key (starts with "alva_")
@@ -954,6 +1098,9 @@ Verify that your credentials are valid by calling the Alva API. Shows your
954
1098
  username, subscription tier, and which profile/endpoint is being used.
955
1099
  Use this after 'alva configure' to confirm everything works.
956
1100
 
1101
+ Output also includes _meta.arrays_jwt (exists, expires_at, renewal_needed,
1102
+ tier) when the backend is reachable; the field is omitted on RPC failure.
1103
+
957
1104
  Examples:
958
1105
  alva whoami
959
1106
  alva --profile staging whoami`,
@@ -987,16 +1134,26 @@ Subcommands:
987
1134
  grant Grant access permission to a user or group
988
1135
  revoke Revoke access permission
989
1136
 
990
- Common flags:
991
- --path <path> File or directory path (required for most subcommands)
992
- --recursive Enable recursive operation (readdir, remove)
993
- --no-recursive Disable recursive operation
994
- --mkdir-parents Create parent directories on write (default for write)
995
- --no-mkdir-parents Disable automatic parent directory creation on write
1137
+ Subcommand flags:
1138
+ read --path (required), [--offset <n>], [--size <n>]
1139
+ write --path (required), --data <text> OR --file <local-path> (one required),
1140
+ [--mkdir-parents | --no-mkdir-parents]
1141
+ stat --path (required)
1142
+ readdir --path (required), [--recursive | --no-recursive]
1143
+ mkdir --path (required)
1144
+ remove --path (required), [--recursive | --no-recursive]
1145
+ rename --old-path (required), --new-path (required)
1146
+ copy --src-path (required), --dst-path (required)
1147
+ symlink --target-path (required), --link-path (required)
1148
+ readlink --path (required)
1149
+ chmod --path (required), --mode <octal> (required)
1150
+ grant --path (required), --subject <s> (required), --permission <p> (required)
1151
+ revoke --path (required), --subject <s> (required), --permission <p> (required)
996
1152
 
997
1153
  Path conventions:
998
1154
  ~/... Home-relative path (expands to /alva/home/<username>/...)
999
1155
  /alva/home/alice/... Absolute path (required for public/unauthenticated reads)
1156
+ Quote tilde paths to prevent shell expansion: --path "~/data" (not --path ~/data).
1000
1157
 
1001
1158
  Time series reads:
1002
1159
  Paths under feed data directories support virtual suffixes:
@@ -1012,21 +1169,23 @@ Grant/revoke subjects:
1012
1169
  user:<id> Specific user by ID
1013
1170
 
1014
1171
  Examples:
1015
- alva fs readdir --path ~/
1016
- alva fs readdir --path ~/data --recursive
1017
- alva fs read --path ~/data/prices.json
1018
- alva fs read --path ~/feeds/btc-ema/v1/data/metrics/prices/@last/100
1172
+ alva fs readdir --path "~/"
1173
+ alva fs readdir --path "~/data" --recursive
1174
+ alva fs read --path "~/data/prices.json"
1175
+ alva fs read --path "~/feeds/btc-ema/v1/data/metrics/prices/@last/100"
1019
1176
  alva fs read --path /alva/home/alice/feeds/btc-ema/v1/data/metrics/prices/@last/10
1020
- alva fs write --path ~/hello.txt --data "Hello, world!"
1021
- alva fs write --path ~/feeds/my-feed/v1/src/index.js --file ./local-script.js --mkdir-parents
1022
- alva fs stat --path ~/hello.txt
1023
- alva fs mkdir --path ~/feeds/my-feed/v1/src
1024
- alva fs remove --path ~/old-folder --recursive
1025
- alva fs rename --old-path ~/a.txt --new-path ~/b.txt
1026
- alva fs copy --src-path ~/a.txt --dst-path ~/b.txt
1027
- alva fs chmod --path ~/script.js --mode 755
1028
- alva fs grant --path ~/feeds/btc-ema --subject "special:user:*" --permission read
1029
- alva fs revoke --path ~/feeds/btc-ema --subject "special:user:*" --permission read`,
1177
+ alva fs write --path "~/hello.txt" --data "Hello, world!"
1178
+ alva fs write --path "~/feeds/my-feed/v1/src/index.js" --file ./local-script.js --mkdir-parents
1179
+ alva fs stat --path "~/hello.txt"
1180
+ alva fs mkdir --path "~/feeds/my-feed/v1/src"
1181
+ alva fs remove --path "~/old-folder" --recursive
1182
+ alva fs rename --old-path "~/a.txt" --new-path "~/b.txt"
1183
+ alva fs copy --src-path "~/a.txt" --dst-path "~/b.txt"
1184
+ alva fs chmod --path "~/script.js" --mode 755
1185
+ alva fs grant --path "~/feeds/btc-ema" --subject "special:user:*" --permission read
1186
+ alva fs revoke --path "~/feeds/btc-ema" --subject "special:user:*" --permission read
1187
+ alva fs symlink --target-path "~/real-file.txt" --link-path "~/my-link.txt"
1188
+ alva fs readlink --path "~/my-link.txt"`,
1030
1189
  run: `Usage: alva run [options]
1031
1190
 
1032
1191
  Execute JavaScript code in the Alva V8 runtime. Provide either inline code
@@ -1067,8 +1226,8 @@ Constraints:
1067
1226
  Examples:
1068
1227
  alva run --code "1 + 2 + 3;"
1069
1228
  alva run --code "JSON.stringify(require('env').args);" --args '{"symbol":"BTC"}'
1070
- alva run --entry-path ~/feeds/my-feed/v1/src/index.js
1071
- alva run --entry-path ~/tasks/analyze/src/index.js --args '{"symbol":"NVDA","limit":50}'
1229
+ alva run --entry-path "~/feeds/my-feed/v1/src/index.js"
1230
+ alva run --entry-path "~/tasks/analyze/src/index.js" --args '{"symbol":"NVDA","limit":50}'
1072
1231
  alva run --local-file ./my-script.js --args '{"symbol":"BTC"}'`,
1073
1232
  deploy: `Usage: alva deploy <subcommand> [options]
1074
1233
 
@@ -1083,6 +1242,8 @@ Subcommands:
1083
1242
  delete Delete a cronjob
1084
1243
  pause Pause a running cronjob
1085
1244
  resume Resume a paused cronjob
1245
+ runs List runs for a cronjob (cursor-paginated)
1246
+ run-logs Get stdout/stderr logs for a single cronjob run
1086
1247
 
1087
1248
  Create flags:
1088
1249
  --name <name> Cronjob name (required, 1-63 lowercase alphanumeric/hyphens)
@@ -1099,6 +1260,15 @@ List flags:
1099
1260
  Get/Update/Delete/Pause/Resume flags:
1100
1261
  --id <id> Cronjob ID (required)
1101
1262
 
1263
+ Runs flags:
1264
+ --id <id> Cronjob ID (required)
1265
+ --first <n> Max results per page
1266
+ --cursor <cursor> Pagination cursor from previous response
1267
+
1268
+ Run-logs flags:
1269
+ --id <id> Cronjob ID (required)
1270
+ --run-id <id> Run ID (required)
1271
+
1102
1272
  Name format: 1-63 lowercase alphanumeric or hyphens, no leading/trailing hyphens.
1103
1273
  Valid: btc-ema-update, my-strategy-1
1104
1274
  Invalid: BTC EMA, -my-job-, my_job
@@ -1110,8 +1280,8 @@ Recommended cron schedules:
1110
1280
  "0 0 * * *" Daily at midnight (end-of-day summaries)
1111
1281
 
1112
1282
  Examples:
1113
- alva deploy create --name btc-ema --path ~/feeds/btc-ema/v1/src/index.js --cron "0 */4 * * *"
1114
- alva deploy create --name alert --path ~/feeds/alert/v1/src/index.js --cron "*/5 * * * *" --push-notify --args '{"threshold":100}'
1283
+ alva deploy create --name btc-ema --path "~/feeds/btc-ema/v1/src/index.js" --cron "0 */4 * * *"
1284
+ alva deploy create --name alert --path "~/feeds/alert/v1/src/index.js" --cron "*/5 * * * *" --push-notify --args '{"threshold":100}'
1115
1285
  alva deploy list
1116
1286
  alva deploy list --limit 10
1117
1287
  alva deploy get --id 42
@@ -1147,7 +1317,7 @@ Playbook-draft flags:
1147
1317
  --name <name> URL-safe playbook name, unique per user (required)
1148
1318
  --display-name <name> Human-readable title, max 40 chars (required)
1149
1319
  --feeds <json> JSON array of {feed_id, feed_major?} (required)
1150
- --changelog <text> Release changelog (required)
1320
+ --changelog <text> Release changelog
1151
1321
  --description <text> Playbook description
1152
1322
  --trading-symbols <json> JSON array of tickers, e.g. '["BTC","ETH"]' (max 50)
1153
1323
 
@@ -1215,20 +1385,37 @@ Flags:
1215
1385
  --partition <name> Partition name for 'partition-summary' (required)
1216
1386
 
1217
1387
  Key partitions:
1218
- spot_market_price_and_volume Spot OHLCV for crypto and equities
1219
- crypto_futures_data Perpetual futures, funding rates, OI
1220
- crypto_technical_metrics MA, RSI, MACD, MVRV, SOPR, NUPL (20 modules)
1221
- equity_fundamentals Income, balance sheet, PE, ROE (31 modules)
1222
- equity_estimates_and_targets Analyst targets, consensus estimates
1223
- equity_ownership_and_flow Insider trades, senator trading, institutions
1224
- macro_and_economics_data CPI, GDP, Treasury rates, VIX (20 modules)
1388
+ feed_widgets Per-handle/channel rolling subscriptions
1389
+ unified_search Web search and URL scraping tools (X/Grok, Google, Brave, serper, decodo)
1225
1390
  technical_indicator_calculation_helpers 50+ pure calculators (RSI, MACD, Bollinger)
1226
1391
 
1227
1392
  Examples:
1228
1393
  alva sdk partitions
1229
- alva sdk partition-summary --partition spot_market_price_and_volume
1230
- alva sdk doc --name "@arrays/crypto/ohlcv:v1.0.0"
1231
- alva sdk doc --name "@arrays/data/stock/ohlcv:v1.0.0"`,
1394
+ alva sdk partition-summary --partition feed_widgets
1395
+ alva sdk doc --name "@arrays/data/widget-scrap/twitter:v1.0.0"
1396
+ alva sdk doc --name "@arrays/data/search/search-grok-x:v1.0.0"`,
1397
+ skills: `Usage: alva skills <subcommand> [options]
1398
+
1399
+ Browse the Arrays backend's data-skill documentation. These endpoints are
1400
+ public \u2014 no Alva credentials required.
1401
+
1402
+ Subcommands:
1403
+ list List all available data skills
1404
+ summary Get the endpoints table for a skill (requires --name)
1405
+ endpoint Get full documentation for a specific endpoint (requires --name and --file)
1406
+
1407
+ Flags:
1408
+ --name <name> Skill name (required for summary and endpoint)
1409
+ --file <file> Endpoint file name from the "File" column of 'skills summary' (required for endpoint)
1410
+
1411
+ Global override:
1412
+ --arrays-endpoint <url> Arrays backend URL (or ARRAYS_ENDPOINT env)
1413
+ Default: https://data-tools.prd.space.id
1414
+
1415
+ Examples:
1416
+ alva skills list
1417
+ alva skills summary --name <skill>
1418
+ alva skills endpoint --name <skill> --file <endpoint-file>`,
1232
1419
  comments: `Usage: alva comments <subcommand> [options]
1233
1420
 
1234
1421
  Manage comments on Alva playbooks. Supports top-level comments and threaded
@@ -1348,7 +1535,20 @@ Examples:
1348
1535
  alva trading subscribe --account-id acc_123 --source-username alice --source-feed btc-signals --playbook-id pb_1 --playbook-version v1.0.0
1349
1536
  alva trading unsubscribe --subscription-id sub_456
1350
1537
  alva trading execute --account-id acc_123 --signal '{"symbol":"BTC","side":"buy","qty":0.1}' --dry-run
1351
- 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`
1538
+ 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`,
1539
+ arrays: `Usage: alva arrays token <subcommand>
1540
+
1541
+ Manage the Arrays JWT used by sandbox scripts (secret.loadPlaintext('ARRAYS_JWT')).
1542
+ The JWT is stored server-side as a jagent secret; the CLI never receives the
1543
+ token itself. 'alva configure' auto-runs 'token ensure' after saving credentials.
1544
+
1545
+ Subcommands:
1546
+ token ensure Provision or refresh the Arrays JWT (idempotent)
1547
+ token status Show Arrays JWT presence, expiry, tier, and renewal hint
1548
+
1549
+ Examples:
1550
+ alva arrays token ensure
1551
+ alva arrays token status`
1352
1552
  };
1353
1553
  async function handleConfigure(args, deps) {
1354
1554
  const flags = parseFlags2(args.slice(1));
@@ -1373,6 +1573,15 @@ async function handleConfigure(args, deps) {
1373
1573
  readFile: (path) => fsPromises2.readFile(path, "utf-8")
1374
1574
  };
1375
1575
  const result = await writeConfig(configInput, writeDeps, profileName);
1576
+ const client = new AlvaClient(baseUrl ? { apiKey, baseUrl } : { apiKey });
1577
+ const runHooks = writeDeps.runHooks ?? ((c) => runPostConfigureHooks(c));
1578
+ try {
1579
+ await runHooks(client);
1580
+ } catch (err) {
1581
+ const msg = err instanceof Error ? err.message : String(err);
1582
+ process.stderr?.write?.(`warning: post-configure hooks crashed: ${msg}
1583
+ `);
1584
+ }
1376
1585
  return {
1377
1586
  status: "configured",
1378
1587
  apiKey: result.apiKey,
@@ -1458,12 +1667,21 @@ async function dispatch(client, args, meta) {
1458
1667
  const user = await client.user.me();
1459
1668
  const record = user;
1460
1669
  const version = meta?.cliVersion ?? CLI_VERSION;
1670
+ let arraysJwtStatus;
1671
+ try {
1672
+ arraysJwtStatus = await client.arraysJwt.status();
1673
+ } catch {
1674
+ }
1675
+ const metaBlock = {
1676
+ profile: meta?.profile ?? "default",
1677
+ endpoint: meta?.baseUrl ?? client.baseUrl
1678
+ };
1679
+ if (arraysJwtStatus !== void 0) {
1680
+ metaBlock.arrays_jwt = arraysJwtStatus;
1681
+ }
1461
1682
  const result = {
1462
1683
  ...record,
1463
- _meta: {
1464
- profile: meta?.profile ?? "default",
1465
- endpoint: meta?.baseUrl ?? client.baseUrl
1466
- }
1684
+ _meta: metaBlock
1467
1685
  };
1468
1686
  const minVersion = record.toolkit_min_version;
1469
1687
  if (typeof minVersion === "string" && version && version !== "dev" && isVersionOlderThan(version, minVersion)) {
@@ -1674,11 +1892,7 @@ async function dispatch(client, args, meta) {
1674
1892
  requireFlag(flags, "feeds", "release playbook-draft")
1675
1893
  ),
1676
1894
  trading_symbols: flags["trading-symbols"] ? jsonParse(flags["trading-symbols"]) : void 0,
1677
- changelog: requireFlag(
1678
- flags,
1679
- "changelog",
1680
- "release playbook-draft"
1681
- )
1895
+ changelog: flags["changelog"]
1682
1896
  });
1683
1897
  case "playbook":
1684
1898
  return client.release.playbook({
@@ -1748,6 +1962,28 @@ async function dispatch(client, args, meta) {
1748
1962
  );
1749
1963
  }
1750
1964
  }
1965
+ case "skills": {
1966
+ if (!subcommand)
1967
+ throw new CliUsageError("Missing subcommand for skills", "skills");
1968
+ switch (subcommand) {
1969
+ case "list":
1970
+ return client.skills.list();
1971
+ case "summary":
1972
+ return client.skills.summary({
1973
+ name: requireFlag(flags, "name", "skills summary")
1974
+ });
1975
+ case "endpoint":
1976
+ return client.skills.endpoint({
1977
+ name: requireFlag(flags, "name", "skills endpoint"),
1978
+ file: requireFlag(flags, "file", "skills endpoint")
1979
+ });
1980
+ default:
1981
+ throw new CliUsageError(
1982
+ `Unknown subcommand: skills ${subcommand}`,
1983
+ "skills"
1984
+ );
1985
+ }
1986
+ }
1751
1987
  case "comments": {
1752
1988
  if (!subcommand)
1753
1989
  throw new CliUsageError("Missing subcommand for comments", "comments");
@@ -1786,6 +2022,30 @@ async function dispatch(client, args, meta) {
1786
2022
  },
1787
2023
  parents: jsonParse(requireFlag(flags, "parents", "remix"))
1788
2024
  });
2025
+ case "arrays": {
2026
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
2027
+ return { _help: true, text: COMMAND_HELP.arrays };
2028
+ }
2029
+ if (subcommand === "token") {
2030
+ const leaf = args[2];
2031
+ if (!leaf || leaf === "--help" || leaf === "-h") {
2032
+ return { _help: true, text: COMMAND_HELP.arrays };
2033
+ }
2034
+ switch (leaf) {
2035
+ case "ensure":
2036
+ return client.arraysJwt.ensure();
2037
+ case "status":
2038
+ return client.arraysJwt.status();
2039
+ default:
2040
+ throw new Error(
2041
+ `Unknown subcommand 'arrays token ${leaf}'. Use 'alva arrays --help' for usage.`
2042
+ );
2043
+ }
2044
+ }
2045
+ throw new Error(
2046
+ `Unknown subcommand 'arrays ${subcommand}'. Use 'alva arrays --help' for usage.`
2047
+ );
2048
+ }
1789
2049
  case "screenshot": {
1790
2050
  const outFile = requireFlag(flags, "out", "screenshot");
1791
2051
  const result = await client.screenshot.capture({
@@ -1794,6 +2054,12 @@ async function dispatch(client, args, meta) {
1794
2054
  xpath: flags["xpath"]
1795
2055
  });
1796
2056
  const buf = Buffer.from(result);
2057
+ if (buf.length === 0) {
2058
+ throw new CliUsageError(
2059
+ "Screenshot service returned empty response (0 bytes). The service may be overloaded \u2014 retry in a few seconds.",
2060
+ "screenshot"
2061
+ );
2062
+ }
1797
2063
  fs.writeFileSync(outFile, buf);
1798
2064
  return { written: outFile, bytes: buf.length };
1799
2065
  }
@@ -1917,6 +2183,27 @@ async function dispatch(client, args, meta) {
1917
2183
  throw new CliUsageError(`Unknown command: '${group}'`);
1918
2184
  }
1919
2185
  }
2186
+ function stripGlobalFlags(argv) {
2187
+ const GLOBAL_FLAGS = [
2188
+ "--api-key",
2189
+ "--base-url",
2190
+ "--profile",
2191
+ "--arrays-endpoint"
2192
+ ];
2193
+ const result = [];
2194
+ for (let i = 0; i < argv.length; i++) {
2195
+ const a = argv[i];
2196
+ if (GLOBAL_FLAGS.includes(a)) {
2197
+ i++;
2198
+ continue;
2199
+ }
2200
+ if (GLOBAL_FLAGS.some((f) => a.startsWith(`${f}=`))) {
2201
+ continue;
2202
+ }
2203
+ result.push(a);
2204
+ }
2205
+ return result;
2206
+ }
1920
2207
  async function main() {
1921
2208
  try {
1922
2209
  const rawArgs = process.argv.slice(2);
@@ -1959,20 +2246,10 @@ async function main() {
1959
2246
  });
1960
2247
  const client = new AlvaClient({
1961
2248
  apiKey: config.apiKey,
1962
- baseUrl: config.baseUrl
2249
+ baseUrl: config.baseUrl,
2250
+ arraysBaseUrl: config.arraysBaseUrl
1963
2251
  });
1964
- const cleanArgs = [];
1965
- for (let i = 0; i < rawArgs.length; i++) {
1966
- const a = rawArgs[i];
1967
- if (a === "--api-key" || a === "--base-url" || a === "--profile") {
1968
- i++;
1969
- continue;
1970
- }
1971
- if (a.startsWith("--api-key=") || a.startsWith("--base-url=") || a.startsWith("--profile=")) {
1972
- continue;
1973
- }
1974
- cleanArgs.push(a);
1975
- }
2252
+ const cleanArgs = stripGlobalFlags(rawArgs);
1976
2253
  const result = await dispatch(client, cleanArgs, {
1977
2254
  profile: config.profile,
1978
2255
  baseUrl: config.baseUrl,
@@ -1993,6 +2270,10 @@ async function main() {
1993
2270
  process.stdout.write(Buffer.from(result));
1994
2271
  return;
1995
2272
  }
2273
+ if (typeof result === "string") {
2274
+ process.stdout.write(result);
2275
+ return;
2276
+ }
1996
2277
  if (result !== void 0) {
1997
2278
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
1998
2279
  }
@@ -2030,6 +2311,7 @@ export {
2030
2311
  CLI_VERSION,
2031
2312
  dispatch,
2032
2313
  handleConfigure,
2033
- isVersionOlderThan
2314
+ isVersionOlderThan,
2315
+ stripGlobalFlags
2034
2316
  };
2035
2317
  //# sourceMappingURL=cli.js.map