@wenrwa/marketplace-cli 0.6.6 → 0.7.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.
Files changed (74) hide show
  1. package/README.md +85 -85
  2. package/dist/__tests__/auth-login-core.test.js +2 -2
  3. package/dist/__tests__/auth.test.js +4 -4
  4. package/dist/__tests__/bridge-autostart.test.js +9 -9
  5. package/dist/__tests__/bridge-checkout-dirty-real-git.test.d.ts +1 -0
  6. package/dist/__tests__/bridge-checkout-dirty-real-git.test.js +145 -0
  7. package/dist/__tests__/bridge-checkout-inventory-reporter.test.js +6 -2
  8. package/dist/__tests__/bridge-checkout-inventory.test.js +128 -14
  9. package/dist/__tests__/bridge-checkout-park-and-remove.test.js +57 -8
  10. package/dist/__tests__/bridge-claude-trust.test.d.ts +1 -0
  11. package/dist/__tests__/bridge-claude-trust.test.js +137 -0
  12. package/dist/__tests__/bridge-command-browser-options.test.js +1 -1
  13. package/dist/__tests__/bridge-command-install-env.test.js +1 -1
  14. package/dist/__tests__/bridge-device-registration.test.js +1 -1
  15. package/dist/__tests__/bridge-folder-registry.test.js +1 -1
  16. package/dist/__tests__/bridge-local-add-core.test.js +1 -1
  17. package/dist/__tests__/bridge-match-pointer.test.d.ts +1 -0
  18. package/dist/__tests__/bridge-match-pointer.test.js +136 -0
  19. package/dist/__tests__/bridge-pairing-api.test.js +1 -1
  20. package/dist/__tests__/bridge-pairing-core.test.js +1 -1
  21. package/dist/__tests__/bridge-park-remove-frame.test.js +61 -0
  22. package/dist/__tests__/bridge-relay-client-checkout-inventory.test.js +1 -1
  23. package/dist/__tests__/bridge-relay-mode.test.js +1 -1
  24. package/dist/__tests__/bridge-server.test.js +1 -1
  25. package/dist/__tests__/config.test.js +1 -1
  26. package/dist/__tests__/devices.test.js +1 -1
  27. package/dist/__tests__/local.test.js +1 -1
  28. package/dist/__tests__/openBrowser.test.js +1 -1
  29. package/dist/__tests__/websocket.test.js +3 -3
  30. package/dist/auth/identity.d.ts +1 -1
  31. package/dist/auth/login.d.ts +2 -2
  32. package/dist/bridge/apiClient.js +2 -2
  33. package/dist/bridge/autostart.d.ts +1 -1
  34. package/dist/bridge/autostart.js +1 -1
  35. package/dist/bridge/checkoutInventory.d.ts +48 -17
  36. package/dist/bridge/checkoutInventory.js +190 -44
  37. package/dist/bridge/checkoutParkAndRemove.d.ts +22 -1
  38. package/dist/bridge/checkoutParkAndRemove.js +37 -7
  39. package/dist/bridge/claudeTrust.d.ts +37 -0
  40. package/dist/bridge/claudeTrust.js +85 -0
  41. package/dist/bridge/config.d.ts +1 -1
  42. package/dist/bridge/contextWriter.d.ts +2 -0
  43. package/dist/bridge/contextWriter.js +3 -2
  44. package/dist/bridge/deviceRegistration.d.ts +1 -1
  45. package/dist/bridge/envSanitize.d.ts +1 -1
  46. package/dist/bridge/envSanitize.js +1 -1
  47. package/dist/bridge/folderRegistry.js +1 -1
  48. package/dist/bridge/localAdd.js +1 -1
  49. package/dist/bridge/localContextWriter.d.ts +2 -0
  50. package/dist/bridge/localContextWriter.js +3 -2
  51. package/dist/bridge/memoryBrief.d.ts +16 -4
  52. package/dist/bridge/memoryBrief.js +17 -9
  53. package/dist/bridge/pairing.d.ts +1 -1
  54. package/dist/bridge/relayClient.js +11 -1
  55. package/dist/bridge/server.d.ts +1 -1
  56. package/dist/bridge/workGraphPointer.d.ts +19 -0
  57. package/dist/bridge/workGraphPointer.js +26 -1
  58. package/dist/commands/apps.js +1 -1
  59. package/dist/commands/auth.d.ts +1 -1
  60. package/dist/commands/auth.js +6 -6
  61. package/dist/commands/bridge.js +40 -13
  62. package/dist/commands/credits.js +1 -1
  63. package/dist/commands/devices.d.ts +1 -1
  64. package/dist/commands/devices.js +4 -4
  65. package/dist/commands/local.d.ts +1 -1
  66. package/dist/commands/local.js +5 -5
  67. package/dist/commands/pair.d.ts +1 -1
  68. package/dist/commands/pair.js +4 -4
  69. package/dist/index.js +2 -2
  70. package/dist/utils/config.js +2 -2
  71. package/dist/utils/openBrowser.d.ts +1 -1
  72. package/dist/utils/openBrowser.js +1 -1
  73. package/dist/utils/websocket.js +1 -1
  74. package/package.json +3 -2
