@thirdfy/agent-cli 0.2.77 → 0.2.79
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/CHANGELOG.md +27 -0
- package/README.md +8 -8
- package/package.json +2 -2
- package/scripts/validate-cli-contract-schemas.mjs +1 -0
- package/src/cli/manifest.mjs +9 -2
- package/src/cli/options/data.mjs +14 -0
- package/src/cli/options/index.mjs +3 -0
- package/src/commands/avantis.mjs +7 -4
- package/src/commands/condor.mjs +6 -5
- package/src/commands/credits.mjs +3 -1
- package/src/commands/doctor.mjs +5 -4
- package/src/commands/execute.mjs +28 -22
- package/src/commands/hummingbot.mjs +7 -6
- package/src/commands/polymarket.mjs +2 -1
- package/src/commands/signals/command.mjs +39 -0
- package/src/commands/signals/signalRows.mjs +153 -0
- package/src/commands/signals/typedReading.mjs +55 -0
- package/src/commands/telemetry.mjs +5 -1
- package/src/commands/venue.mjs +5 -2
- package/src/commands/wallet.mjs +3 -3
- package/src/contracts/cli/schemas/signals.response.schema.json +78 -0
- package/src/core/actionTimeouts.mjs +4 -5
- package/src/core/envelope.mjs +46 -7
- package/src/core/venueBrand.mjs +76 -0
- package/src/runtime/actions/selection.mjs +66 -18
- package/src/runtime/certification/executionCertify.mjs +3 -1
- package/src/runtime/certification/venueActionSchemas.mjs +5 -3
- package/src/runtime/execution/lanes.mjs +4 -2
- package/src/runtime/execution/payloads.mjs +3 -4
- package/src/runtime/execution/runners.mjs +1 -1
- package/src/runtime/handlers.mjs +4 -0
- package/src/runtime/main.mjs +4 -4
- package/src/runtime/providerHints.mjs +92 -65
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The typed reading of a data read (Thirdfy API typed results, thirdfy-api-v2#681): `typed`, with
|
|
3
|
+
* `parseIssues`, `freshness` and `attribution`, wherever the run result nests it. Human output shows
|
|
4
|
+
* it before the full result, so a reader does not dig through the provider's raw `payload`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const isRecord = (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
8
|
+
|
|
9
|
+
/** Keys never searched: the raw vendor reply can be large and never holds the typed reading. */
|
|
10
|
+
const SKIPPED_KEYS = new Set(['payload', 'telemetry', 'preflight', 'laneMetadata']);
|
|
11
|
+
const MAX_DEPTH = 6;
|
|
12
|
+
|
|
13
|
+
function parseJsonText(value) {
|
|
14
|
+
if (typeof value !== 'string') return value;
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
if (!trimmed.startsWith('{')) return value;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(trimmed);
|
|
19
|
+
} catch {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The first object (breadth-first) that carries `typed`, returned as
|
|
26
|
+
* `{ typed, parseIssues, freshness?, attribution? }`; null when the result has no typed reading.
|
|
27
|
+
*/
|
|
28
|
+
export function findTypedReading(result) {
|
|
29
|
+
let level = [result];
|
|
30
|
+
for (let depth = 0; depth <= MAX_DEPTH && level.length; depth += 1) {
|
|
31
|
+
const next = [];
|
|
32
|
+
for (const raw of level) {
|
|
33
|
+
const candidate = parseJsonText(raw);
|
|
34
|
+
if (!isRecord(candidate)) continue;
|
|
35
|
+
if (Object.prototype.hasOwnProperty.call(candidate, 'typed')) {
|
|
36
|
+
return {
|
|
37
|
+
typed: candidate.typed ?? null,
|
|
38
|
+
parseIssues: Array.isArray(candidate.parseIssues) ? candidate.parseIssues : [],
|
|
39
|
+
...(isRecord(candidate.freshness) ? { freshness: candidate.freshness } : {}),
|
|
40
|
+
...(typeof candidate.attribution === 'string' && candidate.attribution ? { attribution: candidate.attribution } : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
for (const [key, value] of Object.entries(candidate)) {
|
|
44
|
+
if (!SKIPPED_KEYS.has(key)) next.push(value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
level = next;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The human-mode block printed before the full result. */
|
|
53
|
+
export function formatTypedReadingLead(reading) {
|
|
54
|
+
return `Typed reading:\n${JSON.stringify(reading, null, 2)}\nFull result:`;
|
|
55
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { requireFlag, parseJsonFlag } from '../core/args.mjs';
|
|
2
2
|
import { apiGet, apiPost } from '../core/http.mjs';
|
|
3
|
+
import { hasVerantaToken, normalizeVenueProviderId } from '../core/venueBrand.mjs';
|
|
3
4
|
export function createTelemetryCommands({ printEnvelope, resolveAgentApiKey }) {
|
|
4
5
|
function applySharedPortfolioListingFiltersToQuery(query, flags) {
|
|
5
6
|
if (flags.chainId) query.set('chainId', String(flags.chainId));
|
|
@@ -110,7 +111,10 @@ async function commandTrackAction(ctx, flags, capabilities) {
|
|
|
110
111
|
if (flags.idempotencyKey) payload.idempotencyKey = String(flags.idempotencyKey);
|
|
111
112
|
if (flags.category) payload.category = String(flags.category);
|
|
112
113
|
if (flags.source) payload.source = String(flags.source);
|
|
113
|
-
if (flags.provider)
|
|
114
|
+
if (flags.provider) {
|
|
115
|
+
// `veranta` / `veranta-vault` report under the internal `avantis` / `avantis-vault` ids.
|
|
116
|
+
payload.provider = hasVerantaToken(flags.provider) ? normalizeVenueProviderId(flags.provider) : String(flags.provider);
|
|
117
|
+
}
|
|
114
118
|
if (flags.protocol) payload.protocol = String(flags.protocol);
|
|
115
119
|
if (flags.namespace) payload.namespace = String(flags.namespace);
|
|
116
120
|
if (flags.actionType) payload.actionType = String(flags.actionType);
|
package/src/commands/venue.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { apiGet, apiPost } from '../core/http.mjs';
|
|
2
2
|
import { loadProfileConfig } from '../core/context.mjs';
|
|
3
|
+
import { normalizeVenueProviderId } from '../core/venueBrand.mjs';
|
|
3
4
|
|
|
5
|
+
// `avantis` is the internal venue id the API expects; `veranta` (the venue's new name) maps onto it.
|
|
4
6
|
const VENUES = new Set(['polymarket', 'hyperliquid', 'lighter', 'avantis']);
|
|
5
7
|
|
|
6
8
|
function resolveVenue(flags) {
|
|
7
9
|
const fromQuery = Array.isArray(flags.query) ? flags.query[0] : flags.query;
|
|
8
|
-
const
|
|
10
|
+
const requested = String(flags.venue || fromQuery || '').trim().toLowerCase();
|
|
11
|
+
const venue = normalizeVenueProviderId(requested);
|
|
9
12
|
if (!VENUES.has(venue)) {
|
|
10
|
-
throw new Error(`Unsupported venue "${
|
|
13
|
+
throw new Error(`Unsupported venue "${requested}". Use polymarket, hyperliquid, lighter, or veranta.`);
|
|
11
14
|
}
|
|
12
15
|
return venue;
|
|
13
16
|
}
|
package/src/commands/wallet.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { requireFlag } from '../core/args.mjs';
|
|
2
|
-
import { createCliError } from '../core/envelope.mjs';
|
|
2
|
+
import { createCliError, exitAfterOutput } from '../core/envelope.mjs';
|
|
3
3
|
import { loadProfileConfig } from '../core/context.mjs';
|
|
4
4
|
|
|
5
5
|
export function createWalletCommands(deps) {
|
|
@@ -47,7 +47,7 @@ async function commandWalletExecute(ctx, flags, capabilities) {
|
|
|
47
47
|
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
48
48
|
},
|
|
49
49
|
});
|
|
50
|
-
if (!result.success)
|
|
50
|
+
if (!result.success) await exitAfterOutput(1);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
|
|
@@ -76,7 +76,7 @@ async function commandWalletSign(ctx, flags, capabilities) {
|
|
|
76
76
|
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
77
77
|
},
|
|
78
78
|
});
|
|
79
|
-
if (!selfResult.success)
|
|
79
|
+
if (!selfResult.success) await exitAfterOutput(1);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "CLI Endpoint: signals",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["apps", "signals"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"apps": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"required": ["appId", "title", "signalIds"],
|
|
12
|
+
"properties": {
|
|
13
|
+
"appId": { "type": "string" },
|
|
14
|
+
"title": { "type": "string" },
|
|
15
|
+
"kind": { "type": ["string", "null"] },
|
|
16
|
+
"category": { "type": ["string", "null"] },
|
|
17
|
+
"summary": { "type": ["string", "null"] },
|
|
18
|
+
"manifestVersion": { "type": ["number", "null"] },
|
|
19
|
+
"attribution": { "type": ["string", "null"] },
|
|
20
|
+
"displayValues": { "type": ["boolean", "null"] },
|
|
21
|
+
"docsUrl": { "type": "string" },
|
|
22
|
+
"signalIds": { "type": "array", "items": { "type": "string" } }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"signals": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["id", "title", "fields", "freshness", "roles", "action"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"id": { "type": "string" },
|
|
33
|
+
"title": { "type": "string" },
|
|
34
|
+
"summary": { "type": ["string", "null"] },
|
|
35
|
+
"measures": { "type": ["string", "null"] },
|
|
36
|
+
"scope": { "type": ["string", "null"] },
|
|
37
|
+
"universe": {},
|
|
38
|
+
"fields": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"required": ["name"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"name": { "type": "string" },
|
|
45
|
+
"path": { "type": ["string", "null"] },
|
|
46
|
+
"type": { "type": ["string", "null"] },
|
|
47
|
+
"unit": { "type": ["string", "null"] },
|
|
48
|
+
"range": { "type": "array", "items": { "type": "number" } },
|
|
49
|
+
"values": { "type": "array", "items": { "type": "string" } },
|
|
50
|
+
"description": { "type": "string" }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"asOfPath": { "type": ["string", "null"] },
|
|
55
|
+
"freshness": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"properties": {
|
|
58
|
+
"cadenceSec": { "type": ["number", "null"] },
|
|
59
|
+
"freshnessSec": { "type": ["number", "null"] },
|
|
60
|
+
"horizonHours": { "type": ["number", "null"] }
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"roles": { "type": "array", "items": { "type": "string" } },
|
|
64
|
+
"rules": { "type": "array", "items": { "type": "string" } },
|
|
65
|
+
"action": { "type": "string" },
|
|
66
|
+
"dataCategory": { "type": ["string", "null"] },
|
|
67
|
+
"billingTier": { "type": ["string", "null"] },
|
|
68
|
+
"credits": { "type": ["number", "null"] },
|
|
69
|
+
"appId": { "type": ["string", "null"] },
|
|
70
|
+
"manifestVersion": { "type": ["number", "null"] },
|
|
71
|
+
"attribution": { "type": ["string", "null"] },
|
|
72
|
+
"displayValues": { "type": ["boolean", "null"] },
|
|
73
|
+
"docsUrl": { "type": "string" }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_TIMEOUT_MS } from './constants.mjs';
|
|
2
2
|
import { isReadOnlyActionName, isReadOnlyResolvedAction } from './readOnlyActions.mjs';
|
|
3
|
+
import { toLegacyVenueActionKey } from './venueBrand.mjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Default HTTP timeout for managed writes. A perps order or TP/SL update signs, broadcasts, and waits
|
|
@@ -8,6 +9,7 @@ import { isReadOnlyActionName, isReadOnlyResolvedAction } from './readOnlyAction
|
|
|
8
9
|
*/
|
|
9
10
|
const DEFAULT_WRITE_TIMEOUT_MS = 90_000;
|
|
10
11
|
|
|
12
|
+
// Keyed by the Avantis spelling; `normalizeActionId` maps Veranta names onto it.
|
|
11
13
|
const LONG_RUNNING_ACTIONS = new Set([
|
|
12
14
|
'complete_avantis_onboarding',
|
|
13
15
|
'get_avantis_setup_status',
|
|
@@ -29,17 +31,14 @@ const LONG_RUNNING_ACTIONS = new Set([
|
|
|
29
31
|
'swap',
|
|
30
32
|
'execute_optimal_swap',
|
|
31
33
|
'deposit_hyperliquid_cctp',
|
|
32
|
-
// Avantis market open/close: the API streams the batched order for up to 90s, then polls its
|
|
34
|
+
// Veranta (formerly Avantis) market open/close: the API streams the batched order for up to 90s, then polls its
|
|
33
35
|
// tracking status for up to 90s more before it answers.
|
|
34
36
|
'place_avantis_perps_order',
|
|
35
37
|
'close_avantis_position',
|
|
36
38
|
]);
|
|
37
39
|
|
|
38
40
|
function normalizeActionId(action) {
|
|
39
|
-
return
|
|
40
|
-
.trim()
|
|
41
|
-
.replace(/-/g, '_')
|
|
42
|
-
.toLowerCase();
|
|
41
|
+
return toLegacyVenueActionKey(action).replace(/-/g, '_');
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
function positiveEnvMs(...names) {
|
package/src/core/envelope.mjs
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { writeSync } from 'node:fs';
|
|
2
|
-
|
|
3
1
|
export function createCliError(code, message, payload = {}) {
|
|
4
2
|
const error = new Error(message);
|
|
5
3
|
error.payload = {
|
|
@@ -11,11 +9,52 @@ export function createCliError(code, message, payload = {}) {
|
|
|
11
9
|
return error;
|
|
12
10
|
}
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
/**
|
|
13
|
+
* `humanLead`: text printed in human mode between the status line and the data JSON (a typed
|
|
14
|
+
* reading, a signal list). `humanData: false` prints the lead instead of the data JSON, with a
|
|
15
|
+
* pointer to --json. JSON mode ignores both: the envelope stays as it was.
|
|
16
|
+
*/
|
|
17
|
+
export function printEnvelope(payload, { jsonMode = false, humanLead, humanData = true } = {}) {
|
|
18
|
+
// process.stdout.write queues the whole text. fs.writeSync(1) did not: once Node has opened
|
|
19
|
+
// process.stdout, a pipe is non-blocking and writeSync writes only what fits in the pipe buffer
|
|
20
|
+
// (64 KiB on macOS), so a large --json envelope read by a slow consumer was cut. Exits go
|
|
21
|
+
// through exitAfterOutput, which waits for this queue to drain.
|
|
22
|
+
const lead = typeof humanLead === 'string' && humanLead ? `${humanLead}\n` : '';
|
|
23
|
+
const body = lead && humanData === false ? 'Use --json for the full listing.' : JSON.stringify(payload.data || {}, null, 2);
|
|
17
24
|
const text = jsonMode
|
|
18
25
|
? `${JSON.stringify(payload, null, 2)}\n`
|
|
19
|
-
: `[${payload.success ? 'OK' : 'ERROR'}] ${payload.code}: ${payload.message}\n${
|
|
20
|
-
|
|
26
|
+
: `[${payload.success ? 'OK' : 'ERROR'}] ${payload.code}: ${payload.message}\n${lead}${body}\n`;
|
|
27
|
+
process.stdout.write(text);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** How long exitAfterOutput waits for output to drain before exiting anyway. */
|
|
31
|
+
export const DEFAULT_EXIT_DRAIN_TIMEOUT_MS = 10_000;
|
|
32
|
+
|
|
33
|
+
function exitDrainTimeoutMs() {
|
|
34
|
+
const override = Number(process.env.THIRDFY_AGENT_EXIT_DRAIN_TIMEOUT_MS);
|
|
35
|
+
return Number.isFinite(override) && override >= 0 ? override : DEFAULT_EXIT_DRAIN_TIMEOUT_MS;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Exit with `code` after stdout and stderr have written everything queued. process.exit() right
|
|
40
|
+
* after a write drops what a pipe has not taken yet. A reader that stops reading cannot hold the
|
|
41
|
+
* CLI open: after THIRDFY_AGENT_EXIT_DRAIN_TIMEOUT_MS (default 10 s) it exits with `code` anyway.
|
|
42
|
+
* The returned promise never settles, so `await exitAfterOutput(1)` (or `return
|
|
43
|
+
* exitAfterOutput(1)`) stops the caller where process.exit did.
|
|
44
|
+
*/
|
|
45
|
+
export function exitAfterOutput(code = 0) {
|
|
46
|
+
process.exitCode = code;
|
|
47
|
+
const streams = [process.stdout, process.stderr].filter((stream) => stream && !stream.destroyed && stream.writable);
|
|
48
|
+
let pending = streams.length;
|
|
49
|
+
if (!pending) process.exit(code);
|
|
50
|
+
setTimeout(() => process.exit(code), exitDrainTimeoutMs());
|
|
51
|
+
for (const stream of streams) {
|
|
52
|
+
// The callback runs once every earlier write on this stream has been handed to the OS
|
|
53
|
+
// (or failed, for example EPIPE when the reader has gone).
|
|
54
|
+
stream.write('', () => {
|
|
55
|
+
pending -= 1;
|
|
56
|
+
if (pending === 0) process.exit(code);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return new Promise(() => {});
|
|
21
60
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Veranta (formerly Avantis) naming.
|
|
3
|
+
*
|
|
4
|
+
* Avantis, the Base perps venue, rebranded to Veranta. The Thirdfy API is moving its action names
|
|
5
|
+
* from `*_avantis_*` to `*_veranta_*` (Veranta canonical, Avantis kept as aliases). Until every API
|
|
6
|
+
* deployment has the Veranta rows, the CLI has to work against both catalogs:
|
|
7
|
+
*
|
|
8
|
+
* - A Veranta action name resolves to whatever the catalog in hand has (the Avantis row on an older API).
|
|
9
|
+
* - An Avantis action name keeps working unchanged.
|
|
10
|
+
* - Hardcoded CLI lists (lanes, timeouts, address defaults) are keyed by the Avantis spelling, so they
|
|
11
|
+
* compare through `toLegacyVenueActionKey`.
|
|
12
|
+
*
|
|
13
|
+
* Provider ids `avantis` / `avantis-vault` stay the internal identifiers. `veranta` / `veranta-vault`
|
|
14
|
+
* are accepted wherever the CLI reads a provider id.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const LEGACY_TOKEN = 'avantis';
|
|
18
|
+
const BRAND_TOKEN = 'veranta';
|
|
19
|
+
|
|
20
|
+
// The token only counts as a whole word inside an action or provider id (`_`/`-` separated).
|
|
21
|
+
const BRAND_TOKEN_RE = /(^|[_-])veranta(?=[_-]|$)/gi;
|
|
22
|
+
const LEGACY_TOKEN_RE = /(^|[_-])avantis(?=[_-]|$)/gi;
|
|
23
|
+
|
|
24
|
+
/** Human-facing venue name. */
|
|
25
|
+
export const VERANTA_DISPLAY_NAME = 'Veranta';
|
|
26
|
+
|
|
27
|
+
/** True when the id carries the Veranta token (`place_veranta_perps_order`, `veranta-vault`). */
|
|
28
|
+
export function hasVerantaToken(value) {
|
|
29
|
+
BRAND_TOKEN_RE.lastIndex = 0;
|
|
30
|
+
return BRAND_TOKEN_RE.test(String(value || ''));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** True when the id carries the Avantis token (`place_avantis_perps_order`, `avantis-vault`). */
|
|
34
|
+
export function hasAvantisToken(value) {
|
|
35
|
+
LEGACY_TOKEN_RE.lastIndex = 0;
|
|
36
|
+
return LEGACY_TOKEN_RE.test(String(value || ''));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** `place_veranta_perps_order` -> `place_avantis_perps_order`. Other names pass through unchanged. */
|
|
40
|
+
export function toAvantisSpelling(value) {
|
|
41
|
+
return String(value ?? '').replace(BRAND_TOKEN_RE, `$1${LEGACY_TOKEN}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** `place_avantis_perps_order` -> `place_veranta_perps_order`. Other names pass through unchanged. */
|
|
45
|
+
export function toVerantaSpelling(value) {
|
|
46
|
+
return String(value ?? '').replace(LEGACY_TOKEN_RE, `$1${BRAND_TOKEN}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The other spelling of a venue action name, or null when the name has no Avantis/Veranta token.
|
|
51
|
+
* Used to retry catalog resolution with the spelling the API in hand still uses.
|
|
52
|
+
*/
|
|
53
|
+
export function alternateVenueActionSpelling(value) {
|
|
54
|
+
const text = String(value ?? '');
|
|
55
|
+
if (hasVerantaToken(text)) return toAvantisSpelling(text);
|
|
56
|
+
if (hasAvantisToken(text)) return toVerantaSpelling(text);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Brand-insensitive action key for hardcoded CLI lists: trimmed, lowercased, Veranta mapped to the
|
|
62
|
+
* Avantis spelling. Separators are preserved; callers still pick `_` or `-`.
|
|
63
|
+
*/
|
|
64
|
+
export function toLegacyVenueActionKey(value) {
|
|
65
|
+
return toAvantisSpelling(String(value ?? '').trim().toLowerCase());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Normalize a provider or venue id the CLI reads from flags: trimmed, lowercased, and
|
|
70
|
+
* `veranta` / `veranta-vault` mapped to the internal `avantis` / `avantis-vault`.
|
|
71
|
+
*/
|
|
72
|
+
export function normalizeVenueProviderId(value) {
|
|
73
|
+
const id = String(value ?? '').trim().toLowerCase();
|
|
74
|
+
if (!id) return id;
|
|
75
|
+
return toAvantisSpelling(id);
|
|
76
|
+
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { requireFlag } from '../../core/args.mjs';
|
|
2
2
|
import { getCachedActionsCatalog } from '../../core/context.mjs';
|
|
3
3
|
import { suggestCatalogActions } from '../validation/actionParamsGate.mjs';
|
|
4
|
+
import {
|
|
5
|
+
alternateVenueActionSpelling,
|
|
6
|
+
normalizeVenueProviderId,
|
|
7
|
+
toAvantisSpelling,
|
|
8
|
+
toLegacyVenueActionKey,
|
|
9
|
+
} from '../../core/venueBrand.mjs';
|
|
4
10
|
export function createActionSelection({ getTradingProviderHint, formatProviderAmbiguousActionHint, formatProviderUnknownActionHint }) {
|
|
5
11
|
function getActionKey(action) {
|
|
6
12
|
return String(action?.action || action?.id || action?.key || action?.name || '')
|
|
@@ -21,7 +27,8 @@ function formatActionList(items) {
|
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
function normalizeProviderId(value) {
|
|
24
|
-
|
|
30
|
+
// Lowercased id; `veranta` / `veranta-vault` map to the internal `avantis` / `avantis-vault`.
|
|
31
|
+
return normalizeVenueProviderId(value || '');
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
function rowActionKey(action) {
|
|
@@ -46,7 +53,8 @@ function canonicalizeCatalogActionKey(value) {
|
|
|
46
53
|
function inferActionProvider(action) {
|
|
47
54
|
// API catalog metadata (`providerId`, `domain`, `category`) is the source of truth.
|
|
48
55
|
// This fallback only keeps older catalog rows / older API deployments discoverable.
|
|
49
|
-
|
|
56
|
+
// Veranta action names infer like their Avantis spelling (`avantis` / `avantis-vault`).
|
|
57
|
+
const key = toLegacyVenueActionKey(canonicalActionKey(action));
|
|
50
58
|
const knownMap = {
|
|
51
59
|
swap: 'trading',
|
|
52
60
|
'execute-optimal-swap': 'trading',
|
|
@@ -263,7 +271,8 @@ function isSpotTradingCatalogAction(action) {
|
|
|
263
271
|
}
|
|
264
272
|
|
|
265
273
|
function isProviderActionKey(action, provider) {
|
|
266
|
-
|
|
274
|
+
// Brand-insensitive: `place_veranta_perps_order` matches provider `avantis`.
|
|
275
|
+
const actionKey = toLegacyVenueActionKey(rowActionKey(action));
|
|
267
276
|
const normalizedProvider = normalizeProviderId(provider).replace(/-/g, '_');
|
|
268
277
|
if (!actionKey || !normalizedProvider) return false;
|
|
269
278
|
return (
|
|
@@ -283,6 +292,7 @@ function actionMatchesProvider(action, requestedProvider, allActions) {
|
|
|
283
292
|
if (requestedProvider === 'hyperliquid') return isProviderActionKey(action, 'hyperliquid');
|
|
284
293
|
if (requestedProvider === 'lighter') return isProviderActionKey(action, 'lighter');
|
|
285
294
|
if (requestedProvider === 'avantis') {
|
|
295
|
+
// Veranta (formerly Avantis) perps; `--provider veranta` normalizes to `avantis` before this runs.
|
|
286
296
|
return isProviderActionKey(action, 'avantis') && !actionKey.includes('vault');
|
|
287
297
|
}
|
|
288
298
|
if (requestedProvider === 'polymarket') return isProviderActionKey(action, 'polymarket');
|
|
@@ -338,6 +348,16 @@ function applyActionFilters(actions, flags) {
|
|
|
338
348
|
const expectedProvider = normalizeProviderId(flags.provider);
|
|
339
349
|
filtered = filtered.filter((a) => actionMatchesProvider(a, expectedProvider, filtered));
|
|
340
350
|
}
|
|
351
|
+
if (flags.dataCategory) {
|
|
352
|
+
// Catalog `dataCategory` (Thirdfy API data contract); rows without one are not data reads.
|
|
353
|
+
const categories = new Set(
|
|
354
|
+
String(flags.dataCategory)
|
|
355
|
+
.split(',')
|
|
356
|
+
.map((value) => value.trim().toLowerCase())
|
|
357
|
+
.filter(Boolean),
|
|
358
|
+
);
|
|
359
|
+
filtered = filtered.filter((a) => categories.has(String(a?.dataCategory || '').trim().toLowerCase()));
|
|
360
|
+
}
|
|
341
361
|
return filtered;
|
|
342
362
|
}
|
|
343
363
|
|
|
@@ -351,20 +371,12 @@ async function resolveActionSelection(ctx, flags) {
|
|
|
351
371
|
return resolveActionFromRows(requestedAction, actions, flags);
|
|
352
372
|
}
|
|
353
373
|
|
|
354
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Exact catalog match for one spelling: canonical overrides, action id, `_`/`-` canonical id, then
|
|
376
|
+
* alias. Returns null when nothing matches; throws on ambiguity.
|
|
377
|
+
*/
|
|
378
|
+
function matchCatalogEntry(requestedAction, keyed, flags = {}) {
|
|
355
379
|
const requestedLower = String(requestedAction || '').toLowerCase();
|
|
356
|
-
const keyed = actions
|
|
357
|
-
.map((action) => ({
|
|
358
|
-
key: getActionKey(action),
|
|
359
|
-
aliases: getActionAliases(action),
|
|
360
|
-
action,
|
|
361
|
-
}))
|
|
362
|
-
.filter((entry) => entry.key);
|
|
363
|
-
|
|
364
|
-
if (!keyed.length) {
|
|
365
|
-
return { requestedAction, resolvedAction: requestedAction, resolvedActionMeta: null, suggestions: [] };
|
|
366
|
-
}
|
|
367
|
-
|
|
368
380
|
const canonicalAliasOverrides = {
|
|
369
381
|
get_vaults_fyi_vaults: 'get-vaults-fyi-vaults',
|
|
370
382
|
get_vaults_fyi_opportunities: 'get-vaults-fyi-vaults',
|
|
@@ -446,11 +458,47 @@ function resolveActionFromRows(requestedAction, actions, flags = {}) {
|
|
|
446
458
|
)}.${formatProviderAmbiguousActionHint(providerHint)}`
|
|
447
459
|
);
|
|
448
460
|
}
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function resolveActionFromRows(requestedAction, actions, flags = {}) {
|
|
465
|
+
const requestedLower = String(requestedAction || '').toLowerCase();
|
|
466
|
+
const keyed = actions
|
|
467
|
+
.map((action) => ({
|
|
468
|
+
key: getActionKey(action),
|
|
469
|
+
aliases: getActionAliases(action),
|
|
470
|
+
action,
|
|
471
|
+
}))
|
|
472
|
+
.filter((entry) => entry.key);
|
|
473
|
+
|
|
474
|
+
if (!keyed.length) {
|
|
475
|
+
// No catalog to check against: send the Avantis spelling of a Veranta name, which every API
|
|
476
|
+
// deployment accepts (older ones only know `*_avantis_*`; newer ones keep it as an alias).
|
|
477
|
+
return {
|
|
478
|
+
requestedAction,
|
|
479
|
+
resolvedAction: alternateVenueActionSpelling(requestedAction) ? toAvantisSpelling(requestedAction) : requestedAction,
|
|
480
|
+
resolvedActionMeta: null,
|
|
481
|
+
suggestions: [],
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const direct = matchCatalogEntry(requestedAction, keyed, flags);
|
|
486
|
+
if (direct) return direct;
|
|
487
|
+
|
|
488
|
+
// Veranta (formerly Avantis): retry with the other spelling so `place_veranta_perps_order` finds the
|
|
489
|
+
// `place_avantis_perps_order` row on an API that has not shipped Veranta names, and the reverse.
|
|
490
|
+
const alternate = alternateVenueActionSpelling(requestedAction);
|
|
491
|
+
if (alternate) {
|
|
492
|
+
const viaAlternate = matchCatalogEntry(alternate, keyed, flags);
|
|
493
|
+
if (viaAlternate) return { ...viaAlternate, requestedAction };
|
|
494
|
+
}
|
|
495
|
+
const alternateLower = alternate ? alternate.toLowerCase() : null;
|
|
449
496
|
|
|
497
|
+
const fuzzyNeedles = alternateLower ? [requestedLower, alternateLower] : [requestedLower];
|
|
450
498
|
const fuzzy = keyed.filter(
|
|
451
499
|
(entry) =>
|
|
452
|
-
entry.key.toLowerCase().includes(
|
|
453
|
-
entry.aliases.some((a) => a.toLowerCase().includes(
|
|
500
|
+
fuzzyNeedles.some((needle) => entry.key.toLowerCase().includes(needle)) ||
|
|
501
|
+
entry.aliases.some((a) => fuzzyNeedles.some((needle) => a.toLowerCase().includes(needle)))
|
|
454
502
|
);
|
|
455
503
|
if (fuzzy.length === 1) {
|
|
456
504
|
return { requestedAction, resolvedAction: fuzzy[0].key, resolvedActionMeta: fuzzy[0].action || null };
|
|
@@ -3,6 +3,7 @@ import { createHash } from 'node:crypto';
|
|
|
3
3
|
import { CLI_MANIFEST } from '../../cli/manifest.mjs';
|
|
4
4
|
import { validateParamsSchema, sanitizeParamsForSchemaValidation } from '../validation/paramsSchema.mjs';
|
|
5
5
|
import { VENUE_WRITE_ACTION_FIXTURES } from './venueActionSchemas.mjs';
|
|
6
|
+
import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
|
|
6
7
|
|
|
7
8
|
const require = createRequire(import.meta.url);
|
|
8
9
|
|
|
@@ -23,7 +24,8 @@ const AUTHORITY_INPUT_CHECKS = [
|
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
function normalizeActionKey(action) {
|
|
26
|
-
|
|
27
|
+
// Brand-insensitive: a live `place_veranta_perps_order` row replaces the `place_avantis_perps_order` fixture.
|
|
28
|
+
return toLegacyVenueActionKey(action).replace(/_/g, '-');
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
function isWriteAction(action) {
|
|
@@ -218,8 +218,10 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
|
|
|
218
218
|
validSample: { orderId: '0xabc' },
|
|
219
219
|
},
|
|
220
220
|
{
|
|
221
|
+
// Veranta (formerly Avantis). Fixture ids keep the Avantis spelling so the default schema digest
|
|
222
|
+
// is unchanged; the Veranta names are aliases.
|
|
221
223
|
action: 'place_avantis_perps_order',
|
|
222
|
-
aliases: ['place-avantis-perps-order'],
|
|
224
|
+
aliases: ['place-avantis-perps-order', 'place_veranta_perps_order', 'place-veranta-perps-order'],
|
|
223
225
|
provider: 'avantis',
|
|
224
226
|
authorityInputs: ['agentApiKey', 'estimatedAmountUsd'],
|
|
225
227
|
paramsSchema: {
|
|
@@ -249,7 +251,7 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
|
|
|
249
251
|
},
|
|
250
252
|
{
|
|
251
253
|
action: 'cancel_avantis_perps_order',
|
|
252
|
-
aliases: ['cancel-avantis-perps-order'],
|
|
254
|
+
aliases: ['cancel-avantis-perps-order', 'cancel_veranta_perps_order', 'cancel-veranta-perps-order'],
|
|
253
255
|
provider: 'avantis',
|
|
254
256
|
authorityInputs: ['agentApiKey'],
|
|
255
257
|
paramsSchema: {
|
|
@@ -267,7 +269,7 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
|
|
|
267
269
|
},
|
|
268
270
|
{
|
|
269
271
|
action: 'close_avantis_position',
|
|
270
|
-
aliases: ['close-avantis-position'],
|
|
272
|
+
aliases: ['close-avantis-position', 'close_veranta_position', 'close-veranta-position'],
|
|
271
273
|
provider: 'avantis',
|
|
272
274
|
authorityInputs: ['agentApiKey', 'estimatedAmountUsd'],
|
|
273
275
|
paramsSchema: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createCliError } from '../../core/envelope.mjs';
|
|
2
|
+
import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
|
|
2
3
|
export function createExecutionLanes() {
|
|
3
4
|
const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
|
|
4
5
|
'place_hyperliquid_perps_order',
|
|
@@ -29,6 +30,7 @@ const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
|
|
|
29
30
|
'condor-executor-create',
|
|
30
31
|
'condor_executor_stop',
|
|
31
32
|
'condor-executor-stop',
|
|
33
|
+
// Veranta (formerly Avantis): keyed by the Avantis spelling; Veranta names map onto it below.
|
|
32
34
|
'place_avantis_perps_order',
|
|
33
35
|
'place-avantis-perps-order',
|
|
34
36
|
'cancel_avantis_perps_order',
|
|
@@ -57,11 +59,11 @@ const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
|
|
|
57
59
|
'cancel-avantis-mm-order',
|
|
58
60
|
]);
|
|
59
61
|
function enforceOffchainVenueLaneCompatibility(action, runMode) {
|
|
60
|
-
const normalizedAction =
|
|
62
|
+
const normalizedAction = toLegacyVenueActionKey(action);
|
|
61
63
|
if (runMode !== 'self' || !OFFCHAIN_VENUE_ORDER_ACTIONS.has(normalizedAction)) return;
|
|
62
64
|
throw createCliError(
|
|
63
65
|
'OFFCHAIN_VENUE_ORDER_SELF_UNSUPPORTED',
|
|
64
|
-
'Polymarket CLOB, Hyperliquid perps, Lighter perps,
|
|
66
|
+
'Polymarket CLOB, Hyperliquid perps, Lighter perps, Veranta perps, Hummingbot executor, and Condor executor actions are offchain signed API actions, not EVM build-tx actions. Use self for setup/funding signatures, then run venue orders with --run-mode thirdfy, --run-mode hybrid, or --run-mode agent_wallet.',
|
|
65
67
|
{
|
|
66
68
|
recommendedRunModes: ['thirdfy', 'hybrid', 'agent_wallet'],
|
|
67
69
|
setupOnlyRunMode: 'self',
|
|
@@ -4,6 +4,7 @@ import { resolveExecutionContextFromEnv } from '../../core/executionContext.mjs'
|
|
|
4
4
|
import { requireFlag, parseJsonFlag } from '../../core/args.mjs';
|
|
5
5
|
import { loadProfileConfig } from '../../core/context.mjs';
|
|
6
6
|
import { createCliError } from '../../core/envelope.mjs';
|
|
7
|
+
import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
|
|
7
8
|
import { normalizeRunMode, normalizeManagedRunMode, normalizeHybridWalletMode } from '../../core/runMode.mjs';
|
|
8
9
|
import { assertHostedRuntimePolymarketWriteIdempotency } from './hostedRuntimeWriteGate.mjs';
|
|
9
10
|
import { resolveTokenInForFunding } from '../../core/fundingTokenIn.mjs';
|
|
@@ -29,10 +30,8 @@ export function mergeConfirmWritesIntoParams(params, flags) {
|
|
|
29
30
|
export function resolveExecuteEstimatedAmountUsd(action, flags) {
|
|
30
31
|
const fromFlags = Number(flags?.estimatedAmountUsd);
|
|
31
32
|
if (Number.isFinite(fromFlags) && fromFlags > 0) return fromFlags;
|
|
32
|
-
const key =
|
|
33
|
-
|
|
34
|
-
.toLowerCase()
|
|
35
|
-
.replace(/-/g, '_');
|
|
33
|
+
const key = toLegacyVenueActionKey(action).replace(/-/g, '_');
|
|
34
|
+
// Veranta onboarding (either spelling).
|
|
36
35
|
if (key === 'complete_avantis_onboarding') return 1;
|
|
37
36
|
return undefined;
|
|
38
37
|
}
|
|
@@ -527,7 +527,7 @@ async function executeManagedWalletRun(ctx, flags, resolved, options) {
|
|
|
527
527
|
// (often as a JSON string under data.result). Reads skip funding gates; routing still fails closed.
|
|
528
528
|
const isRead = isReadOnlyAction(resolved);
|
|
529
529
|
const skipPreflight = Boolean(options?.skipPreflight);
|
|
530
|
-
// A seeded preflight (Hyperliquid/Lighter/
|
|
530
|
+
// A seeded preflight (Hyperliquid/Lighter/Veranta address defaults) already ran; reuse it.
|
|
531
531
|
const readFastPath =
|
|
532
532
|
!skipPreflight && !options?.managedPreflight && shouldUseManagedReadFastPath(flags, resolved, options);
|
|
533
533
|
let preflight = readFastPath ? READ_FAST_PATH_PREFLIGHT : null;
|
package/src/runtime/handlers.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { createDelegationCommands } from '../commands/delegation.mjs';
|
|
|
11
11
|
import { createConsoleMandateCommands } from '../commands/consoleMandate.mjs';
|
|
12
12
|
import { createBootstrapCommands } from '../commands/bootstrap.mjs';
|
|
13
13
|
import { createDiscoveryCommands } from '../commands/discovery.mjs';
|
|
14
|
+
import { createSignalsCommands } from '../commands/signals/command.mjs';
|
|
14
15
|
import { createCreditsCommands } from '../commands/credits.mjs';
|
|
15
16
|
import { createChatCommands } from '../commands/chat.mjs';
|
|
16
17
|
import { createBrainCommands } from '../commands/brain.mjs';
|
|
@@ -67,6 +68,8 @@ export function createRuntimeHandlers(deps) {
|
|
|
67
68
|
resolveAgentApiKey,
|
|
68
69
|
});
|
|
69
70
|
|
|
71
|
+
const __signals = createSignalsCommands({ printEnvelope });
|
|
72
|
+
|
|
70
73
|
const __credits = createCreditsCommands({
|
|
71
74
|
printEnvelope,
|
|
72
75
|
getAuthToken,
|
|
@@ -232,6 +235,7 @@ export function createRuntimeHandlers(deps) {
|
|
|
232
235
|
commandManagedSetup: __doctor.commandManagedSetup,
|
|
233
236
|
commandCatalogsList: __discovery.commandCatalogsList,
|
|
234
237
|
commandActions: __discovery.commandActions,
|
|
238
|
+
commandSignals: __signals.commandSignals,
|
|
235
239
|
commandCreditsBalance: __discovery.commandCreditsBalance,
|
|
236
240
|
commandCreditsTransactions: __credits.commandCreditsTransactions,
|
|
237
241
|
commandCreditsUsage: __credits.commandCreditsUsage,
|