agentplugged 1.0.1 → 1.0.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/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), through `@napi-rs/keyring`. Tests pass their own service name
6523
- * and delete their entries afterwards: the machine's real entries are never touched.
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
- /** The entry of `account`; on Linux, in the Secret Service only (`KEYCHAIN_ENTRY_OPTIONS`). */
6551
- async #entry(account) {
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 entry = await explained(() => this.#entry(account));
7042
+ const backend = await explained(() => this.#backend());
6563
7043
  try {
6564
- await entry.setPassword(value);
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.#entry(this.account)).deleteCredential());
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 products = await load().then(({ findCredentialsAsync }) => findCredentialsAsync(this.service)).then((found) => found.map((entry) => entry.account).filter((name) => name.startsWith(PRODUCT_ACCOUNT))).catch(() => []);
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 (await this.#entry(account)).deleteCredential() || deleted;
7096
+ ]) deleted = await backend.remove(this.service, account) || deleted;
6616
7097
  return deleted;
6617
7098
  }
6618
7099
  };
@@ -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 macOS, the system keychain is part of the system: restart the session and try again."
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 macOS, le trousseau fait partie du système : redémarre la session et réessaie."
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentplugged",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
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.1",
51
- "@agentplugged/adapters-tools": "1.0.1",
52
- "@agentplugged/core": "1.0.1",
53
- "@agentplugged/crypto": "1.0.1",
54
- "@agentplugged/i18n": "1.0.1",
55
- "@agentplugged/mcp": "1.0.1",
50
+ "@agentplugged/adapters-git": "1.0.2",
51
+ "@agentplugged/adapters-tools": "1.0.2",
52
+ "@agentplugged/core": "1.0.2",
53
+ "@agentplugged/crypto": "1.0.2",
54
+ "@agentplugged/i18n": "1.0.2",
55
+ "@agentplugged/mcp": "1.0.2",
56
56
  "@agentplugged/method": "0.0.0",
57
- "@agentplugged/protocol": "1.0.1",
57
+ "@agentplugged/protocol": "1.0.2",
58
58
  "@biomejs/biome": "~2.5.15",
59
59
  "@modelcontextprotocol/client": "~2.3.0",
60
60
  "@types/node": "^24.19.1",