@wenrwa/marketplace-cli 0.8.11 → 0.8.13
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/dist/__tests__/bridge-cleanup-frame.test.d.ts +1 -0
- package/dist/__tests__/bridge-cleanup-frame.test.js +217 -0
- package/dist/__tests__/bridge-clone-grow-quote.test.d.ts +1 -0
- package/dist/__tests__/bridge-clone-grow-quote.test.js +145 -0
- package/dist/__tests__/bridge-disk-pressure-frame.test.d.ts +1 -0
- package/dist/__tests__/bridge-disk-pressure-frame.test.js +179 -0
- package/dist/__tests__/bridge-preview-checkout.test.js +9 -0
- package/dist/__tests__/bridge-storage-cleanup-real-fs.test.d.ts +1 -0
- package/dist/__tests__/bridge-storage-cleanup-real-fs.test.js +372 -0
- package/dist/__tests__/git-token-refresh.test.d.ts +1 -0
- package/dist/__tests__/git-token-refresh.test.js +231 -0
- package/dist/bridge/diskPreflight.d.ts +4 -1
- package/dist/bridge/diskPreflight.js +5 -3
- package/dist/bridge/gitTokenBroker.d.ts +28 -0
- package/dist/bridge/gitTokenBroker.js +124 -0
- package/dist/bridge/previewCheckout.d.ts +11 -0
- package/dist/bridge/previewCheckout.js +18 -1
- package/dist/bridge/relayClient.d.ts +47 -0
- package/dist/bridge/relayClient.js +150 -2
- package/dist/bridge/repoCheckout.d.ts +3 -1
- package/dist/bridge/repoCheckout.js +6 -1
- package/dist/bridge/storageCleanup.d.ts +66 -0
- package/dist/bridge/storageCleanup.js +337 -0
- package/dist/bridge/storageQuotaFetch.js +12 -0
- package/dist/bridge/storageQuotaPreflight.d.ts +12 -0
- package/dist/commands/bridge.d.ts +10 -0
- package/dist/commands/bridge.js +59 -3
- package/dist/index.js +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,337 @@
|
|
|
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.BUILD_OUTPUT_DIRS = exports.CLEANUP_VERSION = exports.CLEANUP_CATEGORIES = void 0;
|
|
37
|
+
exports.defaultCacheDirs = defaultCacheDirs;
|
|
38
|
+
exports.createStorageCleaner = createStorageCleaner;
|
|
39
|
+
/**
|
|
40
|
+
* stc-05 (docs/prd/storage-billed-in-credits.md, ADR-0018; ADR-0008's floor) —
|
|
41
|
+
* measure and delete ONLY data the box can rebuild by itself:
|
|
42
|
+
*
|
|
43
|
+
* caches npm / uv / pip caches (the user's home, and the volume)
|
|
44
|
+
* previews Team preview slots (`<previews>/<ws>/{a,b}`) of a preview
|
|
45
|
+
* that is not running, and the shared uv cache under
|
|
46
|
+
* `<previews>/.tools/uv-cache` when no preview runs at all
|
|
47
|
+
* removedCheckouts a `<repos>/<ws>` directory with no repo left in it and
|
|
48
|
+
* nothing but empty dirs / build output — what an
|
|
49
|
+
* interrupted removal leaves behind
|
|
50
|
+
* buildOutput gitignored build directories (node_modules, .next, dist,
|
|
51
|
+
* build, target, .venv, ...) inside a CLEAN checkout
|
|
52
|
+
*
|
|
53
|
+
* Never: source, `.env` (or any other file a repo ignores that is not a known
|
|
54
|
+
* build directory), a checkout with uncommitted / untracked / unpushed /
|
|
55
|
+
* stashed work (checkoutInventory.ts's inspectDirty — the one dirty
|
|
56
|
+
* definition), or anything belonging to a workspace with an attached session.
|
|
57
|
+
* Attachment, dirtiness and preview activity are re-checked IMMEDIATELY before
|
|
58
|
+
* each delete (after the target is measured), so a session that attaches or a
|
|
59
|
+
* file written mid-run aborts that target (ADR-0008: "the attachment check
|
|
60
|
+
* happens immediately before removal").
|
|
61
|
+
*
|
|
62
|
+
* The figure the storage view shows ("N.N GB can be freed") is `measure()`;
|
|
63
|
+
* `clean()` frees exactly the targets it would have counted, each measured
|
|
64
|
+
* right before its delete, so the two agree unless something changed between.
|
|
65
|
+
*
|
|
66
|
+
* Automatic cleanup (stc-06, before a disk grows) is `runAutomaticCleanup()`
|
|
67
|
+
* and sits behind a kill switch that defaults OFF; when on it touches only the
|
|
68
|
+
* caches and preview categories. The user's own Clean up button (`clean()`)
|
|
69
|
+
* works regardless of the switch.
|
|
70
|
+
*
|
|
71
|
+
* HOSTED LANE ONLY — the local bridge never constructs one (HiveOp does not
|
|
72
|
+
* garbage-collect a laptop it does not own).
|
|
73
|
+
*/
|
|
74
|
+
const fs = __importStar(require("fs"));
|
|
75
|
+
const path = __importStar(require("path"));
|
|
76
|
+
const bundleInstaller_1 = require("./bundleInstaller");
|
|
77
|
+
const checkoutInventory_1 = require("./checkoutInventory");
|
|
78
|
+
exports.CLEANUP_CATEGORIES = ['caches', 'previews', 'removedCheckouts', 'buildOutput'];
|
|
79
|
+
/** Stamped on every report so the backend/arena can tell a bridge that can clean. */
|
|
80
|
+
exports.CLEANUP_VERSION = 1;
|
|
81
|
+
/** Directory names that are build output by convention — deleted only when the repo ignores them. */
|
|
82
|
+
exports.BUILD_OUTPUT_DIRS = new Set([
|
|
83
|
+
'node_modules',
|
|
84
|
+
'.next',
|
|
85
|
+
'.nuxt',
|
|
86
|
+
'.turbo',
|
|
87
|
+
'dist',
|
|
88
|
+
'build',
|
|
89
|
+
'target',
|
|
90
|
+
'.venv',
|
|
91
|
+
'__pycache__',
|
|
92
|
+
]);
|
|
93
|
+
/** npm / uv / pip caches under each home-like root, de-duplicated. */
|
|
94
|
+
function defaultCacheDirs(roots) {
|
|
95
|
+
const out = new Set();
|
|
96
|
+
for (const r of roots) {
|
|
97
|
+
if (!r)
|
|
98
|
+
continue;
|
|
99
|
+
for (const rel of ['.npm/_cacache', '.npm/_npx', '.cache/pip', '.cache/uv'])
|
|
100
|
+
out.add(path.join(r, rel));
|
|
101
|
+
}
|
|
102
|
+
return [...out];
|
|
103
|
+
}
|
|
104
|
+
function zeros() {
|
|
105
|
+
return { caches: 0, previews: 0, removedCheckouts: 0, buildOutput: 0 };
|
|
106
|
+
}
|
|
107
|
+
async function walkBytes(p) {
|
|
108
|
+
let st;
|
|
109
|
+
try {
|
|
110
|
+
st = await fs.promises.lstat(p);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
const own = typeof st.blocks === 'number' ? st.blocks * 512 : st.size;
|
|
116
|
+
if (!st.isDirectory())
|
|
117
|
+
return own;
|
|
118
|
+
let entries = [];
|
|
119
|
+
try {
|
|
120
|
+
entries = await fs.promises.readdir(p);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return own;
|
|
124
|
+
}
|
|
125
|
+
let total = own;
|
|
126
|
+
for (const e of entries)
|
|
127
|
+
total += await walkBytes(path.join(p, e));
|
|
128
|
+
return total;
|
|
129
|
+
}
|
|
130
|
+
const defaultRm = (p) => fs.promises.rm(p, { recursive: true, force: true });
|
|
131
|
+
/** A real directory (not a symlink to one). */
|
|
132
|
+
function isDir(p) {
|
|
133
|
+
try {
|
|
134
|
+
return fs.lstatSync(p).isDirectory();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function childDirs(dir) {
|
|
141
|
+
try {
|
|
142
|
+
return fs.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Nothing in here but directories and build-output directories — no file anyone could need. */
|
|
149
|
+
function holdsOnlyRebuildable(dir) {
|
|
150
|
+
let entries;
|
|
151
|
+
try {
|
|
152
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
for (const e of entries) {
|
|
158
|
+
if (!e.isDirectory())
|
|
159
|
+
return false; // a file, symlink, socket... keep
|
|
160
|
+
if (exports.BUILD_OUTPUT_DIRS.has(e.name))
|
|
161
|
+
continue;
|
|
162
|
+
if (!holdsOnlyRebuildable(path.join(dir, e.name)))
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
function createStorageCleaner(deps) {
|
|
168
|
+
const sizeBytes = deps.sizeBytes ?? walkBytes;
|
|
169
|
+
const rm = deps.rm ?? defaultRm;
|
|
170
|
+
let running = false;
|
|
171
|
+
const activity = () => deps.previewActivity?.() ?? { opInFlight: false, activeWorkspaces: [] };
|
|
172
|
+
function previewHasPidfile(ws) {
|
|
173
|
+
return !!deps.previewsBaseDir && fs.existsSync(path.join(deps.previewsBaseDir, ws, 'preview.pid'));
|
|
174
|
+
}
|
|
175
|
+
/** Why a preview slot of `ws` (or the shared tools cache, ws=null) must stay — null when it may go. */
|
|
176
|
+
function previewHold(ws) {
|
|
177
|
+
const a = activity();
|
|
178
|
+
if (a.opInFlight)
|
|
179
|
+
return 'a preview is starting or updating';
|
|
180
|
+
if (ws === null) {
|
|
181
|
+
if (a.activeWorkspaces.length > 0)
|
|
182
|
+
return 'a preview is running';
|
|
183
|
+
const anyPid = deps.previewsBaseDir ? childDirs(deps.previewsBaseDir).some((d) => previewHasPidfile(d)) : false;
|
|
184
|
+
return anyPid ? 'a preview may still be running' : null;
|
|
185
|
+
}
|
|
186
|
+
if (a.activeWorkspaces.includes(ws))
|
|
187
|
+
return 'this preview is running';
|
|
188
|
+
if (previewHasPidfile(ws))
|
|
189
|
+
return 'this preview may still be running';
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
async function checkoutIsClean(dir) {
|
|
193
|
+
try {
|
|
194
|
+
const d = await (0, checkoutInventory_1.inspectDirty)(dir, deps.run, deps.readText);
|
|
195
|
+
return d !== 'unparseable' && !d.dirty;
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false; // inconclusive is never clean
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async function ignoredBuildDirs(dir) {
|
|
202
|
+
let out;
|
|
203
|
+
try {
|
|
204
|
+
const r = await deps.run('git', ['-C', dir, 'ls-files', '--others', '--ignored', '--exclude-standard', '--directory', '-z']);
|
|
205
|
+
if (r.code !== 0)
|
|
206
|
+
return [];
|
|
207
|
+
out = r.stdout;
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
const root = path.resolve(dir);
|
|
213
|
+
const found = [];
|
|
214
|
+
for (const rel of out.split('\0')) {
|
|
215
|
+
if (!rel.endsWith('/'))
|
|
216
|
+
continue;
|
|
217
|
+
const clean = rel.replace(/\/+$/, '');
|
|
218
|
+
if (!exports.BUILD_OUTPUT_DIRS.has(path.basename(clean)))
|
|
219
|
+
continue;
|
|
220
|
+
if (clean.split('/').includes('.git'))
|
|
221
|
+
continue;
|
|
222
|
+
const abs = path.resolve(root, clean);
|
|
223
|
+
if (!abs.startsWith(root + path.sep))
|
|
224
|
+
continue;
|
|
225
|
+
if (isDir(abs))
|
|
226
|
+
found.push(abs);
|
|
227
|
+
}
|
|
228
|
+
return found;
|
|
229
|
+
}
|
|
230
|
+
async function plan(categories) {
|
|
231
|
+
const want = new Set(categories);
|
|
232
|
+
const targets = [];
|
|
233
|
+
if (want.has('caches')) {
|
|
234
|
+
for (const c of deps.cacheDirs)
|
|
235
|
+
if (isDir(c))
|
|
236
|
+
targets.push({ category: 'caches', path: c });
|
|
237
|
+
}
|
|
238
|
+
if (want.has('previews') && deps.previewsBaseDir) {
|
|
239
|
+
const base = deps.previewsBaseDir;
|
|
240
|
+
for (const name of childDirs(base)) {
|
|
241
|
+
if (name === '.tools') {
|
|
242
|
+
const uvCache = path.join(base, '.tools', 'uv-cache');
|
|
243
|
+
if (isDir(uvCache) && previewHold(null) === null)
|
|
244
|
+
targets.push({ category: 'previews', path: uvCache, workspaceId: null });
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (!(0, bundleInstaller_1.isSafeWorkspaceId)(name) || previewHold(name) !== null)
|
|
248
|
+
continue;
|
|
249
|
+
for (const slot of ['a', 'b']) {
|
|
250
|
+
const p = path.join(base, name, slot);
|
|
251
|
+
if (isDir(p))
|
|
252
|
+
targets.push({ category: 'previews', path: p, workspaceId: name });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (want.has('removedCheckouts') || want.has('buildOutput')) {
|
|
257
|
+
for (const ws of childDirs(deps.reposBaseDir)) {
|
|
258
|
+
if (ws.startsWith('.') || !(0, bundleInstaller_1.isSafeWorkspaceId)(ws) || deps.isAttached(ws))
|
|
259
|
+
continue;
|
|
260
|
+
const dir = path.join(deps.reposBaseDir, ws);
|
|
261
|
+
if (fs.existsSync(path.join(dir, '.git'))) {
|
|
262
|
+
if (!want.has('buildOutput') || !(await checkoutIsClean(dir)))
|
|
263
|
+
continue;
|
|
264
|
+
for (const p of await ignoredBuildDirs(dir))
|
|
265
|
+
targets.push({ category: 'buildOutput', path: p, workspaceId: ws, checkout: dir });
|
|
266
|
+
}
|
|
267
|
+
else if (want.has('removedCheckouts') && holdsOnlyRebuildable(dir)) {
|
|
268
|
+
targets.push({ category: 'removedCheckouts', path: dir, workspaceId: ws });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Never count (or delete) a path twice: drop any target inside another.
|
|
273
|
+
return targets.filter((t) => !targets.some((o) => o !== t && t.path.startsWith(o.path + path.sep)));
|
|
274
|
+
}
|
|
275
|
+
/** Re-derived right before the delete. null = still safe to remove. */
|
|
276
|
+
async function hold(t) {
|
|
277
|
+
switch (t.category) {
|
|
278
|
+
case 'caches':
|
|
279
|
+
return null;
|
|
280
|
+
case 'previews':
|
|
281
|
+
return previewHold(t.workspaceId);
|
|
282
|
+
case 'removedCheckouts':
|
|
283
|
+
if (deps.isAttached(t.workspaceId))
|
|
284
|
+
return 'a session attached';
|
|
285
|
+
if (fs.existsSync(path.join(t.path, '.git')))
|
|
286
|
+
return 'a checkout appeared';
|
|
287
|
+
return holdsOnlyRebuildable(t.path) ? null : 'files appeared';
|
|
288
|
+
case 'buildOutput':
|
|
289
|
+
if (deps.isAttached(t.workspaceId))
|
|
290
|
+
return 'a session attached';
|
|
291
|
+
return (await checkoutIsClean(t.checkout)) ? null : 'the checkout has unsaved work';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async function measure() {
|
|
295
|
+
const byCategory = zeros();
|
|
296
|
+
for (const t of await plan(exports.CLEANUP_CATEGORIES))
|
|
297
|
+
byCategory[t.category] += await sizeBytes(t.path).catch(() => 0);
|
|
298
|
+
return { reclaimableBytes: exports.CLEANUP_CATEGORIES.reduce((s, c) => s + byCategory[c], 0), byCategory };
|
|
299
|
+
}
|
|
300
|
+
async function clean(categories = exports.CLEANUP_CATEGORIES) {
|
|
301
|
+
if (running)
|
|
302
|
+
return { busy: true, freedBytes: 0, byCategory: zeros(), skipped: [] };
|
|
303
|
+
running = true;
|
|
304
|
+
try {
|
|
305
|
+
const byCategory = zeros();
|
|
306
|
+
const skipped = [];
|
|
307
|
+
for (const t of await plan(categories)) {
|
|
308
|
+
const bytes = await sizeBytes(t.path).catch(() => 0);
|
|
309
|
+
const why = await hold(t);
|
|
310
|
+
if (why) {
|
|
311
|
+
skipped.push({ category: t.category, path: t.path, reason: why });
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
await rm(t.path);
|
|
316
|
+
byCategory[t.category] += bytes;
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
skipped.push({ category: t.category, path: t.path, reason: err instanceof Error ? err.message : String(err) });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return { busy: false, freedBytes: exports.CLEANUP_CATEGORIES.reduce((s, c) => s + byCategory[c], 0), byCategory, skipped };
|
|
323
|
+
}
|
|
324
|
+
finally {
|
|
325
|
+
running = false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function runAutomaticCleanup() {
|
|
329
|
+
if (!deps.autoCleanupEnabled)
|
|
330
|
+
return { ran: false, reason: 'disabled' };
|
|
331
|
+
const r = await clean(['caches', 'previews']);
|
|
332
|
+
if (r.busy)
|
|
333
|
+
return { ran: false, reason: 'busy' };
|
|
334
|
+
return { ran: true, ...r };
|
|
335
|
+
}
|
|
336
|
+
return { measure, clean, runAutomaticCleanup };
|
|
337
|
+
}
|
|
@@ -7,6 +7,16 @@ function optString(v) {
|
|
|
7
7
|
function isFiniteNumber(v) {
|
|
8
8
|
return typeof v === 'number' && Number.isFinite(v);
|
|
9
9
|
}
|
|
10
|
+
/** stc-04 — all three numbers finite and positive, or no quote at all. */
|
|
11
|
+
function parseQuote(v) {
|
|
12
|
+
if (!v || typeof v !== 'object')
|
|
13
|
+
return null;
|
|
14
|
+
const q = v;
|
|
15
|
+
const ok = (n) => isFiniteNumber(n) && n > 0;
|
|
16
|
+
if (!ok(q.addGb) || !ok(q.newDiskGb) || !isFiniteNumber(q.newCreditsPerDay) || q.newCreditsPerDay < 0)
|
|
17
|
+
return null;
|
|
18
|
+
return { addGb: q.addGb, newDiskGb: q.newDiskGb, newCreditsPerDay: q.newCreditsPerDay };
|
|
19
|
+
}
|
|
10
20
|
async function fetchStorageQuotaFact(opts) {
|
|
11
21
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
12
22
|
if (!fetchImpl)
|
|
@@ -39,6 +49,7 @@ async function fetchStorageQuotaFact(opts) {
|
|
|
39
49
|
isFiniteNumber(err.capGb) &&
|
|
40
50
|
isFiniteNumber(err.usedGb) &&
|
|
41
51
|
isFiniteNumber(err.additionalGb)) {
|
|
52
|
+
const quote = parseQuote(err.quote);
|
|
42
53
|
return {
|
|
43
54
|
allowed: false,
|
|
44
55
|
tier: err.tier,
|
|
@@ -49,6 +60,7 @@ async function fetchStorageQuotaFact(opts) {
|
|
|
49
60
|
suggestedCapGb: isFiniteNumber(err.suggestedCapGb) ? err.suggestedCapGb : null,
|
|
50
61
|
tierName: optString(err.tierName),
|
|
51
62
|
suggestedTierName: optString(err.suggestedTierName),
|
|
63
|
+
...(quote ? { quote } : {}),
|
|
52
64
|
};
|
|
53
65
|
}
|
|
54
66
|
}
|
|
@@ -68,6 +68,18 @@ export interface StorageQuotaFact {
|
|
|
68
68
|
* Absent from an older backend — the raw id is the fallback. */
|
|
69
69
|
tierName?: string | null;
|
|
70
70
|
suggestedTierName?: string | null;
|
|
71
|
+
/** stc-04 — the backend's price for growing the disk to fit this clone
|
|
72
|
+
* (present only while storage billing is on and growth is possible).
|
|
73
|
+
* Carried to the pane untouched; the bridge never grows the disk itself —
|
|
74
|
+
* the grow route spends money, so the arena's Grow click makes that call
|
|
75
|
+
* with the user's own session. */
|
|
76
|
+
quote?: GrowQuote;
|
|
77
|
+
}
|
|
78
|
+
/** stc-04 — `POST /me/hive/storage/grow`'s price, quoted by the clone check. */
|
|
79
|
+
export interface GrowQuote {
|
|
80
|
+
addGb: number;
|
|
81
|
+
newDiskGb: number;
|
|
82
|
+
newCreditsPerDay: number;
|
|
71
83
|
}
|
|
72
84
|
/** "about 4.0 GB", or "under 0.1 GB" for a positive sliver — never a raw
|
|
73
85
|
* float, never "about 0.0 GB" for something real. */
|
|
@@ -2,6 +2,16 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { CompanionForwarder } from '../bridge/companionForwarder';
|
|
3
3
|
export declare const DEFAULT_BRIDGE_PORT = 8377;
|
|
4
4
|
export declare function bridgeApiUrl(): string;
|
|
5
|
+
/**
|
|
6
|
+
* stc-06 — free and total GB of the volume holding `p` (the SAME statfs as
|
|
7
|
+
* readReposVolumeFreeMb), for the disk-pressure report on the `sessions`
|
|
8
|
+
* frame. null = unknown (no statfsSync, read failure): the field is omitted
|
|
9
|
+
* and the backend never acts on a guess.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readVolumeSpaceGb(p: string): {
|
|
12
|
+
freeGb: number;
|
|
13
|
+
totalGb: number;
|
|
14
|
+
} | null;
|
|
5
15
|
export type RelayCliOpts = {
|
|
6
16
|
relay?: string;
|
|
7
17
|
relayToken?: string;
|
package/dist/commands/bridge.js
CHANGED
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.bridgeCommand = exports.DEFAULT_BRIDGE_PORT = void 0;
|
|
40
40
|
exports.bridgeApiUrl = bridgeApiUrl;
|
|
41
|
+
exports.readVolumeSpaceGb = readVolumeSpaceGb;
|
|
41
42
|
exports.resolveRelayMode = resolveRelayMode;
|
|
42
43
|
exports.buildBrowserDriverOptions = buildBrowserDriverOptions;
|
|
43
44
|
exports.buildCompanionForwarderEnvOptions = buildCompanionForwarderEnvOptions;
|
|
@@ -63,6 +64,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
63
64
|
const sessionManager_1 = require("../bridge/sessionManager");
|
|
64
65
|
const server_1 = require("../bridge/server");
|
|
65
66
|
const relayClient_1 = require("../bridge/relayClient");
|
|
67
|
+
const gitTokenBroker_1 = require("../bridge/gitTokenBroker");
|
|
66
68
|
const browserDriver_1 = require("../bridge/browserDriver");
|
|
67
69
|
const browserHost_1 = require("../bridge/browserHost");
|
|
68
70
|
const browserIdle_1 = require("../bridge/browserIdle");
|
|
@@ -124,6 +126,7 @@ const companionForwarder_1 = require("../bridge/companionForwarder");
|
|
|
124
126
|
const previewForwarder_1 = require("../bridge/previewForwarder");
|
|
125
127
|
// tp-05 — the Team preview checkout runner (hosted lane only, ADR-0014).
|
|
126
128
|
const previewCheckout_1 = require("../bridge/previewCheckout");
|
|
129
|
+
const storageCleanup_1 = require("../bridge/storageCleanup");
|
|
127
130
|
/** lws-12 — real fs binding for writeGitignore.ts: mirrors realLocalContextWriterDeps
|
|
128
131
|
* above, but a `.gitignore` may already hold user content, so this is a plain
|
|
129
132
|
* read + overwrite (append), never atomic-rename — there's no concurrent
|
|
@@ -320,6 +323,28 @@ function readReposVolumeUsedGb(p) {
|
|
|
320
323
|
return null;
|
|
321
324
|
}
|
|
322
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* stc-06 — free and total GB of the volume holding `p` (the SAME statfs as
|
|
328
|
+
* readReposVolumeFreeMb), for the disk-pressure report on the `sessions`
|
|
329
|
+
* frame. null = unknown (no statfsSync, read failure): the field is omitted
|
|
330
|
+
* and the backend never acts on a guess.
|
|
331
|
+
*/
|
|
332
|
+
function readVolumeSpaceGb(p) {
|
|
333
|
+
try {
|
|
334
|
+
const statfsSync = fs.statfsSync;
|
|
335
|
+
if (!statfsSync)
|
|
336
|
+
return null;
|
|
337
|
+
const stats = statfsSync(p);
|
|
338
|
+
const GB = 1024 * 1024 * 1024;
|
|
339
|
+
const totalGb = (stats.blocks * stats.bsize) / GB;
|
|
340
|
+
if (!(totalGb > 0))
|
|
341
|
+
return null;
|
|
342
|
+
return { freeGb: (stats.bavail * stats.bsize) / GB, totalGb };
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
323
348
|
/**
|
|
324
349
|
* hcs-05/hcs-07 — recursive+force removal, shared by checkoutReclaimDeps.rm
|
|
325
350
|
* (removing a RECLAIM-verdict checkout) and resolveWorkspaceCwd's cleanup of
|
|
@@ -650,17 +675,28 @@ exports.bridgeCommand = new commander_1.Command('bridge')
|
|
|
650
675
|
// .env, served on 127.0.0.1:<preview port> (the port the forwarder
|
|
651
676
|
// above targets). Container branch only; the local bridge's relay
|
|
652
677
|
// client answers every preview frame `unsupported / local_lane`.
|
|
678
|
+
const previewsBaseDir = process.env.WENRWA_PREVIEWS_DIR || path.join(path.dirname(reposBaseDir), 'previews');
|
|
653
679
|
const previewRunner = new previewCheckout_1.PreviewRunner({
|
|
654
|
-
previewsBaseDir
|
|
680
|
+
previewsBaseDir,
|
|
655
681
|
workingCopyDir: (workspaceId) => path.join(reposBaseDir, workspaceId),
|
|
656
682
|
port: (0, previewForwarder_1.resolvePreviewPorts)(process.env).previewPort,
|
|
657
683
|
});
|
|
658
684
|
// cvt-07: the box's one shared Chromium, created lazily by the first tab.
|
|
659
685
|
let browserHost;
|
|
660
|
-
|
|
686
|
+
// a9be9720 — agents' git credentials stay fresh past the hour the
|
|
687
|
+
// injected one lives: git-credential-wenrwa asks this socket, the broker
|
|
688
|
+
// asks the relay. Hosted lane only — a laptop pushes with its own git.
|
|
689
|
+
const gitTokenSocket = process.env.WENRWA_GIT_TOKEN_SOCKET || path.join(os.homedir(), '.wenrwa', 'git-token.sock');
|
|
690
|
+
let relayClient = null;
|
|
691
|
+
const gitTokenBroker = new gitTokenBroker_1.GitTokenBroker((workspaceId) => relayClient
|
|
692
|
+
? relayClient.requestGitToken(workspaceId)
|
|
693
|
+
: Promise.reject(Object.assign(new Error('The bridge is starting'), { code: 'OFFLINE' })));
|
|
694
|
+
(0, gitTokenBroker_1.startGitTokenSocket)({ path: gitTokenSocket, broker: gitTokenBroker }).catch((err) => console.error(`[bridge] git token socket failed to start — agents fall back to the token from open: ${err instanceof Error ? err.message : String(err)}`));
|
|
695
|
+
relayClient = (0, relayClient_1.startRelayClient)({
|
|
661
696
|
relayUrl: relayMode.relayUrl,
|
|
662
697
|
relayToken: relayMode.relayToken,
|
|
663
698
|
sessions,
|
|
699
|
+
gitTokenSocket,
|
|
664
700
|
// Rock-solid sleep/wake: this is the HOSTED container bridge, the only
|
|
665
701
|
// process that knows which agent PTYs are actually running. The backend
|
|
666
702
|
// reconciles claude_code_agents.lifecycle_state against this report —
|
|
@@ -743,12 +779,32 @@ exports.bridgeCommand = new commander_1.Command('bridge')
|
|
|
743
779
|
exists: (p) => fs.existsSync(p),
|
|
744
780
|
run: execRunner,
|
|
745
781
|
},
|
|
782
|
+
// stc-05 — Clean up: measure + delete only rebuildable data (npm/uv/
|
|
783
|
+
// pip caches in the home dir and on the volume, preview slots not in
|
|
784
|
+
// use, removed-checkout leftovers, build output in clean repos). The
|
|
785
|
+
// button always works; AUTOMATIC cleanup (stc-06, before a disk
|
|
786
|
+
// grows) stays off unless HIVEOP_AUTO_CLEANUP=1 — the ADR-0008 kill
|
|
787
|
+
// switch. HOSTED LANE ONLY, like enableReclaim.
|
|
788
|
+
enableStorageCleanup: true,
|
|
789
|
+
storageCleanupDeps: {
|
|
790
|
+
previewsBaseDir,
|
|
791
|
+
cacheDirs: (0, storageCleanup_1.defaultCacheDirs)([os.homedir(), process.env.HOME, path.dirname(reposBaseDir)]),
|
|
792
|
+
previewActivity: () => previewRunner.activity(),
|
|
793
|
+
run: execRunner,
|
|
794
|
+
sizeBytes: duSizeBytes,
|
|
795
|
+
rm: rmCheckoutDir,
|
|
796
|
+
autoCleanupEnabled: process.env.HIVEOP_AUTO_CLEANUP === '1',
|
|
797
|
+
},
|
|
746
798
|
// Rock-solid sleep/wake — refuse a PTY spawn the box can't hold,
|
|
747
799
|
// rather than OOM-killing it behind an indefinite "Waking the
|
|
748
800
|
// machine…" (production 2026-08-14: MemAvailable: 0 kB). Linux-only
|
|
749
801
|
// by nature; a missing /proc/meminfo parses to null, which is UNKNOWN
|
|
750
802
|
// and spawns exactly as before.
|
|
751
803
|
readMemInfo: readContainerMemInfo,
|
|
804
|
+
// stc-06 — free space on the checkouts' volume rides every `sessions`
|
|
805
|
+
// frame; the backend grows a nearly-full disk while agents work
|
|
806
|
+
// (cleanup first, kill switch above). HOSTED LANE ONLY.
|
|
807
|
+
readDiskSpace: () => readVolumeSpaceGb(reposBaseDir),
|
|
752
808
|
checkLoginValid: () => (0, loginStatus_1.checkClaudeLoginValid)({
|
|
753
809
|
claudeDir,
|
|
754
810
|
exists: (p) => fs.existsSync(p),
|
|
@@ -776,7 +832,7 @@ exports.bridgeCommand = new commander_1.Command('bridge')
|
|
|
776
832
|
// failure here gets. `result.code` is threaded through so the
|
|
777
833
|
// frame the pane receives carries the RIGHT code — free-space
|
|
778
834
|
// and quota refusals must never collapse onto the same one.
|
|
779
|
-
throw new diskPreflight_1.ClonePreflightRefusalError(result.message, result.code);
|
|
835
|
+
throw new diskPreflight_1.ClonePreflightRefusalError(result.message, result.code, result.quote);
|
|
780
836
|
}
|
|
781
837
|
// Playtest 2026-10-01 — Claude Code opens a fresh checkout on its
|
|
782
838
|
// folder-trust dialog (default "No, exit") even in bypass mode. The
|
package/dist/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenrwa/marketplace-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.13",
|
|
4
4
|
"description": "CLI tool for the Wenrwa Agent Marketplace — monitor bounties, agents, workspaces, and treasury from your terminal",
|
|
5
5
|
"author": "Wenrwa <dev@wenrwa.com>",
|
|
6
6
|
"license": "MIT",
|