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.
Files changed (119) hide show
  1. package/AGENTS.md +112 -318
  2. package/ARCHITECTURE.md +107 -0
  3. package/CHANGELOG.md +79 -0
  4. package/README.md +186 -18
  5. package/dist/cli.js +131 -25
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/cart-list.js +2 -1
  8. package/dist/commands/cart-list.js.map +1 -1
  9. package/dist/commands/checkout-confirm.js +8 -8
  10. package/dist/commands/checkout-confirm.js.map +1 -1
  11. package/dist/commands/compare.js +107 -0
  12. package/dist/commands/compare.js.map +1 -0
  13. package/dist/commands/doctor.js +64 -47
  14. package/dist/commands/doctor.js.map +1 -1
  15. package/dist/commands/inbox.js +1 -1
  16. package/dist/commands/inbox.js.map +1 -1
  17. package/dist/commands/login.js +14 -14
  18. package/dist/commands/login.js.map +1 -1
  19. package/dist/commands/logout.js +6 -4
  20. package/dist/commands/logout.js.map +1 -1
  21. package/dist/commands/offer.js +7 -5
  22. package/dist/commands/offer.js.map +1 -1
  23. package/dist/commands/order-list.js +4 -2
  24. package/dist/commands/order-list.js.map +1 -1
  25. package/dist/commands/order-logistics.js +4 -2
  26. package/dist/commands/order-logistics.js.map +1 -1
  27. package/dist/commands/profile.js +25 -9
  28. package/dist/commands/profile.js.map +1 -1
  29. package/dist/commands/research.js +142 -0
  30. package/dist/commands/research.js.map +1 -0
  31. package/dist/commands/search.js +59 -18
  32. package/dist/commands/search.js.map +1 -1
  33. package/dist/commands/seller-chat.js +1 -1
  34. package/dist/commands/seller-chat.js.map +1 -1
  35. package/dist/commands/seller-inquire.js +1 -1
  36. package/dist/commands/seller-inquire.js.map +1 -1
  37. package/dist/commands/seller-messages.js +8 -5
  38. package/dist/commands/seller-messages.js.map +1 -1
  39. package/dist/commands/sourcing-utils.js +438 -0
  40. package/dist/commands/sourcing-utils.js.map +1 -0
  41. package/dist/commands/supplier-inspect.js +559 -0
  42. package/dist/commands/supplier-inspect.js.map +1 -0
  43. package/dist/commands/supplier-search.js +522 -0
  44. package/dist/commands/supplier-search.js.map +1 -0
  45. package/dist/commands/whoami.js +6 -3
  46. package/dist/commands/whoami.js.map +1 -1
  47. package/dist/daemon/client.js +10 -6
  48. package/dist/daemon/client.js.map +1 -1
  49. package/dist/daemon/manager.js +53 -37
  50. package/dist/daemon/manager.js.map +1 -1
  51. package/dist/daemon/protocol.js +2 -1
  52. package/dist/daemon/protocol.js.map +1 -1
  53. package/dist/daemon/server.js +26 -22
  54. package/dist/daemon/server.js.map +1 -1
  55. package/dist/session/context.js +1 -1
  56. package/dist/session/context.js.map +1 -1
  57. package/dist/session/dispatch.js +25 -22
  58. package/dist/session/dispatch.js.map +1 -1
  59. package/dist/session/im-ws.js +8 -5
  60. package/dist/session/im-ws.js.map +1 -1
  61. package/dist/session/lock.js +14 -14
  62. package/dist/session/lock.js.map +1 -1
  63. package/dist/session/paths.js +50 -16
  64. package/dist/session/paths.js.map +1 -1
  65. package/dist/session/search-mtop.js +53 -0
  66. package/dist/session/search-mtop.js.map +1 -1
  67. package/dist/session/shared.js +17 -7
  68. package/dist/session/shared.js.map +1 -1
  69. package/dist/session/state.js +7 -7
  70. package/dist/session/state.js.map +1 -1
  71. package/dist/session/supplier-search.js +403 -0
  72. package/dist/session/supplier-search.js.map +1 -0
  73. package/dist/util/encoding.js +8 -0
  74. package/dist/util/encoding.js.map +1 -0
  75. package/dist/util/temp.js +6 -0
  76. package/dist/util/temp.js.map +1 -0
  77. package/docs/AGENT_MAPS_PLAN.md +171 -0
  78. package/docs/AGENT_WORKING_PRINCIPLES.md +143 -0
  79. package/docs/COMMANDS.md +205 -0
  80. package/docs/FEATURES.md +45 -0
  81. package/docs/JSON_CONTRACTS.md +476 -0
  82. package/docs/QUALITY_SCORE.md +61 -0
  83. package/docs/README.md +36 -0
  84. package/docs/RELIABILITY.md +69 -0
  85. package/docs/SAFETY.md +99 -0
  86. package/docs/WORKFLOW.md +82 -0
  87. package/docs/exec-plans/README.md +9 -0
  88. package/docs/exec-plans/active/README.md +4 -0
  89. package/docs/exec-plans/completed/2026-05-28-sourcing-research-v1.md +125 -0
  90. package/docs/exec-plans/completed/2026-05-31-supplier-inspect-v1.md +113 -0
  91. package/docs/exec-plans/completed/2026-06-04-supplier-search-v1.md +81 -0
  92. package/docs/exec-plans/completed/2026-06-07-windows-cli-compatibility.md +138 -0
  93. package/docs/exec-plans/completed/2026-06-16-profile-daemon.md +146 -0
  94. package/docs/exec-plans/completed/README.md +4 -0
  95. package/docs/exec-plans/tech-debt-tracker.md +5 -0
  96. package/docs/generated/command-index.md +54 -0
  97. package/docs/generated/json-shapes.md +111 -0
  98. package/docs/generated/module-map.md +13 -0
  99. package/docs/generated/test-index.md +34 -0
  100. package/docs/playbooks/add-command.md +15 -0
  101. package/docs/playbooks/add-mtop-capture.md +13 -0
  102. package/docs/playbooks/change-json-output.md +11 -0
  103. package/docs/playbooks/debug-risk-control.md +12 -0
  104. package/docs/playbooks/update-cli-release.md +61 -0
  105. package/docs/records/release-omissions.md +34 -0
  106. package/docs/specs/checkout-and-orders.md +30 -0
  107. package/docs/specs/index.md +9 -0
  108. package/docs/specs/profile-daemon.md +114 -0
  109. package/docs/specs/seller-im.md +28 -0
  110. package/docs/specs/sourcing-research.md +186 -0
  111. package/docs/specs/supplier-inspect.md +144 -0
  112. package/docs/specs/supplier-search.md +179 -0
  113. package/docs/specs/windows-cli-compatibility.md +123 -0
  114. package/package.json +21 -4
  115. package/scripts/check_agent_map.mjs +87 -0
  116. package/scripts/check_release.mjs +40 -0
  117. package/scripts/fix_bin_mode.mjs +18 -0
  118. package/scripts/generate_agent_context.mjs +253 -0
  119. package/scripts/postinstall.mjs +12 -4
