@merchbase/cli 1.0.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +20 -2
- package/dist/auth.d.ts +22 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +76 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +212 -94
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +6 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +52 -3
- package/dist/config.js.map +1 -1
- package/dist/help.d.ts +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +154 -27
- package/dist/help.js.map +1 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +17 -2
- package/dist/output.js.map +1 -1
- package/dist/secret-input.d.ts +3 -0
- package/dist/secret-input.d.ts.map +1 -0
- package/dist/secret-input.js +53 -0
- package/dist/secret-input.js.map +1 -0
- package/dist/secure-store.d.ts +23 -0
- package/dist/secure-store.d.ts.map +1 -0
- package/dist/secure-store.js +156 -0
- package/dist/secure-store.js.map +1 -0
- package/dist/setup-help.d.ts +1 -1
- package/dist/setup-help.d.ts.map +1 -1
- package/dist/setup-help.js +15 -35
- package/dist/setup-help.js.map +1 -1
- package/package.json +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# MerchBase CLI Changelog
|
|
2
2
|
|
|
3
|
+
## v3.0.0 - 2026-06-15
|
|
4
|
+
|
|
5
|
+
- Added `merchbase auth status`, `merchbase auth set`, `merchbase auth set --stdin`, and `merchbase auth clear` so local users and desktop agents can use macOS Keychain auth while automation can keep using `MERCHBASE_API_KEY`.
|
|
6
|
+
- Changed missing-auth guidance to point operators at `merchbase auth set` for local setup and `MERCHBASE_API_KEY` for CI, scripts, and agent runtimes.
|
|
7
|
+
- Changed `merchbase config show` to report the active auth source without printing secrets.
|
|
8
|
+
- Changed config management to use explicit key/value commands: `config get`, `config set`, `config unset`, and `config reset`.
|
|
9
|
+
- Removed `config clear`; use `merchbase config reset`.
|
|
10
|
+
|
|
11
|
+
## v2.0.0 - 2026-06-10
|
|
12
|
+
|
|
13
|
+
- Product lookup now accepts ASINs copied from sales records, and product marketplace filters match concrete seller marketplaces case-insensitively.
|
|
14
|
+
- Help now includes high-value examples for products, designs, sales, dashboard snapshots, and planner status.
|
|
15
|
+
- Top-level `--help` output now uses the same curated help topics as `merchbase help`, so common command groups are easier to discover.
|
|
16
|
+
- `merchbase health` now verifies auth, resolved account context, and sync readiness.
|
|
17
|
+
- CLI config no longer stores a default account; API keys resolve the account, with marketplace override still available.
|
|
18
|
+
- The CLI is now read-oriented for seller and agent analysis. Server-side mutation commands are no longer exposed for product sync, sales sync, Merch status-count sync, planner enable/disable, design facet updates, or facet classification.
|
|
19
|
+
- `merchbase ingestion planner state` remains available as a read-only planner status command; planner controls and ripcord reset remain dashboard actions.
|
|
20
|
+
- `merchbase setup` commands are no longer exposed; account discovery and metadata setup are handled by the automatic planner.
|
|
21
|
+
- Missing API keys now return an explicit auth error instead of a generic internal error.
|
|
22
|
+
|
|
3
23
|
## v1.0.2 - 2026-04-02
|
|
4
24
|
|
|
5
25
|
- Setup help now adapts to your saved account and current setup status, so `merchbase --help` and sync command help point to the next missing step instead of showing the same static checklist every time.
|
package/README.md
CHANGED
|
@@ -11,18 +11,36 @@ npm install -g @merchbase/cli
|
|
|
11
11
|
## Setup
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
|
|
14
|
+
merchbase auth set
|
|
15
15
|
merchbase config set base-url https://app.merchbase.co
|
|
16
|
-
merchbase config set
|
|
16
|
+
merchbase config set marketplace US # optional
|
|
17
|
+
merchbase health
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`merchbase auth set` stores the API key in macOS Keychain. For automation,
|
|
21
|
+
CI, and agent runtimes, `MERCHBASE_API_KEY` overrides the stored key:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export MERCHBASE_API_KEY=mb_live_xxx
|
|
17
25
|
```
|
|
18
26
|
|
|
19
27
|
## Usage
|
|
20
28
|
|
|
21
29
|
```bash
|
|
22
30
|
merchbase help
|
|
31
|
+
merchbase auth status
|
|
32
|
+
merchbase config show
|
|
33
|
+
merchbase health
|
|
23
34
|
merchbase products list
|
|
35
|
+
merchbase products get B000000001 --marketplace us
|
|
36
|
+
merchbase products metadata --marketplace us
|
|
24
37
|
merchbase sales summary --range 30d
|
|
38
|
+
merchbase ingestion planner state
|
|
25
39
|
merchbase changelog
|
|
26
40
|
```
|
|
27
41
|
|
|
42
|
+
Product ASIN lookup accepts ASINs copied from sales records. Product
|
|
43
|
+
marketplace filters are case-insensitive and match concrete seller
|
|
44
|
+
marketplaces.
|
|
45
|
+
|
|
28
46
|
The CLI reads release notes from the packaged `CHANGELOG.md`, so installed users can inspect the current shipped notes with `merchbase changelog`.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SecureStoreStatus } from "./secure-store.js";
|
|
2
|
+
export declare const API_KEY_ENV_VAR = "MERCHBASE_API_KEY";
|
|
3
|
+
export type AuthSource = "env" | "secure-store" | "missing";
|
|
4
|
+
export interface CliAuthState {
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
authenticated: boolean;
|
|
7
|
+
envVar: typeof API_KEY_ENV_VAR;
|
|
8
|
+
hasEnvOverride: boolean;
|
|
9
|
+
hasStoredApiKey: boolean;
|
|
10
|
+
secureStore: SecureStoreStatus;
|
|
11
|
+
source: AuthSource;
|
|
12
|
+
}
|
|
13
|
+
export type PublicCliAuthState = Omit<CliAuthState, "apiKey">;
|
|
14
|
+
export declare const resolveAuthState: () => Promise<CliAuthState>;
|
|
15
|
+
export declare const setStoredApiKey: (apiKey: string) => Promise<CliAuthState>;
|
|
16
|
+
export declare const clearStoredApiKey: () => Promise<{
|
|
17
|
+
auth: CliAuthState;
|
|
18
|
+
cleared: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
export declare const toPublicAuthState: (state: CliAuthState) => PublicCliAuthState;
|
|
21
|
+
export declare const getMissingAuthMessage: () => string;
|
|
22
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAe,MAAM,mBAAmB,CAAC;AAExE,eAAO,MAAM,eAAe,sBAAsB,CAAC;AAEnD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,cAAc,GAAG,SAAS,CAAC;AAE5D,MAAM,WAAW,YAAY;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,eAAe,CAAC;IAC/B,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,MAAM,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAE9D,eAAO,MAAM,gBAAgB,QAAa,OAAO,CAAC,YAAY,CAwC7D,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,QAAQ,MAAM,0BAWnD,CAAC;AAEF,eAAO,MAAM,iBAAiB;;;EAO7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,YAAY,KAAG,kBAOtD,CAAC;AAEH,eAAO,MAAM,qBAAqB,cACyG,CAAC"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { secureStore } from "./secure-store.js";
|
|
2
|
+
export const API_KEY_ENV_VAR = "MERCHBASE_API_KEY";
|
|
3
|
+
export const resolveAuthState = async () => {
|
|
4
|
+
const status = await secureStore.getStatus();
|
|
5
|
+
const envApiKey = resolveEnvApiKey();
|
|
6
|
+
if (envApiKey) {
|
|
7
|
+
return {
|
|
8
|
+
apiKey: envApiKey,
|
|
9
|
+
authenticated: true,
|
|
10
|
+
envVar: API_KEY_ENV_VAR,
|
|
11
|
+
hasEnvOverride: true,
|
|
12
|
+
hasStoredApiKey: status.configured,
|
|
13
|
+
secureStore: status,
|
|
14
|
+
source: "env",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (status.available && status.configured) {
|
|
18
|
+
const storedApiKey = await secureStore.readApiKey();
|
|
19
|
+
if (storedApiKey) {
|
|
20
|
+
return {
|
|
21
|
+
apiKey: storedApiKey,
|
|
22
|
+
authenticated: true,
|
|
23
|
+
envVar: API_KEY_ENV_VAR,
|
|
24
|
+
hasEnvOverride: false,
|
|
25
|
+
hasStoredApiKey: true,
|
|
26
|
+
secureStore: status,
|
|
27
|
+
source: "secure-store",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
authenticated: false,
|
|
33
|
+
envVar: API_KEY_ENV_VAR,
|
|
34
|
+
hasEnvOverride: false,
|
|
35
|
+
hasStoredApiKey: status.configured,
|
|
36
|
+
secureStore: status,
|
|
37
|
+
source: "missing",
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export const setStoredApiKey = async (apiKey) => {
|
|
41
|
+
const trimmed = apiKey.trim();
|
|
42
|
+
if (!trimmed) {
|
|
43
|
+
throw createCliError("BAD_REQUEST", "auth set requires an API key from an argument, prompt, or --stdin.");
|
|
44
|
+
}
|
|
45
|
+
await secureStore.writeApiKey(trimmed);
|
|
46
|
+
return resolveAuthState();
|
|
47
|
+
};
|
|
48
|
+
export const clearStoredApiKey = async () => {
|
|
49
|
+
const cleared = await secureStore.clearApiKey();
|
|
50
|
+
return {
|
|
51
|
+
auth: await resolveAuthState(),
|
|
52
|
+
cleared,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export const toPublicAuthState = (state) => ({
|
|
56
|
+
authenticated: state.authenticated,
|
|
57
|
+
envVar: state.envVar,
|
|
58
|
+
hasEnvOverride: state.hasEnvOverride,
|
|
59
|
+
hasStoredApiKey: state.hasStoredApiKey,
|
|
60
|
+
secureStore: state.secureStore,
|
|
61
|
+
source: state.source,
|
|
62
|
+
});
|
|
63
|
+
export const getMissingAuthMessage = () => `Missing MerchBase auth. Run \`merchbase auth set\` for local use or export ${API_KEY_ENV_VAR} for automation, CI, and agent runtimes.`;
|
|
64
|
+
const resolveEnvApiKey = () => {
|
|
65
|
+
const apiKey = process.env[API_KEY_ENV_VAR]?.trim();
|
|
66
|
+
if (!apiKey || apiKey === "null" || apiKey === "undefined") {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return apiKey;
|
|
70
|
+
};
|
|
71
|
+
const createCliError = (code, message) => {
|
|
72
|
+
const error = new Error(message);
|
|
73
|
+
error.code = code;
|
|
74
|
+
return error;
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqDpC,eAAO,MAAM,aAAa,eAyoBzB,CAAC;AAEF,eAAO,MAAM,MAAM,GAAU,eAAmB,kBAS/C,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { createMerchbaseClient, } from "@merchbase/http-client";
|
|
3
3
|
import { Command } from "commander";
|
|
4
|
+
import { clearStoredApiKey, getMissingAuthMessage, resolveAuthState, setStoredApiKey, toPublicAuthState, } from "./auth.js";
|
|
4
5
|
import { normalizeBaseUrl } from "./base-url.js";
|
|
5
6
|
import { loadCliChangelog, loadCliVersion, normalizeChangelogVersion, selectCliChangelogEntry, } from "./changelog.js";
|
|
6
|
-
import { clearConfig, getConfigPath, loadConfig,
|
|
7
|
+
import { CONFIG_KEYS, clearConfig, getConfigPath, getConfigValue, loadConfig, parseConfigKey, setConfigValue, unsetConfigValue, } from "./config.js";
|
|
7
8
|
import { renderHelp, resolveHelpTopicKey } from "./help.js";
|
|
8
9
|
import { printError, printSuccess } from "./output.js";
|
|
10
|
+
import { readSecretFromPrompt, readSecretFromStdin } from "./secret-input.js";
|
|
9
11
|
import { buildSetupHelpText } from "./setup-help.js";
|
|
10
|
-
const API_KEY_ENV_VAR = "MERCHBASE_API_KEY";
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
const CLI_PACKAGE = require("../package.json");
|
|
13
14
|
const CLI_VERSION = CLI_PACKAGE.version ?? "0.0.0";
|
|
14
15
|
const HELP_FLAGS = new Set(["-h", "--help"]);
|
|
16
|
+
const TOP_LEVEL_HELP_TOPICS = new Set([
|
|
17
|
+
"auth",
|
|
18
|
+
"changelog",
|
|
19
|
+
"config",
|
|
20
|
+
"dashboard",
|
|
21
|
+
"designs",
|
|
22
|
+
"health",
|
|
23
|
+
"ingestion",
|
|
24
|
+
"merch-account",
|
|
25
|
+
"products",
|
|
26
|
+
"sales",
|
|
27
|
+
]);
|
|
15
28
|
const SETUP_TARGETS_BY_COMMAND_PATH = {
|
|
16
29
|
"merch-account get": "merch-account-get",
|
|
17
|
-
"merch-account sync": "merch-account-sync",
|
|
18
|
-
"products sync": "products-sync",
|
|
19
|
-
"sales sync": "sales-sync",
|
|
20
30
|
};
|
|
21
31
|
export const createProgram = () => {
|
|
22
32
|
const program = new Command();
|
|
@@ -75,20 +85,99 @@ export const createProgram = () => {
|
|
|
75
85
|
}));
|
|
76
86
|
const configCommand = program
|
|
77
87
|
.command("config")
|
|
78
|
-
.description("Manage CLI config")
|
|
88
|
+
.description("Manage local CLI config")
|
|
89
|
+
.action(async () => {
|
|
90
|
+
process.stdout.write(await renderHelp("config"));
|
|
91
|
+
});
|
|
92
|
+
const authCommand = program
|
|
93
|
+
.command("auth")
|
|
94
|
+
.description("Manage CLI authentication")
|
|
95
|
+
.action(async () => {
|
|
96
|
+
process.stdout.write(await renderHelp("auth"));
|
|
97
|
+
});
|
|
98
|
+
authCommand
|
|
99
|
+
.command("status")
|
|
100
|
+
.description("Show resolved auth source")
|
|
101
|
+
.action(wrap(async () => toPublicAuthState(await resolveAuthState())));
|
|
102
|
+
authCommand
|
|
103
|
+
.command("set [apiKey]")
|
|
104
|
+
.description("Store an API key in macOS Keychain")
|
|
105
|
+
.option("--stdin", "Read the API key from stdin")
|
|
106
|
+
.action(wrap(async (apiKey, options) => {
|
|
107
|
+
const input = await resolveAuthSetInput({
|
|
108
|
+
apiKey,
|
|
109
|
+
stdin: Boolean(options.stdin),
|
|
110
|
+
});
|
|
111
|
+
const auth = await setStoredApiKey(input.apiKey);
|
|
112
|
+
return {
|
|
113
|
+
inputSource: input.source,
|
|
114
|
+
stored: true,
|
|
115
|
+
auth: toPublicAuthState(auth),
|
|
116
|
+
};
|
|
117
|
+
}));
|
|
118
|
+
authCommand
|
|
119
|
+
.command("clear")
|
|
120
|
+
.description("Remove stored Keychain auth")
|
|
121
|
+
.action(wrap(async () => {
|
|
122
|
+
const result = await clearStoredApiKey();
|
|
123
|
+
return {
|
|
124
|
+
auth: toPublicAuthState(result.auth),
|
|
125
|
+
cleared: result.cleared,
|
|
126
|
+
};
|
|
127
|
+
}));
|
|
79
128
|
configCommand
|
|
80
129
|
.command("show")
|
|
81
130
|
.description("Show current config")
|
|
82
131
|
.action(wrap(async () => {
|
|
83
132
|
const config = await loadConfig();
|
|
84
133
|
return {
|
|
134
|
+
auth: toPublicAuthState(await resolveAuthState()),
|
|
85
135
|
path: getConfigPath(),
|
|
86
136
|
config,
|
|
87
137
|
};
|
|
88
138
|
}));
|
|
89
139
|
configCommand
|
|
90
|
-
.command("
|
|
91
|
-
.description("
|
|
140
|
+
.command("get [key]")
|
|
141
|
+
.description("Get one config value")
|
|
142
|
+
.action(wrap(async (keyInput) => {
|
|
143
|
+
const key = requireConfigKey(keyInput, "get");
|
|
144
|
+
const config = await loadConfig();
|
|
145
|
+
return {
|
|
146
|
+
key,
|
|
147
|
+
path: getConfigPath(),
|
|
148
|
+
value: getConfigValue(config, key),
|
|
149
|
+
};
|
|
150
|
+
}));
|
|
151
|
+
configCommand
|
|
152
|
+
.command("set [key] [value]")
|
|
153
|
+
.description("Set one config value")
|
|
154
|
+
.action(wrap(async (keyInput, value) => {
|
|
155
|
+
const key = requireConfigKey(keyInput, "set");
|
|
156
|
+
const nextValue = requireConfigValue(value, key);
|
|
157
|
+
const config = await setConfigValue(key, key === "base-url" ? normalizeBaseUrl(nextValue) : nextValue);
|
|
158
|
+
return {
|
|
159
|
+
config,
|
|
160
|
+
key,
|
|
161
|
+
path: getConfigPath(),
|
|
162
|
+
saved: true,
|
|
163
|
+
};
|
|
164
|
+
}));
|
|
165
|
+
configCommand
|
|
166
|
+
.command("unset [key]")
|
|
167
|
+
.description("Unset one config value")
|
|
168
|
+
.action(wrap(async (keyInput) => {
|
|
169
|
+
const key = requireConfigKey(keyInput, "unset");
|
|
170
|
+
const config = await unsetConfigValue(key);
|
|
171
|
+
return {
|
|
172
|
+
config,
|
|
173
|
+
key,
|
|
174
|
+
path: getConfigPath(),
|
|
175
|
+
saved: true,
|
|
176
|
+
};
|
|
177
|
+
}));
|
|
178
|
+
configCommand
|
|
179
|
+
.command("reset")
|
|
180
|
+
.description("Reset local config")
|
|
92
181
|
.action(wrap(async () => {
|
|
93
182
|
const config = await clearConfig();
|
|
94
183
|
return {
|
|
@@ -96,28 +185,41 @@ export const createProgram = () => {
|
|
|
96
185
|
config,
|
|
97
186
|
};
|
|
98
187
|
}));
|
|
99
|
-
|
|
100
|
-
.command("
|
|
101
|
-
.description("
|
|
102
|
-
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
188
|
+
program
|
|
189
|
+
.command("health")
|
|
190
|
+
.description("Check auth, account context, and sync readiness")
|
|
191
|
+
.action(wrapWithClient(async (client, config) => {
|
|
192
|
+
const [account, readiness, planner] = await Promise.all([
|
|
193
|
+
client.accounts.get.query(),
|
|
194
|
+
client.setup.status.query(),
|
|
195
|
+
client.ingestion.planner.state.query(),
|
|
196
|
+
]);
|
|
197
|
+
const missingReadiness = readiness.steps
|
|
198
|
+
.filter((step) => !step.ready)
|
|
199
|
+
.map((step) => step.key);
|
|
200
|
+
return {
|
|
201
|
+
ok: true,
|
|
202
|
+
baseUrl: config.baseUrl,
|
|
203
|
+
marketplace: config.marketplace ?? account.marketplace,
|
|
204
|
+
account,
|
|
205
|
+
readiness: {
|
|
206
|
+
ready: missingReadiness.length === 0,
|
|
207
|
+
missing: missingReadiness,
|
|
208
|
+
steps: readiness.steps,
|
|
209
|
+
ingestionClient: planner.ingestionClient,
|
|
210
|
+
planner: {
|
|
211
|
+
enabled: planner.state.enabled,
|
|
212
|
+
updatedAt: planner.state.updatedAt,
|
|
213
|
+
due: planner.work.due,
|
|
214
|
+
dispatchable: planner.work.dispatchable,
|
|
215
|
+
active: planner.work.active,
|
|
216
|
+
blockedBy: planner.work.blockedBy,
|
|
217
|
+
nextAction: planner.work.nextAction,
|
|
218
|
+
},
|
|
219
|
+
storedData: planner.storedData,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}));
|
|
121
223
|
const merchAccountCommand = program
|
|
122
224
|
.command("merch-account")
|
|
123
225
|
.description("Amazon Merch account API commands");
|
|
@@ -129,12 +231,25 @@ export const createProgram = () => {
|
|
|
129
231
|
if (merchAccount) {
|
|
130
232
|
return merchAccount;
|
|
131
233
|
}
|
|
132
|
-
throw createCliError("PRECONDITION_FAILED", "Merch account metadata is not available for this account.
|
|
234
|
+
throw createCliError("PRECONDITION_FAILED", "Merch account metadata is not available for this account. Keep the ingestion client connected and enable automatic planning.");
|
|
133
235
|
}));
|
|
134
|
-
merchAccountCommand
|
|
135
|
-
.command("
|
|
136
|
-
.description("
|
|
137
|
-
|
|
236
|
+
const merchAccountStatusCountsCommand = merchAccountCommand
|
|
237
|
+
.command("status-counts")
|
|
238
|
+
.description("Amazon Merch status counts commands");
|
|
239
|
+
merchAccountStatusCountsCommand
|
|
240
|
+
.command("get")
|
|
241
|
+
.description("Get saved Amazon Merch status counts")
|
|
242
|
+
.action(wrapWithClient(async (client) => client.merchAccount.statusCounts.get.query()));
|
|
243
|
+
const ingestionCommand = program
|
|
244
|
+
.command("ingestion")
|
|
245
|
+
.description("Ingestion operator state commands");
|
|
246
|
+
const plannerCommand = ingestionCommand
|
|
247
|
+
.command("planner")
|
|
248
|
+
.description("Automatic planner commands");
|
|
249
|
+
plannerCommand
|
|
250
|
+
.command("state")
|
|
251
|
+
.description("Get automatic planner state")
|
|
252
|
+
.action(wrapWithClient(async (client) => client.ingestion.planner.state.query()));
|
|
138
253
|
const productsCommand = program
|
|
139
254
|
.command("products")
|
|
140
255
|
.description("Products API commands");
|
|
@@ -171,7 +286,7 @@ export const createProgram = () => {
|
|
|
171
286
|
productsCommand
|
|
172
287
|
.command("get <asin>")
|
|
173
288
|
.description("Get product details")
|
|
174
|
-
.
|
|
289
|
+
.requiredOption("--marketplace <marketplace>")
|
|
175
290
|
.action(wrapWithClient(async (client, _config, asin, options) => client.products.get.query({
|
|
176
291
|
asin,
|
|
177
292
|
marketplace: options.marketplace,
|
|
@@ -180,10 +295,8 @@ export const createProgram = () => {
|
|
|
180
295
|
.command("metadata")
|
|
181
296
|
.description("Get product dataset metadata")
|
|
182
297
|
.option("--marketplace <marketplace>")
|
|
183
|
-
.option("--asin <asin>")
|
|
184
298
|
.action(wrapWithClient(async (client, _config, options) => client.products.metadata.query({
|
|
185
299
|
marketplace: options.marketplace,
|
|
186
|
-
asin: options.asin,
|
|
187
300
|
})));
|
|
188
301
|
const productCatalogCommand = productsCommand
|
|
189
302
|
.command("catalog")
|
|
@@ -205,13 +318,6 @@ export const createProgram = () => {
|
|
|
205
318
|
.action(wrapWithClient(async (client, _config, productType) => client.products.catalog.product.query({
|
|
206
319
|
productType,
|
|
207
320
|
})));
|
|
208
|
-
productsCommand
|
|
209
|
-
.command("sync")
|
|
210
|
-
.description("Start a products sync")
|
|
211
|
-
.option("--range <range>", "1d|3d|7d|30d|90d", "7d")
|
|
212
|
-
.action(wrapWithClient(async (client, _config, options) => client.products.sync.mutate({
|
|
213
|
-
rangePreset: options.range,
|
|
214
|
-
})));
|
|
215
321
|
const designsCommand = program
|
|
216
322
|
.command("designs")
|
|
217
323
|
.description("Designs API commands");
|
|
@@ -241,21 +347,6 @@ export const createProgram = () => {
|
|
|
241
347
|
.command("get <designId>")
|
|
242
348
|
.description("Get design facets")
|
|
243
349
|
.action(wrapWithClient(async (client, _config, designId) => client.designs.facets.get.query({ designId })));
|
|
244
|
-
designFacetsCommand
|
|
245
|
-
.command("update <designId>")
|
|
246
|
-
.description("Replace design facets")
|
|
247
|
-
.requiredOption("--facet <facetValue...>", "Facet value in the form facet:name. Repeat flag for multiple values.")
|
|
248
|
-
.action(wrapWithClient(async (client, _config, designId, options) => client.designs.facets.update.mutate({
|
|
249
|
-
designId,
|
|
250
|
-
facets: options.facet.map(parseFacetValue),
|
|
251
|
-
})));
|
|
252
|
-
designFacetsCommand
|
|
253
|
-
.command("classify")
|
|
254
|
-
.description("Queue facet classification")
|
|
255
|
-
.requiredOption("--design-id <designIds...>")
|
|
256
|
-
.action(wrapWithClient(async (client, _config, options) => client.designs.facets.classify.mutate({
|
|
257
|
-
designIds: options.designId,
|
|
258
|
-
})));
|
|
259
350
|
designFacetsCommand
|
|
260
351
|
.command("status <jobId>")
|
|
261
352
|
.description("Get classify job status")
|
|
@@ -263,13 +354,6 @@ export const createProgram = () => {
|
|
|
263
354
|
const salesCommand = program
|
|
264
355
|
.command("sales")
|
|
265
356
|
.description("Sales API commands");
|
|
266
|
-
salesCommand
|
|
267
|
-
.command("sync")
|
|
268
|
-
.description("Start a sales sync")
|
|
269
|
-
.option("--range <range>", "1d|3d|7d|30d|90d", "7d")
|
|
270
|
-
.action(wrapWithClient(async (client, _config, options) => client.sales.sync.mutate({
|
|
271
|
-
rangePreset: options.range,
|
|
272
|
-
})));
|
|
273
357
|
salesCommand
|
|
274
358
|
.command("summary")
|
|
275
359
|
.description("Get sales summary")
|
|
@@ -444,54 +528,72 @@ function wrap(handler) {
|
|
|
444
528
|
function wrapWithClient(handler) {
|
|
445
529
|
return wrap(async (...args) => {
|
|
446
530
|
const config = await loadConfig();
|
|
447
|
-
const
|
|
448
|
-
if (!apiKey) {
|
|
449
|
-
throw
|
|
531
|
+
const auth = await resolveAuthState();
|
|
532
|
+
if (!auth.apiKey) {
|
|
533
|
+
throw createCliError("UNAUTHORIZED", getMissingAuthMessage());
|
|
450
534
|
}
|
|
451
535
|
const headers = {};
|
|
452
|
-
if (config.account) {
|
|
453
|
-
headers["x-merchbase-account"] = config.account;
|
|
454
|
-
}
|
|
455
536
|
if (config.marketplace) {
|
|
456
537
|
headers["x-merchbase-marketplace"] = config.marketplace;
|
|
457
538
|
}
|
|
458
539
|
const client = createMerchbaseClient({
|
|
459
540
|
baseUrl: config.baseUrl,
|
|
460
|
-
apiKey,
|
|
541
|
+
apiKey: auth.apiKey,
|
|
461
542
|
headers,
|
|
462
543
|
});
|
|
463
544
|
return handler(client, config, ...args);
|
|
464
545
|
});
|
|
465
546
|
}
|
|
466
|
-
function parseFacetValue(value) {
|
|
467
|
-
const separator = value.indexOf(":");
|
|
468
|
-
if (separator < 1 || separator === value.length - 1) {
|
|
469
|
-
throw new Error(`Invalid facet value "${value}". Expected <facet>:<name>.`);
|
|
470
|
-
}
|
|
471
|
-
return {
|
|
472
|
-
facet: value.slice(0, separator),
|
|
473
|
-
name: value.slice(separator + 1),
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
547
|
function resolveRange(config, range) {
|
|
477
548
|
return range ?? config.range;
|
|
478
549
|
}
|
|
479
|
-
function resolveApiKey() {
|
|
480
|
-
const apiKey = process.env[API_KEY_ENV_VAR]?.trim();
|
|
481
|
-
if (!apiKey || apiKey === "null" || apiKey === "undefined") {
|
|
482
|
-
return undefined;
|
|
483
|
-
}
|
|
484
|
-
return apiKey;
|
|
485
|
-
}
|
|
486
550
|
const createCliError = (code, message) => {
|
|
487
551
|
const error = new Error(message);
|
|
488
552
|
error.code = code;
|
|
489
553
|
return error;
|
|
490
554
|
};
|
|
491
|
-
const
|
|
555
|
+
const resolveAuthSetInput = async (input) => {
|
|
556
|
+
if (input.apiKey && input.stdin) {
|
|
557
|
+
throw createCliError("BAD_REQUEST", "Use either an API-key argument or --stdin, not both.");
|
|
558
|
+
}
|
|
559
|
+
if (input.apiKey) {
|
|
560
|
+
return {
|
|
561
|
+
apiKey: input.apiKey,
|
|
562
|
+
source: "argument",
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
if (input.stdin) {
|
|
566
|
+
return {
|
|
567
|
+
apiKey: await readSecretFromStdin(),
|
|
568
|
+
source: "stdin",
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
apiKey: await readSecretFromPrompt("MerchBase API key: "),
|
|
573
|
+
source: "prompt",
|
|
574
|
+
};
|
|
575
|
+
};
|
|
576
|
+
const requireConfigKey = (value, command) => {
|
|
577
|
+
const key = parseConfigKey(value);
|
|
578
|
+
if (key) {
|
|
579
|
+
return key;
|
|
580
|
+
}
|
|
581
|
+
if (!value) {
|
|
582
|
+
throw createCliError("BAD_REQUEST", `Missing config key. Usage: merchbase config ${command} <key>${command === "set" ? " <value>" : ""}. Expected: ${CONFIG_KEYS.join(", ")}.`);
|
|
583
|
+
}
|
|
584
|
+
throw createCliError("BAD_REQUEST", `Unknown config key "${value}". Expected: ${CONFIG_KEYS.join(", ")}.`);
|
|
585
|
+
};
|
|
586
|
+
const requireConfigValue = (value, key) => {
|
|
587
|
+
const trimmed = value?.trim();
|
|
588
|
+
if (!trimmed) {
|
|
589
|
+
throw createCliError("BAD_REQUEST", `Missing value for config key "${key}". Usage: merchbase config set ${key} <value>.`);
|
|
590
|
+
}
|
|
591
|
+
return trimmed;
|
|
592
|
+
};
|
|
593
|
+
const maybeRenderHelp = async (program, argv) => {
|
|
492
594
|
const args = argv.slice(2);
|
|
493
595
|
if (args.length === 0) {
|
|
494
|
-
return
|
|
596
|
+
return await renderHelp("global");
|
|
495
597
|
}
|
|
496
598
|
if (args[0] === "help") {
|
|
497
599
|
return null;
|
|
@@ -501,7 +603,11 @@ const maybeRenderHelp = (program, argv) => {
|
|
|
501
603
|
return null;
|
|
502
604
|
}
|
|
503
605
|
const commandPath = getCommandPath(args.slice(0, helpFlagIndex));
|
|
504
|
-
|
|
606
|
+
const topicKey = getTopLevelHelpTopic(commandPath);
|
|
607
|
+
if (topicKey) {
|
|
608
|
+
return await renderHelp(topicKey);
|
|
609
|
+
}
|
|
610
|
+
return await renderCommandHelp(program, commandPath);
|
|
505
611
|
};
|
|
506
612
|
const renderCommandHelp = async (program, commandPath) => {
|
|
507
613
|
const command = findCommand(program, commandPath) ?? program;
|
|
@@ -544,4 +650,16 @@ const findCommand = (program, commandPath) => {
|
|
|
544
650
|
const getSetupHelpTarget = (commandPath) => {
|
|
545
651
|
return SETUP_TARGETS_BY_COMMAND_PATH[commandPath.join(" ")] ?? null;
|
|
546
652
|
};
|
|
653
|
+
const getTopLevelHelpTopic = (commandPath) => {
|
|
654
|
+
if (commandPath.length === 0) {
|
|
655
|
+
return "global";
|
|
656
|
+
}
|
|
657
|
+
if (commandPath.length > 1) {
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
if (!TOP_LEVEL_HELP_TOPICS.has(commandPath[0] ?? "")) {
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
663
|
+
return resolveHelpTopicKey(commandPath);
|
|
664
|
+
};
|
|
547
665
|
//# sourceMappingURL=cli.js.map
|