1688-cli 0.1.41 → 0.1.43
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/AGENTS.md +112 -318
- package/ARCHITECTURE.md +107 -0
- package/CHANGELOG.md +79 -0
- package/README.md +186 -18
- package/dist/cli.js +131 -25
- package/dist/cli.js.map +1 -1
- package/dist/commands/cart-list.js +2 -1
- package/dist/commands/cart-list.js.map +1 -1
- package/dist/commands/checkout-confirm.js +8 -8
- package/dist/commands/checkout-confirm.js.map +1 -1
- package/dist/commands/compare.js +107 -0
- package/dist/commands/compare.js.map +1 -0
- package/dist/commands/doctor.js +64 -47
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/inbox.js +1 -1
- package/dist/commands/inbox.js.map +1 -1
- package/dist/commands/login.js +14 -14
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.js +6 -4
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/offer.js +7 -5
- package/dist/commands/offer.js.map +1 -1
- package/dist/commands/order-list.js +4 -2
- package/dist/commands/order-list.js.map +1 -1
- package/dist/commands/order-logistics.js +4 -2
- package/dist/commands/order-logistics.js.map +1 -1
- package/dist/commands/profile.js +25 -9
- package/dist/commands/profile.js.map +1 -1
- package/dist/commands/research.js +142 -0
- package/dist/commands/research.js.map +1 -0
- package/dist/commands/search.js +59 -18
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/seller-chat.js +1 -1
- package/dist/commands/seller-chat.js.map +1 -1
- package/dist/commands/seller-inquire.js +1 -1
- package/dist/commands/seller-inquire.js.map +1 -1
- package/dist/commands/seller-messages.js +8 -5
- package/dist/commands/seller-messages.js.map +1 -1
- package/dist/commands/sourcing-utils.js +438 -0
- package/dist/commands/sourcing-utils.js.map +1 -0
- package/dist/commands/supplier-inspect.js +559 -0
- package/dist/commands/supplier-inspect.js.map +1 -0
- package/dist/commands/supplier-search.js +522 -0
- package/dist/commands/supplier-search.js.map +1 -0
- package/dist/commands/whoami.js +6 -3
- package/dist/commands/whoami.js.map +1 -1
- package/dist/daemon/client.js +10 -6
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/manager.js +53 -37
- package/dist/daemon/manager.js.map +1 -1
- package/dist/daemon/protocol.js +2 -1
- package/dist/daemon/protocol.js.map +1 -1
- package/dist/daemon/server.js +26 -22
- package/dist/daemon/server.js.map +1 -1
- package/dist/session/context.js +1 -1
- package/dist/session/context.js.map +1 -1
- package/dist/session/dispatch.js +25 -22
- package/dist/session/dispatch.js.map +1 -1
- package/dist/session/im-ws.js +8 -5
- package/dist/session/im-ws.js.map +1 -1
- package/dist/session/lock.js +14 -14
- package/dist/session/lock.js.map +1 -1
- package/dist/session/paths.js +50 -16
- package/dist/session/paths.js.map +1 -1
- package/dist/session/search-mtop.js +53 -0
- package/dist/session/search-mtop.js.map +1 -1
- package/dist/session/shared.js +17 -7
- package/dist/session/shared.js.map +1 -1
- package/dist/session/state.js +7 -7
- package/dist/session/state.js.map +1 -1
- package/dist/session/supplier-search.js +403 -0
- package/dist/session/supplier-search.js.map +1 -0
- package/dist/util/encoding.js +8 -0
- package/dist/util/encoding.js.map +1 -0
- package/dist/util/temp.js +6 -0
- package/dist/util/temp.js.map +1 -0
- package/docs/AGENT_MAPS_PLAN.md +171 -0
- package/docs/AGENT_WORKING_PRINCIPLES.md +143 -0
- package/docs/COMMANDS.md +205 -0
- package/docs/FEATURES.md +45 -0
- package/docs/JSON_CONTRACTS.md +476 -0
- package/docs/QUALITY_SCORE.md +61 -0
- package/docs/README.md +36 -0
- package/docs/RELIABILITY.md +69 -0
- package/docs/SAFETY.md +99 -0
- package/docs/WORKFLOW.md +82 -0
- package/docs/exec-plans/README.md +9 -0
- package/docs/exec-plans/active/README.md +4 -0
- package/docs/exec-plans/completed/2026-05-28-sourcing-research-v1.md +125 -0
- package/docs/exec-plans/completed/2026-05-31-supplier-inspect-v1.md +113 -0
- package/docs/exec-plans/completed/2026-06-04-supplier-search-v1.md +81 -0
- package/docs/exec-plans/completed/2026-06-07-windows-cli-compatibility.md +138 -0
- package/docs/exec-plans/completed/2026-06-16-profile-daemon.md +146 -0
- package/docs/exec-plans/completed/README.md +4 -0
- package/docs/exec-plans/tech-debt-tracker.md +5 -0
- package/docs/generated/command-index.md +54 -0
- package/docs/generated/json-shapes.md +111 -0
- package/docs/generated/module-map.md +13 -0
- package/docs/generated/test-index.md +34 -0
- package/docs/playbooks/add-command.md +15 -0
- package/docs/playbooks/add-mtop-capture.md +13 -0
- package/docs/playbooks/change-json-output.md +11 -0
- package/docs/playbooks/debug-risk-control.md +12 -0
- package/docs/playbooks/update-cli-release.md +61 -0
- package/docs/records/release-omissions.md +34 -0
- package/docs/specs/checkout-and-orders.md +30 -0
- package/docs/specs/index.md +9 -0
- package/docs/specs/profile-daemon.md +114 -0
- package/docs/specs/seller-im.md +28 -0
- package/docs/specs/sourcing-research.md +186 -0
- package/docs/specs/supplier-inspect.md +144 -0
- package/docs/specs/supplier-search.md +179 -0
- package/docs/specs/windows-cli-compatibility.md +123 -0
- package/package.json +21 -4
- package/scripts/check_agent_map.mjs +87 -0
- package/scripts/check_release.mjs +40 -0
- package/scripts/fix_bin_mode.mjs +18 -0
- package/scripts/generate_agent_context.mjs +253 -0
- package/scripts/postinstall.mjs +12 -4
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Spec: Profile Daemon
|
|
2
|
+
|
|
3
|
+
> Status: draft
|
|
4
|
+
> Product: 1688-cli daemon/session runtime
|
|
5
|
+
> Scope: profile-scoped daemon processes, browser contexts, locks, runtime artifacts, and diagnostics
|
|
6
|
+
|
|
7
|
+
## 1. Summary
|
|
8
|
+
|
|
9
|
+
`1688-cli` supports multiple local profiles, but the warm daemon runtime is
|
|
10
|
+
currently global/default-oriented. This spec changes daemon ownership to one
|
|
11
|
+
daemon per profile so commands run against the daemon for their selected
|
|
12
|
+
profile, different profiles can operate in parallel, and default-profile
|
|
13
|
+
behavior remains compatible for users who do not pass `--profile`.
|
|
14
|
+
|
|
15
|
+
## 2. Goals
|
|
16
|
+
|
|
17
|
+
- Resolve every command profile to `default` when no profile is supplied.
|
|
18
|
+
- Support `1688 serve --profile <name>`.
|
|
19
|
+
- Support `1688 daemon start|stop|status|reload --profile <name>`.
|
|
20
|
+
- Route ordinary dispatched commands such as `search --profile acc-a` to the
|
|
21
|
+
daemon for `acc-a` when that daemon is reachable.
|
|
22
|
+
- Keep headed mode and explicitly daemon-disabled runs inline.
|
|
23
|
+
- Use one persistent browser context per daemon, bound to that daemon profile.
|
|
24
|
+
- Use profile-scoped lock, socket or named pipe, pid, version, log, and state
|
|
25
|
+
artifacts.
|
|
26
|
+
- Allow different profiles to run concurrently without sharing one process lock.
|
|
27
|
+
- Surface profile names in daemon, lock, and diagnostic error messages.
|
|
28
|
+
- Preserve default behavior for commands that omit `--profile`.
|
|
29
|
+
|
|
30
|
+
## 3. Non-goals
|
|
31
|
+
|
|
32
|
+
- Do not build one daemon that multiplexes multiple profiles.
|
|
33
|
+
- Do not change checkout confirmation safety or route `checkout confirm`
|
|
34
|
+
through the daemon.
|
|
35
|
+
- Do not run live 1688 login, search, or browser mutation checks as automated
|
|
36
|
+
verification.
|
|
37
|
+
- Do not change command result contracts except for additive diagnostic fields
|
|
38
|
+
on daemon/profile/doctor surfaces.
|
|
39
|
+
- Do not migrate or delete historical global daemon artifacts automatically
|
|
40
|
+
beyond normal stale-artifact cleanup for the selected profile.
|
|
41
|
+
|
|
42
|
+
## 4. Behavior contract
|
|
43
|
+
|
|
44
|
+
- `defaultProfileName(profile)` resolves missing, empty, or whitespace-only
|
|
45
|
+
profile input to `default`.
|
|
46
|
+
- Runtime artifact helpers accept an optional profile argument and use the
|
|
47
|
+
resolved profile:
|
|
48
|
+
- `socketPath(profile)`
|
|
49
|
+
- `pidFile(profile)`
|
|
50
|
+
- `daemonVersionFile(profile)`
|
|
51
|
+
- `daemonLogFile(profile)`
|
|
52
|
+
- `lockFile(profile)`
|
|
53
|
+
- `stateFile(profile)`
|
|
54
|
+
- Windows named pipe names include both the root hash and a profile-derived
|
|
55
|
+
segment so two profiles under the same `BB1688_HOME` do not collide.
|
|
56
|
+
- Non-Windows daemon sockets are profile-scoped filesystem paths under the
|
|
57
|
+
selected profile/runtime area.
|
|
58
|
+
- `acquireLock(profile)` locks only the selected profile.
|
|
59
|
+
- Inline sessions use `profilePath(profile)` and `acquireLock(profile)`.
|
|
60
|
+
- A daemon process is bound to exactly one profile at startup.
|
|
61
|
+
- `getSharedContext(profile)` creates or reuses the browser context only for
|
|
62
|
+
the daemon-bound profile and stores cookies/session in that profile's
|
|
63
|
+
persistent context directory.
|
|
64
|
+
- `runOnSharedCtx` serializes operations within one daemon process only.
|
|
65
|
+
- `dispatch(name, args, { profile })` attempts the selected profile daemon
|
|
66
|
+
unless headed, no-daemon, or `BB1688_NO_DAEMON=1` is set.
|
|
67
|
+
- If inline fallback is needed, dispatch pauses only the selected profile
|
|
68
|
+
daemon, not daemons for other profiles.
|
|
69
|
+
- `login --profile <name>` writes identity state for that profile and, unless
|
|
70
|
+
`--no-daemon` is set, attempts to start that profile daemon after login or
|
|
71
|
+
after detecting an already-logged-in profile.
|
|
72
|
+
- `doctor --profile <name>` checks the selected profile's directory, lock,
|
|
73
|
+
state, daemon, and live daemon socket status.
|
|
74
|
+
- `profile status <name>` reports the selected profile's profile directory,
|
|
75
|
+
profile-scoped lock, profile-scoped state, recent event, and daemon status.
|
|
76
|
+
- Error messages for daemon running, start timeout, lock busy, stale daemon, and
|
|
77
|
+
daemon pause identify the affected profile.
|
|
78
|
+
|
|
79
|
+
## 5. Verification
|
|
80
|
+
|
|
81
|
+
- Focused unit coverage for profile-scoped paths, including Windows pipe names.
|
|
82
|
+
- Focused unit coverage for profile status using profile-scoped locks/states.
|
|
83
|
+
- Focused unit coverage for doctor platform helpers and profile-aware daemon
|
|
84
|
+
checks where deterministic.
|
|
85
|
+
- Typecheck with `pnpm typecheck`.
|
|
86
|
+
- Deterministic tests with `pnpm test:unit`.
|
|
87
|
+
- Regenerate generated docs with `pnpm agent-context` after command/source/test
|
|
88
|
+
changes.
|
|
89
|
+
- Run `pnpm agent-verify` as the final local gate.
|
|
90
|
+
|
|
91
|
+
## 6. Acceptance criteria
|
|
92
|
+
|
|
93
|
+
- `1688 daemon start --profile acc-a` starts only the `acc-a` daemon.
|
|
94
|
+
- `1688 daemon start --profile acc-b` can coexist with `acc-a`.
|
|
95
|
+
- `1688 daemon status --profile acc-a` and `--profile acc-b` inspect different
|
|
96
|
+
artifacts and sockets.
|
|
97
|
+
- `1688 serve --profile acc-a` binds a daemon to `acc-a`.
|
|
98
|
+
- `1688 search "..." --profile acc-a` tries the `acc-a` daemon first.
|
|
99
|
+
- `1688 search "..." --profile acc-b` tries the `acc-b` daemon first.
|
|
100
|
+
- A lock held by `acc-a` does not make `acc-b` report `LOCK_BUSY`.
|
|
101
|
+
- A daemon pause or risk-control state in one profile is represented in that
|
|
102
|
+
profile daemon status and does not stop another profile daemon.
|
|
103
|
+
- Commands without `--profile` continue to use the `default` profile.
|
|
104
|
+
|
|
105
|
+
## 7. Assumptions and open questions
|
|
106
|
+
|
|
107
|
+
- [ASSUMED] Profile names used by real workflows are simple names such as
|
|
108
|
+
`default`, `acc-a`, or `work`, without path separators.
|
|
109
|
+
- [ASSUMED] Moving default daemon artifacts behind profile-aware helpers is
|
|
110
|
+
compatible because all public commands use those helpers rather than fixed
|
|
111
|
+
artifact paths.
|
|
112
|
+
- [ASSUMED] Historical global `state.json` does not need an automated migration
|
|
113
|
+
for this change; a profile-specific state file is authoritative once this
|
|
114
|
+
version runs.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Seller IM
|
|
2
|
+
|
|
3
|
+
Seller IM commands cover pre-sale inquiry and post-sale follow-up through
|
|
4
|
+
Wangwang/1688 chat.
|
|
5
|
+
|
|
6
|
+
## Commands
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
1688 seller inquire <offerId> <message>
|
|
10
|
+
1688 seller chat <orderId|loginId> <message>
|
|
11
|
+
1688 seller messages --offer <offerId>
|
|
12
|
+
1688 seller messages <orderId|loginId>
|
|
13
|
+
1688 seller messages ... --watch
|
|
14
|
+
1688 inbox
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Safety
|
|
18
|
+
|
|
19
|
+
Sending messages contacts real suppliers. Follow `docs/SAFETY.md` before
|
|
20
|
+
sending agent-authored text.
|
|
21
|
+
|
|
22
|
+
## Agent Requirements
|
|
23
|
+
|
|
24
|
+
- Preserve line-delimited JSON in watch mode.
|
|
25
|
+
- Deduplicate messages by server-side `messageId` when available.
|
|
26
|
+
- Preserve `kind` and `card` fields so agents can distinguish text, offer
|
|
27
|
+
cards, order cards, images, and auto replies.
|
|
28
|
+
- Keep order/offer scoped conversations attached to the right context.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Sourcing Research
|
|
2
|
+
|
|
3
|
+
This spec defines the first durable sourcing-research layer for `1688-cli`.
|
|
4
|
+
It turns search results into procurement decisions while preserving the CLI's
|
|
5
|
+
buyer-workflow identity: human-paced, logged-in, safe for a real account, and
|
|
6
|
+
agent-friendly JSON.
|
|
7
|
+
|
|
8
|
+
## Goal
|
|
9
|
+
|
|
10
|
+
Help an agent or buyer answer:
|
|
11
|
+
|
|
12
|
+
- Which offers have demand?
|
|
13
|
+
- Which suppliers look trustworthy?
|
|
14
|
+
- Which offers are cheap enough for the target range?
|
|
15
|
+
- Which offers deserve detail-page enrichment?
|
|
16
|
+
- Which offer IDs should be compared before inquiry/cart/checkout?
|
|
17
|
+
|
|
18
|
+
## Non-Goals
|
|
19
|
+
|
|
20
|
+
- Do not build a bulk scraping farm.
|
|
21
|
+
- Do not bypass login, risk control, or slider verification.
|
|
22
|
+
- Do not add multi-account orchestration.
|
|
23
|
+
- Do not claim supplier scores, repurchase rate, or service guarantees unless
|
|
24
|
+
the current 1688 payload exposes them reliably.
|
|
25
|
+
- Do not make ordinary `search` slow by fetching every detail page.
|
|
26
|
+
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
### `search`
|
|
30
|
+
|
|
31
|
+
Add research-oriented read-only controls to ordinary keyword search:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
1688 search <keyword> \
|
|
35
|
+
--sort relevance|best-selling|price-asc|price-desc \
|
|
36
|
+
--price-min 1 \
|
|
37
|
+
--price-max 50 \
|
|
38
|
+
--province 广东 \
|
|
39
|
+
--city 深圳 \
|
|
40
|
+
--verified any|factory|business|super-factory \
|
|
41
|
+
--min-turnover 100 \
|
|
42
|
+
--exclude-ads
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`search` remains fast. Filters and local sort apply to the collected result
|
|
46
|
+
set. Remote sort parameters may be added to the search URL when known, but the
|
|
47
|
+
command must still locally normalize output ordering for deterministic agent
|
|
48
|
+
behavior.
|
|
49
|
+
|
|
50
|
+
### `research`
|
|
51
|
+
|
|
52
|
+
Add a multi-keyword research command:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
1688 research <keyword...> \
|
|
56
|
+
--max-per-query 60 \
|
|
57
|
+
--sort best-selling \
|
|
58
|
+
--price-max 50 \
|
|
59
|
+
--verified super-factory \
|
|
60
|
+
--enrich top:10 \
|
|
61
|
+
--jsonl
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`research` runs keyword searches one by one, applies the same filters, scores
|
|
65
|
+
offers, deduplicates by `offerId`, and optionally enriches only the top N
|
|
66
|
+
results by calling `offer`.
|
|
67
|
+
|
|
68
|
+
Supported export modes:
|
|
69
|
+
|
|
70
|
+
- default: human table
|
|
71
|
+
- JSON: normal automatic JSON when stdout is piped or `--json` is used
|
|
72
|
+
- `--jsonl`: one research item per line
|
|
73
|
+
- `--csv`: comma-separated table
|
|
74
|
+
- optional `--output <file>`: write export to a file
|
|
75
|
+
|
|
76
|
+
### `compare`
|
|
77
|
+
|
|
78
|
+
Add a read-only offer comparison command:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
1688 compare <offerId...>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
It fetches each offer detail, computes comparable fields and a sourcing score,
|
|
85
|
+
and shows price, MOQ, sale count, SKU count, stock, supplier, freight/package
|
|
86
|
+
hints, and detail fetch errors.
|
|
87
|
+
|
|
88
|
+
### `supplier inspect`
|
|
89
|
+
|
|
90
|
+
Supplier-level inspection now lives in
|
|
91
|
+
[`supplier-inspect.md`](supplier-inspect.md). V1 supports offerId, offer URL,
|
|
92
|
+
`b2b-*` memberId, and factory-card URL. Direct loginId lookup remains out of
|
|
93
|
+
scope because live probing showed it can resolve to the wrong factory.
|
|
94
|
+
|
|
95
|
+
### `supplier search` / `supplier research`
|
|
96
|
+
|
|
97
|
+
Supplier discovery from 1688's company search is specified separately in
|
|
98
|
+
[`supplier-search.md`](supplier-search.md). These commands must use company
|
|
99
|
+
search payloads and must not build supplier lists by aggregating offer-search
|
|
100
|
+
results.
|
|
101
|
+
|
|
102
|
+
## Data Model
|
|
103
|
+
|
|
104
|
+
### Search Item
|
|
105
|
+
|
|
106
|
+
Each `search` offer may include the existing fields plus additive research
|
|
107
|
+
signals:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
{
|
|
111
|
+
offerId: string,
|
|
112
|
+
title: string,
|
|
113
|
+
price: { text: string, min: number | null, max: number | null },
|
|
114
|
+
supplier: { name: string | null, shopUrl: string | null, years: number | null },
|
|
115
|
+
verified: { factory: boolean, business: boolean, superFactory: boolean },
|
|
116
|
+
tags: string[],
|
|
117
|
+
isP4P: boolean,
|
|
118
|
+
turnover: string | null,
|
|
119
|
+
demand?: {
|
|
120
|
+
orderCountText: string | null,
|
|
121
|
+
orderCount: number | null,
|
|
122
|
+
repurchaseRateText: string | null,
|
|
123
|
+
repurchaseRate: number | null,
|
|
124
|
+
},
|
|
125
|
+
serviceTags?: string[],
|
|
126
|
+
productBadges?: string[],
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Research Item
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
{
|
|
134
|
+
sourceKeyword: string,
|
|
135
|
+
sourceRank: number,
|
|
136
|
+
globalRank: number,
|
|
137
|
+
offer: Offer,
|
|
138
|
+
demand: {
|
|
139
|
+
turnoverText: string | null,
|
|
140
|
+
orderCount: number | null,
|
|
141
|
+
repurchaseRate: number | null,
|
|
142
|
+
},
|
|
143
|
+
supplier: {
|
|
144
|
+
years: number | null,
|
|
145
|
+
verified: Offer["verified"],
|
|
146
|
+
tags: string[],
|
|
147
|
+
isAd: boolean,
|
|
148
|
+
},
|
|
149
|
+
score: number,
|
|
150
|
+
scoreBreakdown: Array<{ name: string, points: number, reason: string }>,
|
|
151
|
+
enriched?: OfferDetailSummary,
|
|
152
|
+
error?: { code: string, message: string },
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Sourcing Score V1
|
|
157
|
+
|
|
158
|
+
The score is explainable and bounded to 100.
|
|
159
|
+
|
|
160
|
+
- Price: up to 25 points for a valid low price.
|
|
161
|
+
- Demand: up to 25 points from turnover/order count.
|
|
162
|
+
- Supplier tenure: up to 15 points from shop years.
|
|
163
|
+
- Verification: up to 15 points for super factory, factory, or business
|
|
164
|
+
verification.
|
|
165
|
+
- Service tags: up to 10 points from tags/service badges.
|
|
166
|
+
- Organic result: up to 10 points when the offer is not P4P/ad.
|
|
167
|
+
|
|
168
|
+
The score is a ranking aid, not a truth claim.
|
|
169
|
+
|
|
170
|
+
## Failure Semantics
|
|
171
|
+
|
|
172
|
+
For `research` and `compare`, distinguish:
|
|
173
|
+
|
|
174
|
+
- run-level failure: login expired, risk control, browser/network failure that
|
|
175
|
+
prevents the command from continuing.
|
|
176
|
+
- item-level failure: one offer detail fails during enrichment or comparison.
|
|
177
|
+
|
|
178
|
+
Item-level failures stay attached to the item and should not fail the whole
|
|
179
|
+
run unless every item fails.
|
|
180
|
+
|
|
181
|
+
## Verification
|
|
182
|
+
|
|
183
|
+
- Unit tests cover sorting, filters, score calculation, export formatting, and
|
|
184
|
+
enrichment option parsing.
|
|
185
|
+
- `pnpm agent-context` refreshes generated command and JSON-shape indexes.
|
|
186
|
+
- `pnpm agent-verify` is the default gate.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Supplier Inspect
|
|
2
|
+
|
|
3
|
+
This spec defines the first reliable supplier-level inspection command for
|
|
4
|
+
`1688-cli`. It is read-only and aimed at sourcing decisions after a buyer or
|
|
5
|
+
agent has found an offer or supplier `memberId`.
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Help an agent or buyer answer:
|
|
10
|
+
|
|
11
|
+
- Who is the supplier behind this offer?
|
|
12
|
+
- Does the supplier expose factory/trust/service signals?
|
|
13
|
+
- What factory card data is available: location, years, authentication,
|
|
14
|
+
production scope, staff/scale hints, and available offer count?
|
|
15
|
+
- Which fields are observed from 1688 payloads versus unavailable?
|
|
16
|
+
|
|
17
|
+
## Non-Goals
|
|
18
|
+
|
|
19
|
+
- Do not bypass login, risk control, or slider verification.
|
|
20
|
+
- Do not bulk scrape supplier catalogs.
|
|
21
|
+
- Do not claim loginId lookup is reliable when the current site cannot resolve
|
|
22
|
+
it deterministically.
|
|
23
|
+
- Do not perform write actions such as inquiry, favorite, cart, or checkout.
|
|
24
|
+
- Do not invent scores that are not backed by observed payload fields.
|
|
25
|
+
|
|
26
|
+
## Probe Findings
|
|
27
|
+
|
|
28
|
+
Live headed probe on 2026-05-31 found these useful sources:
|
|
29
|
+
|
|
30
|
+
- Offer detail page:
|
|
31
|
+
- `window.context.result.global.globalData.model.sellerModel`
|
|
32
|
+
- `mtop.1688.moga.pc.shopcard`
|
|
33
|
+
- Factory card page:
|
|
34
|
+
- `https://sale.1688.com/factory/card.html?memberId=<memberId>`
|
|
35
|
+
- `mtop.com.alibaba.china.factory.card.common.fn.mtop.tpp.faas`
|
|
36
|
+
- Factory card DOM text can expose a visible available-offer count such as
|
|
37
|
+
`共34个商品`.
|
|
38
|
+
|
|
39
|
+
Direct `loginId` factory-card lookup is not reliable. A probe with
|
|
40
|
+
`loginId=<sellerLoginId>` returned a different factory, so V1 must reject
|
|
41
|
+
loginId-only input with a clear error instead of returning possibly wrong data.
|
|
42
|
+
|
|
43
|
+
## Command
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
1688 supplier inspect <offerId|memberId|offerUrl|factoryCardUrl>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Supported target forms:
|
|
50
|
+
|
|
51
|
+
- numeric `offerId`
|
|
52
|
+
- `https://detail.1688.com/offer/<offerId>.html`
|
|
53
|
+
- `b2b-*` supplier `memberId`
|
|
54
|
+
- factory-card URL with a `memberId` query parameter
|
|
55
|
+
|
|
56
|
+
Unsupported in V1:
|
|
57
|
+
|
|
58
|
+
- loginId-only input, because live probe showed it can misresolve
|
|
59
|
+
|
|
60
|
+
Options:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
--profile <name>
|
|
64
|
+
--headed
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## JSON Contract
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
{
|
|
71
|
+
target: {
|
|
72
|
+
input: string,
|
|
73
|
+
type: "offerId" | "memberId",
|
|
74
|
+
offerId: string | null,
|
|
75
|
+
memberId: string | null,
|
|
76
|
+
},
|
|
77
|
+
supplier: {
|
|
78
|
+
name: string | null,
|
|
79
|
+
loginId: string | null,
|
|
80
|
+
memberId: string | null,
|
|
81
|
+
userId: string | null,
|
|
82
|
+
companyId: string | null,
|
|
83
|
+
shopUrl: string | null,
|
|
84
|
+
shopUrls: Record<string, string>,
|
|
85
|
+
identity: string | null,
|
|
86
|
+
signs: Record<string, boolean>,
|
|
87
|
+
},
|
|
88
|
+
factory: {
|
|
89
|
+
isFactory: boolean,
|
|
90
|
+
superFactory: boolean,
|
|
91
|
+
tpYears: number | null,
|
|
92
|
+
medalLevel: string | null,
|
|
93
|
+
thirdPartyAuthProvider: string | null,
|
|
94
|
+
establishedAtText: string | null,
|
|
95
|
+
location: string | null,
|
|
96
|
+
address: string | null,
|
|
97
|
+
coordinates: { latitude: number | null, longitude: number | null },
|
|
98
|
+
productionService: string | null,
|
|
99
|
+
employeeScale: string | null,
|
|
100
|
+
workerCount: string | null,
|
|
101
|
+
profile: string | null,
|
|
102
|
+
tags: string[],
|
|
103
|
+
},
|
|
104
|
+
trust: {
|
|
105
|
+
companyLabel: string | null,
|
|
106
|
+
retentionRate: number | null,
|
|
107
|
+
companyIcons: Array<{ title: string; link: string | null }>,
|
|
108
|
+
shopTags: string[],
|
|
109
|
+
serviceScores: Array<{ key: string; label: string; score: number | null }>,
|
|
110
|
+
},
|
|
111
|
+
offers: {
|
|
112
|
+
availableCount: number | null,
|
|
113
|
+
source: "factory-card-dom" | null,
|
|
114
|
+
},
|
|
115
|
+
sources: {
|
|
116
|
+
offerUrl: string | null,
|
|
117
|
+
factoryCardUrl: string | null,
|
|
118
|
+
shopcardCaptured: boolean,
|
|
119
|
+
factoryCardCaptured: boolean,
|
|
120
|
+
},
|
|
121
|
+
warnings: string[],
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
All fields are additive and nullable. Missing values mean the current page or
|
|
126
|
+
payload did not expose the signal.
|
|
127
|
+
|
|
128
|
+
## Failure Semantics
|
|
129
|
+
|
|
130
|
+
- `BAD_INPUT`: target is empty, malformed, or loginId-only.
|
|
131
|
+
- `NOT_LOGGED_IN`: session expired.
|
|
132
|
+
- `RISK_CONTROL`: 1688 risk challenge appeared; retry with `--headed`.
|
|
133
|
+
- `NETWORK_ERROR`: navigation failed.
|
|
134
|
+
- `SUPPLIER_NOT_FOUND`: no supplier identity could be read from a supported
|
|
135
|
+
target.
|
|
136
|
+
|
|
137
|
+
## Verification
|
|
138
|
+
|
|
139
|
+
- Unit tests cover target normalization and payload assembly helpers.
|
|
140
|
+
- A live smoke test should inspect a known offerId and confirm:
|
|
141
|
+
- supplier identity is present
|
|
142
|
+
- memberId is present
|
|
143
|
+
- factory-card capture succeeds when memberId exists
|
|
144
|
+
- `loginId` direct input fails with `BAD_INPUT`
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Supplier Search And Research
|
|
2
|
+
|
|
3
|
+
This spec defines supplier discovery that starts from 1688's company search,
|
|
4
|
+
not product-offer aggregation.
|
|
5
|
+
|
|
6
|
+
## Goal
|
|
7
|
+
|
|
8
|
+
Help a buyer or agent answer:
|
|
9
|
+
|
|
10
|
+
- Which suppliers match this category keyword?
|
|
11
|
+
- Which matching suppliers expose factory/trust/service signals?
|
|
12
|
+
- Which suppliers deserve deeper `supplier inspect` enrichment?
|
|
13
|
+
- Which suppliers should be contacted or compared after product discovery?
|
|
14
|
+
|
|
15
|
+
## Source Boundary
|
|
16
|
+
|
|
17
|
+
`supplier search` and `supplier research` must use 1688 company search. The
|
|
18
|
+
known durable business endpoint from live probing is:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
search.1688.com/service/companySearchBusinessService
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The page entry URL is:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
https://s.1688.com/company/company_search.htm?keywords=<GBK-percent-keyword>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Important encoding rule: `s.1688.com` expects GBK percent-encoded keywords.
|
|
31
|
+
UTF-8 percent-encoding can search for mojibake and return zero or irrelevant
|
|
32
|
+
results.
|
|
33
|
+
|
|
34
|
+
Do not implement supplier discovery by running offer search and grouping
|
|
35
|
+
offers by supplier. That is a different signal and can hide suppliers that are
|
|
36
|
+
available in company search.
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
### `supplier search`
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
1688 supplier search <keyword...> \
|
|
44
|
+
--max 20 \
|
|
45
|
+
--factory-only \
|
|
46
|
+
--province 广东 \
|
|
47
|
+
--city 深圳 \
|
|
48
|
+
--min-years 3 \
|
|
49
|
+
--min-repeat-rate 0.4 \
|
|
50
|
+
--min-response-rate 0.6 \
|
|
51
|
+
--enrich 0
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Default behavior is supplier discovery only. `--enrich` is optional and
|
|
55
|
+
defaults to `0`.
|
|
56
|
+
|
|
57
|
+
### `supplier research`
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
1688 supplier research <keyword...> \
|
|
61
|
+
--max 20 \
|
|
62
|
+
--factory-only \
|
|
63
|
+
--enrich top:10 \
|
|
64
|
+
--jsonl
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`supplier research` uses the same company-search source and scoring, but
|
|
68
|
+
defaults to `--enrich top:10`. Enrichment calls `supplier inspect` with the
|
|
69
|
+
company-search `memberId` when present.
|
|
70
|
+
|
|
71
|
+
Supported export modes:
|
|
72
|
+
|
|
73
|
+
- default: human table
|
|
74
|
+
- JSON: automatic when stdout is piped or `--json` is used
|
|
75
|
+
- `--jsonl`: one supplier item per line
|
|
76
|
+
- `--csv`: comma-separated table
|
|
77
|
+
- `--output <file>`: write JSONL/CSV to a file
|
|
78
|
+
|
|
79
|
+
## Data Model
|
|
80
|
+
|
|
81
|
+
Each item records source keyword/rank, normalized company-search supplier
|
|
82
|
+
signals, score, and optional inspect enrichment:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
{
|
|
86
|
+
sourceKeyword: string,
|
|
87
|
+
sourceRank: number,
|
|
88
|
+
globalRank: number,
|
|
89
|
+
supplier: {
|
|
90
|
+
companyName: string,
|
|
91
|
+
loginId: string | null,
|
|
92
|
+
memberId: string | null,
|
|
93
|
+
enterpriseId: string | null,
|
|
94
|
+
realUserId: string | null,
|
|
95
|
+
companyId: string | null,
|
|
96
|
+
shopUrl: string | null,
|
|
97
|
+
factoryCardUrl: string | null,
|
|
98
|
+
location: { province: string | null, city: string | null, address: string | null },
|
|
99
|
+
productionService: string | null,
|
|
100
|
+
tp: { serviceYears: number | null, memberLevel: string | null },
|
|
101
|
+
factory: {
|
|
102
|
+
isFactory: boolean,
|
|
103
|
+
factoryTag: string | null,
|
|
104
|
+
factoryLevel: string | null,
|
|
105
|
+
superFactory: boolean,
|
|
106
|
+
businessInspection: boolean,
|
|
107
|
+
factoryInspection: boolean,
|
|
108
|
+
},
|
|
109
|
+
service: {
|
|
110
|
+
compositeScore: number | null,
|
|
111
|
+
wwResponseRate: number | null,
|
|
112
|
+
repeatRate: number | null,
|
|
113
|
+
},
|
|
114
|
+
demand: {
|
|
115
|
+
payOrderCount3m: number | null,
|
|
116
|
+
payAmount3m: number | null,
|
|
117
|
+
fuzzyPayAmount3m: string | null,
|
|
118
|
+
saleQuantity3m: number | null,
|
|
119
|
+
},
|
|
120
|
+
tags: string[],
|
|
121
|
+
offersPreview: SupplierOfferPreview[],
|
|
122
|
+
},
|
|
123
|
+
score: number,
|
|
124
|
+
scoreBreakdown: Array<{ name: string, points: number, reason: string }>,
|
|
125
|
+
inspect?: SupplierInspectResult,
|
|
126
|
+
error?: { code: string, message: string },
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The top-level result includes:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
source: {
|
|
134
|
+
kind: "company-search",
|
|
135
|
+
endpoint: "companySearchBusinessService",
|
|
136
|
+
offerAggregation: false,
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Score V1
|
|
141
|
+
|
|
142
|
+
The supplier score is a ranking aid, not a truth claim.
|
|
143
|
+
|
|
144
|
+
- Company-search demand: up to 25 points from 3-month pay order count.
|
|
145
|
+
- Supplier tenure: up to 15 points from service years.
|
|
146
|
+
- Factory/trust: up to 20 points from factory/super-factory/inspection flags.
|
|
147
|
+
- Service rates: up to 15 points from repeat and Wangwang response rates.
|
|
148
|
+
- Composite score: up to 10 points.
|
|
149
|
+
- Offer preview depth: up to 10 points from company-search previews.
|
|
150
|
+
|
|
151
|
+
## Failure Semantics
|
|
152
|
+
|
|
153
|
+
- Run-level failure: login expired, risk control, browser/network failure that
|
|
154
|
+
prevents company search from loading.
|
|
155
|
+
- Empty result: company search loads but returns no supplier payload.
|
|
156
|
+
- Item-level enrichment failure: `supplier inspect` fails for one supplier;
|
|
157
|
+
keep the supplier item and attach `error`.
|
|
158
|
+
|
|
159
|
+
If a command exits with risk-control code `4`, retry once with `--headed` and
|
|
160
|
+
solve the slider manually.
|
|
161
|
+
|
|
162
|
+
## V1 Boundaries
|
|
163
|
+
|
|
164
|
+
Live probing on 2026-06-04 showed the company search page emits
|
|
165
|
+
`companySearchBusinessService` with `companyWithOfferLists`. A typical first
|
|
166
|
+
page async response used `startIndex=6&asyncCount=14`; this likely means some
|
|
167
|
+
top-page suppliers may be server-rendered before the async service response.
|
|
168
|
+
V1 uses the stable browser-emitted business response and keeps the largest
|
|
169
|
+
captured company-search payload. A later V2 can add HTML/DOM extraction for
|
|
170
|
+
server-rendered supplier cards if we need exact 20-per-page completeness.
|
|
171
|
+
|
|
172
|
+
## Verification
|
|
173
|
+
|
|
174
|
+
- Unit tests cover GBK company-search URL construction.
|
|
175
|
+
- Unit tests cover `companySearchBusinessService` parsing and offer previews.
|
|
176
|
+
- Unit tests cover capture `keep: "largest"` behavior.
|
|
177
|
+
- Unit tests cover enrich option parsing and CSV escaping.
|
|
178
|
+
- `pnpm agent-context` refreshes generated command and JSON-shape indexes.
|
|
179
|
+
- `pnpm agent-verify` is the default gate.
|