@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/README.md +28 -0
- package/dist/browser.global.js +1 -1
- package/dist/browser.global.js.map +1 -1
- package/dist/cli.js +412 -51
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +210 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +204 -1
- package/dist/index.d.ts +204 -1
- package/dist/index.js +210 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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 (
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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.
|
|
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>
|
|
904
|
-
--base-url <url>
|
|
905
|
-
--profile <name>
|
|
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}]' --
|
|
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
|
-
|
|
1242
|
-
|
|
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
|
|
1253
|
-
alva sdk doc --name "@arrays/
|
|
1254
|
-
alva sdk doc --name "@arrays/data/
|
|
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
|