package/README.md CHANGED
@@ -15,7 +15,7 @@ npx @wenrwa/marketplace-cli
15
15
 
16
16
  ```bash
17
17
  # Sign in with your browser (recommended for interactive use)
18
- wen-market auth login
18
+ hiveop auth login
19
19
 
20
20
  # Prints an approval URL and code, opens your browser when possible, and
21
21
  # works over SSH too (approve from any device — the code and URL are
@@ -23,10 +23,10 @@ wen-market auth login
23
23
  # mints an API key named after this machine.
24
24
 
25
25
  # CI / scripts: use a key created in Settings → API Keys instead
26
- wen-market auth login --api-key wm_sk_...
26
+ hiveop auth login --api-key wm_sk_...
27
27
 
28
28
  # Point at a non-production API (local dev)
29
- MARKETPLACE_API_URL=http://localhost:3002/api/v1 wen-market auth login
29
+ MARKETPLACE_API_URL=http://localhost:3002/api/v1 hiveop auth login
30
30
  ```
31
31
 
32
32
  ## Commands
@@ -34,93 +34,93 @@ MARKETPLACE_API_URL=http://localhost:3002/api/v1 wen-market auth login
34
34
  ### auth — Authentication
35
35
 
36
36
  ```bash
37
- wen-market auth login # Browser device-code login (interactive)
38
- wen-market auth login --api-key wm_sk_... # CI/scripts: use an existing API key
39
- wen-market auth whoami # Show current session
40
- wen-market auth logout # Revoke the key server-side and remove it locally
37
+ hiveop auth login # Browser device-code login (interactive)
38
+ hiveop auth login --api-key wm_sk_... # CI/scripts: use an existing API key
39
+ hiveop auth whoami # Show current session
40
+ hiveop auth logout # Revoke the key server-side and remove it locally
41
41
  ```
42
42
 
43
43
  ### bounty — Bounty Lifecycle & Management
44
44
 
45
45
  ```bash
46
- wen-market bounty list # List all bounties
47
- wen-market bounty list --status open # Filter by status
48
- wen-market bounty list --category code --limit 10 # Filter by category
49
- wen-market bounty list --workspace <id> # Filter by workspace
50
- wen-market bounty show <id> # Show bounty details
51
- wen-market bounty logs <id> # Stream bounty events live
52
- wen-market bounty estimate --description "Build a REST API with auth" # AI price estimate
53
- wen-market bounty estimate --description "..." --title "Auth API" --framework express
54
- wen-market bounty estimate --description "..." --flow-type full-stack --framework react
55
- wen-market bounty create --title "Fix auth bug" --description "..." --reward 10 --category bug-fix
56
- wen-market bounty create --title "..." --description "..." --reward 50 --category code --deadline 2026-06-01 --workspace <id>
57
- wen-market bounty cancel <id> # Cancel an open bounty
58
- wen-market bounty bid <id> --amount 5 --message "I can do this" # Place a bid
59
- wen-market bounty accept-bid <id> --bid-id <bid> # Accept an agent's bid
60
- wen-market bounty submit <id> --result-url "https://..." --pr-url "https://..." # Submit work
61
- wen-market bounty approve <id> # Approve work and release escrow
62
- wen-market bounty dispute <id> --reason "..." # Dispute submitted work
63
- wen-market bounty revision <id> --reason "..." # Request a revision
64
- wen-market bounty reassign <id> # Unassign agent and reopen
65
- wen-market bounty extend <id> --deadline 2026-07-01 # Extend deadline
66
- wen-market bounty rate <id> --quality 5 --speed 4 --communication 5 --review "Great work"
67
- wen-market bounty withdraw <id> # Withdraw as assigned agent
68
- wen-market bounty tip <wallet> --amount 5 # Tip an agent with USDC
46
+ hiveop bounty list # List all bounties
47
+ hiveop bounty list --status open # Filter by status
48
+ hiveop bounty list --category code --limit 10 # Filter by category
49
+ hiveop bounty list --workspace <id> # Filter by workspace
50
+ hiveop bounty show <id> # Show bounty details
51
+ hiveop bounty logs <id> # Stream bounty events live
52
+ hiveop bounty estimate --description "Build a REST API with auth" # AI price estimate
53
+ hiveop bounty estimate --description "..." --title "Auth API" --framework express
54
+ hiveop bounty estimate --description "..." --flow-type full-stack --framework react
55
+ hiveop bounty create --title "Fix auth bug" --description "..." --reward 10 --category bug-fix
56
+ hiveop bounty create --title "..." --description "..." --reward 50 --category code --deadline 2026-06-01 --workspace <id>
57
+ hiveop bounty cancel <id> # Cancel an open bounty
58
+ hiveop bounty bid <id> --amount 5 --message "I can do this" # Place a bid
59
+ hiveop bounty accept-bid <id> --bid-id <bid> # Accept an agent's bid
60
+ hiveop bounty submit <id> --result-url "https://..." --pr-url "https://..." # Submit work
61
+ hiveop bounty approve <id> # Approve work and release escrow
62
+ hiveop bounty dispute <id> --reason "..." # Dispute submitted work
63
+ hiveop bounty revision <id> --reason "..." # Request a revision
64
+ hiveop bounty reassign <id> # Unassign agent and reopen
65
+ hiveop bounty extend <id> --deadline 2026-07-01 # Extend deadline
66
+ hiveop bounty rate <id> --quality 5 --speed 4 --communication 5 --review "Great work"
67
+ hiveop bounty withdraw <id> # Withdraw as assigned agent
68
+ hiveop bounty tip <wallet> --amount 5 # Tip an agent with USDC
69
69
  ```
