superbee 0.1.4-pre.1 → 0.1.5-pre.1
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 +22 -14
- package/dist/publication-bridge.mjs +89 -32
- package/dist/publication.mjs +123 -37
- package/dist/superbee.mjs +985 -370
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -40,24 +40,32 @@ know invisible to the humans they work for. Superbee fixes all three:
|
|
|
40
40
|
The npm package ships one self-contained executable with zero runtime dependencies, plus an
|
|
41
41
|
Agent Skill that teaches agents how to use it.
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## How do I download Superbee on Windows?
|
|
44
44
|
|
|
45
|
-
**Requirements: Node.js 20 or newer on macOS, Linux, or Windows.**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
the globally installed `superbee.cmd` entrypoint are covered by the required CI contract.
|
|
45
|
+
**Requirements: Node.js 20 or newer on macOS, Linux, or native Windows.** You do not need WSL,
|
|
46
|
+
Ubuntu, or Docker. On Windows, Superbee keeps per-user operational state under
|
|
47
|
+
`%LOCALAPPDATA%\Superbee`; npm installs the command as `superbee.cmd`.
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
Superbee currently has two npm release channels:
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
- `latest` is the stable channel selected by bare `superbee`. Its current release predates native
|
|
52
|
+
Windows support.
|
|
53
|
+
- `next` is the prerelease channel. It contains the current native-Windows build.
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
Install the current prerelease on Windows:
|
|
56
|
+
|
|
57
|
+
```powershell
|
|
58
|
+
npm install -g superbee@next
|
|
59
|
+
superbee.cmd setup
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
On macOS or Linux, the stable channel remains available with `npm install -g superbee`. To test the
|
|
63
|
+
same prerelease as Windows, install `superbee@next` instead.
|
|
64
|
+
|
|
65
|
+
After installation, ask your AI agent to run `superbee setup` (`superbee.cmd setup` when invoking
|
|
66
|
+
the Windows shim explicitly). Setup walks the agent through Agent Skill, SessionStart hook, and MCP
|
|
67
|
+
registration. It is read-only: it inspects your configuration and returns one safe next command at
|
|
68
|
+
a time, so the agent performs any actual changes with your approval.
|
|
61
69
|
|
|
62
70
|
Upgrading from the legacy `@holaxis/aslite` package or the retired marketplace plugin? Install
|
|
63
71
|
`superbee` alongside it, have your agent run `superbee setup` to migrate the exact legacy
|
|
@@ -3947,46 +3947,69 @@ async function selectLockRoot(options2) {
|
|
|
3947
3947
|
await ensurePrivateLockRoot(lockRoot);
|
|
3948
3948
|
return lockRoot;
|
|
3949
3949
|
}
|
|
3950
|
+
var WINDOWS_CLAIM_CONTENTION_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
|
|
3951
|
+
async function classifyLockClaimFailure(error, lockPath) {
|
|
3952
|
+
const code = error.code;
|
|
3953
|
+
if (code === "EEXIST") return "contention";
|
|
3954
|
+
if (process.platform !== "win32" || !WINDOWS_CLAIM_CONTENTION_CODES.has(code ?? "")) return "terminal";
|
|
3955
|
+
try {
|
|
3956
|
+
await fs.lstat(lockPath);
|
|
3957
|
+
return "contention";
|
|
3958
|
+
} catch (probeError) {
|
|
3959
|
+
const probeCode = probeError.code;
|
|
3960
|
+
return probeCode === "ENOENT" || probeCode === "ENOTDIR" ? "unwitnessed-windows-sharing-error" : "terminal";
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3950
3963
|
async function claimLockPath(lockPath, owner, waitMs, pollMs) {
|
|
3951
3964
|
const started = owner.created_at_ms;
|
|
3965
|
+
let unwitnessedWindowsRetryUsed = false;
|
|
3952
3966
|
while (true) {
|
|
3953
3967
|
try {
|
|
3954
3968
|
await fs.mkdir(lockPath, { mode: 448 });
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
}
|
|
3963
|
-
|
|
3964
|
-
});
|
|
3965
|
-
throw err;
|
|
3969
|
+
} catch (err) {
|
|
3970
|
+
const failure = await classifyLockClaimFailure(err, lockPath);
|
|
3971
|
+
if (failure === "terminal") throw err;
|
|
3972
|
+
if (failure === "unwitnessed-windows-sharing-error") {
|
|
3973
|
+
if (unwitnessedWindowsRetryUsed) throw err;
|
|
3974
|
+
unwitnessedWindowsRetryUsed = true;
|
|
3975
|
+
continue;
|
|
3976
|
+
} else {
|
|
3977
|
+
unwitnessedWindowsRetryUsed = false;
|
|
3966
3978
|
}
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3979
|
-
throw new FilesystemMutationLockError(
|
|
3980
|
-
`mutation completed but filesystem lock '${lockPath}' could not be removed (${message}); inspect the lock before retrying.`,
|
|
3981
|
-
{ lockPath, owner: current, stale: false, malformed: false }
|
|
3982
|
-
);
|
|
3983
|
-
}
|
|
3984
|
-
};
|
|
3979
|
+
if (Date.now() - started >= waitMs) throw timeoutError(lockPath, await readOwner(lockPath), owner.target);
|
|
3980
|
+
await delay(pollMs);
|
|
3981
|
+
continue;
|
|
3982
|
+
}
|
|
3983
|
+
try {
|
|
3984
|
+
await fs.writeFile(path.join(lockPath, OWNER_FILE), `${JSON.stringify(owner)}
|
|
3985
|
+
`, {
|
|
3986
|
+
encoding: "utf8",
|
|
3987
|
+
flag: "wx",
|
|
3988
|
+
mode: 384
|
|
3989
|
+
});
|
|
3985
3990
|
} catch (err) {
|
|
3986
|
-
|
|
3991
|
+
await fs.rm(lockPath, { recursive: true, force: true }).catch(() => {
|
|
3992
|
+
});
|
|
3993
|
+
throw err;
|
|
3987
3994
|
}
|
|
3988
|
-
|
|
3989
|
-
|
|
3995
|
+
return async () => {
|
|
3996
|
+
const current = await readOwner(lockPath);
|
|
3997
|
+
if (current?.token !== owner.token) {
|
|
3998
|
+
throw new FilesystemMutationLockError(
|
|
3999
|
+
`refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`,
|
|
4000
|
+
{ lockPath, owner: current, stale: false, malformed: current === null }
|
|
4001
|
+
);
|
|
4002
|
+
}
|
|
4003
|
+
try {
|
|
4004
|
+
await fs.rm(lockPath, { recursive: true, force: false });
|
|
4005
|
+
} catch (err) {
|
|
4006
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4007
|
+
throw new FilesystemMutationLockError(
|
|
4008
|
+
`mutation completed but filesystem lock '${lockPath}' could not be removed (${message}); inspect the lock before retrying.`,
|
|
4009
|
+
{ lockPath, owner: current, stale: false, malformed: false }
|
|
4010
|
+
);
|
|
4011
|
+
}
|
|
4012
|
+
};
|
|
3990
4013
|
}
|
|
3991
4014
|
}
|
|
3992
4015
|
function newOwner(target) {
|
|
@@ -5038,6 +5061,10 @@ var VALID_FIELDS_KEYS = /* @__PURE__ */ new Set([
|
|
|
5038
5061
|
"descriptions"
|
|
5039
5062
|
]);
|
|
5040
5063
|
var MISPLACED_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["enum", "enums", "values", "constraints"]);
|
|
5064
|
+
var CLAIM_COORDINATE_KEYS = [
|
|
5065
|
+
["owner_field", "ownerField"],
|
|
5066
|
+
["state_field", "stateField"]
|
|
5067
|
+
];
|
|
5041
5068
|
function parseConventionDoc(doc) {
|
|
5042
5069
|
const fm = doc.frontmatter;
|
|
5043
5070
|
const governs = typeof fm.governs === "string" ? fm.governs.trim() : "";
|
|
@@ -5346,6 +5373,35 @@ function parseConventionDoc(doc) {
|
|
|
5346
5373
|
if (Object.keys(parsed).length > 0) expectsInbound = parsed;
|
|
5347
5374
|
}
|
|
5348
5375
|
}
|
|
5376
|
+
const claimSource = fm.claim;
|
|
5377
|
+
let claim;
|
|
5378
|
+
if (claimSource !== void 0) {
|
|
5379
|
+
if (!isPlainObject(claimSource)) {
|
|
5380
|
+
warnings.push({
|
|
5381
|
+
code: "KIND_CONVENTION_BAD_SHAPE",
|
|
5382
|
+
message: `kind convention '${doc.id}' has a non-map 'claim' key (${describeShape(claimSource)}; expected a map declaring 'owner_field' and/or 'state_field'); ignoring it.`,
|
|
5383
|
+
field: "claim",
|
|
5384
|
+
severity: "warning"
|
|
5385
|
+
});
|
|
5386
|
+
} else {
|
|
5387
|
+
const parsed = {};
|
|
5388
|
+
for (const [key, target] of CLAIM_COORDINATE_KEYS) {
|
|
5389
|
+
const declared = claimSource[key];
|
|
5390
|
+
if (declared === void 0) continue;
|
|
5391
|
+
if (!isScalar(declared) || String(declared).trim() === "") {
|
|
5392
|
+
warnings.push({
|
|
5393
|
+
code: "KIND_CONVENTION_BAD_MEMBER",
|
|
5394
|
+
message: `kind convention '${doc.id}' has a malformed 'claim.${key}' (${describeShape(declared)}; expected a declared field name); skipping it.`,
|
|
5395
|
+
field: `claim.${key}`,
|
|
5396
|
+
severity: "warning"
|
|
5397
|
+
});
|
|
5398
|
+
continue;
|
|
5399
|
+
}
|
|
5400
|
+
parsed[target] = String(declared).trim();
|
|
5401
|
+
}
|
|
5402
|
+
if (parsed.ownerField !== void 0 || parsed.stateField !== void 0) claim = parsed;
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5349
5405
|
const sections = Array.isArray(fm.sections) ? fm.sections.filter((s) => typeof s === "string" && s.trim() !== "") : void 0;
|
|
5350
5406
|
const title = typeof fm.title === "string" && fm.title.trim() !== "" ? fm.title.trim() : governs;
|
|
5351
5407
|
let description;
|
|
@@ -5378,6 +5434,7 @@ function parseConventionDoc(doc) {
|
|
|
5378
5434
|
if (sections && sections.length > 0) kind.sections = sections;
|
|
5379
5435
|
if (freshnessHorizon !== void 0) kind.freshnessHorizon = freshnessHorizon;
|
|
5380
5436
|
if (browseCollapsed !== void 0) kind.browseCollapsed = browseCollapsed;
|
|
5437
|
+
if (claim !== void 0) kind.claim = claim;
|
|
5381
5438
|
return {
|
|
5382
5439
|
ok: true,
|
|
5383
5440
|
kind,
|
package/dist/publication.mjs
CHANGED
|
@@ -31901,46 +31901,69 @@ async function selectLockRoot(options2) {
|
|
|
31901
31901
|
await ensurePrivateLockRoot(lockRoot);
|
|
31902
31902
|
return lockRoot;
|
|
31903
31903
|
}
|
|
31904
|
+
var WINDOWS_CLAIM_CONTENTION_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
|
|
31905
|
+
async function classifyLockClaimFailure(error, lockPath) {
|
|
31906
|
+
const code2 = error.code;
|
|
31907
|
+
if (code2 === "EEXIST") return "contention";
|
|
31908
|
+
if (process.platform !== "win32" || !WINDOWS_CLAIM_CONTENTION_CODES.has(code2 ?? "")) return "terminal";
|
|
31909
|
+
try {
|
|
31910
|
+
await fs.lstat(lockPath);
|
|
31911
|
+
return "contention";
|
|
31912
|
+
} catch (probeError) {
|
|
31913
|
+
const probeCode = probeError.code;
|
|
31914
|
+
return probeCode === "ENOENT" || probeCode === "ENOTDIR" ? "unwitnessed-windows-sharing-error" : "terminal";
|
|
31915
|
+
}
|
|
31916
|
+
}
|
|
31904
31917
|
async function claimLockPath(lockPath, owner, waitMs, pollMs) {
|
|
31905
31918
|
const started = owner.created_at_ms;
|
|
31919
|
+
let unwitnessedWindowsRetryUsed = false;
|
|
31906
31920
|
while (true) {
|
|
31907
31921
|
try {
|
|
31908
31922
|
await fs.mkdir(lockPath, { mode: 448 });
|
|
31909
|
-
|
|
31910
|
-
|
|
31923
|
+
} catch (err) {
|
|
31924
|
+
const failure = await classifyLockClaimFailure(err, lockPath);
|
|
31925
|
+
if (failure === "terminal") throw err;
|
|
31926
|
+
if (failure === "unwitnessed-windows-sharing-error") {
|
|
31927
|
+
if (unwitnessedWindowsRetryUsed) throw err;
|
|
31928
|
+
unwitnessedWindowsRetryUsed = true;
|
|
31929
|
+
continue;
|
|
31930
|
+
} else {
|
|
31931
|
+
unwitnessedWindowsRetryUsed = false;
|
|
31932
|
+
}
|
|
31933
|
+
if (Date.now() - started >= waitMs) throw timeoutError(lockPath, await readOwner(lockPath), owner.target);
|
|
31934
|
+
await delay(pollMs);
|
|
31935
|
+
continue;
|
|
31936
|
+
}
|
|
31937
|
+
try {
|
|
31938
|
+
await fs.writeFile(path.join(lockPath, OWNER_FILE), `${JSON.stringify(owner)}
|
|
31911
31939
|
`, {
|
|
31912
|
-
|
|
31913
|
-
|
|
31914
|
-
|
|
31915
|
-
|
|
31916
|
-
} catch (err) {
|
|
31917
|
-
await fs.rm(lockPath, { recursive: true, force: true }).catch(() => {
|
|
31918
|
-
});
|
|
31919
|
-
throw err;
|
|
31920
|
-
}
|
|
31921
|
-
return async () => {
|
|
31922
|
-
const current = await readOwner(lockPath);
|
|
31923
|
-
if (current?.token !== owner.token) {
|
|
31924
|
-
throw new FilesystemMutationLockError(
|
|
31925
|
-
`refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`,
|
|
31926
|
-
{ lockPath, owner: current, stale: false, malformed: current === null }
|
|
31927
|
-
);
|
|
31928
|
-
}
|
|
31929
|
-
try {
|
|
31930
|
-
await fs.rm(lockPath, { recursive: true, force: false });
|
|
31931
|
-
} catch (err) {
|
|
31932
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
31933
|
-
throw new FilesystemMutationLockError(
|
|
31934
|
-
`mutation completed but filesystem lock '${lockPath}' could not be removed (${message}); inspect the lock before retrying.`,
|
|
31935
|
-
{ lockPath, owner: current, stale: false, malformed: false }
|
|
31936
|
-
);
|
|
31937
|
-
}
|
|
31938
|
-
};
|
|
31940
|
+
encoding: "utf8",
|
|
31941
|
+
flag: "wx",
|
|
31942
|
+
mode: 384
|
|
31943
|
+
});
|
|
31939
31944
|
} catch (err) {
|
|
31940
|
-
|
|
31945
|
+
await fs.rm(lockPath, { recursive: true, force: true }).catch(() => {
|
|
31946
|
+
});
|
|
31947
|
+
throw err;
|
|
31941
31948
|
}
|
|
31942
|
-
|
|
31943
|
-
|
|
31949
|
+
return async () => {
|
|
31950
|
+
const current = await readOwner(lockPath);
|
|
31951
|
+
if (current?.token !== owner.token) {
|
|
31952
|
+
throw new FilesystemMutationLockError(
|
|
31953
|
+
`refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`,
|
|
31954
|
+
{ lockPath, owner: current, stale: false, malformed: current === null }
|
|
31955
|
+
);
|
|
31956
|
+
}
|
|
31957
|
+
try {
|
|
31958
|
+
await fs.rm(lockPath, { recursive: true, force: false });
|
|
31959
|
+
} catch (err) {
|
|
31960
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
31961
|
+
throw new FilesystemMutationLockError(
|
|
31962
|
+
`mutation completed but filesystem lock '${lockPath}' could not be removed (${message}); inspect the lock before retrying.`,
|
|
31963
|
+
{ lockPath, owner: current, stale: false, malformed: false }
|
|
31964
|
+
);
|
|
31965
|
+
}
|
|
31966
|
+
};
|
|
31944
31967
|
}
|
|
31945
31968
|
}
|
|
31946
31969
|
function newOwner(target) {
|
|
@@ -33027,6 +33050,10 @@ var VALID_FIELDS_KEYS = /* @__PURE__ */ new Set([
|
|
|
33027
33050
|
"descriptions"
|
|
33028
33051
|
]);
|
|
33029
33052
|
var MISPLACED_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["enum", "enums", "values", "constraints"]);
|
|
33053
|
+
var CLAIM_COORDINATE_KEYS = [
|
|
33054
|
+
["owner_field", "ownerField"],
|
|
33055
|
+
["state_field", "stateField"]
|
|
33056
|
+
];
|
|
33030
33057
|
function parseConventionDoc(doc) {
|
|
33031
33058
|
const fm = doc.frontmatter;
|
|
33032
33059
|
const governs = typeof fm.governs === "string" ? fm.governs.trim() : "";
|
|
@@ -33335,6 +33362,35 @@ function parseConventionDoc(doc) {
|
|
|
33335
33362
|
if (Object.keys(parsed).length > 0) expectsInbound = parsed;
|
|
33336
33363
|
}
|
|
33337
33364
|
}
|
|
33365
|
+
const claimSource = fm.claim;
|
|
33366
|
+
let claim;
|
|
33367
|
+
if (claimSource !== void 0) {
|
|
33368
|
+
if (!isPlainObject(claimSource)) {
|
|
33369
|
+
warnings.push({
|
|
33370
|
+
code: "KIND_CONVENTION_BAD_SHAPE",
|
|
33371
|
+
message: `kind convention '${doc.id}' has a non-map 'claim' key (${describeShape(claimSource)}; expected a map declaring 'owner_field' and/or 'state_field'); ignoring it.`,
|
|
33372
|
+
field: "claim",
|
|
33373
|
+
severity: "warning"
|
|
33374
|
+
});
|
|
33375
|
+
} else {
|
|
33376
|
+
const parsed = {};
|
|
33377
|
+
for (const [key, target] of CLAIM_COORDINATE_KEYS) {
|
|
33378
|
+
const declared = claimSource[key];
|
|
33379
|
+
if (declared === void 0) continue;
|
|
33380
|
+
if (!isScalar(declared) || String(declared).trim() === "") {
|
|
33381
|
+
warnings.push({
|
|
33382
|
+
code: "KIND_CONVENTION_BAD_MEMBER",
|
|
33383
|
+
message: `kind convention '${doc.id}' has a malformed 'claim.${key}' (${describeShape(declared)}; expected a declared field name); skipping it.`,
|
|
33384
|
+
field: `claim.${key}`,
|
|
33385
|
+
severity: "warning"
|
|
33386
|
+
});
|
|
33387
|
+
continue;
|
|
33388
|
+
}
|
|
33389
|
+
parsed[target] = String(declared).trim();
|
|
33390
|
+
}
|
|
33391
|
+
if (parsed.ownerField !== void 0 || parsed.stateField !== void 0) claim = parsed;
|
|
33392
|
+
}
|
|
33393
|
+
}
|
|
33338
33394
|
const sections = Array.isArray(fm.sections) ? fm.sections.filter((s) => typeof s === "string" && s.trim() !== "") : void 0;
|
|
33339
33395
|
const title = typeof fm.title === "string" && fm.title.trim() !== "" ? fm.title.trim() : governs;
|
|
33340
33396
|
let description;
|
|
@@ -33367,6 +33423,7 @@ function parseConventionDoc(doc) {
|
|
|
33367
33423
|
if (sections && sections.length > 0) kind.sections = sections;
|
|
33368
33424
|
if (freshnessHorizon !== void 0) kind.freshnessHorizon = freshnessHorizon;
|
|
33369
33425
|
if (browseCollapsed !== void 0) kind.browseCollapsed = browseCollapsed;
|
|
33426
|
+
if (claim !== void 0) kind.claim = claim;
|
|
33370
33427
|
return {
|
|
33371
33428
|
ok: true,
|
|
33372
33429
|
kind,
|
|
@@ -43191,18 +43248,37 @@ async function authorizePublicationRoot(requested) {
|
|
|
43191
43248
|
}
|
|
43192
43249
|
async function assertPublicationRoot(identity) {
|
|
43193
43250
|
let entry;
|
|
43251
|
+
try {
|
|
43252
|
+
entry = await lstat(identity.requested);
|
|
43253
|
+
} catch (error) {
|
|
43254
|
+
const code2 = error.code;
|
|
43255
|
+
if (code2 === "ENOENT" || code2 === "ENOTDIR") {
|
|
43256
|
+
throw new PublicationError("SOURCE_CHANGED", "the publication source identity became unavailable during capture", { retryable: true, cause: error });
|
|
43257
|
+
}
|
|
43258
|
+
throw new PublicationError("IO_ERROR", "the publication source identity could not be read during capture", { cause: error });
|
|
43259
|
+
}
|
|
43260
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) {
|
|
43261
|
+
throw new PublicationError("SOURCE_CHANGED", "the publication source root changed during capture", {
|
|
43262
|
+
retryable: true,
|
|
43263
|
+
expected: { canonical: identity.canonical, dev: identity.dev, ino: identity.ino },
|
|
43264
|
+
actual: { symlink: entry.isSymbolicLink(), directory: entry.isDirectory() }
|
|
43265
|
+
});
|
|
43266
|
+
}
|
|
43194
43267
|
let canonical2;
|
|
43195
43268
|
let current;
|
|
43196
43269
|
try {
|
|
43197
|
-
[
|
|
43198
|
-
lstat(identity.requested),
|
|
43270
|
+
[canonical2, current] = await Promise.all([
|
|
43199
43271
|
realpath(identity.requested),
|
|
43200
43272
|
stat(identity.requested)
|
|
43201
43273
|
]);
|
|
43202
43274
|
} catch (error) {
|
|
43203
|
-
|
|
43275
|
+
const code2 = error.code;
|
|
43276
|
+
if (code2 === "ENOENT" || code2 === "ENOTDIR") {
|
|
43277
|
+
throw new PublicationError("SOURCE_CHANGED", "the publication source identity became unavailable during capture", { retryable: true, cause: error });
|
|
43278
|
+
}
|
|
43279
|
+
throw new PublicationError("IO_ERROR", "the publication source identity could not be read during capture", { cause: error });
|
|
43204
43280
|
}
|
|
43205
|
-
if (
|
|
43281
|
+
if (!current.isDirectory() || canonical2 !== identity.canonical || current.dev !== identity.dev || current.ino !== identity.ino) {
|
|
43206
43282
|
throw new PublicationError("SOURCE_CHANGED", "the publication source root changed during capture", {
|
|
43207
43283
|
retryable: true,
|
|
43208
43284
|
expected: { canonical: identity.canonical, dev: identity.dev, ino: identity.ino },
|
|
@@ -43378,6 +43454,16 @@ function mapCaptureError(error) {
|
|
|
43378
43454
|
}
|
|
43379
43455
|
return new PublicationError("INVALID_BUNDLE", error instanceof Error ? error.message : "the bundle is invalid", { cause: error });
|
|
43380
43456
|
}
|
|
43457
|
+
async function classifyCaptureError(error, rootIdentity) {
|
|
43458
|
+
const mapped = mapCaptureError(error);
|
|
43459
|
+
if (mapped.code !== "IO_ERROR") return mapped;
|
|
43460
|
+
try {
|
|
43461
|
+
await assertPublicationRoot(rootIdentity);
|
|
43462
|
+
} catch (rootError) {
|
|
43463
|
+
if (rootError instanceof PublicationError && rootError.code === "SOURCE_CHANGED") return rootError;
|
|
43464
|
+
}
|
|
43465
|
+
return mapped;
|
|
43466
|
+
}
|
|
43381
43467
|
function addObject(objects, bytes, mediaType, representation) {
|
|
43382
43468
|
const digest2 = sha256(bytes);
|
|
43383
43469
|
if (!objects.has(digest2)) objects.set(digest2, bytes.slice());
|
|
@@ -43577,7 +43663,7 @@ async function capturePublicationSnapshot(options2) {
|
|
|
43577
43663
|
}
|
|
43578
43664
|
return handle;
|
|
43579
43665
|
} catch (error) {
|
|
43580
|
-
lastError =
|
|
43666
|
+
lastError = await classifyCaptureError(error, rootIdentity);
|
|
43581
43667
|
if (!lastError.retryable || attempt === maxAttempts) throw lastError;
|
|
43582
43668
|
}
|
|
43583
43669
|
}
|