@memorax/memorax-code 0.1.5 → 0.1.7

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.
Files changed (86) hide show
  1. package/README.md +7 -18
  2. package/bin/memorax-code-npm-preinstall.mjs +28 -39
  3. package/bin/memorax-code-plugin-postinstall.mjs +23 -1537
  4. package/bin/memorax-code-setup.mjs +1615 -0
  5. package/bin/memorax-code.mjs +289 -5
  6. package/docs/configuration.md +66 -17
  7. package/docs/troubleshooting.md +123 -7
  8. package/lib/memorax-code-adapter-common/src/config-utils.d.mts +5 -1
  9. package/lib/memorax-code-adapter-common/src/config-utils.mjs +36 -3
  10. package/lib/memorax-code-adapter-common/src/credentials/linux-secret-service.mjs +110 -0
  11. package/lib/memorax-code-adapter-common/src/credentials/macos-keychain.mjs +224 -0
  12. package/lib/memorax-code-adapter-common/src/credentials/secure-command.mjs +340 -0
  13. package/lib/memorax-code-adapter-common/src/credentials/trial-credential-record.d.mts +82 -0
  14. package/lib/memorax-code-adapter-common/src/credentials/trial-credential-record.mjs +183 -0
  15. package/lib/memorax-code-adapter-common/src/credentials/trial-credential-store.d.mts +82 -0
  16. package/lib/memorax-code-adapter-common/src/credentials/trial-credential-store.mjs +271 -0
  17. package/lib/memorax-code-adapter-common/src/credentials/windows-dpapi.mjs +440 -0
  18. package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +7 -4
  19. package/lib/memorax-code-adapter-common/src/setup-completion.d.mts +81 -0
  20. package/lib/memorax-code-adapter-common/src/setup-completion.mjs +154 -0
  21. package/lib/memorax-code-backend/dist/clients/claude/memory-hook-runtime.js +21 -2
  22. package/lib/memorax-code-backend/dist/clients/codex/memory-hook-runtime.js +23 -3
  23. package/lib/memorax-code-backend/dist/clients/opencode/memory-hook-runtime.js +19 -1
  24. package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +17 -16
  25. package/lib/memorax-code-backend/dist/lifecycle/backend/service.js +1 -0
  26. package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +81 -22
  27. package/lib/memorax-code-backend/dist/memorax-cli.js +3 -0
  28. package/lib/memorax-code-backend/dist/memory/automatic-retrieval.js +8 -2
  29. package/lib/memorax-code-backend/dist/memory/automatic-writeback.js +13 -1
  30. package/lib/memorax-code-backend/dist/memory/cli.js +10 -0
  31. package/lib/memorax-code-backend/dist/memory/quota-notice.js +208 -0
  32. package/lib/memorax-code-backend/dist/memory/service.js +11 -0
  33. package/lib/memorax-code-backend/dist/provider/memorax/adapter.js +4 -2
  34. package/lib/memorax-code-backend/dist/provider/memorax/http.js +10 -3
  35. package/lib/memorax-code-backend/dist/provider/memorax/quota.js +26 -0
  36. package/lib/memorax-code-backend/package.json +1 -1
  37. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  38. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  39. package/lib/memorax-code-claude-adapter/package.json +1 -1
  40. package/lib/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -3
  41. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +2 -0
  42. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +4 -2
  43. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  44. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  45. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.d.mts +5 -1
  46. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.mjs +36 -3
  47. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/linux-secret-service.mjs +110 -0
  48. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/macos-keychain.mjs +224 -0
  49. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/secure-command.mjs +340 -0
  50. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-record.d.mts +82 -0
  51. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-record.mjs +183 -0
  52. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-store.d.mts +82 -0
  53. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-store.mjs +271 -0
  54. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/windows-dpapi.mjs +440 -0
  55. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +7 -4
  56. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/setup-completion.d.mts +81 -0
  57. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/setup-completion.mjs +154 -0
  58. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  59. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -3
  60. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +2 -0
  61. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +4 -2
  62. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  63. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  64. package/lib/memorax-code-codex-adapter/package.json +1 -1
  65. package/lib/memorax-code-codex-adapter/runtime-hooks/memory-skill-reminder.mjs +2 -0
  66. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +2 -0
  67. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +4 -2
  68. package/lib/memorax-code-dsh-adapter/hooks/repo-memory-job.mjs +5 -2
  69. package/lib/memorax-code-dsh-adapter/package.json +1 -1
  70. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +2 -0
  71. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +4 -2
  72. package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +158 -7
  73. package/lib/memorax-code-dsh-adapter/src/runtime-state.mjs +21 -0
  74. package/lib/memorax-code-opencode-adapter/package.json +1 -1
  75. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +2 -0
  76. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +4 -2
  77. package/lib/memorax-code-opencode-adapter/src/plugin.mjs +20 -0
  78. package/lib/package-transition.mjs +312 -0
  79. package/lib/resolve-codex-command.mjs +32 -6
  80. package/lib/setup-memory-preferences.mjs +113 -0
  81. package/lib/setup-reconcile.mjs +128 -0
  82. package/lib/trial-plugin-mark.mjs +171 -0
  83. package/lib/trial-provision-client.mjs +307 -0
  84. package/lib/trial-provision-flow.mjs +215 -0
  85. package/lib/trial-setup.mjs +53 -0
  86. package/package.json +1 -1