@@ -0,0 +1,123 @@
1
+ # Windows CLI Compatibility
2
+
3
+ This spec defines the compatibility baseline required before `1688-cli` can
4
+ claim normal Windows command-line support.
5
+
6
+ ## Goal
7
+
8
+ Make the installed CLI and local development workflow work from Windows
9
+ PowerShell and cmd.exe for read-only and daemon-backed commands.
10
+
11
+ ## Scope
12
+
13
+ - npm package install and build scripts must not depend on Unix shell commands.
14
+ - The daemon IPC path must work on Windows named pipes and avoid collisions
15
+ between different `BB1688_HOME` roots.
16
+ - Runtime paths, temporary files, and diagnostics must use Node path helpers
17
+ instead of hard-coded Unix paths where they are part of normal command code.
18
+ - `doctor` fix hints must be executable or meaningful on Windows.
19
+ - README and command docs must show Windows alternatives where Unix examples
20
+ use `jq`, shell assignment, `/tmp`, or `~/.1688`.
21
+ - Deterministic tests must cover the Windows-specific path and hint logic.
22
+ - CI/package verification must include a Windows-compatible build and smoke
23
+ path, or document the exact manual Windows checks when CI is not available
24
+ in the current environment.
25
+
26
+ ## Non-Goals
27
+
28
+ - Do not bypass 1688 login, slider verification, or risk control.
29
+ - Do not automate Windows UI interaction beyond Playwright's existing browser
30
+ launch behavior.
31
+ - Do not guarantee live 1688 network/search success in CI; CI checks should be
32
+ deterministic and use `doctor --no-launch` unless a real account/session is
33
+ explicitly supplied.
34
+ - Do not change public JSON contracts except by additive diagnostic fields.
35
+ - Do not add a new installer, native binary, or Windows service wrapper.
36
+
37
+ ## Behavior Contract
38
+
39
+ ### Build And Install
40
+
41
+ `pnpm build` must run on Windows, macOS, and Linux. Any executable-bit fix must
42
+ be implemented in Node and become a no-op on Windows.
43
+
44
+ `scripts/postinstall.mjs` must:
45
+
46
+ - locate the daemon pid file under `BB1688_HOME` when set
47
+ - detect Windows Chrome install paths
48
+ - invoke `npx.cmd` on Windows and `npx` elsewhere
49
+ - print retry commands that are valid for the current platform
50
+
51
+ ### Daemon IPC
52
+
53
+ Unix-like platforms continue to use `<BB1688_HOME>/daemon.sock`.
54
+
55
+ Windows must use a named pipe:
56
+
57
+ ```text
58
+ \\.\pipe\1688-cli-daemon-<stable-root-hash>
59
+ ```
60
+
61
+ The hash must be stable for a given `BB1688_HOME`/default root and different
62
+ for different roots so tests, profiles, and concurrent users do not collide.
63
+
64
+ ### Diagnostics
65
+
66
+ `1688 doctor` must emit platform-appropriate fix hints:
67
+
68
+ - Unix-like stale lock: `rm -rf "..."`
69
+ - Windows stale lock: `Remove-Item -Recurse -Force "..."`
70
+ - Unix-like writable-directory issue: `chmod u+w "..."`
71
+ - Windows writable-directory issue: explain to grant write permission or choose
72
+ another `BB1688_HOME`
73
+
74
+ The daemon protocol documentation and README must not describe the daemon as
75
+ Unix-only.
76
+
77
+ ### Documentation
78
+
79
+ README and command docs must include:
80
+
81
+ - PowerShell examples using built-in `--get`/`--pick` instead of requiring `jq`
82
+ - Windows output paths such as `$env:TEMP\suppliers.csv`
83
+ - Windows local state paths using `%USERPROFILE%\.1688` or `$env:USERPROFILE`
84
+ - named pipe note for daemon IPC on Windows
85
+
86
+ ## Acceptance Criteria
87
+
88
+ - `pnpm build` succeeds without requiring `chmod` from the shell.
89
+ - Unit tests cover Windows named pipe generation and platform-specific doctor
90
+ hints.
91
+ - `pnpm test:unit` passes.
92
+ - `pnpm agent-verify` passes.
93
+ - `npm pack --dry-run` succeeds.
94
+ - README and `docs/COMMANDS.md` no longer present Unix-only examples as the
95
+ only way to use JSON, output files, or local paths.
96
+ - Manual Windows smoke checklist is documented:
97
+ - `npm i -g 1688-cli`
98
+ - `1688 --version`
99
+ - `1688 doctor --no-launch --json`
100
+ - `1688 daemon start`
101
+ - `1688 daemon status --json`
102
+ - `1688 daemon stop`
103
+ - `1688 search 雨伞 --max 1 --json`
104
+ - `1688 supplier search 键盘 --max 1 --json`
105
+
106
+ ## Verification Signals
107
+
108
+ - Focused tests:
109
+ - `tests/paths.test.ts`
110
+ - `tests/doctor.test.ts` or existing doctor tests
111
+ - Package checks:
112
+ - `pnpm build`
113
+ - `pnpm test:unit`
114
+ - `pnpm agent-verify`
115
+ - `npm pack --dry-run`
116
+ - Manual Windows smoke checks listed above when a Windows machine/session is
117
+ available.
118
+
119
+ ## Open Questions
120
+
121
+ - Whether to add GitHub Actions `windows-latest` is a repository operations
122
+ decision. The code/docs work should make that job straightforward, but adding
123
+ CI config is not required unless the repository already uses GitHub Actions.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "1688-cli",
3
- "version": "0.1.41",
4
- "description": "1688.com CLI for humans, Codex, and Claude Code. Sourcing (search / image-search / offer / inquire) and orders (list / detail / logistics / seller chat).",
3
+ "version": "0.1.43",
4
+ "description": "1688.com CLI for humans, Codex, and Claude Code. Product search, supplier scraper/research, inquiry, cart, orders, logistics, and seller chat.",
5
5
  "license": "MIT",
