@vouchagents/client 0.1.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.
- package/.turbo/turbo-build.log +4 -0
- package/dist/consent.d.ts +35 -0
- package/dist/consent.d.ts.map +1 -0
- package/dist/consent.js +43 -0
- package/dist/consent.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/password.d.ts +11 -0
- package/dist/password.d.ts.map +1 -0
- package/dist/password.js +61 -0
- package/dist/password.js.map +1 -0
- package/dist/vault/index.d.ts +4 -0
- package/dist/vault/index.d.ts.map +1 -0
- package/dist/vault/index.js +3 -0
- package/dist/vault/index.js.map +1 -0
- package/dist/vault/storage.d.ts +32 -0
- package/dist/vault/storage.d.ts.map +1 -0
- package/dist/vault/storage.js +69 -0
- package/dist/vault/storage.js.map +1 -0
- package/dist/vault/vault.d.ts +82 -0
- package/dist/vault/vault.d.ts.map +1 -0
- package/dist/vault/vault.js +231 -0
- package/dist/vault/vault.js.map +1 -0
- package/package.json +24 -0
- package/src/consent.ts +83 -0
- package/src/index.ts +24 -0
- package/src/password.ts +79 -0
- package/src/vault/index.ts +9 -0
- package/src/vault/storage.ts +80 -0
- package/src/vault/vault.ts +343 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type AgentSignupDiscovery } from "@vouchagents/protocol";
|
|
2
|
+
import type { Vault } from "./vault/vault.js";
|
|
3
|
+
export interface ConsentRequest {
|
|
4
|
+
site: AgentSignupDiscovery["branding"] & {
|
|
5
|
+
url: string;
|
|
6
|
+
};
|
|
7
|
+
requestedFields: {
|
|
8
|
+
required: Array<{
|
|
9
|
+
field: string;
|
|
10
|
+
}>;
|
|
11
|
+
optional?: Array<{
|
|
12
|
+
field: string;
|
|
13
|
+
}>;
|
|
14
|
+
};
|
|
15
|
+
nonce: string;
|
|
16
|
+
siteEndpoint: string;
|
|
17
|
+
agentId?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ConsentApproved {
|
|
20
|
+
approved: true;
|
|
21
|
+
token: string;
|
|
22
|
+
fields: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
export interface ConsentDenied {
|
|
25
|
+
approved: false;
|
|
26
|
+
reason: "user_denied" | "timeout" | "missing_fields" | "vault_locked";
|
|
27
|
+
}
|
|
28
|
+
export type ConsentResult = ConsentApproved | ConsentDenied;
|
|
29
|
+
export declare class ConsentManager {
|
|
30
|
+
private vault;
|
|
31
|
+
constructor(vault: Vault);
|
|
32
|
+
/** Request consent for a signup. Checks for matching auto-approve policies first. */
|
|
33
|
+
requestConsent(request: ConsentRequest): Promise<ConsentResult>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=consent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../src/consent.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,oBAAoB,CAAC,UAAU,CAAC,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,eAAe,EAAE;QACf,QAAQ,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACnC,QAAQ,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACrC,CAAC;IACF,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,IAAI,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,KAAK,CAAC;IAChB,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,gBAAgB,GAAG,cAAc,CAAC;CACvE;AAED,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,aAAa,CAAC;AAE5D,qBAAa,cAAc;IACb,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,KAAK;IAEhC,qFAAqF;IAC/E,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA8CtE"}
|
package/dist/consent.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createConsentToken, canonicalHash, SensitiveString, } from "@vouchagents/protocol";
|
|
2
|
+
export class ConsentManager {
|
|
3
|
+
vault;
|
|
4
|
+
constructor(vault) {
|
|
5
|
+
this.vault = vault;
|
|
6
|
+
}
|
|
7
|
+
/** Request consent for a signup. Checks for matching auto-approve policies first. */
|
|
8
|
+
async requestConsent(request) {
|
|
9
|
+
if (!this.vault.isUnlocked()) {
|
|
10
|
+
return { approved: false, reason: "vault_locked" };
|
|
11
|
+
}
|
|
12
|
+
const requiredFieldNames = request.requestedFields.required.map((f) => f.field);
|
|
13
|
+
// Check for matching auto-approve policy
|
|
14
|
+
const policy = await this.vault.findMatchingPolicy(request.site.url, requiredFieldNames);
|
|
15
|
+
// Get the field values
|
|
16
|
+
const fields = await this.vault.getFieldsRaw(requiredFieldNames);
|
|
17
|
+
// Check all required fields are available
|
|
18
|
+
const missing = requiredFieldNames.filter((f) => !fields[f]);
|
|
19
|
+
if (missing.length > 0) {
|
|
20
|
+
return { approved: false, reason: "missing_fields" };
|
|
21
|
+
}
|
|
22
|
+
// Create consent token
|
|
23
|
+
const keyPair = this.vault.getKeyPair();
|
|
24
|
+
const token = await createConsentToken({
|
|
25
|
+
userKeyPair: keyPair,
|
|
26
|
+
agentId: request.agentId ?? "unknown-agent",
|
|
27
|
+
audience: request.site.url,
|
|
28
|
+
nonce: request.nonce,
|
|
29
|
+
fields: requiredFieldNames,
|
|
30
|
+
data: fields,
|
|
31
|
+
purpose: "account_creation",
|
|
32
|
+
consentMode: policy ? "pre_authorized" : "explicit",
|
|
33
|
+
siteEndpoint: request.siteEndpoint,
|
|
34
|
+
policyId: policy?.id,
|
|
35
|
+
});
|
|
36
|
+
// Decrement policy uses if auto-approved
|
|
37
|
+
if (policy && policy.remainingUses !== undefined) {
|
|
38
|
+
policy.remainingUses--;
|
|
39
|
+
}
|
|
40
|
+
return { approved: true, token, fields };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../src/consent.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,eAAe,GAEhB,MAAM,uBAAuB,CAAC;AA2B/B,MAAM,OAAO,cAAc;IACL;IAApB,YAAoB,KAAY;QAAZ,UAAK,GAAL,KAAK,CAAO;IAAG,CAAC;IAEpC,qFAAqF;IACrF,KAAK,CAAC,cAAc,CAAC,OAAuB;QAC1C,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC;YAC7B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QACrD,CAAC;QAED,MAAM,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAC7D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CACf,CAAC;QAEF,yCAAyC;QACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAChD,OAAO,CAAC,IAAI,CAAC,GAAG,EAChB,kBAAkB,CACnB,CAAC;QAEF,uBAAuB;QACvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC;QAEjE,0CAA0C;QAC1C,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QACvD,CAAC;QAED,uBAAuB;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC;YACrC,WAAW,EAAE,OAAO;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,eAAe;YAC3C,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,kBAAkB;YAC1B,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,kBAAkB;YAC3B,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU;YACnD,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,QAAQ,EAAE,MAAM,EAAE,EAAE;SACrB,CAAC,CAAC;QAEH,yCAAyC;QACzC,IAAI,MAAM,IAAI,MAAM,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACjD,MAAM,CAAC,aAAa,EAAE,CAAC;QACzB,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3C,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Vault, FileStorage, LocalStorageBackend, MemoryStorage, defaultVaultPath, type StorageBackend, type VaultData, type SignupHistoryEntry, type PolicyEntry, } from "./vault/index.js";
|
|
2
|
+
export { ConsentManager, type ConsentRequest, type ConsentResult, type ConsentApproved, type ConsentDenied, } from "./consent.js";
|
|
3
|
+
export { generatePassword, type PasswordPolicy } from "./password.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Vault
|
|
2
|
+
export { Vault, FileStorage, LocalStorageBackend, MemoryStorage, defaultVaultPath, } from "./vault/index.js";
|
|
3
|
+
// Consent
|
|
4
|
+
export { ConsentManager, } from "./consent.js";
|
|
5
|
+
// Password
|
|
6
|
+
export { generatePassword } from "./password.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,OAAO,EACL,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,gBAAgB,GAKjB,MAAM,kBAAkB,CAAC;AAE1B,UAAU;AACV,OAAO,EACL,cAAc,GAKf,MAAM,cAAc,CAAC;AAEtB,WAAW;AACX,OAAO,EAAE,gBAAgB,EAAuB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SensitiveString } from "@vouchagents/protocol";
|
|
2
|
+
export interface PasswordPolicy {
|
|
3
|
+
minLength?: number;
|
|
4
|
+
requireUppercase?: boolean;
|
|
5
|
+
requireLowercase?: boolean;
|
|
6
|
+
requireNumbers?: boolean;
|
|
7
|
+
requireSymbols?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Generate a cryptographically random password meeting the given policy. */
|
|
10
|
+
export declare function generatePassword(policy?: PasswordPolicy): SensitiveString;
|
|
11
|
+
//# sourceMappingURL=password.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../src/password.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAeD,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,eAAe,CAuCzE"}
|
package/dist/password.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { SensitiveString } from "@vouchagents/protocol";
|
|
2
|
+
const DEFAULT_POLICY = {
|
|
3
|
+
minLength: 20,
|
|
4
|
+
requireUppercase: true,
|
|
5
|
+
requireLowercase: true,
|
|
6
|
+
requireNumbers: true,
|
|
7
|
+
requireSymbols: true,
|
|
8
|
+
};
|
|
9
|
+
const UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
10
|
+
const LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
|
|
11
|
+
const NUMBERS = "0123456789";
|
|
12
|
+
const SYMBOLS = "!@#$%^&*()-_=+[]{}|;:,.<>?";
|
|
13
|
+
/** Generate a cryptographically random password meeting the given policy. */
|
|
14
|
+
export function generatePassword(policy) {
|
|
15
|
+
const p = { ...DEFAULT_POLICY, ...policy };
|
|
16
|
+
const length = Math.max(p.minLength, 12);
|
|
17
|
+
let charset = "";
|
|
18
|
+
const required = [];
|
|
19
|
+
if (p.requireUppercase) {
|
|
20
|
+
charset += UPPERCASE;
|
|
21
|
+
required.push(randomChar(UPPERCASE));
|
|
22
|
+
}
|
|
23
|
+
if (p.requireLowercase) {
|
|
24
|
+
charset += LOWERCASE;
|
|
25
|
+
required.push(randomChar(LOWERCASE));
|
|
26
|
+
}
|
|
27
|
+
if (p.requireNumbers) {
|
|
28
|
+
charset += NUMBERS;
|
|
29
|
+
required.push(randomChar(NUMBERS));
|
|
30
|
+
}
|
|
31
|
+
if (p.requireSymbols) {
|
|
32
|
+
charset += SYMBOLS;
|
|
33
|
+
required.push(randomChar(SYMBOLS));
|
|
34
|
+
}
|
|
35
|
+
if (charset.length === 0) {
|
|
36
|
+
charset = UPPERCASE + LOWERCASE + NUMBERS;
|
|
37
|
+
}
|
|
38
|
+
// Fill remaining length with random chars from the full charset
|
|
39
|
+
const remaining = length - required.length;
|
|
40
|
+
const chars = [...required];
|
|
41
|
+
for (let i = 0; i < remaining; i++) {
|
|
42
|
+
chars.push(randomChar(charset));
|
|
43
|
+
}
|
|
44
|
+
// Shuffle to avoid predictable positions for required chars
|
|
45
|
+
shuffle(chars);
|
|
46
|
+
return new SensitiveString(chars.join(""));
|
|
47
|
+
}
|
|
48
|
+
function randomChar(charset) {
|
|
49
|
+
const array = new Uint32Array(1);
|
|
50
|
+
globalThis.crypto.getRandomValues(array);
|
|
51
|
+
return charset[array[0] % charset.length];
|
|
52
|
+
}
|
|
53
|
+
function shuffle(array) {
|
|
54
|
+
for (let i = array.length - 1; i > 0; i--) {
|
|
55
|
+
const randomValues = new Uint32Array(1);
|
|
56
|
+
globalThis.crypto.getRandomValues(randomValues);
|
|
57
|
+
const j = randomValues[0] % (i + 1);
|
|
58
|
+
[array[i], array[j]] = [array[j], array[i]];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=password.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password.js","sourceRoot":"","sources":["../src/password.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Vault } from "./vault.js";
|
|
2
|
+
export type { VaultData, SignupHistoryEntry, PolicyEntry } from "./vault.js";
|
|
3
|
+
export { FileStorage, LocalStorageBackend, MemoryStorage, defaultVaultPath, type StorageBackend, } from "./storage.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vault/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vault/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,gBAAgB,GAEjB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Storage backend interface. The vault doesn't care where the encrypted blob lives. */
|
|
2
|
+
export interface StorageBackend {
|
|
3
|
+
read(): Promise<string | null>;
|
|
4
|
+
write(data: string): Promise<void>;
|
|
5
|
+
exists(): Promise<boolean>;
|
|
6
|
+
}
|
|
7
|
+
/** File-based storage (~/.agent-signup/vault.json). Works in Node.js/Bun. */
|
|
8
|
+
export declare class FileStorage implements StorageBackend {
|
|
9
|
+
private path;
|
|
10
|
+
constructor(path: string);
|
|
11
|
+
read(): Promise<string | null>;
|
|
12
|
+
write(data: string): Promise<void>;
|
|
13
|
+
exists(): Promise<boolean>;
|
|
14
|
+
}
|
|
15
|
+
/** localStorage-based storage. Works in browsers. */
|
|
16
|
+
export declare class LocalStorageBackend implements StorageBackend {
|
|
17
|
+
private key;
|
|
18
|
+
constructor(key?: string);
|
|
19
|
+
read(): Promise<string | null>;
|
|
20
|
+
write(data: string): Promise<void>;
|
|
21
|
+
exists(): Promise<boolean>;
|
|
22
|
+
}
|
|
23
|
+
/** In-memory storage for testing. */
|
|
24
|
+
export declare class MemoryStorage implements StorageBackend {
|
|
25
|
+
private data;
|
|
26
|
+
read(): Promise<string | null>;
|
|
27
|
+
write(data: string): Promise<void>;
|
|
28
|
+
exists(): Promise<boolean>;
|
|
29
|
+
}
|
|
30
|
+
/** Get the default vault file path. */
|
|
31
|
+
export declare function defaultVaultPath(): string;
|
|
32
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/vault/storage.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED,6EAA6E;AAC7E,qBAAa,WAAY,YAAW,cAAc;IACpC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,MAAM;IAE1B,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAS9B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CASjC;AAED,qDAAqD;AACrD,qBAAa,mBAAoB,YAAW,cAAc;IAC5C,OAAO,CAAC,GAAG;gBAAH,GAAG,GAAE,MAA6B;IAEhD,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CAGjC;AAED,qCAAqC;AACrC,qBAAa,aAAc,YAAW,cAAc;IAClD,OAAO,CAAC,IAAI,CAAuB;IAE7B,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CAGjC;AAED,uCAAuC;AACvC,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** File-based storage (~/.agent-signup/vault.json). Works in Node.js/Bun. */
|
|
2
|
+
export class FileStorage {
|
|
3
|
+
path;
|
|
4
|
+
constructor(path) {
|
|
5
|
+
this.path = path;
|
|
6
|
+
}
|
|
7
|
+
async read() {
|
|
8
|
+
try {
|
|
9
|
+
const fs = await import("node:fs/promises");
|
|
10
|
+
return await fs.readFile(this.path, "utf-8");
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async write(data) {
|
|
17
|
+
const fs = await import("node:fs/promises");
|
|
18
|
+
const path = await import("node:path");
|
|
19
|
+
await fs.mkdir(path.dirname(this.path), { recursive: true });
|
|
20
|
+
await fs.writeFile(this.path, data, "utf-8");
|
|
21
|
+
}
|
|
22
|
+
async exists() {
|
|
23
|
+
try {
|
|
24
|
+
const fs = await import("node:fs/promises");
|
|
25
|
+
await fs.access(this.path);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** localStorage-based storage. Works in browsers. */
|
|
34
|
+
export class LocalStorageBackend {
|
|
35
|
+
key;
|
|
36
|
+
constructor(key = "agent-signup:vault") {
|
|
37
|
+
this.key = key;
|
|
38
|
+
}
|
|
39
|
+
async read() {
|
|
40
|
+
return globalThis.localStorage?.getItem(this.key) ?? null;
|
|
41
|
+
}
|
|
42
|
+
async write(data) {
|
|
43
|
+
globalThis.localStorage?.setItem(this.key, data);
|
|
44
|
+
}
|
|
45
|
+
async exists() {
|
|
46
|
+
return globalThis.localStorage?.getItem(this.key) !== null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** In-memory storage for testing. */
|
|
50
|
+
export class MemoryStorage {
|
|
51
|
+
data = null;
|
|
52
|
+
async read() {
|
|
53
|
+
return this.data;
|
|
54
|
+
}
|
|
55
|
+
async write(data) {
|
|
56
|
+
this.data = data;
|
|
57
|
+
}
|
|
58
|
+
async exists() {
|
|
59
|
+
return this.data !== null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Get the default vault file path. */
|
|
63
|
+
export function defaultVaultPath() {
|
|
64
|
+
const home = typeof process !== "undefined"
|
|
65
|
+
? process.env.HOME || process.env.USERPROFILE || "."
|
|
66
|
+
: ".";
|
|
67
|
+
return `${home}/.agent-signup/vault.json`;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/vault/storage.ts"],"names":[],"mappings":"AAOA,6EAA6E;AAC7E,MAAM,OAAO,WAAW;IACF;IAApB,YAAoB,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAEpC,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAC5C,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAC5C,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF;AAED,qDAAqD;AACrD,MAAM,OAAO,mBAAmB;IACV;IAApB,YAAoB,MAAc,oBAAoB;QAAlC,QAAG,GAAH,GAAG,CAA+B;IAAG,CAAC;IAE1D,KAAK,CAAC,IAAI;QACR,OAAO,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,MAAM;QACV,OAAO,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IAC7D,CAAC;CACF;AAED,qCAAqC;AACrC,MAAM,OAAO,aAAa;IAChB,IAAI,GAAkB,IAAI,CAAC;IAEnC,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,MAAM;QACV,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;IAC5B,CAAC;CACF;AAED,uCAAuC;AACvC,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GACR,OAAO,OAAO,KAAK,WAAW;QAC5B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG;QACpD,CAAC,CAAC,GAAG,CAAC;IACV,OAAO,GAAG,IAAI,2BAA2B,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type KeyPair, SensitiveString } from "@vouchagents/protocol";
|
|
2
|
+
import type { StorageBackend } from "./storage.js";
|
|
3
|
+
export interface VaultData {
|
|
4
|
+
fields: Record<string, string>;
|
|
5
|
+
passwords: Record<string, string>;
|
|
6
|
+
history: SignupHistoryEntry[];
|
|
7
|
+
policies: PolicyEntry[];
|
|
8
|
+
custom: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
export interface SignupHistoryEntry {
|
|
11
|
+
id: string;
|
|
12
|
+
site: string;
|
|
13
|
+
siteUrl: string;
|
|
14
|
+
fields: string[];
|
|
15
|
+
consentMode: "explicit" | "pre_authorized" | "manual";
|
|
16
|
+
status: "pending" | "verified" | "expired" | "failed" | "user_directed";
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PolicyEntry {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
scopes: string[];
|
|
23
|
+
allowedOrigins: string[];
|
|
24
|
+
allowedFields: string[];
|
|
25
|
+
maxUses?: number;
|
|
26
|
+
remainingUses?: number;
|
|
27
|
+
expiresAt: string;
|
|
28
|
+
createdAt: string;
|
|
29
|
+
}
|
|
30
|
+
export declare class Vault {
|
|
31
|
+
private storage;
|
|
32
|
+
private encryptionKey;
|
|
33
|
+
private keyPair;
|
|
34
|
+
private data;
|
|
35
|
+
private vaultFile;
|
|
36
|
+
private lockedAt;
|
|
37
|
+
private readonly autoLockMs;
|
|
38
|
+
private constructor();
|
|
39
|
+
/** Create a new vault with a passphrase. */
|
|
40
|
+
static create(options: {
|
|
41
|
+
passphrase: string;
|
|
42
|
+
storage: StorageBackend;
|
|
43
|
+
}): Promise<Vault>;
|
|
44
|
+
/** Open an existing vault with a passphrase. */
|
|
45
|
+
static open(options: {
|
|
46
|
+
passphrase: string;
|
|
47
|
+
storage: StorageBackend;
|
|
48
|
+
}): Promise<Vault>;
|
|
49
|
+
/** Check if a vault exists at the storage location. */
|
|
50
|
+
static exists(storage: StorageBackend): Promise<boolean>;
|
|
51
|
+
setField(name: string, value: string): Promise<void>;
|
|
52
|
+
getField(name: string): Promise<SensitiveString | null>;
|
|
53
|
+
listFields(): Promise<string[]>;
|
|
54
|
+
getFieldsRaw(fieldNames: string[]): Promise<Record<string, string>>;
|
|
55
|
+
setPassword(siteUrl: string, password: SensitiveString): Promise<void>;
|
|
56
|
+
getPassword(siteUrl: string): Promise<SensitiveString | null>;
|
|
57
|
+
setCustomField(name: string, value: string): Promise<void>;
|
|
58
|
+
addHistory(entry: SignupHistoryEntry): Promise<void>;
|
|
59
|
+
getHistory(options?: {
|
|
60
|
+
limit?: number;
|
|
61
|
+
siteUrl?: string;
|
|
62
|
+
}): Promise<SignupHistoryEntry[]>;
|
|
63
|
+
addPolicy(policy: PolicyEntry): Promise<void>;
|
|
64
|
+
getPolicies(): Promise<PolicyEntry[]>;
|
|
65
|
+
removePolicy(policyId: string): Promise<void>;
|
|
66
|
+
findMatchingPolicy(origin: string, fields: string[]): Promise<PolicyEntry | null>;
|
|
67
|
+
getIdentity(): {
|
|
68
|
+
publicKey: string;
|
|
69
|
+
keyId: string;
|
|
70
|
+
};
|
|
71
|
+
getKeyPair(): KeyPair;
|
|
72
|
+
isUnlocked(): boolean;
|
|
73
|
+
lock(): void;
|
|
74
|
+
export(): Promise<string>;
|
|
75
|
+
static import(exported: string, options: {
|
|
76
|
+
passphrase: string;
|
|
77
|
+
storage: StorageBackend;
|
|
78
|
+
}): Promise<Vault>;
|
|
79
|
+
private ensureUnlocked;
|
|
80
|
+
private save;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=vault.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../src/vault/vault.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,OAAO,EACZ,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,UAAU,GAAG,gBAAgB,GAAG,QAAQ,CAAC;IACtD,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,eAAe,CAAC;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAgBD,qBAAa,KAAK;IAQI,OAAO,CAAC,OAAO;IAPnC,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAE7C,OAAO;IAEP,4CAA4C;WAC/B,MAAM,CAAC,OAAO,EAAE;QAC3B,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,cAAc,CAAC;KACzB,GAAG,OAAO,CAAC,KAAK,CAAC;IAmClB,gDAAgD;WACnC,IAAI,CAAC,OAAO,EAAE;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,cAAc,CAAC;KACzB,GAAG,OAAO,CAAC,KAAK,CAAC;IAoClB,uDAAuD;WAC1C,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAMxD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAMvD,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAK/B,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAYnE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQ7D,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1D,UAAU,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD,UAAU,CAAC,OAAO,CAAC,EAAE;QACzB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAc3B,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAOrC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAkB9B,WAAW,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAQnD,UAAU,IAAI,OAAO;IAOrB,UAAU,IAAI,OAAO;IASrB,IAAI,IAAI,IAAI;IAQN,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;WAMlB,MAAM,CACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,cAAc,CAAA;KAAE,GACvD,OAAO,CAAC,KAAK,CAAC;IAOjB,OAAO,CAAC,cAAc;YAOR,IAAI;CAoBnB"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { generateKeyPair, deriveEncryptionKey, encrypt, decrypt, generateSalt, base64urlEncode, base64urlDecode, SensitiveString, } from "@vouchagents/protocol";
|
|
2
|
+
export class Vault {
|
|
3
|
+
storage;
|
|
4
|
+
encryptionKey = null;
|
|
5
|
+
keyPair = null;
|
|
6
|
+
data = null;
|
|
7
|
+
vaultFile = null;
|
|
8
|
+
lockedAt = 0;
|
|
9
|
+
autoLockMs = 15 * 60 * 1000; // 15 minutes
|
|
10
|
+
constructor(storage) {
|
|
11
|
+
this.storage = storage;
|
|
12
|
+
}
|
|
13
|
+
/** Create a new vault with a passphrase. */
|
|
14
|
+
static async create(options) {
|
|
15
|
+
const vault = new Vault(options.storage);
|
|
16
|
+
const salt = generateSalt();
|
|
17
|
+
const encryptionKey = await deriveEncryptionKey(options.passphrase, salt);
|
|
18
|
+
const keyPair = await generateKeyPair();
|
|
19
|
+
vault.encryptionKey = encryptionKey;
|
|
20
|
+
vault.keyPair = keyPair;
|
|
21
|
+
vault.data = {
|
|
22
|
+
fields: {},
|
|
23
|
+
passwords: {},
|
|
24
|
+
history: [],
|
|
25
|
+
policies: [],
|
|
26
|
+
custom: {},
|
|
27
|
+
};
|
|
28
|
+
vault.vaultFile = {
|
|
29
|
+
version: 1,
|
|
30
|
+
kdf: {
|
|
31
|
+
algorithm: "pbkdf2",
|
|
32
|
+
iterations: 600_000,
|
|
33
|
+
salt: base64urlEncode(salt),
|
|
34
|
+
},
|
|
35
|
+
vault: "", // filled on save
|
|
36
|
+
vault_iv: "",
|
|
37
|
+
signing_public_key: base64urlEncode(keyPair.publicKey),
|
|
38
|
+
created_at: new Date().toISOString(),
|
|
39
|
+
updated_at: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
vault.lockedAt = Date.now();
|
|
42
|
+
await vault.save();
|
|
43
|
+
return vault;
|
|
44
|
+
}
|
|
45
|
+
/** Open an existing vault with a passphrase. */
|
|
46
|
+
static async open(options) {
|
|
47
|
+
const vault = new Vault(options.storage);
|
|
48
|
+
const raw = await options.storage.read();
|
|
49
|
+
if (!raw) {
|
|
50
|
+
throw new Error("No vault found");
|
|
51
|
+
}
|
|
52
|
+
const vaultFile = JSON.parse(raw);
|
|
53
|
+
vault.vaultFile = vaultFile;
|
|
54
|
+
const salt = base64urlDecode(vaultFile.kdf.salt);
|
|
55
|
+
const encryptionKey = await deriveEncryptionKey(options.passphrase, salt);
|
|
56
|
+
vault.encryptionKey = encryptionKey;
|
|
57
|
+
// Decrypt the vault data
|
|
58
|
+
const ciphertext = base64urlDecode(vaultFile.vault);
|
|
59
|
+
const iv = base64urlDecode(vaultFile.vault_iv);
|
|
60
|
+
try {
|
|
61
|
+
const decrypted = await decrypt(ciphertext, encryptionKey, iv);
|
|
62
|
+
const json = new TextDecoder().decode(decrypted);
|
|
63
|
+
const parsed = JSON.parse(json);
|
|
64
|
+
vault.data = parsed.data;
|
|
65
|
+
vault.keyPair = {
|
|
66
|
+
publicKey: base64urlDecode(vaultFile.signing_public_key),
|
|
67
|
+
privateKey: base64urlDecode(parsed.privateKey),
|
|
68
|
+
keyId: parsed.keyId,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new Error("Wrong passphrase");
|
|
73
|
+
}
|
|
74
|
+
vault.lockedAt = Date.now();
|
|
75
|
+
return vault;
|
|
76
|
+
}
|
|
77
|
+
/** Check if a vault exists at the storage location. */
|
|
78
|
+
static async exists(storage) {
|
|
79
|
+
return storage.exists();
|
|
80
|
+
}
|
|
81
|
+
// --- Field operations ---
|
|
82
|
+
async setField(name, value) {
|
|
83
|
+
this.ensureUnlocked();
|
|
84
|
+
this.data.fields[name] = value;
|
|
85
|
+
await this.save();
|
|
86
|
+
}
|
|
87
|
+
async getField(name) {
|
|
88
|
+
this.ensureUnlocked();
|
|
89
|
+
const value = this.data.fields[name];
|
|
90
|
+
return value ? new SensitiveString(value) : null;
|
|
91
|
+
}
|
|
92
|
+
async listFields() {
|
|
93
|
+
this.ensureUnlocked();
|
|
94
|
+
return Object.keys(this.data.fields);
|
|
95
|
+
}
|
|
96
|
+
async getFieldsRaw(fieldNames) {
|
|
97
|
+
this.ensureUnlocked();
|
|
98
|
+
const result = {};
|
|
99
|
+
for (const name of fieldNames) {
|
|
100
|
+
const value = this.data.fields[name] ?? this.data.custom[name];
|
|
101
|
+
if (value)
|
|
102
|
+
result[name] = value;
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
// --- Password operations ---
|
|
107
|
+
async setPassword(siteUrl, password) {
|
|
108
|
+
this.ensureUnlocked();
|
|
109
|
+
this.data.passwords[siteUrl] = password.unsafeUnwrap();
|
|
110
|
+
await this.save();
|
|
111
|
+
}
|
|
112
|
+
async getPassword(siteUrl) {
|
|
113
|
+
this.ensureUnlocked();
|
|
114
|
+
const value = this.data.passwords[siteUrl];
|
|
115
|
+
return value ? new SensitiveString(value) : null;
|
|
116
|
+
}
|
|
117
|
+
// --- Custom fields ---
|
|
118
|
+
async setCustomField(name, value) {
|
|
119
|
+
this.ensureUnlocked();
|
|
120
|
+
this.data.custom[name] = value;
|
|
121
|
+
await this.save();
|
|
122
|
+
}
|
|
123
|
+
// --- History ---
|
|
124
|
+
async addHistory(entry) {
|
|
125
|
+
this.ensureUnlocked();
|
|
126
|
+
this.data.history.unshift(entry);
|
|
127
|
+
await this.save();
|
|
128
|
+
}
|
|
129
|
+
async getHistory(options) {
|
|
130
|
+
this.ensureUnlocked();
|
|
131
|
+
let entries = this.data.history;
|
|
132
|
+
if (options?.siteUrl) {
|
|
133
|
+
entries = entries.filter((e) => e.siteUrl === options.siteUrl);
|
|
134
|
+
}
|
|
135
|
+
if (options?.limit) {
|
|
136
|
+
entries = entries.slice(0, options.limit);
|
|
137
|
+
}
|
|
138
|
+
return entries;
|
|
139
|
+
}
|
|
140
|
+
// --- Policies ---
|
|
141
|
+
async addPolicy(policy) {
|
|
142
|
+
this.ensureUnlocked();
|
|
143
|
+
this.data.policies.push(policy);
|
|
144
|
+
await this.save();
|
|
145
|
+
}
|
|
146
|
+
async getPolicies() {
|
|
147
|
+
this.ensureUnlocked();
|
|
148
|
+
return this.data.policies.filter((p) => new Date(p.expiresAt) > new Date());
|
|
149
|
+
}
|
|
150
|
+
async removePolicy(policyId) {
|
|
151
|
+
this.ensureUnlocked();
|
|
152
|
+
this.data.policies = this.data.policies.filter((p) => p.id !== policyId);
|
|
153
|
+
await this.save();
|
|
154
|
+
}
|
|
155
|
+
async findMatchingPolicy(origin, fields) {
|
|
156
|
+
this.ensureUnlocked();
|
|
157
|
+
const now = new Date();
|
|
158
|
+
return (this.data.policies.find((p) => {
|
|
159
|
+
if (new Date(p.expiresAt) <= now)
|
|
160
|
+
return false;
|
|
161
|
+
if (p.remainingUses !== undefined && p.remainingUses <= 0)
|
|
162
|
+
return false;
|
|
163
|
+
const originMatch = p.allowedOrigins.some((o) => o === "*" || origin.includes(o));
|
|
164
|
+
if (!originMatch)
|
|
165
|
+
return false;
|
|
166
|
+
return fields.every((f) => p.allowedFields.includes(f));
|
|
167
|
+
}) ?? null);
|
|
168
|
+
}
|
|
169
|
+
// --- Identity ---
|
|
170
|
+
getIdentity() {
|
|
171
|
+
this.ensureUnlocked();
|
|
172
|
+
return {
|
|
173
|
+
publicKey: base64urlEncode(this.keyPair.publicKey),
|
|
174
|
+
keyId: this.keyPair.keyId,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
getKeyPair() {
|
|
178
|
+
this.ensureUnlocked();
|
|
179
|
+
return this.keyPair;
|
|
180
|
+
}
|
|
181
|
+
// --- Lock/unlock ---
|
|
182
|
+
isUnlocked() {
|
|
183
|
+
if (!this.encryptionKey || !this.data)
|
|
184
|
+
return false;
|
|
185
|
+
if (Date.now() - this.lockedAt > this.autoLockMs) {
|
|
186
|
+
this.lock();
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
lock() {
|
|
192
|
+
this.encryptionKey = null;
|
|
193
|
+
this.keyPair = null;
|
|
194
|
+
this.data = null;
|
|
195
|
+
}
|
|
196
|
+
// --- Export/import ---
|
|
197
|
+
async export() {
|
|
198
|
+
const raw = await this.storage.read();
|
|
199
|
+
if (!raw)
|
|
200
|
+
throw new Error("No vault to export");
|
|
201
|
+
return raw;
|
|
202
|
+
}
|
|
203
|
+
static async import(exported, options) {
|
|
204
|
+
await options.storage.write(exported);
|
|
205
|
+
return Vault.open(options);
|
|
206
|
+
}
|
|
207
|
+
// --- Internal ---
|
|
208
|
+
ensureUnlocked() {
|
|
209
|
+
if (!this.isUnlocked()) {
|
|
210
|
+
throw new Error("Vault is locked");
|
|
211
|
+
}
|
|
212
|
+
this.lockedAt = Date.now(); // reset idle timer
|
|
213
|
+
}
|
|
214
|
+
async save() {
|
|
215
|
+
if (!this.encryptionKey || !this.data || !this.keyPair || !this.vaultFile) {
|
|
216
|
+
throw new Error("Cannot save: vault not initialized");
|
|
217
|
+
}
|
|
218
|
+
const plaintext = JSON.stringify({
|
|
219
|
+
data: this.data,
|
|
220
|
+
privateKey: base64urlEncode(this.keyPair.privateKey),
|
|
221
|
+
keyId: this.keyPair.keyId,
|
|
222
|
+
});
|
|
223
|
+
const encoded = new TextEncoder().encode(plaintext);
|
|
224
|
+
const { ciphertext, iv } = await encrypt(encoded, this.encryptionKey);
|
|
225
|
+
this.vaultFile.vault = base64urlEncode(ciphertext);
|
|
226
|
+
this.vaultFile.vault_iv = base64urlEncode(iv);
|
|
227
|
+
this.vaultFile.updated_at = new Date().toISOString();
|
|
228
|
+
await this.storage.write(JSON.stringify(this.vaultFile, null, 2));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=vault.js.map
|