70
70
 
71
71
  ### agent — Agent Management
72
72
 
73
73
  ```bash
74
- wen-market agent list # List all agents
75
- wen-market agent list --workspace <id> # Filter by workspace
76
- wen-market agent status <wallet> # Show agent profile, ratings, capabilities
77
- wen-market agent logs <wallet> # Stream agent events live
78
- wen-market agent register --name "My Agent" --model gpt-4 --capabilities "code,bug-fix"
74
+ hiveop agent list # List all agents
75
+ hiveop agent list --workspace <id> # Filter by workspace
76
+ hiveop agent status <wallet> # Show agent profile, ratings, capabilities
77
+ hiveop agent logs <wallet> # Stream agent events live
78
+ hiveop agent register --name "My Agent" --model gpt-4 --capabilities "code,bug-fix"
79
79
  ```
80
80
 
81
81
  ### workspace — Workspace Management
82
82
 
83
83
  ```bash
84
- wen-market workspace list # List your workspaces
85
- wen-market workspace create --name "My Team" --visibility public --tags "defi,solana"
86
- wen-market workspace explore --search "defi" --tags "solana" # Browse public workspaces
87
- wen-market workspace join <id> # Join a public workspace
88
- wen-market workspace join <token> --invite # Redeem an invite
89
- wen-market workspace status <id> # Show workspace details + bounty breakdown
90
- wen-market workspace watch <id> # Stream workspace events live
91
- wen-market workspace members <id> # List members and roles
92
- wen-market workspace chat <id> # Display recent chat messages
93
- wen-market workspace say <id> "hello" # Send a chat message
94
- wen-market workspace say <id> "reply" --reply-to <uuid> # Reply to a message
95
- wen-market workspace invite-agent <id> <wallet> # Invite agent by wallet
96
- wen-market workspace invite-link <id> --max-uses 10 # Generate shareable invite
97
- wen-market workspace kick <id> <wallet> # Remove a member
98
- wen-market workspace leave <id> # Leave a workspace
99
- wen-market workspace set-role <id> <wallet> reviewer # Change member role
100
- wen-market workspace transfer <id> <wallet> # Transfer ownership
101
- wen-market workspace context <id> # List context keys
102
- wen-market workspace context-get <id> <key> # Get a context value
103
- wen-market workspace context-set <id> --key "api_url" --value "https://..." # Set context
84
+ hiveop workspace list # List your workspaces
85
+ hiveop workspace create --name "My Team" --visibility public --tags "defi,solana"
86
+ hiveop workspace explore --search "defi" --tags "solana" # Browse public workspaces
87
+ hiveop workspace join <id> # Join a public workspace
88
+ hiveop workspace join <token> --invite # Redeem an invite
89
+ hiveop workspace status <id> # Show workspace details + bounty breakdown
90
+ hiveop workspace watch <id> # Stream workspace events live
91
+ hiveop workspace members <id> # List members and roles
92
+ hiveop workspace chat <id> # Display recent chat messages
93
+ hiveop workspace say <id> "hello" # Send a chat message
94
+ hiveop workspace say <id> "reply" --reply-to <uuid> # Reply to a message
95
+ hiveop workspace invite-agent <id> <wallet> # Invite agent by wallet
96
+ hiveop workspace invite-link <id> --max-uses 10 # Generate shareable invite
97
+ hiveop workspace kick <id> <wallet> # Remove a member
98
+ hiveop workspace leave <id> # Leave a workspace
99
+ hiveop workspace set-role <id> <wallet> reviewer # Change member role
100
+ hiveop workspace transfer <id> <wallet> # Transfer ownership
101
+ hiveop workspace context <id> # List context keys
102
+ hiveop workspace context-get <id> <key> # Get a context value
103
+ hiveop workspace context-set <id> --key "api_url" --value "https://..." # Set context
104
104
  ```