6
6
  "author": "nobodyjack",
7
7
  "homepage": "https://github.com/superjack2050/1688-cli#readme",
@@ -22,6 +22,11 @@
22
22
  "claude-code",
23
23
  "codex",
24
24
  "automation",
25
+ "scraper",
26
+ "supplier-scraper",
27
+ "supplier-search",
28
+ "product-scraper",
29
+ "sourcing",
25
30
  "ecommerce",
26
31
  "procurement",
27
32
  "playwright",
@@ -36,7 +41,13 @@
36
41
  "files": [
37
42
  "dist",
38
43
  "scripts/postinstall.mjs",
44
+ "scripts/fix_bin_mode.mjs",
45
+ "scripts/generate_agent_context.mjs",
46
+ "scripts/check_agent_map.mjs",
47
+ "scripts/check_release.mjs",
48
+ "docs",
39
49
  "AGENTS.md",
50
+ "ARCHITECTURE.md",
40
51
  "README.md",
41
52
  "LICENSE",
42
53
  "CHANGELOG.md"
@@ -46,11 +57,17 @@
46
57
  },
47
58
  "scripts": {
48
59
  "dev": "tsx src/cli.ts",
49
- "build": "tsc -p tsconfig.json && chmod +x dist/cli.js",
60
+ "build": "tsc -p tsconfig.json && node scripts/fix_bin_mode.mjs",
50
61
  "test": "vitest run",
62
+ "test:unit": "vitest run --exclude tests/doctor-live.test.ts",
51
63
  "test:watch": "vitest",
52
64
  "typecheck": "tsc --noEmit",
53
- "prepublishOnly": "pnpm build",
65
+ "agent-context": "node scripts/generate_agent_context.mjs",
66
+ "docs-check": "node scripts/generate_agent_context.mjs --check",
67
+ "agent-map-check": "node scripts/check_agent_map.mjs",
68
+ "release-check": "node scripts/check_release.mjs",
69
+ "agent-verify": "pnpm typecheck && pnpm test:unit && pnpm docs-check && pnpm agent-map-check && pnpm release-check",
70
+ "prepublishOnly": "pnpm release-check && pnpm build",
54
71
  "postinstall": "node scripts/postinstall.mjs"
55
72
  },
