@mifune/agro 0.12.1 → 0.12.2
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/agro.js +193 -80
- package/dist/oh.js +193 -80
- package/package.json +1 -1
package/dist/agro.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { dirname as dirname10, join as join14, resolve as
|
|
5
|
+
import { dirname as dirname10, join as join14, resolve as resolve13 } from "node:path";
|
|
6
6
|
import { existsSync as existsSync20, mkdtempSync as mkdtempSync4, readFileSync as readFileSync12, rmSync as rmSync4 } from "node:fs";
|
|
7
7
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
8
8
|
|
|
@@ -230,16 +230,10 @@ function resolveRegistryHome(env = process.env, home = homedir(), warn) {
|
|
|
230
230
|
if (configured !== void 0) return { path: resolve(configured), configured: true };
|
|
231
231
|
return { path: home, configured: false };
|
|
232
232
|
}
|
|
233
|
-
function
|
|
233
|
+
function resolveAgroUserStateHome(env = process.env, home = homedir(), warn) {
|
|
234
234
|
const configured = aliasedEnvValue(env, "HOME", warn);
|
|
235
235
|
if (configured !== void 0) return resolve(configured);
|
|
236
|
-
return join(home, GENERATIONS
|
|
237
|
-
}
|
|
238
|
-
function resolveAgroUserStateHome(env = process.env, home = homedir(), warn) {
|
|
239
|
-
return generationUserStateHome("agro", env, home, warn);
|
|
240
|
-
}
|
|
241
|
-
function resolveLegacyUserStateHome(env = process.env, home = homedir(), warn) {
|
|
242
|
-
return generationUserStateHome("legacy", env, home, warn);
|
|
236
|
+
return join(home, GENERATIONS.agro.userStateDir);
|
|
243
237
|
}
|
|
244
238
|
function resolveUserStateHome(env = process.env, home = homedir(), warn) {
|
|
245
239
|
const configured = aliasedEnvValue(env, "HOME", warn);
|
|
@@ -1970,8 +1964,8 @@ function redact(token) {
|
|
|
1970
1964
|
async function ask(question) {
|
|
1971
1965
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1972
1966
|
try {
|
|
1973
|
-
return await new Promise((
|
|
1974
|
-
rl.question(` ${question} `, (answer) =>
|
|
1967
|
+
return await new Promise((resolve14) => {
|
|
1968
|
+
rl.question(` ${question} `, (answer) => resolve14(answer.trim()));
|
|
1975
1969
|
});
|
|
1976
1970
|
} finally {
|
|
1977
1971
|
rl.close();
|
|
@@ -1983,7 +1977,7 @@ async function askSecret(question) {
|
|
|
1983
1977
|
process.stdin.setRawMode(true);
|
|
1984
1978
|
process.stdin.resume();
|
|
1985
1979
|
const bytes = [];
|
|
1986
|
-
return await new Promise((
|
|
1980
|
+
return await new Promise((resolve14) => {
|
|
1987
1981
|
const cleanup = () => {
|
|
1988
1982
|
try {
|
|
1989
1983
|
process.stdin.setRawMode(false);
|
|
@@ -2001,7 +1995,7 @@ async function askSecret(question) {
|
|
|
2001
1995
|
} else if (byte === 13 || byte === 10) {
|
|
2002
1996
|
cleanup();
|
|
2003
1997
|
process.stdout.write("\n");
|
|
2004
|
-
|
|
1998
|
+
resolve14(Buffer.from(bytes).toString("utf8").trim());
|
|
2005
1999
|
return;
|
|
2006
2000
|
} else if (byte === 127 || byte === 8) {
|
|
2007
2001
|
if (bytes.length > 0) {
|
|
@@ -2051,10 +2045,10 @@ async function askSecretPiped(question) {
|
|
|
2051
2045
|
};
|
|
2052
2046
|
process.on("SIGINT", onSigint);
|
|
2053
2047
|
try {
|
|
2054
|
-
return await new Promise((
|
|
2048
|
+
return await new Promise((resolve14) => {
|
|
2055
2049
|
rl.question(` ${question} `, (answer) => {
|
|
2056
2050
|
process.stdout.write("\n");
|
|
2057
|
-
|
|
2051
|
+
resolve14(answer.trim());
|
|
2058
2052
|
});
|
|
2059
2053
|
});
|
|
2060
2054
|
} finally {
|
|
@@ -4012,7 +4006,7 @@ async function runSandboxList(opts, io) {
|
|
|
4012
4006
|
// src/commands/harness.ts
|
|
4013
4007
|
import { existsSync as existsSync18 } from "node:fs";
|
|
4014
4008
|
import { homedir as homedir2 } from "node:os";
|
|
4015
|
-
import { delimiter as delimiter2, join as join12
|
|
4009
|
+
import { delimiter as delimiter2, join as join12 } from "node:path";
|
|
4016
4010
|
|
|
4017
4011
|
// src/lib/docs.ts
|
|
4018
4012
|
var SOURCE_DOCS_BASE = "https://github.com/mifunedev/agro/blob/main/";
|
|
@@ -4028,24 +4022,35 @@ function sourceDocsUrl(docsPath) {
|
|
|
4028
4022
|
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync11, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
4029
4023
|
import { basename as basename6, dirname as dirname8, isAbsolute as isAbsolute2, join as join10, resolve as resolve10 } from "node:path";
|
|
4030
4024
|
var HOST_CONFIG_MODE = 420;
|
|
4031
|
-
|
|
4032
|
-
|
|
4025
|
+
var HOST_CONFIG_FILE = "config.json";
|
|
4026
|
+
var WORKSPACES_SUBDIR = "workspaces";
|
|
4027
|
+
var DEFAULT_WORKSPACE_NAME = "default";
|
|
4028
|
+
function hostStateHome(env, home) {
|
|
4029
|
+
return resolveAgroUserStateHome(env, home);
|
|
4033
4030
|
}
|
|
4034
|
-
function
|
|
4035
|
-
return join10(
|
|
4031
|
+
function hostConfigPath(env, home) {
|
|
4032
|
+
return join10(hostStateHome(env, home), HOST_CONFIG_FILE);
|
|
4036
4033
|
}
|
|
4037
|
-
function
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4034
|
+
function assertWorkspaceName(name) {
|
|
4035
|
+
if (!SANDBOX_NAME_PATTERN.test(name)) {
|
|
4036
|
+
throw new Error(
|
|
4037
|
+
`invalid workspace name "${name}" \u2014 use lowercase letters, digits and dashes, starting with a letter or digit`
|
|
4038
|
+
);
|
|
4039
|
+
}
|
|
4042
4040
|
}
|
|
4043
|
-
function
|
|
4044
|
-
return
|
|
4041
|
+
function workspacesRoot(env, home) {
|
|
4042
|
+
return join10(hostStateHome(env, home), WORKSPACES_SUBDIR);
|
|
4043
|
+
}
|
|
4044
|
+
function workspaceRoot(name, env, home) {
|
|
4045
|
+
assertWorkspaceName(name);
|
|
4046
|
+
return join10(workspacesRoot(env, home), name);
|
|
4047
|
+
}
|
|
4048
|
+
function defaultHarnessRoot(env, home) {
|
|
4049
|
+
return join10(workspacesRoot(env, home), DEFAULT_WORKSPACE_NAME);
|
|
4045
4050
|
}
|
|
4046
4051
|
function validateHostConfig(value) {
|
|
4047
4052
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4048
|
-
throw new Error(`${
|
|
4053
|
+
throw new Error(`${HOST_CONFIG_FILE}: must contain a JSON object`);
|
|
4049
4054
|
}
|
|
4050
4055
|
const record = value;
|
|
4051
4056
|
if (record.version !== void 0 && record.version !== 1) {
|
|
@@ -4097,8 +4102,8 @@ function validateReceipts(value, field, noun) {
|
|
|
4097
4102
|
}
|
|
4098
4103
|
}
|
|
4099
4104
|
}
|
|
4100
|
-
function readHostConfig(env
|
|
4101
|
-
const path4 =
|
|
4105
|
+
function readHostConfig(env, home) {
|
|
4106
|
+
const path4 = hostConfigPath(env, home);
|
|
4102
4107
|
if (!existsSync16(path4)) return { version: 1 };
|
|
4103
4108
|
let raw;
|
|
4104
4109
|
try {
|
|
@@ -4115,7 +4120,7 @@ function readHostConfig(env = process.env, home) {
|
|
|
4115
4120
|
}
|
|
4116
4121
|
return validateHostConfig(parsed);
|
|
4117
4122
|
}
|
|
4118
|
-
function writeHostConfig(config, env
|
|
4123
|
+
function writeHostConfig(config, env, home) {
|
|
4119
4124
|
const path4 = hostConfigPath(env, home);
|
|
4120
4125
|
const validated = validateHostConfig({ ...config, version: 1 });
|
|
4121
4126
|
const body = `${JSON.stringify(validated, null, 2)}
|
|
@@ -4125,7 +4130,6 @@ function writeHostConfig(config, env = process.env, home) {
|
|
|
4125
4130
|
try {
|
|
4126
4131
|
writeFileSync8(tmp, body, { mode: HOST_CONFIG_MODE, encoding: "utf8" });
|
|
4127
4132
|
renameSync5(tmp, path4);
|
|
4128
|
-
retireLegacyHostConfig(env, home, path4);
|
|
4129
4133
|
} catch (error) {
|
|
4130
4134
|
try {
|
|
4131
4135
|
unlinkSync5(tmp);
|
|
@@ -4134,19 +4138,14 @@ function writeHostConfig(config, env = process.env, home) {
|
|
|
4134
4138
|
throw error;
|
|
4135
4139
|
}
|
|
4136
4140
|
}
|
|
4137
|
-
function resolveHarnessRoot(explicit, env
|
|
4141
|
+
function resolveHarnessRoot(explicit, env, home) {
|
|
4138
4142
|
if (typeof explicit === "string" && explicit !== "") return resolve10(explicit);
|
|
4139
4143
|
const configured = readHostConfig(env, home).harnessRoot;
|
|
4140
4144
|
if (typeof configured === "string" && configured !== "") return resolve10(configured);
|
|
4141
4145
|
return defaultHarnessRoot(env, home);
|
|
4142
4146
|
}
|
|
4143
|
-
function retireLegacyHostConfig(env, home, written) {
|
|
4144
|
-
const legacy = legacyHostConfigPath(env, home);
|
|
4145
|
-
if (legacy === written || !existsSync16(legacy)) return;
|
|
4146
|
-
unlinkSync5(legacy);
|
|
4147
|
-
}
|
|
4148
4147
|
function fieldError2(path4, requirement) {
|
|
4149
|
-
return new Error(`${
|
|
4148
|
+
return new Error(`${HOST_CONFIG_FILE}: ${path4} ${requirement}`);
|
|
4150
4149
|
}
|
|
4151
4150
|
|
|
4152
4151
|
// src/lib/host-workspace.ts
|
|
@@ -4159,6 +4158,40 @@ var IGNORABLE_ENTRIES = [
|
|
|
4159
4158
|
GENERATIONS.agro.configFile,
|
|
4160
4159
|
GENERATIONS.legacy.configFile
|
|
4161
4160
|
];
|
|
4161
|
+
var STATE_HOME_DIRS = [
|
|
4162
|
+
GENERATIONS.legacy.userStateDir,
|
|
4163
|
+
GENERATIONS.agro.userStateDir
|
|
4164
|
+
];
|
|
4165
|
+
function isDirectory2(path4) {
|
|
4166
|
+
return statSync5(path4, { throwIfNoEntry: false })?.isDirectory() === true;
|
|
4167
|
+
}
|
|
4168
|
+
function stateHomes(home) {
|
|
4169
|
+
return STATE_HOME_DIRS.map((name) => join11(home, name));
|
|
4170
|
+
}
|
|
4171
|
+
function stateHomeRefusal(bin, home) {
|
|
4172
|
+
const [legacy, agro] = stateHomes(home);
|
|
4173
|
+
if (isDirectory2(legacy) && isDirectory2(agro)) {
|
|
4174
|
+
return `${bin} harness: the host state home is split \u2014 ${legacy} and ${agro} both exist.
|
|
4175
|
+
Merge them with \`${bin} migrate --home\`, then re-run this command.
|
|
4176
|
+
`;
|
|
4177
|
+
}
|
|
4178
|
+
if (isDirectory2(legacy)) {
|
|
4179
|
+
return `${bin} harness: the host state home is the legacy ${legacy}, and host state now lives in ${agro}.
|
|
4180
|
+
Creating ${agro} beside it would split the two.
|
|
4181
|
+
Migrate first with \`${bin} migrate --home\`, then re-run this command.
|
|
4182
|
+
`;
|
|
4183
|
+
}
|
|
4184
|
+
return void 0;
|
|
4185
|
+
}
|
|
4186
|
+
function stateHomeRootRefusal(bin, root, home) {
|
|
4187
|
+
const target = resolve11(root);
|
|
4188
|
+
const enclosing = stateHomes(home).find((state) => target === state);
|
|
4189
|
+
if (enclosing === void 0) return void 0;
|
|
4190
|
+
return `${bin} harness: the harness root ${target} is the state home ${enclosing} itself.
|
|
4191
|
+
A workspace there collides with AGRO state and blocks every command run from ${home}.
|
|
4192
|
+
Move it out \u2014 for example \`mv ${enclosing} ${join11(home, "agro")}\` \u2014 then re-run this command.
|
|
4193
|
+
`;
|
|
4194
|
+
}
|
|
4162
4195
|
function isIgnorableEntry(name) {
|
|
4163
4196
|
return name.startsWith(".") || IGNORABLE_ENTRIES.includes(name);
|
|
4164
4197
|
}
|
|
@@ -4229,7 +4262,8 @@ var HARNESS_CATALOG = [
|
|
|
4229
4262
|
verifyArgv: ["claude", "--version"],
|
|
4230
4263
|
uninstallArgv: ["npm", "--prefix", HARNESS_PREFIX_TOKEN, "uninstall", "-g", "@anthropic-ai/claude-code"],
|
|
4231
4264
|
docsPath: "docs/harnesses/claude-code.md",
|
|
4232
|
-
kind: "installable"
|
|
4265
|
+
kind: "installable",
|
|
4266
|
+
bypassPermissionsFlag: "--permission-mode bypassPermissions"
|
|
4233
4267
|
},
|
|
4234
4268
|
{
|
|
4235
4269
|
id: "codex",
|
|
@@ -4376,6 +4410,9 @@ function resolveVerifyArgv(entry, prefix) {
|
|
|
4376
4410
|
function resolveUninstallArgv(entry, prefix) {
|
|
4377
4411
|
return entry.uninstallArgv === null ? null : substitute(entry.uninstallArgv, prefix);
|
|
4378
4412
|
}
|
|
4413
|
+
function harnessLaunchCommand(entry) {
|
|
4414
|
+
return entry.bypassPermissionsFlag === void 0 ? entry.binary : `${entry.binary} ${entry.bypassPermissionsFlag}`;
|
|
4415
|
+
}
|
|
4379
4416
|
function harnessBinPath(prefix) {
|
|
4380
4417
|
return `${prefix}/bin`;
|
|
4381
4418
|
}
|
|
@@ -4443,7 +4480,7 @@ function stateOf(entry, installed, location) {
|
|
|
4443
4480
|
function probeableHost(env, home) {
|
|
4444
4481
|
let root;
|
|
4445
4482
|
try {
|
|
4446
|
-
root = resolveHarnessRoot(void 0, env);
|
|
4483
|
+
root = resolveHarnessRoot(void 0, env, home);
|
|
4447
4484
|
} catch {
|
|
4448
4485
|
return void 0;
|
|
4449
4486
|
}
|
|
@@ -4578,7 +4615,13 @@ function isInteractive(opts) {
|
|
|
4578
4615
|
async function installOnHost(entry, opts, io, run, status) {
|
|
4579
4616
|
const bin = opts.bin;
|
|
4580
4617
|
const env = opts.env ?? process.env;
|
|
4581
|
-
const
|
|
4618
|
+
const home = homeOf(opts);
|
|
4619
|
+
const flagged = opts.host === true || opts.path !== void 0 || opts.workspace !== void 0;
|
|
4620
|
+
const generation = stateHomeRefusal(bin, home);
|
|
4621
|
+
if (generation !== void 0) {
|
|
4622
|
+
io.stderr(generation);
|
|
4623
|
+
return 1;
|
|
4624
|
+
}
|
|
4582
4625
|
if (!flagged && !isInteractive(opts)) {
|
|
4583
4626
|
io.stderr(
|
|
4584
4627
|
sandboxRefusal(bin, status) + `Or install on the host with \`${bin} harness install ${entry.id} --host\`.
|
|
@@ -4593,15 +4636,17 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4593
4636
|
}
|
|
4594
4637
|
let root;
|
|
4595
4638
|
let recorded;
|
|
4639
|
+
let explicit;
|
|
4596
4640
|
try {
|
|
4597
|
-
|
|
4598
|
-
|
|
4641
|
+
explicit = opts.workspace !== void 0 ? workspaceRoot(opts.workspace, env, home) : opts.path;
|
|
4642
|
+
recorded = readHostConfig(env, home).harnessRoot;
|
|
4643
|
+
root = resolveHarnessRoot(explicit, env, home);
|
|
4599
4644
|
} catch (err) {
|
|
4600
4645
|
io.stderr(`${bin} harness: ${messageOf(err)}
|
|
4601
4646
|
`);
|
|
4602
4647
|
return 1;
|
|
4603
4648
|
}
|
|
4604
|
-
const sticky =
|
|
4649
|
+
const sticky = explicit === void 0 && recorded !== void 0 && recorded !== "";
|
|
4605
4650
|
if (!flagged) {
|
|
4606
4651
|
const ask2 = io.ask ?? ask;
|
|
4607
4652
|
const answer = (await ask2(
|
|
@@ -4612,12 +4657,23 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4612
4657
|
return 1;
|
|
4613
4658
|
}
|
|
4614
4659
|
if (!sticky) {
|
|
4615
|
-
const chosen = (await ask2(`
|
|
4616
|
-
|
|
4660
|
+
const chosen = (await ask2(`Workspace name [${DEFAULT_WORKSPACE_NAME}]:`)).trim();
|
|
4661
|
+
try {
|
|
4662
|
+
root = workspaceRoot(chosen === "" ? DEFAULT_WORKSPACE_NAME : chosen, env, home);
|
|
4663
|
+
} catch (err) {
|
|
4664
|
+
io.stderr(`${bin} harness: ${messageOf(err)}
|
|
4665
|
+
`);
|
|
4666
|
+
return 1;
|
|
4667
|
+
}
|
|
4617
4668
|
}
|
|
4618
4669
|
}
|
|
4619
4670
|
if (sticky) io.stdout(`using the recorded harness root ${root}
|
|
4620
4671
|
`);
|
|
4672
|
+
const nested = stateHomeRootRefusal(bin, root, home);
|
|
4673
|
+
if (nested !== void 0) {
|
|
4674
|
+
io.stderr(nested);
|
|
4675
|
+
return 1;
|
|
4676
|
+
}
|
|
4621
4677
|
try {
|
|
4622
4678
|
if (!existsSync18(join12(root, ".git"))) io.stdout(`cloning ${AGRO_REPO_URL} into ${root}\u2026
|
|
4623
4679
|
`);
|
|
@@ -4633,8 +4689,21 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4633
4689
|
`);
|
|
4634
4690
|
return 1;
|
|
4635
4691
|
}
|
|
4636
|
-
const prefix = hostPrefix(
|
|
4692
|
+
const prefix = hostPrefix(home);
|
|
4637
4693
|
const target = hostTargetFor(root, prefix, run, env);
|
|
4694
|
+
const linked = await target.exec({
|
|
4695
|
+
argv: remoteControlDirScript(root, "scripts/link-providers.sh", ["--init"]),
|
|
4696
|
+
env: aliasedEnvPair("PROJECT_ROOT", root),
|
|
4697
|
+
stdio: "inherit"
|
|
4698
|
+
});
|
|
4699
|
+
if (linked.exitCode !== 0) {
|
|
4700
|
+
io.stderr(
|
|
4701
|
+
`${bin} harness: could not link provider skills in ${root} (exit ${linked.exitCode}); nothing was installed.
|
|
4702
|
+
Run: bash ${root}/.agro/scripts/link-providers.sh --init
|
|
4703
|
+
`
|
|
4704
|
+
);
|
|
4705
|
+
return 1;
|
|
4706
|
+
}
|
|
4638
4707
|
const hermes = entry.id === "hermes";
|
|
4639
4708
|
const installEnv = hermes ? {
|
|
4640
4709
|
...aliasedEnvPair("PROJECT_ROOT", root),
|
|
@@ -4678,14 +4747,15 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4678
4747
|
workspaceRoot: root
|
|
4679
4748
|
};
|
|
4680
4749
|
try {
|
|
4681
|
-
const config = readHostConfig(env);
|
|
4750
|
+
const config = readHostConfig(env, home);
|
|
4682
4751
|
writeHostConfig(
|
|
4683
4752
|
{
|
|
4684
4753
|
...config,
|
|
4685
4754
|
harnessRoot: root,
|
|
4686
4755
|
hostHarnesses: { ...config.hostHarnesses ?? {}, [entry.id]: receipt }
|
|
4687
4756
|
},
|
|
4688
|
-
env
|
|
4757
|
+
env,
|
|
4758
|
+
home
|
|
4689
4759
|
);
|
|
4690
4760
|
} catch (err) {
|
|
4691
4761
|
io.stderr(`${bin} harness: could not record the install: ${messageOf(err)}
|
|
@@ -4698,7 +4768,7 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4698
4768
|
io.stdout(`Add this line to your shell profile: export PATH="${harnessBinPath(prefix)}:$PATH"
|
|
4699
4769
|
`);
|
|
4700
4770
|
}
|
|
4701
|
-
io.stdout(`Run ${entry.
|
|
4771
|
+
io.stdout(`Run ${entry.title} in the AGRO workspace: cd ${root} && ${harnessLaunchCommand(entry)}
|
|
4702
4772
|
`);
|
|
4703
4773
|
return 0;
|
|
4704
4774
|
}
|
|
@@ -4742,7 +4812,13 @@ async function removeHarness(entry, target, prefix, user, opts, io) {
|
|
|
4742
4812
|
async function uninstallOnHost(entry, opts, io, run) {
|
|
4743
4813
|
const bin = opts.bin;
|
|
4744
4814
|
const env = opts.env ?? process.env;
|
|
4745
|
-
const
|
|
4815
|
+
const home = homeOf(opts);
|
|
4816
|
+
const computed = hostPrefix(home);
|
|
4817
|
+
const generation = stateHomeRefusal(bin, home);
|
|
4818
|
+
if (generation !== void 0) {
|
|
4819
|
+
io.stderr(generation);
|
|
4820
|
+
return 1;
|
|
4821
|
+
}
|
|
4746
4822
|
if (resolveUninstallArgv(entry, computed) === null) {
|
|
4747
4823
|
io.stdout(`${entry.id}: nothing to remove \u2014 npx fetches it at each run
|
|
4748
4824
|
`);
|
|
@@ -4751,13 +4827,18 @@ async function uninstallOnHost(entry, opts, io, run) {
|
|
|
4751
4827
|
let config;
|
|
4752
4828
|
let workspace;
|
|
4753
4829
|
try {
|
|
4754
|
-
config = readHostConfig(env);
|
|
4755
|
-
workspace = resolveHarnessRoot(void 0, env);
|
|
4830
|
+
config = readHostConfig(env, home);
|
|
4831
|
+
workspace = resolveHarnessRoot(void 0, env, home);
|
|
4756
4832
|
} catch (err) {
|
|
4757
4833
|
io.stderr(`${bin} harness: ${messageOf(err)}
|
|
4758
4834
|
`);
|
|
4759
4835
|
return 1;
|
|
4760
4836
|
}
|
|
4837
|
+
const nested = stateHomeRootRefusal(bin, workspace, home);
|
|
4838
|
+
if (nested !== void 0) {
|
|
4839
|
+
io.stderr(nested);
|
|
4840
|
+
return 1;
|
|
4841
|
+
}
|
|
4761
4842
|
const receipt = config.hostHarnesses?.[entry.id];
|
|
4762
4843
|
if (receipt === void 0 && opts.force !== true) {
|
|
4763
4844
|
io.stderr(
|
|
@@ -4774,7 +4855,7 @@ Removing it could delete a harness you installed yourself. Re-run with \`--force
|
|
|
4774
4855
|
const remaining = { ...config.hostHarnesses ?? {} };
|
|
4775
4856
|
delete remaining[entry.id];
|
|
4776
4857
|
try {
|
|
4777
|
-
writeHostConfig({ ...config, hostHarnesses: remaining }, env);
|
|
4858
|
+
writeHostConfig({ ...config, hostHarnesses: remaining }, env, home);
|
|
4778
4859
|
} catch (err) {
|
|
4779
4860
|
io.stderr(`${bin} harness: could not clear the install record: ${messageOf(err)}
|
|
4780
4861
|
`);
|
|
@@ -4859,7 +4940,7 @@ async function runHarnessInstall(name, opts, io) {
|
|
|
4859
4940
|
// src/commands/tool.ts
|
|
4860
4941
|
import { existsSync as existsSync19 } from "node:fs";
|
|
4861
4942
|
import { homedir as homedir3 } from "node:os";
|
|
4862
|
-
import { delimiter as delimiter3, join as join13, resolve as
|
|
4943
|
+
import { delimiter as delimiter3, join as join13, resolve as resolve12 } from "node:path";
|
|
4863
4944
|
|
|
4864
4945
|
// src/lib/tools/catalog.ts
|
|
4865
4946
|
var TOOLS_DOC = "docs/installation.md";
|
|
@@ -5145,7 +5226,7 @@ function rowOf(entry, installed, version, location) {
|
|
|
5145
5226
|
function probeableHost2(env, home) {
|
|
5146
5227
|
let root;
|
|
5147
5228
|
try {
|
|
5148
|
-
root = resolveHarnessRoot(void 0, env);
|
|
5229
|
+
root = resolveHarnessRoot(void 0, env, home);
|
|
5149
5230
|
} catch {
|
|
5150
5231
|
return void 0;
|
|
5151
5232
|
}
|
|
@@ -5293,6 +5374,7 @@ ${hostCapableToolIds().map((t) => ` ${bin} tool install ${t} --host`).join("\n"
|
|
|
5293
5374
|
async function installOnHost2(entry, opts, io, run, status) {
|
|
5294
5375
|
const bin = opts.bin;
|
|
5295
5376
|
const env = opts.env ?? process.env;
|
|
5377
|
+
const home = homeOf2(opts);
|
|
5296
5378
|
const flagged = opts.host === true || opts.path !== void 0;
|
|
5297
5379
|
if (!entry.hostCapable) {
|
|
5298
5380
|
io.stderr(sandboxRefusal2(bin, status));
|
|
@@ -5326,8 +5408,8 @@ Every tool installer in the catalog is Debian-specific. Install ${entry.id} with
|
|
|
5326
5408
|
let root;
|
|
5327
5409
|
let recorded;
|
|
5328
5410
|
try {
|
|
5329
|
-
recorded = readHostConfig(env).harnessRoot;
|
|
5330
|
-
root = resolveHarnessRoot(opts.path, env);
|
|
5411
|
+
recorded = readHostConfig(env, home).harnessRoot;
|
|
5412
|
+
root = resolveHarnessRoot(opts.path, env, home);
|
|
5331
5413
|
} catch (err) {
|
|
5332
5414
|
io.stderr(`${bin} tool: ${messageOf2(err)}
|
|
5333
5415
|
`);
|
|
@@ -5345,7 +5427,7 @@ Every tool installer in the catalog is Debian-specific. Install ${entry.id} with
|
|
|
5345
5427
|
}
|
|
5346
5428
|
if (!sticky) {
|
|
5347
5429
|
const chosen = (await ask2(`Harness root [${root}]:`)).trim();
|
|
5348
|
-
if (chosen !== "") root =
|
|
5430
|
+
if (chosen !== "") root = resolve12(chosen);
|
|
5349
5431
|
}
|
|
5350
5432
|
}
|
|
5351
5433
|
if (sticky) io.stdout(`using the recorded harness root ${root}
|
|
@@ -5365,7 +5447,7 @@ Every tool installer in the catalog is Debian-specific. Install ${entry.id} with
|
|
|
5365
5447
|
`);
|
|
5366
5448
|
return 1;
|
|
5367
5449
|
}
|
|
5368
|
-
const prefix = hostPrefix2(
|
|
5450
|
+
const prefix = hostPrefix2(home);
|
|
5369
5451
|
const installEnv = { NPM_USER_PREFIX: prefix };
|
|
5370
5452
|
const target = hostTargetFor2(root, prefix, run, env);
|
|
5371
5453
|
if (await probeInstalled2(target, entry, void 0, installEnv) === true) {
|
|
@@ -5394,14 +5476,15 @@ Every tool installer in the catalog is Debian-specific. Install ${entry.id} with
|
|
|
5394
5476
|
workspaceRoot: root
|
|
5395
5477
|
};
|
|
5396
5478
|
try {
|
|
5397
|
-
const config = readHostConfig(env);
|
|
5479
|
+
const config = readHostConfig(env, home);
|
|
5398
5480
|
writeHostConfig(
|
|
5399
5481
|
{
|
|
5400
5482
|
...config,
|
|
5401
5483
|
harnessRoot: root,
|
|
5402
5484
|
hostTools: { ...config.hostTools ?? {}, [entry.id]: receipt }
|
|
5403
5485
|
},
|
|
5404
|
-
env
|
|
5486
|
+
env,
|
|
5487
|
+
home
|
|
5405
5488
|
);
|
|
5406
5489
|
} catch (err) {
|
|
5407
5490
|
io.stderr(`${bin} tool: could not record the install: ${messageOf2(err)}
|
|
@@ -5502,12 +5585,13 @@ async function removeTool(entry, target, prefix, user, opts, io) {
|
|
|
5502
5585
|
async function uninstallOnHost2(entry, opts, io, run) {
|
|
5503
5586
|
const bin = opts.bin;
|
|
5504
5587
|
const env = opts.env ?? process.env;
|
|
5505
|
-
const
|
|
5588
|
+
const home = homeOf2(opts);
|
|
5589
|
+
const computed = hostPrefix2(home);
|
|
5506
5590
|
let config;
|
|
5507
5591
|
let workspace;
|
|
5508
5592
|
try {
|
|
5509
|
-
config = readHostConfig(env);
|
|
5510
|
-
workspace = resolveHarnessRoot(void 0, env);
|
|
5593
|
+
config = readHostConfig(env, home);
|
|
5594
|
+
workspace = resolveHarnessRoot(void 0, env, home);
|
|
5511
5595
|
} catch (err) {
|
|
5512
5596
|
io.stderr(`${bin} tool: ${messageOf2(err)}
|
|
5513
5597
|
`);
|
|
@@ -5529,7 +5613,7 @@ Removing it could delete a tool you installed yourself. Re-run with \`--force\`
|
|
|
5529
5613
|
const remaining = { ...config.hostTools ?? {} };
|
|
5530
5614
|
delete remaining[entry.id];
|
|
5531
5615
|
try {
|
|
5532
|
-
writeHostConfig({ ...config, hostTools: remaining }, env);
|
|
5616
|
+
writeHostConfig({ ...config, hostTools: remaining }, env, home);
|
|
5533
5617
|
} catch (err) {
|
|
5534
5618
|
io.stderr(`${bin} tool: could not clear the install record: ${messageOf2(err)}
|
|
5535
5619
|
`);
|
|
@@ -5624,8 +5708,8 @@ function fetchRemoteSource(opts) {
|
|
|
5624
5708
|
}
|
|
5625
5709
|
|
|
5626
5710
|
// src/cli.ts
|
|
5627
|
-
var VERSION = true ? "0.12.
|
|
5628
|
-
var DEFAULT_SOURCE_OH_DIR =
|
|
5711
|
+
var VERSION = true ? "0.12.2" : "0.0.0-dev";
|
|
5712
|
+
var DEFAULT_SOURCE_OH_DIR = resolve13(
|
|
5629
5713
|
dirname10(fileURLToPath(import.meta.url)),
|
|
5630
5714
|
"../.."
|
|
5631
5715
|
);
|
|
@@ -5862,10 +5946,11 @@ Usage:
|
|
|
5862
5946
|
|
|
5863
5947
|
\`install\` and \`uninstall\` act on the running sandbox. When no sandbox is
|
|
5864
5948
|
reachable they act on the host. A host install clones the AGRO workspace into the
|
|
5865
|
-
harness root, then installs the harness into \`~/.local\`.
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5949
|
+
harness root, then installs the harness into \`~/.local\`. The default root is the
|
|
5950
|
+
workspace registry entry \`~/.agro/workspaces/default\`; an interactive run asks for
|
|
5951
|
+
the workspace name. \`--workspace <name>\` chooses that entry and \`--path <dir>\` a
|
|
5952
|
+
directory outside the registry. A successful install records the root as
|
|
5953
|
+
\`harnessRoot\` in \`~/.agro/config.json\`. The install prefix is always \`~/.local\`.
|
|
5869
5954
|
|
|
5870
5955
|
On the host, \`uninstall\` removes only the harness \`install\` recorded, from the
|
|
5871
5956
|
prefix in that record. Without a record it refuses, and \`--force\` overrides. In
|
|
@@ -5874,7 +5959,8 @@ the sandbox it needs no record. See \`docs/harnesses/overview.md\`.
|
|
|
5874
5959
|
Flags:
|
|
5875
5960
|
--json Machine-readable output (list/status)
|
|
5876
5961
|
--host Install on the host when the sandbox is not running (install)
|
|
5877
|
-
--
|
|
5962
|
+
--workspace <name> Workspace registry entry to clone into; implies --host (install)
|
|
5963
|
+
--path <dir> Harness root outside the registry; implies --host (install)
|
|
5878
5964
|
--force Remove without a recorded host install (uninstall)
|
|
5879
5965
|
|
|
5880
5966
|
Harnesses:
|
|
@@ -5930,8 +6016,8 @@ only on these tools:
|
|
|
5930
6016
|
${hostCapableToolIds().map((t) => ` ${t}`).join("\n")}
|
|
5931
6017
|
|
|
5932
6018
|
A host install clones the AGRO workspace into the harness root \u2014 \`--path <dir>\`,
|
|
5933
|
-
then \`harnessRoot\` in
|
|
5934
|
-
\`~/.local\`. It records the install as \`hostTools\` in that same file. On the host,
|
|
6019
|
+
then \`harnessRoot\` in \`~/.agro/config.json\`, then \`~/.agro/workspaces/default\` \u2014 and
|
|
6020
|
+
installs into \`~/.local\`. It records the install as \`hostTools\` in that same file. On the host,
|
|
5935
6021
|
\`uninstall\` removes only what \`install\` recorded, from the prefix in that record.
|
|
5936
6022
|
Without a record it refuses, and \`--force\` overrides.
|
|
5937
6023
|
|
|
@@ -6331,11 +6417,16 @@ function parseHarnessArgs(rest, bin = LEGACY_PRODUCT.bin) {
|
|
|
6331
6417
|
}
|
|
6332
6418
|
const positionals = [];
|
|
6333
6419
|
let expectPath = false;
|
|
6420
|
+
let expectWorkspace = false;
|
|
6334
6421
|
for (const token of rest) {
|
|
6335
6422
|
if (expectPath) {
|
|
6336
6423
|
expectPath = false;
|
|
6337
6424
|
args.path = token;
|
|
6338
6425
|
args.host = true;
|
|
6426
|
+
} else if (expectWorkspace) {
|
|
6427
|
+
expectWorkspace = false;
|
|
6428
|
+
args.workspace = token;
|
|
6429
|
+
args.host = true;
|
|
6339
6430
|
} else if (token === "--json") {
|
|
6340
6431
|
args.json = true;
|
|
6341
6432
|
} else if (token === "--host") {
|
|
@@ -6347,6 +6438,11 @@ function parseHarnessArgs(rest, bin = LEGACY_PRODUCT.bin) {
|
|
|
6347
6438
|
} else if (token.startsWith("--path=")) {
|
|
6348
6439
|
args.path = token.slice("--path=".length);
|
|
6349
6440
|
args.host = true;
|
|
6441
|
+
} else if (token === "--workspace") {
|
|
6442
|
+
expectWorkspace = true;
|
|
6443
|
+
} else if (token.startsWith("--workspace=")) {
|
|
6444
|
+
args.workspace = token.slice("--workspace=".length);
|
|
6445
|
+
args.host = true;
|
|
6350
6446
|
} else if (token.startsWith("-")) {
|
|
6351
6447
|
return { ok: false, error: `${bin} harness: unknown flag "${token}"` };
|
|
6352
6448
|
} else {
|
|
@@ -6356,6 +6452,15 @@ function parseHarnessArgs(rest, bin = LEGACY_PRODUCT.bin) {
|
|
|
6356
6452
|
if (expectPath || args.path === "") {
|
|
6357
6453
|
return { ok: false, error: `${bin} harness: --path requires a directory` };
|
|
6358
6454
|
}
|
|
6455
|
+
if (expectWorkspace || args.workspace === "") {
|
|
6456
|
+
return { ok: false, error: `${bin} harness: --workspace requires a name` };
|
|
6457
|
+
}
|
|
6458
|
+
if (args.path !== void 0 && args.workspace !== void 0) {
|
|
6459
|
+
return {
|
|
6460
|
+
ok: false,
|
|
6461
|
+
error: `${bin} harness: --workspace names a registry entry and --path names a directory \u2014 pass one, not both`
|
|
6462
|
+
};
|
|
6463
|
+
}
|
|
6359
6464
|
const [sub, name, ...extra] = positionals;
|
|
6360
6465
|
if (sub !== "list" && sub !== "install" && sub !== "status" && sub !== "uninstall") {
|
|
6361
6466
|
return {
|
|
@@ -6374,7 +6479,10 @@ function parseHarnessArgs(rest, bin = LEGACY_PRODUCT.bin) {
|
|
|
6374
6479
|
return { ok: false, error: `${bin} harness list: unexpected argument "${name}"` };
|
|
6375
6480
|
}
|
|
6376
6481
|
if (sub !== "install" && args.host) {
|
|
6377
|
-
return {
|
|
6482
|
+
return {
|
|
6483
|
+
ok: false,
|
|
6484
|
+
error: `${bin} harness ${sub}: --host, --workspace and --path apply to install only`
|
|
6485
|
+
};
|
|
6378
6486
|
}
|
|
6379
6487
|
if (sub !== "uninstall" && args.force) {
|
|
6380
6488
|
return { ok: false, error: `${bin} harness ${sub}: --force applies to uninstall only` };
|
|
@@ -6449,13 +6557,13 @@ function bundledPayloadExists(bundled, exists = existsSync20) {
|
|
|
6449
6557
|
function resolveUpdateSource(args, bundled, bin = LEGACY_PRODUCT.bin) {
|
|
6450
6558
|
const exists = bundled.exists ?? existsSync20;
|
|
6451
6559
|
if (args.fromDir !== void 0) {
|
|
6452
|
-
return { kind: "local", fromDir:
|
|
6560
|
+
return { kind: "local", fromDir: resolve13(args.fromDir) };
|
|
6453
6561
|
}
|
|
6454
6562
|
if (args.fromRemote) {
|
|
6455
6563
|
return { kind: "remote", ref: args.ref };
|
|
6456
6564
|
}
|
|
6457
6565
|
if (bundledPayloadExists(bundled, exists)) {
|
|
6458
|
-
return { kind: "local", fromDir:
|
|
6566
|
+
return { kind: "local", fromDir: resolve13(bundled.sourceOhDir, "..") };
|
|
6459
6567
|
}
|
|
6460
6568
|
return {
|
|
6461
6569
|
kind: "remote",
|
|
@@ -6747,7 +6855,12 @@ async function main(argv) {
|
|
|
6747
6855
|
}
|
|
6748
6856
|
return await runHarnessInstall(
|
|
6749
6857
|
a.name,
|
|
6750
|
-
{
|
|
6858
|
+
{
|
|
6859
|
+
bin,
|
|
6860
|
+
host: a.host,
|
|
6861
|
+
...a.path !== void 0 ? { path: a.path } : {},
|
|
6862
|
+
...a.workspace !== void 0 ? { workspace: a.workspace } : {}
|
|
6863
|
+
},
|
|
6751
6864
|
io
|
|
6752
6865
|
);
|
|
6753
6866
|
}
|
package/dist/oh.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { dirname as dirname10, join as join14, resolve as
|
|
5
|
+
import { dirname as dirname10, join as join14, resolve as resolve13 } from "node:path";
|
|
6
6
|
import { existsSync as existsSync20, mkdtempSync as mkdtempSync4, readFileSync as readFileSync12, rmSync as rmSync4 } from "node:fs";
|
|
7
7
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
8
8
|
|
|
@@ -230,16 +230,10 @@ function resolveRegistryHome(env = process.env, home = homedir(), warn) {
|
|
|
230
230
|
if (configured !== void 0) return { path: resolve(configured), configured: true };
|
|
231
231
|
return { path: home, configured: false };
|
|
232
232
|
}
|
|
233
|
-
function
|
|
233
|
+
function resolveAgroUserStateHome(env = process.env, home = homedir(), warn) {
|
|
234
234
|
const configured = aliasedEnvValue(env, "HOME", warn);
|
|
235
235
|
if (configured !== void 0) return resolve(configured);
|
|
236
|
-
return join(home, GENERATIONS
|
|
237
|
-
}
|
|
238
|
-
function resolveAgroUserStateHome(env = process.env, home = homedir(), warn) {
|
|
239
|
-
return generationUserStateHome("agro", env, home, warn);
|
|
240
|
-
}
|
|
241
|
-
function resolveLegacyUserStateHome(env = process.env, home = homedir(), warn) {
|
|
242
|
-
return generationUserStateHome("legacy", env, home, warn);
|
|
236
|
+
return join(home, GENERATIONS.agro.userStateDir);
|
|
243
237
|
}
|
|
244
238
|
function resolveUserStateHome(env = process.env, home = homedir(), warn) {
|
|
245
239
|
const configured = aliasedEnvValue(env, "HOME", warn);
|
|
@@ -1970,8 +1964,8 @@ function redact(token) {
|
|
|
1970
1964
|
async function ask(question) {
|
|
1971
1965
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1972
1966
|
try {
|
|
1973
|
-
return await new Promise((
|
|
1974
|
-
rl.question(` ${question} `, (answer) =>
|
|
1967
|
+
return await new Promise((resolve14) => {
|
|
1968
|
+
rl.question(` ${question} `, (answer) => resolve14(answer.trim()));
|
|
1975
1969
|
});
|
|
1976
1970
|
} finally {
|
|
1977
1971
|
rl.close();
|
|
@@ -1983,7 +1977,7 @@ async function askSecret(question) {
|
|
|
1983
1977
|
process.stdin.setRawMode(true);
|
|
1984
1978
|
process.stdin.resume();
|
|
1985
1979
|
const bytes = [];
|
|
1986
|
-
return await new Promise((
|
|
1980
|
+
return await new Promise((resolve14) => {
|
|
1987
1981
|
const cleanup = () => {
|
|
1988
1982
|
try {
|
|
1989
1983
|
process.stdin.setRawMode(false);
|
|
@@ -2001,7 +1995,7 @@ async function askSecret(question) {
|
|
|
2001
1995
|
} else if (byte === 13 || byte === 10) {
|
|
2002
1996
|
cleanup();
|
|
2003
1997
|
process.stdout.write("\n");
|
|
2004
|
-
|
|
1998
|
+
resolve14(Buffer.from(bytes).toString("utf8").trim());
|
|
2005
1999
|
return;
|
|
2006
2000
|
} else if (byte === 127 || byte === 8) {
|
|
2007
2001
|
if (bytes.length > 0) {
|
|
@@ -2051,10 +2045,10 @@ async function askSecretPiped(question) {
|
|
|
2051
2045
|
};
|
|
2052
2046
|
process.on("SIGINT", onSigint);
|
|
2053
2047
|
try {
|
|
2054
|
-
return await new Promise((
|
|
2048
|
+
return await new Promise((resolve14) => {
|
|
2055
2049
|
rl.question(` ${question} `, (answer) => {
|
|
2056
2050
|
process.stdout.write("\n");
|
|
2057
|
-
|
|
2051
|
+
resolve14(answer.trim());
|
|
2058
2052
|
});
|
|
2059
2053
|
});
|
|
2060
2054
|
} finally {
|
|
@@ -4012,7 +4006,7 @@ async function runSandboxList(opts, io) {
|
|
|
4012
4006
|
// src/commands/harness.ts
|
|
4013
4007
|
import { existsSync as existsSync18 } from "node:fs";
|
|
4014
4008
|
import { homedir as homedir2 } from "node:os";
|
|
4015
|
-
import { delimiter as delimiter2, join as join12
|
|
4009
|
+
import { delimiter as delimiter2, join as join12 } from "node:path";
|
|
4016
4010
|
|
|
4017
4011
|
// src/lib/docs.ts
|
|
4018
4012
|
var SOURCE_DOCS_BASE = "https://github.com/mifunedev/agro/blob/main/";
|
|
@@ -4028,24 +4022,35 @@ function sourceDocsUrl(docsPath) {
|
|
|
4028
4022
|
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync11, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
4029
4023
|
import { basename as basename6, dirname as dirname8, isAbsolute as isAbsolute2, join as join10, resolve as resolve10 } from "node:path";
|
|
4030
4024
|
var HOST_CONFIG_MODE = 420;
|
|
4031
|
-
|
|
4032
|
-
|
|
4025
|
+
var HOST_CONFIG_FILE = "config.json";
|
|
4026
|
+
var WORKSPACES_SUBDIR = "workspaces";
|
|
4027
|
+
var DEFAULT_WORKSPACE_NAME = "default";
|
|
4028
|
+
function hostStateHome(env, home) {
|
|
4029
|
+
return resolveAgroUserStateHome(env, home);
|
|
4033
4030
|
}
|
|
4034
|
-
function
|
|
4035
|
-
return join10(
|
|
4031
|
+
function hostConfigPath(env, home) {
|
|
4032
|
+
return join10(hostStateHome(env, home), HOST_CONFIG_FILE);
|
|
4036
4033
|
}
|
|
4037
|
-
function
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4034
|
+
function assertWorkspaceName(name) {
|
|
4035
|
+
if (!SANDBOX_NAME_PATTERN.test(name)) {
|
|
4036
|
+
throw new Error(
|
|
4037
|
+
`invalid workspace name "${name}" \u2014 use lowercase letters, digits and dashes, starting with a letter or digit`
|
|
4038
|
+
);
|
|
4039
|
+
}
|
|
4042
4040
|
}
|
|
4043
|
-
function
|
|
4044
|
-
return
|
|
4041
|
+
function workspacesRoot(env, home) {
|
|
4042
|
+
return join10(hostStateHome(env, home), WORKSPACES_SUBDIR);
|
|
4043
|
+
}
|
|
4044
|
+
function workspaceRoot(name, env, home) {
|
|
4045
|
+
assertWorkspaceName(name);
|
|
4046
|
+
return join10(workspacesRoot(env, home), name);
|
|
4047
|
+
}
|
|
4048
|
+
function defaultHarnessRoot(env, home) {
|
|
4049
|
+
return join10(workspacesRoot(env, home), DEFAULT_WORKSPACE_NAME);
|
|
4045
4050
|
}
|
|
4046
4051
|
function validateHostConfig(value) {
|
|
4047
4052
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4048
|
-
throw new Error(`${
|
|
4053
|
+
throw new Error(`${HOST_CONFIG_FILE}: must contain a JSON object`);
|
|
4049
4054
|
}
|
|
4050
4055
|
const record = value;
|
|
4051
4056
|
if (record.version !== void 0 && record.version !== 1) {
|
|
@@ -4097,8 +4102,8 @@ function validateReceipts(value, field, noun) {
|
|
|
4097
4102
|
}
|
|
4098
4103
|
}
|
|
4099
4104
|
}
|
|
4100
|
-
function readHostConfig(env
|
|
4101
|
-
const path4 =
|
|
4105
|
+
function readHostConfig(env, home) {
|
|
4106
|
+
const path4 = hostConfigPath(env, home);
|
|
4102
4107
|
if (!existsSync16(path4)) return { version: 1 };
|
|
4103
4108
|
let raw;
|
|
4104
4109
|
try {
|
|
@@ -4115,7 +4120,7 @@ function readHostConfig(env = process.env, home) {
|
|
|
4115
4120
|
}
|
|
4116
4121
|
return validateHostConfig(parsed);
|
|
4117
4122
|
}
|
|
4118
|
-
function writeHostConfig(config, env
|
|
4123
|
+
function writeHostConfig(config, env, home) {
|
|
4119
4124
|
const path4 = hostConfigPath(env, home);
|
|
4120
4125
|
const validated = validateHostConfig({ ...config, version: 1 });
|
|
4121
4126
|
const body = `${JSON.stringify(validated, null, 2)}
|
|
@@ -4125,7 +4130,6 @@ function writeHostConfig(config, env = process.env, home) {
|
|
|
4125
4130
|
try {
|
|
4126
4131
|
writeFileSync8(tmp, body, { mode: HOST_CONFIG_MODE, encoding: "utf8" });
|
|
4127
4132
|
renameSync5(tmp, path4);
|
|
4128
|
-
retireLegacyHostConfig(env, home, path4);
|
|
4129
4133
|
} catch (error) {
|
|
4130
4134
|
try {
|
|
4131
4135
|
unlinkSync5(tmp);
|
|
@@ -4134,19 +4138,14 @@ function writeHostConfig(config, env = process.env, home) {
|
|
|
4134
4138
|
throw error;
|
|
4135
4139
|
}
|
|
4136
4140
|
}
|
|
4137
|
-
function resolveHarnessRoot(explicit, env
|
|
4141
|
+
function resolveHarnessRoot(explicit, env, home) {
|
|
4138
4142
|
if (typeof explicit === "string" && explicit !== "") return resolve10(explicit);
|
|
4139
4143
|
const configured = readHostConfig(env, home).harnessRoot;
|
|
4140
4144
|
if (typeof configured === "string" && configured !== "") return resolve10(configured);
|
|
4141
4145
|
return defaultHarnessRoot(env, home);
|
|
4142
4146
|
}
|
|
4143
|
-
function retireLegacyHostConfig(env, home, written) {
|
|
4144
|
-
const legacy = legacyHostConfigPath(env, home);
|
|
4145
|
-
if (legacy === written || !existsSync16(legacy)) return;
|
|
4146
|
-
unlinkSync5(legacy);
|
|
4147
|
-
}
|
|
4148
4147
|
function fieldError2(path4, requirement) {
|
|
4149
|
-
return new Error(`${
|
|
4148
|
+
return new Error(`${HOST_CONFIG_FILE}: ${path4} ${requirement}`);
|
|
4150
4149
|
}
|
|
4151
4150
|
|
|
4152
4151
|
// src/lib/host-workspace.ts
|
|
@@ -4159,6 +4158,40 @@ var IGNORABLE_ENTRIES = [
|
|
|
4159
4158
|
GENERATIONS.agro.configFile,
|
|
4160
4159
|
GENERATIONS.legacy.configFile
|
|
4161
4160
|
];
|
|
4161
|
+
var STATE_HOME_DIRS = [
|
|
4162
|
+
GENERATIONS.legacy.userStateDir,
|
|
4163
|
+
GENERATIONS.agro.userStateDir
|
|
4164
|
+
];
|
|
4165
|
+
function isDirectory2(path4) {
|
|
4166
|
+
return statSync5(path4, { throwIfNoEntry: false })?.isDirectory() === true;
|
|
4167
|
+
}
|
|
4168
|
+
function stateHomes(home) {
|
|
4169
|
+
return STATE_HOME_DIRS.map((name) => join11(home, name));
|
|
4170
|
+
}
|
|
4171
|
+
function stateHomeRefusal(bin, home) {
|
|
4172
|
+
const [legacy, agro] = stateHomes(home);
|
|
4173
|
+
if (isDirectory2(legacy) && isDirectory2(agro)) {
|
|
4174
|
+
return `${bin} harness: the host state home is split \u2014 ${legacy} and ${agro} both exist.
|
|
4175
|
+
Merge them with \`${bin} migrate --home\`, then re-run this command.
|
|
4176
|
+
`;
|
|
4177
|
+
}
|
|
4178
|
+
if (isDirectory2(legacy)) {
|
|
4179
|
+
return `${bin} harness: the host state home is the legacy ${legacy}, and host state now lives in ${agro}.
|
|
4180
|
+
Creating ${agro} beside it would split the two.
|
|
4181
|
+
Migrate first with \`${bin} migrate --home\`, then re-run this command.
|
|
4182
|
+
`;
|
|
4183
|
+
}
|
|
4184
|
+
return void 0;
|
|
4185
|
+
}
|
|
4186
|
+
function stateHomeRootRefusal(bin, root, home) {
|
|
4187
|
+
const target = resolve11(root);
|
|
4188
|
+
const enclosing = stateHomes(home).find((state) => target === state);
|
|
4189
|
+
if (enclosing === void 0) return void 0;
|
|
4190
|
+
return `${bin} harness: the harness root ${target} is the state home ${enclosing} itself.
|
|
4191
|
+
A workspace there collides with AGRO state and blocks every command run from ${home}.
|
|
4192
|
+
Move it out \u2014 for example \`mv ${enclosing} ${join11(home, "agro")}\` \u2014 then re-run this command.
|
|
4193
|
+
`;
|
|
4194
|
+
}
|
|
4162
4195
|
function isIgnorableEntry(name) {
|
|
4163
4196
|
return name.startsWith(".") || IGNORABLE_ENTRIES.includes(name);
|
|
4164
4197
|
}
|
|
@@ -4229,7 +4262,8 @@ var HARNESS_CATALOG = [
|
|
|
4229
4262
|
verifyArgv: ["claude", "--version"],
|
|
4230
4263
|
uninstallArgv: ["npm", "--prefix", HARNESS_PREFIX_TOKEN, "uninstall", "-g", "@anthropic-ai/claude-code"],
|
|
4231
4264
|
docsPath: "docs/harnesses/claude-code.md",
|
|
4232
|
-
kind: "installable"
|
|
4265
|
+
kind: "installable",
|
|
4266
|
+
bypassPermissionsFlag: "--permission-mode bypassPermissions"
|
|
4233
4267
|
},
|
|
4234
4268
|
{
|
|
4235
4269
|
id: "codex",
|
|
@@ -4376,6 +4410,9 @@ function resolveVerifyArgv(entry, prefix) {
|
|
|
4376
4410
|
function resolveUninstallArgv(entry, prefix) {
|
|
4377
4411
|
return entry.uninstallArgv === null ? null : substitute(entry.uninstallArgv, prefix);
|
|
4378
4412
|
}
|
|
4413
|
+
function harnessLaunchCommand(entry) {
|
|
4414
|
+
return entry.bypassPermissionsFlag === void 0 ? entry.binary : `${entry.binary} ${entry.bypassPermissionsFlag}`;
|
|
4415
|
+
}
|
|
4379
4416
|
function harnessBinPath(prefix) {
|
|
4380
4417
|
return `${prefix}/bin`;
|
|
4381
4418
|
}
|
|
@@ -4443,7 +4480,7 @@ function stateOf(entry, installed, location) {
|
|
|
4443
4480
|
function probeableHost(env, home) {
|
|
4444
4481
|
let root;
|
|
4445
4482
|
try {
|
|
4446
|
-
root = resolveHarnessRoot(void 0, env);
|
|
4483
|
+
root = resolveHarnessRoot(void 0, env, home);
|
|
4447
4484
|
} catch {
|
|
4448
4485
|
return void 0;
|
|
4449
4486
|
}
|
|
@@ -4578,7 +4615,13 @@ function isInteractive(opts) {
|
|
|
4578
4615
|
async function installOnHost(entry, opts, io, run, status) {
|
|
4579
4616
|
const bin = opts.bin;
|
|
4580
4617
|
const env = opts.env ?? process.env;
|
|
4581
|
-
const
|
|
4618
|
+
const home = homeOf(opts);
|
|
4619
|
+
const flagged = opts.host === true || opts.path !== void 0 || opts.workspace !== void 0;
|
|
4620
|
+
const generation = stateHomeRefusal(bin, home);
|
|
4621
|
+
if (generation !== void 0) {
|
|
4622
|
+
io.stderr(generation);
|
|
4623
|
+
return 1;
|
|
4624
|
+
}
|
|
4582
4625
|
if (!flagged && !isInteractive(opts)) {
|
|
4583
4626
|
io.stderr(
|
|
4584
4627
|
sandboxRefusal(bin, status) + `Or install on the host with \`${bin} harness install ${entry.id} --host\`.
|
|
@@ -4593,15 +4636,17 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4593
4636
|
}
|
|
4594
4637
|
let root;
|
|
4595
4638
|
let recorded;
|
|
4639
|
+
let explicit;
|
|
4596
4640
|
try {
|
|
4597
|
-
|
|
4598
|
-
|
|
4641
|
+
explicit = opts.workspace !== void 0 ? workspaceRoot(opts.workspace, env, home) : opts.path;
|
|
4642
|
+
recorded = readHostConfig(env, home).harnessRoot;
|
|
4643
|
+
root = resolveHarnessRoot(explicit, env, home);
|
|
4599
4644
|
} catch (err) {
|
|
4600
4645
|
io.stderr(`${bin} harness: ${messageOf(err)}
|
|
4601
4646
|
`);
|
|
4602
4647
|
return 1;
|
|
4603
4648
|
}
|
|
4604
|
-
const sticky =
|
|
4649
|
+
const sticky = explicit === void 0 && recorded !== void 0 && recorded !== "";
|
|
4605
4650
|
if (!flagged) {
|
|
4606
4651
|
const ask2 = io.ask ?? ask;
|
|
4607
4652
|
const answer = (await ask2(
|
|
@@ -4612,12 +4657,23 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4612
4657
|
return 1;
|
|
4613
4658
|
}
|
|
4614
4659
|
if (!sticky) {
|
|
4615
|
-
const chosen = (await ask2(`
|
|
4616
|
-
|
|
4660
|
+
const chosen = (await ask2(`Workspace name [${DEFAULT_WORKSPACE_NAME}]:`)).trim();
|
|
4661
|
+
try {
|
|
4662
|
+
root = workspaceRoot(chosen === "" ? DEFAULT_WORKSPACE_NAME : chosen, env, home);
|
|
4663
|
+
} catch (err) {
|
|
4664
|
+
io.stderr(`${bin} harness: ${messageOf(err)}
|
|
4665
|
+
`);
|
|
4666
|
+
return 1;
|
|
4667
|
+
}
|
|
4617
4668
|
}
|
|
4618
4669
|
}
|
|
4619
4670
|
if (sticky) io.stdout(`using the recorded harness root ${root}
|
|
4620
4671
|
`);
|
|
4672
|
+
const nested = stateHomeRootRefusal(bin, root, home);
|
|
4673
|
+
if (nested !== void 0) {
|
|
4674
|
+
io.stderr(nested);
|
|
4675
|
+
return 1;
|
|
4676
|
+
}
|
|
4621
4677
|
try {
|
|
4622
4678
|
if (!existsSync18(join12(root, ".git"))) io.stdout(`cloning ${AGRO_REPO_URL} into ${root}\u2026
|
|
4623
4679
|
`);
|
|
@@ -4633,8 +4689,21 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4633
4689
|
`);
|
|
4634
4690
|
return 1;
|
|
4635
4691
|
}
|
|
4636
|
-
const prefix = hostPrefix(
|
|
4692
|
+
const prefix = hostPrefix(home);
|
|
4637
4693
|
const target = hostTargetFor(root, prefix, run, env);
|
|
4694
|
+
const linked = await target.exec({
|
|
4695
|
+
argv: remoteControlDirScript(root, "scripts/link-providers.sh", ["--init"]),
|
|
4696
|
+
env: aliasedEnvPair("PROJECT_ROOT", root),
|
|
4697
|
+
stdio: "inherit"
|
|
4698
|
+
});
|
|
4699
|
+
if (linked.exitCode !== 0) {
|
|
4700
|
+
io.stderr(
|
|
4701
|
+
`${bin} harness: could not link provider skills in ${root} (exit ${linked.exitCode}); nothing was installed.
|
|
4702
|
+
Run: bash ${root}/.agro/scripts/link-providers.sh --init
|
|
4703
|
+
`
|
|
4704
|
+
);
|
|
4705
|
+
return 1;
|
|
4706
|
+
}
|
|
4638
4707
|
const hermes = entry.id === "hermes";
|
|
4639
4708
|
const installEnv = hermes ? {
|
|
4640
4709
|
...aliasedEnvPair("PROJECT_ROOT", root),
|
|
@@ -4678,14 +4747,15 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4678
4747
|
workspaceRoot: root
|
|
4679
4748
|
};
|
|
4680
4749
|
try {
|
|
4681
|
-
const config = readHostConfig(env);
|
|
4750
|
+
const config = readHostConfig(env, home);
|
|
4682
4751
|
writeHostConfig(
|
|
4683
4752
|
{
|
|
4684
4753
|
...config,
|
|
4685
4754
|
harnessRoot: root,
|
|
4686
4755
|
hostHarnesses: { ...config.hostHarnesses ?? {}, [entry.id]: receipt }
|
|
4687
4756
|
},
|
|
4688
|
-
env
|
|
4757
|
+
env,
|
|
4758
|
+
home
|
|
4689
4759
|
);
|
|
4690
4760
|
} catch (err) {
|
|
4691
4761
|
io.stderr(`${bin} harness: could not record the install: ${messageOf(err)}
|
|
@@ -4698,7 +4768,7 @@ async function installOnHost(entry, opts, io, run, status) {
|
|
|
4698
4768
|
io.stdout(`Add this line to your shell profile: export PATH="${harnessBinPath(prefix)}:$PATH"
|
|
4699
4769
|
`);
|
|
4700
4770
|
}
|
|
4701
|
-
io.stdout(`Run ${entry.
|
|
4771
|
+
io.stdout(`Run ${entry.title} in the AGRO workspace: cd ${root} && ${harnessLaunchCommand(entry)}
|
|
4702
4772
|
`);
|
|
4703
4773
|
return 0;
|
|
4704
4774
|
}
|
|
@@ -4742,7 +4812,13 @@ async function removeHarness(entry, target, prefix, user, opts, io) {
|
|
|
4742
4812
|
async function uninstallOnHost(entry, opts, io, run) {
|
|
4743
4813
|
const bin = opts.bin;
|
|
4744
4814
|
const env = opts.env ?? process.env;
|
|
4745
|
-
const
|
|
4815
|
+
const home = homeOf(opts);
|
|
4816
|
+
const computed = hostPrefix(home);
|
|
4817
|
+
const generation = stateHomeRefusal(bin, home);
|
|
4818
|
+
if (generation !== void 0) {
|
|
4819
|
+
io.stderr(generation);
|
|
4820
|
+
return 1;
|
|
4821
|
+
}
|
|
4746
4822
|
if (resolveUninstallArgv(entry, computed) === null) {
|
|
4747
4823
|
io.stdout(`${entry.id}: nothing to remove \u2014 npx fetches it at each run
|
|
4748
4824
|
`);
|
|
@@ -4751,13 +4827,18 @@ async function uninstallOnHost(entry, opts, io, run) {
|
|
|
4751
4827
|
let config;
|
|
4752
4828
|
let workspace;
|
|
4753
4829
|
try {
|
|
4754
|
-
config = readHostConfig(env);
|
|
4755
|
-
workspace = resolveHarnessRoot(void 0, env);
|
|
4830
|
+
config = readHostConfig(env, home);
|
|
4831
|
+
workspace = resolveHarnessRoot(void 0, env, home);
|
|
4756
4832
|
} catch (err) {
|
|
4757
4833
|
io.stderr(`${bin} harness: ${messageOf(err)}
|
|
4758
4834
|
`);
|
|
4759
4835
|
return 1;
|
|
4760
4836
|
}
|
|
4837
|
+
const nested = stateHomeRootRefusal(bin, workspace, home);
|
|
4838
|
+
if (nested !== void 0) {
|
|
4839
|
+
io.stderr(nested);
|
|
4840
|
+
return 1;
|
|
4841
|
+
}
|
|
4761
4842
|
const receipt = config.hostHarnesses?.[entry.id];
|
|
4762
4843
|
if (receipt === void 0 && opts.force !== true) {
|
|
4763
4844
|
io.stderr(
|
|
@@ -4774,7 +4855,7 @@ Removing it could delete a harness you installed yourself. Re-run with \`--force
|
|
|
4774
4855
|
const remaining = { ...config.hostHarnesses ?? {} };
|
|
4775
4856
|
delete remaining[entry.id];
|
|
4776
4857
|
try {
|
|
4777
|
-
writeHostConfig({ ...config, hostHarnesses: remaining }, env);
|
|
4858
|
+
writeHostConfig({ ...config, hostHarnesses: remaining }, env, home);
|
|
4778
4859
|
} catch (err) {
|
|
4779
4860
|
io.stderr(`${bin} harness: could not clear the install record: ${messageOf(err)}
|
|
4780
4861
|
`);
|
|
@@ -4859,7 +4940,7 @@ async function runHarnessInstall(name, opts, io) {
|
|
|
4859
4940
|
// src/commands/tool.ts
|
|
4860
4941
|
import { existsSync as existsSync19 } from "node:fs";
|
|
4861
4942
|
import { homedir as homedir3 } from "node:os";
|
|
4862
|
-
import { delimiter as delimiter3, join as join13, resolve as
|
|
4943
|
+
import { delimiter as delimiter3, join as join13, resolve as resolve12 } from "node:path";
|
|
4863
4944
|
|
|
4864
4945
|
// src/lib/tools/catalog.ts
|
|
4865
4946
|
var TOOLS_DOC = "docs/installation.md";
|
|
@@ -5145,7 +5226,7 @@ function rowOf(entry, installed, version, location) {
|
|
|
5145
5226
|
function probeableHost2(env, home) {
|
|
5146
5227
|
let root;
|
|
5147
5228
|
try {
|
|
5148
|
-
root = resolveHarnessRoot(void 0, env);
|
|
5229
|
+
root = resolveHarnessRoot(void 0, env, home);
|
|
5149
5230
|
} catch {
|
|
5150
5231
|
return void 0;
|
|
5151
5232
|
}
|
|
@@ -5293,6 +5374,7 @@ ${hostCapableToolIds().map((t) => ` ${bin} tool install ${t} --host`).join("\n"
|
|
|
5293
5374
|
async function installOnHost2(entry, opts, io, run, status) {
|
|
5294
5375
|
const bin = opts.bin;
|
|
5295
5376
|
const env = opts.env ?? process.env;
|
|
5377
|
+
const home = homeOf2(opts);
|
|
5296
5378
|
const flagged = opts.host === true || opts.path !== void 0;
|
|
5297
5379
|
if (!entry.hostCapable) {
|
|
5298
5380
|
io.stderr(sandboxRefusal2(bin, status));
|
|
@@ -5326,8 +5408,8 @@ Every tool installer in the catalog is Debian-specific. Install ${entry.id} with
|
|
|
5326
5408
|
let root;
|
|
5327
5409
|
let recorded;
|
|
5328
5410
|
try {
|
|
5329
|
-
recorded = readHostConfig(env).harnessRoot;
|
|
5330
|
-
root = resolveHarnessRoot(opts.path, env);
|
|
5411
|
+
recorded = readHostConfig(env, home).harnessRoot;
|
|
5412
|
+
root = resolveHarnessRoot(opts.path, env, home);
|
|
5331
5413
|
} catch (err) {
|
|
5332
5414
|
io.stderr(`${bin} tool: ${messageOf2(err)}
|
|
5333
5415
|
`);
|
|
@@ -5345,7 +5427,7 @@ Every tool installer in the catalog is Debian-specific. Install ${entry.id} with
|
|
|
5345
5427
|
}
|
|
5346
5428
|
if (!sticky) {
|
|
5347
5429
|
const chosen = (await ask2(`Harness root [${root}]:`)).trim();
|
|
5348
|
-
if (chosen !== "") root =
|
|
5430
|
+
if (chosen !== "") root = resolve12(chosen);
|
|
5349
5431
|
}
|
|
5350
5432
|
}
|
|
5351
5433
|
if (sticky) io.stdout(`using the recorded harness root ${root}
|
|
@@ -5365,7 +5447,7 @@ Every tool installer in the catalog is Debian-specific. Install ${entry.id} with
|
|
|
5365
5447
|
`);
|
|
5366
5448
|
return 1;
|
|
5367
5449
|
}
|
|
5368
|
-
const prefix = hostPrefix2(
|
|
5450
|
+
const prefix = hostPrefix2(home);
|
|
5369
5451
|
const installEnv = { NPM_USER_PREFIX: prefix };
|
|
5370
5452
|
const target = hostTargetFor2(root, prefix, run, env);
|
|
5371
5453
|
if (await probeInstalled2(target, entry, void 0, installEnv) === true) {
|
|
@@ -5394,14 +5476,15 @@ Every tool installer in the catalog is Debian-specific. Install ${entry.id} with
|
|
|
5394
5476
|
workspaceRoot: root
|
|
5395
5477
|
};
|
|
5396
5478
|
try {
|
|
5397
|
-
const config = readHostConfig(env);
|
|
5479
|
+
const config = readHostConfig(env, home);
|
|
5398
5480
|
writeHostConfig(
|
|
5399
5481
|
{
|
|
5400
5482
|
...config,
|
|
5401
5483
|
harnessRoot: root,
|
|
5402
5484
|
hostTools: { ...config.hostTools ?? {}, [entry.id]: receipt }
|
|
5403
5485
|
},
|
|
5404
|
-
env
|
|
5486
|
+
env,
|
|
5487
|
+
home
|
|
5405
5488
|
);
|
|
5406
5489
|
} catch (err) {
|
|
5407
5490
|
io.stderr(`${bin} tool: could not record the install: ${messageOf2(err)}
|
|
@@ -5502,12 +5585,13 @@ async function removeTool(entry, target, prefix, user, opts, io) {
|
|
|
5502
5585
|
async function uninstallOnHost2(entry, opts, io, run) {
|
|
5503
5586
|
const bin = opts.bin;
|
|
5504
5587
|
const env = opts.env ?? process.env;
|
|
5505
|
-
const
|
|
5588
|
+
const home = homeOf2(opts);
|
|
5589
|
+
const computed = hostPrefix2(home);
|
|
5506
5590
|
let config;
|
|
5507
5591
|
let workspace;
|
|
5508
5592
|
try {
|
|
5509
|
-
config = readHostConfig(env);
|
|
5510
|
-
workspace = resolveHarnessRoot(void 0, env);
|
|
5593
|
+
config = readHostConfig(env, home);
|
|
5594
|
+
workspace = resolveHarnessRoot(void 0, env, home);
|
|
5511
5595
|
} catch (err) {
|
|
5512
5596
|
io.stderr(`${bin} tool: ${messageOf2(err)}
|
|
5513
5597
|
`);
|
|
@@ -5529,7 +5613,7 @@ Removing it could delete a tool you installed yourself. Re-run with \`--force\`
|
|
|
5529
5613
|
const remaining = { ...config.hostTools ?? {} };
|
|
5530
5614
|
delete remaining[entry.id];
|
|
5531
5615
|
try {
|
|
5532
|
-
writeHostConfig({ ...config, hostTools: remaining }, env);
|
|
5616
|
+
writeHostConfig({ ...config, hostTools: remaining }, env, home);
|
|
5533
5617
|
} catch (err) {
|
|
5534
5618
|
io.stderr(`${bin} tool: could not clear the install record: ${messageOf2(err)}
|
|
5535
5619
|
`);
|
|
@@ -5624,8 +5708,8 @@ function fetchRemoteSource(opts) {
|
|
|
5624
5708
|
}
|
|
5625
5709
|
|
|
5626
5710
|
// src/cli.ts
|
|
5627
|
-
var VERSION = true ? "0.12.
|
|
5628
|
-
var DEFAULT_SOURCE_OH_DIR =
|
|
5711
|
+
var VERSION = true ? "0.12.2" : "0.0.0-dev";
|
|
5712
|
+
var DEFAULT_SOURCE_OH_DIR = resolve13(
|
|
5629
5713
|
dirname10(fileURLToPath(import.meta.url)),
|
|
5630
5714
|
"../.."
|
|
5631
5715
|
);
|
|
@@ -5862,10 +5946,11 @@ Usage:
|
|
|
5862
5946
|
|
|
5863
5947
|
\`install\` and \`uninstall\` act on the running sandbox. When no sandbox is
|
|
5864
5948
|
reachable they act on the host. A host install clones the AGRO workspace into the
|
|
5865
|
-
harness root, then installs the harness into \`~/.local\`.
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5949
|
+
harness root, then installs the harness into \`~/.local\`. The default root is the
|
|
5950
|
+
workspace registry entry \`~/.agro/workspaces/default\`; an interactive run asks for
|
|
5951
|
+
the workspace name. \`--workspace <name>\` chooses that entry and \`--path <dir>\` a
|
|
5952
|
+
directory outside the registry. A successful install records the root as
|
|
5953
|
+
\`harnessRoot\` in \`~/.agro/config.json\`. The install prefix is always \`~/.local\`.
|
|
5869
5954
|
|
|
5870
5955
|
On the host, \`uninstall\` removes only the harness \`install\` recorded, from the
|
|
5871
5956
|
prefix in that record. Without a record it refuses, and \`--force\` overrides. In
|
|
@@ -5874,7 +5959,8 @@ the sandbox it needs no record. See \`docs/harnesses/overview.md\`.
|
|
|
5874
5959
|
Flags:
|
|
5875
5960
|
--json Machine-readable output (list/status)
|
|
5876
5961
|
--host Install on the host when the sandbox is not running (install)
|
|
5877
|
-
--
|
|
5962
|
+
--workspace <name> Workspace registry entry to clone into; implies --host (install)
|
|
5963
|
+
--path <dir> Harness root outside the registry; implies --host (install)
|
|
5878
5964
|
--force Remove without a recorded host install (uninstall)
|
|
5879
5965
|
|
|
5880
5966
|
Harnesses:
|
|
@@ -5930,8 +6016,8 @@ only on these tools:
|
|
|
5930
6016
|
${hostCapableToolIds().map((t) => ` ${t}`).join("\n")}
|
|
5931
6017
|
|
|
5932
6018
|
A host install clones the AGRO workspace into the harness root \u2014 \`--path <dir>\`,
|
|
5933
|
-
then \`harnessRoot\` in
|
|
5934
|
-
\`~/.local\`. It records the install as \`hostTools\` in that same file. On the host,
|
|
6019
|
+
then \`harnessRoot\` in \`~/.agro/config.json\`, then \`~/.agro/workspaces/default\` \u2014 and
|
|
6020
|
+
installs into \`~/.local\`. It records the install as \`hostTools\` in that same file. On the host,
|
|
5935
6021
|
\`uninstall\` removes only what \`install\` recorded, from the prefix in that record.
|
|
5936
6022
|
Without a record it refuses, and \`--force\` overrides.
|
|
5937
6023
|
|
|
@@ -6331,11 +6417,16 @@ function parseHarnessArgs(rest, bin = LEGACY_PRODUCT.bin) {
|
|
|
6331
6417
|
}
|
|
6332
6418
|
const positionals = [];
|
|
6333
6419
|
let expectPath = false;
|
|
6420
|
+
let expectWorkspace = false;
|
|
6334
6421
|
for (const token of rest) {
|
|
6335
6422
|
if (expectPath) {
|
|
6336
6423
|
expectPath = false;
|
|
6337
6424
|
args.path = token;
|
|
6338
6425
|
args.host = true;
|
|
6426
|
+
} else if (expectWorkspace) {
|
|
6427
|
+
expectWorkspace = false;
|
|
6428
|
+
args.workspace = token;
|
|
6429
|
+
args.host = true;
|
|
6339
6430
|
} else if (token === "--json") {
|
|
6340
6431
|
args.json = true;
|
|
6341
6432
|
} else if (token === "--host") {
|
|
@@ -6347,6 +6438,11 @@ function parseHarnessArgs(rest, bin = LEGACY_PRODUCT.bin) {
|
|
|
6347
6438
|
} else if (token.startsWith("--path=")) {
|
|
6348
6439
|
args.path = token.slice("--path=".length);
|
|
6349
6440
|
args.host = true;
|
|
6441
|
+
} else if (token === "--workspace") {
|
|
6442
|
+
expectWorkspace = true;
|
|
6443
|
+
} else if (token.startsWith("--workspace=")) {
|
|
6444
|
+
args.workspace = token.slice("--workspace=".length);
|
|
6445
|
+
args.host = true;
|
|
6350
6446
|
} else if (token.startsWith("-")) {
|
|
6351
6447
|
return { ok: false, error: `${bin} harness: unknown flag "${token}"` };
|
|
6352
6448
|
} else {
|
|
@@ -6356,6 +6452,15 @@ function parseHarnessArgs(rest, bin = LEGACY_PRODUCT.bin) {
|
|
|
6356
6452
|
if (expectPath || args.path === "") {
|
|
6357
6453
|
return { ok: false, error: `${bin} harness: --path requires a directory` };
|
|
6358
6454
|
}
|
|
6455
|
+
if (expectWorkspace || args.workspace === "") {
|
|
6456
|
+
return { ok: false, error: `${bin} harness: --workspace requires a name` };
|
|
6457
|
+
}
|
|
6458
|
+
if (args.path !== void 0 && args.workspace !== void 0) {
|
|
6459
|
+
return {
|
|
6460
|
+
ok: false,
|
|
6461
|
+
error: `${bin} harness: --workspace names a registry entry and --path names a directory \u2014 pass one, not both`
|
|
6462
|
+
};
|
|
6463
|
+
}
|
|
6359
6464
|
const [sub, name, ...extra] = positionals;
|
|
6360
6465
|
if (sub !== "list" && sub !== "install" && sub !== "status" && sub !== "uninstall") {
|
|
6361
6466
|
return {
|
|
@@ -6374,7 +6479,10 @@ function parseHarnessArgs(rest, bin = LEGACY_PRODUCT.bin) {
|
|
|
6374
6479
|
return { ok: false, error: `${bin} harness list: unexpected argument "${name}"` };
|
|
6375
6480
|
}
|
|
6376
6481
|
if (sub !== "install" && args.host) {
|
|
6377
|
-
return {
|
|
6482
|
+
return {
|
|
6483
|
+
ok: false,
|
|
6484
|
+
error: `${bin} harness ${sub}: --host, --workspace and --path apply to install only`
|
|
6485
|
+
};
|
|
6378
6486
|
}
|
|
6379
6487
|
if (sub !== "uninstall" && args.force) {
|
|
6380
6488
|
return { ok: false, error: `${bin} harness ${sub}: --force applies to uninstall only` };
|
|
@@ -6449,13 +6557,13 @@ function bundledPayloadExists(bundled, exists = existsSync20) {
|
|
|
6449
6557
|
function resolveUpdateSource(args, bundled, bin = LEGACY_PRODUCT.bin) {
|
|
6450
6558
|
const exists = bundled.exists ?? existsSync20;
|
|
6451
6559
|
if (args.fromDir !== void 0) {
|
|
6452
|
-
return { kind: "local", fromDir:
|
|
6560
|
+
return { kind: "local", fromDir: resolve13(args.fromDir) };
|
|
6453
6561
|
}
|
|
6454
6562
|
if (args.fromRemote) {
|
|
6455
6563
|
return { kind: "remote", ref: args.ref };
|
|
6456
6564
|
}
|
|
6457
6565
|
if (bundledPayloadExists(bundled, exists)) {
|
|
6458
|
-
return { kind: "local", fromDir:
|
|
6566
|
+
return { kind: "local", fromDir: resolve13(bundled.sourceOhDir, "..") };
|
|
6459
6567
|
}
|
|
6460
6568
|
return {
|
|
6461
6569
|
kind: "remote",
|
|
@@ -6747,7 +6855,12 @@ async function main(argv) {
|
|
|
6747
6855
|
}
|
|
6748
6856
|
return await runHarnessInstall(
|
|
6749
6857
|
a.name,
|
|
6750
|
-
{
|
|
6858
|
+
{
|
|
6859
|
+
bin,
|
|
6860
|
+
host: a.host,
|
|
6861
|
+
...a.path !== void 0 ? { path: a.path } : {},
|
|
6862
|
+
...a.workspace !== void 0 ? { workspace: a.workspace } : {}
|
|
6863
|
+
},
|
|
6751
6864
|
io
|
|
6752
6865
|
);
|
|
6753
6866
|
}
|