agentplugged 1.0.1 → 1.0.3
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/agent-commands.js +38 -18
- package/dist/main.js +495 -14
- package/dist/method/agents/noah/references/conduct.md +1 -0
- package/dist/method/doctor.yaml +18 -2
- package/dist/product-root.js +9 -3
- package/package.json +8 -8
package/dist/agent-commands.js
CHANGED
|
@@ -14500,6 +14500,10 @@ retrospective, an installation and a validation point never wait for it.
|
|
|
14500
14500
|
scope: \`guard.creator_action\` \`scope_widen\`) or a decision of the architecture contradicted, and
|
|
14501
14501
|
lets the write through in doubt. Reading, and writes in other tools, stay governed by the
|
|
14502
14502
|
permission rules alone: \`read_secrets\` is always denied, whatever the session.
|
|
14503
|
+
5. A write the engine refused is never redone by another path: not by a terminal command
|
|
14504
|
+
(\`sed -i\`, a redirection \`>\`, \`Set-Content\`, \`Out-File\`, a copy, a script), not by another tool.
|
|
14505
|
+
The agent stops, tells the coordinator which file it needs and why, and the coordinator offers
|
|
14506
|
+
the creator to widen the scope (\`guard.creator_action\` \`scope_widen\`) in one gesture.
|
|
14503
14507
|
`;
|
|
14504
14508
|
}
|
|
14505
14509
|
/** The folder layout and the seven rules: in the short constitution and in the reference alike. */
|
|
@@ -17783,6 +17787,21 @@ function claudeActionRules(action, mainBranch) {
|
|
|
17783
17787
|
push(rules, patternsOf(action, mainBranch).flatMap(claudeRules));
|
|
17784
17788
|
return rules;
|
|
17785
17789
|
}
|
|
17790
|
+
/**
|
|
17791
|
+
* The rules as Claude Code's settings hold them. A backslash right before the closing parenthesis
|
|
17792
|
+
* escapes it there, so such a rule is written with that one backslash doubled (the others are
|
|
17793
|
+
* read literally). Claude Code's `PowerShell` tool has its own rules: each `Bash(x)` rule gets
|
|
17794
|
+
* its `PowerShell(x)` twin, or a command would pass by being run through the other shell.
|
|
17795
|
+
*/
|
|
17796
|
+
function claudeCommandRules(rules) {
|
|
17797
|
+
const out = [];
|
|
17798
|
+
for (const rule of rules) {
|
|
17799
|
+
const escaped = rule.endsWith("\\)") && !rule.endsWith("\\\\)") ? `${rule.slice(0, -1)}\\)` : rule;
|
|
17800
|
+
push(out, [escaped]);
|
|
17801
|
+
if (escaped.startsWith("Bash(")) push(out, [`PowerShell${escaped.slice(4)}`]);
|
|
17802
|
+
}
|
|
17803
|
+
return out;
|
|
17804
|
+
}
|
|
17786
17805
|
/** The Claude Code rule that allows all the tools of the `agentplugged` MCP server. */
|
|
17787
17806
|
const ENGINE_MCP_RULE = "mcp__agentplugged";
|
|
17788
17807
|
/**
|
|
@@ -18991,13 +19010,22 @@ const WRITE_TOOLS = [
|
|
|
18991
19010
|
text: "new_source"
|
|
18992
19011
|
}
|
|
18993
19012
|
];
|
|
19013
|
+
/** The two tools that run a shell command in Claude Code: both carry it in `tool_input.command`. */
|
|
19014
|
+
const SHELL_MATCHER = "Bash|PowerShell";
|
|
19015
|
+
/**
|
|
19016
|
+
* The `if` conditions of a hook on the shell tools: each `Bash(x)` condition and its
|
|
19017
|
+
* `PowerShell(x)` twin, as a condition matches the calls of one tool only.
|
|
19018
|
+
*/
|
|
19019
|
+
function shellConditions(conditions) {
|
|
19020
|
+
return conditions.flatMap((condition) => [condition, `PowerShell${condition.slice(4)}`]);
|
|
19021
|
+
}
|
|
18994
19022
|
/** The hooks `sync` writes for Claude Code, from the rules in force. */
|
|
18995
19023
|
function claudeTrustHooks(rules, mainBranch) {
|
|
18996
19024
|
const session = hookValue("session_id");
|
|
18997
19025
|
const pre = [];
|
|
18998
|
-
const conditions = rules.install_dependency === "deny" ? [] : claudeActionRules("install_dependency", mainBranch).filter((entry) => entry.startsWith("Bash("));
|
|
19026
|
+
const conditions = rules.install_dependency === "deny" ? [] : shellConditions(claudeActionRules("install_dependency", mainBranch).filter((entry) => entry.startsWith("Bash(")));
|
|
18999
19027
|
if (conditions.length > 0) pre.push({
|
|
19000
|
-
matcher:
|
|
19028
|
+
matcher: SHELL_MATCHER,
|
|
19001
19029
|
hooks: conditions.map((condition) => handler("check_action", {
|
|
19002
19030
|
action: "install_dependency",
|
|
19003
19031
|
command: hookValue("tool_input.command"),
|
|
@@ -19032,9 +19060,9 @@ function claudeTrustHooks(rules, mainBranch) {
|
|
|
19032
19060
|
})]
|
|
19033
19061
|
},
|
|
19034
19062
|
{
|
|
19035
|
-
matcher:
|
|
19036
|
-
hooks: NETWORK_COMMANDS.map((condition) => handler("record_external_read", {
|
|
19037
|
-
tool: "Bash",
|
|
19063
|
+
matcher: SHELL_MATCHER,
|
|
19064
|
+
hooks: shellConditions(NETWORK_COMMANDS).map((condition) => handler("record_external_read", {
|
|
19065
|
+
tool: condition.startsWith("PowerShell(") ? "PowerShell" : "Bash",
|
|
19038
19066
|
source: hookValue("tool_input.command"),
|
|
19039
19067
|
session
|
|
19040
19068
|
}, condition))
|
|
@@ -19775,9 +19803,9 @@ const syncCommand = defineCommand({
|
|
|
19775
19803
|
flavor: claudeFlavor(loaded.config.permissions?.mode),
|
|
19776
19804
|
current: claudeSettings
|
|
19777
19805
|
}, has("claude-code") ? {
|
|
19778
|
-
deny:
|
|
19779
|
-
allow:
|
|
19780
|
-
ask:
|
|
19806
|
+
deny: claudeCommandRules(unique([...denyRules(permissionInputs), ...claudeRules.deny])),
|
|
19807
|
+
allow: claudeCommandRules(claudeRules.allow),
|
|
19808
|
+
ask: claudeCommandRules(claudeRules.ask)
|
|
19781
19809
|
} : {}, {
|
|
19782
19810
|
deny: manifest.permissions,
|
|
19783
19811
|
...previousLists[CLAUDE_SETTINGS_PATH]
|
|
@@ -19809,7 +19837,7 @@ const syncCommand = defineCommand({
|
|
|
19809
19837
|
count: owned.allow.length
|
|
19810
19838
|
});
|
|
19811
19839
|
}
|
|
19812
|
-
const conflicts = [...has("claude-code") ? allowConflicts(CLAUDE_SETTINGS_PATH, claudeSettings?.value, claudeRules.allow, {
|
|
19840
|
+
const conflicts = [...has("claude-code") ? allowConflicts(CLAUDE_SETTINGS_PATH, claudeSettings?.value, claudeCommandRules(claudeRules.allow), {
|
|
19813
19841
|
deny: manifest.permissions,
|
|
19814
19842
|
...previousLists[CLAUDE_SETTINGS_PATH]
|
|
19815
19843
|
}) : [], ...has("cursor") ? allowConflicts(CURSOR_CLI_PATH, cursorSettings?.value, cursorRules.allow, { deny: previousCursor }) : []];
|
|
@@ -19878,14 +19906,6 @@ const CODEX_HOME_DENY = [
|
|
|
19878
19906
|
function unique(rules) {
|
|
19879
19907
|
return [...new Set(rules)];
|
|
19880
19908
|
}
|
|
19881
|
-
/**
|
|
19882
|
-
* The rules Claude Code's settings can hold: a backslash before the closing parenthesis escapes it
|
|
19883
|
-
* there, so such a rule would be malformed (the `C:/` spelling of the same root stays). The
|
|
19884
|
-
* engine's own guards keep them: they read the rules themselves.
|
|
19885
|
-
*/
|
|
19886
|
-
function claudeWritable(rules) {
|
|
19887
|
-
return rules.filter((rule) => !rule.endsWith("\\)"));
|
|
19888
|
-
}
|
|
19889
19909
|
/** The tool a generated path belongs to, for the summary. */
|
|
19890
19910
|
function groupOf(path) {
|
|
19891
19911
|
if (path.startsWith(".claude/") || path === "CLAUDE.md") return "claude-code";
|
|
@@ -31012,7 +31032,7 @@ function readSource(tool, raw) {
|
|
|
31012
31032
|
const toolName = truncate(cleanText(tool), 200) || "unknown";
|
|
31013
31033
|
const given = cleanText(raw ?? "");
|
|
31014
31034
|
const usable = given !== "" && !given.includes("${");
|
|
31015
|
-
if (usable && toolName === "Bash") return {
|
|
31035
|
+
if (usable && (toolName === "Bash" || toolName === "PowerShell")) return {
|
|
31016
31036
|
tool: toolName,
|
|
31017
31037
|
source: truncate(hostOf$1(given, true) ?? commandName(given), 200)
|
|
31018
31038
|
};
|
package/dist/main.js
CHANGED
|
@@ -6447,6 +6447,415 @@ var NodeFolderReader = class {
|
|
|
6447
6447
|
return new NodeWorkspaceFiles(path);
|
|
6448
6448
|
}
|
|
6449
6449
|
};
|
|
6450
|
+
/**
|
|
6451
|
+
* The helper's script: compiles the C#, says it is ready (or why it cannot be), then answers one
|
|
6452
|
+
* JSON line per request line until its input closes. Constrained language mode forbids `Add-Type`,
|
|
6453
|
+
* which is said before trying.
|
|
6454
|
+
*/
|
|
6455
|
+
const HELPER_SCRIPT = `
|
|
6456
|
+
$ErrorActionPreference = 'Stop'
|
|
6457
|
+
$out = [Console]::Out
|
|
6458
|
+
try {
|
|
6459
|
+
$mode = [string]$ExecutionContext.SessionState.LanguageMode
|
|
6460
|
+
if ($mode -ne 'FullLanguage') { throw ('PowerShell is in ' + $mode + ' mode, which forbids compiling code') }
|
|
6461
|
+
Add-Type -ReferencedAssemblies 'System.Web.Extensions' -TypeDefinition @'
|
|
6462
|
+
${String.raw`
|
|
6463
|
+
using System;
|
|
6464
|
+
using System.Runtime.InteropServices;
|
|
6465
|
+
using System.Text;
|
|
6466
|
+
using System.Collections.Generic;
|
|
6467
|
+
using System.Web.Script.Serialization;
|
|
6468
|
+
|
|
6469
|
+
public static class ApCredentials {
|
|
6470
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
6471
|
+
struct CREDENTIAL {
|
|
6472
|
+
public uint Flags;
|
|
6473
|
+
public uint Type;
|
|
6474
|
+
public string TargetName;
|
|
6475
|
+
public string Comment;
|
|
6476
|
+
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
|
|
6477
|
+
public uint CredentialBlobSize;
|
|
6478
|
+
public IntPtr CredentialBlob;
|
|
6479
|
+
public uint Persist;
|
|
6480
|
+
public uint AttributeCount;
|
|
6481
|
+
public IntPtr Attributes;
|
|
6482
|
+
public string TargetAlias;
|
|
6483
|
+
public string UserName;
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6486
|
+
[DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
6487
|
+
static extern bool CredRead(string target, uint type, uint flags, out IntPtr credential);
|
|
6488
|
+
[DllImport("advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
6489
|
+
static extern bool CredWrite(ref CREDENTIAL credential, uint flags);
|
|
6490
|
+
[DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
6491
|
+
static extern bool CredDelete(string target, uint type, uint flags);
|
|
6492
|
+
[DllImport("advapi32.dll", EntryPoint = "CredEnumerateW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
6493
|
+
static extern bool CredEnumerate(string filter, uint flags, out uint count, out IntPtr credentials);
|
|
6494
|
+
[DllImport("advapi32.dll", EntryPoint = "CredFree")]
|
|
6495
|
+
static extern void CredFree(IntPtr buffer);
|
|
6496
|
+
|
|
6497
|
+
const uint Generic = 1;
|
|
6498
|
+
const uint PersistEnterprise = 3;
|
|
6499
|
+
const int NotFound = 1168;
|
|
6500
|
+
|
|
6501
|
+
static string Text(string b64) {
|
|
6502
|
+
return b64 == null ? null : Encoding.UTF8.GetString(Convert.FromBase64String(b64));
|
|
6503
|
+
}
|
|
6504
|
+
static string B64(string text) {
|
|
6505
|
+
return Convert.ToBase64String(Encoding.UTF8.GetBytes(text == null ? "" : text));
|
|
6506
|
+
}
|
|
6507
|
+
public static string Failure(int code, string message) {
|
|
6508
|
+
return "{\"ok\":false,\"code\":" + code + ",\"message\":\"" + B64(message) + "\"}";
|
|
6509
|
+
}
|
|
6510
|
+
static string Win32Failure(int code) {
|
|
6511
|
+
string message;
|
|
6512
|
+
try { message = new System.ComponentModel.Win32Exception(code).Message; } catch (Exception) { message = ""; }
|
|
6513
|
+
return Failure(code, message);
|
|
6514
|
+
}
|
|
6515
|
+
|
|
6516
|
+
// One request line in, one answer line out. The line is parsed here, not by a cmdlet, so that
|
|
6517
|
+
// PowerShell module logging (when a policy turns it on) never records a secret.
|
|
6518
|
+
public static string HandleLine(string line) {
|
|
6519
|
+
int id = 0;
|
|
6520
|
+
try {
|
|
6521
|
+
Dictionary<string, object> request =
|
|
6522
|
+
(Dictionary<string, object>)new JavaScriptSerializer().DeserializeObject(line);
|
|
6523
|
+
id = Convert.ToInt32(request["id"]);
|
|
6524
|
+
string answer = Handle(Field(request, "op"), Field(request, "target"), Field(request, "user"), Field(request, "secret"));
|
|
6525
|
+
return "{\"id\":" + id + "," + answer.Substring(1);
|
|
6526
|
+
} catch (Exception) {
|
|
6527
|
+
return "{\"id\":" + id + "," + Failure(0, "the credential helper could not read a request").Substring(1);
|
|
6528
|
+
}
|
|
6529
|
+
}
|
|
6530
|
+
static string Field(Dictionary<string, object> request, string name) {
|
|
6531
|
+
object value;
|
|
6532
|
+
return request.TryGetValue(name, out value) && value != null ? value.ToString() : "";
|
|
6533
|
+
}
|
|
6534
|
+
|
|
6535
|
+
public static string Handle(string op, string targetB64, string userB64, string secretB64) {
|
|
6536
|
+
try {
|
|
6537
|
+
string target = Text(targetB64);
|
|
6538
|
+
if (op == "read") return Read(target);
|
|
6539
|
+
if (op == "write") return Write(target, Text(userB64), Convert.FromBase64String(secretB64));
|
|
6540
|
+
if (op == "delete") return Delete(target);
|
|
6541
|
+
if (op == "list") return List(target);
|
|
6542
|
+
return Failure(0, "unknown operation");
|
|
6543
|
+
} catch (Exception) {
|
|
6544
|
+
return Failure(0, "the credential helper failed");
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
|
|
6548
|
+
static string Read(string target) {
|
|
6549
|
+
IntPtr pointer;
|
|
6550
|
+
if (!CredRead(target, Generic, 0, out pointer)) {
|
|
6551
|
+
int error = Marshal.GetLastWin32Error();
|
|
6552
|
+
return error == NotFound ? "{\"ok\":true,\"found\":false}" : Win32Failure(error);
|
|
6553
|
+
}
|
|
6554
|
+
try {
|
|
6555
|
+
CREDENTIAL credential = (CREDENTIAL)Marshal.PtrToStructure(pointer, typeof(CREDENTIAL));
|
|
6556
|
+
byte[] blob = new byte[credential.CredentialBlobSize];
|
|
6557
|
+
if (blob.Length > 0) Marshal.Copy(credential.CredentialBlob, blob, 0, blob.Length);
|
|
6558
|
+
string answer = "{\"ok\":true,\"found\":true,\"secret\":\"" + Convert.ToBase64String(blob) + "\"}";
|
|
6559
|
+
Array.Clear(blob, 0, blob.Length);
|
|
6560
|
+
return answer;
|
|
6561
|
+
} finally {
|
|
6562
|
+
CredFree(pointer);
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
|
|
6566
|
+
static string Write(string target, string user, byte[] blob) {
|
|
6567
|
+
IntPtr memory = Marshal.AllocHGlobal(blob.Length == 0 ? 1 : blob.Length);
|
|
6568
|
+
try {
|
|
6569
|
+
if (blob.Length > 0) Marshal.Copy(blob, 0, memory, blob.Length);
|
|
6570
|
+
CREDENTIAL credential = new CREDENTIAL();
|
|
6571
|
+
credential.Type = Generic;
|
|
6572
|
+
credential.TargetName = target;
|
|
6573
|
+
credential.CredentialBlobSize = (uint)blob.Length;
|
|
6574
|
+
credential.CredentialBlob = memory;
|
|
6575
|
+
credential.Persist = PersistEnterprise;
|
|
6576
|
+
credential.UserName = user;
|
|
6577
|
+
if (!CredWrite(ref credential, 0)) return Win32Failure(Marshal.GetLastWin32Error());
|
|
6578
|
+
return "{\"ok\":true}";
|
|
6579
|
+
} finally {
|
|
6580
|
+
byte[] zeros = new byte[blob.Length];
|
|
6581
|
+
if (zeros.Length > 0) Marshal.Copy(zeros, 0, memory, zeros.Length);
|
|
6582
|
+
Marshal.FreeHGlobal(memory);
|
|
6583
|
+
Array.Clear(blob, 0, blob.Length);
|
|
6584
|
+
}
|
|
6585
|
+
}
|
|
6586
|
+
|
|
6587
|
+
static string Delete(string target) {
|
|
6588
|
+
if (CredDelete(target, Generic, 0)) return "{\"ok\":true,\"found\":true}";
|
|
6589
|
+
int error = Marshal.GetLastWin32Error();
|
|
6590
|
+
return error == NotFound ? "{\"ok\":true,\"found\":false}" : Win32Failure(error);
|
|
6591
|
+
}
|
|
6592
|
+
|
|
6593
|
+
// The accounts of a service: every generic credential whose target is "<user>.<service>".
|
|
6594
|
+
static string List(string service) {
|
|
6595
|
+
uint count;
|
|
6596
|
+
IntPtr array;
|
|
6597
|
+
if (!CredEnumerate(null, 0, out count, out array)) {
|
|
6598
|
+
int error = Marshal.GetLastWin32Error();
|
|
6599
|
+
return error == NotFound ? "{\"ok\":true,\"users\":[]}" : Win32Failure(error);
|
|
6600
|
+
}
|
|
6601
|
+
try {
|
|
6602
|
+
List<string> users = new List<string>();
|
|
6603
|
+
for (int index = 0; index < count; index++) {
|
|
6604
|
+
IntPtr pointer = Marshal.ReadIntPtr(array, index * IntPtr.Size);
|
|
6605
|
+
CREDENTIAL credential = (CREDENTIAL)Marshal.PtrToStructure(pointer, typeof(CREDENTIAL));
|
|
6606
|
+
if (credential.Type == Generic && credential.UserName != null
|
|
6607
|
+
&& credential.TargetName == credential.UserName + "." + service) {
|
|
6608
|
+
users.Add("\"" + B64(credential.UserName) + "\"");
|
|
6609
|
+
}
|
|
6610
|
+
}
|
|
6611
|
+
return "{\"ok\":true,\"users\":[" + string.Join(",", users.ToArray()) + "]}";
|
|
6612
|
+
} finally {
|
|
6613
|
+
CredFree(array);
|
|
6614
|
+
}
|
|
6615
|
+
}
|
|
6616
|
+
}
|
|
6617
|
+
`}
|
|
6618
|
+
'@
|
|
6619
|
+
$out.WriteLine('{"ready":true}')
|
|
6620
|
+
} catch {
|
|
6621
|
+
# Constrained language mode forbids method calls: the reason goes out as plain ASCII text,
|
|
6622
|
+
# through a cmdlet.
|
|
6623
|
+
$reason = ([string]$_) -replace '[^ -~]', ' ' -replace '["\\\\]', ' '
|
|
6624
|
+
Write-Output ('{"ready":false,"message":"' + $reason + '"}')
|
|
6625
|
+
exit 0
|
|
6626
|
+
}
|
|
6627
|
+
while ($null -ne ($line = [Console]::In.ReadLine())) {
|
|
6628
|
+
if ($line.Trim().Length -eq 0) { continue }
|
|
6629
|
+
$out.WriteLine([ApCredentials]::HandleLine($line))
|
|
6630
|
+
}
|
|
6631
|
+
`;
|
|
6632
|
+
/** Windows PowerShell 5.1 by its full path: never a `powershell` found on the PATH. */
|
|
6633
|
+
function windowsPowerShellPath() {
|
|
6634
|
+
const root = process.env.SystemRoot ?? process.env.windir;
|
|
6635
|
+
if (root === void 0) return void 0;
|
|
6636
|
+
const path = join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
6637
|
+
return existsSync(path) ? path : void 0;
|
|
6638
|
+
}
|
|
6639
|
+
/** The helper cannot be used on this machine (no PowerShell, constrained language, a crash...). */
|
|
6640
|
+
var PowerShellUnavailableError = class extends Error {
|
|
6641
|
+
name = "PowerShellUnavailableError";
|
|
6642
|
+
};
|
|
6643
|
+
const decode = (text) => Buffer.from(text ?? "", "base64").toString("utf8");
|
|
6644
|
+
/** One running helper process. */
|
|
6645
|
+
var Session = class Session {
|
|
6646
|
+
#child;
|
|
6647
|
+
#pending = /* @__PURE__ */ new Map();
|
|
6648
|
+
#nextId = 1;
|
|
6649
|
+
#buffer = "";
|
|
6650
|
+
#alive = true;
|
|
6651
|
+
#onReady;
|
|
6652
|
+
constructor(child) {
|
|
6653
|
+
this.#child = child;
|
|
6654
|
+
child.stdout?.setEncoding("utf8");
|
|
6655
|
+
child.stdout?.on("data", (chunk) => this.#receive(chunk));
|
|
6656
|
+
child.stdin?.on("error", () => {});
|
|
6657
|
+
child.on("error", () => this.#end("PowerShell could not be started"));
|
|
6658
|
+
child.on("close", () => this.#end("the PowerShell credential helper stopped"));
|
|
6659
|
+
}
|
|
6660
|
+
/** Starts a helper and waits until it is ready, or says why it cannot be. */
|
|
6661
|
+
static start(powershell, startupTimeoutMs) {
|
|
6662
|
+
const script = Buffer.from(HELPER_SCRIPT, "utf16le").toString("base64");
|
|
6663
|
+
const child = spawn(powershell, [
|
|
6664
|
+
"-NoLogo",
|
|
6665
|
+
"-NoProfile",
|
|
6666
|
+
"-NonInteractive",
|
|
6667
|
+
"-EncodedCommand",
|
|
6668
|
+
script
|
|
6669
|
+
], {
|
|
6670
|
+
stdio: [
|
|
6671
|
+
"pipe",
|
|
6672
|
+
"pipe",
|
|
6673
|
+
"ignore"
|
|
6674
|
+
],
|
|
6675
|
+
windowsHide: true
|
|
6676
|
+
});
|
|
6677
|
+
const session = new Session(child);
|
|
6678
|
+
return new Promise((resolve, reject) => {
|
|
6679
|
+
const timer = setTimeout(() => {
|
|
6680
|
+
session.close();
|
|
6681
|
+
reject(new PowerShellUnavailableError("PowerShell did not answer in time"));
|
|
6682
|
+
}, startupTimeoutMs);
|
|
6683
|
+
session.#onReady = (answer) => {
|
|
6684
|
+
clearTimeout(timer);
|
|
6685
|
+
if (answer.ready === true) {
|
|
6686
|
+
session.#idle();
|
|
6687
|
+
resolve(session);
|
|
6688
|
+
} else {
|
|
6689
|
+
session.close();
|
|
6690
|
+
reject(new PowerShellUnavailableError(answer.message || "PowerShell refused"));
|
|
6691
|
+
}
|
|
6692
|
+
};
|
|
6693
|
+
child.once("error", (error) => {
|
|
6694
|
+
clearTimeout(timer);
|
|
6695
|
+
reject(new PowerShellUnavailableError(error.message));
|
|
6696
|
+
});
|
|
6697
|
+
child.once("close", () => {
|
|
6698
|
+
clearTimeout(timer);
|
|
6699
|
+
reject(new PowerShellUnavailableError("PowerShell stopped before it was ready"));
|
|
6700
|
+
});
|
|
6701
|
+
});
|
|
6702
|
+
}
|
|
6703
|
+
get alive() {
|
|
6704
|
+
return this.#alive;
|
|
6705
|
+
}
|
|
6706
|
+
/** Stops the helper; its input closing also ends it if the kill fails. */
|
|
6707
|
+
close() {
|
|
6708
|
+
this.#alive = false;
|
|
6709
|
+
this.#child.stdin?.end();
|
|
6710
|
+
this.#child.kill();
|
|
6711
|
+
}
|
|
6712
|
+
/** Sends one request and waits for its answer (a failure of the Credential Manager is thrown). */
|
|
6713
|
+
request(fields, timeoutMs) {
|
|
6714
|
+
if (!this.#alive) return Promise.reject(/* @__PURE__ */ new Error("the PowerShell credential helper is not running"));
|
|
6715
|
+
const id = this.#nextId;
|
|
6716
|
+
this.#nextId += 1;
|
|
6717
|
+
return new Promise((resolve, reject) => {
|
|
6718
|
+
const timer = setTimeout(() => {
|
|
6719
|
+
this.#pending.delete(id);
|
|
6720
|
+
this.close();
|
|
6721
|
+
reject(/* @__PURE__ */ new Error("the PowerShell credential helper did not answer in time"));
|
|
6722
|
+
}, timeoutMs);
|
|
6723
|
+
this.#pending.set(id, {
|
|
6724
|
+
resolve,
|
|
6725
|
+
reject,
|
|
6726
|
+
timer
|
|
6727
|
+
});
|
|
6728
|
+
this.#busy();
|
|
6729
|
+
const line = JSON.stringify({
|
|
6730
|
+
id,
|
|
6731
|
+
op: fields.op,
|
|
6732
|
+
target: Buffer.from(fields.target, "utf8").toString("base64"),
|
|
6733
|
+
user: Buffer.from(fields.user ?? "", "utf8").toString("base64"),
|
|
6734
|
+
secret: fields.secret
|
|
6735
|
+
});
|
|
6736
|
+
this.#child.stdin?.write(`${line}\n`);
|
|
6737
|
+
});
|
|
6738
|
+
}
|
|
6739
|
+
#receive(chunk) {
|
|
6740
|
+
this.#buffer += chunk;
|
|
6741
|
+
for (let end = this.#buffer.indexOf("\n"); end >= 0; end = this.#buffer.indexOf("\n")) {
|
|
6742
|
+
const line = this.#buffer.slice(0, end).trim();
|
|
6743
|
+
this.#buffer = this.#buffer.slice(end + 1);
|
|
6744
|
+
if (line === "") continue;
|
|
6745
|
+
let answer;
|
|
6746
|
+
try {
|
|
6747
|
+
answer = JSON.parse(line);
|
|
6748
|
+
} catch {
|
|
6749
|
+
continue;
|
|
6750
|
+
}
|
|
6751
|
+
if (answer.ready !== void 0) this.#onReady?.(answer);
|
|
6752
|
+
else if (answer.id !== void 0) {
|
|
6753
|
+
const pending = this.#pending.get(answer.id);
|
|
6754
|
+
if (pending === void 0) continue;
|
|
6755
|
+
this.#pending.delete(answer.id);
|
|
6756
|
+
clearTimeout(pending.timer);
|
|
6757
|
+
pending.resolve(answer);
|
|
6758
|
+
if (this.#pending.size === 0) this.#idle();
|
|
6759
|
+
}
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6762
|
+
#end(reason) {
|
|
6763
|
+
this.#alive = false;
|
|
6764
|
+
for (const pending of this.#pending.values()) {
|
|
6765
|
+
clearTimeout(pending.timer);
|
|
6766
|
+
pending.reject(new Error(reason));
|
|
6767
|
+
}
|
|
6768
|
+
this.#pending.clear();
|
|
6769
|
+
}
|
|
6770
|
+
/** While a request is in flight, Node must wait for the answer. */
|
|
6771
|
+
#busy() {
|
|
6772
|
+
for (const handle of this.#handles()) handle.ref?.();
|
|
6773
|
+
}
|
|
6774
|
+
/** With nothing in flight, the helper never keeps Node from exiting. */
|
|
6775
|
+
#idle() {
|
|
6776
|
+
for (const handle of this.#handles()) handle.unref?.();
|
|
6777
|
+
}
|
|
6778
|
+
#handles() {
|
|
6779
|
+
return [
|
|
6780
|
+
this.#child,
|
|
6781
|
+
this.#child.stdin,
|
|
6782
|
+
this.#child.stdout
|
|
6783
|
+
];
|
|
6784
|
+
}
|
|
6785
|
+
};
|
|
6786
|
+
const STARTUP_TIMEOUT_MS = 3e4;
|
|
6787
|
+
const REQUEST_TIMEOUT_MS = 3e4;
|
|
6788
|
+
/** `CredentialBackend` through a PowerShell helper, started on the first request. */
|
|
6789
|
+
var PowerShellCredentials = class PowerShellCredentials {
|
|
6790
|
+
#powershell;
|
|
6791
|
+
#session;
|
|
6792
|
+
constructor(powershell) {
|
|
6793
|
+
this.#powershell = powershell;
|
|
6794
|
+
}
|
|
6795
|
+
/**
|
|
6796
|
+
* Starts the helper now. Rejects with `PowerShellUnavailableError` when PowerShell is absent, in
|
|
6797
|
+
* constrained language mode, or otherwise unable to compile the helper.
|
|
6798
|
+
*/
|
|
6799
|
+
static async open(options = {}) {
|
|
6800
|
+
const powershell = options.powershell ?? windowsPowerShellPath();
|
|
6801
|
+
if (powershell === void 0) throw new PowerShellUnavailableError("Windows PowerShell was not found");
|
|
6802
|
+
const backend = new PowerShellCredentials(powershell);
|
|
6803
|
+
await backend.#ensure();
|
|
6804
|
+
return backend;
|
|
6805
|
+
}
|
|
6806
|
+
/** The running helper; a new one when the last stopped. */
|
|
6807
|
+
async #ensure() {
|
|
6808
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
6809
|
+
this.#session ??= Session.start(this.#powershell, STARTUP_TIMEOUT_MS).catch((error) => {
|
|
6810
|
+
this.#session = void 0;
|
|
6811
|
+
throw error;
|
|
6812
|
+
});
|
|
6813
|
+
const pending = this.#session;
|
|
6814
|
+
const session = await pending;
|
|
6815
|
+
if (session.alive) return session;
|
|
6816
|
+
if (this.#session === pending) this.#session = void 0;
|
|
6817
|
+
}
|
|
6818
|
+
throw new Error("the PowerShell credential helper keeps stopping");
|
|
6819
|
+
}
|
|
6820
|
+
async #ask(fields) {
|
|
6821
|
+
const answer = await (await this.#ensure()).request(fields, REQUEST_TIMEOUT_MS);
|
|
6822
|
+
if (answer.ok !== true) {
|
|
6823
|
+
const message = decode(answer.message) || "the Credential Manager refused";
|
|
6824
|
+
throw new Error(`Windows Credential Manager failed: ${message} (Windows error code ${answer.code ?? 0})`);
|
|
6825
|
+
}
|
|
6826
|
+
return answer;
|
|
6827
|
+
}
|
|
6828
|
+
async read(service, account) {
|
|
6829
|
+
const answer = await this.#ask({
|
|
6830
|
+
op: "read",
|
|
6831
|
+
target: `${account}.${service}`,
|
|
6832
|
+
user: account
|
|
6833
|
+
});
|
|
6834
|
+
if (answer.found !== true) return void 0;
|
|
6835
|
+
return Buffer.from(answer.secret ?? "", "base64").toString("utf16le");
|
|
6836
|
+
}
|
|
6837
|
+
async write(service, account, secret) {
|
|
6838
|
+
await this.#ask({
|
|
6839
|
+
op: "write",
|
|
6840
|
+
target: `${account}.${service}`,
|
|
6841
|
+
user: account,
|
|
6842
|
+
secret: Buffer.from(secret, "utf16le").toString("base64")
|
|
6843
|
+
});
|
|
6844
|
+
}
|
|
6845
|
+
async remove(service, account) {
|
|
6846
|
+
return (await this.#ask({
|
|
6847
|
+
op: "delete",
|
|
6848
|
+
target: `${account}.${service}`,
|
|
6849
|
+
user: account
|
|
6850
|
+
})).found === true;
|
|
6851
|
+
}
|
|
6852
|
+
async accounts(service) {
|
|
6853
|
+
return ((await this.#ask({
|
|
6854
|
+
op: "list",
|
|
6855
|
+
target: service
|
|
6856
|
+
})).users ?? []).map(decode);
|
|
6857
|
+
}
|
|
6858
|
+
};
|
|
6450
6859
|
//#endregion
|
|
6451
6860
|
//#region ../../packages/adapters-git/dist/node-key-store.js
|
|
6452
6861
|
/**
|
|
@@ -6509,6 +6918,78 @@ function explainKeychainError(error, platform = process.platform) {
|
|
|
6509
6918
|
if (platform !== "linux" || !/secret.?service|d-?bus|platform secure storage/i.test(message)) return error;
|
|
6510
6919
|
return new Error(`the Secret Service of this Linux session could not be used: start one and unlock its default keyring (GNOME Keyring, KWallet or KeePassXC; on a machine without a desktop, gnome-keyring-daemon --unlock in a dbus-run-session), then run the command again. The system said: ${message}`, { cause: error });
|
|
6511
6920
|
}
|
|
6921
|
+
/** What Windows says when it refuses a file under Smart App Control or an application control policy. */
|
|
6922
|
+
const BLOCKED_BY_POLICY = /application control|blocked this file|smart app control|signing level|code integrity|contr.le d.application|bloqu. ce fichier|error 4551|0x800711c7/i;
|
|
6923
|
+
/** The message of `error` and of the errors under its `cause`, outermost first. */
|
|
6924
|
+
function messageChain(error) {
|
|
6925
|
+
const messages = [];
|
|
6926
|
+
let current = error;
|
|
6927
|
+
for (let depth = 0; depth < 20 && current !== void 0 && current !== null; depth += 1) {
|
|
6928
|
+
messages.push(current instanceof Error ? current.message : String(current));
|
|
6929
|
+
current = current instanceof Error ? current.cause : void 0;
|
|
6930
|
+
}
|
|
6931
|
+
return messages;
|
|
6932
|
+
}
|
|
6933
|
+
/**
|
|
6934
|
+
* The error for a keychain that cannot be reached at all: `native` is why `@napi-rs/keyring` did
|
|
6935
|
+
* not load and, on Windows, `powershell` why the PowerShell helper could not be used. Says what
|
|
6936
|
+
* blocks, in plain words: the generic "npm has a bug" of the module's loader never stands alone,
|
|
6937
|
+
* and a Windows policy blocking the module is named, with the file.
|
|
6938
|
+
*/
|
|
6939
|
+
function unavailableKeychainError(native, powershell) {
|
|
6940
|
+
const real = messageChain(native).filter((message) => !/cannot find native binding|npm has a bug/i.test(message));
|
|
6941
|
+
const blocked = real.find((message) => BLOCKED_BY_POLICY.test(message));
|
|
6942
|
+
const file = blocked === void 0 ? void 0 : /[A-Za-z]:\\[^\r\n"']*?\.node/.exec(blocked)?.[0];
|
|
6943
|
+
const viaPowerShell = powershell === void 0 ? "" : ` The keys could not be reached through Windows PowerShell either (${messageChain(powershell)[0]}).`;
|
|
6944
|
+
if (blocked !== void 0) {
|
|
6945
|
+
const said = blocked.split(/\r?\n/).find((line) => line.trim() !== "") ?? blocked;
|
|
6946
|
+
return new Error(`Windows blocked the system keychain module${file === void 0 ? "" : ` (${file})`}: Smart App Control or an application control policy refuses this unsigned file ("${said.trim()}").${viaPowerShell} Turn off Smart App Control (Windows Security, App & browser control) or allow this file in your application control policy, make sure Windows PowerShell can run in full language mode, then run the command again.`, { cause: native });
|
|
6947
|
+
}
|
|
6948
|
+
const why = real.length > 0 ? real.join("; ") : "Node gave no further reason";
|
|
6949
|
+
return new Error(`the system keychain module could not be loaded: ${why}.${viaPowerShell}`, { cause: native });
|
|
6950
|
+
}
|
|
6951
|
+
/** The keychain through `@napi-rs/keyring`. */
|
|
6952
|
+
async function nativeBackend() {
|
|
6953
|
+
const { AsyncEntry, findCredentialsAsync } = await load().catch((error) => {
|
|
6954
|
+
throw unavailableKeychainError(error);
|
|
6955
|
+
});
|
|
6956
|
+
const entry = (service, account) => new AsyncEntry(service, account, KEYCHAIN_ENTRY_OPTIONS);
|
|
6957
|
+
return {
|
|
6958
|
+
read: async (service, account) => await entry(service, account).getPassword() ?? void 0,
|
|
6959
|
+
write: (service, account, secret) => entry(service, account).setPassword(secret),
|
|
6960
|
+
remove: (service, account) => entry(service, account).deleteCredential(),
|
|
6961
|
+
accounts: async (service) => (await findCredentialsAsync(service)).map((found) => found.account)
|
|
6962
|
+
};
|
|
6963
|
+
}
|
|
6964
|
+
let system;
|
|
6965
|
+
/**
|
|
6966
|
+
* The system keychain of this machine. On Windows, the PowerShell helper first (no native module
|
|
6967
|
+
* to load, which Smart App Control may refuse), then `@napi-rs/keyring` when PowerShell cannot be
|
|
6968
|
+
* used; elsewhere `@napi-rs/keyring`. Chosen once per process, kept once it works.
|
|
6969
|
+
*/
|
|
6970
|
+
function systemCredentials() {
|
|
6971
|
+
system ??= (async () => {
|
|
6972
|
+
let powershell;
|
|
6973
|
+
if (process.platform === "win32") try {
|
|
6974
|
+
return await PowerShellCredentials.open();
|
|
6975
|
+
} catch (error) {
|
|
6976
|
+
powershell = error;
|
|
6977
|
+
}
|
|
6978
|
+
try {
|
|
6979
|
+
return await nativeBackend();
|
|
6980
|
+
} catch (error) {
|
|
6981
|
+
throw powershell === void 0 ? error : unavailableKeychainError(nativeCause(error), powershell);
|
|
6982
|
+
}
|
|
6983
|
+
})().catch((error) => {
|
|
6984
|
+
system = void 0;
|
|
6985
|
+
throw error;
|
|
6986
|
+
});
|
|
6987
|
+
return system;
|
|
6988
|
+
}
|
|
6989
|
+
/** The error `@napi-rs/keyring` failed with, under the explained one `nativeBackend` throws. */
|
|
6990
|
+
function nativeCause(error) {
|
|
6991
|
+
return error instanceof Error && error.cause !== void 0 ? error.cause : error;
|
|
6992
|
+
}
|
|
6512
6993
|
/** `operation`, its failure explained by `explainKeychainError`. */
|
|
6513
6994
|
async function explained(operation) {
|
|
6514
6995
|
try {
|
|
@@ -6519,8 +7000,9 @@ async function explained(operation) {
|
|
|
6519
7000
|
}
|
|
6520
7001
|
/**
|
|
6521
7002
|
* `KeyStore` in the system keychain (Windows Credential Manager, macOS Keychain, the Secret
|
|
6522
|
-
* Service on Linux)
|
|
6523
|
-
*
|
|
7003
|
+
* Service on Linux): through a PowerShell helper on Windows (`systemCredentials`) and
|
|
7004
|
+
* `@napi-rs/keyring` elsewhere, or when PowerShell cannot be used. Tests pass their own service
|
|
7005
|
+
* name and delete their entries afterwards: the machine's real entries are never touched.
|
|
6524
7006
|
*/
|
|
6525
7007
|
var NodeKeyStore = class {
|
|
6526
7008
|
service;
|
|
@@ -6533,11 +7015,13 @@ var NodeKeyStore = class {
|
|
|
6533
7015
|
* answering empty under concurrent access: elsewhere, none (no wait added to a first `init`).
|
|
6534
7016
|
*/
|
|
6535
7017
|
settleDelays;
|
|
7018
|
+
#engine;
|
|
6536
7019
|
constructor(options = {}) {
|
|
6537
7020
|
this.service = options.service ?? "agentplugged";
|
|
6538
7021
|
this.account = options.account ?? "machine";
|
|
6539
7022
|
this.humanAccount = options.humanAccount ?? "human";
|
|
6540
7023
|
this.presenceAccount = options.presenceAccount ?? "human-presence";
|
|
7024
|
+
this.#engine = options.engine ?? "system";
|
|
6541
7025
|
this.settleDelays = options.settleDelays ?? (process.platform === "win32" ? KEYCHAIN_SETTLE_DELAYS_MS : []);
|
|
6542
7026
|
}
|
|
6543
7027
|
/** Waits `settleDelays[attempt]`; false once they are all spent. */
|
|
@@ -6547,21 +7031,17 @@ var NodeKeyStore = class {
|
|
|
6547
7031
|
await setTimeout$1(delay);
|
|
6548
7032
|
return true;
|
|
6549
7033
|
}
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
const { AsyncEntry } = await load();
|
|
6553
|
-
return new AsyncEntry(this.service, account, KEYCHAIN_ENTRY_OPTIONS);
|
|
7034
|
+
#backend() {
|
|
7035
|
+
return this.#engine === "native" ? nativeBackend() : systemCredentials();
|
|
6554
7036
|
}
|
|
6555
7037
|
#read(account) {
|
|
6556
|
-
return explained(async () =>
|
|
6557
|
-
return await (await this.#entry(account)).getPassword() ?? void 0;
|
|
6558
|
-
});
|
|
7038
|
+
return explained(async () => (await this.#backend()).read(this.service, account));
|
|
6559
7039
|
}
|
|
6560
7040
|
/** A full keychain gives `KEYCHAIN_FULL`; any other failure is thrown, explained. */
|
|
6561
7041
|
async #write(account, value) {
|
|
6562
|
-
const
|
|
7042
|
+
const backend = await explained(() => this.#backend());
|
|
6563
7043
|
try {
|
|
6564
|
-
await
|
|
7044
|
+
await backend.write(this.service, account, value);
|
|
6565
7045
|
} catch (error) {
|
|
6566
7046
|
if (isKeychainFullError(error)) throw keychainFull(error instanceof Error ? error.message : String(error));
|
|
6567
7047
|
throw explainKeychainError(error);
|
|
@@ -6596,7 +7076,7 @@ var NodeKeyStore = class {
|
|
|
6596
7076
|
}
|
|
6597
7077
|
/** Deletes the machine key's entry; true when there was one. */
|
|
6598
7078
|
deleteMachineSecret() {
|
|
6599
|
-
return explained(async () => (await this.#
|
|
7079
|
+
return explained(async () => (await this.#backend()).remove(this.service, this.account));
|
|
6600
7080
|
}
|
|
6601
7081
|
/**
|
|
6602
7082
|
* Deletes the entries of this service, product keys included (tests: a service of their own).
|
|
@@ -6606,13 +7086,14 @@ var NodeKeyStore = class {
|
|
|
6606
7086
|
async deleteAll() {
|
|
6607
7087
|
if (this.service === "agentplugged") throw new Error(`deleteAll refuses the real keychain service ${KEYCHAIN_SERVICE}`);
|
|
6608
7088
|
let deleted = false;
|
|
6609
|
-
const
|
|
7089
|
+
const backend = await explained(() => this.#backend());
|
|
7090
|
+
const products = await backend.accounts(this.service).then((found) => found.filter((name) => name.startsWith(PRODUCT_ACCOUNT))).catch(() => []);
|
|
6610
7091
|
for (const account of [
|
|
6611
7092
|
this.account,
|
|
6612
7093
|
this.humanAccount,
|
|
6613
7094
|
this.presenceAccount,
|
|
6614
7095
|
...products
|
|
6615
|
-
]) deleted = await (
|
|
7096
|
+
]) deleted = await backend.remove(this.service, account) || deleted;
|
|
6616
7097
|
return deleted;
|
|
6617
7098
|
}
|
|
6618
7099
|
};
|
|
@@ -59,6 +59,7 @@ back, and the decisions to take.
|
|
|
59
59
|
other tool under his id (`ROLE_FORBIDDEN`): he never opens a checkpoint (an end of phase
|
|
60
60
|
included), never starts or delivers a story, never submits a result in another agent's place.
|
|
61
61
|
He never reports under another agent's id.
|
|
62
|
+
- When a developer reports a write the engine refused, Noah never lets it be redone by another path (a terminal command, a redirection, a copy): he proposes to the creator to widen the scope (`guard.creator_action` `scope_widen`), in one gesture.
|
|
62
63
|
- Decisions belong to the creator: the scope, the options of a checkpoint, an approval, a new
|
|
63
64
|
agent, and every point they kept for themselves. Noah recommends; the creator decides. The points
|
|
64
65
|
they did not keep are decided by the agent responsible, never by Noah (`checkpoint.decide` is not
|
package/dist/method/doctor.yaml
CHANGED
|
@@ -454,10 +454,12 @@ checks:
|
|
|
454
454
|
- steps:
|
|
455
455
|
en:
|
|
456
456
|
- "On Linux, install and unlock a secret service (GNOME Keyring or KWallet), then run: npx agentplugged doctor"
|
|
457
|
-
- "On Windows or
|
|
457
|
+
- "On Windows, if the message says Smart App Control or an application control policy blocked a file: turn off Smart App Control (Windows Security, App & browser control) or allow that file in your policy, check that Windows PowerShell is not in constrained language mode, then run: npx agentplugged doctor"
|
|
458
|
+
- "On Windows or macOS otherwise, the system keychain is part of the system: restart the session and try again."
|
|
458
459
|
fr:
|
|
459
460
|
- "Sous Linux, installe et déverrouille un service de secrets (GNOME Keyring ou KWallet), puis lance : npx agentplugged doctor"
|
|
460
|
-
- "Sous Windows ou
|
|
461
|
+
- "Sous Windows, si le message dit que Smart App Control ou une stratégie de contrôle des applications a bloqué un fichier : désactive Smart App Control (Sécurité Windows, Contrôle des applications et du navigateur) ou autorise ce fichier, vérifie que Windows PowerShell n'est pas en mode de langage restreint, puis lance : npx agentplugged doctor"
|
|
462
|
+
- "Sous Windows ou macOS sinon, le trousseau fait partie du système : redémarre la session et réessaie."
|
|
461
463
|
|
|
462
464
|
- id: presence
|
|
463
465
|
group: keys
|
|
@@ -1027,3 +1029,17 @@ signatures:
|
|
|
1027
1029
|
en: "A write failed for lack of room"
|
|
1028
1030
|
fr: "Une écriture a échoué faute de place"
|
|
1029
1031
|
read: [disk.space]
|
|
1032
|
+
- id: keychain-blocked
|
|
1033
|
+
when:
|
|
1034
|
+
output:
|
|
1035
|
+
- "smart app control"
|
|
1036
|
+
- "application control policy"
|
|
1037
|
+
- "blocked the system keychain module"
|
|
1038
|
+
- "stratégie de contrôle d'application"
|
|
1039
|
+
say:
|
|
1040
|
+
en: "Windows refused to load the keychain's module (Smart App Control or an application control policy)"
|
|
1041
|
+
fr: "Windows a refusé de charger le module du trousseau (Smart App Control ou une stratégie de contrôle des applications)"
|
|
1042
|
+
read: [keychain]
|
|
1043
|
+
assumed:
|
|
1044
|
+
en: "The message names the blocked file; whether turning off Smart App Control or adding an allow rule is right for this machine is your call, I only checked that the keychain does not answer"
|
|
1045
|
+
fr: "Le message nomme le fichier bloqué ; désactiver Smart App Control ou ajouter une règle d'autorisation, c'est à toi de décider pour cette machine, je n'ai vérifié que le fait que le trousseau ne répond pas"
|
package/dist/product-root.js
CHANGED
|
@@ -16803,7 +16803,8 @@ function alreadyRecorded(listing, read, session) {
|
|
|
16803
16803
|
* source is a `${…}`), or for an absent source: when in doubt, the read counts.
|
|
16804
16804
|
*/
|
|
16805
16805
|
function isLocalShellCommand(tool, source) {
|
|
16806
|
-
|
|
16806
|
+
const shell = tool.trim();
|
|
16807
|
+
if (shell !== "Bash" && shell !== "PowerShell" || source === void 0) return false;
|
|
16807
16808
|
const command = source.trim();
|
|
16808
16809
|
if (command === "" || /^\$\{[^}]*\}$/.test(command) || /^https?:\/\//i.test(command)) return false;
|
|
16809
16810
|
return !isNetworkCommand(command);
|
|
@@ -17101,9 +17102,14 @@ const FILE_EXTENSIONS = "ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|yaml|yml|toml|p
|
|
|
17101
17102
|
const CODE_SPAN = /`([^`\n]{1,200})`/g;
|
|
17102
17103
|
const BARE_PATH = new RegExp(`(?<![\\w@./:~-])((?:\\./)?[\\w@.-]+(?:/[\\w@.*-]+)*\\.(?:${FILE_EXTENSIONS}))(?![\\w/-])`, "g");
|
|
17103
17104
|
const BARE_FOLDER = /(?<![\w@./:~-])((?:\.\/)?[\w@.-]+(?:\/[\w@.*-]+)+\/)(?![\w])/g;
|
|
17105
|
+
const TEMPLATE_SEGMENT = /^(?:<[A-Za-z_][\w-]*>|\{[A-Za-z_][\w-]*\}|\[[A-Za-z_][\w-]*\]|:[A-Za-z_][\w-]*)$/;
|
|
17104
17106
|
/** A token read as a path of the project, or undefined (a URL, an absolute path, a word). */
|
|
17105
17107
|
function pathOf$1(token) {
|
|
17106
17108
|
let text = token.trim().replaceAll("\\", "/").replace(/^\{project-root\}\//, "").replace(/^\.\//, "");
|
|
17109
|
+
if (text.includes("/")) {
|
|
17110
|
+
if (TEMPLATE_SEGMENT.test(text.split("/")[0] ?? "")) return void 0;
|
|
17111
|
+
text = text.split("/").map((part) => TEMPLATE_SEGMENT.test(part) ? "*" : part).join("/");
|
|
17112
|
+
}
|
|
17107
17113
|
text = text.replace(/[:#][\w,-]*$/, "");
|
|
17108
17114
|
if (text === "" || text.length > 200) return void 0;
|
|
17109
17115
|
if (/^(?:[a-z][a-z0-9+.-]*:|\/|~|[A-Za-z]:)/.test(text) || text.includes("//")) return void 0;
|
|
@@ -18353,7 +18359,7 @@ async function judge(context, index, request, root) {
|
|
|
18353
18359
|
kind: "refuse",
|
|
18354
18360
|
rule: "scope",
|
|
18355
18361
|
story: owner.id,
|
|
18356
|
-
message: `AgentPlugged refused this write: rule scope, story ${owner.id} does not name ${oneLine(relative, 120)}. If it is needed, ask the creator to widen the scope (guard.creator_action scope_widen {story: "${owner.id}", paths: ["${oneLine(relative, 120)}"]}); otherwise leave that file alone. Nothing was written.`
|
|
18362
|
+
message: `AgentPlugged refused this write: rule scope, story ${owner.id} does not name ${oneLine(relative, 120)}. If it is needed, ask the creator to widen the scope (guard.creator_action scope_widen {story: "${owner.id}", paths: ["${oneLine(relative, 120)}"]}); otherwise leave that file alone: do not write it by another tool or command (sed -i, a redirection, Set-Content, a copy, a script), and tell the coordinator which file you need and why. Nothing was written.`
|
|
18357
18363
|
};
|
|
18358
18364
|
if (request.text !== void 0 && index.decisions !== void 0) {
|
|
18359
18365
|
const broken = decisionViolation(index.decisions.guards, relative, request.text);
|
|
@@ -18363,7 +18369,7 @@ async function judge(context, index, request, root) {
|
|
|
18363
18369
|
story: owner.id,
|
|
18364
18370
|
decision: broken.decision,
|
|
18365
18371
|
detail: broken.detail,
|
|
18366
|
-
message: `AgentPlugged refused this write: decision ${broken.decision} of the architecture forbids it (${oneLine(broken.detail, 120)}). Follow the decision, or ask the creator to change it first. Nothing was written.`
|
|
18372
|
+
message: `AgentPlugged refused this write: decision ${broken.decision} of the architecture forbids it (${oneLine(broken.detail, 120)}). Follow the decision, or ask the creator to change it first; do not write the file by another tool or command. Nothing was written.`
|
|
18367
18373
|
};
|
|
18368
18374
|
}
|
|
18369
18375
|
if (placed === void 0) return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentplugged",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Install the AgentPlugged method in a repository and make its agents callable from Claude Code, Codex, Antigravity/Gemini CLI and Cursor.",
|
|
5
5
|
"releaseSummary": "The engine updates itself: your session moves to a newer version between two calls, with nothing to reconnect.",
|
|
6
6
|
"license": "FSL-1.1-MIT",
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"koffi": "~3.3.2"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@agentplugged/adapters-git": "1.0.
|
|
51
|
-
"@agentplugged/adapters-tools": "1.0.
|
|
52
|
-
"@agentplugged/core": "1.0.
|
|
53
|
-
"@agentplugged/crypto": "1.0.
|
|
54
|
-
"@agentplugged/i18n": "1.0.
|
|
55
|
-
"@agentplugged/mcp": "1.0.
|
|
50
|
+
"@agentplugged/adapters-git": "1.0.3",
|
|
51
|
+
"@agentplugged/adapters-tools": "1.0.3",
|
|
52
|
+
"@agentplugged/core": "1.0.3",
|
|
53
|
+
"@agentplugged/crypto": "1.0.3",
|
|
54
|
+
"@agentplugged/i18n": "1.0.3",
|
|
55
|
+
"@agentplugged/mcp": "1.0.3",
|
|
56
56
|
"@agentplugged/method": "0.0.0",
|
|
57
|
-
"@agentplugged/protocol": "1.0.
|
|
57
|
+
"@agentplugged/protocol": "1.0.3",
|
|
58
58
|
"@biomejs/biome": "~2.5.15",
|
|
59
59
|
"@modelcontextprotocol/client": "~2.3.0",
|
|
60
60
|
"@types/node": "^24.19.1",
|