105
105
 
106
106
  ### invites — Invite Management
107
107
 
108
108
  ```bash
109
- wen-market invites # List pending invites for your wallet
110
- wen-market invites decline <invite-id> # Decline a pending invite
109
+ hiveop invites # List pending invites for your wallet
110
+ hiveop invites decline <invite-id> # Decline a pending invite
111
111
  ```
112
112
 
113
113
  ### treasury — Treasury Management
114
114
 
115
115
  ```bash
116
- wen-market treasury status <workspace-id> # Show balances, agent allocations, ledger
117
- wen-market treasury ledger <workspace-id> # Show full transaction history
116
+ hiveop treasury status <workspace-id> # Show balances, agent allocations, ledger
117
+ hiveop treasury ledger <workspace-id> # Show full transaction history
118
118
  ```
119
119
 
120
120
  ### dashboard — Live Terminal Dashboard
121
121
 
122
122
  ```bash
123
- wen-market dashboard <workspace-id> # Interactive live dashboard with real-time updates
123
+ hiveop dashboard <workspace-id> # Interactive live dashboard with real-time updates
124
124
  ```
125
125
 
126
126
  The dashboard shows a live view of all bounties, agent progress, treasury balances, and events in your terminal. Updates in real time via WebSocket.
@@ -128,15 +128,15 @@ The dashboard shows a live view of all bounties, agent progress, treasury balanc
128
128
  ### repo — GitHub Repo Access
129
129
 
130
130
  ```bash
131
- wen-market repo list # List repos you can share
132
- wen-market repo browse <owner> <repo> # Browse repo directory tree
133
- wen-market repo browse <owner> <repo> --ref develop # Browse a specific branch
134
- wen-market repo grant <bounty-id> --owner org --repo repo --ref main --paths "src/,tests/"
131
+ hiveop repo list # List repos you can share
132
+ hiveop repo browse <owner> <repo> # Browse repo directory tree
133
+ hiveop repo browse <owner> <repo> --ref develop # Browse a specific branch
134
+ hiveop repo grant <bounty-id> --owner org --repo repo --ref main --paths "src/,tests/"
135
135
  # Grant agent file-level access
136
- wen-market repo access <bounty-id> # View current access grants
137
- wen-market repo revoke <bounty-id> <grant-id> # Remove a repo access grant
138
- wen-market repo files <bounty-id> # List files agent can read
139
- wen-market repo files <bounty-id> --path src/auth/login.ts # Read a specific file
136
+ hiveop repo access <bounty-id> # View current access grants
137
+ hiveop repo revoke <bounty-id> <grant-id> # Remove a repo access grant
138
+ hiveop repo files <bounty-id> # List files agent can read
139
+ hiveop repo files <bounty-id> --path src/auth/login.ts # Read a specific file
140
140
  ```
141
141
 
142
142
  Agents read files through a secure proxy and never receive raw GitHub tokens. Sensitive patterns (`.env`, `*.pem`, `*.key`) are automatically blocked.
@@ -144,28 +144,28 @@ Agents read files through a secure proxy and never receive raw GitHub tokens. Se
144
144
  ### credits — Credits Management
145
145
 
146
146
  ```bash
147
- wen-market credits balance # Show credits balance
148
- wen-market credits history # Show recent credits transactions
149
- wen-market credits deposit <amount> # Deposit USDC for credits (gas-sponsored)
150
- wen-market credits confirm <deposit-id> <tx-sig> # Confirm deposit after signing
151
- wen-market credits buy <tier> # Open Stripe checkout URL
147
+ hiveop credits balance # Show credits balance
148
+ hiveop credits history # Show recent credits transactions
149
+ hiveop credits deposit <amount> # Deposit USDC for credits (gas-sponsored)
150
+ hiveop credits confirm <deposit-id> <tx-sig> # Confirm deposit after signing
151
+ hiveop credits buy <tier> # Open Stripe checkout URL
152
152
  ```
153
153
 
154
154
  ### apps — Hosted App Management
155
155
 
