@agentcentral/cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +148 -35
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,11 +17,12 @@ var CliError = class extends Error {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
// src/constants.ts
|
|
20
|
-
var CLI_VERSION = "0.1.
|
|
20
|
+
var CLI_VERSION = "0.1.1";
|
|
21
21
|
var DEFAULT_MCP_ENDPOINT = "https://mcp.agentcentral.to/mcp";
|
|
22
22
|
var ENV_API_KEY = "AGENT_CENTRAL_API_KEY";
|
|
23
23
|
var ENV_API_KEY_LEGACY = "AGENTCENTRAL_API_KEY";
|
|
24
24
|
var ENV_ENDPOINT = "AGENT_CENTRAL_ENDPOINT";
|
|
25
|
+
var ENV_ALLOW_INSECURE = "AGENT_CENTRAL_ALLOW_INSECURE";
|
|
25
26
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
26
27
|
var USER_AGENT = "agentcentral-cli";
|
|
27
28
|
var DEFAULT_RETRY = {
|
|
@@ -32,7 +33,16 @@ var DEFAULT_RETRY = {
|
|
|
32
33
|
var API_KEY_PREFIX = "ac_live_";
|
|
33
34
|
|
|
34
35
|
// src/config.ts
|
|
35
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
chmodSync,
|
|
38
|
+
closeSync,
|
|
39
|
+
existsSync,
|
|
40
|
+
fstatSync,
|
|
41
|
+
mkdirSync,
|
|
42
|
+
openSync,
|
|
43
|
+
readFileSync,
|
|
44
|
+
writeFileSync
|
|
45
|
+
} from "fs";
|
|
36
46
|
import { homedir } from "os";
|
|
37
47
|
import { dirname, join } from "path";
|
|
38
48
|
import { z } from "zod";
|
|
@@ -60,16 +70,23 @@ function loadConfig(env = process.env) {
|
|
|
60
70
|
if (!existsSync(path)) {
|
|
61
71
|
return { profiles: [] };
|
|
62
72
|
}
|
|
63
|
-
const stat = statSync(path);
|
|
64
|
-
if (process.platform !== "win32" && (stat.mode & 63) !== 0) {
|
|
65
|
-
throw new CliError(
|
|
66
|
-
1 /* GeneralFailure */,
|
|
67
|
-
`config ${path} is group/world-accessible; run: chmod 600 ${path}`
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
73
|
let parsed;
|
|
74
|
+
let raw;
|
|
75
|
+
const fd = openSync(path, "r");
|
|
76
|
+
try {
|
|
77
|
+
const stat = fstatSync(fd);
|
|
78
|
+
if (process.platform !== "win32" && (stat.mode & 63) !== 0) {
|
|
79
|
+
throw new CliError(
|
|
80
|
+
1 /* GeneralFailure */,
|
|
81
|
+
`config ${path} is group/world-accessible; run: chmod 600 ${path}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
raw = readFileSync(fd, "utf8");
|
|
85
|
+
} finally {
|
|
86
|
+
closeSync(fd);
|
|
87
|
+
}
|
|
71
88
|
try {
|
|
72
|
-
parsed = JSON.parse(
|
|
89
|
+
parsed = JSON.parse(raw);
|
|
73
90
|
} catch (error) {
|
|
74
91
|
throw new CliError(
|
|
75
92
|
1 /* GeneralFailure */,
|
|
@@ -116,15 +133,44 @@ function setActiveProfile(config, name) {
|
|
|
116
133
|
|
|
117
134
|
// src/auth.ts
|
|
118
135
|
function redactKey(key) {
|
|
119
|
-
const last4 = key.slice(-4);
|
|
120
136
|
if (key.startsWith(API_KEY_PREFIX)) {
|
|
121
|
-
|
|
137
|
+
const secret = key.slice(API_KEY_PREFIX.length);
|
|
138
|
+
if (secret.length < 8) {
|
|
139
|
+
return `${API_KEY_PREFIX}***`;
|
|
140
|
+
}
|
|
141
|
+
return `${API_KEY_PREFIX}***${secret.slice(-4)}`;
|
|
142
|
+
}
|
|
143
|
+
if (key.length < 8) {
|
|
144
|
+
return "***";
|
|
122
145
|
}
|
|
123
|
-
return `***${
|
|
146
|
+
return `***${key.slice(-4)}`;
|
|
124
147
|
}
|
|
125
148
|
function looksLikeApiKey(key) {
|
|
126
149
|
return key.startsWith(API_KEY_PREFIX);
|
|
127
150
|
}
|
|
151
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
152
|
+
function assertSecureEndpoint(endpoint, env) {
|
|
153
|
+
let url;
|
|
154
|
+
try {
|
|
155
|
+
url = new URL(endpoint);
|
|
156
|
+
} catch {
|
|
157
|
+
throw new CliError(2 /* UsageError */, `invalid endpoint URL: ${endpoint}`);
|
|
158
|
+
}
|
|
159
|
+
if (url.protocol === "https:") {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const host = url.hostname.toLowerCase();
|
|
163
|
+
if (url.protocol === "http:" && LOOPBACK_HOSTS.has(host)) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (env[ENV_ALLOW_INSECURE] === "1") {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
throw new CliError(
|
|
170
|
+
2 /* UsageError */,
|
|
171
|
+
`refusing to send the API key over a non-https endpoint (${endpoint}); set ${ENV_ALLOW_INSECURE}=1 to override`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
128
174
|
async function resolveKey(input) {
|
|
129
175
|
const { flags, env, config, readKeyFromStdin: readKeyFromStdin2 } = input;
|
|
130
176
|
if (flags.apiKeyStdin === true) {
|
|
@@ -188,6 +234,7 @@ async function resolveAuth(input) {
|
|
|
188
234
|
warn("API key does not start with ac_live_; continuing.");
|
|
189
235
|
}
|
|
190
236
|
const endpoint = resolution.profile?.endpoint ?? env[ENV_ENDPOINT] ?? DEFAULT_MCP_ENDPOINT;
|
|
237
|
+
assertSecureEndpoint(endpoint, env);
|
|
191
238
|
return {
|
|
192
239
|
apiKey,
|
|
193
240
|
endpoint,
|
|
@@ -460,9 +507,12 @@ function writeEnvelopeFrom(result) {
|
|
|
460
507
|
return structured;
|
|
461
508
|
}
|
|
462
509
|
function toolClassification(def) {
|
|
510
|
+
const hint = def.annotations;
|
|
511
|
+
const annotationWrite = hint?.destructiveHint === true || hint?.readOnlyHint === false;
|
|
463
512
|
const properties = def.inputSchema.properties;
|
|
464
513
|
const props = properties && typeof properties === "object" ? properties : {};
|
|
465
|
-
const
|
|
514
|
+
const schemaWrite = "confirm" in props && "idempotency_key" in props;
|
|
515
|
+
const isWrite = annotationWrite || schemaWrite;
|
|
466
516
|
return {
|
|
467
517
|
isWrite,
|
|
468
518
|
supportsPreview: isWrite
|
|
@@ -613,6 +663,11 @@ var TOOL_DOMAIN_BY_NAME = TOOL_DOMAINS.reduce(
|
|
|
613
663
|
},
|
|
614
664
|
{}
|
|
615
665
|
);
|
|
666
|
+
function toolDomain(def) {
|
|
667
|
+
const metaDomain = def._meta?.["agentcentral/domain"];
|
|
668
|
+
if (typeof metaDomain === "string") return metaDomain;
|
|
669
|
+
return TOOL_DOMAIN_BY_NAME[def.name];
|
|
670
|
+
}
|
|
616
671
|
function requireTool(tools, name) {
|
|
617
672
|
const def = tools.find((tool) => tool.name === name);
|
|
618
673
|
if (!def) {
|
|
@@ -632,7 +687,7 @@ async function toolsList(ctx) {
|
|
|
632
687
|
const client = await ctx.getClient();
|
|
633
688
|
const tools = await client.listTools({ timeoutMs: ctx.flags.timeoutMs });
|
|
634
689
|
const domainFilter = ctx.flags.domain;
|
|
635
|
-
const filtered = domainFilter ? tools.filter((def) =>
|
|
690
|
+
const filtered = domainFilter ? tools.filter((def) => toolDomain(def) === domainFilter) : tools;
|
|
636
691
|
const rows = filtered.map((def) => ({
|
|
637
692
|
name: def.name,
|
|
638
693
|
write: toolClassification(def).isWrite,
|
|
@@ -750,7 +805,7 @@ async function toolsCall(ctx, name, inputSpec) {
|
|
|
750
805
|
const value = selectOutputValue(result);
|
|
751
806
|
const rendered = formatOutput(value, ctx.flags.format);
|
|
752
807
|
if (ctx.flags.output) {
|
|
753
|
-
await writeFile(ctx.flags.output, rendered, "utf8");
|
|
808
|
+
await writeFile(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
754
809
|
ctx.err(`wrote result to ${ctx.flags.output}`);
|
|
755
810
|
} else {
|
|
756
811
|
ctx.out(rendered);
|
|
@@ -963,7 +1018,7 @@ async function mcpTest(ctx) {
|
|
|
963
1018
|
}
|
|
964
1019
|
|
|
965
1020
|
// src/commands/diagnostics.ts
|
|
966
|
-
import { statSync
|
|
1021
|
+
import { statSync } from "fs";
|
|
967
1022
|
function payloadFromResult(result) {
|
|
968
1023
|
if (result.structuredContent) {
|
|
969
1024
|
return result.structuredContent;
|
|
@@ -1054,7 +1109,7 @@ function renderMarkdownReport(checks) {
|
|
|
1054
1109
|
function checkConfigPerms(env) {
|
|
1055
1110
|
const path = configPath(env);
|
|
1056
1111
|
try {
|
|
1057
|
-
const stat =
|
|
1112
|
+
const stat = statSync(path);
|
|
1058
1113
|
if (process.platform === "win32") {
|
|
1059
1114
|
return { name: "config perms", status: "ok", detail: `${path} (POSIX perms not enforced on Windows)` };
|
|
1060
1115
|
}
|
|
@@ -1071,7 +1126,7 @@ function checkConfigPerms(env) {
|
|
|
1071
1126
|
return { name: "config perms", status: "info", detail: `no config file (${path})` };
|
|
1072
1127
|
}
|
|
1073
1128
|
}
|
|
1074
|
-
async function checkHealth(origin, timeoutMs) {
|
|
1129
|
+
async function checkHealth(origin, timeoutMs, redact) {
|
|
1075
1130
|
if (!origin) {
|
|
1076
1131
|
return {
|
|
1077
1132
|
reachable: false,
|
|
@@ -1079,6 +1134,7 @@ async function checkHealth(origin, timeoutMs) {
|
|
|
1079
1134
|
};
|
|
1080
1135
|
}
|
|
1081
1136
|
const url = `${origin}/health`;
|
|
1137
|
+
const shown = redact ? "/health" : url;
|
|
1082
1138
|
try {
|
|
1083
1139
|
const res = await fetch(url, {
|
|
1084
1140
|
method: "GET",
|
|
@@ -1088,7 +1144,7 @@ async function checkHealth(origin, timeoutMs) {
|
|
|
1088
1144
|
if (!res.ok) {
|
|
1089
1145
|
return {
|
|
1090
1146
|
reachable: false,
|
|
1091
|
-
check: { name: "hosted /health", status: "fail", detail: `${
|
|
1147
|
+
check: { name: "hosted /health", status: "fail", detail: `${shown} -> HTTP ${res.status}` }
|
|
1092
1148
|
};
|
|
1093
1149
|
}
|
|
1094
1150
|
let status;
|
|
@@ -1099,14 +1155,14 @@ async function checkHealth(origin, timeoutMs) {
|
|
|
1099
1155
|
status = void 0;
|
|
1100
1156
|
}
|
|
1101
1157
|
if (status === "ok") {
|
|
1102
|
-
return { reachable: true, check: { name: "hosted /health", status: "ok", detail:
|
|
1158
|
+
return { reachable: true, check: { name: "hosted /health", status: "ok", detail: shown } };
|
|
1103
1159
|
}
|
|
1104
1160
|
return {
|
|
1105
1161
|
reachable: false,
|
|
1106
1162
|
check: {
|
|
1107
1163
|
name: "hosted /health",
|
|
1108
1164
|
status: "warn",
|
|
1109
|
-
detail: `${
|
|
1165
|
+
detail: `${shown} reachable but status=${String(status)}`
|
|
1110
1166
|
}
|
|
1111
1167
|
};
|
|
1112
1168
|
} catch (error) {
|
|
@@ -1115,28 +1171,29 @@ async function checkHealth(origin, timeoutMs) {
|
|
|
1115
1171
|
check: {
|
|
1116
1172
|
name: "hosted /health",
|
|
1117
1173
|
status: "fail",
|
|
1118
|
-
detail: `${
|
|
1174
|
+
detail: `${shown} unreachable: ${error instanceof Error ? error.message : String(error)}`
|
|
1119
1175
|
}
|
|
1120
1176
|
};
|
|
1121
1177
|
}
|
|
1122
1178
|
}
|
|
1123
|
-
async function checkAuth(ctx) {
|
|
1179
|
+
async function checkAuth(ctx, redact) {
|
|
1124
1180
|
try {
|
|
1125
1181
|
const auth = await ctx.resolveAuth();
|
|
1126
1182
|
const profile = auth.profileName ? ` profile=${auth.profileName}` : "";
|
|
1183
|
+
const endpoint = redact ? "" : ` endpoint=${auth.endpoint}`;
|
|
1127
1184
|
return {
|
|
1128
1185
|
auth,
|
|
1129
1186
|
check: {
|
|
1130
1187
|
name: "auth",
|
|
1131
1188
|
status: "ok",
|
|
1132
|
-
detail: `${redactKey(auth.apiKey)} (source=${auth.source}${profile})
|
|
1189
|
+
detail: `${redactKey(auth.apiKey)} (source=${auth.source}${profile})${endpoint}`
|
|
1133
1190
|
}
|
|
1134
1191
|
};
|
|
1135
1192
|
} catch {
|
|
1136
1193
|
return { auth: void 0, check: { name: "auth", status: "fail", detail: "no API key" } };
|
|
1137
1194
|
}
|
|
1138
1195
|
}
|
|
1139
|
-
function accountCheckFromPayload(payload) {
|
|
1196
|
+
function accountCheckFromPayload(payload, redact) {
|
|
1140
1197
|
const info = payload;
|
|
1141
1198
|
const mp = info.marketplace;
|
|
1142
1199
|
const where = mp ? mp.country_name ?? mp.country_code ?? mp.marketplace_id ?? "unknown" : "(none)";
|
|
@@ -1145,23 +1202,27 @@ function accountCheckFromPayload(payload) {
|
|
|
1145
1202
|
const domains = info.enabled_domains ?? [];
|
|
1146
1203
|
const reads = info.tools?.read_enabled ?? 0;
|
|
1147
1204
|
const writes = info.tools?.write_enabled ?? 0;
|
|
1205
|
+
const prefix = redact ? "" : `${where} \xB7 `;
|
|
1148
1206
|
return {
|
|
1149
1207
|
name: "account",
|
|
1150
1208
|
status: "ok",
|
|
1151
|
-
detail: `${
|
|
1209
|
+
detail: `${prefix}seller_central=${sc}, amazon_ads=${ads} \xB7 domains=${domains.length} \xB7 ${reads} read / ${writes} write tools`
|
|
1152
1210
|
};
|
|
1153
1211
|
}
|
|
1154
1212
|
async function doctor(ctx) {
|
|
1213
|
+
const redact = ctx.flags.redact === true;
|
|
1155
1214
|
const checks = [];
|
|
1156
1215
|
checks.push({ name: "cli version", status: "info", detail: CLI_VERSION });
|
|
1157
1216
|
checks.push({ name: "node version", status: "info", detail: process.version });
|
|
1158
1217
|
checks.push({ name: "platform", status: "info", detail: process.platform });
|
|
1159
|
-
|
|
1160
|
-
|
|
1218
|
+
if (!redact) {
|
|
1219
|
+
checks.push({ name: "install path", status: "info", detail: process.argv[1] ?? "(unknown)" });
|
|
1220
|
+
}
|
|
1221
|
+
const { auth, check: authCheck } = await checkAuth(ctx, redact);
|
|
1161
1222
|
const authed = auth !== void 0;
|
|
1162
1223
|
const checksWithAuth = [...checks, authCheck, checkConfigPerms(ctx.env)];
|
|
1163
1224
|
const origin = auth ? originFromEndpoint(auth.endpoint) : void 0;
|
|
1164
|
-
const { reachable, check: healthCheck } = await checkHealth(origin, ctx.flags.timeoutMs);
|
|
1225
|
+
const { reachable, check: healthCheck } = await checkHealth(origin, ctx.flags.timeoutMs, redact);
|
|
1165
1226
|
const checksWithHealth = auth ? [...checksWithAuth, healthCheck] : checksWithAuth;
|
|
1166
1227
|
const finalChecks = await (async () => {
|
|
1167
1228
|
if (!authed) {
|
|
@@ -1171,7 +1232,7 @@ async function doctor(ctx) {
|
|
|
1171
1232
|
const client = await ctx.getClient();
|
|
1172
1233
|
const result = await client.callTool("get_account_info", {});
|
|
1173
1234
|
const payload = payloadFromResult(result);
|
|
1174
|
-
return [...checksWithHealth, accountCheckFromPayload(payload)];
|
|
1235
|
+
return [...checksWithHealth, accountCheckFromPayload(payload, redact)];
|
|
1175
1236
|
} catch (error) {
|
|
1176
1237
|
const detail = error instanceof Error ? error.message : String(error);
|
|
1177
1238
|
return [
|
|
@@ -1180,7 +1241,6 @@ async function doctor(ctx) {
|
|
|
1180
1241
|
];
|
|
1181
1242
|
}
|
|
1182
1243
|
})();
|
|
1183
|
-
void ctx.flags.redact;
|
|
1184
1244
|
const markdown = ctx.flags.format === "markdown";
|
|
1185
1245
|
const report = markdown ? renderMarkdownReport(finalChecks) : renderTextReport(finalChecks);
|
|
1186
1246
|
ctx.out(report);
|
|
@@ -1455,9 +1515,25 @@ function extraFlags(ctx) {
|
|
|
1455
1515
|
return {
|
|
1456
1516
|
since: flags.since,
|
|
1457
1517
|
groupBy: flags.groupBy,
|
|
1458
|
-
asin: flags.asin
|
|
1518
|
+
asin: flags.asin,
|
|
1519
|
+
fulfillmentChannel: flags.fulfillmentChannel
|
|
1459
1520
|
};
|
|
1460
1521
|
}
|
|
1522
|
+
var FULFILLMENT_CHANNELS = ["AFN", "MFN"];
|
|
1523
|
+
function parseFulfillmentChannel(value) {
|
|
1524
|
+
if (value === void 0) {
|
|
1525
|
+
return void 0;
|
|
1526
|
+
}
|
|
1527
|
+
const normalized = value.trim().toUpperCase();
|
|
1528
|
+
const match = FULFILLMENT_CHANNELS.find((channel) => channel === normalized);
|
|
1529
|
+
if (!match) {
|
|
1530
|
+
throw new CliError(
|
|
1531
|
+
2 /* UsageError */,
|
|
1532
|
+
`invalid --fulfillment-channel: ${value} (expected AFN or MFN)`
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
return match;
|
|
1536
|
+
}
|
|
1461
1537
|
function selectOutputValue2(result) {
|
|
1462
1538
|
if (result.structuredContent !== void 0) {
|
|
1463
1539
|
return result.structuredContent;
|
|
@@ -1491,7 +1567,7 @@ async function emit(ctx, name, args) {
|
|
|
1491
1567
|
const value = selectOutputValue2(result);
|
|
1492
1568
|
const rendered = formatOutput(value, ctx.flags.format);
|
|
1493
1569
|
if (ctx.flags.output) {
|
|
1494
|
-
await writeFile2(ctx.flags.output, rendered, "utf8");
|
|
1570
|
+
await writeFile2(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
1495
1571
|
ctx.err(`wrote result to ${ctx.flags.output}`);
|
|
1496
1572
|
} else {
|
|
1497
1573
|
ctx.out(rendered);
|
|
@@ -1519,6 +1595,25 @@ async function actionsHistory(ctx) {
|
|
|
1519
1595
|
const dates = parseSince(since);
|
|
1520
1596
|
return emit(ctx, "get_action_history", { ...dates, ...limitArg(ctx) });
|
|
1521
1597
|
}
|
|
1598
|
+
async function todaysSales(ctx) {
|
|
1599
|
+
const { fulfillmentChannel } = extraFlags(ctx);
|
|
1600
|
+
const channel = parseFulfillmentChannel(fulfillmentChannel);
|
|
1601
|
+
const channelArg = channel ? { fulfillment_channel: channel } : {};
|
|
1602
|
+
return emit(ctx, "get_todays_sales", { ...channelArg, ...limitArg(ctx) });
|
|
1603
|
+
}
|
|
1604
|
+
async function orders(ctx) {
|
|
1605
|
+
const { since, asin, fulfillmentChannel } = extraFlags(ctx);
|
|
1606
|
+
const dates = parseSince(since);
|
|
1607
|
+
const asinArg = asin ? { asin } : {};
|
|
1608
|
+
const channel = parseFulfillmentChannel(fulfillmentChannel);
|
|
1609
|
+
const channelArg = channel ? { fulfillment_channel: channel } : {};
|
|
1610
|
+
return emit(ctx, "get_orders", { ...dates, ...asinArg, ...channelArg, ...limitArg(ctx) });
|
|
1611
|
+
}
|
|
1612
|
+
async function inventoryRisk(ctx) {
|
|
1613
|
+
const { asin } = extraFlags(ctx);
|
|
1614
|
+
const asinArg = asin ? { asin } : {};
|
|
1615
|
+
return emit(ctx, "get_inventory_risk_triage", { ...asinArg, ...limitArg(ctx) });
|
|
1616
|
+
}
|
|
1522
1617
|
|
|
1523
1618
|
// src/cli.ts
|
|
1524
1619
|
var FORMATS = ["json", "ndjson", "table", "csv", "markdown"];
|
|
@@ -1538,6 +1633,7 @@ var COMMAND_NAMES = [
|
|
|
1538
1633
|
"auth status",
|
|
1539
1634
|
"auth logout",
|
|
1540
1635
|
"inventory health",
|
|
1636
|
+
"inventory risk",
|
|
1541
1637
|
"ads performance",
|
|
1542
1638
|
"catalog quality",
|
|
1543
1639
|
"actions history",
|
|
@@ -1750,7 +1846,7 @@ async function run(argv) {
|
|
|
1750
1846
|
);
|
|
1751
1847
|
withGlobalOptions(cli.command("ads performance", "summarize campaign performance")).option("--since <dur>", "lookback window: a relative duration like 30d or a YYYY-MM-DD date").option("--group-by <field>", "advisory grain hint (documented; not wired server-side)").action(
|
|
1752
1848
|
(options) => dispatch((ctx) => adsPerformance(ctx), options, {
|
|
1753
|
-
extra: { since: asString(options.since), groupBy: asString(options
|
|
1849
|
+
extra: { since: asString(options.since), groupBy: asString(options.groupBy) }
|
|
1754
1850
|
})
|
|
1755
1851
|
);
|
|
1756
1852
|
withGlobalOptions(cli.command("catalog quality", "summarize listing quality")).option("--asin <asin>", "scope to a single ASIN").action(
|
|
@@ -1759,6 +1855,23 @@ async function run(argv) {
|
|
|
1759
1855
|
withGlobalOptions(cli.command("actions history", "list write-action history")).option("--since <dur>", "lookback window: a relative duration like 30d or a YYYY-MM-DD date").action(
|
|
1760
1856
|
(options) => dispatch((ctx) => actionsHistory(ctx), options, { extra: { since: asString(options.since) } })
|
|
1761
1857
|
);
|
|
1858
|
+
withGlobalOptions(cli.command("today", "summarize today's sales")).option("--fulfillment-channel <channel>", "filter by fulfillment channel: AFN or MFN").action(
|
|
1859
|
+
(options) => dispatch((ctx) => todaysSales(ctx), options, {
|
|
1860
|
+
extra: { fulfillmentChannel: asString(options.fulfillmentChannel) }
|
|
1861
|
+
})
|
|
1862
|
+
);
|
|
1863
|
+
withGlobalOptions(cli.command("orders", "list orders")).option("--since <dur>", "lookback window: a relative duration like 30d or a YYYY-MM-DD date").option("--asin <asin>", "scope to a single ASIN").option("--fulfillment-channel <channel>", "filter by fulfillment channel: AFN or MFN").action(
|
|
1864
|
+
(options) => dispatch((ctx) => orders(ctx), options, {
|
|
1865
|
+
extra: {
|
|
1866
|
+
since: asString(options.since),
|
|
1867
|
+
asin: asString(options.asin),
|
|
1868
|
+
fulfillmentChannel: asString(options.fulfillmentChannel)
|
|
1869
|
+
}
|
|
1870
|
+
})
|
|
1871
|
+
);
|
|
1872
|
+
withGlobalOptions(cli.command("inventory risk", "triage inventory risk")).option("--asin <asin>", "scope to a single ASIN").action(
|
|
1873
|
+
(options) => dispatch((ctx) => inventoryRisk(ctx), options, { extra: { asin: asString(options.asin) } })
|
|
1874
|
+
);
|
|
1762
1875
|
cli.help();
|
|
1763
1876
|
cli.version(CLI_VERSION);
|
|
1764
1877
|
try {
|