agentwheel 0.20.18 → 0.20.19
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 +6 -0
- package/dist/{apply-ZGCJLZMZ.js → apply-QJHTL7IK.js} +3 -3
- package/dist/{chunk-FP26KSBM.js → chunk-BHT57MK5.js} +3 -3
- package/dist/{chunk-GE7F76AC.js → chunk-BNIF3FUR.js} +9 -0
- package/dist/{chunk-A44XYLUT.js → chunk-L37KQVRS.js} +2 -2
- package/dist/{chunk-U2GRG4UI.js → chunk-QABA4WUP.js} +1 -1
- package/dist/{identify-O54QXPQR.js → identify-M6IA4RS7.js} +2 -2
- package/dist/index.js +343 -239
- package/dist/{transaction-4KBNHDQX.js → transaction-JHZJWBII.js} +2 -2
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +1 -1
- package/skills/agentwheel-discovery/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -132,6 +132,12 @@ Apply the same command with the reviewed values added as `--expected-hash <sha25
|
|
|
132
132
|
journal, rechecks the old owner and the live artifact hash, then atomically updates only the install
|
|
133
133
|
manifest. Run one target at a time; named SSH agents use the same contract through `--agent`.
|
|
134
134
|
|
|
135
|
+
Use `--from-unknown-owner` instead of `--from-workspace-root` for an entry recorded as
|
|
136
|
+
`workspace:unknown`. A plain file or directory whose runtime no longer matches the recorded hash is
|
|
137
|
+
refused unless `--carry-drift` is passed: the dry-run prints the runtime hash, the apply also needs
|
|
138
|
+
`--expected-runtime-hash <sha256>`, and the manifest keeps the recorded hash so the next install
|
|
139
|
+
still reports the drift. Merge and managed-block entries never carry drift.
|
|
140
|
+
|
|
135
141
|
For a one-time MCP rename, keep the legacy artifact in a dedicated cutover workspace rather than
|
|
136
142
|
the canonical desired-state profile. Set the legacy manifest `stateKey` on its named agent, then
|
|
137
143
|
preview one runtime at a time:
|
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
commitManifestMetadataJournal,
|
|
6
6
|
recoverPendingApply,
|
|
7
7
|
uninstall
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-L37KQVRS.js";
|
|
9
|
+
import "./chunk-BHT57MK5.js";
|
|
10
|
+
import "./chunk-BNIF3FUR.js";
|
|
11
11
|
export {
|
|
12
12
|
applyCombinedInstallPlan,
|
|
13
13
|
applyInstallPlan,
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
hashPath,
|
|
5
5
|
pathExists,
|
|
6
6
|
writeJsonAtomic
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BNIF3FUR.js";
|
|
8
8
|
|
|
9
9
|
// src/install/transaction.ts
|
|
10
10
|
import { createHash as createHash6 } from "crypto";
|
|
@@ -2865,8 +2865,8 @@ async function recoverMutationRuntime(operationId, options = {}) {
|
|
|
2865
2865
|
}
|
|
2866
2866
|
mutation = GovernedMutation.activateExisting(receipt, baseline, lock);
|
|
2867
2867
|
const [{ recoverPendingApply }, { readLinkedLocalApplyJournal: readLinkedLocalApplyJournal2, removeApplyJournal: removeApplyJournal2, localPathExists: localPathExists2 }] = await Promise.all([
|
|
2868
|
-
import("./apply-
|
|
2869
|
-
import("./transaction-
|
|
2868
|
+
import("./apply-QJHTL7IK.js"),
|
|
2869
|
+
import("./transaction-JHZJWBII.js")
|
|
2870
2870
|
]);
|
|
2871
2871
|
let missingReservation = false;
|
|
2872
2872
|
for (const entry of pending) {
|
|
@@ -24,6 +24,14 @@ async function pathExists(path) {
|
|
|
24
24
|
return false;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
async function removeOnFailure(path, run) {
|
|
28
|
+
try {
|
|
29
|
+
return await run();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
await rm(path, { recursive: true, force: true });
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
27
35
|
async function hashPath(path) {
|
|
28
36
|
const stats = await stat(path);
|
|
29
37
|
if (stats.isFile()) {
|
|
@@ -129,6 +137,7 @@ function isAlreadyExists(error) {
|
|
|
129
137
|
|
|
130
138
|
export {
|
|
131
139
|
pathExists,
|
|
140
|
+
removeOnFailure,
|
|
132
141
|
hashPath,
|
|
133
142
|
listFiles,
|
|
134
143
|
isIgnoredGeneratedEntry,
|
|
@@ -24,11 +24,11 @@ import {
|
|
|
24
24
|
rollbackCompletedOperations,
|
|
25
25
|
sourceLockPath,
|
|
26
26
|
writeApplyJournal
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-BHT57MK5.js";
|
|
28
28
|
import {
|
|
29
29
|
pathExists,
|
|
30
30
|
writeJsonAtomic
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-BNIF3FUR.js";
|
|
32
32
|
|
|
33
33
|
// src/install/apply.ts
|
|
34
34
|
import { execFile } from "child_process";
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
uninstall,
|
|
38
38
|
withManifestRevision,
|
|
39
39
|
writeInstallManifest
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-L37KQVRS.js";
|
|
41
41
|
import {
|
|
42
42
|
CURRENT_WORKSPACE_SCHEMA_VERSION,
|
|
43
43
|
GovernedMutation,
|
|
@@ -98,25 +98,26 @@ import {
|
|
|
98
98
|
workspaceSelectionImportSchema,
|
|
99
99
|
writeApplyJournal,
|
|
100
100
|
writeWorkspaceConfig
|
|
101
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-BHT57MK5.js";
|
|
102
102
|
import {
|
|
103
103
|
inferSourceDriverName
|
|
104
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-QABA4WUP.js";
|
|
105
105
|
import {
|
|
106
106
|
hashPath,
|
|
107
107
|
isAlreadyExists,
|
|
108
108
|
isIgnoredGeneratedEntry,
|
|
109
109
|
listFiles,
|
|
110
110
|
pathExists,
|
|
111
|
+
removeOnFailure,
|
|
111
112
|
withFilesystemLock,
|
|
112
113
|
writeJsonAtomic
|
|
113
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-BNIF3FUR.js";
|
|
114
115
|
|
|
115
116
|
// src/cli/index.ts
|
|
116
117
|
import { createHash as createHash16 } from "crypto";
|
|
117
118
|
import { existsSync } from "fs";
|
|
118
|
-
import { mkdir as mkdir16, mkdtemp as
|
|
119
|
-
import { homedir as homedir12, tmpdir as
|
|
119
|
+
import { mkdir as mkdir16, mkdtemp as mkdtemp3, realpath as realpath3, rm as rm10, writeFile as writeFile13 } from "fs/promises";
|
|
120
|
+
import { homedir as homedir12, tmpdir as tmpdir4 } from "os";
|
|
120
121
|
import { basename as basename21, dirname as dirname23, isAbsolute as isAbsolute5, join as join46, relative as relative11, resolve as resolve27 } from "path";
|
|
121
122
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
122
123
|
import { Command } from "commander";
|
|
@@ -4079,6 +4080,58 @@ function errorMessage3(error) {
|
|
|
4079
4080
|
return error instanceof Error ? error.message : String(error);
|
|
4080
4081
|
}
|
|
4081
4082
|
|
|
4083
|
+
// src/source/git-source.ts
|
|
4084
|
+
import { posix } from "path";
|
|
4085
|
+
var SUBPATH_SEPARATOR = "//";
|
|
4086
|
+
function parseGitSource(source) {
|
|
4087
|
+
if (source.startsWith("github:")) {
|
|
4088
|
+
const rest = source.slice("github:".length);
|
|
4089
|
+
const hashIndex = rest.indexOf("#");
|
|
4090
|
+
const repo = hashIndex >= 0 ? rest.slice(0, hashIndex) : rest;
|
|
4091
|
+
if (!repo.includes("/")) throw new Error(`Invalid GitHub source: ${source}`);
|
|
4092
|
+
return {
|
|
4093
|
+
url: `https://github.com/${repo.replace(/\.git$/i, "")}.git`,
|
|
4094
|
+
...splitFragment(hashIndex >= 0 ? rest.slice(hashIndex + 1) : void 0, source)
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
4097
|
+
if (source.startsWith("git:")) {
|
|
4098
|
+
const rest = source.slice("git:".length);
|
|
4099
|
+
const hashIndex = rest.lastIndexOf("#");
|
|
4100
|
+
if (hashIndex < 0) return { url: rest };
|
|
4101
|
+
return { url: rest.slice(0, hashIndex), ...splitFragment(rest.slice(hashIndex + 1), source) };
|
|
4102
|
+
}
|
|
4103
|
+
throw new Error(`Invalid git source: ${source}`);
|
|
4104
|
+
}
|
|
4105
|
+
function formatGitSource(url, ref, subpath) {
|
|
4106
|
+
return subpath ? `git:${url}#${ref}${SUBPATH_SEPARATOR}${subpath}` : `git:${url}#${ref}`;
|
|
4107
|
+
}
|
|
4108
|
+
function normalizeGitSubpath(subpath, source) {
|
|
4109
|
+
if (subpath === void 0) return void 0;
|
|
4110
|
+
if (subpath.includes("\\")) throw new Error(`Git source subpath must use forward slashes: ${source}`);
|
|
4111
|
+
if (subpath.startsWith("/")) throw new Error(`Git source subpath must be relative to the repository: ${source}`);
|
|
4112
|
+
const normalized = posix.normalize(subpath).replace(/\/+$/g, "");
|
|
4113
|
+
if (normalized === "." || normalized === "") return void 0;
|
|
4114
|
+
if (normalized === ".." || normalized.startsWith("../")) {
|
|
4115
|
+
throw new Error(`Git source subpath escapes the repository: ${source}`);
|
|
4116
|
+
}
|
|
4117
|
+
return normalized;
|
|
4118
|
+
}
|
|
4119
|
+
function resolveGitRelativeSubpath(declaringSubpath, relative12, source) {
|
|
4120
|
+
const joined = posix.normalize(posix.join(declaringSubpath ?? ".", relative12));
|
|
4121
|
+
if (joined === ".." || joined.startsWith("../")) {
|
|
4122
|
+
throw new Error(`Relative dependency '${relative12}' escapes the git repository it is declared in: ${source}`);
|
|
4123
|
+
}
|
|
4124
|
+
return normalizeGitSubpath(joined, source);
|
|
4125
|
+
}
|
|
4126
|
+
function splitFragment(fragment, source) {
|
|
4127
|
+
if (fragment === void 0) return {};
|
|
4128
|
+
const separator = fragment.indexOf(SUBPATH_SEPARATOR);
|
|
4129
|
+
if (separator < 0) return fragment ? { ref: fragment } : {};
|
|
4130
|
+
const ref = fragment.slice(0, separator);
|
|
4131
|
+
const subpath = normalizeGitSubpath(fragment.slice(separator + SUBPATH_SEPARATOR.length), source);
|
|
4132
|
+
return { ...ref ? { ref } : {}, ...subpath ? { subpath } : {} };
|
|
4133
|
+
}
|
|
4134
|
+
|
|
4082
4135
|
// src/source/git.ts
|
|
4083
4136
|
var execFileAsync = promisify(execFile);
|
|
4084
4137
|
var GitSourceDriver = class {
|
|
@@ -4138,15 +4191,18 @@ var GitSourceDriver = class {
|
|
|
4138
4191
|
return { path, leasePath };
|
|
4139
4192
|
}
|
|
4140
4193
|
);
|
|
4141
|
-
const
|
|
4142
|
-
|
|
4194
|
+
const packageRoot = parsed.subpath ? join19(snapshot.path, parsed.subpath) : snapshot.path;
|
|
4195
|
+
if (parsed.subpath && !await isDirectory(packageRoot)) {
|
|
4196
|
+
throw new Error(`Git source subpath '${parsed.subpath}' does not exist at ${resolvedCommit} in ${parsed.url}`);
|
|
4197
|
+
}
|
|
4198
|
+
const manifest = await readPackageManifest(packageRoot);
|
|
4143
4199
|
return {
|
|
4144
4200
|
...resolved,
|
|
4145
|
-
resolvedPath:
|
|
4201
|
+
resolvedPath: packageRoot,
|
|
4146
4202
|
packageName: manifest?.name,
|
|
4147
4203
|
packageVersion: manifest?.version,
|
|
4148
4204
|
resolvedCommit,
|
|
4149
|
-
sourceHash: await hashPath(
|
|
4205
|
+
sourceHash: await hashPath(packageRoot),
|
|
4150
4206
|
cacheLeasePath: snapshot.leasePath
|
|
4151
4207
|
};
|
|
4152
4208
|
}, "git cache");
|
|
@@ -4164,22 +4220,12 @@ var GitSourceDriver = class {
|
|
|
4164
4220
|
return resolved;
|
|
4165
4221
|
}
|
|
4166
4222
|
};
|
|
4167
|
-
function
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
return { url: `https://github.com/${repo}.git`, ref };
|
|
4173
|
-
}
|
|
4174
|
-
if (source.startsWith("git:")) {
|
|
4175
|
-
const rest = source.slice("git:".length);
|
|
4176
|
-
const hashIndex = rest.lastIndexOf("#");
|
|
4177
|
-
if (hashIndex >= 0) {
|
|
4178
|
-
return { url: rest.slice(0, hashIndex), ref: rest.slice(hashIndex + 1) };
|
|
4179
|
-
}
|
|
4180
|
-
return { url: rest };
|
|
4223
|
+
async function isDirectory(path) {
|
|
4224
|
+
try {
|
|
4225
|
+
return (await stat4(path)).isDirectory();
|
|
4226
|
+
} catch {
|
|
4227
|
+
return false;
|
|
4181
4228
|
}
|
|
4182
|
-
throw new Error(`Invalid git source: ${source}`);
|
|
4183
4229
|
}
|
|
4184
4230
|
function cachePathFor2(url, cacheRoot) {
|
|
4185
4231
|
const root = cacheRoot ? resolve5(cacheRoot) : join19(homedir2(), ".agentwheel", "cache");
|
|
@@ -5337,7 +5383,8 @@ function claudeAgentName(artifact) {
|
|
|
5337
5383
|
|
|
5338
5384
|
// src/staging/staging.ts
|
|
5339
5385
|
async function stageSource(driver, source, options = {}) {
|
|
5340
|
-
|
|
5386
|
+
const raw = await stageSourceRaw(driver, source, options);
|
|
5387
|
+
return removeOnFailure(raw.root, () => renderStagedBundle(raw, options));
|
|
5341
5388
|
}
|
|
5342
5389
|
async function stageSourceRaw(driver, source, options = {}) {
|
|
5343
5390
|
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(source, options))));
|
|
@@ -5352,25 +5399,27 @@ async function stageResolvedSourceRaw(driver, resolved) {
|
|
|
5352
5399
|
const artifacts = await driver.list(resolved);
|
|
5353
5400
|
return stageResolvedArtifactsRaw(resolved, artifacts);
|
|
5354
5401
|
}
|
|
5355
|
-
async function stageResolvedArtifactsRaw(resolved, artifacts) {
|
|
5356
|
-
const root = await mkdtemp(join27(
|
|
5402
|
+
async function stageResolvedArtifactsRaw(resolved, artifacts, parentDir = tmpdir2()) {
|
|
5403
|
+
const root = await mkdtemp(join27(parentDir, "agentwheel-stage-"));
|
|
5357
5404
|
const stagedArtifacts = [];
|
|
5358
|
-
|
|
5359
|
-
const
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5405
|
+
await removeOnFailure(root, async () => {
|
|
5406
|
+
for (const artifact of artifacts) {
|
|
5407
|
+
const stagedPath = join27(root, artifact.relativePath);
|
|
5408
|
+
await mkdir11(dirname13(stagedPath), { recursive: true });
|
|
5409
|
+
await cp3(artifact.sourcePath, stagedPath, {
|
|
5410
|
+
recursive: artifact.kind === "dir",
|
|
5411
|
+
dereference: true,
|
|
5412
|
+
filter: (path) => !isIgnoredGeneratedEntry(basename16(path))
|
|
5413
|
+
});
|
|
5414
|
+
await composeAssets(artifact, resolved.resolvedPath, stagedPath);
|
|
5415
|
+
stagedArtifacts.push({
|
|
5416
|
+
...artifact,
|
|
5417
|
+
stagedPath,
|
|
5418
|
+
hash: await hashPath(stagedPath),
|
|
5419
|
+
channel: artifact.channel ?? "managed"
|
|
5420
|
+
});
|
|
5421
|
+
}
|
|
5422
|
+
});
|
|
5374
5423
|
return {
|
|
5375
5424
|
root,
|
|
5376
5425
|
source: resolved,
|
|
@@ -5527,8 +5576,7 @@ import { dirname as dirname15, join as join30, relative as relative6 } from "pat
|
|
|
5527
5576
|
|
|
5528
5577
|
// src/resolve/graph.ts
|
|
5529
5578
|
import { createHash as createHash6 } from "crypto";
|
|
5530
|
-
import {
|
|
5531
|
-
import { tmpdir as tmpdir3 } from "os";
|
|
5579
|
+
import { readdir as readdir7, readFile as readFile17, stat as stat11 } from "fs/promises";
|
|
5532
5580
|
import { basename as basename17, extname as extname4, join as join29 } from "path";
|
|
5533
5581
|
|
|
5534
5582
|
// src/model/workspace-composition.ts
|
|
@@ -5789,7 +5837,7 @@ var RegistryClient = class {
|
|
|
5789
5837
|
}
|
|
5790
5838
|
};
|
|
5791
5839
|
async function resolvePackageSource(source, workspaceRoot, options = {}) {
|
|
5792
|
-
const { isExplicitSource } = await import("./identify-
|
|
5840
|
+
const { isExplicitSource } = await import("./identify-M6IA4RS7.js");
|
|
5793
5841
|
if (await isExplicitSource(source)) return { source };
|
|
5794
5842
|
const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
|
|
5795
5843
|
if (!entry) {
|
|
@@ -5831,6 +5879,17 @@ async function normalizeDependencySource(source, options) {
|
|
|
5831
5879
|
if (isBareRegistryName(trimmed)) {
|
|
5832
5880
|
return normalizeRegistrySource(trimmed, options);
|
|
5833
5881
|
}
|
|
5882
|
+
if (options.declaringGitSource && (trimmed === "." || trimmed === ".." || trimmed.startsWith("./") || trimmed.startsWith("../"))) {
|
|
5883
|
+
const declaring = options.declaringGitSource;
|
|
5884
|
+
const subpath = resolveGitRelativeSubpath(declaring.subpath, trimmed, source);
|
|
5885
|
+
const normalizedSource = formatGitSource(declaring.url, declaring.commit, subpath);
|
|
5886
|
+
return {
|
|
5887
|
+
source: normalizedSource,
|
|
5888
|
+
normalizedSource,
|
|
5889
|
+
driver: "git",
|
|
5890
|
+
requestedRef: declaring.commit
|
|
5891
|
+
};
|
|
5892
|
+
}
|
|
5834
5893
|
if (isLocalSource(trimmed)) {
|
|
5835
5894
|
const path = localSourcePath(trimmed);
|
|
5836
5895
|
const resolvedPath = resolveLocalPath(path, options.declaringPackageRoot);
|
|
@@ -5841,10 +5900,10 @@ async function normalizeDependencySource(source, options) {
|
|
|
5841
5900
|
};
|
|
5842
5901
|
}
|
|
5843
5902
|
if (trimmed.startsWith("github:") || trimmed.startsWith("git:")) {
|
|
5844
|
-
const parsed =
|
|
5903
|
+
const parsed = parseGitSource(trimmed);
|
|
5845
5904
|
const requestedRef = options.ref ?? parsed.ref ?? "HEAD";
|
|
5846
5905
|
const url = normalizeGitRemoteUrl(parsed.url, options.declaringPackageRoot);
|
|
5847
|
-
const normalizedSource =
|
|
5906
|
+
const normalizedSource = formatGitSource(url, requestedRef, parsed.subpath);
|
|
5848
5907
|
return {
|
|
5849
5908
|
source: normalizedSource,
|
|
5850
5909
|
normalizedSource,
|
|
@@ -5921,20 +5980,6 @@ async function normalizeRegistrySource(name, options) {
|
|
|
5921
5980
|
normalizedSource: `registry:${name}:${resolved.normalizedSource}`
|
|
5922
5981
|
};
|
|
5923
5982
|
}
|
|
5924
|
-
function parseGitSource2(source) {
|
|
5925
|
-
if (source.startsWith("github:")) {
|
|
5926
|
-
const rest2 = source.slice("github:".length);
|
|
5927
|
-
const [repo, ref] = rest2.split("#", 2);
|
|
5928
|
-
if (!repo.includes("/")) throw new Error(`Invalid GitHub dependency source: ${source}`);
|
|
5929
|
-
return { url: `https://github.com/${repo.replace(/\.git$/i, "")}.git`, ref };
|
|
5930
|
-
}
|
|
5931
|
-
const rest = source.slice("git:".length);
|
|
5932
|
-
const hashIndex = rest.lastIndexOf("#");
|
|
5933
|
-
if (hashIndex >= 0) {
|
|
5934
|
-
return { url: rest.slice(0, hashIndex), ref: rest.slice(hashIndex + 1) };
|
|
5935
|
-
}
|
|
5936
|
-
return { url: rest };
|
|
5937
|
-
}
|
|
5938
5983
|
function normalizeGitRemoteUrl(url, declaringPackageRoot) {
|
|
5939
5984
|
if (url.startsWith("./") || url.startsWith("../") || url.startsWith("/") || url === "~" || url.startsWith("~/")) {
|
|
5940
5985
|
return resolveLocalPath(url, declaringPackageRoot);
|
|
@@ -5986,7 +6031,6 @@ var SnapshotConflictError = class extends Error {
|
|
|
5986
6031
|
var cacheLocks = /* @__PURE__ */ new Map();
|
|
5987
6032
|
async function resolveDependencyGraph(roots, options) {
|
|
5988
6033
|
if (roots.length === 0) throw new Error("At least one graph root is required.");
|
|
5989
|
-
const graphRoot = await mkdtemp2(join29(tmpdir3(), "agentwheel-graph-"));
|
|
5990
6034
|
const fetchCache = /* @__PURE__ */ new Map();
|
|
5991
6035
|
const nodesByKey = /* @__PURE__ */ new Map();
|
|
5992
6036
|
const nodesBySource = /* @__PURE__ */ new Map();
|
|
@@ -6052,7 +6096,6 @@ async function resolveDependencyGraph(roots, options) {
|
|
|
6052
6096
|
}
|
|
6053
6097
|
detectDirectCollisions(rawNodes);
|
|
6054
6098
|
return {
|
|
6055
|
-
root: graphRoot,
|
|
6056
6099
|
roots: rootResults.sort((a, b) => a.rootId.localeCompare(b.rootId)),
|
|
6057
6100
|
nodes: rawNodes.map((state) => state.node),
|
|
6058
6101
|
rawNodes,
|
|
@@ -6094,6 +6137,7 @@ async function processRequirement(requirement, options, fetchCache, nodesByKey,
|
|
|
6094
6137
|
let lockedByReference = lockedNodeForRequirementReference(requirement, options, lockLabel);
|
|
6095
6138
|
let normalized = lockedByReference ? normalizedSourceFromLockedNode(lockedByReference.node) : await normalizeDependencySource(requirement.source, {
|
|
6096
6139
|
declaringPackageRoot: requirement.declaringPackageRoot,
|
|
6140
|
+
declaringGitSource: requirement.declaringGitSource,
|
|
6097
6141
|
workspaceRoot: options.workspaceRoot,
|
|
6098
6142
|
ref: requirement.ref,
|
|
6099
6143
|
registryClient: options.registryClient
|
|
@@ -6101,6 +6145,7 @@ async function processRequirement(requirement, options, fetchCache, nodesByKey,
|
|
|
6101
6145
|
if (lockedByReference && shouldCheckLockedRootSource(requirement)) {
|
|
6102
6146
|
const declared = await normalizeDependencySource(requirement.source, {
|
|
6103
6147
|
declaringPackageRoot: requirement.declaringPackageRoot,
|
|
6148
|
+
declaringGitSource: requirement.declaringGitSource,
|
|
6104
6149
|
workspaceRoot: options.workspaceRoot,
|
|
6105
6150
|
ref: requirement.ref,
|
|
6106
6151
|
registryClient: options.registryClient
|
|
@@ -6378,6 +6423,7 @@ function dependencyRequirement(state, fetched, alias, dependency, select, chain,
|
|
|
6378
6423
|
useLock: dependency.mode !== "tracking" || options.lockedResolution === true && !updateClosure,
|
|
6379
6424
|
updateClosure,
|
|
6380
6425
|
declaringPackageRoot: fetched.resolved.resolvedPath,
|
|
6426
|
+
declaringGitSource: declaringGitSource(fetched),
|
|
6381
6427
|
requiredBy: state.node.id,
|
|
6382
6428
|
alias,
|
|
6383
6429
|
parentId: state.node.id,
|
|
@@ -6885,6 +6931,15 @@ async function mapLimit(items, limit, fn) {
|
|
|
6885
6931
|
if (failure !== void 0) throw failure;
|
|
6886
6932
|
return out;
|
|
6887
6933
|
}
|
|
6934
|
+
function declaringGitSource(fetched) {
|
|
6935
|
+
if (fetched.resolved.driver !== "git" || !fetched.resolved.resolvedCommit) return void 0;
|
|
6936
|
+
const parsed = parseGitSource(fetched.resolved.source);
|
|
6937
|
+
return {
|
|
6938
|
+
url: normalizeGitRemoteUrl(parsed.url, fetched.resolved.resolvedPath),
|
|
6939
|
+
commit: fetched.resolved.resolvedCommit,
|
|
6940
|
+
subpath: parsed.subpath
|
|
6941
|
+
};
|
|
6942
|
+
}
|
|
6888
6943
|
|
|
6889
6944
|
// src/lifecycle/customization.ts
|
|
6890
6945
|
async function remember(workspaceRoot, runtime, text) {
|
|
@@ -7039,7 +7094,7 @@ import { rm as rm7 } from "fs/promises";
|
|
|
7039
7094
|
// src/lifecycle/source-plan.ts
|
|
7040
7095
|
import { createHash as createHash8 } from "crypto";
|
|
7041
7096
|
import { mkdir as mkdir14, readdir as readdir9 } from "fs/promises";
|
|
7042
|
-
import { basename as basename18, dirname as dirname17, join as join33, posix as
|
|
7097
|
+
import { basename as basename18, dirname as dirname17, join as join33, posix as posix3, resolve as resolve16 } from "path";
|
|
7043
7098
|
|
|
7044
7099
|
// src/resolve/graph-diff.ts
|
|
7045
7100
|
function diffGraphLocks(previous, next) {
|
|
@@ -7202,11 +7257,14 @@ function formatSelectionImport(root) {
|
|
|
7202
7257
|
|
|
7203
7258
|
// src/resolve/render.ts
|
|
7204
7259
|
import { createHash as createHash7 } from "crypto";
|
|
7205
|
-
import { readFile as readFile18, mkdtemp as
|
|
7206
|
-
import { tmpdir as
|
|
7260
|
+
import { readFile as readFile18, mkdtemp as mkdtemp2 } from "fs/promises";
|
|
7261
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
7207
7262
|
import { join as join31 } from "path";
|
|
7208
7263
|
async function renderGraphForTarget(graph, targetContext = {}) {
|
|
7209
|
-
const root = await
|
|
7264
|
+
const root = await mkdtemp2(join31(tmpdir3(), "agentwheel-render-"));
|
|
7265
|
+
return removeOnFailure(root, () => renderGraphIntoRoot(root, graph, targetContext));
|
|
7266
|
+
}
|
|
7267
|
+
async function renderGraphIntoRoot(root, graph, targetContext) {
|
|
7210
7268
|
const artifacts = [];
|
|
7211
7269
|
const stagedNodes = /* @__PURE__ */ new Map();
|
|
7212
7270
|
const includeEdges = /* @__PURE__ */ new Map();
|
|
@@ -7214,7 +7272,7 @@ async function renderGraphForTarget(graph, targetContext = {}) {
|
|
|
7214
7272
|
for (const rawNode of graph.rawNodes) {
|
|
7215
7273
|
const ownsCompositionRules = rawNode.depth === 0 && rawNode.manifest?.schemaVersion === 3 && Boolean(rawNode.manifest.compositionRules?.length);
|
|
7216
7274
|
if (rawNode.node.selected.length === 0 && !ownsCompositionRules) continue;
|
|
7217
|
-
const rawBundle = await stageResolvedArtifactsRaw(rawNode.resolved, rawNode.artifacts);
|
|
7275
|
+
const rawBundle = await stageResolvedArtifactsRaw(rawNode.resolved, rawNode.artifacts, root);
|
|
7218
7276
|
const fragmentCustomized = targetContext.workspaceRoot && targetContext.adapter ? await applyFragmentCustomizations(rawBundle.artifacts, {
|
|
7219
7277
|
workspaceRoot: targetContext.workspaceRoot,
|
|
7220
7278
|
adapter: targetContext.adapter,
|
|
@@ -8081,7 +8139,7 @@ function containsPath(parent, candidate) {
|
|
|
8081
8139
|
}
|
|
8082
8140
|
|
|
8083
8141
|
// src/model/target-state.ts
|
|
8084
|
-
import { posix, resolve as resolve15 } from "path";
|
|
8142
|
+
import { posix as posix2, resolve as resolve15 } from "path";
|
|
8085
8143
|
var targetEvolutionFields = /* @__PURE__ */ new Set([
|
|
8086
8144
|
"adapterCodeHash",
|
|
8087
8145
|
"adapterConfig",
|
|
@@ -8114,7 +8172,7 @@ function persistentTargetParts(value) {
|
|
|
8114
8172
|
);
|
|
8115
8173
|
}
|
|
8116
8174
|
function normalizeInstallRoot(path, transportKind) {
|
|
8117
|
-
return transportKind === "ssh" ?
|
|
8175
|
+
return transportKind === "ssh" ? posix2.normalize(path) : resolve15(path);
|
|
8118
8176
|
}
|
|
8119
8177
|
function targetEndpoint(value, transportKind) {
|
|
8120
8178
|
if (transportKind !== "ssh") return { kind: "local" };
|
|
@@ -8271,85 +8329,87 @@ async function createGraphSourcePlan(options) {
|
|
|
8271
8329
|
noDeps: options.noDeps,
|
|
8272
8330
|
warn
|
|
8273
8331
|
});
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
const resolvedInstallationType = resolveInstallationTypeForArtifacts(options.adapter, desiredArtifacts.map((artifact) => artifact.type), installationType);
|
|
8281
|
-
const resolvedInstallRoot = installRootForArtifacts(options.adapter, options.targetRoot, resolvedInstallationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
|
|
8282
|
-
const graphLockDigest = digestGraphLock(bundle.graphLock);
|
|
8283
|
-
const graphDiff = diffGraphLocks(previousLock, bundle.graphLock);
|
|
8284
|
-
if (normalizeInstallRoot2(resolvedInstallRoot, transport.kind) !== normalizeInstallRoot2(installRoot, transport.kind)) {
|
|
8285
|
-
throw new Error(
|
|
8286
|
-
`Adapter ${options.adapter.name} resolved a different install root after rendering; target state identity cannot be proven.`
|
|
8332
|
+
return removeOnFailure(bundle.root, async () => {
|
|
8333
|
+
const desiredArtifacts = filterArtifactsByAdapterTargets(
|
|
8334
|
+
desiredArtifactsFromGraphBundle(bundle),
|
|
8335
|
+
options.adapter,
|
|
8336
|
+
installationType,
|
|
8337
|
+
{ warn }
|
|
8287
8338
|
);
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
{
|
|
8297
|
-
installationType: resolvedInstallationType,
|
|
8298
|
-
stateKey,
|
|
8299
|
-
workspaceOwner,
|
|
8300
|
-
expectedFromWorkspaceOwner: options.expectedFromWorkspaceOwner,
|
|
8301
|
-
graphLockDigest
|
|
8339
|
+
const resolvedInstallationType = resolveInstallationTypeForArtifacts(options.adapter, desiredArtifacts.map((artifact) => artifact.type), installationType);
|
|
8340
|
+
const resolvedInstallRoot = installRootForArtifacts(options.adapter, options.targetRoot, resolvedInstallationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
|
|
8341
|
+
const graphLockDigest = digestGraphLock(bundle.graphLock);
|
|
8342
|
+
const graphDiff = diffGraphLocks(previousLock, bundle.graphLock);
|
|
8343
|
+
if (normalizeInstallRoot2(resolvedInstallRoot, transport.kind) !== normalizeInstallRoot2(installRoot, transport.kind)) {
|
|
8344
|
+
throw new Error(
|
|
8345
|
+
`Adapter ${options.adapter.name} resolved a different install root after rendering; target state identity cannot be proven.`
|
|
8346
|
+
);
|
|
8302
8347
|
}
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
forceDrift: options.forceDrift,
|
|
8310
|
-
forceConflict: options.forceConflict,
|
|
8311
|
-
replaceConflict: options.replaceConflict,
|
|
8312
|
-
stateMigration: priorState.migration,
|
|
8313
|
-
warn
|
|
8314
|
-
});
|
|
8315
|
-
const plan = priorState.migration && !basePlan.stateMigration ? { ...basePlan, stateMigration: priorState.migration } : basePlan;
|
|
8316
|
-
plan.targetStateFilePreconditions = {
|
|
8317
|
-
graphLockPath,
|
|
8318
|
-
graphLockRevision: stableLock ? digestGraphLock(stableLock) : null,
|
|
8319
|
-
sourceLockRevision: computeSourceLockRevision(stableSourceLock)
|
|
8320
|
-
};
|
|
8321
|
-
let foreignStateObservations = [];
|
|
8322
|
-
if (options.forceForeignState !== true && options.deferForeignStateCheck !== true) {
|
|
8323
|
-
foreignStateObservations = await assertNoForeignWorkspaceState({
|
|
8324
|
-
installRoot: resolvedInstallRoot,
|
|
8325
|
-
adapter: options.adapter.name,
|
|
8348
|
+
const manifest = priorState.manifest;
|
|
8349
|
+
const basePlan = options.retireExactMcp ? await createExactMcpRetirementPlan(
|
|
8350
|
+
desiredArtifacts,
|
|
8351
|
+
options.adapter,
|
|
8352
|
+
options.targetRoot,
|
|
8353
|
+
manifest,
|
|
8326
8354
|
transport,
|
|
8327
|
-
|
|
8355
|
+
{
|
|
8356
|
+
installationType: resolvedInstallationType,
|
|
8357
|
+
stateKey,
|
|
8358
|
+
workspaceOwner,
|
|
8359
|
+
expectedFromWorkspaceOwner: options.expectedFromWorkspaceOwner,
|
|
8360
|
+
graphLockDigest
|
|
8361
|
+
}
|
|
8362
|
+
) : await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
|
|
8363
|
+
baseRevision: manifest?.revision ?? null,
|
|
8364
|
+
graphLockDigest,
|
|
8328
8365
|
workspaceOwner,
|
|
8329
|
-
|
|
8366
|
+
installationType: resolvedInstallationType,
|
|
8330
8367
|
stateKey,
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8368
|
+
forceDrift: options.forceDrift,
|
|
8369
|
+
forceConflict: options.forceConflict,
|
|
8370
|
+
replaceConflict: options.replaceConflict,
|
|
8371
|
+
stateMigration: priorState.migration,
|
|
8372
|
+
warn
|
|
8334
8373
|
});
|
|
8335
|
-
plan
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8374
|
+
const plan = priorState.migration && !basePlan.stateMigration ? { ...basePlan, stateMigration: priorState.migration } : basePlan;
|
|
8375
|
+
plan.targetStateFilePreconditions = {
|
|
8376
|
+
graphLockPath,
|
|
8377
|
+
graphLockRevision: stableLock ? digestGraphLock(stableLock) : null,
|
|
8378
|
+
sourceLockRevision: computeSourceLockRevision(stableSourceLock)
|
|
8379
|
+
};
|
|
8380
|
+
let foreignStateObservations = [];
|
|
8381
|
+
if (options.forceForeignState !== true && options.deferForeignStateCheck !== true) {
|
|
8382
|
+
foreignStateObservations = await assertNoForeignWorkspaceState({
|
|
8383
|
+
installRoot: resolvedInstallRoot,
|
|
8384
|
+
adapter: options.adapter.name,
|
|
8385
|
+
transport,
|
|
8386
|
+
workspaceRoot,
|
|
8387
|
+
workspaceOwner,
|
|
8388
|
+
globalRoot: options.globalRoot,
|
|
8389
|
+
stateKey,
|
|
8390
|
+
migratingStateKey: priorState.migration?.fromStateKey,
|
|
8391
|
+
plannedPaths: plan.operations.map((operation) => operation.relativeDestPath),
|
|
8392
|
+
plannedOperations: plan.operations
|
|
8393
|
+
});
|
|
8394
|
+
plan.hasBlockingChanges = plan.operations.some((operation) => operation.action === "drift" || operation.action === "conflict");
|
|
8395
|
+
}
|
|
8396
|
+
return {
|
|
8397
|
+
plan,
|
|
8398
|
+
graph,
|
|
8399
|
+
bundle,
|
|
8400
|
+
desiredArtifacts,
|
|
8401
|
+
graphLockPath,
|
|
8402
|
+
graphLockDigest,
|
|
8403
|
+
targetFingerprint,
|
|
8404
|
+
warnings,
|
|
8405
|
+
newTransitiveSources: trustEvaluation.promptSources,
|
|
8406
|
+
graphDiff,
|
|
8407
|
+
recoveredPendingApply,
|
|
8408
|
+
previousManifest: priorState.manifest,
|
|
8409
|
+
previousGraphLock: priorState.graphLock,
|
|
8410
|
+
foreignStateObservations
|
|
8411
|
+
};
|
|
8412
|
+
});
|
|
8353
8413
|
}
|
|
8354
8414
|
async function assertNoForeignWorkspaceStateForPlan(plan, options) {
|
|
8355
8415
|
if (!plan.stateKey) throw new Error(`Foreign-state validation requires a state key for ${plan.adapter}.`);
|
|
@@ -8708,7 +8768,7 @@ function stableJson2(value) {
|
|
|
8708
8768
|
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort((left, right) => left.localeCompare(right)).map((key) => `${JSON.stringify(key)}:${stableJson2(record[key])}`).join(",")}}`;
|
|
8709
8769
|
}
|
|
8710
8770
|
function normalizeInstallRoot2(path, transportKind) {
|
|
8711
|
-
return transportKind === "ssh" ?
|
|
8771
|
+
return transportKind === "ssh" ? posix3.normalize(path) : resolve16(path);
|
|
8712
8772
|
}
|
|
8713
8773
|
var workspaceOwnerPrefix2 = "workspace-root:";
|
|
8714
8774
|
async function assertNoForeignWorkspaceState(check) {
|
|
@@ -9785,6 +9845,7 @@ function resolveCliVersion() {
|
|
|
9785
9845
|
|
|
9786
9846
|
// src/lifecycle/ownership.ts
|
|
9787
9847
|
import { resolve as resolve20 } from "path";
|
|
9848
|
+
var unknownWorkspaceOwner = "workspace:unknown";
|
|
9788
9849
|
async function planArtifactOwnershipHandoff(request) {
|
|
9789
9850
|
return validateOwnershipHandoff(request, request.transport ?? localTransport);
|
|
9790
9851
|
}
|
|
@@ -9803,6 +9864,9 @@ async function applyArtifactOwnershipHandoff(request) {
|
|
|
9803
9864
|
throw new Error("Cannot hand off ownership while an apply journal is pending. Recover or abort it first.");
|
|
9804
9865
|
}
|
|
9805
9866
|
const plan = await validateOwnershipHandoff(request, transport);
|
|
9867
|
+
if (plan.drifted && !request.expectedRuntimeHash) {
|
|
9868
|
+
throw new Error("Applying a drifted ownership handoff requires expectedRuntimeHash from a reviewed dry-run.");
|
|
9869
|
+
}
|
|
9806
9870
|
const manifest = await readInstallManifest(request.targetRoot, request.adapter, transport, scope);
|
|
9807
9871
|
if (!manifest || manifest.version !== 2) {
|
|
9808
9872
|
throw new Error("Ownership handoff requires an Agentwheel v2 install manifest.");
|
|
@@ -9833,6 +9897,13 @@ async function applyArtifactOwnershipHandoff(request) {
|
|
|
9833
9897
|
async function validateOwnershipHandoff(request, transport) {
|
|
9834
9898
|
if (request.expectedHash) assertSha256(request.expectedHash, "expected artifact hash");
|
|
9835
9899
|
if (request.expectedRevision) assertSha256(request.expectedRevision, "expected manifest revision");
|
|
9900
|
+
if (request.expectedRuntimeHash) {
|
|
9901
|
+
assertSha256(request.expectedRuntimeHash, "expected runtime hash");
|
|
9902
|
+
if (request.carryDrift !== true) throw new Error("An expected runtime hash is only valid with carryDrift.");
|
|
9903
|
+
}
|
|
9904
|
+
if (request.fromWorkspaceRoot === void 0 === (request.fromUnknownOwner !== true)) {
|
|
9905
|
+
throw new Error("Ownership handoff requires exactly one of fromWorkspaceRoot or fromUnknownOwner.");
|
|
9906
|
+
}
|
|
9836
9907
|
const scope = { installationType: request.installationType, stateKey: request.stateKey };
|
|
9837
9908
|
const manifest = await readInstallManifest(request.targetRoot, request.adapter, transport, scope);
|
|
9838
9909
|
if (!manifest) throw new Error(`No install manifest for ${request.adapter} at ${request.targetRoot}`);
|
|
@@ -9850,7 +9921,7 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
9850
9921
|
);
|
|
9851
9922
|
}
|
|
9852
9923
|
const entry = matches[0];
|
|
9853
|
-
const fromOwner = workspaceOwnerForRoot(request.fromWorkspaceRoot);
|
|
9924
|
+
const fromOwner = request.fromUnknownOwner === true ? unknownWorkspaceOwner : workspaceOwnerForRoot(request.fromWorkspaceRoot);
|
|
9854
9925
|
const toOwner = workspaceOwnerForRoot(request.toWorkspaceRoot, request.toFleetId);
|
|
9855
9926
|
if (fromOwner === toOwner) throw new Error("Ownership handoff requires different workspace roots.");
|
|
9856
9927
|
if (entry.workspaceOwner !== fromOwner) {
|
|
@@ -9861,9 +9932,14 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
9861
9932
|
}
|
|
9862
9933
|
const destPath = containedArtifactPath(request.targetRoot, entry.path);
|
|
9863
9934
|
if (!await transport.pathExists(destPath)) throw new Error(`Managed artifact is missing: ${entry.path}`);
|
|
9864
|
-
const
|
|
9865
|
-
|
|
9866
|
-
|
|
9935
|
+
const runtimeHash = await verifiedEntryHash(entry, destPath, transport, request.carryDrift === true);
|
|
9936
|
+
const drifted = runtimeHash !== entry.hash;
|
|
9937
|
+
if (drifted) {
|
|
9938
|
+
if (request.expectedRuntimeHash && runtimeHash !== request.expectedRuntimeHash) {
|
|
9939
|
+
throw new Error(`Runtime hash precondition failed for ${entry.path}: expected ${request.expectedRuntimeHash}, found ${runtimeHash}`);
|
|
9940
|
+
}
|
|
9941
|
+
} else if (request.expectedRuntimeHash) {
|
|
9942
|
+
throw new Error(`Runtime hash precondition failed for ${entry.path}: the artifact is no longer drifted`);
|
|
9867
9943
|
}
|
|
9868
9944
|
return {
|
|
9869
9945
|
adapter: request.adapter,
|
|
@@ -9872,13 +9948,15 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
9872
9948
|
targetRoot: request.targetRoot,
|
|
9873
9949
|
selector: `${request.artifactType}/${request.artifactName}`,
|
|
9874
9950
|
path: entry.path,
|
|
9875
|
-
artifactHash:
|
|
9951
|
+
artifactHash: entry.hash,
|
|
9952
|
+
runtimeHash,
|
|
9953
|
+
drifted,
|
|
9876
9954
|
manifestRevision: manifest.revision,
|
|
9877
9955
|
fromOwner,
|
|
9878
9956
|
toOwner
|
|
9879
9957
|
};
|
|
9880
9958
|
}
|
|
9881
|
-
async function verifiedEntryHash(entry, destPath, transport) {
|
|
9959
|
+
async function verifiedEntryHash(entry, destPath, transport, carryFileDrift = false) {
|
|
9882
9960
|
if (entry.semanticPlugin) throw new Error(`Ownership handoff cannot verify semantic plugin state at ${destPath}`);
|
|
9883
9961
|
if (entry.mode === "managed-block") {
|
|
9884
9962
|
const selector = managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName);
|
|
@@ -9896,7 +9974,7 @@ async function verifiedEntryHash(entry, destPath, transport) {
|
|
|
9896
9974
|
return entry.hash;
|
|
9897
9975
|
}
|
|
9898
9976
|
const currentHash = await transport.hashPath(destPath);
|
|
9899
|
-
if (currentHash !== entry.hash) {
|
|
9977
|
+
if (currentHash !== entry.hash && !carryFileDrift) {
|
|
9900
9978
|
throw new Error(`Managed artifact is drifted at ${destPath}: manifest ${entry.hash}, current ${currentHash}`);
|
|
9901
9979
|
}
|
|
9902
9980
|
return currentHash;
|
|
@@ -10226,7 +10304,7 @@ import { isAbsolute as isAbsolute3, relative as relative9, resolve as resolve23
|
|
|
10226
10304
|
// src/lifecycle/legacy-recovery-plan.ts
|
|
10227
10305
|
import { createHash as createHash10 } from "crypto";
|
|
10228
10306
|
import { lstat as lstat3, readdir as readdir10 } from "fs/promises";
|
|
10229
|
-
import { basename as basename20, dirname as dirname21, join as join39, posix as
|
|
10307
|
+
import { basename as basename20, dirname as dirname21, join as join39, posix as posix4, relative as relative8, resolve as resolve22 } from "path";
|
|
10230
10308
|
async function inventoryHistoricalLocks(fleetRoot) {
|
|
10231
10309
|
const root = join39(fleetRoot, ".agentwheel", "locks");
|
|
10232
10310
|
const found = [];
|
|
@@ -10384,7 +10462,7 @@ function claimFor(item, entry, locks, binding) {
|
|
|
10384
10462
|
const matches = locks.filter((candidate) => candidate.digest === digest2);
|
|
10385
10463
|
const evidence = [];
|
|
10386
10464
|
const owner = "workspaceOwner" in entry ? entry.workspaceOwner : legacyUnownedWorkspaceOwner;
|
|
10387
|
-
const sameRoot = binding.transportKind === "ssh" ?
|
|
10465
|
+
const sameRoot = binding.transportKind === "ssh" ? posix4.normalize(item.manifest.targetRoot) === posix4.normalize(binding.installRoot) : resolve22(item.manifest.targetRoot) === resolve22(binding.installRoot);
|
|
10388
10466
|
const inTargetDir = matches.filter((candidate) => resolve22(dirname21(candidate.path)) === resolve22(binding.targetLockDir));
|
|
10389
10467
|
const boundLocks = inTargetDir.filter((candidate) => lockBindsState(candidate, item, binding));
|
|
10390
10468
|
if (owner !== binding.expectedOwner) evidence.push("workspace owner differs from selected Fleet target");
|
|
@@ -14355,10 +14433,13 @@ program.command("remember").description("append text to the local instructions o
|
|
|
14355
14433
|
console.log(nextInstallNudge());
|
|
14356
14434
|
});
|
|
14357
14435
|
var ownershipCommand = program.command("ownership").description("inspect and transfer manifest ownership without rewriting runtime artifacts");
|
|
14358
|
-
ownershipCommand.command("handoff").description("transfer one managed artifact between Agentwheel workspace roots").argument("<selector>", "exact artifact selector in type/name form").
|
|
14436
|
+
ownershipCommand.command("handoff").description("transfer one managed artifact between Agentwheel workspace roots").argument("<selector>", "exact artifact selector in type/name form").option("--from-workspace-root <path>", "current owning workspace root").option("--from-unknown-owner", "hand off an artifact recorded as workspace:unknown", false).requiredOption("--to-workspace-root <path>", "new owning workspace root").option("--to-fleet <id>", "qualify the new owner with a registered fleet id").option("--expected-hash <sha256>", "expected recorded artifact hash; required when applying").option("--expected-revision <sha256>", "expected install manifest revision; required when applying").option("--carry-drift", "accept a file or directory whose runtime differs from the recorded hash; the recorded hash is kept", false).option("--expected-runtime-hash <sha256>", "expected drifted runtime hash; required when applying with --carry-drift").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--profile <name>", "workspace runtime profile (must resolve to one target)").option("--dry-run", "validate all preconditions without writing the manifest", false).action(async (selector, options) => {
|
|
14359
14437
|
if (!options.dryRun && (!options.expectedHash || !options.expectedRevision)) {
|
|
14360
14438
|
throw new Error("Applying an ownership handoff requires --expected-hash and --expected-revision from a reviewed --dry-run.");
|
|
14361
14439
|
}
|
|
14440
|
+
if (Boolean(options.fromWorkspaceRoot) === (options.fromUnknownOwner === true)) {
|
|
14441
|
+
throw new Error("Ownership handoff requires exactly one of --from-workspace-root or --from-unknown-owner.");
|
|
14442
|
+
}
|
|
14362
14443
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
14363
14444
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
14364
14445
|
if (targets.length !== 1) {
|
|
@@ -14390,11 +14471,13 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
14390
14471
|
adapter: adapter.name,
|
|
14391
14472
|
artifactType,
|
|
14392
14473
|
artifactName,
|
|
14393
|
-
fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
|
|
14474
|
+
...options.fromUnknownOwner === true ? { fromUnknownOwner: true } : { fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot) },
|
|
14394
14475
|
toWorkspaceRoot,
|
|
14395
14476
|
toFleetId: options.toFleet,
|
|
14396
14477
|
expectedHash: options.expectedHash,
|
|
14397
14478
|
expectedRevision: options.expectedRevision,
|
|
14479
|
+
carryDrift: options.carryDrift === true,
|
|
14480
|
+
expectedRuntimeHash: options.expectedRuntimeHash,
|
|
14398
14481
|
transport: transportForTarget(target)
|
|
14399
14482
|
};
|
|
14400
14483
|
const result = options.dryRun ? await planArtifactOwnershipHandoff(request) : await applyArtifactOwnershipHandoff(request);
|
|
@@ -14402,6 +14485,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
14402
14485
|
console.log(`Target: ${result.adapter}/${result.installationType ?? "default"} at ${result.targetRoot}`);
|
|
14403
14486
|
console.log(`Path: ${result.path}`);
|
|
14404
14487
|
console.log(`Hash: ${result.artifactHash}`);
|
|
14488
|
+
if (result.drifted) console.log(`Runtime hash: ${result.runtimeHash} (drift carried; the recorded hash is kept)`);
|
|
14405
14489
|
console.log(`Manifest revision: ${result.manifestRevision}`);
|
|
14406
14490
|
console.log(`Owner: ${result.fromOwner} -> ${result.toOwner}`);
|
|
14407
14491
|
});
|
|
@@ -14911,30 +14995,34 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
14911
14995
|
throw teachingInstallError(nameOrSource, error);
|
|
14912
14996
|
}
|
|
14913
14997
|
}
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
if (
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
executePlugins
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
if (
|
|
14998
|
+
const results = await buildGraphPlansForTarget(target, source, { ...targetOptions, scope, extraPackage, reportFormat: outputFormat }, { mode: "install" });
|
|
14999
|
+
try {
|
|
15000
|
+
for (const result of results) {
|
|
15001
|
+
if (!behavior.quiet) console.log(formatGraphPlan(result));
|
|
15002
|
+
if (behavior.apply) {
|
|
15003
|
+
declareMutationPath(result.graphLockPath);
|
|
15004
|
+
if (extraPackage && !targetOptions.onlySource) declareMutationPath(workspaceConfigPath(target.workspaceRoot));
|
|
15005
|
+
const transport = transportForTarget(target);
|
|
15006
|
+
const executePlugins = target.executePlugins ?? targetOptions.executePlugins;
|
|
15007
|
+
await applyCombinedInstallPlan(result.plan, {
|
|
15008
|
+
executePlugins,
|
|
15009
|
+
transport,
|
|
15010
|
+
graphLockDigest: result.graphLockDigest,
|
|
15011
|
+
graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
|
|
15012
|
+
});
|
|
15013
|
+
const reloaded = await reloadRuntimeAfterPluginChanges(result.plan, target, transport, {
|
|
15014
|
+
enabled: target.reloadRuntimes ?? shouldReloadRuntimes(targetOptions),
|
|
15015
|
+
executePlugins
|
|
15016
|
+
});
|
|
15017
|
+
if (!behavior.quiet) {
|
|
15018
|
+
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
15019
|
+
if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
|
|
15020
|
+
}
|
|
14934
15021
|
}
|
|
15022
|
+
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
14935
15023
|
}
|
|
14936
|
-
|
|
14937
|
-
|
|
15024
|
+
} finally {
|
|
15025
|
+
await Promise.all(results.map((result) => rm10(result.bundle.root, { recursive: true, force: true })));
|
|
14938
15026
|
}
|
|
14939
15027
|
if (behavior.apply && extraPackage && !targetOptions.onlySource) {
|
|
14940
15028
|
await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), extraPackage));
|
|
@@ -15385,31 +15473,43 @@ async function runSkillUpdateCommand(skillName, options) {
|
|
|
15385
15473
|
}
|
|
15386
15474
|
async function runConfiguredGraphPackages(target, options, behavior) {
|
|
15387
15475
|
const results = await buildGraphPlansForTarget(target, void 0, options, behavior);
|
|
15388
|
-
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
|
|
15396
|
-
|
|
15397
|
-
|
|
15398
|
-
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15403
|
-
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15476
|
+
try {
|
|
15477
|
+
for (const result of results) {
|
|
15478
|
+
console.log(`${behavior.mode === "update" ? "Update" : "Install"} ${result.plan.adapter} at ${result.plan.targetRoot}:`);
|
|
15479
|
+
console.log(formatGraphPlan(result));
|
|
15480
|
+
if (!options.dryRun) {
|
|
15481
|
+
declareMutationPath(result.graphLockPath);
|
|
15482
|
+
const transport = transportForTarget(target);
|
|
15483
|
+
const executePlugins = target.executePlugins ?? options.executePlugins;
|
|
15484
|
+
await applyCombinedInstallPlan(result.plan, {
|
|
15485
|
+
executePlugins,
|
|
15486
|
+
transport,
|
|
15487
|
+
graphLockDigest: result.graphLockDigest,
|
|
15488
|
+
graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
|
|
15489
|
+
});
|
|
15490
|
+
const reloaded = await reloadRuntimeAfterPluginChanges(result.plan, target, transport, {
|
|
15491
|
+
enabled: target.reloadRuntimes ?? shouldReloadRuntimes(options),
|
|
15492
|
+
executePlugins
|
|
15493
|
+
});
|
|
15494
|
+
console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
|
|
15495
|
+
if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
|
|
15496
|
+
}
|
|
15497
|
+
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
15407
15498
|
}
|
|
15408
|
-
|
|
15409
|
-
|
|
15499
|
+
} finally {
|
|
15500
|
+
await Promise.all(results.map((result) => rm10(result.bundle.root, { recursive: true, force: true })));
|
|
15410
15501
|
}
|
|
15411
15502
|
}
|
|
15412
15503
|
async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
15504
|
+
const bundleRoots = [];
|
|
15505
|
+
try {
|
|
15506
|
+
return await collectGraphPlansForTarget(target, source, options, behavior, bundleRoots);
|
|
15507
|
+
} catch (error) {
|
|
15508
|
+
await Promise.all(bundleRoots.map((root) => rm10(root, { recursive: true, force: true })));
|
|
15509
|
+
throw error;
|
|
15510
|
+
}
|
|
15511
|
+
}
|
|
15512
|
+
async function collectGraphPlansForTarget(target, source, options, behavior, bundleRoots) {
|
|
15413
15513
|
const targetOptions = optionsForResolvedTarget(options, target);
|
|
15414
15514
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
15415
15515
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -15603,6 +15703,7 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
15603
15703
|
expectedFromWorkspaceOwner: targetOptions.expectedFromWorkspaceOwner,
|
|
15604
15704
|
recoverLegacyState: targetOptions.recoverLegacyState
|
|
15605
15705
|
});
|
|
15706
|
+
bundleRoots.push(result.bundle.root);
|
|
15606
15707
|
if ((behavior.mode === "install" || behavior.mode === "update") && scopedRootId) {
|
|
15607
15708
|
const manifest = result.previousManifest;
|
|
15608
15709
|
const previousLock = result.previousGraphLock;
|
|
@@ -16239,7 +16340,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
16239
16340
|
let renderedRoot;
|
|
16240
16341
|
if (remainingGroup && remainingGroup.packages.length > 0) {
|
|
16241
16342
|
const remainingAdapter = await resolveAdapterForTarget(remainingGroup.target, remainingGroup.adapterOptions);
|
|
16242
|
-
const
|
|
16343
|
+
const result = await createGraphSourcePlan({
|
|
16243
16344
|
roots: remainingGroup.packages.map((pkg2) => ({
|
|
16244
16345
|
rootId: pkg2.name,
|
|
16245
16346
|
source: pkg2.source,
|
|
@@ -16271,46 +16372,49 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
16271
16372
|
readOnly: options.dryRun === true,
|
|
16272
16373
|
isTTY: process.stdin.isTTY === true
|
|
16273
16374
|
});
|
|
16274
|
-
remainingGraphPlan =
|
|
16275
|
-
remainingDesired = desiredArtifactsFromGraphBundle(
|
|
16276
|
-
renderedRoot =
|
|
16375
|
+
remainingGraphPlan = result;
|
|
16376
|
+
remainingDesired = desiredArtifactsFromGraphBundle(result.bundle);
|
|
16377
|
+
renderedRoot = result.bundle.root;
|
|
16277
16378
|
}
|
|
16278
|
-
|
|
16279
|
-
|
|
16280
|
-
|
|
16281
|
-
|
|
16282
|
-
|
|
16283
|
-
|
|
16284
|
-
|
|
16285
|
-
|
|
16286
|
-
|
|
16287
|
-
|
|
16288
|
-
|
|
16289
|
-
|
|
16290
|
-
|
|
16291
|
-
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16295
|
-
|
|
16296
|
-
|
|
16297
|
-
|
|
16298
|
-
const result = await uninstall(plan, {
|
|
16299
|
-
dryRun: options.dryRun,
|
|
16300
|
-
force: options.force,
|
|
16301
|
-
keepFiles: options.keepFiles,
|
|
16302
|
-
transport,
|
|
16303
|
-
...graphLockFinalState,
|
|
16304
|
-
workspaceConfig: {
|
|
16305
|
-
path: workspaceConfigPath(target.workspaceRoot),
|
|
16306
|
-
data: { ...config, packages: remaining }
|
|
16379
|
+
try {
|
|
16380
|
+
const uninstallPlan = await createOwnershipUninstallPlan(manifest, remainingDesired, adapter, transport, {
|
|
16381
|
+
graphLockDigest: remainingGraphPlan?.graphLockDigest
|
|
16382
|
+
});
|
|
16383
|
+
const transitionPlan = remainingGraphPlan?.plan;
|
|
16384
|
+
const plan = {
|
|
16385
|
+
...uninstallPlan,
|
|
16386
|
+
stateKey: transitionPlan?.stateKey ?? removedState.stableStateKey,
|
|
16387
|
+
stateMigration: transitionPlan?.stateMigration ?? removedState.migration,
|
|
16388
|
+
targetStateFilePreconditions: transitionPlan?.targetStateFilePreconditions ?? removedState.targetStateFilePreconditions
|
|
16389
|
+
};
|
|
16390
|
+
console.log(`Uninstall ${pkg.name} (${adapter.name} at ${removedTarget.targetRoot}):`);
|
|
16391
|
+
console.log(formatPlan(plan));
|
|
16392
|
+
const graphLockFinalState = remainingGraphPlan ? { graphLock: { path: remainingGraphPlan.graphLockPath, lock: remainingGraphPlan.bundle.graphLock } } : {
|
|
16393
|
+
removeGraphLockPath: removedState.graphLockPath ?? removedState.stableGraphLockPath
|
|
16394
|
+
};
|
|
16395
|
+
if (!options.dryRun) {
|
|
16396
|
+
declareMutationPath(workspaceConfigPath(target.workspaceRoot));
|
|
16397
|
+
const declarativeGraphPath = remainingGraphPlan ? remainingGraphPlan.graphLockPath : removedState.graphLockPath ?? removedState.stableGraphLockPath;
|
|
16398
|
+
declareMutationPath(declarativeGraphPath);
|
|
16307
16399
|
}
|
|
16308
|
-
|
|
16309
|
-
|
|
16310
|
-
|
|
16400
|
+
const result = await uninstall(plan, {
|
|
16401
|
+
dryRun: options.dryRun,
|
|
16402
|
+
force: options.force,
|
|
16403
|
+
keepFiles: options.keepFiles,
|
|
16404
|
+
transport,
|
|
16405
|
+
...graphLockFinalState,
|
|
16406
|
+
workspaceConfig: {
|
|
16407
|
+
path: workspaceConfigPath(target.workspaceRoot),
|
|
16408
|
+
data: { ...config, packages: remaining }
|
|
16409
|
+
}
|
|
16410
|
+
});
|
|
16411
|
+
if (!options.dryRun) {
|
|
16412
|
+
console.log(formatUninstallResult(result));
|
|
16413
|
+
}
|
|
16414
|
+
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
16415
|
+
} finally {
|
|
16416
|
+
if (renderedRoot) await rm10(renderedRoot, { recursive: true, force: true });
|
|
16311
16417
|
}
|
|
16312
|
-
if (renderedRoot) await rm10(renderedRoot, { recursive: true, force: true });
|
|
16313
|
-
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
16314
16418
|
}
|
|
16315
16419
|
}
|
|
16316
16420
|
function graphGroupForPackage(groups, target, pkg, options) {
|
|
@@ -16367,7 +16471,7 @@ function targetFingerprintParts(target, adapter, options, installationType) {
|
|
|
16367
16471
|
};
|
|
16368
16472
|
}
|
|
16369
16473
|
async function withFreshReadOnlyGraphPlan(target, adapter, adapterOptions, installationType, packages, use) {
|
|
16370
|
-
const stage = await
|
|
16474
|
+
const stage = await mkdtemp3(join46(tmpdir4(), "agentwheel-recovery-"));
|
|
16371
16475
|
const priorTmpdir = process.env.TMPDIR;
|
|
16372
16476
|
try {
|
|
16373
16477
|
process.env.TMPDIR = stage;
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
removeApplyJournal,
|
|
19
19
|
rollbackCompletedOperations,
|
|
20
20
|
writeApplyJournal
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import "./chunk-
|
|
21
|
+
} from "./chunk-BHT57MK5.js";
|
|
22
|
+
import "./chunk-BNIF3FUR.js";
|
|
23
23
|
export {
|
|
24
24
|
abortApplyJournal,
|
|
25
25
|
acquireApplyLock,
|
package/openpack.json
CHANGED
package/package.json
CHANGED