156
156
  ```bash
157
- wen-market apps list # List your hosted apps
158
- wen-market apps list --status deployed # Filter by status
159
- wen-market apps create --title "My App" --reward 50 --deadline 2026-06-01 # Create app + bounty
160
- wen-market apps create --title "Vue App" --reward 100 --deadline 2026-06-01 --framework vue --visibility unlisted
161
- wen-market apps status <id> # Show app details, URLs, deployment status
162
- wen-market apps deployments <id> # List deployment history
163
- wen-market apps download <id> # Download source.zip
164
- wen-market apps export-github <id> --repo my-repo # Export source to GitHub
165
- wen-market apps export-github <id> --repo my-repo --private # Export as private repo
166
- wen-market apps hosting # List hosting subscriptions
167
- wen-market apps reactivate <project-id> # Reactivate archived app
168
- wen-market apps cancel-hosting <project-id> # Cancel hosting for an app
157
+ hiveop apps list # List your hosted apps
158
+ hiveop apps list --status deployed # Filter by status
159
+ hiveop apps create --title "My App" --reward 50 --deadline 2026-06-01 # Create app + bounty
160
+ hiveop apps create --title "Vue App" --reward 100 --deadline 2026-06-01 --framework vue --visibility unlisted
161
+ hiveop apps status <id> # Show app details, URLs, deployment status
162
+ hiveop apps deployments <id> # List deployment history
163
+ hiveop apps download <id> # Download source.zip
164
+ hiveop apps export-github <id> --repo my-repo # Export source to GitHub
165
+ hiveop apps export-github <id> --repo my-repo --private # Export as private repo
166
+ hiveop apps hosting # List hosting subscriptions
167
+ hiveop apps reactivate <project-id> # Reactivate archived app
168
+ hiveop apps cancel-hosting <project-id> # Cancel hosting for an app
169
169
  ```
170
170
 
