vaultkeeper 0.5.3 → 0.7.0

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.
@@ -1,18 +1,54 @@
1
- import { createClient, DesktopAuth, DesktopSessionExpiredError } from '@1password/sdk';
2
1
  import { existsSync, readFileSync } from 'fs';
3
2
  import { dirname, resolve } from 'path';
4
3
  import { fileURLToPath } from 'url';
5
4
 
6
- // src/backend/one-password-worker.ts
5
+ // src/backend/one-password-constants.ts
6
+
7
+ // src/errors.ts
8
+ var VaultError = class extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "VaultError";
12
+ }
13
+ };
14
+ var SetupError = class extends VaultError {
15
+ /**
16
+ * The name of the dependency that caused the setup failure.
17
+ */
18
+ dependency;
19
+ constructor(message, dependency) {
20
+ super(message);
21
+ this.name = "SetupError";
22
+ this.dependency = dependency;
23
+ }
24
+ };
25
+
26
+ // src/backend/one-password-constants.ts
7
27
  var INTEGRATION_NAME = "vaultkeeper";
28
+ var SDK_PACKAGE = "@1password/sdk";
29
+ var SDK_NOT_INSTALLED_MESSAGE = `1Password SDK (${SDK_PACKAGE}) is not installed. Install it to use the 1Password backend.`;
30
+ var PRESENCE_WRITE_TIMEOUT_MS = 3e4;
31
+ function isModuleNotFoundError(error) {
32
+ const hasNotFoundCode = (value) => {
33
+ if (value === null || typeof value !== "object" || !("code" in value)) {
34
+ return false;
35
+ }
36
+ const { code } = value;
37
+ return code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND";
38
+ };
39
+ if (hasNotFoundCode(error)) {
40
+ return true;
41
+ }
42
+ if (error !== null && typeof error === "object" && "cause" in error) {
43
+ return hasNotFoundCode(error.cause);
44
+ }
45
+ return false;
46
+ }
8
47
  var cachedVersion;
