agentapprove 0.1.13 → 0.1.20
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/cli.js +195 -45
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2430,10 +2430,28 @@ function getOpenClawPluginTargets(openclawConfigPath) {
|
|
|
2430
2430
|
legacyPath ? createRemovalTarget(legacyPath, "plugin_artifact", true) : null
|
|
2431
2431
|
].filter((target) => target !== null);
|
|
2432
2432
|
}
|
|
2433
|
-
function getOpenCodePluginTargets(opencodeConfigDir) {
|
|
2433
|
+
function getOpenCodePluginTargets(opencodeConfigDir, opencodeCacheDir) {
|
|
2434
2434
|
const pluginPath = safeJoinWithinBase(opencodeConfigDir, "node_modules", "@agentapprove", "opencode");
|
|
2435
|
+
const cacheNodeModulesPath = opencodeCacheDir ? safeJoinWithinBase(opencodeCacheDir, "node_modules", "@agentapprove", "opencode") : null;
|
|
2436
|
+
const packageCacheTargets = [];
|
|
2437
|
+
if (opencodeCacheDir) {
|
|
2438
|
+
const scopedPackageCacheDir = safeJoinWithinBase(opencodeCacheDir, "packages", "@agentapprove");
|
|
2439
|
+
if (scopedPackageCacheDir && existsSync(scopedPackageCacheDir)) {
|
|
2440
|
+
for (const entry of readdirSync(scopedPackageCacheDir)) {
|
|
2441
|
+
if (!entry.startsWith("opencode@")) {
|
|
2442
|
+
continue;
|
|
2443
|
+
}
|
|
2444
|
+
const packageCachePath = safeJoinWithinBase(scopedPackageCacheDir, entry);
|
|
2445
|
+
if (packageCachePath) {
|
|
2446
|
+
packageCacheTargets.push(createRemovalTarget(packageCachePath, "plugin_artifact", true));
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2435
2451
|
return [
|
|
2436
|
-
pluginPath ? createRemovalTarget(pluginPath, "plugin_artifact", true) : null
|
|
2452
|
+
pluginPath ? createRemovalTarget(pluginPath, "plugin_artifact", true) : null,
|
|
2453
|
+
cacheNodeModulesPath ? createRemovalTarget(cacheNodeModulesPath, "plugin_artifact", true) : null,
|
|
2454
|
+
...packageCacheTargets
|
|
2437
2455
|
].filter((target) => target !== null);
|
|
2438
2456
|
}
|
|
2439
2457
|
function collectBackupTargets(configPaths) {
|
|
@@ -2613,7 +2631,7 @@ function shouldCreateFreshPairing(connectionMethod) {
|
|
|
2613
2631
|
}
|
|
2614
2632
|
|
|
2615
2633
|
// src/cli.ts
|
|
2616
|
-
var VERSION = "0.1.
|
|
2634
|
+
var VERSION = "0.1.20";
|
|
2617
2635
|
function getApiUrl() {
|
|
2618
2636
|
return process.env.AGENTAPPROVE_API || "https://api.agentapprove.com";
|
|
2619
2637
|
}
|
|
@@ -2626,6 +2644,12 @@ function getOpenCodeConfigDir() {
|
|
|
2626
2644
|
function getOpenCodeConfigPath() {
|
|
2627
2645
|
return join(getOpenCodeConfigDir(), "opencode.json");
|
|
2628
2646
|
}
|
|
2647
|
+
function getXdgCacheHome() {
|
|
2648
|
+
return process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
|
|
2649
|
+
}
|
|
2650
|
+
function getOpenCodeCacheDir() {
|
|
2651
|
+
return join(getXdgCacheHome(), "opencode");
|
|
2652
|
+
}
|
|
2629
2653
|
var API_URL = getApiUrl();
|
|
2630
2654
|
var API_VERSION = process.env.AGENTAPPROVE_API_VERSION || "v001";
|
|
2631
2655
|
function hasFlag2(flag) {
|
|
@@ -2633,18 +2657,36 @@ function hasFlag2(flag) {
|
|
|
2633
2657
|
}
|
|
2634
2658
|
function updateEnvValue(key, value) {
|
|
2635
2659
|
const envPath = join(getAgentApproveDir(), "env");
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
${key}=${value}
|
|
2645
|
-
`;
|
|
2660
|
+
let content;
|
|
2661
|
+
try {
|
|
2662
|
+
content = readFileSync(envPath, "utf-8");
|
|
2663
|
+
} catch (err) {
|
|
2664
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2667
|
+
throw err;
|
|
2646
2668
|
}
|
|
2647
|
-
|
|
2669
|
+
const lines = content.endsWith(`
|
|
2670
|
+
`) ? content.slice(0, -1).split(`
|
|
2671
|
+
`) : content.split(`
|
|
2672
|
+
`);
|
|
2673
|
+
let seenKey = false;
|
|
2674
|
+
const updatedLines = lines.flatMap((line) => {
|
|
2675
|
+
const trimmed = line.replace(/^export\s+/, "").trim();
|
|
2676
|
+
const eqIdx = trimmed.indexOf("=");
|
|
2677
|
+
if (eqIdx <= 0 || trimmed.slice(0, eqIdx) !== key)
|
|
2678
|
+
return [line];
|
|
2679
|
+
if (seenKey)
|
|
2680
|
+
return [];
|
|
2681
|
+
seenKey = true;
|
|
2682
|
+
return [`${key}=${value}`];
|
|
2683
|
+
});
|
|
2684
|
+
if (!seenKey) {
|
|
2685
|
+
updatedLines.push(`${key}=${value}`);
|
|
2686
|
+
}
|
|
2687
|
+
writeFileSync(envPath, `${updatedLines.join(`
|
|
2688
|
+
`)}
|
|
2689
|
+
`, { mode: 384 });
|
|
2648
2690
|
}
|
|
2649
2691
|
function getCommand() {
|
|
2650
2692
|
const args = process.argv.slice(2);
|
|
@@ -2661,10 +2703,11 @@ function getCommand() {
|
|
|
2661
2703
|
}
|
|
2662
2704
|
return filtered[0] || "install";
|
|
2663
2705
|
}
|
|
2664
|
-
var OPENCODE_PLUGIN_VERSION = "0.1.
|
|
2665
|
-
var
|
|
2706
|
+
var OPENCODE_PLUGIN_VERSION = "0.1.16";
|
|
2707
|
+
var OPENCODE_PLUGIN_SPEC = `@agentapprove/opencode@${OPENCODE_PLUGIN_VERSION}`;
|
|
2708
|
+
var OPENCLAW_PLUGIN_VERSION = "0.2.10";
|
|
2666
2709
|
var OPENCLAW_PLUGIN_SPEC = `@agentapprove/openclaw@${OPENCLAW_PLUGIN_VERSION}`;
|
|
2667
|
-
var PI_PLUGIN_VERSION = "0.1.
|
|
2710
|
+
var PI_PLUGIN_VERSION = "0.1.5";
|
|
2668
2711
|
var PI_PLUGIN_SPEC = `npm:@agentapprove/pi@${PI_PLUGIN_VERSION}`;
|
|
2669
2712
|
var PI_STATUS_TIMEOUT_MS = 5000;
|
|
2670
2713
|
var AGENTS = {
|
|
@@ -3208,7 +3251,7 @@ function buildUninstallPlan() {
|
|
|
3208
3251
|
const configuredAgents = detectInstalledAgents();
|
|
3209
3252
|
const pluginArtifactTargets = uniqueRemovalTargets([
|
|
3210
3253
|
...getOpenClawPluginTargets(AGENTS.openclaw.configPath),
|
|
3211
|
-
...getOpenCodePluginTargets(getOpenCodeConfigDir())
|
|
3254
|
+
...getOpenCodePluginTargets(getOpenCodeConfigDir(), getOpenCodeCacheDir())
|
|
3212
3255
|
]);
|
|
3213
3256
|
return {
|
|
3214
3257
|
configuredAgents,
|
|
@@ -3335,6 +3378,13 @@ function readExistingConfig() {
|
|
|
3335
3378
|
case "AGENTAPPROVE_RETENTION_DAYS":
|
|
3336
3379
|
config.retentionDays = value === "forever" ? 365 : parseInt(value, 10);
|
|
3337
3380
|
break;
|
|
3381
|
+
case "AGENTAPPROVE_CONFIG_SET_AT": {
|
|
3382
|
+
const parsed = parseInt(value, 10);
|
|
3383
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3384
|
+
config.configSetAt = parsed;
|
|
3385
|
+
}
|
|
3386
|
+
break;
|
|
3387
|
+
}
|
|
3338
3388
|
case "AGENTAPPROVE_DEBUG_LOG":
|
|
3339
3389
|
config.debugLog = value === "true";
|
|
3340
3390
|
break;
|
|
@@ -3904,32 +3954,41 @@ async function installHooksForAgent(agentId, hooksDir, mode = "approval") {
|
|
|
3904
3954
|
return { success: true, backupPath, hooks: installedHooks };
|
|
3905
3955
|
}
|
|
3906
3956
|
if (agentId === "opencode") {
|
|
3907
|
-
|
|
3908
|
-
|
|
3957
|
+
const installResult = installOpenCodePluginViaCli();
|
|
3958
|
+
if (!installResult.success) {
|
|
3959
|
+
return { success: false, backupPath, hooks: [], error: installResult.error };
|
|
3909
3960
|
}
|
|
3910
|
-
const
|
|
3911
|
-
if (!
|
|
3912
|
-
|
|
3961
|
+
const opencodeConfig = readJsonConfig(agent.configPath);
|
|
3962
|
+
if (!Array.isArray(opencodeConfig.plugin)) {
|
|
3963
|
+
opencodeConfig.plugin = [];
|
|
3913
3964
|
}
|
|
3914
|
-
|
|
3965
|
+
const pluginArray = opencodeConfig.plugin.filter((entry) => typeof entry === "string").filter((entry) => entry !== "@agentapprove/opencode" && !entry.startsWith("@agentapprove/opencode@"));
|
|
3966
|
+
if (!pluginArray.includes(OPENCODE_PLUGIN_SPEC)) {
|
|
3967
|
+
pluginArray.push(OPENCODE_PLUGIN_SPEC);
|
|
3968
|
+
}
|
|
3969
|
+
opencodeConfig.plugin = pluginArray;
|
|
3970
|
+
writeJsonConfig(agent.configPath, opencodeConfig);
|
|
3915
3971
|
const opencodePkgPath = join(getOpenCodeConfigDir(), "package.json");
|
|
3916
3972
|
try {
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
writeFileSync(opencodePkgPath, JSON.stringify(pkgJson, null, 2) + `
|
|
3973
|
+
const pkgJson = JSON.parse(readFileSync(opencodePkgPath, "utf-8"));
|
|
3974
|
+
const deps = pkgJson.dependencies;
|
|
3975
|
+
if (deps?.["@agentapprove/opencode"]) {
|
|
3976
|
+
delete deps["@agentapprove/opencode"];
|
|
3977
|
+
if (Object.keys(deps).length === 0) {
|
|
3978
|
+
delete pkgJson.dependencies;
|
|
3979
|
+
}
|
|
3980
|
+
writeFileSync(opencodePkgPath, JSON.stringify(pkgJson, null, 2) + `
|
|
3926
3981
|
`);
|
|
3982
|
+
}
|
|
3927
3983
|
} catch (err) {
|
|
3984
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
3985
|
+
return { success: true, backupPath, hooks: [...installedHooks, installResult.label] };
|
|
3986
|
+
}
|
|
3928
3987
|
if (err instanceof Error) {
|
|
3929
3988
|
console.warn(`Warning: Could not update package.json: ${err.message}`);
|
|
3930
3989
|
}
|
|
3931
3990
|
}
|
|
3932
|
-
installedHooks.push(
|
|
3991
|
+
installedHooks.push(installResult.label);
|
|
3933
3992
|
return { success: true, backupPath, hooks: installedHooks };
|
|
3934
3993
|
}
|
|
3935
3994
|
const hooksToInstall = mode === "observe" ? agent.hooks.filter((h2) => !h2.isApprovalHook) : agent.hooks;
|
|
@@ -4338,19 +4397,21 @@ codex_hooks = true`;
|
|
|
4338
4397
|
`);
|
|
4339
4398
|
} else if (agentId === "opencode") {
|
|
4340
4399
|
const configJson = JSON.stringify({
|
|
4341
|
-
plugin: [
|
|
4400
|
+
plugin: [OPENCODE_PLUGIN_SPEC]
|
|
4342
4401
|
}, null, 2);
|
|
4343
4402
|
const opencodeConfigPath = process.env.XDG_CONFIG_HOME ? `${process.env.XDG_CONFIG_HOME}/opencode/opencode.json` : "~/.config/opencode/opencode.json";
|
|
4344
4403
|
return [
|
|
4345
|
-
|
|
4404
|
+
"Install the Agent Approve plugin for OpenCode:",
|
|
4346
4405
|
"",
|
|
4347
|
-
|
|
4406
|
+
` opencode plugin ${OPENCODE_PLUGIN_SPEC} --global --force`,
|
|
4348
4407
|
"",
|
|
4349
|
-
|
|
4408
|
+
`This command installs the npm plugin and updates your global OpenCode config (${opencodeConfigPath}).`,
|
|
4350
4409
|
"",
|
|
4351
|
-
|
|
4410
|
+
"If you need to edit the config manually, ensure it contains:",
|
|
4352
4411
|
"",
|
|
4353
|
-
|
|
4412
|
+
configJson,
|
|
4413
|
+
"",
|
|
4414
|
+
"Restart OpenCode to activate the plugin."
|
|
4354
4415
|
].join(`
|
|
4355
4416
|
`);
|
|
4356
4417
|
} else if (agentId === "pi") {
|
|
@@ -4436,6 +4497,89 @@ function installOpenClawPluginViaCli() {
|
|
|
4436
4497
|
return { success: false, error: message, label: "Agent Approve plugin" };
|
|
4437
4498
|
}
|
|
4438
4499
|
}
|
|
4500
|
+
function readJsonPackageVersion(packagePath) {
|
|
4501
|
+
if (!existsSync2(packagePath)) {
|
|
4502
|
+
return null;
|
|
4503
|
+
}
|
|
4504
|
+
try {
|
|
4505
|
+
const pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
|
|
4506
|
+
return typeof pkg.version === "string" ? pkg.version : null;
|
|
4507
|
+
} catch {
|
|
4508
|
+
return null;
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
function readOpenCodeInstalledVersion() {
|
|
4512
|
+
const cacheDir = getOpenCodeCacheDir();
|
|
4513
|
+
const candidates = [
|
|
4514
|
+
join(cacheDir, "packages", "@agentapprove", `opencode@${OPENCODE_PLUGIN_VERSION}`, "node_modules", "@agentapprove", "opencode", "package.json"),
|
|
4515
|
+
join(cacheDir, "packages", "@agentapprove", "opencode@latest", "node_modules", "@agentapprove", "opencode", "package.json"),
|
|
4516
|
+
join(cacheDir, "node_modules", "@agentapprove", "opencode", "package.json"),
|
|
4517
|
+
join(getOpenCodeConfigDir(), "node_modules", "@agentapprove", "opencode", "package.json")
|
|
4518
|
+
];
|
|
4519
|
+
for (const candidate of candidates) {
|
|
4520
|
+
const version = readJsonPackageVersion(candidate);
|
|
4521
|
+
if (version === OPENCODE_PLUGIN_VERSION) {
|
|
4522
|
+
return version;
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
for (const candidate of candidates) {
|
|
4526
|
+
const version = readJsonPackageVersion(candidate);
|
|
4527
|
+
if (version) {
|
|
4528
|
+
return version;
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
return null;
|
|
4532
|
+
}
|
|
4533
|
+
function installOpenCodePluginViaCli() {
|
|
4534
|
+
try {
|
|
4535
|
+
const result = spawnSync("opencode", ["plugin", OPENCODE_PLUGIN_SPEC, "--global", "--force"], {
|
|
4536
|
+
encoding: "utf8",
|
|
4537
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4538
|
+
});
|
|
4539
|
+
if (result.error) {
|
|
4540
|
+
return {
|
|
4541
|
+
success: false,
|
|
4542
|
+
label: "Agent Approve plugin",
|
|
4543
|
+
error: `Could not run opencode plugin installer: ${result.error.message}`
|
|
4544
|
+
};
|
|
4545
|
+
}
|
|
4546
|
+
if (result.status !== 0) {
|
|
4547
|
+
const detail = [result.stdout, result.stderr].filter(Boolean).join(`
|
|
4548
|
+
`).trim();
|
|
4549
|
+
return {
|
|
4550
|
+
success: false,
|
|
4551
|
+
label: "Agent Approve plugin",
|
|
4552
|
+
error: detail || `opencode plugin installer exited with code ${result.status}`
|
|
4553
|
+
};
|
|
4554
|
+
}
|
|
4555
|
+
const installedVersion = readOpenCodeInstalledVersion();
|
|
4556
|
+
if (!installedVersion) {
|
|
4557
|
+
return {
|
|
4558
|
+
success: false,
|
|
4559
|
+
label: "Agent Approve plugin",
|
|
4560
|
+
error: `OpenCode installed ${OPENCODE_PLUGIN_SPEC}, but the installer could not verify the package version. Re-run manually with: opencode plugin ${OPENCODE_PLUGIN_SPEC} --global --force`
|
|
4561
|
+
};
|
|
4562
|
+
}
|
|
4563
|
+
if (installedVersion !== OPENCODE_PLUGIN_VERSION) {
|
|
4564
|
+
return {
|
|
4565
|
+
success: false,
|
|
4566
|
+
label: "Agent Approve plugin",
|
|
4567
|
+
error: `OpenCode installed ${installedVersion}, expected ${OPENCODE_PLUGIN_VERSION}. Re-run after the npm registry exposes the latest package.`
|
|
4568
|
+
};
|
|
4569
|
+
}
|
|
4570
|
+
return {
|
|
4571
|
+
success: true,
|
|
4572
|
+
version: installedVersion,
|
|
4573
|
+
label: `Agent Approve plugin v${installedVersion}`
|
|
4574
|
+
};
|
|
4575
|
+
} catch (err) {
|
|
4576
|
+
return {
|
|
4577
|
+
success: false,
|
|
4578
|
+
label: "Agent Approve plugin",
|
|
4579
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4580
|
+
};
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4439
4583
|
function installPiPluginViaCli() {
|
|
4440
4584
|
try {
|
|
4441
4585
|
execSync(`pi install ${PI_PLUGIN_SPEC}`, { stdio: "pipe" });
|
|
@@ -4730,7 +4874,7 @@ Installs hooks and extensions for Cursor, Claude, Gemini, Pi, OpenCode, OpenClaw
|
|
|
4730
4874
|
let failBehavior = selectedInstallConfig.failBehavior;
|
|
4731
4875
|
let installMode = selectedInstallConfig.installMode;
|
|
4732
4876
|
let useE2E = selectedInstallConfig.e2eEnabled;
|
|
4733
|
-
let configSetAt = Math.floor(Date.now() / 1000);
|
|
4877
|
+
let configSetAt = setupProfile === "existing-config" && existingConfig?.configSetAt ? existingConfig.configSetAt : Math.floor(Date.now() / 1000);
|
|
4734
4878
|
if (setupProfile === "customize") {
|
|
4735
4879
|
const modeChoice = await le({
|
|
4736
4880
|
message: "Choose your security mode:",
|
|
@@ -4898,6 +5042,9 @@ Installs hooks and extensions for Cursor, Claude, Gemini, Pi, OpenCode, OpenClaw
|
|
|
4898
5042
|
if (connectionMethod === "existing") {
|
|
4899
5043
|
token = existingConfig.token;
|
|
4900
5044
|
apiUrl = existingConfig.apiUrl || API_URL;
|
|
5045
|
+
if (setupProfile === "existing-config" && existingConfig?.configSetAt) {
|
|
5046
|
+
configSetAt = existingConfig.configSetAt;
|
|
5047
|
+
}
|
|
4901
5048
|
const e2eKeyExists = existsSync2(existingKeyPath);
|
|
4902
5049
|
useE2E = e2eKeyExists;
|
|
4903
5050
|
e2eUserKey = null;
|
|
@@ -5062,7 +5209,10 @@ AGENTAPPROVE_E2E_MODE=${installMode}
|
|
|
5062
5209
|
filesToModify.push(join(homedir(), ".codex", "config.toml"));
|
|
5063
5210
|
}
|
|
5064
5211
|
if (agentId === "opencode") {
|
|
5065
|
-
filesToModify.push(
|
|
5212
|
+
filesToModify.push(`OpenCode plugin cache (via \`opencode plugin ${OPENCODE_PLUGIN_SPEC} --global --force\`)`);
|
|
5213
|
+
}
|
|
5214
|
+
if (agentId === "openclaw") {
|
|
5215
|
+
filesToModify.push(`OpenClaw plugin registry (via \`openclaw plugins install ${OPENCLAW_PLUGIN_SPEC}\`)`);
|
|
5066
5216
|
}
|
|
5067
5217
|
if (agentId === "pi") {
|
|
5068
5218
|
filesToModify.push("Pi package registry (via `pi install`)");
|
|
@@ -5094,8 +5244,8 @@ Backups will be created with timestamp`, "Files to be modified");
|
|
|
5094
5244
|
if (result.success) {
|
|
5095
5245
|
const installedHookNames = result.hooks.join(", ");
|
|
5096
5246
|
const backupMsg = result.backupPath ? source_default.dim(` (backup created)`) : "";
|
|
5097
|
-
const
|
|
5098
|
-
spinner.stop(`${
|
|
5247
|
+
const agentName = source_default.cyan(agent.name);
|
|
5248
|
+
spinner.stop(`${agentName}: ${installedHookNames}${backupMsg}`);
|
|
5099
5249
|
if (agentId === "vscode-agent") {
|
|
5100
5250
|
const vsCodeSpinner = _2();
|
|
5101
5251
|
vsCodeSpinner.start("Registering hook path in VS Code settings");
|
|
@@ -5566,7 +5716,7 @@ but if unused for 30 days they expire. Get a new one below.`, "Token Expired");
|
|
|
5566
5716
|
} else {
|
|
5567
5717
|
v2.info('Refresh updates the token only. Use "npx agentapprove pair" if you need to repair or change E2E pairing.');
|
|
5568
5718
|
}
|
|
5569
|
-
const configSetAt = Math.floor(Date.now() / 1000);
|
|
5719
|
+
const configSetAt = existingConfig.configSetAt || Math.floor(Date.now() / 1000);
|
|
5570
5720
|
const privacy = existingConfig.privacy || "full";
|
|
5571
5721
|
const retentionDays = existingConfig.retentionDays ?? 30;
|
|
5572
5722
|
const failBehavior = existingConfig.failBehavior || "ask";
|