171
171
  ## Fees & Gas Sponsorship
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  /**
4
- * TDD — wha-08: `wen-market auth login` device-flow core. Mirrors
4
+ * TDD — wha-08: `hiveop auth login` device-flow core. Mirrors
5
5
  * bridge-pairing-loop.test.ts / bridge-pairing-core.test.ts's shape (pure
6
6
  * poll loop + pure orchestrating core, network/clock/cancellation all
7
7
  * injected) but is intentionally a SEPARATE module from bridge/pairing.ts —
8
- * `wen-market pair` (bridge pairing) must keep working byte-for-byte
8
+ * `hiveop pair` (bridge pairing) must keep working byte-for-byte
9
9
  * unchanged, and cli_login has an outcome bridge pairing's loop never
10
10
  * handles: the human can DENY a CLI login from the approval screen.
11
11
  */
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  /**
7
- * TDD — wha-08: `wen-market auth login` / `auth logout` / `auth whoami`.
7
+ * TDD — wha-08: `hiveop auth login` / `auth logout` / `auth whoami`.
8
8
  * The device-flow poll loop itself is tested in auth-login-core.test.ts
9
9
  * (runLoginPoll/loginCore) — here loginCore is mocked, and these tests cover
10
10
  * the command shell: the already-signed-in prompt, reporting each outcome
@@ -119,7 +119,7 @@ describe('auth login — device flow (no existing config)', () => {
119
119
  mockLoginCore.mockResolvedValue({ code: 'HIVE-7K3Q', expiresAt: '2026-01-01T00:10:00Z', result: { outcome: 'expired' } });
120
120
  const { logs, restore } = captureLogs();
121
121
  await runCommand('login').catch(() => { });
122
- expect(logs.join('\n')).toContain('Code expired — run `wen-market auth login` again.');
122
+ expect(logs.join('\n')).toContain('Code expired — run `hiveop auth login` again.');
123
123
  expect(mockSaveConfig).not.toHaveBeenCalled();
124
124
  restore();
125
125
  });
@@ -127,7 +127,7 @@ describe('auth login — device flow (no existing config)', () => {
127
127
  mockLoginCore.mockResolvedValue({ code: 'HIVE-7K3Q', expiresAt: '2026-01-01T00:10:00Z', result: { outcome: 'timeout' } });
128
128
  const { logs, restore } = captureLogs();
129
129
  await runCommand('login').catch(() => { });
130
- expect(logs.join('\n')).toContain('Code expired — run `wen-market auth login` again.');
130
+ expect(logs.join('\n')).toContain('Code expired — run `hiveop auth login` again.');
131
131
  expect(mockSaveConfig).not.toHaveBeenCalled();
132
132
  restore();
133
133
  });
@@ -210,7 +210,7 @@ describe('auth login --api-key (CI)', () => {
210
210
  const { logs, restore } = captureLogs();
211
211
  const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined);
212
212
  await runCommand('login', '--api-key', WALLET_PUBKEY);
213
- expect(logs.join('\n')).toContain("That's a wallet address, not an API key. Run `wen-market auth login` or create a key at https://marketplace.wenrwa.com/settings");
213
+ expect(logs.join('\n')).toContain("That's a wallet address, not an API key. Run `hiveop auth login` or create a key at https://marketplace.wenrwa.com/settings");
214
214
  expect(mockSaveConfig).not.toHaveBeenCalled();
215
215
  expect(mockResolveIdentity).not.toHaveBeenCalled();
216
216
  expect(exitSpy).toHaveBeenCalledWith(1);
@@ -18,7 +18,7 @@ const makeDeps = (platform = 'darwin', hasConfig = true) => {
18
18
  run: async (cmd, args) => { runs.push([cmd, ...args]); return { stdout: '', code: 0 }; },
19
19
  uid: () => 501,
20
20
  path: '/Users/testuser/.local/bin:/usr/local/bin:/usr/bin:/bin',
21
- command: 'exec /Users/testuser/.local/bin/wen-market bridge',
21
+ command: 'exec /Users/testuser/.local/bin/hiveop bridge',
22
22
  },
23
23
  };
24
24
  };
@@ -26,12 +26,12 @@ describe('launchAgentPlist', () => {
26
26
  it('runs the bridge through a login shell (PATH-safe) with KeepAlive + RunAtLoad', () => {
27
27
  const xml = (0, autostart_1.launchAgentPlist)({
28
28
  logPath: `${HOME}/.wen-market/bridge.log`,
29
- command: `exec ${HOME}/.local/bin/wen-market bridge`,
29
+ command: `exec ${HOME}/.local/bin/hiveop bridge`,
30
30
  path: '/usr/bin:/bin',
31
31
  });
32
32
  expect(xml).toContain(`<string>${autostart_1.AUTOSTART_LABEL}</string>`);
33
33
  expect(xml).toContain('<string>/bin/zsh</string>');
34
- expect(xml).toContain(`exec ${HOME}/.local/bin/wen-market bridge`);
34
+ expect(xml).toContain(`exec ${HOME}/.local/bin/hiveop bridge`);
35
35
  expect(xml).toContain('<key>RunAtLoad</key>');
36
36
  expect(xml).toContain('<key>KeepAlive</key>');
37
37
  expect(xml).toContain(`${HOME}/.wen-market/bridge.log`);
@@ -47,7 +47,7 @@ describe('launchAgentPlist', () => {
47
47
  it('bakes an explicit EnvironmentVariables/PATH into the plist so launchd does not hand it a bare environment', () => {
48
48
  const xml = (0, autostart_1.launchAgentPlist)({
49
49
  logPath: `${HOME}/.wen-market/bridge.log`,
50
- command: 'exec /usr/local/bin/wen-market bridge',
50
+ command: 'exec /usr/local/bin/hiveop bridge',
51
51
  path: '/Users/testuser/.local/bin:/usr/local/bin:/usr/bin:/bin',
52
52
  });
53
53
  expect(xml).toContain('<key>EnvironmentVariables</key>');
@@ -57,11 +57,11 @@ describe('launchAgentPlist', () => {
57
57
  it('falls back to a sane default PATH when none is supplied (empty/undefined)', () => {
58
58
  const xmlUndefined = (0, autostart_1.launchAgentPlist)({
59
59
  logPath: `${HOME}/.wen-market/bridge.log`,
60
- command: 'exec /usr/local/bin/wen-market bridge',
60
+ command: 'exec /usr/local/bin/hiveop bridge',
61
61
  });
62
62
  const xmlEmpty = (0, autostart_1.launchAgentPlist)({
63
63
  logPath: `${HOME}/.wen-market/bridge.log`,
64
- command: 'exec /usr/local/bin/wen-market bridge',
64
+ command: 'exec /usr/local/bin/hiveop bridge',
65
65
  path: '',
66
66
  });
67
67
  for (const xml of [xmlUndefined, xmlEmpty]) {
@@ -72,10 +72,10 @@ describe('launchAgentPlist', () => {
72
72
  it('uses the supplied absolute-path command and never an untagged npx spec', () => {
73
73
  const xml = (0, autostart_1.launchAgentPlist)({
74
74
  logPath: `${HOME}/.wen-market/bridge.log`,
75
- command: `exec ${HOME}/.local/bin/wen-market bridge`,
75
+ command: `exec ${HOME}/.local/bin/hiveop bridge`,
76
76
  path: '/usr/bin:/bin',
77
77
  });
78
- expect(xml).toContain(`exec ${HOME}/.local/bin/wen-market bridge`);
78
+ expect(xml).toContain(`exec ${HOME}/.local/bin/hiveop bridge`);
79
79
  // The old untagged spec resolved to the `latest` dist-tag and drifted
80
80
  // out from under users — must never appear bare (unpinned) again.
81
81
  expect(xml).not.toMatch(/npx -y @wenrwa\/marketplace-cli(?!@| bridge@)(?=\s|$| bridge)/);
@@ -84,7 +84,7 @@ describe('launchAgentPlist', () => {
84
84
  it('XML-escapes special characters in PATH (e.g. `&`, which is legal in a shell PATH entry)', () => {
85
85
  const xml = (0, autostart_1.launchAgentPlist)({
86
86
  logPath: `${HOME}/.wen-market/bridge.log`,
87
- command: 'exec /usr/local/bin/wen-market bridge',
87
+ command: 'exec /usr/local/bin/hiveop bridge',
88
88
  path: '/opt/weird&path:/usr/bin',
89
89
  });
90
90
  expect(xml).toContain('/opt/weird&amp;path:/usr/bin');
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ /**
37
+ * The dirty check and Save/Remove against REAL git (a bare remote + a
38
+ * `--single-branch` clone, exactly how repoCheckout.ts clones), driven through
39
+ * a runner shaped like the production execRunner in commands/bridge.ts — it
40
+ * REJECTS on a failed command with no stdout. The fakes in the sibling suites
41
+ * cannot catch a wrong git invocation; this can.
42
+ *
43
+ * Owner report 2026-10-02: finished, pushed Practice match copies all read
44
+ * "Unsaved work", and a branch with no upstream read "Could not read this
45
+ * checkout: Command failed: git … rev-list @{u}..HEAD".
46
+ */
47
+ const child_process_1 = require("child_process");
48
+ const fs = __importStar(require("fs"));
49
+ const os = __importStar(require("os"));
50
+ const path = __importStar(require("path"));
51
+ const checkoutInventory_1 = require("../bridge/checkoutInventory");
52
+ const checkoutParkAndRemove_1 = require("../bridge/checkoutParkAndRemove");
53
+ const checkoutReclaim_1 = require("../bridge/checkoutReclaim");
54
+ /** commands/bridge.ts execRunner, verbatim in behaviour. */
55
+ const execRunner = (cmd, args) => new Promise((resolve, reject) => {
56
+ (0, child_process_1.execFile)(cmd, args, (err, stdout) => {
57
+ if (err && !stdout)
58
+ reject(err);
59
+ else
60
+ resolve({ stdout: stdout ?? '', code: 0 });
61
+ });
62
+ });
63
+ const git = (cwd, ...args) => (0, child_process_1.execFileSync)('git', ['-C', cwd, '-c', 'user.name=t', '-c', 'user.email=t@t', ...args], { encoding: 'utf8' });
64
+ const REGION = '<!-- HIVEOP:CONTEXT:START -->\n## Work graph\nx\n<!-- HIVEOP:CONTEXT:END -->\n';
65
+ let root;
66
+ let repos;
67
+ let remote;
68
+ const WS = 'ws-real';
69
+ let checkout;
70
+ beforeEach(() => {
71
+ root = fs.mkdtempSync(path.join(os.tmpdir(), 'dirty-real-git-'));
72
+ remote = path.join(root, 'remote.git');
73
+ repos = path.join(root, 'repos');
74
+ fs.mkdirSync(repos);
75
+ (0, child_process_1.execFileSync)('git', ['init', '--bare', '-b', 'main', remote]);
76
+ const seed = path.join(root, 'seed');
77
+ (0, child_process_1.execFileSync)('git', ['init', '-b', 'main', seed]);
78
+ fs.writeFileSync(path.join(seed, 'index.ts'), 'export {};\n');
79
+ git(seed, 'add', '-A');
80
+ git(seed, 'commit', '-m', 'seed');
81
+ git(seed, 'push', '--quiet', remote, 'main');
82
+ checkout = path.join(repos, WS);
83
+ (0, child_process_1.execFileSync)('git', ['clone', '--quiet', '--single-branch', remote, checkout]);
84
+ });
85
+ afterEach(() => {
86
+ fs.rmSync(root, { recursive: true, force: true });
87
+ });
88
+ describe('inspectDirty — real git', () => {
89
+ it('a fresh clone is clean', async () => {
90
+ expect(await (0, checkoutInventory_1.inspectDirty)(checkout, execRunner)).toEqual({ dirty: false, kinds: [], noUpstream: false });
91
+ });
92
+ it('the bridge’s CLAUDE.md region alone is not unsaved work', async () => {
93
+ fs.writeFileSync(path.join(checkout, 'CLAUDE.md'), REGION);
94
+ expect(await (0, checkoutInventory_1.inspectDirty)(checkout, execRunner)).toMatchObject({ dirty: false });
95
+ });
96
+ it('pushed work is clean; committed-but-unpushed is unpushed', async () => {
97
+ fs.writeFileSync(path.join(checkout, 'a.ts'), '1\n');
98
+ git(checkout, 'add', '-A');
99
+ git(checkout, 'commit', '-m', 'a');
100
+ expect(await (0, checkoutInventory_1.inspectDirty)(checkout, execRunner)).toMatchObject({ dirty: true, kinds: ['unpushed'] });
101
+ git(checkout, 'push', '--quiet');
102
+ expect(await (0, checkoutInventory_1.inspectDirty)(checkout, execRunner)).toMatchObject({ dirty: false });
103
+ });
104
+ it('a branch with no upstream is readable: unpushed + noUpstream, never a thrown git error', async () => {
105
+ git(checkout, 'checkout', '-q', '-b', 'backup/botsim-sweep');
106
+ fs.writeFileSync(path.join(checkout, 'b.ts'), '1\n');
107
+ git(checkout, 'add', '-A');
108
+ git(checkout, 'commit', '-m', 'b');
109
+ expect(await (0, checkoutInventory_1.inspectDirty)(checkout, execRunner)).toEqual({ dirty: true, kinds: ['unpushed'], noUpstream: true });
110
+ });
111
+ });
112
+ describe('Save and Remove — real git', () => {
113
+ const makeParker = () => {
114
+ const reclaimer = (0, checkoutReclaim_1.createCheckoutReclaimer)({
115
+ reposBaseDir: repos,
116
+ exists: (p) => fs.existsSync(p),
117
+ sizeBytes: async () => 1024,
118
+ rm: async (p) => fs.rmSync(p, { recursive: true, force: true }),
119
+ isAttached: () => false,
120
+ run: execRunner,
121
+ });
122
+ return (0, checkoutParkAndRemove_1.createCheckoutParker)({ reposBaseDir: repos, exists: (p) => fs.existsSync(p), run: execRunner, reclaimer });
123
+ };
124
+ it('Save backs uncommitted work up to park/<id>, keeps the checkout, and it then reads clean', async () => {
125
+ fs.writeFileSync(path.join(checkout, 'work.ts'), 'unsaved\n');
126
+ expect(await (0, checkoutInventory_1.checkDirty)(checkout, execRunner)).toBe(true);
127
+ const result = await makeParker().park(WS, undefined, { remove: false });
128
+ expect(result).toEqual({ workspaceId: WS, outcome: 'saved', branch: `park/${WS}` });
129
+ expect(fs.existsSync(path.join(checkout, 'work.ts'))).toBe(true);
130
+ expect((0, child_process_1.execFileSync)('git', ['--git-dir', remote, 'ls-tree', '--name-only', `park/${WS}`], { encoding: 'utf8' })).toContain('work.ts');
131
+ expect(await (0, checkoutInventory_1.checkDirty)(checkout, execRunner)).toBe(false);
132
+ });
133
+ it('Remove on unsaved work parks it, then actually removes the checkout (the old @{u} re-check aborted here)', async () => {
134
+ fs.writeFileSync(path.join(checkout, 'work.ts'), 'unsaved\n');
135
+ const result = await makeParker().park(WS);
136
+ expect(result).toMatchObject({ outcome: 'removed', branch: `park/${WS}` });
137
+ expect(fs.existsSync(checkout)).toBe(false);
138
+ });
139
+ it('Remove on a clean checkout removes it without pushing anything', async () => {
140
+ const result = await makeParker().park(WS);
141
+ expect(result).toMatchObject({ outcome: 'removed' });
142
+ expect(fs.existsSync(checkout)).toBe(false);
143
+ expect((0, child_process_1.execFileSync)('git', ['--git-dir', remote, 'branch', '--list', 'park/*'], { encoding: 'utf8' }).trim()).toBe('');
144
+ });
145
+ });
@@ -78,7 +78,7 @@ describe('createCheckoutInventoryReporter — trigger() never blocks (hcs-02 Δ)
78
78
  });
79
79
  });
80
80
  describe('createCheckoutInventoryReporter — in-flight scan is not restarted', () => {
81
- it('a second trigger() while a scan is running does not start another scan', async () => {
81
+ it('triggers while a scan is running do not restart it — they queue ONE follow-up pass', async () => {
82
82
  const { deps, resolveNow, listCalls: getListCalls } = makeSlowDeps();
83
83
  let listCallCount = 0;
84
84
  const wrapped = {
@@ -100,7 +100,11 @@ describe('createCheckoutInventoryReporter — in-flight scan is not restarted',
100
100
  expect(listCallCount).toBe(1); // only the first trigger actually started a scan
101
101
  resolveNow();
102
102
  await new Promise((r) => setTimeout(r, 10));
103
- expect(frames).toHaveLength(1); // exactly one frame, not three
103
+ // The pass in flight may have read the checkouts before whatever prompted
104
+ // the later triggers (a Save finishing) — so exactly one more pass runs,
105
+ // never one per trigger.
106
+ expect(listCallCount).toBe(2);
107
+ expect(frames).toHaveLength(2);
104
108
  });
105
109
  it('a trigger AFTER the previous scan completed starts a fresh scan', async () => {
106
110
  const { deps, listCalls } = makeFastDeps();