9
48
  function getIntegrationVersion() {
10
49
  if (cachedVersion !== void 0) return cachedVersion;
11
50
  const dir = dirname(fileURLToPath(import.meta.url));
12
- const candidates = [
13
- resolve(dir, "..", "..", "package.json"),
14
- resolve(dir, "..", "package.json")
15
- ];
51
+ const candidates = [resolve(dir, "..", "..", "package.json"), resolve(dir, "..", "package.json")];
16
52
  for (const candidate of candidates) {
17
53
  if (!existsSync(candidate)) continue;
18
54
  const raw = JSON.parse(readFileSync(candidate, "utf8"));
@@ -21,80 +57,254 @@ function getIntegrationVersion() {
21
57
  return cachedVersion;
22
58
  }
23
59
  }
24
- throw new Error(
25
- `Could not read version from vaultkeeper package.json. Tried paths: ${candidates.join(", ")}`
60
+ throw new SetupError(
61
+ `Could not read version from vaultkeeper package.json. Tried paths: ${candidates.join(", ")}`,
62
+ "vaultkeeper package.json"
26
63
  );
27
64
  }
28
65
 
29
- // src/backend/one-password-worker.ts
66
+ // src/backend/one-password-item-ops.ts
30
67
  var TAG = "vaultkeeper";
31
68
  var PASSWORD_FIELD_TITLE = "password";
32
- function writeSuccess(value) {
69
+ async function findItemOverviewByTitle(client, vaultId, title) {
70
+ const overviews = await client.items.list(vaultId);
71
+ for (const overview of overviews) {
72
+ if (overview.title === title && overview.tags.includes(TAG)) {
73
+ return overview;
74
+ }
75
+ }
76
+ return void 0;
77
+ }
78
+ async function findItemByTitle(client, vaultId, title) {
79
+ const overview = await findItemOverviewByTitle(client, vaultId, title);
80
+ if (overview === void 0) return void 0;
81
+ return client.items.get(vaultId, overview.id);
82
+ }
83
+ function extractPasswordField(item) {
84
+ for (const field of item.fields) {
85
+ if (field.title === PASSWORD_FIELD_TITLE) {
86
+ return field.value;
87
+ }
88
+ }
89
+ return void 0;
90
+ }
91
+ async function storeSecretItem(client, vaultId, title, secret, passwordCategory, concealedFieldType) {
92
+ const existing = await findItemByTitle(client, vaultId, title);
93
+ if (existing !== void 0) {
94
+ const hasPasswordField = existing.fields.some((f) => f.title === PASSWORD_FIELD_TITLE);
95
+ const updatedFields = hasPasswordField ? existing.fields.map((f) => f.title === PASSWORD_FIELD_TITLE ? { ...f, value: secret } : f) : [
96
+ ...existing.fields,
97
+ {
98
+ id: "password",
99
+ title: PASSWORD_FIELD_TITLE,
100
+ fieldType: concealedFieldType,
101
+ value: secret
102
+ }
103
+ ];
104
+ await client.items.put({ ...existing, fields: updatedFields });
105
+ } else {
106
+ await client.items.create({
107
+ category: passwordCategory,
108
+ vaultId,
109
+ title,
110
+ tags: [TAG],
111
+ fields: [
112
+ {
113
+ id: "password",
114
+ title: PASSWORD_FIELD_TITLE,
115
+ fieldType: concealedFieldType,
116
+ value: secret
117
+ }
118
+ ]
119
+ });
120
+ }
121
+ }
122
+ async function deleteSecretItem(client, vaultId, title) {
123
+ const overview = await findItemOverviewByTitle(client, vaultId, title);
124
+ if (overview === void 0) return false;
125
+ await client.items.delete(vaultId, overview.id);
126
+ return true;
127
+ }
128
+
129
+ // src/backend/one-password-worker.ts
130
+ var VALID_OPS = ["retrieve", "store", "delete"];
131
+ function isValidOp(value) {
132
+ if (value === void 0) return false;
133
+ for (const op of VALID_OPS) {
134
+ if (op === value) return true;
135
+ }
136
+ return false;
137
+ }
138
+ function writeRetrieveSuccess(value) {
33
139
  const response = { value };
34
140
  process.stdout.write(JSON.stringify(response));
35
141
  }
142
+ function writeWriteSuccess() {
143
+ const response = { ok: true };
144
+ process.stdout.write(JSON.stringify(response));
145
+ }
36
146
  function writeFailure(error, code) {
37
147
  const response = { error, code };
38
148
  process.stdout.write(JSON.stringify(response));
39
149
  }
150
+ function readStdin() {
151
+ return new Promise((resolve2, reject) => {
152
+ const chunks = [];
153
+ process.stdin.setEncoding("utf8");
154
+ process.stdin.on("data", (chunk) => {
155
+ chunks.push(chunk);
156
+ });
157
+ process.stdin.on("end", () => {
158
+ resolve2(chunks.join(""));
159
+ });
160
+ process.stdin.on("error", (err) => {
161
+ reject(err instanceof Error ? err : new Error(String(err)));
162
+ });
163
+ });
164
+ }
165
+ var PresenceTimeoutSentinel = class extends Error {
166
+ };
40
167
  async function main() {
41
- const [, , accountName, vaultId, secretId] = process.argv;
168
+ const [, , accountName, vaultId, secretId, opArg] = process.argv;
42
169
  if (accountName === void 0 || vaultId === void 0 || secretId === void 0) {
43
170
  writeFailure("Worker invoked with missing arguments", "INTERNAL");
44
171
  process.exit(1);
45
172
  }
46
- let client;
173
+ if (opArg !== void 0 && !isValidOp(opArg)) {
174
+ writeFailure(`Worker invoked with unknown operation: ${opArg}`, "INTERNAL");
175
+ process.exit(1);
176
+ }
177
+ const op = isValidOp(opArg) ? opArg : "retrieve";
178
+ let sdk;
47
179
  try {
48
- client = await createClient({
49
- auth: new DesktopAuth(accountName),
50
- integrationName: INTEGRATION_NAME,
51
- integrationVersion: getIntegrationVersion()
52
- });
180
+ sdk = await import('@1password/sdk');
53
181
  } catch (err) {
54
- if (err instanceof DesktopSessionExpiredError) {
55
- writeFailure("1Password session has expired", "LOCKED");
182
+ if (isModuleNotFoundError(err)) {
183
+ writeFailure(SDK_NOT_INSTALLED_MESSAGE, "PLUGIN_NOT_FOUND");
56
184
  } else {
57
- writeFailure(`Authentication failed: ${String(err)}`, "AUTH_DENIED");
185
+ writeFailure(`Failed to load 1Password SDK: ${String(err)}`, "INTERNAL");
58
186
  }
59
187
  process.exit(1);
60
188
  }
61
- let overviews;
189
+ const { createClient, DesktopAuth, DesktopSessionExpiredError, RateLimitExceededError } = sdk;
190
+ let pendingSecret;
191
+ if (op === "store") {
192
+ try {
193
+ pendingSecret = await readStdin();
194
+ } catch (err) {
195
+ writeFailure(`Failed to read secret value from stdin: ${String(err)}`, "INTERNAL");
196
+ process.exit(1);
197
+ }
198
+ }
199
+ let client;
200
+ if (op === "retrieve") {
201
+ try {
202
+ client = await createClient({
203
+ auth: new DesktopAuth(accountName),
204
+ integrationName: INTEGRATION_NAME,
205
+ integrationVersion: getIntegrationVersion()
206
+ });
207
+ } catch (err) {
208
+ if (err instanceof DesktopSessionExpiredError) {
209
+ writeFailure("1Password session has expired", "LOCKED");
210
+ } else {
211
+ writeFailure(`Authentication failed: ${String(err)}`, "AUTH_DENIED");
212
+ }
213
+ process.exit(1);
214
+ }
215
+ } else {
216
+ let timerId;
217
+ const timeoutPromise = new Promise((_resolve, reject) => {
218
+ timerId = setTimeout(() => {
219
+ reject(new PresenceTimeoutSentinel("presence action timed out"));
220
+ }, PRESENCE_WRITE_TIMEOUT_MS);
221
+ });
222
+ try {
223
+ client = await Promise.race([
224
+ createClient({
225
+ auth: new DesktopAuth(accountName),
226
+ integrationName: INTEGRATION_NAME,
227
+ integrationVersion: getIntegrationVersion()
228
+ }),
229
+ timeoutPromise
230
+ ]);
231
+ } catch (err) {
232
+ if (err instanceof PresenceTimeoutSentinel) {
233
+ writeFailure(
234
+ `No fresh presence action within ${String(PRESENCE_WRITE_TIMEOUT_MS)}ms`,
235
+ "PRESENCE_TIMEOUT"
236
+ );
237
+ } else if (err instanceof DesktopSessionExpiredError) {
238
+ writeFailure("1Password session has expired", "LOCKED");
239
+ } else if (typeof RateLimitExceededError === "function" && err instanceof RateLimitExceededError) {
240
+ writeFailure(`1Password rate limit exceeded: ${String(err)}`, "INTERNAL");
241
+ } else {
242
+ writeFailure(`1Password presence action declined: ${String(err)}`, "PRESENCE_DECLINED");
243
+ }
244
+ process.exit(1);
245
+ } finally {
246
+ if (timerId !== void 0) {
247
+ clearTimeout(timerId);
248
+ }
249
+ }
250
+ }
251
+ if (op === "store") {
252
+ try {
253
+ await storeSecretItem(
254
+ client,
255
+ vaultId,
256
+ secretId,
257
+ pendingSecret ?? "",
258
+ sdk.ItemCategory.Password,
259
+ sdk.ItemFieldType.Concealed
260
+ );
261
+ } catch (err) {
262
+ writeFailure(`Failed to store item: ${String(err)}`, "INTERNAL");
263
+ process.exit(1);
264
+ }
265
+ writeWriteSuccess();
266
+ return;
267
+ }
268
+ if (op === "delete") {
269
+ let deleted;
270
+ try {
271
+ deleted = await deleteSecretItem(client, vaultId, secretId);
272
+ } catch (err) {
273
+ writeFailure(`Failed to delete item: ${String(err)}`, "INTERNAL");
274
+ process.exit(1);
275
+ return;
276
+ }
277
+ if (!deleted) {
278
+ writeFailure(`Secret not found: ${secretId}`, "NOT_FOUND");
279
+ process.exit(1);
280
+ }
281
+ writeWriteSuccess();
282
+ return;
283
+ }
284
+ let overview;
62
285
  try {
63
- overviews = await client.items.list(vaultId);
286
+ overview = await findItemOverviewByTitle(client, vaultId, secretId);
64
287
  } catch (err) {
65
288
  writeFailure(`Failed to list items: ${String(err)}`, "INTERNAL");
66
289
  process.exit(1);
67
290
  }
68
- let targetId;
69
- for (const overview of overviews) {
70
- if (overview.title === secretId && overview.tags.includes(TAG)) {
71
- targetId = overview.id;
72
- break;
73
- }
74
- }
75
- if (targetId === void 0) {
291
+ if (overview === void 0) {
76
292
  writeFailure(`Secret not found: ${secretId}`, "NOT_FOUND");
77
293
  process.exit(1);
78
294
  }
79
295
  let item;
80
296
  try {
81
- item = await client.items.get(vaultId, targetId);
297
+ item = await client.items.get(vaultId, overview.id);
82
298
  } catch (err) {
83
299
  writeFailure(`Failed to retrieve item: ${String(err)}`, "NOT_FOUND");
84
300
  process.exit(1);
85
301
  }
86
- let secretValue;
87
- for (const field of item.fields) {
88
- if (field.title === PASSWORD_FIELD_TITLE) {
89
- secretValue = field.value;
90
- break;
91
- }
92
- }
302
+ const secretValue = extractPasswordField(item);
93
303
  if (secretValue === void 0) {
94
304
  writeFailure(`Item found but missing password field: ${secretId}`, "NOT_FOUND");
95
305
  process.exit(1);
96
306
  }
97
- writeSuccess(secretValue);
307
+ writeRetrieveSuccess(secretValue);
98
308
  }
99
309
  main().catch((err) => {
100
310
  writeFailure(`Unexpected worker error: ${String(err)}`, "INTERNAL");
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/backend/one-password-constants.ts","../src/backend/one-password-worker.ts"],"names":[],"mappings":"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","file":"one-password-worker.js","sourcesContent":["/**\n * Shared constants for 1Password SDK integration.\n *\n * @remarks\n * Centralised here so the backend, worker, and discovery modules stay in sync.\n *\n * @internal\n */\n\nimport { readFileSync, existsSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** Name reported to the 1Password SDK for integration tracking. */\nexport const INTEGRATION_NAME = 'vaultkeeper'\n\nlet cachedVersion: string | undefined\n\n/**\n * Version reported to the 1Password SDK.\n *\n * @remarks\n * Lazily derived from packages/vaultkeeper/package.json on first call so that\n * consumers who never use the 1Password backend pay no I/O cost at import time.\n * The result is memoized for subsequent calls.\n *\n * @internal\n */\nexport function getIntegrationVersion(): string {\n if (cachedVersion !== undefined) return cachedVersion\n\n const dir = dirname(fileURLToPath(import.meta.url))\n // Source: src/backend/ → ../../package.json\n // Bundled: dist/ → ../package.json\n const candidates = [\n resolve(dir, '..', '..', 'package.json'),\n resolve(dir, '..', 'package.json'),\n ]\n for (const candidate of candidates) {\n if (!existsSync(candidate)) continue\n const raw: unknown = JSON.parse(readFileSync(candidate, 'utf8'))\n if (\n raw !== null &&\n typeof raw === 'object' &&\n 'version' in raw &&\n typeof raw.version === 'string'\n ) {\n cachedVersion = raw.version\n return cachedVersion\n }\n }\n throw new Error(\n `Could not read version from vaultkeeper package.json. Tried paths: ${candidates.join(', ')}`,\n )\n}\n","/**\n * Per-access worker script for the 1Password SDK backend.\n *\n * @remarks\n * This script is spawned as a child process by `OnePasswordBackend` when\n * `accessMode` is set to `'per-access'`. It creates a fresh SDK client\n * (which triggers a biometric prompt via the desktop app), retrieves a single\n * secret, writes the result to stdout as JSON, then exits immediately.\n *\n * argv layout:\n * node one-password-worker.js <accountName> <vaultId> <secretId>\n *\n * stdout on success: `{ \"value\": \"<secret>\" }`\n * stdout on failure: `{ \"error\": \"<message>\", \"code\": \"<code>\" }`\n */\n\nimport { createClient, DesktopAuth, DesktopSessionExpiredError } from '@1password/sdk'\n\nconst TAG = 'vaultkeeper'\nconst PASSWORD_FIELD_TITLE = 'password'\nimport { INTEGRATION_NAME, getIntegrationVersion } from './one-password-constants.js'\n\ninterface SuccessResponse {\n value: string\n}\n\ninterface FailureResponse {\n error: string\n code: string\n}\n\nfunction writeSuccess(value: string): void {\n const response: SuccessResponse = { value }\n process.stdout.write(JSON.stringify(response))\n}\n\nfunction writeFailure(error: string, code: string): void {\n const response: FailureResponse = { error, code }\n process.stdout.write(JSON.stringify(response))\n}\n\nasync function main(): Promise<void> {\n const [, , accountName, vaultId, secretId] = process.argv\n\n if (accountName === undefined || vaultId === undefined || secretId === undefined) {\n writeFailure('Worker invoked with missing arguments', 'INTERNAL')\n process.exit(1)\n }\n\n let client\n try {\n client = await createClient({\n auth: new DesktopAuth(accountName),\n integrationName: INTEGRATION_NAME,\n integrationVersion: getIntegrationVersion(),\n })\n } catch (err) {\n if (err instanceof DesktopSessionExpiredError) {\n writeFailure('1Password session has expired', 'LOCKED')\n } else {\n writeFailure(`Authentication failed: ${String(err)}`, 'AUTH_DENIED')\n }\n process.exit(1)\n }\n\n let overviews\n try {\n overviews = await client.items.list(vaultId)\n } catch (err) {\n writeFailure(`Failed to list items: ${String(err)}`, 'INTERNAL')\n process.exit(1)\n }\n\n let targetId: string | undefined\n for (const overview of overviews) {\n if (overview.title === secretId && overview.tags.includes(TAG)) {\n targetId = overview.id\n break\n }\n }\n\n if (targetId === undefined) {\n writeFailure(`Secret not found: ${secretId}`, 'NOT_FOUND')\n process.exit(1)\n }\n\n let item\n try {\n item = await client.items.get(vaultId, targetId)\n } catch (err) {\n writeFailure(`Failed to retrieve item: ${String(err)}`, 'NOT_FOUND')\n process.exit(1)\n }\n\n let secretValue: string | undefined\n for (const field of item.fields) {\n if (field.title === PASSWORD_FIELD_TITLE) {\n secretValue = field.value\n break\n }\n }\n\n if (secretValue === undefined) {\n writeFailure(`Item found but missing password field: ${secretId}`, 'NOT_FOUND')\n process.exit(1)\n }\n\n writeSuccess(secretValue)\n}\n\nmain().catch((err: unknown) => {\n writeFailure(`Unexpected worker error: ${String(err)}`, 'INTERNAL')\n process.exit(1)\n})\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/backend/one-password-constants.ts","../src/backend/one-password-item-ops.ts","../src/backend/one-password-worker.ts"],"names":["resolve"],"mappings":"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","file":"one-password-worker.js","sourcesContent":["/**\n * Error hierarchy for vaultkeeper.\n */\n\n/** Base error for all vaultkeeper errors. */\nexport class VaultError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'VaultError'\n }\n}\n\n// --- Backend Access Failures ---\n\n/**\n * Thrown when the backend keychain or credential store is locked and requires\n * user interaction (e.g. biometric prompt or password entry) before access can\n * be granted.\n */\nexport class BackendLockedError extends VaultError {\n /**\n * Whether the lock can be resolved through an interactive user prompt.\n * When `true`, callers may retry after prompting the user.\n */\n readonly interactive: boolean\n\n constructor(message: string, interactive: boolean) {\n super(message)\n this.name = 'BackendLockedError'\n this.interactive = interactive\n }\n}\n\n/**\n * Thrown when a hardware device (e.g. YubiKey or smart card) required for\n * authentication is not currently connected.\n */\nexport class DeviceNotPresentError extends VaultError {\n /**\n * How long (in milliseconds) the operation waited for the device before\n * giving up.\n */\n readonly timeoutMs: number\n\n constructor(message: string, timeoutMs: number) {\n super(message)\n this.name = 'DeviceNotPresentError'\n this.timeoutMs = timeoutMs\n }\n}\n\n/**\n * Thrown when the user explicitly denies an authorization request for a\n * secret access operation (e.g. cancels an OS permission dialog).\n */\nexport class AuthorizationDeniedError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'AuthorizationDeniedError'\n }\n}\n\n/**\n * Thrown when an operation requires a backend capability (e.g.\n * presence-per-use) that the active backend cannot provide.\n *\n * @remarks\n * This is a configuration/backend mismatch, not a runtime authorization\n * failure: the requirement was asserted against a backend whose configured\n * instance does not advertise the capability, so no credential, session, or\n * device is ever touched before this is thrown. It is fixable by switching to\n * (or reconfiguring) a backend that provides the capability — inspect\n * {@link NotCapableError.capability} for which one was required and\n * {@link NotCapableError.backendType} for the backend that lacked it. Distinct\n * from {@link AuthorizationDeniedError} (a human/token rejection) and\n * {@link PresenceDeclinedError} (a human declined a fresh action).\n *\n * @public\n */\nexport class NotCapableError extends VaultError {\n /** The `type` identifier of the active backend that lacked the capability. */\n readonly backendType: string\n\n /**\n * The machine-readable capability key that was required but not advertised\n * (e.g. `'presencePerUse'`).\n */\n readonly capability: string\n\n constructor(message: string, backendType: string, capability: string) {\n super(message)\n this.name = 'NotCapableError'\n this.backendType = backendType\n this.capability = capability\n }\n}\n\n/**\n * Thrown when a required fresh, per-use human presence action was explicitly\n * declined by the human (e.g. a biometric or touch prompt was cancelled).\n *\n * @remarks\n * Distinct from {@link AuthorizationDeniedError}, which signals a token or\n * capability rejection, and from {@link PresenceTimeoutError}, which signals the\n * device was present but no action happened in time. A declined presence action\n * means the human was asked and said no.\n *\n * @public\n */\nexport class PresenceDeclinedError extends VaultError {\n /** The `type` identifier of the backend that requested the presence action. */\n readonly backendType: string\n\n constructor(message: string, backendType: string) {\n super(message)\n this.name = 'PresenceDeclinedError'\n this.backendType = backendType\n }\n}\n\n/**\n * Thrown when a required fresh, per-use human presence action did not happen\n * within the allotted time — the device was present and ready, but no touch,\n * tap, or biometric approval was performed before the timeout elapsed.\n *\n * @remarks\n * Distinct from {@link DeviceNotPresentError} (the device itself was absent) and\n * from {@link PresenceDeclinedError} (the human actively declined). Inspect\n * {@link PresenceTimeoutError.timeoutMs} for how long the operation waited.\n *\n * @public\n */\nexport class PresenceTimeoutError extends VaultError {\n /** The `type` identifier of the backend that requested the presence action. */\n readonly backendType: string\n\n /** How long (in milliseconds) the operation waited for the presence action. */\n readonly timeoutMs: number\n\n constructor(message: string, backendType: string, timeoutMs: number) {\n super(message)\n this.name = 'PresenceTimeoutError'\n this.backendType = backendType\n this.timeoutMs = timeoutMs\n }\n}\n\n/**\n * Thrown when no configured backend is available or reachable.\n * Inspect `reason` for a machine-readable cause and `attempted` for the list\n * of backend types that were tried.\n */\nexport class BackendUnavailableError extends VaultError {\n /**\n * Machine-readable reason code describing why the backend is unavailable\n * (e.g. `'none-enabled'`, `'all-failed'`).\n */\n readonly reason: string\n\n /**\n * The backend type identifiers that were attempted before this error was\n * thrown.\n */\n readonly attempted: string[]\n\n constructor(message: string, reason: string, attempted: string[]) {\n super(message)\n this.name = 'BackendUnavailableError'\n this.reason = reason\n this.attempted = attempted\n }\n}\n\n/**\n * Thrown when a required backend plugin (e.g. a third-party credential\n * manager) is not installed on the current system.\n */\nexport class PluginNotFoundError extends VaultError {\n /**\n * The plugin package or binary name that was not found.\n */\n readonly plugin: string\n\n /**\n * A URL pointing to installation instructions for the missing plugin.\n */\n readonly installUrl: string\n\n constructor(message: string, plugin: string, installUrl: string) {\n super(message)\n this.name = 'PluginNotFoundError'\n this.plugin = plugin\n this.installUrl = installUrl\n }\n}\n\n/**\n * Thrown when a requested secret does not exist in the backend store.\n */\nexport class SecretNotFoundError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'SecretNotFoundError'\n }\n}\n\n// --- JWE Lifecycle Failures ---\n\n/**\n * Thrown when a JWE token has passed its expiration time (`exp` claim).\n */\nexport class TokenExpiredError extends VaultError {\n /**\n * Whether the token can be refreshed by calling `setup()` again.\n * When `true`, the secret still exists in the backend and a new token can be\n * issued.\n */\n readonly canRefresh: boolean\n\n constructor(message: string, canRefresh: boolean) {\n super(message)\n this.name = 'TokenExpiredError'\n this.canRefresh = canRefresh\n }\n}\n\n/**\n * Thrown when the encryption key that was used to create a JWE has since been\n * rotated out of the grace period and can no longer be used for decryption.\n */\nexport class KeyRotatedError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'KeyRotatedError'\n }\n}\n\n/**\n * Thrown when the encryption key referenced by a JWE's `kid` header has been\n * explicitly revoked and is no longer available for decryption.\n */\nexport class KeyRevokedError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'KeyRevokedError'\n }\n}\n\n/**\n * Thrown when a JWE token has been explicitly blocked (e.g. after a single-use\n * token has already been consumed).\n */\nexport class TokenRevokedError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'TokenRevokedError'\n }\n}\n\n/**\n * Thrown when a token with a finite `use` limit has been presented more times\n * than the limit allows.\n */\nexport class UsageLimitExceededError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'UsageLimitExceededError'\n }\n}\n\n// --- Identity and Trust Failures ---\n\n/**\n * Thrown when the hash of an executable no longer matches the previously\n * approved hash stored in the trust manifest (TOFU conflict).\n *\n * Callers must re-approve the executable before a new token can be issued for\n * it.\n */\nexport class IdentityMismatchError extends VaultError {\n /**\n * The hash that was recorded in the trust manifest at approval time.\n */\n readonly previousHash: string\n\n /**\n * The hash computed from the executable at the current moment.\n */\n readonly currentHash: string\n\n constructor(message: string, previousHash: string, currentHash: string) {\n super(message)\n this.name = 'IdentityMismatchError'\n this.previousHash = previousHash\n this.currentHash = currentHash\n }\n}\n\n/**\n * Thrown by {@link VaultKeeper.setup} when the caller does not make an\n * unambiguous executable-trust decision.\n *\n * `setup()` deliberately has no default trust behaviour: the caller must either\n * pass a real `executablePath` (which runs trust-on-first-use verification) or\n * explicitly opt out with `skipTrust: true` (a development-only escape hatch).\n * Supplying neither — or both at once — throws this error rather than silently\n * skipping verification. Passing the retired `'dev'` sentinel as `executablePath`\n * also throws this error. Inspect {@link ExecutableTrustRequiredError.reason} to\n * distinguish the cases.\n *\n * @public\n */\nexport class ExecutableTrustRequiredError extends VaultError {\n /**\n * Machine-readable discriminator for why the trust choice was rejected.\n * `'missing-choice'` means neither `executablePath` nor `skipTrust: true`\n * was provided, so no trust decision was expressed. `'conflicting-choice'`\n * means both `executablePath` and `skipTrust: true` were provided, which\n * are mutually exclusive intents. `'legacy-dev-sentinel'` means\n * `executablePath` was the retired literal `'dev'` opt-out sentinel, which is\n * no longer supported and must be replaced with `skipTrust: true`.\n */\n readonly reason: 'missing-choice' | 'conflicting-choice' | 'legacy-dev-sentinel'\n\n constructor(\n message: string,\n reason: 'missing-choice' | 'conflicting-choice' | 'legacy-dev-sentinel',\n ) {\n super(message)\n this.name = 'ExecutableTrustRequiredError'\n this.reason = reason\n }\n}\n\n// --- Access Pattern Failures ---\n\n/**\n * Thrown when spawning or running a subprocess fails.\n *\n * This covers a delegated `exec()` call failing due to an invalid request\n * (e.g. a `{{secret}}` placeholder in the `command` field) or a\n * process-level error (e.g. the command binary is not found or cannot be\n * spawned) — and also vaultkeeper's own internal tool invocations (doctor\n * version probes, the keychain/secret-tool/dpapi/yubikey credential helpers),\n * which run through the same subprocess utility. Callers that need to handle\n * only delegated-exec failures should scope their `catch` to the code paths\n * that call `exec()`, not discriminate on this type alone.\n *\n * @public\n */\nexport class ExecError extends VaultError {\n /**\n * The command that failed to execute.\n */\n readonly command: string\n\n constructor(message: string, command: string) {\n super(message)\n this.name = 'ExecError'\n this.command = command\n }\n}\n\n/**\n * Thrown when a JWE string is invalid or cannot be processed — for example,\n * it is structurally malformed (wrong number of segments, invalid\n * Base64URL), decryption fails (wrong key, tampered ciphertext), or the\n * decrypted payload does not match the expected claims schema.\n *\n * @public\n */\nexport class InvalidTokenError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'InvalidTokenError'\n }\n}\n\n/**\n * Thrown when `SecretAccessor.read()` is called after the accessor has\n * already been consumed.\n *\n * @public\n */\nexport class AccessorConsumedError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'AccessorConsumedError'\n }\n}\n\n// --- Infrastructure Failures ---\n\n/**\n * Thrown when a caller requests a signing key algorithm that is not a\n * supported JOSE algorithm identifier. The signing algorithm registry uses\n * strict JOSE identifiers (currently `'EdDSA'`); an unrecognized value is\n * rejected rather than defaulted.\n *\n * @public\n */\nexport class InvalidAlgorithmError extends VaultError {\n /**\n * The algorithm that was requested.\n */\n readonly algorithm: string\n\n /**\n * The set of algorithms that are allowed.\n */\n readonly allowed: string[]\n\n constructor(message: string, algorithm: string, allowed: string[]) {\n super(message)\n this.name = 'InvalidAlgorithmError'\n this.algorithm = algorithm\n this.allowed = allowed\n }\n}\n\n/**\n * Thrown when signing-key material cannot be parsed. Two paths raise it. During\n * verification, the supplied public key is not a structurally parseable SPKI PEM\n * public key (or a private key was passed where a public key is required); this\n * is an operational fault distinct from a signature that simply does not verify,\n * which returns `false`. During key use, a stored signing key decrypts cleanly\n * but is not valid private key material — corrupt or tampered on disk — when\n * exporting its public half or signing with it (`getPublicKey`/`signWithKey`).\n * The message never echoes any part of the key material.\n *\n * @public\n */\nexport class InvalidKeyMaterialError extends VaultError {\n constructor(message: string) {\n super(message)\n this.name = 'InvalidKeyMaterialError'\n }\n}\n\n/**\n * Thrown when a named signing key does not exist in the active backend — for\n * example `key export` or `sign` is asked for a name that was never enrolled\n * with `key create`. This is distinct from {@link SecretNotFoundError}: signing\n * keys occupy their own namespace and are never returned as ordinary secrets.\n *\n * @public\n */\nexport class SigningKeyNotFoundError extends VaultError {\n /**\n * The signing-key name that was requested (the caller-facing `--name`, not\n * the internal namespaced identifier).\n */\n readonly keyName: string\n\n constructor(message: string, keyName: string) {\n super(message)\n this.name = 'SigningKeyNotFoundError'\n this.keyName = keyName\n }\n}\n\n/**\n * Thrown when `key create` (or `createSigningKey`) is asked to enroll a signing\n * key under a name that already exists. Enrollment never silently overwrites an\n * existing key, because a regenerated keypair would invalidate every public key\n * that was previously exported and pinned by a verifier.\n *\n * @public\n */\nexport class SigningKeyAlreadyExistsError extends VaultError {\n /**\n * The signing-key name that already exists (the caller-facing `--name`, not\n * the internal namespaced identifier).\n */\n readonly keyName: string\n\n constructor(message: string, keyName: string) {\n super(message)\n this.name = 'SigningKeyAlreadyExistsError'\n this.keyName = keyName\n }\n}\n\n/**\n * Thrown when a signing operation (`key create`, `key export`, `sign`) is\n * requested against a backend that does not implement the signing contract.\n * Signing is never silently emulated on a backend that cannot perform it in a\n * key-stays-backend-side manner; inspect {@link SigningNotSupportedError.builtInSigningBackends}\n * for the built-in backend types that do.\n *\n * @public\n */\nexport class SigningNotSupportedError extends VaultError {\n /** The type identifier of the active backend that cannot sign. */\n readonly backendType: string\n\n /**\n * The **built-in** backend type identifiers known to implement the signing\n * contract (currently just the `file` backend). This is deliberately a\n * static list of built-ins, not a live capability survey: a consumer that\n * registers its own {@link SigningBackend} is not enumerated here, because\n * discovering that would require instantiating every registered backend —\n * a side effect that must not happen on an error path. A caller can still\n * point a user at a working built-in, and custom backends may implement the\n * contract independently.\n */\n readonly builtInSigningBackends: string[]\n\n constructor(message: string, backendType: string, builtInSigningBackends: string[]) {\n super(message)\n this.name = 'SigningNotSupportedError'\n this.backendType = backendType\n this.builtInSigningBackends = builtInSigningBackends\n }\n}\n\n/**\n * Thrown when an encrypted-at-rest secret entry cannot be decrypted — e.g.\n * the stored ciphertext is truncated/corrupted or the AES-GCM auth tag fails\n * to verify. The message never echoes any part of the secret or key\n * material.\n *\n * @public\n */\nexport class DecryptionError extends VaultError {\n /**\n * The path of the encrypted entry that failed to decrypt.\n */\n readonly path: string\n\n constructor(message: string, path: string) {\n super(message)\n this.name = 'DecryptionError'\n this.path = path\n }\n}\n\n/**\n * Thrown when a delegated `fetch()` call fails before a `Response` can be\n * produced — for example the URL is malformed or the underlying network\n * request fails (DNS failure, connection refused, TLS error).\n *\n * @public\n */\nexport class FetchError extends VaultError {\n /**\n * The unresolved URL template that fetch failed to request, with\n * `{{secret}}`/`{{secret:name}}` placeholders left intact. The\n * placeholder-resolved URL is deliberately never stored here, so an\n * injected secret is never exposed.\n */\n readonly url: string\n\n constructor(message: string, url: string) {\n super(message)\n this.name = 'FetchError'\n this.url = url\n }\n}\n\n/**\n * Thrown when a config value fails structural or semantic validation (e.g. a\n * whitespace-only `BackendConfig.path`).\n */\nexport class ConfigValidationError extends VaultError {\n /**\n * The dotted/bracketed path to the offending config field (e.g.\n * `'backends[0].path'`).\n */\n readonly field: string\n\n /**\n * The path of the config file that failed validation, when the error\n * originated from loading a file on disk (via `loadConfig`) rather than\n * from validating an in-memory value directly. This is `configDir` joined\n * with `config.json` exactly as provided to `loadConfig` — it is not\n * guaranteed to be absolute (`loadConfig` does not resolve a relative\n * `configDir`).\n */\n readonly configFilePath: string | undefined\n\n constructor(message: string, field: string, configFilePath?: string) {\n super(message)\n this.name = 'ConfigValidationError'\n this.field = field\n this.configFilePath = configFilePath\n }\n}\n\n/**\n * Thrown when a config's `backends[].type` names a backend that is not\n * registered with the {@link BackendRegistry}.\n *\n * A specialization of {@link ConfigValidationError}: an unknown backend type is\n * a semantic schema failure (the config parses and is structurally valid, but\n * names a backend that cannot be created), so it fails config validation the\n * same way `version !== 1` does. It carries the offending `backendType` and the\n * set of `knownTypes` so a consumer — notably `doctor` — can give the same\n * \"Available types: …\" guidance the runtime {@link BackendUnavailableError}\n * gives, without parsing the human-readable message. This closes the gap where\n * a config with an unknown backend type parsed as valid JSON and passed\n * `doctor` with a false \"System ready.\", only for the next real command to\n * throw {@link BackendUnavailableError} (issue #215).\n */\nexport class UnknownBackendTypeError extends ConfigValidationError {\n /** The unregistered backend type named in the config. */\n readonly backendType: string\n\n /**\n * The backend type identifiers that were registered when validation ran —\n * the valid options to offer in remediation.\n */\n readonly knownTypes: string[]\n\n constructor(\n message: string,\n field: string,\n backendType: string,\n knownTypes: string[],\n configFilePath?: string,\n ) {\n super(message, field, configFilePath)\n this.name = 'UnknownBackendTypeError'\n this.backendType = backendType\n this.knownTypes = knownTypes\n }\n}\n\n/**\n * Thrown when a config file's contents cannot be parsed as JSON.\n *\n * The `message` already embeds the file path, the parse location (when the\n * underlying `SyntaxError` exposes one), and a remediation hint that either\n * points at `vaultkeeper config init` (via the separate `@vaultkeeper/cli`\n * package — this library ships no CLI of its own) or at repairing/replacing\n * the config directly through this library's JS API — see issues #68, #100.\n * `path` and `location` are also exposed individually for callers (e.g.\n * `doctor`) that want to report them as structured fields rather than\n * re-parsing the message.\n */\nexport class ConfigParseError extends VaultError {\n /**\n * The path of the config file that failed to parse. This is `configDir`\n * joined with `config.json` exactly as provided to `loadConfig` — it is\n * not guaranteed to be absolute (`loadConfig` does not resolve a relative\n * `configDir`).\n */\n readonly path: string\n\n /**\n * A human-readable parse location (e.g. `'line 3, column 12'`), when one\n * could be derived from the underlying `SyntaxError`.\n */\n readonly location: string | undefined\n\n constructor(message: string, path: string, location: string | undefined) {\n super(message)\n this.name = 'ConfigParseError'\n this.path = path\n this.location = location\n }\n}\n\n/**\n * Thrown during initialization when a required system dependency (e.g. OpenSSL\n * or a native credential helper) is missing or incompatible.\n */\nexport class SetupError extends VaultError {\n /**\n * The name of the dependency that caused the setup failure.\n */\n readonly dependency: string\n\n constructor(message: string, dependency: string) {\n super(message)\n this.name = 'SetupError'\n this.dependency = dependency\n }\n}\n\n/**\n * Thrown when a filesystem operation fails. Common causes include a\n * permission or access problem, for example the config directory is not\n * writable, but the underlying failure may be any Node.js errno condition,\n * for example the disk is full or a file was expected but a directory was\n * found. Inspect the code property for the specific errno code when one is\n * available.\n */\nexport class FilesystemError extends VaultError {\n /**\n * The path of the file or directory that caused the error, as provided by\n * the caller. Not guaranteed to be absolute — e.g. `loadConfig` throws this\n * with `configDir` joined with `config.json` exactly as given, without\n * resolving a relative `configDir`.\n */\n readonly path: string\n\n /**\n * The file operation or access mode that was being attempted when the\n * failure occurred, for example 'read', 'write', 'delete', or 'rwx' for a\n * directory create/access check. Despite the field name, this does not\n * imply the failure was itself a permission problem — it names the\n * attempted operation regardless of the underlying errno, which may be a\n * non-permission code such as ENOSPC or EISDIR.\n */\n readonly permission: string\n\n /**\n * The Node.js errno code from the underlying filesystem failure, for\n * example EACCES, EPERM, ENOSPC, or EISDIR. Undefined when the error was\n * constructed without an underlying cause, or when that cause did not\n * expose a string errno code. Prefer this over parsing the message text,\n * which is not a contractual format.\n */\n readonly code: string | undefined\n\n /**\n * @param message - Human-readable description of the failure.\n * @param filePath - The path of the file or directory that caused the error.\n * @param permission - The file operation or access mode being attempted,\n * for example 'read', 'write', 'delete', or 'rwx'. See the `permission`\n * property for why this need not indicate an actual permission problem.\n * @param cause - The underlying error that was caught, if any. Recorded as\n * the standard `Error.cause` and used to populate `code` when it exposes a\n * string errno code.\n */\n constructor(message: string, filePath: string, permission: string, cause?: unknown) {\n super(message)\n this.name = 'FilesystemError'\n this.path = filePath\n this.permission = permission\n this.code = hasErrnoCode(cause) ? cause.code : undefined\n if (cause !== undefined) {\n // Matches the property descriptor the native `new Error(message, {\n // cause })` form installs (non-enumerable), rather than a plain\n // assignment, which would make `cause` enumerable and thus show up in\n // `Object.keys()`/`JSON.stringify()` output unlike a standard cause.\n Object.defineProperty(this, 'cause', {\n value: cause,\n writable: true,\n enumerable: false,\n configurable: true,\n })\n }\n }\n}\n\n/**\n * True when `err` is an `Error` carrying a Node.js-style string `code`\n * property — the `NodeJS.ErrnoException` convention used by filesystem and\n * other system-call failures.\n */\nfunction hasErrnoCode(err: unknown): err is Error & { code: string } {\n return err instanceof Error && 'code' in err && typeof err.code === 'string'\n}\n\n/**\n * Build a typed FilesystemError from a caught Node.js filesystem failure,\n * describing which resource and operation were involved. The original error\n * is preserved as `cause`, and its errno code, when present, is copied onto\n * the returned error for machine-readable discrimination.\n *\n * @param err - The value caught from the failed filesystem call.\n * @param resourceLabel - A short noun phrase describing what was being\n * accessed, for example 'secret file' or 'wrapping key file'.\n * @param filePath - The path that was being accessed.\n * @param permission - The operation being attempted, for example 'read',\n * 'write', or 'delete'.\n * @internal\n */\nexport function toFilesystemError(\n err: unknown,\n resourceLabel: string,\n filePath: string,\n permission: string,\n): FilesystemError {\n const detail = err instanceof Error ? err.message : String(err)\n return new FilesystemError(\n `Failed to ${permission} ${resourceLabel} at ${filePath}: ${detail}`,\n filePath,\n permission,\n err,\n )\n}\n\n/**\n * Thrown when a key rotation is requested while a previous rotation is still\n * within its grace period (i.e. the previous key has not yet been retired).\n */\nexport class RotationInProgressError extends VaultError {\n constructor(message: string) {\n const trimmed = message.trim()\n const nextSteps =\n 'Either wait for the current grace period to elapse before rotating again, ' +\n \"or run 'vaultkeeper revoke-key' (or call revokeKey()) to invalidate the previous key \" +\n 'immediately and clear the grace period.'\n const prefix = trimmed.length === 0 ? '' : `${trimmed}${/[.!?]$/.test(trimmed) ? '' : '.'} `\n super(`${prefix}${nextSteps}`)\n this.name = 'RotationInProgressError'\n }\n}\n","/**\n * Shared constants for 1Password SDK integration.\n *\n * @remarks\n * Centralised here so the backend, worker, and discovery modules stay in sync.\n *\n * @internal\n */\n\nimport { readFileSync, existsSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { SetupError } from '../errors.js'\n\n/** Name reported to the 1Password SDK for integration tracking. */\nexport const INTEGRATION_NAME = 'vaultkeeper'\n\n/** npm package name of the optional 1Password SDK peer dependency. */\nexport const SDK_PACKAGE = '@1password/sdk'\n\n/** Installation instructions surfaced when the SDK is missing. */\nexport const SDK_INSTALL_URL = 'https://developer.1password.com/docs/sdks/'\n\n/**\n * Single source of truth for the \"SDK not installed\" error message, so the\n * backend, worker, and discovery modules report it identically.\n */\nexport const SDK_NOT_INSTALLED_MESSAGE = `1Password SDK (${SDK_PACKAGE}) is not installed. Install it to use the 1Password backend.`\n\n/**\n * How long the per-access worker waits for `createClient()` to resolve during\n * a presence-covered `store`/`delete` write before treating the fresh action\n * as timed out (`PresenceTimeoutError`).\n *\n * @remarks\n * Shared between the worker (which starts the timer) and the backend (which\n * reports this value on the resulting {@link PresenceTimeoutError}), so the\n * two never drift. Reads are unaffected — issue #211 only extends presence\n * coverage to writes; see {@link https://github.com/mike-north/vaultkeeper/issues/211}.\n */\nexport const PRESENCE_WRITE_TIMEOUT_MS = 30_000\n\n/**\n * Whether an error thrown by `import('@1password/sdk')` means the module could\n * not be resolved (i.e. the optional peer is not installed), as opposed to a\n * present-but-broken SDK (native binding failure, init throw, incompatible\n * Node). Only the former should be reported as \"not installed\" — the latter\n * must surface its real error so users don't reinstall something already there.\n *\n * @remarks\n * Node's loader rejects a missing import with `code` set directly on the error;\n * some loaders/wrappers instead attach the original via `error.cause`, so one\n * level of cause is inspected too.\n */\nexport function isModuleNotFoundError(error: unknown): boolean {\n const hasNotFoundCode = (value: unknown): boolean => {\n if (value === null || typeof value !== 'object' || !('code' in value)) {\n return false\n }\n const { code } = value\n return code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND'\n }\n if (hasNotFoundCode(error)) {\n return true\n }\n if (error !== null && typeof error === 'object' && 'cause' in error) {\n return hasNotFoundCode(error.cause)\n }\n return false\n}\n\nlet cachedVersion: string | undefined\n\n/**\n * Version reported to the 1Password SDK.\n *\n * @remarks\n * Lazily derived from packages/vaultkeeper/package.json on first call so that\n * consumers who never use the 1Password backend pay no I/O cost at import time.\n * The result is memoized for subsequent calls.\n *\n * @internal\n */\nexport function getIntegrationVersion(): string {\n if (cachedVersion !== undefined) return cachedVersion\n\n const dir = dirname(fileURLToPath(import.meta.url))\n // Source: src/backend/ → ../../package.json\n // Bundled: dist/ → ../package.json\n const candidates = [resolve(dir, '..', '..', 'package.json'), resolve(dir, '..', 'package.json')]\n for (const candidate of candidates) {\n if (!existsSync(candidate)) continue\n const raw: unknown = JSON.parse(readFileSync(candidate, 'utf8'))\n if (\n raw !== null &&\n typeof raw === 'object' &&\n 'version' in raw &&\n typeof raw.version === 'string'\n ) {\n cachedVersion = raw.version\n return cachedVersion\n }\n }\n throw new SetupError(\n `Could not read version from vaultkeeper package.json. Tried paths: ${candidates.join(', ')}`,\n 'vaultkeeper package.json',\n )\n}\n","/**\n * Shared 1Password item read/write operations.\n *\n * @remarks\n * Both `OnePasswordBackend` (session client) and `one-password-worker.ts` (the\n * per-access worker process spawned for a fresh biometric approval) look items\n * up by title the same way, and must create-or-update / delete a\n * `Password`-category item identically — see issue #211. Centralising that\n * logic here keeps the two call sites from diverging.\n *\n * All SDK types are imported `import type` only, so this module carries no\n * runtime dependency on `@1password/sdk` and stays lazy-load-safe like the\n * rest of the 1Password integration: callers pass in an already-created\n * `Client` plus the SDK's own `ItemCategory`/`ItemFieldType` enum members\n * (obtained from their own dynamically-loaded SDK instance).\n *\n * @internal\n */\n\nimport type { Client, Item, ItemOverview, ItemCategory, ItemFieldType } from '@1password/sdk'\n\n/** Tag applied to every item vaultkeeper manages, used to scope list/find calls. */\nexport const TAG = 'vaultkeeper'\n\n/** Title of the field that carries the secret value. */\nexport const PASSWORD_FIELD_TITLE = 'password'\n\n/**\n * List all items in the vault tagged \"vaultkeeper\" and find one with the\n * matching title (= secret ID). Returns `undefined` if not found.\n */\nexport async function findItemOverviewByTitle(\n client: Client,\n vaultId: string,\n title: string,\n): Promise<ItemOverview | undefined> {\n const overviews = await client.items.list(vaultId)\n for (const overview of overviews) {\n if (overview.title === title && overview.tags.includes(TAG)) {\n return overview\n }\n }\n return undefined\n}\n\n/**\n * Fetch the full item for a given secret id. Returns `undefined` when no\n * tagged item with that title exists; rejects if the item fetch itself fails\n * (the overview was found but `items.get` errored).\n */\nexport async function findItemByTitle(\n client: Client,\n vaultId: string,\n title: string,\n): Promise<Item | undefined> {\n const overview = await findItemOverviewByTitle(client, vaultId, title)\n if (overview === undefined) return undefined\n return client.items.get(vaultId, overview.id)\n}\n\n/**\n * Extract the concealed password field value from an item, or `undefined` if\n * the item has no `password`-titled field.\n */\nexport function extractPasswordField(item: Item): string | undefined {\n for (const field of item.fields) {\n if (field.title === PASSWORD_FIELD_TITLE) {\n return field.value\n }\n }\n return undefined\n}\n\n/**\n * Create-or-update a `Password`-category item's concealed password field.\n *\n * @remarks\n * Updates the existing item's password field (appending one if missing) via\n * `items.put`, or creates a new tagged item via `items.create` when none\n * exists yet under `title`. `passwordCategory`/`concealedFieldType` are the\n * caller's own `ItemCategory.Password` / `ItemFieldType.Concealed` enum\n * members — passed in rather than imported here so this module never needs a\n * value import of the optional `@1password/sdk` peer.\n */\nexport async function storeSecretItem(\n client: Client,\n vaultId: string,\n title: string,\n secret: string,\n passwordCategory: ItemCategory,\n concealedFieldType: ItemFieldType,\n): Promise<void> {\n const existing = await findItemByTitle(client, vaultId, title)\n\n if (existing !== undefined) {\n const hasPasswordField = existing.fields.some((f) => f.title === PASSWORD_FIELD_TITLE)\n const updatedFields = hasPasswordField\n ? existing.fields.map((f) => (f.title === PASSWORD_FIELD_TITLE ? { ...f, value: secret } : f))\n : [\n ...existing.fields,\n {\n id: 'password',\n title: PASSWORD_FIELD_TITLE,\n fieldType: concealedFieldType,\n value: secret,\n },\n ]\n await client.items.put({ ...existing, fields: updatedFields })\n } else {\n await client.items.create({\n category: passwordCategory,\n vaultId,\n title,\n tags: [TAG],\n fields: [\n {\n id: 'password',\n title: PASSWORD_FIELD_TITLE,\n fieldType: concealedFieldType,\n value: secret,\n },\n ],\n })\n }\n}\n\n/**\n * Delete the tagged item matching `title`.\n * @returns `true` if a matching item was found and deleted, `false` if none matched.\n */\nexport async function deleteSecretItem(\n client: Client,\n vaultId: string,\n title: string,\n): Promise<boolean> {\n const overview = await findItemOverviewByTitle(client, vaultId, title)\n if (overview === undefined) return false\n await client.items.delete(vaultId, overview.id)\n return true\n}\n","/**\n * Per-access worker script for the 1Password SDK backend.\n *\n * @remarks\n * This script is spawned as a child process by `OnePasswordBackend` when\n * `accessMode` is set to `'per-access'`. It creates a fresh SDK client (which\n * triggers a biometric prompt via the desktop app), performs a single keyed\n * operation, writes the result to stdout as JSON, then exits immediately.\n *\n * argv layout:\n * node one-password-worker.js <accountName> <vaultId> <secretId> [op]\n *\n * `op` is one of `'retrieve'` (default, for backward compatibility with the\n * original 3-argument invocation), `'store'`, or `'delete'`.\n *\n * For `'store'`, the secret value is read from **stdin** (UTF-8, until EOF) —\n * never argv — so it never appears in `ps`/process listings, shell history,\n * or logs (repo security rule; mirrors the stdin plumbing already used by\n * `execCommandFull` in `util/exec.ts`).\n *\n * stdout on success:\n * `retrieve` — `{ \"value\": \"<secret>\" }`\n * `store`/`delete` — `{ \"ok\": true }`\n * stdout on failure: `{ \"error\": \"<message>\", \"code\": \"<code>\" }`\n *\n * `store`/`delete` are the presence-covered write paths added by issue #211:\n * each spawns a fresh SDK client exactly like `retrieve` does, forcing the\n * same fresh biometric approval, so `OnePasswordBackend.getCapabilities()`\n * can report `store`/`delete` in `presenceEnforcedOperations` instead of\n * refusing them with `NotCapableError`.\n *\n * @see https://github.com/mike-north/vaultkeeper/issues/211\n */\n\nimport {\n INTEGRATION_NAME,\n SDK_NOT_INSTALLED_MESSAGE,\n PRESENCE_WRITE_TIMEOUT_MS,\n getIntegrationVersion,\n isModuleNotFoundError,\n} from './one-password-constants.js'\nimport {\n storeSecretItem,\n deleteSecretItem,\n findItemOverviewByTitle,\n extractPasswordField,\n} from './one-password-item-ops.js'\n\ninterface RetrieveSuccessResponse {\n value: string\n}\n\ninterface WriteSuccessResponse {\n ok: true\n}\n\ninterface FailureResponse {\n error: string\n code: string\n}\n\nconst VALID_OPS = ['retrieve', 'store', 'delete'] as const\ntype WorkerOp = (typeof VALID_OPS)[number]\n\nfunction isValidOp(value: string | undefined): value is WorkerOp {\n if (value === undefined) return false\n for (const op of VALID_OPS) {\n if (op === value) return true\n }\n return false\n}\n\nfunction writeRetrieveSuccess(value: string): void {\n const response: RetrieveSuccessResponse = { value }\n process.stdout.write(JSON.stringify(response))\n}\n\nfunction writeWriteSuccess(): void {\n const response: WriteSuccessResponse = { ok: true }\n process.stdout.write(JSON.stringify(response))\n}\n\nfunction writeFailure(error: string, code: string): void {\n const response: FailureResponse = { error, code }\n process.stdout.write(JSON.stringify(response))\n}\n\n/** Read all of stdin (UTF-8) into a single string. Used only for `store`. */\nfunction readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: string[] = []\n process.stdin.setEncoding('utf8')\n process.stdin.on('data', (chunk: string) => {\n chunks.push(chunk)\n })\n process.stdin.on('end', () => {\n resolve(chunks.join(''))\n })\n process.stdin.on('error', (err: unknown) => {\n reject(err instanceof Error ? err : new Error(String(err)))\n })\n })\n}\n\n/** Sentinel rejection used to distinguish a presence-timeout from any other createClient failure. */\nclass PresenceTimeoutSentinel extends Error {}\n\nasync function main(): Promise<void> {\n const [, , accountName, vaultId, secretId, opArg] = process.argv\n\n if (accountName === undefined || vaultId === undefined || secretId === undefined) {\n writeFailure('Worker invoked with missing arguments', 'INTERNAL')\n process.exit(1)\n }\n\n // Omitted op defaults to 'retrieve' (compatibility with parents that\n // predate the write path). A PRESENT but unrecognized op is a\n // parent/worker protocol mismatch — fail closed rather than silently\n // running the wrong operation.\n if (opArg !== undefined && !isValidOp(opArg)) {\n writeFailure(`Worker invoked with unknown operation: ${opArg}`, 'INTERNAL')\n process.exit(1)\n }\n const op: WorkerOp = isValidOp(opArg) ? opArg : 'retrieve'\n\n // The SDK is an optional peer dependency, loaded lazily so the worker only\n // requires it when a per-access operation actually runs. A genuine \"module\n // not resolved\" is reported with PLUGIN_NOT_FOUND (the parent maps it to a\n // typed PluginNotFoundError); a present-but-broken SDK surfaces its real\n // error instead of a misleading \"not installed\" message.\n // Type-only annotation (typeof import) keeps the load itself dynamic while\n // giving `sdk` a precise type instead of an implicit any.\n let sdk: typeof import('@1password/sdk')\n try {\n sdk = await import('@1password/sdk')\n } catch (err) {\n if (isModuleNotFoundError(err)) {\n writeFailure(SDK_NOT_INSTALLED_MESSAGE, 'PLUGIN_NOT_FOUND')\n } else {\n writeFailure(`Failed to load 1Password SDK: ${String(err)}`, 'INTERNAL')\n }\n process.exit(1)\n }\n const { createClient, DesktopAuth, DesktopSessionExpiredError, RateLimitExceededError } = sdk\n\n // `store` reads the secret value before triggering the biometric prompt\n // (`createClient` below), so a stdin read failure never costs the user a\n // fresh presence action.\n let pendingSecret: string | undefined\n if (op === 'store') {\n try {\n pendingSecret = await readStdin()\n } catch (err) {\n writeFailure(`Failed to read secret value from stdin: ${String(err)}`, 'INTERNAL')\n process.exit(1)\n }\n }\n\n let client\n if (op === 'retrieve') {\n // Unchanged from before issue #211 — reads are a non-goal of that issue.\n try {\n client = await createClient({\n auth: new DesktopAuth(accountName),\n integrationName: INTEGRATION_NAME,\n integrationVersion: getIntegrationVersion(),\n })\n } catch (err) {\n if (err instanceof DesktopSessionExpiredError) {\n writeFailure('1Password session has expired', 'LOCKED')\n } else {\n writeFailure(`Authentication failed: ${String(err)}`, 'AUTH_DENIED')\n }\n process.exit(1)\n }\n } else {\n // `store`/`delete` are the new presence-covered write paths (#211): a\n // fresh client is required for every call, exactly like `retrieve`, but\n // the failure taxonomy differs because these calls only ever happen when\n // a fresh human action was demanded. The SDK exposes only\n // `DesktopSessionExpiredError` and `RateLimitExceededError` as typed\n // errors (see `@1password/sdk`'s `errors.d.ts`) — there is no distinct\n // \"user cancelled the biometric prompt\" error. A bounded race against\n // `PRESENCE_WRITE_TIMEOUT_MS` distinguishes \"no action within the\n // window\" (`PRESENCE_TIMEOUT`) from any other client-creation failure,\n // which is treated as the human declining the fresh action\n // (`PRESENCE_DECLINED`) rather than the generic `AUTH_DENIED` used for\n // reads, since a write only reaches this point because presence was\n // required.\n let timerId: ReturnType<typeof setTimeout> | undefined\n const timeoutPromise = new Promise<never>((_resolve, reject) => {\n timerId = setTimeout(() => {\n reject(new PresenceTimeoutSentinel('presence action timed out'))\n }, PRESENCE_WRITE_TIMEOUT_MS)\n })\n try {\n client = await Promise.race([\n createClient({\n auth: new DesktopAuth(accountName),\n integrationName: INTEGRATION_NAME,\n integrationVersion: getIntegrationVersion(),\n }),\n timeoutPromise,\n ])\n } catch (err) {\n if (err instanceof PresenceTimeoutSentinel) {\n writeFailure(\n `No fresh presence action within ${String(PRESENCE_WRITE_TIMEOUT_MS)}ms`,\n 'PRESENCE_TIMEOUT',\n )\n } else if (err instanceof DesktopSessionExpiredError) {\n writeFailure('1Password session has expired', 'LOCKED')\n } else if (\n typeof RateLimitExceededError === 'function' &&\n err instanceof RateLimitExceededError\n ) {\n // Not a human declining anything — a service-side throttle. Reporting\n // it as PRESENCE_DECLINED would misdirect the user toward the prompt.\n writeFailure(`1Password rate limit exceeded: ${String(err)}`, 'INTERNAL')\n } else {\n writeFailure(`1Password presence action declined: ${String(err)}`, 'PRESENCE_DECLINED')\n }\n process.exit(1)\n } finally {\n if (timerId !== undefined) {\n clearTimeout(timerId)\n }\n }\n }\n\n if (op === 'store') {\n // `pendingSecret` is always set here — `op === 'store'` only when the\n // stdin read above succeeded (a failed read already exited above).\n try {\n await storeSecretItem(\n client,\n vaultId,\n secretId,\n pendingSecret ?? '',\n sdk.ItemCategory.Password,\n sdk.ItemFieldType.Concealed,\n )\n } catch (err) {\n writeFailure(`Failed to store item: ${String(err)}`, 'INTERNAL')\n process.exit(1)\n }\n writeWriteSuccess()\n return\n }\n\n if (op === 'delete') {\n let deleted: boolean\n try {\n deleted = await deleteSecretItem(client, vaultId, secretId)\n } catch (err) {\n writeFailure(`Failed to delete item: ${String(err)}`, 'INTERNAL')\n process.exit(1)\n return\n }\n if (!deleted) {\n writeFailure(`Secret not found: ${secretId}`, 'NOT_FOUND')\n process.exit(1)\n }\n writeWriteSuccess()\n return\n }\n\n // op === 'retrieve' — the read path, sharing the same find/extract helpers\n // as the parent backend and the write path so the tag/field conventions\n // cannot drift. The failure-code taxonomy is preserved exactly: a list\n // failure is INTERNAL, a missing item or a get failure is NOT_FOUND.\n let overview\n try {\n overview = await findItemOverviewByTitle(client, vaultId, secretId)\n } catch (err) {\n writeFailure(`Failed to list items: ${String(err)}`, 'INTERNAL')\n process.exit(1)\n }\n\n if (overview === undefined) {\n writeFailure(`Secret not found: ${secretId}`, 'NOT_FOUND')\n process.exit(1)\n }\n\n let item\n try {\n item = await client.items.get(vaultId, overview.id)\n } catch (err) {\n writeFailure(`Failed to retrieve item: ${String(err)}`, 'NOT_FOUND')\n process.exit(1)\n }\n\n const secretValue = extractPasswordField(item)\n if (secretValue === undefined) {\n writeFailure(`Item found but missing password field: ${secretId}`, 'NOT_FOUND')\n process.exit(1)\n }\n\n writeRetrieveSuccess(secretValue)\n}\n\nmain().catch((err: unknown) => {\n writeFailure(`Unexpected worker error: ${String(err)}`, 'INTERNAL')\n process.exit(1)\n})\n"]}
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "vaultkeeper",
3
- "version": "0.5.3",
3
+ "version": "0.7.0",
4
4
  "description": "Unified, policy-enforced secret storage across OS backends",
5
+ "homepage": "https://github.com/mike-north/vaultkeeper#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/mike-north/vaultkeeper.git",
9
+ "directory": "packages/vaultkeeper"
10
+ },
11
+ "bugs": "https://github.com/mike-north/vaultkeeper/issues",
5
12
  "type": "module",
6
13
  "main": "./dist/index.cjs",
7
14
  "module": "./dist/index.js",
8
- "types": "./dist/vaultkeeper-public.d.ts",
9
15
  "exports": {
10
16
  ".": {
11
17
  "import": {
@@ -16,10 +22,13 @@
16
22
  "types": "./dist/index.d.cts",
17
23
  "default": "./dist/index.cjs"
18
24
  }
19
- }
25
+ },
26
+ "./package.json": "./package.json"
20
27
  },
21
28
  "files": [
22
- "dist"
29
+ "dist",
30
+ "LICENSE",
31
+ "README.md"
23
32
  ],
24
33
  "engines": {
25
34
  "node": ">=20.0.0"
@@ -31,10 +40,22 @@
31
40
  "access": "public"
32
41
  },
33
42
  "dependencies": {
34
- "@1password/sdk": "^0.4.0",
35
43
  "jose": "^6.0.0"
36
44
  },
45
+ "peerDependencies": {
46
+ "@1password/sdk": "^0.4.0",
47
+ "@types/node": ">=20"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "@1password/sdk": {
51
+ "optional": true
52
+ },
53
+ "@types/node": {
54
+ "optional": true
55
+ }
56
+ },
37
57
  "devDependencies": {
58
+ "@1password/sdk": "^0.4.0",
38
59
  "@microsoft/api-documenter": "^7.0.0",
39
60
  "@microsoft/api-extractor": "^7.57.2",
40
61
  "@types/node": "^22.0.0",
@@ -42,7 +63,7 @@
42
63
  "tsup": "^8.0.0"
43
64
  },
44
65
  "scripts": {
45
- "build": "tsup",
66
+ "build": "tsup && node scripts/inject-node-types-reference.mjs",
46
67
  "clean": "node -e \"import('node:fs').then(fs=>fs.rmSync('dist',{recursive:true,force:true}))\"",
47
68
  "test": "vitest run",
48
69
  "test:watch": "vitest",