56
73
  "dependencies": {
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
7
+
8
+ async function read(relPath) {
9
+ return fs.readFile(path.join(root, relPath), 'utf8');
10
+ }
11
+
12
+ async function exists(relPath) {
13
+ try {
14
+ await fs.access(path.join(root, relPath));
15
+ return true;
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+
21
+ const requiredFiles = [
22
+ 'AGENTS.md',
23
+ 'ARCHITECTURE.md',
24
+ 'docs/AGENT_WORKING_PRINCIPLES.md',
25
+ 'docs/README.md',
26
+ 'docs/WORKFLOW.md',
27
+ 'docs/COMMANDS.md',
28
+ 'docs/JSON_CONTRACTS.md',
29
+ 'docs/SAFETY.md',
30
+ 'docs/RELIABILITY.md',
31
+ 'docs/QUALITY_SCORE.md',
32
+ 'docs/FEATURES.md',
33
+ 'docs/specs/sourcing-research.md',
34
+ 'docs/specs/seller-im.md',
35
+ 'docs/specs/checkout-and-orders.md',
36
+ 'docs/playbooks/add-command.md',
37
+ 'docs/playbooks/change-json-output.md',
38
+ 'docs/playbooks/debug-risk-control.md',
39
+ 'docs/playbooks/add-mtop-capture.md',
40
+ 'docs/playbooks/update-cli-release.md',
41
+ 'docs/records/release-omissions.md',
42
+ 'docs/generated/command-index.md',
43
+ 'docs/generated/module-map.md',
44
+ 'docs/generated/test-index.md',
45
+ 'docs/generated/json-shapes.md',
46
+ ];
47
+
48
+ const missing = [];
49
+ for (const file of requiredFiles) {
50
+ if (!(await exists(file))) missing.push(file);
51
+ }
52
+
53
+ const failures = [];
54
+ if (missing.length) failures.push(`Missing files: ${missing.join(', ')}`);
55
+
56
+ const agents = await read('AGENTS.md').catch(() => '');
57
+ if (!agents.includes('pnpm agent-verify'))
58
+ failures.push('AGENTS.md must mention pnpm agent-verify.');
59
+ if (!agents.includes('docs/playbooks'))
60
+ failures.push('AGENTS.md must route work to docs/playbooks.');
61
+ if (!agents.includes('docs/AGENT_WORKING_PRINCIPLES.md'))
62
+ failures.push('AGENTS.md must link docs/AGENT_WORKING_PRINCIPLES.md.');
63
+ if (agents.split('\n').length > 220)
64
+ failures.push('AGENTS.md should stay short (<= 220 lines).');
65
+
66
+ const pkg = JSON.parse(await read('package.json'));
67
+ for (const scriptName of ['agent-context', 'docs-check', 'agent-map-check', 'release-check', 'agent-verify']) {
68
+ if (!pkg.scripts?.[scriptName]) failures.push(`package.json missing script: ${scriptName}`);
69
+ }
70
+
71
+ const docsReadme = await read('docs/README.md').catch(() => '');
72
+ for (const needle of [
73
+ 'AGENT_WORKING_PRINCIPLES.md',
74
+ 'COMMANDS.md',
75
+ 'JSON_CONTRACTS.md',
76
+ 'SAFETY.md',
77
+ 'generated/',
78
+ ]) {
79
+ if (!docsReadme.includes(needle)) failures.push(`docs/README.md must link ${needle}.`);
80
+ }
81
+
82
+ if (failures.length) {
83
+ console.error(failures.join('\n'));
84
+ process.exit(1);
85
+ }
86
+
87
+ console.log('Agent map structure looks good.');
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
7
+
8
+ async function readJson(relPath) {
9
+ return JSON.parse(await fs.readFile(path.join(root, relPath), 'utf8'));
10
+ }
11
+
12
+ async function read(relPath) {
13
+ return fs.readFile(path.join(root, relPath), 'utf8');
14
+ }
15
+
16
+ const pkg = await readJson('package.json');
17
+ const changelog = await read('CHANGELOG.md');
18
+
19
+ const version = pkg.version;
20
+ const releaseHeading = new RegExp(`^## \\[${version.replaceAll('.', '\\.')}\\] - \\d{4}-\\d{2}-\\d{2}$`, 'm');
21
+ const failures = [];
22
+
23
+ if (!releaseHeading.test(changelog)) {
24
+ failures.push(`CHANGELOG.md missing release heading for package version ${version}.`);
25
+ }
26
+
27
+ if (!pkg.files?.includes('CHANGELOG.md')) {
28
+ failures.push('package.json files must include CHANGELOG.md.');
29
+ }
30
+
31
+ if (!pkg.scripts?.['release-check']) {
32
+ failures.push('package.json missing script: release-check.');
33
+ }
34
+
35
+ if (failures.length) {
36
+ console.error(failures.join('\n'));
37
+ process.exit(1);
38
+ }
39
+
40
+ console.log(`Release metadata looks good for ${pkg.name}@${version}.`);
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ export async function fixBinMode({
6
+ platform = process.platform,
7
+ target = path.join('dist', 'cli.js'),
8
+ } = {}) {
9
+ if (platform === 'win32') {
10
+ return { changed: false, reason: 'windows-noop', target };
11
+ }
12
+ await fs.chmod(target, 0o755);
13
+ return { changed: true, reason: 'chmod-755', target };
14
+ }
15
+
16
+ if (import.meta.url === `file://${process.argv[1]}`) {
17
+ await fixBinMode();
18
+ }
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
7
+ const outDir = path.join(root, 'docs', 'generated');
8
+ const checkOnly = process.argv.includes('--check');
9
+
10
+ const rel = (p) => path.relative(root, p).replaceAll(path.sep, '/');
11
+ const read = (p) => fs.readFile(path.join(root, p), 'utf8');
12
+
13
+ async function exists(p) {
14
+ try {
15
+ await fs.access(path.join(root, p));
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ async function walk(dir, predicate = () => true) {
23
+ const abs = path.join(root, dir);
24
+ const out = [];
25
+ async function visit(current) {
26
+ const entries = await fs.readdir(current, { withFileTypes: true });
27
+ for (const entry of entries) {
28
+ if (entry.name === 'node_modules' || entry.name === '.git') continue;
29
+ const p = path.join(current, entry.name);
30
+ if (entry.isDirectory()) {
31
+ await visit(p);
32
+ } else if (predicate(p)) {
33
+ out.push(p);
34
+ }
35
+ }
36
+ }
37
+ if (await exists(dir)) await visit(abs);
38
+ return out.sort((a, b) => rel(a).localeCompare(rel(b)));
39
+ }
40
+
41
+ function header(title) {
42
+ return `# ${title}\n\n_Generated by \`scripts/generate_agent_context.mjs\`._\n\n`;
43
+ }
44
+
45
+ function cleanDescription(raw) {
46
+ if (!raw) return '-';
47
+ return raw
48
+ .replace(/[`'"]/g, '')
49
+ .replace(/\s*\+\s*/g, ' ')
50
+ .replace(/\s+/g, ' ')
51
+ .trim()
52
+ .replace(/\|/g, '\\|') || '-';
53
+ }
54
+
55
+ function compactList(items) {
56
+ return items.length ? items.map((x) => `\`${x}\``).join('<br>') : '-';
57
+ }
58
+
59
+ async function commandIndex() {
60
+ const cli = await read('src/cli.ts');
61
+ const re =
62
+ /(?:(?:const\s+([A-Za-z0-9_]+)\s*=\s*)?([A-Za-z0-9_]+)\s*\n\s*\.command\('([^']+)'\))/g;
63
+ const matches = [...cli.matchAll(re)];
64
+ const varToCommand = new Map();
65
+ const rows = [];
66
+
67
+ for (let i = 0; i < matches.length; i++) {
68
+ const m = matches[i];
69
+ const assignedVar = m[1];
70
+ const receiver = m[2];
71
+ const command = m[3];
72
+ const start = m.index ?? 0;
73
+ const end = matches[i + 1]?.index ?? cli.length;
74
+ const block = cli.slice(start, end);
75
+ const parent = receiver === 'program' ? '' : varToCommand.get(receiver) ?? receiver;
76
+ const full = parent ? `${parent} ${command}` : command;
77
+ if (assignedVar) varToCommand.set(assignedVar, full);
78
+
79
+ const descRaw = block.match(/\.description\(([\s\S]*?)\)\s*(?:\.|\n)/)?.[1];
80
+ const args = [...block.matchAll(/\.argument\(\s*(['"`])([^'"`]+)\1/g)].map(
81
+ (x) => x[2],
82
+ );
83
+ const opts = [
84
+ ...block.matchAll(/\.(?:requiredOption|option)\(\s*(['"`])([^'"`]+)\1/g),
85
+ ].map((x) => x[2]);
86
+ const source = block.match(/import\('\.\/commands\/([^']+)\.js'\)/)?.[1];
87
+ rows.push({
88
+ command: full,
89
+ source: source ? `src/commands/${source}.ts` : '-',
90
+ args,
91
+ opts,
92
+ description: cleanDescription(descRaw),
93
+ });
94
+ }
95
+
96
+ let out = header('Command Index');
97
+ out += '| Command | Source | Arguments | Options | Description |\n';
98
+ out += '|---|---|---|---|---|\n';
99
+ for (const row of rows) {
100
+ out += `| \`${row.command}\` | ${row.source === '-' ? '-' : `\`${row.source}\``} | ${compactList(
101
+ row.args,
102
+ )} | ${compactList(row.opts)} | ${row.description} |\n`;
103
+ }
104
+ return out;
105
+ }
106
+
107
+ async function moduleMap() {
108
+ const files = await walk('src', (p) => p.endsWith('.ts'));
109
+ const tests = await walk('tests', (p) => p.endsWith('.test.ts') || p.endsWith('.spec.ts'));
110
+ const dirs = new Map();
111
+ for (const file of files) {
112
+ const r = rel(file);
113
+ const parts = r.split('/');
114
+ const dir = parts.length > 2 ? `${parts[0]}/${parts[1]}` : parts[0];
115
+ const entry = dirs.get(dir) ?? { files: 0, tests: 0, notes: '-' };
116
+ entry.files++;
117
+ dirs.set(dir, entry);
118
+ }
119
+ for (const file of tests) {
120
+ const content = await fs.readFile(file, 'utf8');
121
+ const target = content.match(/from ['"]\.\.\/src\/([^/'"]+)/)?.[1];
122
+ if (target) {
123
+ const key = `src/${target}`;
124
+ const entry = dirs.get(key) ?? { files: 0, tests: 0, notes: '-' };
125
+ entry.tests++;
126
+ dirs.set(key, entry);
127
+ }
128
+ }
129
+
130
+ const notes = {
131
+ 'src/commands': 'command executors and renderers',
132
+ 'src/session': 'browser/session automation helpers',
133
+ 'src/daemon': 'background daemon client/server/protocol',
134
+ 'src/io': 'output, prompts, and errors',
135
+ 'src/auth': 'login/session/cookie helpers',
136
+ 'src/util': 'shared utilities',
137
+ };
138
+
139
+ let out = header('Module Map');
140
+ out += '| Directory | Source Files | Related Tests | Notes |\n';
141
+ out += '|---|---:|---:|---|\n';
142
+ for (const dir of [...dirs.keys()].sort()) {
143
+ const entry = dirs.get(dir);
144
+ out += `| \`${dir}\` | ${entry.files} | ${entry.tests} | ${notes[dir] ?? entry.notes} |\n`;
145
+ }
146
+ return out;
147
+ }
148
+
149
+ async function testIndex() {
150
+ const tests = await walk('tests', (p) => p.endsWith('.test.ts') || p.endsWith('.spec.ts'));
151
+ let out = header('Test Index');
152
+ out += '| Test File | Focus | Risk Notes |\n';
153
+ out += '|---|---|---|\n';
154
+ for (const file of tests) {
155
+ const content = await fs.readFile(file, 'utf8');
156
+ const name = path.basename(file).replace(/\.(test|spec)\.ts$/, '');
157
+ const risks = [];
158
+ if (/live|doctor-live|process\.env|BB1688|1688\s/.test(content + name))
159
+ risks.push('live/session-sensitive');
160
+ if (/playwright|BrowserContext|Page|page-state|risk-control/i.test(content))
161
+ risks.push('browser/page-state');
162
+ if (/fixture|jsonp|mtop|capture/i.test(content)) risks.push('fixture/parser');
163
+ out += `| \`${rel(file)}\` | ${name} | ${risks.join(', ') || '-'} |\n`;
164
+ }
165
+ return out;
166
+ }
167
+
168
+ function extractInterfaces(source, file) {
169
+ const lines = source.split('\n');
170
+ const interfaces = [];
171
+ for (let i = 0; i < lines.length; i++) {
172
+ const m = lines[i].match(/^export interface ([A-Za-z0-9_]+)/);
173
+ if (!m) continue;
174
+ const name = m[1];
175
+ const keep =
176
+ /(Args|Opts|Result|Offer|Message|CartItem|Order|Sku|Price|Attribute|Package)/.test(
177
+ name,
178
+ );
179
+ if (!keep) continue;
180
+
181
+ const body = [];
182
+ let depth = 0;
183
+ for (let j = i; j < lines.length; j++) {
184
+ const line = lines[j];
185
+ if (j > i) body.push(line);
186
+ depth += (line.match(/\{/g) ?? []).length;
187
+ depth -= (line.match(/\}/g) ?? []).length;
188
+ if (j > i && depth <= 0) break;
189
+ }
190
+ const fields = body
191
+ .map((line) => line.trim())
192
+ .filter((line) => /^[A-Za-z0-9_?]+:/.test(line) || /^\/\*\*/.test(line))
193
+ .slice(0, 14)
194
+ .map((line) => line.replace(/\|/g, '\\|'));
195
+ interfaces.push({ name, file, fields });
196
+ }
197
+ return interfaces;
198
+ }
199
+
200
+ async function jsonShapes() {
201
+ const files = await walk('src', (p) => p.endsWith('.ts'));
202
+ const interfaces = [];
203
+ for (const file of files) {
204
+ const source = await fs.readFile(file, 'utf8');
205
+ interfaces.push(...extractInterfaces(source, rel(file)));
206
+ }
207
+
208
+ let out = header('JSON Shapes');
209
+ out +=
210
+ 'This is a heuristic index of exported TypeScript interfaces that are likely to matter for agent-facing JSON.\n\n';
211
+ out += '| Interface | File | Notable Fields |\n';
212
+ out += '|---|---|---|\n';
213
+ for (const it of interfaces.sort((a, b) => a.file.localeCompare(b.file) || a.name.localeCompare(b.name))) {
214
+ out += `| \`${it.name}\` | \`${it.file}\` | ${it.fields.length ? it.fields.map((x) => `\`${x}\``).join('<br>') : '-'} |\n`;
215
+ }
216
+ return out;
217
+ }
218
+
219
+ const outputs = new Map([
220
+ ['command-index.md', await commandIndex()],
221
+ ['module-map.md', await moduleMap()],
222
+ ['test-index.md', await testIndex()],
223
+ ['json-shapes.md', await jsonShapes()],
224
+ ]);
225
+
226
+ if (checkOnly) {
227
+ const mismatches = [];
228
+ for (const [name, content] of outputs) {
229
+ const target = path.join(outDir, name);
230
+ let current = '';
231
+ try {
232
+ current = await fs.readFile(target, 'utf8');
233
+ } catch {
234
+ mismatches.push(name);
235
+ continue;
236
+ }
237
+ if (current !== content) mismatches.push(name);
238
+ }
239
+ if (mismatches.length) {
240
+ console.error(
241
+ `Generated agent context is stale: ${mismatches.join(', ')}. Run pnpm agent-context.`,
242
+ );
243
+ process.exit(1);
244
+ }
245
+ console.log('Generated agent context is fresh.');
246
+ } else {
247
+ await fs.mkdir(outDir, { recursive: true });
248
+ for (const [name, content] of outputs) {
249
+ await fs.writeFile(path.join(outDir, name), content);
250
+ }
251
+ console.log(`Generated agent context files in ${rel(outDir)}`);
252
+ }
253
+
@@ -21,7 +21,10 @@ if (process.env.CI || process.env.BB1688_SKIP_POSTINSTALL) {
21
21
  // Otherwise the old daemon keeps serving stale code until the user runs
22
22
  // `1688 daemon stop` manually after every upgrade.
23
23
  try {
24
- const pidFile = path.join(os.homedir(), '.1688', 'daemon.pid');
24
+ const pidFile = path.join(
25
+ process.env.BB1688_HOME ?? path.join(os.homedir(), '.1688'),
26
+ 'daemon.pid',
27
+ );
25
28
  if (fs.existsSync(pidFile)) {
26
29
  const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
27
30
  if (Number.isInteger(pid) && pid > 0) {
@@ -140,19 +143,24 @@ console.log(
140
143
  const env = { ...process.env };
141
144
  if (downloadHost) env.PLAYWRIGHT_DOWNLOAD_HOST = downloadHost;
142
145
 
143
- const res = spawnSync('npx', ['playwright', 'install', 'chromium'], {
146
+ const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
147
+ const res = spawnSync(npxCmd, ['playwright', 'install', 'chromium'], {
144
148
  stdio: 'inherit',
145
149
  env,
146
150
  });
147
151
 
148
152
  if (res.status !== 0) {
153
+ const retryCommand =
154
+ process.platform === 'win32'
155
+ ? ' $env:PLAYWRIGHT_DOWNLOAD_HOST="https://npmmirror.com/mirrors/playwright"; npx playwright install chromium\n'
156
+ : ' PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright \\\n' +
157
+ ' npx playwright install chromium\n';
149
158
  console.log(
150
159
  '\n1688-cli: Chromium download failed (non-fatal).\n' +
151
160
  ' Try running manually:\n' +
152
161
  ' 1) Install Chrome from https://www.google.com/chrome/ (recommended), or\n' +
153
162
  ' 2) Force-retry with mirror:\n' +
154
- ' PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright \\\n' +
155
- ' npx playwright install chromium\n',
163
+ retryCommand,
156
164
  );
157
165
  }
158
166
  process.exit(0);