@ai-digital-factory/lmt-cli 0.1.4 → 0.1.6
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 +47 -4
- package/dist/index.js +289 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,48 @@ lmt candle-export NSE:IDEA --date 2026-10-23 --env dev --profile runner --timeou
|
|
|
30
30
|
|
|
31
31
|
Omitting `--date` asks Broker for the exchange's latest completed trading session, accounting for holidays and Finalization Grace. The stdout JSON `candleExport` contains the immutable Hosted URL, filename, resolved identity/date, source availability, reference-fetch metadata, and comparison counts. Missing data or Zerodha authentication is explained in the workbook. Export does not start Zerodha login or token recovery. Local/dev only; see `lmt --help` for exit conventions.
|
|
32
32
|
|
|
33
|
+
## Price Cross pairs for Runner
|
|
34
|
+
|
|
35
|
+
Price Cross runs in Paper mode only. These commands reach the owning Broker through Gate with the saved Operator Session. They never save Settings, so the Settings Version does not change. They never place Orders, and the CLI has no strategy store or Kite client of its own.
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
lmt pair list --env dev --profile runner
|
|
39
|
+
lmt pair assign --ce 1 --pe 3 --lots 2 --env dev --profile runner
|
|
40
|
+
lmt pair report PAIR_ID --output ./pair.xlsx --env dev --profile runner
|
|
41
|
+
lmt pair report PAIR_ID --publish --env dev --profile runner
|
|
42
|
+
lmt pair unpair PAIR_ID --confirm --env dev --profile runner
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
| Command | Success JSON (one line on stdout) |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `pair list` | `pairs.active` and `pairs.past`. Each pair has its globally unique `pairId`, `ceRow`/`peRow`, frozen `ceInstrument`/`peInstrument`, `pairLots`, `frozenLotSize`, `quantity`, `assignedAt`, `unpairedAt` (past pairs only) and `pnl` (exact per-leg, pair and daily Decision P&L). Ended pairs stay listed after their rows are reused. |
|
|
48
|
+
| `pair assign` | `reason: "pair_assigned"` and `pair`, the Broker's summary with its identity, roles, frozen lot size and quantity. |
|
|
49
|
+
| `pair unpair --confirm` | `reason: "unpaired"` with `unpair.requestedAt`. When a paper position was closed, `unpair.exit` holds the exit decision, including `requestedAt` and `fills[].priceMinute`. When the pair was flat there is no `exit`. A repeat gives `reason: "already_unpaired"`. |
|
|
50
|
+
| `pair report` | Downloads the lifetime workbook unchanged. `report.path`, `report.bytes`, `report.sha256` and `report.decisionCount` describe it. Without `--output`, the file is saved under the server filename in the current directory and an existing file is never overwritten. |
|
|
51
|
+
| `pair report --publish` | `report.url` is a new immutable Hosted URL. It matches the `exportUrl` field on the Broker's `price_cross_report_snapshot_published` log. Publishing again gives a new URL and leaves the old one unchanged. |
|
|
52
|
+
|
|
53
|
+
Exit codes: 0 success; 1 refusal or uncertain; 2 invalid invocation (including `--env prod`); 3 Operator Login required.
|
|
54
|
+
|
|
55
|
+
**Refusals.** A refusal is `outcome: "fail"` with exit 1, and `reason` and `error.code` both hold the Broker's code, unchanged. The codes are `invalid_lots` (Pair Lots outside 1 through the configured maximum, default 10), `same_type`, `already_paired`, `order_in_flight`, `real_position_open`, `row_unavailable`, `row_empty`, the `*_mismatch` codes, `flag_disabled`, `store_unavailable` and, for reports, `pair_not_found`. Each assign refusal matches the `fields_reason` on its `pair_refused` log. `pair unpair` without `--confirm` is refused locally with exit 2 and sends nothing.
|
|
56
|
+
|
|
57
|
+
**Uncertain outcome.** If `pair assign`, `pair unpair` or `pair report --publish` is submitted but no answer arrives before `--timeout` (or Gate or Broker fails with a 5xx other than `store_unavailable`), the result is `outcome: "uncertain"`, `reason: "submission_uncertain"`, exit 1. The `recovery` field says to read `pair list`. The CLI never repeats the submission. Run `lmt pair list` to see whether the change happened before submitting again.
|
|
58
|
+
|
|
59
|
+
### Dev-safe QA sequence
|
|
60
|
+
|
|
61
|
+
Run this outside 09:15–15:30 IST, using a same-expiry CE row and PE row already applied on dev (rows 1 and 3 here).
|
|
62
|
+
|
|
63
|
+
1. `lmt settings read …` and note `settingsVersion`.
|
|
64
|
+
2. `lmt pair assign --ce 1 --pe 3 --lots 2 …`: exit 0. Record `pair.pairId`, `frozenLotSize` and `quantity`.
|
|
65
|
+
3. Check the refusals: `--lots 0` and `--lots 11` give `invalid_lots`, re-running step 2 gives `already_paired`, and `--ce 1 --pe 1` gives `same_type`. Each exits 1.
|
|
66
|
+
4. `lmt pair list …`: the pair is in `pairs.active`.
|
|
67
|
+
5. `lmt pair report PAIR_ID --output ./pair.xlsx …`, then `lmt pair report PAIR_ID --publish …` twice. The two URLs differ, and each matches a `price_cross_report_snapshot_published` row.
|
|
68
|
+
6. `lmt pair unpair PAIR_ID …` without `--confirm`: exit 2, and no `pair_unpaired` row is logged.
|
|
69
|
+
7. `lmt pair unpair PAIR_ID --confirm …`: `unpaired`, with exit details or a flat removal. Repeat it: `already_unpaired`, and there is still only one `pair_unpaired` row.
|
|
70
|
+
8. Uncertain: `lmt pair unpair PAIR_ID --confirm --timeout 1ms …` on a fresh pair either fails before submission or gives `uncertain` with exit 1. In both cases run `lmt pair list` to reconcile. The CI test `a submitted assign or unpair that outlives the timeout is uncertain, nonzero and never repeated` proves that exactly one submission is made.
|
|
71
|
+
9. `lmt pair list …`: the pair is in `pairs.past`. `lmt settings read …` shows the same `settingsVersion` as step 1.
|
|
72
|
+
|
|
73
|
+
Automated coverage: `pnpm --filter @ai-digital-factory/lmt-cli test` runs `src/pair-commands.test.ts` (stubbed Gate) and `integration/price-cross-pairs.test.ts`. The integration test uses real Gate and Broker, a temporary Price Cross Store and a recording order client that must see zero calls. It also covers flag-off: existing pairs are listed and can be Unpaired, and a new assign is refused with `flag_disabled`.
|
|
74
|
+
|
|
33
75
|
## Quick start: interactive dev login
|
|
34
76
|
|
|
35
77
|
1. Obtain your Operator Login username and password. For headless login and Operator Session validation, also obtain an SMS read credential authorized to read `live-market-terminal-operator-directory` in the selected Gate scope from your administrator.
|
|
@@ -51,7 +93,7 @@ Omitting `--date` asks Broker for the exchange's latest completed trading sessio
|
|
|
51
93
|
|
|
52
94
|
Press Enter to submit each prompt. At the Operator password prompt, Escape returns to the username; Ctrl+U clears either field. Passwords remain masked. Press Ctrl+C to exit the terminal; use the logout command or menu action when you want to revoke the Operator Session.
|
|
53
95
|
|
|
54
|
-
Interactive mode defaults to `--env
|
|
96
|
+
Interactive mode defaults to `--env dev --profile default` when these options are omitted. A bare `live-market-terminal` invocation (no subcommand) also starts interactive mode with those defaults. Keep the same environment and profile across commands to reuse a session:
|
|
55
97
|
|
|
56
98
|
```sh
|
|
57
99
|
lmt status --env dev --profile my-dev-session
|
|
@@ -242,8 +284,9 @@ lmt token read --env dev --profile runner
|
|
|
242
284
|
lmt token replace --stdin --env dev --profile runner < /path/to/protected-token-request.json
|
|
243
285
|
```
|
|
244
286
|
|
|
245
|
-
The token request contains
|
|
246
|
-
|
|
247
|
-
|
|
287
|
+
The token request contains expected (the metadata returned by token read), optionally
|
|
288
|
+
accessToken for a new value, verifyCurrent: true to validate the saved token without
|
|
289
|
+
reading its value, and continueReplacement: true after reviewing an active-trading
|
|
290
|
+
confirmation. A same-token submission verifies the submitter without reinstalling the token. Ordinary
|
|
248
291
|
Settings Save never replaces access tokens. Token values and saved credentials do not
|
|
249
292
|
appear in command results.
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,30 @@ var __export = (target, all) => {
|
|
|
14
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// ../../packages/contracts/src/price-cross.ts
|
|
18
|
+
var init_price_cross = __esm({
|
|
19
|
+
"../../packages/contracts/src/price-cross.ts"() {
|
|
20
|
+
"use strict";
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ../../packages/contracts/src/price-cross-time.ts
|
|
25
|
+
var IST_OFFSET_MS;
|
|
26
|
+
var init_price_cross_time = __esm({
|
|
27
|
+
"../../packages/contracts/src/price-cross-time.ts"() {
|
|
28
|
+
"use strict";
|
|
29
|
+
IST_OFFSET_MS = 330 * 6e4;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// ../../packages/contracts/src/price-cross-live.ts
|
|
34
|
+
var init_price_cross_live = __esm({
|
|
35
|
+
"../../packages/contracts/src/price-cross-live.ts"() {
|
|
36
|
+
"use strict";
|
|
37
|
+
init_price_cross_time();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
17
41
|
// ../../packages/contracts/src/index.ts
|
|
18
42
|
function isOperatorMarketProtection(value) {
|
|
19
43
|
return typeof value === "number" && Number.isInteger(value) && value >= 2 && value <= 90;
|
|
@@ -28,6 +52,9 @@ var OPERATOR_SESSION_EXPIRY_TRACE_GRACE_SECONDS, OPERATOR_SESSION_EXPIRY_WARNING
|
|
|
28
52
|
var init_src = __esm({
|
|
29
53
|
"../../packages/contracts/src/index.ts"() {
|
|
30
54
|
"use strict";
|
|
55
|
+
init_price_cross();
|
|
56
|
+
init_price_cross_live();
|
|
57
|
+
init_price_cross_time();
|
|
31
58
|
OPERATOR_SESSION_EXPIRY_TRACE_GRACE_SECONDS = 24 * 60 * 60;
|
|
32
59
|
OPERATOR_SESSION_EXPIRY_WARNING_MS = 24 * 60 * 60 * 1e3;
|
|
33
60
|
DEFAULT_OPERATOR_SESSION_IDLE_HINT_SECONDS = 7 * 24 * 60 * 60;
|
|
@@ -82,7 +109,13 @@ var init_src = __esm({
|
|
|
82
109
|
placeMarketWatchOrder: "/api/market-watch/orders/place",
|
|
83
110
|
retryMarketWatchPlace: "/api/market-watch/orders/place-retry",
|
|
84
111
|
exitMarketWatchOrder: "/api/market-watch/orders/exit",
|
|
85
|
-
cancelMarketWatchOrder: "/api/market-watch/orders/cancel"
|
|
112
|
+
cancelMarketWatchOrder: "/api/market-watch/orders/cancel",
|
|
113
|
+
assignPriceCrossPair: "/api/price-cross/pairs/assign",
|
|
114
|
+
unpairPriceCrossPair: "/api/price-cross/pairs/unpair",
|
|
115
|
+
priceCrossReport: "/api/price-cross/report",
|
|
116
|
+
priceCrossReportShare: "/api/price-cross/report/share",
|
|
117
|
+
priceCrossPairs: "/api/price-cross/pairs",
|
|
118
|
+
priceCrossHistory: "/api/price-cross/history"
|
|
86
119
|
};
|
|
87
120
|
}
|
|
88
121
|
});
|
|
@@ -248,17 +281,17 @@ function createGateApi(origin, fetchImpl = fetch, options = {}) {
|
|
|
248
281
|
const timeout = options.timeoutMs && options.timeoutMs > 0 ? AbortSignal.timeout(options.timeoutMs) : void 0;
|
|
249
282
|
return combineSignals2(options.signal, actionDeadlineSignal, explicitSignal, timeout);
|
|
250
283
|
}
|
|
251
|
-
async function rejectAbsentSession(response,
|
|
252
|
-
if (response.status !== 401 ||
|
|
284
|
+
async function rejectAbsentSession(response, path5) {
|
|
285
|
+
if (response.status !== 401 || path5 === API_PATHS.unlockOperatorSession || path5 === API_PATHS.logoutUnauthenticatedOperator) return;
|
|
253
286
|
const body = await readJson(response.clone());
|
|
254
287
|
if (body && typeof body === "object" && "error" in body && body.error === "operator_session_absent") {
|
|
255
288
|
delete cookies.operatorSessionCookie;
|
|
256
289
|
throw new OperatorSessionRejectedError(rejectionReason(body));
|
|
257
290
|
}
|
|
258
291
|
}
|
|
259
|
-
async function postJson(
|
|
292
|
+
async function postJson(path5, body, explicitSignal, secrets = []) {
|
|
260
293
|
const signal = actionSignal(explicitSignal);
|
|
261
|
-
const url = `${origin}${
|
|
294
|
+
const url = `${origin}${path5}`;
|
|
262
295
|
const response = await requestOrThrow(
|
|
263
296
|
() => fetchImpl(url, {
|
|
264
297
|
method: "POST",
|
|
@@ -272,12 +305,12 @@ function createGateApi(origin, fetchImpl = fetch, options = {}) {
|
|
|
272
305
|
[...sessionSecrets(), ...otpValues, ...secrets]
|
|
273
306
|
);
|
|
274
307
|
rememberCookies(response);
|
|
275
|
-
await rejectAbsentSession(response,
|
|
308
|
+
await rejectAbsentSession(response, path5);
|
|
276
309
|
return response;
|
|
277
310
|
}
|
|
278
|
-
async function getJson(
|
|
311
|
+
async function getJson(path5, explicitSignal, extraHeaders) {
|
|
279
312
|
const signal = actionSignal(explicitSignal);
|
|
280
|
-
const url = `${origin}${
|
|
313
|
+
const url = `${origin}${path5}`;
|
|
281
314
|
const response = await requestOrThrow(
|
|
282
315
|
() => fetchImpl(url, {
|
|
283
316
|
method: "GET",
|
|
@@ -291,13 +324,13 @@ function createGateApi(origin, fetchImpl = fetch, options = {}) {
|
|
|
291
324
|
[...sessionSecrets(), ...otpValues]
|
|
292
325
|
);
|
|
293
326
|
rememberCookies(response);
|
|
294
|
-
await rejectAbsentSession(response,
|
|
327
|
+
await rejectAbsentSession(response, path5);
|
|
295
328
|
return response;
|
|
296
329
|
}
|
|
297
|
-
async function settingsWrite(
|
|
330
|
+
async function settingsWrite(path5, body) {
|
|
298
331
|
let response;
|
|
299
332
|
try {
|
|
300
|
-
response = await fetchImpl(origin +
|
|
333
|
+
response = await fetchImpl(origin + path5, {
|
|
301
334
|
method: "PUT",
|
|
302
335
|
headers: headers({ "content-type": "application/json", accept: "application/json" }),
|
|
303
336
|
body: JSON.stringify(body),
|
|
@@ -307,7 +340,7 @@ function createGateApi(origin, fetchImpl = fetch, options = {}) {
|
|
|
307
340
|
throw new SettingsActionError("submission_uncertain", true);
|
|
308
341
|
}
|
|
309
342
|
rememberCookies(response);
|
|
310
|
-
await rejectAbsentSession(response,
|
|
343
|
+
await rejectAbsentSession(response, path5);
|
|
311
344
|
if (!response.ok) {
|
|
312
345
|
const result = await readJson(response).catch(() => void 0);
|
|
313
346
|
const codes = [
|
|
@@ -336,10 +369,46 @@ function createGateApi(origin, fetchImpl = fetch, options = {}) {
|
|
|
336
369
|
"settings_recovery_required"
|
|
337
370
|
];
|
|
338
371
|
const code = typeof result?.error === "string" && codes.includes(result.error) ? result.error : "operation_rejected";
|
|
339
|
-
|
|
372
|
+
const serverMessage = typeof result?.message === "string" && result.message.trim() ? result.message.trim() : void 0;
|
|
373
|
+
const relayMessage = serverMessage && (code === "token_rejected" || code === "validation_unavailable" || code === "invalid_token_request") ? serverMessage : void 0;
|
|
374
|
+
throw new SettingsActionError(code, code === "settings_save_failed" || response.status >= 500, relayMessage);
|
|
340
375
|
}
|
|
341
376
|
return response;
|
|
342
377
|
}
|
|
378
|
+
async function priceCrossMutation(path5, body, accept) {
|
|
379
|
+
let response;
|
|
380
|
+
try {
|
|
381
|
+
response = await fetchImpl(origin + path5, {
|
|
382
|
+
method: "POST",
|
|
383
|
+
headers: headers({ "content-type": "application/json", accept: "application/json" }),
|
|
384
|
+
body: JSON.stringify(body),
|
|
385
|
+
signal: actionSignal()
|
|
386
|
+
});
|
|
387
|
+
} catch {
|
|
388
|
+
throw new PriceCrossActionError("submission_uncertain", true);
|
|
389
|
+
}
|
|
390
|
+
rememberCookies(response);
|
|
391
|
+
await rejectAbsentSession(response, path5);
|
|
392
|
+
const parsed = await readJson(response);
|
|
393
|
+
if (response.ok) {
|
|
394
|
+
if (accept(parsed)) return parsed;
|
|
395
|
+
throw new PriceCrossActionError("submission_uncertain", true);
|
|
396
|
+
}
|
|
397
|
+
const refusal = priceCrossRefusal(parsed, response.status);
|
|
398
|
+
if (response.status >= 500 && refusal.code !== "store_unavailable") throw new PriceCrossActionError("submission_uncertain", true);
|
|
399
|
+
throw refusal;
|
|
400
|
+
}
|
|
401
|
+
function priceCrossRefusal(body, status) {
|
|
402
|
+
const result = body;
|
|
403
|
+
const code = typeof result?.error === "string" && /^[a-z][a-z0-9_]{0,63}$/.test(result.error) ? result.error : `http_${status}`;
|
|
404
|
+
const message = typeof result?.message === "string" && result.message.trim() ? redactSecrets(result.message.trim(), sessionSecrets()) : void 0;
|
|
405
|
+
return new PriceCrossActionError(code, false, message);
|
|
406
|
+
}
|
|
407
|
+
async function priceCrossRead(path5, body) {
|
|
408
|
+
const response = body === void 0 ? await getJson(path5) : await postJson(path5, body);
|
|
409
|
+
if (!response.ok) throw priceCrossRefusal(await readJson(response), response.status);
|
|
410
|
+
return response;
|
|
411
|
+
}
|
|
343
412
|
function ordinarySettings(body) {
|
|
344
413
|
if (!body || !Number.isSafeInteger(body.settingsVersion) || !Number.isInteger(body.rowCount) || body.rowCount < 1 || body.rowCount > 20 || !isOperatorMarketProtection(body.marketProtection) || !Array.isArray(body.entryCategories)) {
|
|
345
414
|
throw new SettingsActionError("invalid_response");
|
|
@@ -387,6 +456,28 @@ function createGateApi(origin, fetchImpl = fetch, options = {}) {
|
|
|
387
456
|
}
|
|
388
457
|
return body;
|
|
389
458
|
},
|
|
459
|
+
async listPriceCrossPairs() {
|
|
460
|
+
const body = await readJson(await priceCrossRead(API_PATHS.priceCrossPairs));
|
|
461
|
+
if (!body || !Array.isArray(body.active) || !Array.isArray(body.past)) throw new PriceCrossActionError("invalid_response");
|
|
462
|
+
return { active: body.active, past: body.past };
|
|
463
|
+
},
|
|
464
|
+
assignPriceCrossPair(request) {
|
|
465
|
+
return priceCrossMutation(API_PATHS.assignPriceCrossPair, request, (body) => !!body && typeof body === "object" && "pair" in body && !!body.pair && typeof body.pair.pairId === "string");
|
|
466
|
+
},
|
|
467
|
+
unpairPriceCrossPair(request) {
|
|
468
|
+
return priceCrossMutation(API_PATHS.unpairPriceCrossPair, request, (body) => !!body && typeof body === "object" && "outcome" in body && (body.outcome === "unpaired" || body.outcome === "already_unpaired"));
|
|
469
|
+
},
|
|
470
|
+
async downloadPriceCrossReport(pairId) {
|
|
471
|
+
const response = await priceCrossRead(API_PATHS.priceCrossReport, { pairId });
|
|
472
|
+
const filename = /filename="([^"]+)"/.exec(response.headers.get("content-disposition") ?? "")?.[1];
|
|
473
|
+
const count = Number(response.headers.get("x-price-cross-decision-count"));
|
|
474
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
475
|
+
if (!filename || bytes.length === 0) throw new PriceCrossActionError("invalid_response");
|
|
476
|
+
return { bytes, filename, decisionCount: response.headers.has("x-price-cross-decision-count") && Number.isSafeInteger(count) ? count : null };
|
|
477
|
+
},
|
|
478
|
+
publishPriceCrossReport(pairId) {
|
|
479
|
+
return priceCrossMutation(API_PATHS.priceCrossReportShare, { pairId }, (body) => !!body && typeof body === "object" && "url" in body && typeof body.url === "string" && "filename" in body && typeof body.filename === "string");
|
|
480
|
+
},
|
|
390
481
|
setActionSignal(signal) {
|
|
391
482
|
actionDeadlineSignal = signal;
|
|
392
483
|
if (!signal) otpValues.clear();
|
|
@@ -611,21 +702,34 @@ function createGateApi(origin, fetchImpl = fetch, options = {}) {
|
|
|
611
702
|
};
|
|
612
703
|
return api;
|
|
613
704
|
}
|
|
614
|
-
var SettingsActionError, OperatorSessionRejectedError, ZerodhaLoginFailedError, ZerodhaLoginAttemptUnavailableError, OperatorCredentialsRejectedError;
|
|
705
|
+
var SettingsActionError, PRICE_CROSS_UNCERTAIN_MESSAGE, PriceCrossActionError, OperatorSessionRejectedError, ZerodhaLoginFailedError, ZerodhaLoginAttemptUnavailableError, OperatorCredentialsRejectedError;
|
|
615
706
|
var init_gate_api_client = __esm({
|
|
616
707
|
"src/gate-api-client.ts"() {
|
|
617
708
|
"use strict";
|
|
618
709
|
init_src();
|
|
619
710
|
init_profile_name();
|
|
620
711
|
SettingsActionError = class extends Error {
|
|
621
|
-
constructor(code, uncertain = false) {
|
|
622
|
-
super(uncertain ? "Submission could not be confirmed. Read current Settings or token metadata before another submission." : code === "settings_conflict" ? "Settings changed elsewhere. Read saved Settings and submit again with its version." : code === "active_trading_confirmation" ? "Another device has active trading. Review it, then explicitly submit continueReplacement: true to continue." : code === "token_changed" ? "The token changed. Read token metadata and review a fresh submission." : code === "token_write_unresolved" ? "A token write is unresolved. Resolve interrupted save in Web before replacing." : code === "unresolved_order" ? "An Order remains unresolved. Reconcile it before replacing the token." : "The Settings or token operation was rejected.");
|
|
712
|
+
constructor(code, uncertain = false, messageOverride) {
|
|
713
|
+
super(messageOverride ?? (uncertain ? "Submission could not be confirmed. Read current Settings or token metadata before another submission." : code === "settings_conflict" ? "Settings changed elsewhere. Read saved Settings and submit again with its version." : code === "active_trading_confirmation" ? "Another device has active trading. Review it, then explicitly submit continueReplacement: true to continue." : code === "token_changed" ? "The token changed. Read token metadata and review a fresh submission." : code === "token_write_unresolved" ? "A token write is unresolved. Resolve interrupted save in Web before replacing." : code === "unresolved_order" ? "An Order remains unresolved. Reconcile it before replacing the token." : "The Settings or token operation was rejected."));
|
|
623
714
|
this.code = code;
|
|
624
715
|
this.uncertain = uncertain;
|
|
625
716
|
}
|
|
626
717
|
code;
|
|
627
718
|
uncertain;
|
|
628
719
|
};
|
|
720
|
+
PRICE_CROSS_UNCERTAIN_MESSAGE = "Submission could not be confirmed and was not retried. Run pair list before another submission.";
|
|
721
|
+
PriceCrossActionError = class extends Error {
|
|
722
|
+
constructor(code, uncertain = false, message) {
|
|
723
|
+
super(message ?? (uncertain ? PRICE_CROSS_UNCERTAIN_MESSAGE : "The Price Cross request was rejected."));
|
|
724
|
+
this.code = code;
|
|
725
|
+
this.uncertain = uncertain;
|
|
726
|
+
this.name = "PriceCrossActionError";
|
|
727
|
+
if (uncertain) this.recovery = "Read current and past pairs with pair list; the CLI never repeats a submission.";
|
|
728
|
+
}
|
|
729
|
+
code;
|
|
730
|
+
uncertain;
|
|
731
|
+
recovery;
|
|
732
|
+
};
|
|
629
733
|
OperatorSessionRejectedError = class extends Error {
|
|
630
734
|
constructor(reason) {
|
|
631
735
|
super("The saved Operator Session was rejected by Gate.");
|
|
@@ -1370,7 +1474,7 @@ var init_app = __esm({
|
|
|
1370
1474
|
});
|
|
1371
1475
|
|
|
1372
1476
|
// src/index.ts
|
|
1373
|
-
import
|
|
1477
|
+
import path4 from "node:path";
|
|
1374
1478
|
import { realpathSync } from "node:fs";
|
|
1375
1479
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1376
1480
|
|
|
@@ -2302,6 +2406,115 @@ async function runZerodhaValidation(options) {
|
|
|
2302
2406
|
|
|
2303
2407
|
// src/commands.ts
|
|
2304
2408
|
init_zerodha_actions();
|
|
2409
|
+
|
|
2410
|
+
// src/pair-commands.ts
|
|
2411
|
+
import { createHash } from "node:crypto";
|
|
2412
|
+
import { writeFile } from "node:fs/promises";
|
|
2413
|
+
import path3 from "node:path";
|
|
2414
|
+
var PairInvocationError = class extends Error {
|
|
2415
|
+
code = "invalid_invocation";
|
|
2416
|
+
};
|
|
2417
|
+
var PAIR_USAGE = "Use pair list, pair assign --ce ROW --pe ROW --lots N, pair unpair PAIR_ID --confirm, or pair report PAIR_ID [--publish | --output PATH].";
|
|
2418
|
+
var VALUE_OPTIONS = /* @__PURE__ */ new Set(["--env", "--profile", "--timeout", "--ce", "--pe", "--lots", "--output"]);
|
|
2419
|
+
var FLAG_OPTIONS = /* @__PURE__ */ new Set(["--confirm", "--publish"]);
|
|
2420
|
+
var PAIR_ID = /^[A-Za-z0-9_-]{1,200}$/;
|
|
2421
|
+
function isPairInvocation(argv) {
|
|
2422
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
2423
|
+
const token = argv[index];
|
|
2424
|
+
if (token === "--env" || token === "--profile" || token === "--timeout") {
|
|
2425
|
+
index += 1;
|
|
2426
|
+
continue;
|
|
2427
|
+
}
|
|
2428
|
+
if (token.startsWith("--")) continue;
|
|
2429
|
+
return token === "pair";
|
|
2430
|
+
}
|
|
2431
|
+
return false;
|
|
2432
|
+
}
|
|
2433
|
+
function parsePairArguments(argv, parseTimeout2, validateProfile) {
|
|
2434
|
+
const options = /* @__PURE__ */ new Map();
|
|
2435
|
+
const flags = /* @__PURE__ */ new Set();
|
|
2436
|
+
const positional = [];
|
|
2437
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
2438
|
+
const token = argv[index];
|
|
2439
|
+
if (VALUE_OPTIONS.has(token)) {
|
|
2440
|
+
const value = argv[index + 1];
|
|
2441
|
+
if (value === void 0 || value.startsWith("--")) throw new PairInvocationError(`Invalid or incomplete command argument: ${token}`);
|
|
2442
|
+
if (options.has(token)) throw new PairInvocationError(`Argument ${token} may be specified only once.`);
|
|
2443
|
+
options.set(token, value);
|
|
2444
|
+
index += 1;
|
|
2445
|
+
} else if (FLAG_OPTIONS.has(token)) {
|
|
2446
|
+
if (flags.has(token)) throw new PairInvocationError(`Argument ${token} may be specified only once.`);
|
|
2447
|
+
flags.add(token);
|
|
2448
|
+
} else if (token.startsWith("--")) {
|
|
2449
|
+
throw new PairInvocationError(`Unsupported command option: ${token.split("=")[0]}`);
|
|
2450
|
+
} else positional.push(token);
|
|
2451
|
+
}
|
|
2452
|
+
const [, action, pairId, ...extra] = positional;
|
|
2453
|
+
const allowed = {
|
|
2454
|
+
list: [],
|
|
2455
|
+
assign: ["--ce", "--pe", "--lots"],
|
|
2456
|
+
unpair: ["--confirm"],
|
|
2457
|
+
report: ["--publish", "--output"]
|
|
2458
|
+
};
|
|
2459
|
+
if (!action || !(action in allowed) || extra.length > 0 || [...options.keys(), ...flags].some((key) => !["--env", "--profile", "--timeout", ...allowed[action]].includes(key))) {
|
|
2460
|
+
throw new PairInvocationError(PAIR_USAGE);
|
|
2461
|
+
}
|
|
2462
|
+
const environment = options.get("--env"), profile = options.get("--profile");
|
|
2463
|
+
if (!environment || !profile) throw new PairInvocationError("Headless commands require both --env local|dev and --profile NAME.");
|
|
2464
|
+
validateProfile(profile);
|
|
2465
|
+
const target = { environment, profile, ...options.has("--timeout") ? { timeoutMs: parseTimeout2(options.get("--timeout")) } : {} };
|
|
2466
|
+
if (action === "list") {
|
|
2467
|
+
if (pairId !== void 0) throw new PairInvocationError(PAIR_USAGE);
|
|
2468
|
+
return { kind: "pair", action, ...target };
|
|
2469
|
+
}
|
|
2470
|
+
if (action === "assign") {
|
|
2471
|
+
if (pairId !== void 0) throw new PairInvocationError(PAIR_USAGE);
|
|
2472
|
+
const ce = options.get("--ce"), pe = options.get("--pe"), lots = options.get("--lots");
|
|
2473
|
+
if (!ce || !pe || !lots || !/^\d{1,3}$/.test(ce) || !/^\d{1,3}$/.test(pe) || !/^-?\d{1,6}$/.test(lots)) {
|
|
2474
|
+
throw new PairInvocationError("pair assign requires --ce ROW --pe ROW --lots N as whole numbers.");
|
|
2475
|
+
}
|
|
2476
|
+
return { kind: "pair", action, ceRow: Number(ce), peRow: Number(pe), pairLots: Number(lots), ...target };
|
|
2477
|
+
}
|
|
2478
|
+
if (!pairId || !PAIR_ID.test(pairId)) throw new PairInvocationError(`pair ${action} requires a Price Cross pair ID from pair list.`);
|
|
2479
|
+
if (action === "unpair") {
|
|
2480
|
+
if (!flags.has("--confirm")) {
|
|
2481
|
+
throw new PairInvocationError("pair unpair requires --confirm. Unpair closes any paper position at its latest complete close.");
|
|
2482
|
+
}
|
|
2483
|
+
return { kind: "pair", action, pairId, ...target };
|
|
2484
|
+
}
|
|
2485
|
+
const publish = flags.has("--publish");
|
|
2486
|
+
if (publish && options.has("--output")) throw new PairInvocationError("--output downloads the workbook; --publish returns a URL. Choose one.");
|
|
2487
|
+
return { kind: "pair", action: "report", pairId, publish, ...options.has("--output") ? { output: options.get("--output") } : {}, ...target };
|
|
2488
|
+
}
|
|
2489
|
+
async function runPairCommand(command, api) {
|
|
2490
|
+
if (command.action === "list") return { pairs: await api.listPriceCrossPairs() };
|
|
2491
|
+
if (command.action === "assign") {
|
|
2492
|
+
const { pair } = await api.assignPriceCrossPair({ ceRow: command.ceRow, peRow: command.peRow, pairLots: command.pairLots });
|
|
2493
|
+
return { reason: "pair_assigned", pair };
|
|
2494
|
+
}
|
|
2495
|
+
if (command.action === "unpair") {
|
|
2496
|
+
const { pairs: _pairs, ...unpair } = await api.unpairPriceCrossPair({ pairId: command.pairId, confirmExit: true });
|
|
2497
|
+
return { reason: unpair.outcome, unpair: { pairId: command.pairId, ...unpair } };
|
|
2498
|
+
}
|
|
2499
|
+
if (command.publish) {
|
|
2500
|
+
const published = await api.publishPriceCrossReport(command.pairId);
|
|
2501
|
+
return { reason: "report_published", report: { pairId: command.pairId, published: true, url: published.url, filename: published.filename } };
|
|
2502
|
+
}
|
|
2503
|
+
const download = await api.downloadPriceCrossReport(command.pairId);
|
|
2504
|
+
const output = path3.resolve(command.output ?? path3.basename(download.filename));
|
|
2505
|
+
await writeFile(output, download.bytes, { flag: command.output ? "w" : "wx" });
|
|
2506
|
+
return { reason: "report_downloaded", report: {
|
|
2507
|
+
pairId: command.pairId,
|
|
2508
|
+
published: false,
|
|
2509
|
+
filename: download.filename,
|
|
2510
|
+
path: output,
|
|
2511
|
+
bytes: download.bytes.length,
|
|
2512
|
+
sha256: createHash("sha256").update(download.bytes).digest("hex"),
|
|
2513
|
+
decisionCount: download.decisionCount
|
|
2514
|
+
} };
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// src/commands.ts
|
|
2305
2518
|
init_gate_api();
|
|
2306
2519
|
|
|
2307
2520
|
// src/validation-scenario.ts
|
|
@@ -2604,6 +2817,7 @@ function splitHeadlessArgv(argv) {
|
|
|
2604
2817
|
return { positional, options, credentialStdin, otpStdin };
|
|
2605
2818
|
}
|
|
2606
2819
|
function parseArguments(argv) {
|
|
2820
|
+
if (!hasArgumentOtp(argv) && isPairInvocation(argv)) return parsePairArguments(argv, parseTimeout, validateCommandProfile);
|
|
2607
2821
|
const parsedInput = splitHeadlessArgv(argv.filter((value) => value !== "--stdin"));
|
|
2608
2822
|
const commandVerb = parsedInput.positional[0];
|
|
2609
2823
|
if (commandVerb === "settings" || commandVerb === "token") {
|
|
@@ -2739,7 +2953,7 @@ function durationFor(command) {
|
|
|
2739
2953
|
}
|
|
2740
2954
|
function errorDetails(error, signal) {
|
|
2741
2955
|
if (signal?.aborted) return { code: "deadline_exceeded", message: "The action exceeded its deadline.", exitCode: 1 };
|
|
2742
|
-
if (error instanceof InvocationError || error instanceof ProtectedInputError) {
|
|
2956
|
+
if (error instanceof InvocationError || error instanceof ProtectedInputError || error instanceof PairInvocationError) {
|
|
2743
2957
|
return { code: error.code, message: error.message, exitCode: 2 };
|
|
2744
2958
|
}
|
|
2745
2959
|
if (error instanceof OperatorSessionRejectedError) {
|
|
@@ -2956,6 +3170,22 @@ async function runHeadlessCli(argv, dependencies = {}) {
|
|
|
2956
3170
|
}, streams);
|
|
2957
3171
|
return 0;
|
|
2958
3172
|
}
|
|
3173
|
+
if (command.kind === "pair") {
|
|
3174
|
+
await actions.status();
|
|
3175
|
+
const result2 = await runPairCommand(command, actionApi);
|
|
3176
|
+
emit({
|
|
3177
|
+
version: 1,
|
|
3178
|
+
command: `pair ${command.action}`,
|
|
3179
|
+
outcome: "success",
|
|
3180
|
+
...result2,
|
|
3181
|
+
target: { environment: target.environment, origin: target.origin },
|
|
3182
|
+
profile,
|
|
3183
|
+
observedOperator: target.operatorId,
|
|
3184
|
+
elapsedMs: Math.round(performance.now() - started),
|
|
3185
|
+
metadata: { requestId: actionApi.responseMetadata().requestId ?? null, buildId: actionApi.responseMetadata().buildId ?? null }
|
|
3186
|
+
}, streams);
|
|
3187
|
+
return 0;
|
|
3188
|
+
}
|
|
2959
3189
|
if (command.kind === "zerodha") {
|
|
2960
3190
|
await actions.status();
|
|
2961
3191
|
if (command.otpStdin) otp = await readProtectedOtp(dependencies.stdin ?? process.stdin, signal);
|
|
@@ -3019,17 +3249,18 @@ async function runHeadlessCli(argv, dependencies = {}) {
|
|
|
3019
3249
|
if (error instanceof OperatorSessionRejectedError && store && actionApi && target && profile && !signal?.aborted) {
|
|
3020
3250
|
await store.saveSession(profile, target, actionApi.getCookies()).catch(() => void 0);
|
|
3021
3251
|
}
|
|
3022
|
-
const details = error instanceof SettingsActionError ? { code: error.code, message: error.message, exitCode: 1 } : errorDetails(error, signal);
|
|
3252
|
+
const details = error instanceof SettingsActionError || error instanceof PriceCrossActionError ? { code: error.code, message: error.message, exitCode: 1 } : errorDetails(error, signal);
|
|
3023
3253
|
const recovery = error instanceof Error ? error : void 0;
|
|
3024
3254
|
const attemptId = recovery?.attemptId ?? (command?.kind === "zerodha" ? command.attemptId : void 0);
|
|
3025
3255
|
emit({
|
|
3026
3256
|
version: 1,
|
|
3027
|
-
command: command?.kind === "zerodha" || command?.kind === "settings" || command?.kind === "token" ? `${command.kind} ${command.action}` : command?.kind === "credential-provision" ? "credential provision" : command?.kind === "validate-zerodha" ? "validate zerodha-session" : command?.kind === "validate" ? "validate operator-session" : command?.kind ?? "",
|
|
3028
|
-
outcome: error instanceof SettingsActionError && error.uncertain ? "uncertain" : details.exitCode === 4 ? "blocked" : "fail",
|
|
3257
|
+
command: command?.kind === "zerodha" || command?.kind === "settings" || command?.kind === "token" || command?.kind === "pair" ? `${command.kind} ${command.action}` : command?.kind === "credential-provision" ? "credential provision" : command?.kind === "validate-zerodha" ? "validate zerodha-session" : command?.kind === "validate" ? "validate operator-session" : command?.kind ?? "",
|
|
3258
|
+
outcome: (error instanceof SettingsActionError || error instanceof PriceCrossActionError) && error.uncertain ? "uncertain" : details.exitCode === 4 ? "blocked" : "fail",
|
|
3029
3259
|
reason: details.code,
|
|
3030
3260
|
...target ? { target: { environment: target.environment, origin: target.origin } } : command ? { target: { environment: command.environment } } : {},
|
|
3031
3261
|
...profile ? { profile } : {},
|
|
3032
3262
|
...attemptId ? { attemptId, recovery: recovery?.recovery ?? "Observe this exact attempt with the same target and profile; start explicitly only if Gate reports it inactive." } : {},
|
|
3263
|
+
...!attemptId && error instanceof PriceCrossActionError && error.recovery ? { recovery: error.recovery } : {},
|
|
3033
3264
|
...recovery?.observedState ? { observedState: recovery.observedState } : {},
|
|
3034
3265
|
...recovery?.observedOperator ? { observedOperator: recovery.observedOperator } : {},
|
|
3035
3266
|
elapsedMs: Math.round(performance.now() - started),
|
|
@@ -3066,11 +3297,16 @@ function formatCliHelp() {
|
|
|
3066
3297
|
return `Live Market Terminal CLI
|
|
3067
3298
|
|
|
3068
3299
|
Usage (lmt is an alias of live-market-terminal):
|
|
3300
|
+
live-market-terminal [--env local|dev] [--profile NAME]
|
|
3069
3301
|
live-market-terminal interactive [--env local|dev] [--profile NAME]
|
|
3070
3302
|
live-market-terminal interactive credential provision [--env local|dev] [--profile NAME]
|
|
3071
3303
|
live-market-terminal login --env local|dev --profile NAME [--timeout DURATION]
|
|
3072
3304
|
live-market-terminal logout --env local|dev --profile NAME [--timeout DURATION]
|
|
3073
3305
|
live-market-terminal candle-export EXCHANGE:SYMBOL [--date YYYY-MM-DD] --env local|dev --profile NAME [--timeout DURATION]
|
|
3306
|
+
live-market-terminal pair list --env local|dev --profile NAME [--timeout DURATION]
|
|
3307
|
+
live-market-terminal pair assign --ce ROW --pe ROW --lots N --env local|dev --profile NAME [--timeout DURATION]
|
|
3308
|
+
live-market-terminal pair unpair PAIR_ID --confirm --env local|dev --profile NAME [--timeout DURATION]
|
|
3309
|
+
live-market-terminal pair report PAIR_ID [--publish | --output PATH] --env local|dev --profile NAME [--timeout DURATION]
|
|
3074
3310
|
live-market-terminal settings read --env local|dev --profile NAME
|
|
3075
3311
|
live-market-terminal settings save --stdin --env local|dev --profile NAME
|
|
3076
3312
|
live-market-terminal token read --env local|dev --profile NAME
|
|
@@ -3087,16 +3323,26 @@ Usage (lmt is an alias of live-market-terminal):
|
|
|
3087
3323
|
Headless options (--env, --profile, --timeout, --attempt, --otp-stdin, --credential-stdin) may appear before or after the command.
|
|
3088
3324
|
|
|
3089
3325
|
From the monorepo root:
|
|
3090
|
-
pnpm cli [--profile NAME] # interactive; --env defaults to
|
|
3326
|
+
pnpm cli [--profile NAME] # interactive; --env defaults to dev
|
|
3091
3327
|
pnpm cli --env dev --profile my-dev-session
|
|
3092
3328
|
pnpm -s cli:headless status --env local --profile default # build step runs quietly (-s)
|
|
3093
3329
|
|
|
3094
3330
|
Candle Export publishes a new immutable workbook link and returns its context in candleExport JSON.
|
|
3095
3331
|
Empty or unavailable sources are explained in the workbook. Export never starts Zerodha login or token recovery.
|
|
3096
3332
|
|
|
3333
|
+
Price Cross pair commands operate Paper pairs on the owning Broker through Gate; they never save Settings or place Orders.
|
|
3334
|
+
pair list returns current and past pairs (JSON pairs.active / pairs.past). Broker refusals keep their reason codes
|
|
3335
|
+
(invalid_lots, same_type, already_paired, order_in_flight, flag_disabled, ...) with exit 1.
|
|
3336
|
+
pair unpair requires --confirm and returns exit details (unpair.exit) when a paper position closed; a repeat reports already_unpaired.
|
|
3337
|
+
pair report downloads the lifetime workbook (default: server filename in the current directory, never overwritten);
|
|
3338
|
+
--publish returns a new immutable Hosted URL instead.
|
|
3339
|
+
A pair assign, unpair or publish timeout after submission is uncertain (exit 1); read pair list before resubmitting.
|
|
3340
|
+
The CLI never repeats a submission.
|
|
3341
|
+
|
|
3097
3342
|
Settings save reads one JSON object from stdin: expectedSettingsVersion plus selected rowCount,
|
|
3098
3343
|
marketProtection, entryCategories, or complete credentials. Omitted fields are preserved.
|
|
3099
|
-
Token replace reads {accessToken
|
|
3344
|
+
Token replace reads {accessToken?, expected, continueReplacement?, verifyCurrent?} from protected stdin.
|
|
3345
|
+
Use verifyCurrent: true with expected from token read to validate the saved token without supplying accessToken.
|
|
3100
3346
|
Read token metadata through token read. A timeout after submission is uncertain; never retry blindly.
|
|
3101
3347
|
|
|
3102
3348
|
Targets: local and dev only. Production is not permitted.
|
|
@@ -3120,9 +3366,14 @@ Exit codes (headless):
|
|
|
3120
3366
|
4 validation blocked or Zerodha awaiting OTP
|
|
3121
3367
|
|
|
3122
3368
|
Options:
|
|
3123
|
-
--env local|dev Target environment (interactive default:
|
|
3369
|
+
--env local|dev Target environment (interactive default: dev; headless: required)
|
|
3124
3370
|
--profile NAME Client profile (default: default for interactive)
|
|
3125
3371
|
--timeout DURATION Action deadline, e.g. 45s, 2m, 30000ms (headless)
|
|
3372
|
+
--ce ROW / --pe ROW Market Watch rows for pair assign (CE leg, PE leg)
|
|
3373
|
+
--lots N Pair Lots for pair assign; the Broker enforces 1 through its configured maximum
|
|
3374
|
+
--confirm Required by pair unpair
|
|
3375
|
+
--publish pair report: publish an immutable snapshot and return its URL
|
|
3376
|
+
--output PATH pair report: write the downloaded workbook to PATH (overwrites)
|
|
3126
3377
|
--date YYYY-MM-DD Single IST date for candle-export; omitted: latest completed exchange session
|
|
3127
3378
|
--attempt ID Attempt ID returned by zerodha login (required for observe and otp)
|
|
3128
3379
|
--otp-stdin Read one six-digit OTP line from piped protected stdin (maximum 8 bytes)
|
|
@@ -3139,6 +3390,11 @@ var CliInvocationError = class extends Error {
|
|
|
3139
3390
|
function normalizeArgv(argv) {
|
|
3140
3391
|
return argv[0] === "--" ? argv.slice(1) : argv;
|
|
3141
3392
|
}
|
|
3393
|
+
function resolveCliInvocation(argv) {
|
|
3394
|
+
if (argv.length === 0) return { kind: "interactive", argv: ["interactive"] };
|
|
3395
|
+
if (argv[0] === "interactive") return { kind: "interactive", argv };
|
|
3396
|
+
return { kind: "headless", argv };
|
|
3397
|
+
}
|
|
3142
3398
|
async function bootstrapCli(dependencies = {}) {
|
|
3143
3399
|
if (dependencies.runApplication) {
|
|
3144
3400
|
await dependencies.runApplication();
|
|
@@ -3153,14 +3409,15 @@ async function bootstrapCli(dependencies = {}) {
|
|
|
3153
3409
|
process.stdout.write(formatCliHelp());
|
|
3154
3410
|
return;
|
|
3155
3411
|
}
|
|
3156
|
-
|
|
3157
|
-
|
|
3412
|
+
const invocation = resolveCliInvocation(argv);
|
|
3413
|
+
if (invocation.kind === "headless") {
|
|
3414
|
+
process.exitCode = await runHeadlessCli(invocation.argv);
|
|
3158
3415
|
return;
|
|
3159
3416
|
}
|
|
3160
3417
|
let parsed;
|
|
3161
3418
|
let target;
|
|
3162
3419
|
try {
|
|
3163
|
-
parsed = parseInteractiveArguments(argv);
|
|
3420
|
+
parsed = parseInteractiveArguments(invocation.argv);
|
|
3164
3421
|
target = resolveCliTarget({ LMT_ENV: parsed.environment });
|
|
3165
3422
|
} catch (error) {
|
|
3166
3423
|
throw new CliInvocationError(error instanceof Error ? error.message : "Invalid interactive CLI configuration.");
|
|
@@ -3254,7 +3511,7 @@ function parseInteractiveArguments(argv) {
|
|
|
3254
3511
|
if (flag === "--profile") profile2 = value;
|
|
3255
3512
|
}
|
|
3256
3513
|
assertClientProfileName(profile2);
|
|
3257
|
-
return { environment: values2.has("--env") ? environment2 : "
|
|
3514
|
+
return { environment: values2.has("--env") ? environment2 : "dev", profile: profile2, credentialProvision: true };
|
|
3258
3515
|
}
|
|
3259
3516
|
let environment = "";
|
|
3260
3517
|
let profile = "default";
|
|
@@ -3271,10 +3528,10 @@ function parseInteractiveArguments(argv) {
|
|
|
3271
3528
|
if (flag === "--profile") profile = value;
|
|
3272
3529
|
}
|
|
3273
3530
|
assertClientProfileName(profile);
|
|
3274
|
-
return { environment: values.has("--env") ? environment : "
|
|
3531
|
+
return { environment: values.has("--env") ? environment : "dev", profile, credentialProvision: false };
|
|
3275
3532
|
}
|
|
3276
3533
|
var invokedFile = process.argv[1];
|
|
3277
|
-
if (invokedFile &&
|
|
3534
|
+
if (invokedFile && path4.resolve(fileURLToPath2(import.meta.url)) === realpathSync(invokedFile)) {
|
|
3278
3535
|
ensureFetchIgnoresEnvProxy();
|
|
3279
3536
|
bootstrapCli().catch((error) => {
|
|
3280
3537
|
const message = error instanceof Error ? error.message : "CLI failed to start.";
|
|
@@ -3286,5 +3543,6 @@ if (invokedFile && path3.resolve(fileURLToPath2(import.meta.url)) === realpathSy
|
|
|
3286
3543
|
export {
|
|
3287
3544
|
bootstrapCli,
|
|
3288
3545
|
normalizeArgv,
|
|
3289
|
-
parseInteractiveArguments
|
|
3546
|
+
parseInteractiveArguments,
|
|
3547
|
+
resolveCliInvocation
|
|
3290
3548
|
};
|