ubuyfirst 0.1.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/LICENSE +15 -0
- package/README.md +90 -0
- package/dist/cli.js +63 -0
- package/dist/program-YTm6g9Ut.js +2522 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Copyright (c) 2026 Harvest Mobile. All rights reserved.
|
|
2
|
+
|
|
3
|
+
You may install and run this software to access the uBuyFirst service through your
|
|
4
|
+
own uBuyFirst account.
|
|
5
|
+
|
|
6
|
+
You may not copy, modify, distribute, sublicense, sell, or create derivative works
|
|
7
|
+
of this software or any part of it, except where that right cannot be excluded by
|
|
8
|
+
law. No other rights are granted.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
11
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
12
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
|
13
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
14
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
15
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# ubuyfirst
|
|
2
|
+
|
|
3
|
+
Command-line interface for the [uBuyFirst public API](https://app.ubuyfirst.com/api/docs) —
|
|
4
|
+
manage your saved searches, folders, blocklists and notification channels from a terminal or a
|
|
5
|
+
script, and back them up to a file.
|
|
6
|
+
|
|
7
|
+
Requires Node.js 22.12 or newer.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx ubuyfirst --help # run without installing
|
|
13
|
+
npm install -g ubuyfirst # or install the `ubuyfirst` command
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Get an API key
|
|
17
|
+
|
|
18
|
+
Generate one on the **Settings → Account** page of your uBuyFirst account. The key is shown
|
|
19
|
+
once, so copy it before leaving the page. There is one key per account; regenerating it
|
|
20
|
+
immediately invalidates the old one.
|
|
21
|
+
|
|
22
|
+
Store it — the CLI reads the key from stdin so it never lands in your shell history:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ubuyfirst config set-key # paste the key, then press Enter
|
|
26
|
+
ubuyfirst config show # config path, whether a key is stored, effective base URL
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or pass it per-invocation with the `UBUYFIRST_API_KEY` environment variable, which takes
|
|
30
|
+
precedence over the stored key. `ubuyfirst config clear` removes what is stored.
|
|
31
|
+
|
|
32
|
+
## Commands
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
searches list, create, update, pause, resume, delete, export, import
|
|
36
|
+
folders list, create, rename, delete, move
|
|
37
|
+
blocklist list, add, remove (sellers, countries, items)
|
|
38
|
+
notifications list, toggle, settings
|
|
39
|
+
filters export, import
|
|
40
|
+
config set-key, show, clear
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Run `ubuyfirst <group> --help` for the flags of any group.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
ubuyfirst searches list --limit 20
|
|
47
|
+
ubuyfirst searches create --name "Leica M6" --keywords "leica m6" --price-max 2500
|
|
48
|
+
ubuyfirst searches pause abc123
|
|
49
|
+
ubuyfirst blocklist add --type sellers badseller99 anotherseller
|
|
50
|
+
ubuyfirst searches export > backup.json
|
|
51
|
+
ubuyfirst searches import backup.json --preview
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Bulk imports accept `--preview` to report what *would* change without writing anything.
|
|
55
|
+
|
|
56
|
+
## Scripting
|
|
57
|
+
|
|
58
|
+
`--json` makes any command — including a failure — emit exactly one parseable JSON document
|
|
59
|
+
on stdout:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
ubuyfirst --json searches list | jq '.searches[].name'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Exit codes are stable, so a script can branch on the failure without parsing text:
|
|
66
|
+
|
|
67
|
+
| Code | Meaning | Code | Meaning |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| 0 | success | 6 | ambiguous request |
|
|
70
|
+
| 1 | unexpected failure | 7 | account cap exceeded |
|
|
71
|
+
| 2 | bad or missing flags | 8 | rate limited — back off and retry |
|
|
72
|
+
| 3 | validation error | 9 | write failed |
|
|
73
|
+
| 4 | missing or invalid API key | 10 | server error — retriable |
|
|
74
|
+
| 5 | access denied | 11 | partly applied; see the output |
|
|
75
|
+
|
|
76
|
+
Codes 8 and 10 are the retriable ones. Client-side refusals also carry a `cli.`-prefixed error
|
|
77
|
+
code in `--json` output (`cli.usage`, `cli.no_key`, `cli.unexpected`) so you can tell them from
|
|
78
|
+
a server response.
|
|
79
|
+
|
|
80
|
+
## Configuration precedence
|
|
81
|
+
|
|
82
|
+
| Setting | Order |
|
|
83
|
+
|---|---|
|
|
84
|
+
| API key | `UBUYFIRST_API_KEY` → stored config |
|
|
85
|
+
| Base URL | `--base-url` → `UBUYFIRST_API_URL` → stored config → `https://app.ubuyfirst.com` |
|
|
86
|
+
|
|
87
|
+
## Licence
|
|
88
|
+
|
|
89
|
+
Proprietary — see [LICENSE](./LICENSE). You may install and run this software to access the
|
|
90
|
+
uBuyFirst service through your own account.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//#region src/node-floor.ts
|
|
3
|
+
/**
|
|
4
|
+
* The CLI's RUNTIME floor is Node >=22.12.0 — Commander 15's own minimum
|
|
5
|
+
* (verified at the npm registry). This is distinct from, and lower than, the
|
|
6
|
+
* BUILD floor (`devEngines.runtime` in package.json, `^22.18.0 || ^24.11.0 ||
|
|
7
|
+
* >=26.0.0` — tsdown's own floor). Never collapse the two into one number.
|
|
8
|
+
*
|
|
9
|
+
* npm's `engines` field only WARNS unless the consumer sets `engine-strict`,
|
|
10
|
+
* so the built entry (`cli.ts`) performs this check itself, before anything
|
|
11
|
+
* else runs, and exits with a message naming both versions.
|
|
12
|
+
*/
|
|
13
|
+
const REQUIRED_NODE_VERSION = "22.12.0";
|
|
14
|
+
/**
|
|
15
|
+
* Strips a leading `v` (as in `process.version`) and any pre-release /
|
|
16
|
+
* build-metadata suffix (`-nightly...`, `+build...`), then parses the
|
|
17
|
+
* remaining `major.minor.patch` core. A missing or non-numeric segment
|
|
18
|
+
* parses as 0 rather than throwing.
|
|
19
|
+
*/
|
|
20
|
+
function parseVersion(raw) {
|
|
21
|
+
const segments = (raw.replace(/^v/, "").split(/[-+]/)[0] ?? "").split(".");
|
|
22
|
+
const toNumber = (segment) => {
|
|
23
|
+
const value = Number(segment ?? "0");
|
|
24
|
+
return Number.isNaN(value) ? 0 : value;
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
major: toNumber(segments[0]),
|
|
28
|
+
minor: toNumber(segments[1]),
|
|
29
|
+
patch: toNumber(segments[2])
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function compareVersions(a, b) {
|
|
33
|
+
if (a.major !== b.major) return a.major - b.major;
|
|
34
|
+
if (a.minor !== b.minor) return a.minor - b.minor;
|
|
35
|
+
return a.patch - b.patch;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Checks `runningVersion` (defaults to `process.version`) against
|
|
39
|
+
* `REQUIRED_NODE_VERSION`. Pure and injectable so it is testable without
|
|
40
|
+
* switching Node binaries — the caller decides how to report a failure.
|
|
41
|
+
*/
|
|
42
|
+
function checkNodeFloor(runningVersion = process.version) {
|
|
43
|
+
if (compareVersions(parseVersion(runningVersion), parseVersion("22.12.0")) < 0) return {
|
|
44
|
+
ok: false,
|
|
45
|
+
message: `ubuyfirst requires Node.js >=${REQUIRED_NODE_VERSION}; the running version is ${runningVersion}.`
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
message: ""
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/cli.ts
|
|
54
|
+
const floorCheck = checkNodeFloor(process.version);
|
|
55
|
+
if (!floorCheck.ok) {
|
|
56
|
+
process.stderr.write(`${floorCheck.message}\n`);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
} else {
|
|
59
|
+
const { runProgram } = await import("./program-YTm6g9Ut.js");
|
|
60
|
+
await runProgram(process.argv.slice(2));
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export {};
|