@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.
- package/README.md +85 -85
- package/dist/__tests__/auth-login-core.test.js +2 -2
- package/dist/__tests__/auth.test.js +4 -4
- package/dist/__tests__/bridge-autostart.test.js +9 -9
- package/dist/__tests__/bridge-checkout-dirty-real-git.test.d.ts +1 -0
- package/dist/__tests__/bridge-checkout-dirty-real-git.test.js +145 -0
- package/dist/__tests__/bridge-checkout-inventory-reporter.test.js +6 -2
- package/dist/__tests__/bridge-checkout-inventory.test.js +128 -14
- package/dist/__tests__/bridge-checkout-park-and-remove.test.js +57 -8
- package/dist/__tests__/bridge-claude-trust.test.d.ts +1 -0
- package/dist/__tests__/bridge-claude-trust.test.js +137 -0
- package/dist/__tests__/bridge-command-browser-options.test.js +1 -1
- package/dist/__tests__/bridge-command-install-env.test.js +1 -1
- package/dist/__tests__/bridge-device-registration.test.js +1 -1
- package/dist/__tests__/bridge-folder-registry.test.js +1 -1
- package/dist/__tests__/bridge-local-add-core.test.js +1 -1
- package/dist/__tests__/bridge-match-pointer.test.d.ts +1 -0
- package/dist/__tests__/bridge-match-pointer.test.js +136 -0
- package/dist/__tests__/bridge-pairing-api.test.js +1 -1
- package/dist/__tests__/bridge-pairing-core.test.js +1 -1
- package/dist/__tests__/bridge-park-remove-frame.test.js +61 -0
- package/dist/__tests__/bridge-relay-client-checkout-inventory.test.js +1 -1
- package/dist/__tests__/bridge-relay-mode.test.js +1 -1
- package/dist/__tests__/bridge-server.test.js +1 -1
- package/dist/__tests__/config.test.js +1 -1
- package/dist/__tests__/devices.test.js +1 -1
- package/dist/__tests__/local.test.js +1 -1
- package/dist/__tests__/openBrowser.test.js +1 -1
- package/dist/__tests__/websocket.test.js +3 -3
- package/dist/auth/identity.d.ts +1 -1
- package/dist/auth/login.d.ts +2 -2
- package/dist/bridge/apiClient.js +2 -2
- package/dist/bridge/autostart.d.ts +1 -1
- package/dist/bridge/autostart.js +1 -1
- package/dist/bridge/checkoutInventory.d.ts +48 -17
- package/dist/bridge/checkoutInventory.js +190 -44
- package/dist/bridge/checkoutParkAndRemove.d.ts +22 -1
- package/dist/bridge/checkoutParkAndRemove.js +37 -7
- package/dist/bridge/claudeTrust.d.ts +37 -0
- package/dist/bridge/claudeTrust.js +85 -0
- package/dist/bridge/config.d.ts +1 -1
- package/dist/bridge/contextWriter.d.ts +2 -0
- package/dist/bridge/contextWriter.js +3 -2
- package/dist/bridge/deviceRegistration.d.ts +1 -1
- package/dist/bridge/envSanitize.d.ts +1 -1
- package/dist/bridge/envSanitize.js +1 -1
- package/dist/bridge/folderRegistry.js +1 -1
- package/dist/bridge/localAdd.js +1 -1
- package/dist/bridge/localContextWriter.d.ts +2 -0
- package/dist/bridge/localContextWriter.js +3 -2
- package/dist/bridge/memoryBrief.d.ts +16 -4
- package/dist/bridge/memoryBrief.js +17 -9
- package/dist/bridge/pairing.d.ts +1 -1
- package/dist/bridge/relayClient.js +11 -1
- package/dist/bridge/server.d.ts +1 -1
- package/dist/bridge/workGraphPointer.d.ts +19 -0
- package/dist/bridge/workGraphPointer.js +26 -1
- package/dist/commands/apps.js +1 -1
- package/dist/commands/auth.d.ts +1 -1
- package/dist/commands/auth.js +6 -6
- package/dist/commands/bridge.js +40 -13
- package/dist/commands/credits.js +1 -1
- package/dist/commands/devices.d.ts +1 -1
- package/dist/commands/devices.js +4 -4
- package/dist/commands/local.d.ts +1 -1
- package/dist/commands/local.js +5 -5
- package/dist/commands/pair.d.ts +1 -1
- package/dist/commands/pair.js +4 -4
- package/dist/index.js +2 -2
- package/dist/utils/config.js +2 -2
- package/dist/utils/openBrowser.d.ts +1 -1
- package/dist/utils/openBrowser.js +1 -1
- package/dist/utils/websocket.js +1 -1
- package/package.json +3 -2
|
@@ -37,6 +37,19 @@
|
|
|
37
37
|
* against the same working tree would race git itself, not merely the
|
|
38
38
|
* delete — hcs-05's set alone only covers the delete step.
|
|
39
39
|
*
|
|
40
|
+
* SAVE (cli 0.6.9, owner request "why can't I get these things to save
|
|
41
|
+
* without removing?"): `park(id, onPhase, { remove: false })` runs the same
|
|
42
|
+
* commit -> push -> verify and stops there — outcome `saved`, checkout kept.
|
|
43
|
+
* Remove on a checkout with nothing to park now removes it (through the
|
|
44
|
+
* reclaimer, which re-checks clean + unattached first): everything in it is
|
|
45
|
+
* already on a remote, so refusing left clean match copies with no way out
|
|
46
|
+
* but the 14-day automatic sweep.
|
|
47
|
+
*
|
|
48
|
+
* After a verified push the parking branch is recorded as a remote-tracking
|
|
49
|
+
* ref (`refs/remotes/origin/park/<id>`). The clone is `--single-branch`, so
|
|
50
|
+
* git does not do this itself, and without it the dirty check (commits no
|
|
51
|
+
* remote ref contains) could never see that the work is now saved.
|
|
52
|
+
*
|
|
40
53
|
* PHASES: `committing` / `pushing` / `removing`, reported through the
|
|
41
54
|
* optional `onPhase` callback as each begins — the wire layer
|
|
42
55
|
* (relayClient.ts) turns these into `park-remove-progress` frames so a UI
|
|
@@ -55,6 +68,10 @@ export type ParkAndRemoveOutcome = {
|
|
|
55
68
|
workspaceId: string;
|
|
56
69
|
outcome: 'nothing-to-park';
|
|
57
70
|
branch: string;
|
|
71
|
+
} | {
|
|
72
|
+
workspaceId: string;
|
|
73
|
+
outcome: 'saved';
|
|
74
|
+
branch: string;
|
|
58
75
|
} | {
|
|
59
76
|
workspaceId: string;
|
|
60
77
|
outcome: 'retained';
|
|
@@ -87,13 +104,17 @@ export interface CheckoutParkAndRemoveDeps {
|
|
|
87
104
|
* hcs-05 already proved. */
|
|
88
105
|
reclaimer: CheckoutReclaimer;
|
|
89
106
|
}
|
|
107
|
+
export interface ParkOptions {
|
|
108
|
+
/** false = Save: back the work up and keep the checkout. Default true. */
|
|
109
|
+
remove?: boolean;
|
|
110
|
+
}
|
|
90
111
|
export interface CheckoutParker {
|
|
91
112
|
/** Park (commit + push + remove-on-verified-push) one checkout.
|
|
92
113
|
* `onPhase`, when given, is called once per phase as it BEGINS —
|
|
93
114
|
* 'committing' first, always; 'pushing' and 'removing' only if the
|
|
94
115
|
* operation reaches them. Never throws — every failure mode is a typed
|
|
95
116
|
* outcome, same discipline as CheckoutReclaimer#reclaim. */
|
|
96
|
-
park(workspaceId: string, onPhase?: (phase: ParkPhase) => void): Promise<ParkAndRemoveOutcome>;
|
|
117
|
+
park(workspaceId: string, onPhase?: (phase: ParkPhase) => void, opts?: ParkOptions): Promise<ParkAndRemoveOutcome>;
|
|
97
118
|
}
|
|
98
119
|
/**
|
|
99
120
|
* Deterministic from the workspace id alone (no timestamp) — PURE, so a
|
|
@@ -41,6 +41,19 @@ exports.createCheckoutParker = createCheckoutParker;
|
|
|
41
41
|
* against the same working tree would race git itself, not merely the
|
|
42
42
|
* delete — hcs-05's set alone only covers the delete step.
|
|
43
43
|
*
|
|
44
|
+
* SAVE (cli 0.6.9, owner request "why can't I get these things to save
|
|
45
|
+
* without removing?"): `park(id, onPhase, { remove: false })` runs the same
|
|
46
|
+
* commit -> push -> verify and stops there — outcome `saved`, checkout kept.
|
|
47
|
+
* Remove on a checkout with nothing to park now removes it (through the
|
|
48
|
+
* reclaimer, which re-checks clean + unattached first): everything in it is
|
|
49
|
+
* already on a remote, so refusing left clean match copies with no way out
|
|
50
|
+
* but the 14-day automatic sweep.
|
|
51
|
+
*
|
|
52
|
+
* After a verified push the parking branch is recorded as a remote-tracking
|
|
53
|
+
* ref (`refs/remotes/origin/park/<id>`). The clone is `--single-branch`, so
|
|
54
|
+
* git does not do this itself, and without it the dirty check (commits no
|
|
55
|
+
* remote ref contains) could never see that the work is now saved.
|
|
56
|
+
*
|
|
44
57
|
* PHASES: `committing` / `pushing` / `removing`, reported through the
|
|
45
58
|
* optional `onPhase` callback as each begins — the wire layer
|
|
46
59
|
* (relayClient.ts) turns these into `park-remove-progress` frames so a UI
|
|
@@ -81,7 +94,8 @@ function createCheckoutParker(deps) {
|
|
|
81
94
|
// must cover the WHOLE operation (commit+push+remove) and not merely the
|
|
82
95
|
// delete step, which is a SEPARATE lock (hcs-05's reclaimer, reused below).
|
|
83
96
|
const inFlight = new Set();
|
|
84
|
-
async function park(workspaceId, onPhase) {
|
|
97
|
+
async function park(workspaceId, onPhase, opts = {}) {
|
|
98
|
+
const remove = opts.remove !== false;
|
|
85
99
|
// Path safety — resolved from workspaceId ALONE, reusing hcs-05's exact
|
|
86
100
|
// resolver rather than a second implementation. There is no path on the
|
|
87
101
|
// wire to trust in the first place.
|
|
@@ -132,14 +146,25 @@ function createCheckoutParker(deps) {
|
|
|
132
146
|
// A failure here (no upstream configured, detached HEAD, ...) is NOT
|
|
133
147
|
// a hard failure — it just means "assume there might be something to
|
|
134
148
|
// push" and let the push step itself be the source of truth.
|
|
135
|
-
|
|
149
|
+
// Commits no remote-tracking ref contains — the same definition the
|
|
150
|
+
// dirty check uses (checkoutInventory.ts inspectDirty).
|
|
151
|
+
const ahead = await runGit(deps.run, target, ['rev-list', 'HEAD', '--not', '--remotes', '--count']);
|
|
136
152
|
const hasUnpushed = ahead.code === 0 ? parseInt(ahead.stdout.trim(), 10) > 0 : true;
|
|
137
153
|
if (!committedSomething && !hasUnpushed) {
|
|
138
|
-
// Genuinely nothing to park
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
// Genuinely nothing to park: everything here is already on a remote.
|
|
155
|
+
// Save reports that. Remove goes straight to the reclaimer, whose
|
|
156
|
+
// own immediate re-check (clean, unattached) still guards the delete.
|
|
157
|
+
if (!remove)
|
|
158
|
+
return { workspaceId, outcome: 'nothing-to-park', branch };
|
|
159
|
+
phase = 'removing';
|
|
160
|
+
onPhase?.(phase);
|
|
161
|
+
const [cleanResult] = await deps.reclaimer.reclaim([
|
|
162
|
+
{ workspaceId, verdict: 'RECLAIM', reason: 'park-and-remove: nothing unsaved' },
|
|
163
|
+
]);
|
|
164
|
+
if (cleanResult.outcome === 'removed') {
|
|
165
|
+
return { workspaceId, outcome: 'removed', branch, bytesFreed: cleanResult.bytesFreed };
|
|
166
|
+
}
|
|
167
|
+
return { workspaceId, outcome: 'retained', branch, phase, reason: cleanResult.reason };
|
|
143
168
|
}
|
|
144
169
|
// ---- PUSHING --------------------------------------------------------
|
|
145
170
|
phase = 'pushing';
|
|
@@ -167,6 +192,11 @@ function createCheckoutParker(deps) {
|
|
|
167
192
|
if (remoteSha !== localSha) {
|
|
168
193
|
return { workspaceId, outcome: 'retained', branch, phase, reason: `push reported success but the remote ref points at a different commit (${remoteSha.slice(0, 8)} != ${localSha.slice(0, 8)}) — refusing to trust it` };
|
|
169
194
|
}
|
|
195
|
+
// Record what the remote just proved, so the dirty check sees the work
|
|
196
|
+
// as saved. Best-effort: the push is already verified either way.
|
|
197
|
+
await runGit(deps.run, target, ['update-ref', `refs/remotes/origin/${branch}`, localSha]);
|
|
198
|
+
if (!remove)
|
|
199
|
+
return { workspaceId, outcome: 'saved', branch };
|
|
170
200
|
// ---- REMOVING -------------------------------------------------------
|
|
171
201
|
// The work is now PROVABLY on the remote. Only past this point may
|
|
172
202
|
// anything be deleted, and only via hcs-05's own executor — see the
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playtest 2026-10-01 (Qubit Forge, hosted lane) — pre-trust the workspace
|
|
3
|
+
* checkouts this bridge itself clones, so Claude Code never opens on its
|
|
4
|
+
* folder-trust dialog in them.
|
|
5
|
+
*
|
|
6
|
+
* Claude Code (2.1.28x) shows "Quick safety check: Is this a project you
|
|
7
|
+
* created or one you trust? … ❯ No, exit / Yes, I trust this folder" on any
|
|
8
|
+
* folder its config has not trusted — EVEN under --dangerously-skip-
|
|
9
|
+
* permissions — and the default is "No, exit". Every match / workspace gets a
|
|
10
|
+
* fresh /data/repos/<workspaceId>, so every first agent there met it, and a
|
|
11
|
+
* queued first message (Send to agent) answered it with the default: Claude
|
|
12
|
+
* exited to a bare shell.
|
|
13
|
+
*
|
|
14
|
+
* The container is ours, the bridge cloned the folder from the workspace's own
|
|
15
|
+
* repo, and the agent was launched in bypass mode by the pilot's own spawn
|
|
16
|
+
* choice — the trust question has nobody meaningful to ask. So the bridge
|
|
17
|
+
* records the answer Claude would: `projects[<dir>].hasTrustDialogAccepted =
|
|
18
|
+
* true` in `$CLAUDE_CONFIG_DIR/.claude.json` (where Claude keeps its global
|
|
19
|
+
* config when CLAUDE_CONFIG_DIR is set — verified against 2.1.286).
|
|
20
|
+
*
|
|
21
|
+
* The entry must name the checkout itself: inside a git repo Claude only walks
|
|
22
|
+
* up to the repo ROOT looking for a trusted entry, so trusting /data/repos
|
|
23
|
+
* would not count.
|
|
24
|
+
*
|
|
25
|
+
* Scope: HOSTED LANE ONLY (commands/bridge.ts wires it into the container's
|
|
26
|
+
* clone-on-demand path, and only when CLAUDE_CONFIG_DIR is set), and only for
|
|
27
|
+
* folders strictly inside the bridge's repos dir. A local-lane user's own
|
|
28
|
+
* Claude config is never touched.
|
|
29
|
+
*
|
|
30
|
+
* Never clobbers: an unparseable config (it also carries onboarding/login
|
|
31
|
+
* state) is left alone; every other key is preserved; the write is tmp+rename.
|
|
32
|
+
*/
|
|
33
|
+
export type ClaudeTrustResult = 'trusted' | 'already' | 'skipped';
|
|
34
|
+
export declare function trustClaudeProjectDir(dir: string, opts: {
|
|
35
|
+
configDir: string;
|
|
36
|
+
reposBaseDir: string;
|
|
37
|
+
}): ClaudeTrustResult;
|
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
exports.trustClaudeProjectDir = trustClaudeProjectDir;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function trustClaudeProjectDir(dir, opts) {
|
|
40
|
+
const base = path.resolve(opts.reposBaseDir);
|
|
41
|
+
const target = path.resolve(dir);
|
|
42
|
+
if (!target.startsWith(base + path.sep))
|
|
43
|
+
return 'skipped';
|
|
44
|
+
const file = path.join(opts.configDir, '.claude.json');
|
|
45
|
+
let cfg = {};
|
|
46
|
+
let raw = null;
|
|
47
|
+
try {
|
|
48
|
+
raw = fs.readFileSync(file, 'utf-8');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
raw = null; // no config yet — Claude merges its defaults over what we write
|
|
52
|
+
}
|
|
53
|
+
if (raw !== null) {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(raw);
|
|
56
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
57
|
+
return 'skipped';
|
|
58
|
+
cfg = parsed;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return 'skipped';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const projects = cfg.projects && typeof cfg.projects === 'object' && !Array.isArray(cfg.projects)
|
|
65
|
+
? cfg.projects
|
|
66
|
+
: {};
|
|
67
|
+
const entry = projects[target] && typeof projects[target] === 'object' ? projects[target] : {};
|
|
68
|
+
if (entry.hasTrustDialogAccepted === true)
|
|
69
|
+
return 'already';
|
|
70
|
+
const next = { ...cfg, projects: { ...projects, [target]: { ...entry, hasTrustDialogAccepted: true } } };
|
|
71
|
+
fs.mkdirSync(opts.configDir, { recursive: true });
|
|
72
|
+
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
73
|
+
try {
|
|
74
|
+
fs.writeFileSync(tmp, JSON.stringify(next, null, 2) + '\n', { mode: 0o600 });
|
|
75
|
+
fs.renameSync(tmp, file);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
try {
|
|
79
|
+
fs.rmSync(tmp, { force: true });
|
|
80
|
+
}
|
|
81
|
+
catch { /* best effort */ }
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
return 'trusted';
|
|
85
|
+
}
|
package/dist/bridge/config.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface BridgeConfig {
|
|
2
2
|
connectToken: string;
|
|
3
3
|
/** lws-03: set once this machine auto-registers as a device (first
|
|
4
|
-
* `
|
|
4
|
+
* `hiveop bridge` run) — persisted so re-registration is idempotent. */
|
|
5
5
|
deviceId?: string;
|
|
6
6
|
}
|
|
7
7
|
export interface DiscoveryInfo {
|
|
@@ -32,6 +32,8 @@ export declare function writeContext(input: {
|
|
|
32
32
|
/** Absent only for a pointers-only refresh (wmr-07). */
|
|
33
33
|
content?: string;
|
|
34
34
|
workGraphAvailable?: boolean;
|
|
35
|
+
/** 2026-10-02 — a match workspace: MATCH_POINTER replaces the work-queue pointer (workSection). */
|
|
36
|
+
match?: boolean;
|
|
35
37
|
/**
|
|
36
38
|
* wmr-01 — whether to carry the workspace-memory guidance. Decided by the
|
|
37
39
|
* caller on the same signal as workGraphAvailable (WENRWA_CONNECT_TOKEN),
|
|
@@ -148,8 +148,9 @@ function writeContext(input, deps) {
|
|
|
148
148
|
return existing.slice(s0 + exports.CONTEXT_START_MARKER.length, e0).trim();
|
|
149
149
|
})();
|
|
150
150
|
const pointers = [];
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
const work = (0, workGraphPointer_1.workSection)(input);
|
|
152
|
+
if (work)
|
|
153
|
+
pointers.push(work);
|
|
153
154
|
if (input.memoryAvailable)
|
|
154
155
|
pointers.push((0, memoryPointer_1.memorySection)(input.memoryBrief));
|
|
155
156
|
// wmr-07: a pointers-only refresh keeps the region's existing file content
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lws-03 — "Connect this machine": on first `
|
|
2
|
+
* lws-03 — "Connect this machine": on first `hiveop bridge` run with a
|
|
3
3
|
* Connect Token configured, auto-register the device and persist its id
|
|
4
4
|
* into bridge-config.json so re-registration is skipped thereafter (a
|
|
5
5
|
* pure resolver, mirroring resolveRelayMode in commands/bridge.ts, so the
|
|
@@ -20,7 +20,7 @@ export declare const STRIPPED_ENV_PREFIXES: readonly ["CLAUDE_CODE_"];
|
|
|
20
20
|
* fcct-27 — the RELAY CREDENTIALS are stripped here too, for a different and
|
|
21
21
|
* sharper reason than the session markers above.
|
|
22
22
|
*
|
|
23
|
-
* `
|
|
23
|
+
* `hiveop bridge` reads `CREW_RELAY_TOKEN` / `RELAY_TOKEN` from its own
|
|
24
24
|
* environment (the launch agent sets them), and every PTY it spawns inherits
|
|
25
25
|
* that environment. Seen live 2026-08-16: a crew member's `claude` process had
|
|
26
26
|
* the device-bound connect token sitting in plain view in `ps eww`. Nothing
|
|
@@ -24,7 +24,7 @@ exports.STRIPPED_ENV_PREFIXES = ['CLAUDE_CODE_'];
|
|
|
24
24
|
* fcct-27 — the RELAY CREDENTIALS are stripped here too, for a different and
|
|
25
25
|
* sharper reason than the session markers above.
|
|
26
26
|
*
|
|
27
|
-
* `
|
|
27
|
+
* `hiveop bridge` reads `CREW_RELAY_TOKEN` / `RELAY_TOKEN` from its own
|
|
28
28
|
* environment (the launch agent sets them), and every PTY it spawns inherits
|
|
29
29
|
* that environment. Seen live 2026-08-16: a crew member's `claude` process had
|
|
30
30
|
* the device-bound connect token sitting in plain view in `ps eww`. Nothing
|
|
@@ -41,7 +41,7 @@ exports.removeEntryByWorkspaceId = removeEntryByWorkspaceId;
|
|
|
41
41
|
exports.findEntryByPath = findEntryByPath;
|
|
42
42
|
exports.findEntryByWorkspaceId = findEntryByWorkspaceId;
|
|
43
43
|
/**
|
|
44
|
-
* lws-03 — folder→workspace registry. Backs `
|
|
44
|
+
* lws-03 — folder→workspace registry. Backs `hiveop local add/list/
|
|
45
45
|
* remove` and the discovery advertisement (server.ts). Lives alongside the
|
|
46
46
|
* bridge's other dotfiles under ~/.wen-market/ rather than a second dotdir,
|
|
47
47
|
* matching config.ts's convention. File-backed so it survives daemon
|
package/dist/bridge/localAdd.js
CHANGED
|
@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.addLocalWorkspaceCore = addLocalWorkspaceCore;
|
|
37
37
|
/**
|
|
38
38
|
* lws-05 — bridge-side idempotent add core, behind POST /local/add. Mirrors
|
|
39
|
-
* `
|
|
39
|
+
* `hiveop local add`'s behavior (commands/local.ts) so the arena
|
|
40
40
|
* create-modal's Browse/typed-path attach mechanisms (PRD D3b/D3c) end at the
|
|
41
41
|
* exact same registered-workspace outcome as the CLI mechanism (D3a) —
|
|
42
42
|
* deps-injected so server.ts can wire it without this module knowing about
|
|
@@ -29,6 +29,8 @@ export declare function writeLocalContext(input: {
|
|
|
29
29
|
/** Absent only for a pointers-only refresh (wmr-07). */
|
|
30
30
|
content?: string;
|
|
31
31
|
workGraphAvailable?: boolean;
|
|
32
|
+
/** 2026-10-02 — a match workspace: MATCH_POINTER replaces the work-queue pointer (workSection). */
|
|
33
|
+
match?: boolean;
|
|
32
34
|
/** wmr-04 — two-lane parity with contextWriter.ts. Gated by the caller on
|
|
33
35
|
* the local lane's own honest signal (did THIS bridge run register the
|
|
34
36
|
* MCP server), not on a container env var that does not exist here. */
|
|
@@ -89,8 +89,9 @@ function writeLocalContext(input, deps) {
|
|
|
89
89
|
// this would be a two-lane bug nobody would notice until a local pilot
|
|
90
90
|
// reported missing memory.
|
|
91
91
|
const pointers = [];
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
const work = (0, workGraphPointer_1.workSection)(input);
|
|
93
|
+
if (work)
|
|
94
|
+
pointers.push(work);
|
|
94
95
|
if (input.memoryAvailable)
|
|
95
96
|
pointers.push((0, memoryPointer_1.memorySection)(input.memoryBrief));
|
|
96
97
|
// wmr-07 — the whole local file is bridge-owned, so "preserve the pushed
|
|
@@ -33,9 +33,21 @@ export interface FetchMemoryBriefOpts {
|
|
|
33
33
|
timeoutMs?: number;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
|
|
36
|
+
* The brief reply as the bridge uses it: the brief text (null when empty) and
|
|
37
|
+
* whether the workspace is a MATCH (2026-10-02 — the reply's `match: true`;
|
|
38
|
+
* absent on an ordinary workspace and from an older backend).
|
|
39
|
+
*/
|
|
40
|
+
export interface WorkspaceBriefInfo {
|
|
41
|
+
brief: string | null;
|
|
42
|
+
match: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Fetch the brief reply for `workspaceId`. Same failure contract as the module
|
|
46
|
+
* header: never throws, never hangs — any failure resolves null (unknown).
|
|
47
|
+
*/
|
|
48
|
+
export declare function fetchWorkspaceBrief(workspaceId: string, opts?: FetchMemoryBriefOpts): Promise<WorkspaceBriefInfo | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Fetch the workspace memory brief for `workspaceId` — the brief text only
|
|
51
|
+
* (wmr-02's contract, unchanged: null on any failure or an empty brief).
|
|
40
52
|
*/
|
|
41
53
|
export declare function fetchMemoryBrief(workspaceId: string, opts?: FetchMemoryBriefOpts): Promise<string | null>;
|
|
@@ -16,17 +16,16 @@
|
|
|
16
16
|
* point of failure for opening a terminal.
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.fetchWorkspaceBrief = fetchWorkspaceBrief;
|
|
19
20
|
exports.fetchMemoryBrief = fetchMemoryBrief;
|
|
20
21
|
const DEFAULT_API_URL = 'https://api.wenrwa.com/api/v1';
|
|
21
22
|
/** Bounded so a hanging backend can never stall a terminal open. */
|
|
22
23
|
const DEFAULT_TIMEOUT_MS = 4000;
|
|
23
24
|
/**
|
|
24
|
-
* Fetch the
|
|
25
|
-
* header
|
|
26
|
-
* rather than propagating an error or letting a slow response block the
|
|
27
|
-
* caller.
|
|
25
|
+
* Fetch the brief reply for `workspaceId`. Same failure contract as the module
|
|
26
|
+
* header: never throws, never hangs — any failure resolves null (unknown).
|
|
28
27
|
*/
|
|
29
|
-
async function
|
|
28
|
+
async function fetchWorkspaceBrief(workspaceId, opts = {}) {
|
|
30
29
|
const token = process.env.WENRWA_CONNECT_TOKEN;
|
|
31
30
|
if (!token)
|
|
32
31
|
return null; // missing/empty token — never even attempt the call
|
|
@@ -47,10 +46,12 @@ async function fetchMemoryBrief(workspaceId, opts = {}) {
|
|
|
47
46
|
if (!res.ok)
|
|
48
47
|
return null;
|
|
49
48
|
const body = (await res.json());
|
|
50
|
-
if (body?.success !== true
|
|
51
|
-
return null;
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
if (body?.success !== true)
|
|
50
|
+
return null;
|
|
51
|
+
return {
|
|
52
|
+
brief: typeof body.brief === 'string' && body.brief.length > 0 ? body.brief : null,
|
|
53
|
+
match: body.match === true,
|
|
54
|
+
};
|
|
54
55
|
}
|
|
55
56
|
catch {
|
|
56
57
|
// network error, abort/timeout, or json() throwing on malformed JSON —
|
|
@@ -61,3 +62,10 @@ async function fetchMemoryBrief(workspaceId, opts = {}) {
|
|
|
61
62
|
clearTimeout(timer);
|
|
62
63
|
}
|
|
63
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Fetch the workspace memory brief for `workspaceId` — the brief text only
|
|
67
|
+
* (wmr-02's contract, unchanged: null on any failure or an empty brief).
|
|
68
|
+
*/
|
|
69
|
+
async function fetchMemoryBrief(workspaceId, opts = {}) {
|
|
70
|
+
return (await fetchWorkspaceBrief(workspaceId, opts))?.brief ?? null;
|
|
71
|
+
}
|
package/dist/bridge/pairing.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lws-14 — device-code pairing (GitHub-CLI / Apple-TV pattern), replacing
|
|
3
|
-
* "mint a Connect Token, copy it, paste it into a shell". `
|
|
3
|
+
* "mint a Connect Token, copy it, paste it into a shell". `hiveop pair`
|
|
4
4
|
* requests a code, prints it, and polls until a human approves it in the
|
|
5
5
|
* browser — no secret is ever typed or pasted by the user.
|
|
6
6
|
*
|
|
@@ -1300,7 +1300,13 @@ function startRelayClient(opts) {
|
|
|
1300
1300
|
});
|
|
1301
1301
|
break;
|
|
1302
1302
|
}
|
|
1303
|
+
case 'inventory-scan': {
|
|
1304
|
+
checkoutInventoryReporter?.trigger();
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
case 'park-save':
|
|
1303
1308
|
case 'park-remove': {
|
|
1309
|
+
const removeAfter = frame.t === 'park-remove';
|
|
1304
1310
|
// hcs-06: ALWAYS user-initiated (see the frame's own doc comment
|
|
1305
1311
|
// in the ClientFrame union above) — commit + push + (only on a
|
|
1306
1312
|
// verified push) remove, reporting real phases as
|
|
@@ -1320,9 +1326,13 @@ function startRelayClient(opts) {
|
|
|
1320
1326
|
parker
|
|
1321
1327
|
.park(frame.workspaceId, (phase) => {
|
|
1322
1328
|
send(socket, { t: 'park-remove-progress', workspaceId: frame.workspaceId, ...sidFields, phase });
|
|
1323
|
-
})
|
|
1329
|
+
}, { remove: removeAfter })
|
|
1324
1330
|
.then((result) => {
|
|
1325
1331
|
send(socket, { t: 'park-remove-result', ...sidFields, ...result });
|
|
1332
|
+
// The row's truth is the inventory, not this result: re-scan
|
|
1333
|
+
// so a saved checkout reads "Saved" without waiting for the
|
|
1334
|
+
// next connect.
|
|
1335
|
+
checkoutInventoryReporter?.trigger();
|
|
1326
1336
|
})
|
|
1327
1337
|
.catch((err) => {
|
|
1328
1338
|
console.error('[bridge] park-and-remove failed:', err);
|
package/dist/bridge/server.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface BridgeServerOpts {
|
|
|
19
19
|
* (cloned on first use). Absent/null result → shell opens in $HOME. */
|
|
20
20
|
resolveCwd?: (repoUrl: string) => Promise<string | null>;
|
|
21
21
|
/** lws-03: the registered local workspaces to advertise on /discovery, so
|
|
22
|
-
* the arena create-modal can live-detect `
|
|
22
|
+
* the arena create-modal can live-detect `hiveop local add` without a
|
|
23
23
|
* daemon restart. A GETTER (not a static array) — server.ts calls it on
|
|
24
24
|
* every /discovery request, so callers should back it with a fresh
|
|
25
25
|
* folderRegistry.loadFolderRegistry() read (or an in-memory cache kept
|
|
@@ -32,3 +32,22 @@
|
|
|
32
32
|
* spec — never "skip the spec, the queue replaces it."
|
|
33
33
|
*/
|
|
34
34
|
export declare const WORK_GRAPH_POINTER: string;
|
|
35
|
+
/**
|
|
36
|
+
* Qubit Forge playtest 2026-10-02 — the guidance a MATCH workspace gets INSTEAD
|
|
37
|
+
* of the work-queue pointer. An agent in a practice match read "the HiveOp work
|
|
38
|
+
* queue decides what to work on" and went looking for issues to claim. A match
|
|
39
|
+
* has no queue to work: its missions are scored by hidden tests when the pilot
|
|
40
|
+
* presses Submit, against the code on the default branch. So: commit and push,
|
|
41
|
+
* and leave the issue tools alone (claiming or finishing a mission issue does
|
|
42
|
+
* not score it).
|
|
43
|
+
*/
|
|
44
|
+
export declare const MATCH_POINTER: string;
|
|
45
|
+
/**
|
|
46
|
+
* The work section of the pointer block: match guidance for a match
|
|
47
|
+
* workspace, else the work-queue pointer when its tools are registered.
|
|
48
|
+
* One function so the hosted and local writers cannot diverge.
|
|
49
|
+
*/
|
|
50
|
+
export declare function workSection(input: {
|
|
51
|
+
workGraphAvailable?: boolean;
|
|
52
|
+
match?: boolean;
|
|
53
|
+
}): string | null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WORK_GRAPH_POINTER = void 0;
|
|
3
|
+
exports.MATCH_POINTER = exports.WORK_GRAPH_POINTER = void 0;
|
|
4
|
+
exports.workSection = workSection;
|
|
4
5
|
/**
|
|
5
6
|
* wgcp-01 — the one piece of guidance that tells an agent the workspace work
|
|
6
7
|
* graph exists at all. Nothing today does: the MCP tools (`list_ready_issues`,
|
|
@@ -39,3 +40,27 @@ exports.WORK_GRAPH_POINTER = [
|
|
|
39
40
|
'The HiveOp work queue — not the repo — decides WHAT to work on next: `list_ready_issues` to see what is ready, `claim_issue` before starting, `finish_issue` when done.',
|
|
40
41
|
'Repo files (including `issues/*.md`) may hold the spec for an item you claimed — read them for detail; just do not pick work from them.',
|
|
41
42
|
].join('\n');
|
|
43
|
+
/**
|
|
44
|
+
* Qubit Forge playtest 2026-10-02 — the guidance a MATCH workspace gets INSTEAD
|
|
45
|
+
* of the work-queue pointer. An agent in a practice match read "the HiveOp work
|
|
46
|
+
* queue decides what to work on" and went looking for issues to claim. A match
|
|
47
|
+
* has no queue to work: its missions are scored by hidden tests when the pilot
|
|
48
|
+
* presses Submit, against the code on the default branch. So: commit and push,
|
|
49
|
+
* and leave the issue tools alone (claiming or finishing a mission issue does
|
|
50
|
+
* not score it).
|
|
51
|
+
*/
|
|
52
|
+
exports.MATCH_POINTER = [
|
|
53
|
+
'## Match',
|
|
54
|
+
'This workspace is a HiveOp match. Missions are scored when the pilot presses Submit: hidden tests run against the code committed and pushed to the default branch.',
|
|
55
|
+
'Commit and push your work when it is ready. Do not use the issue tools (`list_ready_issues`, `claim_issue`, `finish_issue`) — there is no queue to work here; the mission files (MISSIONS.md, README.md) say what to build.',
|
|
56
|
+
].join('\n');
|
|
57
|
+
/**
|
|
58
|
+
* The work section of the pointer block: match guidance for a match
|
|
59
|
+
* workspace, else the work-queue pointer when its tools are registered.
|
|
60
|
+
* One function so the hosted and local writers cannot diverge.
|
|
61
|
+
*/
|
|
62
|
+
function workSection(input) {
|
|
63
|
+
if (input.match)
|
|
64
|
+
return exports.MATCH_POINTER;
|
|
65
|
+
return input.workGraphAvailable ? exports.WORK_GRAPH_POINTER : null;
|
|
66
|
+
}
|
package/dist/commands/apps.js
CHANGED
|
@@ -26,7 +26,7 @@ exports.appsCommand
|
|
|
26
26
|
const data = await client.get(`/projects?${params}`);
|
|
27
27
|
const projects = data.projects || [];
|
|
28
28
|
if (projects.length === 0) {
|
|
29
|
-
console.log('No apps found. Create one with:
|
|
29
|
+
console.log('No apps found. Create one with: hiveop apps create');
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
32
|
console.log(`${chalk_1.default.dim(`Showing ${projects.length} of ${data.total || projects.length} apps`)}\n`);
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* wha-08 (docs/prd/retire-wallet-header-auth.md, docs/CONTEXT-identity.md) —
|
|
3
|
-
* `
|
|
3
|
+
* `hiveop auth login` / `auth logout` / `auth whoami`.
|
|
4
4
|
*
|
|
5
5
|
* `login` runs the device-code flow (lws-14's mechanism, purpose=cli_login —
|
|
6
6
|
* see auth/login.ts): a code is minted and printed with the approval URL
|
package/dist/commands/auth.js
CHANGED
|
@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.authCommand = void 0;
|
|
40
40
|
/**
|
|
41
41
|
* wha-08 (docs/prd/retire-wallet-header-auth.md, docs/CONTEXT-identity.md) —
|
|
42
|
-
* `
|
|
42
|
+
* `hiveop auth login` / `auth logout` / `auth whoami`.
|
|
43
43
|
*
|
|
44
44
|
* `login` runs the device-code flow (lws-14's mechanism, purpose=cli_login —
|
|
45
45
|
* see auth/login.ts): a code is minted and printed with the approval URL
|
|
@@ -131,7 +131,7 @@ exports.authCommand
|
|
|
131
131
|
.action(async () => {
|
|
132
132
|
const config = (0, config_1.loadConfig)();
|
|
133
133
|
if (!config) {
|
|
134
|
-
(0, format_1.error)('Not signed in. Run:
|
|
134
|
+
(0, format_1.error)('Not signed in. Run: hiveop auth login');
|
|
135
135
|
process.exit(1);
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
@@ -151,7 +151,7 @@ exports.authCommand
|
|
|
151
151
|
async function confirmReplaceExistingLogin(existing) {
|
|
152
152
|
const label = (0, apiKeyFormat_1.shortenKey)(existing.apiKey);
|
|
153
153
|
if (!(0, prompt_1.isInteractive)()) {
|
|
154
|
-
(0, format_1.error)(`Already signed in (${label}). Run this in an interactive terminal to confirm, or \`
|
|
154
|
+
(0, format_1.error)(`Already signed in (${label}). Run this in an interactive terminal to confirm, or \`hiveop auth logout\` first.`);
|
|
155
155
|
return false;
|
|
156
156
|
}
|
|
157
157
|
const yes = await (0, prompt_1.confirm)(`Already signed in as ${label}. Sign in again? (y/N) `, false);
|
|
@@ -163,12 +163,12 @@ async function confirmReplaceExistingLogin(existing) {
|
|
|
163
163
|
// ── --api-key (CI) path ─────────────────────────────────────────────────
|
|
164
164
|
async function loginWithApiKey(key, apiUrl) {
|
|
165
165
|
if ((0, apiKeyFormat_1.looksLikeWalletPubkey)(key)) {
|
|
166
|
-
(0, format_1.error)(`That's a wallet address, not an API key. Run \`
|
|
166
|
+
(0, format_1.error)(`That's a wallet address, not an API key. Run \`hiveop auth login\` or create a key at ${SETTINGS_KEYS_URL}`);
|
|
167
167
|
process.exit(1);
|
|
168
168
|
return;
|
|
169
169
|
}
|
|
170
170
|
if (!(0, apiKeyFormat_1.isRealApiKey)(key)) {
|
|
171
|
-
(0, format_1.error)(`Not a valid API key (expected one starting with wm_sk_). Run \`
|
|
171
|
+
(0, format_1.error)(`Not a valid API key (expected one starting with wm_sk_). Run \`hiveop auth login\` or create a key at ${SETTINGS_KEYS_URL}`);
|
|
172
172
|
process.exit(1);
|
|
173
173
|
return;
|
|
174
174
|
}
|
|
@@ -228,7 +228,7 @@ async function reportLoginOutcome(outcome, apiUrl) {
|
|
|
228
228
|
return;
|
|
229
229
|
case 'expired':
|
|
230
230
|
case 'timeout':
|
|
231
|
-
(0, format_1.error)('Code expired — run `
|
|
231
|
+
(0, format_1.error)('Code expired — run `hiveop auth login` again.');
|
|
232
232
|
return;
|
|
233
233
|
case 'cancelled':
|
|
234
234
|
default:
|