@@ -127,14 +127,17 @@ export async function withJsonFileLockAsync(path, operation, options = {}) {
127
127
  const timeoutMs = positiveInteger(options.timeoutMs, JSON_FILE_LOCK_TIMEOUT_MS);
128
128
  const staleMs = positiveInteger(options.staleMs, JSON_FILE_LOCK_STALE_MS);
129
129
  const retryMs = positiveInteger(options.retryMs, JSON_FILE_LOCK_RETRY_MS);
130
+ const signal = options.signal;
130
131
  const lockPath = `${path}.lock`;
131
132
  const ownerId = `${process.pid}:${randomUUID()}`;
132
133
  const deadline = Date.now() + timeoutMs;
133
134
  const observedProcessStarts = new Map();
134
135
  const directory = dirname(path);
136
+ throwIfJsonFileLockAborted(signal, path, lockPath);
135
137
  ensurePrivateDirectory(directory, { durableBoundary: directory });
136
138
 
137
139
  while (!tryAcquireJsonFileLock(lockPath, ownerId)) {
140
+ throwIfJsonFileLockAborted(signal, path, lockPath);
138
141
  if (removeStaleJsonFileLock(lockPath, staleMs, observedProcessStarts)) continue;
139
142
  const remainingMs = deadline - Date.now();
140
143
  if (remainingMs <= 0) {
@@ -144,10 +147,11 @@ export async function withJsonFileLockAsync(path, operation, options = {}) {
144
147
  error.lockPath = lockPath;
145
148
  throw error;
146
149
  }
147
- await sleep(Math.min(retryMs, remainingMs));
150
+ await sleep(Math.min(retryMs, remainingMs), signal, path, lockPath);
148
151
  }
149
152
 
150
153
  try {
154
+ throwIfJsonFileLockAborted(signal, path, lockPath);
151
155
  return await operation();
152
156
  } finally {
153
157
  releaseJsonFileLock(lockPath, ownerId);
@@ -381,8 +385,37 @@ function sleepSync(milliseconds) {
381
385
  Atomics.wait(LOCK_SLEEP_BUFFER, 0, 0, Math.max(1, Math.trunc(milliseconds)));
382
386
  }
383
387
 
384
- function sleep(milliseconds) {
385
- return new Promise((resolve) => setTimeout(resolve, Math.max(1, Math.trunc(milliseconds))));
388
+ function sleep(milliseconds, signal, path, lockPath) {
389
+ const delayMs = Math.max(1, Math.trunc(milliseconds));
390
+ if (!signal) return new Promise((resolve) => setTimeout(resolve, delayMs));
391
+ throwIfJsonFileLockAborted(signal, path, lockPath);
392
+ return new Promise((resolve, reject) => {
393
+ let settled = false;
394
+ const finish = (error) => {
395
+ if (settled) return;
396
+ settled = true;
397
+ clearTimeout(timeout);
398
+ signal.removeEventListener?.("abort", onAbort);
399
+ if (error) reject(error);
400
+ else resolve();
401
+ };
402
+ const timeout = setTimeout(() => finish(), delayMs);
403
+ const onAbort = () => finish(jsonFileLockAbortError(path, lockPath));
404
+ signal.addEventListener?.("abort", onAbort, { once: true });
405
+ if (signal.aborted) onAbort();
406
+ });
407
+ }
408
+
409
+ function throwIfJsonFileLockAborted(signal, path, lockPath) {
410
+ if (signal?.aborted) throw jsonFileLockAbortError(path, lockPath);
411
+ }
412
+
413
+ function jsonFileLockAbortError(path, lockPath) {
414
+ const error = new Error(`aborted while waiting for JSON state lock: ${lockPath}`);
415
+ error.code = "JSON_FILE_LOCK_ABORTED";
416
+ error.path = path;
417
+ error.lockPath = lockPath;
418
+ return error;
386
419
  }
387
420
 
388
421
  function positiveInteger(value, fallback) {
@@ -0,0 +1,110 @@
1
+ import {
2
+ DEFAULT_SECURE_COMMAND_OUTPUT_BYTES,
3
+ DEFAULT_SECURE_COMMAND_TIMEOUT_MS,
4
+ decodeSecureCredential,
5
+ encodeSecureCredential,
6
+ executeSecureCommand,
7
+ minimalEnvironment,
8
+ runSecureCommand,
9
+ secureCredentialBackendError,
10
+ validateSecureCredentialNamespace,
11
+ wipeSecureCommandResult,
12
+ } from "./secure-command.mjs";
13
+
14
+ const BACKEND = "linux-secret-service";
15
+ const SECRET_TOOL = "/usr/bin/secret-tool";
16
+ const LINUX_ENVIRONMENT_NAMES = Object.freeze([
17
+ "DBUS_SESSION_BUS_ADDRESS",
18
+ "XDG_RUNTIME_DIR",
19
+ "DISPLAY",
20
+ "WAYLAND_DISPLAY",
21
+ "HOME",
22
+ "LANG",
23
+ "LC_ALL",
24
+ ]);
25
+
26
+ export function createLinuxSecretServiceBackend(options) {
27
+ const namespace = validateSecureCredentialNamespace(options?.namespace, BACKEND);
28
+ const runner = options?.runner ?? runSecureCommand;
29
+ const environment = minimalEnvironment(options?.environment ?? process.env, LINUX_ENVIRONMENT_NAMES);
30
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_SECURE_COMMAND_TIMEOUT_MS;
31
+ const maxOutputBytes = options?.maxOutputBytes ?? DEFAULT_SECURE_COMMAND_OUTPUT_BYTES;
32
+ const attributes = Object.freeze([
33
+ "application",
34
+ "memorax-code",
35
+ "record",
36
+ "trial-credentials",
37
+ "namespace",
38
+ namespace,
39
+ ]);
40
+
41
+ async function invoke(operation, args, input = undefined) {
42
+ return executeSecureCommand(runner, {
43
+ backend: BACKEND,
44
+ operation,
45
+ command: SECRET_TOOL,
46
+ args,
47
+ input,
48
+ env: environment,
49
+ timeoutMs,
50
+ maxOutputBytes,
51
+ });
52
+ }
53
+
54
+ return Object.freeze({
55
+ async load() {
56
+ const result = await invoke("load", ["lookup", "--", ...attributes]);
57
+ try {
58
+ if (result.status === 1
59
+ && result.stdout.length === 0
60
+ && result.stderr.length === 0) return null;
61
+ if (result.status !== 0 || result.signal !== null) {
62
+ throw secureCredentialBackendError(BACKEND, "load", "command_failed");
63
+ }
64
+ return decodeSecureCredential(result.stdout, BACKEND, "load");
65
+ } finally {
66
+ wipeSecureCommandResult(result);
67
+ }
68
+ },
69
+
70
+ async save(serialized) {
71
+ const input = encodeSecureCredential(serialized, BACKEND, "save");
72
+ try {
73
+ const result = await invoke(
74
+ "save",
75
+ [
76
+ "store",
77
+ "--label=MemoraX Code trial credentials",
78
+ "--",
79
+ ...attributes,
80
+ ],
81
+ input,
82
+ );
83
+ try {
84
+ if (result.status !== 0 || result.signal !== null) {
85
+ throw secureCredentialBackendError(BACKEND, "save", "command_failed");
86
+ }
87
+ } finally {
88
+ wipeSecureCommandResult(result);
89
+ }
90
+ } finally {
91
+ input.fill(0);
92
+ }
93
+ },
94
+
95
+ async delete() {
96
+ const result = await invoke("delete", ["clear", "--", ...attributes]);
97
+ try {
98
+ if (result.status === 1
99
+ && result.stdout.length === 0
100
+ && result.stderr.length === 0) return false;
101
+ if (result.status !== 0 || result.signal !== null) {
102
+ throw secureCredentialBackendError(BACKEND, "delete", "command_failed");
103
+ }
104
+ return true;
105
+ } finally {
106
+ wipeSecureCommandResult(result);
107
+ }
108
+ },
109
+ });
110
+ }
@@ -0,0 +1,224 @@
1
+ import {
2
+ DEFAULT_SECURE_COMMAND_OUTPUT_BYTES,
3
+ DEFAULT_SECURE_COMMAND_TIMEOUT_MS,
4
+ decodeSecureCredential,
5
+ encodeSecureCredential,
6
+ executeSecureCommand,
7
+ minimalEnvironment,
8
+ runSecureCommand,
9
+ secureCredentialBackendError,
10
+ validateSecureCredentialNamespace,
11
+ wipeSecureCommandResult,
12
+ } from "./secure-command.mjs";
13
+
14
+ const BACKEND = "macos-keychain";
15
+ const OSASCRIPT = "/usr/bin/osascript";
16
+ const KEYCHAIN_SERVICE = "net.memorax.memorax-code.trial";
17
+ const ITEM_NOT_FOUND_EXIT = 44;
18
+ const INVALID_SECRET_EXIT = 65;
19
+ const MACOS_ENVIRONMENT_NAMES = Object.freeze([
20
+ "HOME",
21
+ "LANG",
22
+ "LC_ALL",
23
+ "LOGNAME",
24
+ "TMPDIR",
25
+ "USER",
26
+ ]);
27
+
28
+ const JXA_PREFIX = String.raw`
29
+ ObjC.import("Foundation");
30
+ ObjC.import("Security");
31
+ ObjC.bindFunction("exit", ["void", ["int"]]);
32
+ const errSecItemNotFound = -25300;
33
+ const errSecDuplicateItem = -25299;
34
+ const maxSecretBytes = 4096;
35
+ function quit(code) { $.exit(code); }
36
+ function utf8Length(value) {
37
+ return Number(value.lengthOfBytesUsingEncoding($.NSUTF8StringEncoding));
38
+ }
39
+ function itemIdentity(argv) {
40
+ if (!argv || argv.length !== 2) quit(70);
41
+ const service = $(argv[0]);
42
+ const account = $(argv[1]);
43
+ if (utf8Length(service) < 1 || utf8Length(account) < 1) quit(70);
44
+ return { service, account };
45
+ }
46
+ function findItem(identity, passwordLength, passwordData, item) {
47
+ return $.SecKeychainFindGenericPassword(
48
+ $.nil,
49
+ utf8Length(identity.service),
50
+ identity.service.UTF8String,
51
+ utf8Length(identity.account),
52
+ identity.account.UTF8String,
53
+ passwordLength,
54
+ passwordData,
55
+ item
56
+ );
57
+ }
58
+ `;
59
+
60
+ const LOAD_SCRIPT = `${JXA_PREFIX}
61
+ function run(argv) {
62
+ const identity = itemIdentity(argv);
63
+ const passwordLength = Ref();
64
+ const passwordData = Ref();
65
+ const item = Ref();
66
+ const code = findItem(identity, passwordLength, passwordData, item);
67
+ if (code === errSecItemNotFound) quit(${ITEM_NOT_FOUND_EXIT});
68
+ if (code !== 0) quit(70);
69
+ let invalidSecret = false;
70
+ try {
71
+ const length = Number(passwordLength[0]);
72
+ if (length < 1 || length > maxSecretBytes) {
73
+ invalidSecret = true;
74
+ } else {
75
+ const secret = $.NSData.alloc.initWithBytesLength(passwordData[0], length);
76
+ $.NSFileHandle.fileHandleWithStandardOutput.writeData(secret);
77
+ }
78
+ } finally {
79
+ $.SecKeychainItemFreeContent($.nil, passwordData[0]);
80
+ }
81
+ if (invalidSecret) quit(${INVALID_SECRET_EXIT});
82
+ }
83
+ `;
84
+
85
+ const SAVE_SCRIPT = `${JXA_PREFIX}
86
+ function run(argv) {
87
+ const identity = itemIdentity(argv);
88
+ const secret = $.NSFileHandle.fileHandleWithStandardInput.readDataToEndOfFile;
89
+ const length = Number(secret.length);
90
+ if (length < 1 || length > maxSecretBytes) quit(${INVALID_SECRET_EXIT});
91
+ const item = Ref();
92
+ let code = findItem(identity, null, null, item);
93
+ if (code === 0) {
94
+ code = $.SecKeychainItemModifyAttributesAndData(
95
+ item[0],
96
+ $.nil,
97
+ length,
98
+ secret.bytes
99
+ );
100
+ } else if (code === errSecItemNotFound) {
101
+ code = $.SecKeychainAddGenericPassword(
102
+ $.nil,
103
+ utf8Length(identity.service),
104
+ identity.service.UTF8String,
105
+ utf8Length(identity.account),
106
+ identity.account.UTF8String,
107
+ length,
108
+ secret.bytes,
109
+ item
110
+ );
111
+ if (code === errSecDuplicateItem) {
112
+ code = findItem(identity, null, null, item);
113
+ if (code === 0) {
114
+ code = $.SecKeychainItemModifyAttributesAndData(
115
+ item[0],
116
+ $.nil,
117
+ length,
118
+ secret.bytes
119
+ );
120
+ }
121
+ }
122
+ }
123
+ if (code !== 0) quit(70);
124
+ }
125
+ `;
126
+
127
+ const DELETE_SCRIPT = `${JXA_PREFIX}
128
+ function run(argv) {
129
+ const identity = itemIdentity(argv);
130
+ const item = Ref();
131
+ const found = findItem(identity, null, null, item);
132
+ if (found === errSecItemNotFound) quit(${ITEM_NOT_FOUND_EXIT});
133
+ if (found !== 0) quit(70);
134
+ const deleted = $.SecKeychainItemDelete(item[0]);
135
+ if (deleted !== 0) quit(70);
136
+ }
137
+ `;
138
+
139
+ export function createMacosKeychainBackend(options) {
140
+ const namespace = validateSecureCredentialNamespace(options?.namespace, BACKEND);
141
+ const runner = options?.runner ?? runSecureCommand;
142
+ const environment = minimalEnvironment(
143
+ options?.environment ?? process.env,
144
+ MACOS_ENVIRONMENT_NAMES,
145
+ );
146
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_SECURE_COMMAND_TIMEOUT_MS;
147
+ const maxOutputBytes = options?.maxOutputBytes ?? DEFAULT_SECURE_COMMAND_OUTPUT_BYTES;
148
+ const account = `credential-v1:${namespace}`;
149
+
150
+ async function invoke(operation, script, input = undefined) {
151
+ return executeSecureCommand(runner, {
152
+ backend: BACKEND,
153
+ operation,
154
+ command: OSASCRIPT,
155
+ args: ["-l", "JavaScript", "-e", script, "--", KEYCHAIN_SERVICE, account],
156
+ input,
157
+ env: environment,
158
+ timeoutMs,
159
+ maxOutputBytes,
160
+ });
161
+ }
162
+
163
+ async function load() {
164
+ const result = await invoke("load", LOAD_SCRIPT);
165
+ try {
166
+ if (isEmptyExit(result, ITEM_NOT_FOUND_EXIT)) return null;
167
+ if (result.status === INVALID_SECRET_EXIT) {
168
+ throw secureCredentialBackendError(BACKEND, "load", "invalid_response");
169
+ }
170
+ if (result.status !== 0 || result.signal !== null || result.stderr.length !== 0) {
171
+ throw secureCredentialBackendError(BACKEND, "load", "command_failed");
172
+ }
173
+ return decodeSecureCredential(result.stdout, BACKEND, "load");
174
+ } finally {
175
+ wipeSecureCommandResult(result);
176
+ }
177
+ }
178
+
179
+ return Object.freeze({
180
+ load,
181
+
182
+ async save(serialized) {
183
+ const input = encodeSecureCredential(serialized, BACKEND, "save");
184
+ try {
185
+ const result = await invoke("save", SAVE_SCRIPT, input);
186
+ try {
187
+ if (result.status !== 0
188
+ || result.signal !== null
189
+ || result.stdout.length !== 0
190
+ || result.stderr.length !== 0) {
191
+ throw secureCredentialBackendError(BACKEND, "save", "command_failed");
192
+ }
193
+ } finally {
194
+ wipeSecureCommandResult(result);
195
+ }
196
+ } finally {
197
+ input.fill(0);
198
+ }
199
+ },
200
+
201
+ async delete() {
202
+ const result = await invoke("delete", DELETE_SCRIPT);
203
+ try {
204
+ if (isEmptyExit(result, ITEM_NOT_FOUND_EXIT)) return false;
205
+ if (result.status !== 0
206
+ || result.signal !== null
207
+ || result.stdout.length !== 0
208
+ || result.stderr.length !== 0) {
209
+ throw secureCredentialBackendError(BACKEND, "delete", "command_failed");
210
+ }
211
+ } finally {
212
+ wipeSecureCommandResult(result);
213
+ }
214
+ return true;
215
+ },
216
+ });
217
+ }
218
+
219
+ function isEmptyExit(result, status) {
220
+ return result.status === status
221
+ && result.signal === null
222
+ && result.stdout.length === 0
223
+ && result.stderr.length === 0;
224
+ }