@jini-ai/platform 0.6.12 → 0.6.15-rc.1
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/CHANGELOG.md +33 -0
- package/README.md +52 -0
- package/dist/asset-cache.js +27 -13
- package/dist/command.d.ts +1 -1
- package/dist/command.js +11 -8
- package/dist/download.js +26 -32
- package/dist/fetch-with-timeout.d.ts +54 -0
- package/dist/fetch-with-timeout.js +129 -2
- package/dist/fs/directory-user-config.js +2 -1
- package/dist/fs/directory-workspace.js +3 -1
- package/dist/fs/guarded-reader.d.ts +19 -47
- package/dist/fs/guarded-reader.js +82 -13
- package/dist/mail/email-sender-registry.d.ts +15 -0
- package/dist/mail/email-sender-registry.js +22 -0
- package/dist/mail/email-sender.d.ts +109 -0
- package/dist/mail/email-sender.js +10 -0
- package/dist/mail/index.d.ts +15 -2
- package/dist/mail/index.js +8 -1
- package/dist/mail/mail-status-cache.d.ts +33 -0
- package/dist/mail/mail-status-cache.js +56 -0
- package/dist/mail/prepare-sender-email.d.ts +35 -0
- package/dist/mail/prepare-sender-email.js +68 -0
- package/dist/mail/resolve-mailer.d.ts +95 -0
- package/dist/mail/resolve-mailer.js +348 -0
- package/dist/mail/senders.d.ts +5 -0
- package/dist/mail/senders.js +5 -0
- package/dist/secrets/credential-sets/vault.repo.memory.d.ts +1 -4
- package/dist/secrets/credential-sets/vault.repo.memory.js +3 -1
- package/dist/secrets/credential-sets/vault.service.d.ts +17 -9
- package/dist/secrets/credential-sets/vault.service.js +18 -9
- package/dist/secrets/credential-sets/vault.types.d.ts +10 -4
- package/package.json +8 -2
|
@@ -81,13 +81,15 @@ export class SecretsVaultService {
|
|
|
81
81
|
assertContext(record);
|
|
82
82
|
return { ...record, meta: this.deps.metaParsers[record.kind]({ meta: record.meta, provider: record.provider }) };
|
|
83
83
|
}
|
|
84
|
-
|
|
84
|
+
/** Resolve a matching identity; deletion can retain legacy metadata without interpreting it. */
|
|
85
|
+
async find(input, { parseMeta = true } = {}) {
|
|
85
86
|
const record = await this.deps.repo.find({ workspaceId: input.workspaceId, id: input.id });
|
|
86
87
|
if (!record)
|
|
87
88
|
return null;
|
|
88
89
|
if (buildVaultAad(input) !== buildVaultAad(record))
|
|
89
90
|
throw new VaultError({ code: "context" });
|
|
90
|
-
|
|
91
|
+
assertContext(record);
|
|
92
|
+
return parseMeta ? this.parse(record) : record;
|
|
91
93
|
}
|
|
92
94
|
async seal(input) {
|
|
93
95
|
if (input.payload === null)
|
|
@@ -137,7 +139,7 @@ export class SecretsVaultService {
|
|
|
137
139
|
const { sealed, last4, tokenLength } = await this.putEnvelope({ input, identity, existing });
|
|
138
140
|
const now = this.deps.now({});
|
|
139
141
|
return { ...identity, provider: input.provider, label: input.label, meta, sealed,
|
|
140
|
-
isDefault: input.isDefault ?? (existing !== null && existing.provider === input.provider
|
|
142
|
+
isDefault: input.isDefault ?? (existing !== null && existing.provider === input.provider ? existing.isDefault : undefined),
|
|
141
143
|
revision: (input.expectedRevision ?? 0) + 1, last4, tokenLength,
|
|
142
144
|
createdAt: existing === null ? now : existing.createdAt, updatedAt: now };
|
|
143
145
|
}
|
|
@@ -174,11 +176,15 @@ export class SecretsVaultService {
|
|
|
174
176
|
? credentialTokenHint({ token: input.hintToken }) : null;
|
|
175
177
|
return { sealed, last4: hint?.last4 ?? null, tokenLength: hint?.length ?? null };
|
|
176
178
|
}
|
|
177
|
-
/** Authorized runtime read; returns plaintext only to the caller that supplied exact ownership.
|
|
178
|
-
|
|
179
|
+
/** Authorized runtime read; returns plaintext only to the caller that supplied exact ownership.
|
|
180
|
+
* `provider`, when given, must match the row's group before its envelope is opened (a stale
|
|
181
|
+
* selection never decrypts another group's secret). */
|
|
182
|
+
async get(input, { provider } = {}) {
|
|
179
183
|
assertContext(input);
|
|
180
184
|
await this.authorize({ action: "get", context: input });
|
|
181
185
|
const record = await this.find(input);
|
|
186
|
+
if (record && provider !== undefined && record.provider !== provider)
|
|
187
|
+
throw new VaultError({ code: "context" });
|
|
182
188
|
return record ? { summary: summary(record), payload: await this.open(record) } : null;
|
|
183
189
|
}
|
|
184
190
|
/** Singleton runtime read using one provider-independent lookup. Full expected ownership and
|
|
@@ -273,14 +279,15 @@ export class SecretsVaultService {
|
|
|
273
279
|
const last4 = summary(await this.writeCopy({ source, record, targetIdentity, existing })).last4;
|
|
274
280
|
return { status: "copied", ...(last4 ? { last4 } : {}) };
|
|
275
281
|
}
|
|
276
|
-
/** Overwrite keeps the target's default, revision chain and creation time.
|
|
282
|
+
/** Overwrite keeps the target's default, revision chain and creation time. A new copy leaves
|
|
283
|
+
* placement unspecified, so the repo's first-row rule applies (never the source's default flag). */
|
|
277
284
|
async writeCopy({ source, record, targetIdentity, existing }) {
|
|
278
285
|
if (existing && !isCompatibleOverwrite({ existing, record }))
|
|
279
286
|
throw new VaultError({ code: "context" });
|
|
280
287
|
const now = this.deps.now({});
|
|
281
288
|
const placement = existing
|
|
282
289
|
? { isDefault: existing.isDefault, revision: existing.revision + 1, createdAt: existing.createdAt }
|
|
283
|
-
: { isDefault:
|
|
290
|
+
: { isDefault: undefined, revision: 1, createdAt: now };
|
|
284
291
|
const candidate = this.parse({ ...record, ...targetIdentity, ...placement, updatedAt: now });
|
|
285
292
|
candidate.sealed = await this.seal({ context: targetIdentity, payload: await source.open(record) });
|
|
286
293
|
// Reuse the source's hint policy without guessing a token field from a structured payload.
|
|
@@ -328,11 +335,13 @@ export class SecretsVaultService {
|
|
|
328
335
|
return summary(this.parse(await this.deps.repo.put({ expectedRevision: input.expectedRevision,
|
|
329
336
|
record: { ...existing, sealed, revision: existing.revision + 1, updatedAt: this.deps.now({}) } })));
|
|
330
337
|
}
|
|
331
|
-
/** Idempotent for missing rows; existing rows require matching context and revision.
|
|
338
|
+
/** Idempotent for missing rows; existing rows require matching context and revision.
|
|
339
|
+
* Deletion needs no metadata interpretation, so unupgraded legacy rows remain removable.
|
|
340
|
+
*/
|
|
332
341
|
async delete(input, _options = {}) {
|
|
333
342
|
assertContext(input);
|
|
334
343
|
await this.authorize({ action: "delete", context: input });
|
|
335
|
-
const existing = await this.find(input);
|
|
344
|
+
const existing = await this.find(input, { parseMeta: false });
|
|
336
345
|
if (!existing)
|
|
337
346
|
return false;
|
|
338
347
|
if (existing.revision !== input.expectedRevision)
|
|
@@ -42,6 +42,12 @@ export interface VaultRecord extends VaultIdentity {
|
|
|
42
42
|
export type VaultSummary = Omit<VaultRecord, "sealed"> & {
|
|
43
43
|
configured: boolean;
|
|
44
44
|
};
|
|
45
|
+
/** Unspecified placement lets the repo auto-default a group's first row; false opts out.
|
|
46
|
+
* Persisted records and read models always contain a concrete boolean.
|
|
47
|
+
*/
|
|
48
|
+
export type VaultPutRecord = Omit<VaultRecord, "isDefault"> & {
|
|
49
|
+
isDefault?: boolean | undefined;
|
|
50
|
+
};
|
|
45
51
|
export interface VaultQuery {
|
|
46
52
|
workspaceId: string;
|
|
47
53
|
kind: VaultKind;
|
|
@@ -65,7 +71,7 @@ export interface VaultSingletonQuery {
|
|
|
65
71
|
/** Ordered CAS operations; default side effects affect later operations' revisions. */
|
|
66
72
|
export type VaultBatchOp = {
|
|
67
73
|
put: {
|
|
68
|
-
record:
|
|
74
|
+
record: VaultPutRecord;
|
|
69
75
|
expectedRevision: number | null;
|
|
70
76
|
};
|
|
71
77
|
} | {
|
|
@@ -129,7 +135,7 @@ export interface VaultRepoPort {
|
|
|
129
135
|
}, options?: VaultOptions): Promise<VaultBatchResult[]>;
|
|
130
136
|
list(input: VaultQuery, options?: VaultOptions): Promise<VaultRecord[]>;
|
|
131
137
|
put(input: {
|
|
132
|
-
record:
|
|
138
|
+
record: VaultPutRecord;
|
|
133
139
|
expectedRevision: number | null;
|
|
134
140
|
}, options?: VaultOptions): Promise<VaultRecord>;
|
|
135
141
|
/** Service-validated patch: top-level keys are plain JSON-path identifiers. */
|
|
@@ -148,11 +154,11 @@ export interface VaultRepoPort {
|
|
|
148
154
|
}
|
|
149
155
|
/** Unambiguous scope encoding keeps adapter default/identity checks on the same contract. */
|
|
150
156
|
export declare function vaultGroupKey({ record }: {
|
|
151
|
-
record:
|
|
157
|
+
record: VaultDefaultQuery;
|
|
152
158
|
}, _options?: VaultOptions): string;
|
|
153
159
|
/** Adapters guard CAS and immutable identity even for callers below the service. */
|
|
154
160
|
export declare function assertVaultReplacement({ record, existing, expectedRevision }: {
|
|
155
|
-
record:
|
|
161
|
+
record: VaultPutRecord;
|
|
156
162
|
existing: VaultRecord | null;
|
|
157
163
|
expectedRevision: number | null;
|
|
158
164
|
}, _options?: VaultOptions): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/platform",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.15-rc.1",
|
|
4
4
|
"description": "Generic OS/platform primitives: process exec and lifecycle, filesystem containment, HTTP readiness polling, toolchain discovery, blob storage, AWS signing, and an SSRF-safe asset cache.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"./secrets/testing": "node",
|
|
23
23
|
"./http/guarded": "node",
|
|
24
24
|
"./mail": "node",
|
|
25
|
+
"./mail/senders": "universal",
|
|
25
26
|
"./mail/smtp": "node",
|
|
26
27
|
"./fs/guarded-reader": "node",
|
|
27
28
|
"./fs/durable-json": "node",
|
|
@@ -106,6 +107,11 @@
|
|
|
106
107
|
"import": "./dist/mail/index.js",
|
|
107
108
|
"default": "./dist/mail/index.js"
|
|
108
109
|
},
|
|
110
|
+
"./mail/senders": {
|
|
111
|
+
"types": "./dist/mail/senders.d.ts",
|
|
112
|
+
"import": "./dist/mail/senders.js",
|
|
113
|
+
"default": "./dist/mail/senders.js"
|
|
114
|
+
},
|
|
109
115
|
"./mail/smtp": {
|
|
110
116
|
"types": "./dist/mail/smtp.d.ts",
|
|
111
117
|
"import": "./dist/mail/smtp.js",
|
|
@@ -200,7 +206,7 @@
|
|
|
200
206
|
"!dist/**/*.map"
|
|
201
207
|
],
|
|
202
208
|
"dependencies": {
|
|
203
|
-
"@jini-ai/core": "^0.6.
|
|
209
|
+
"@jini-ai/core": "^0.6.15-rc.1"
|
|
204
210
|
},
|
|
205
211
|
"publishConfig": {
|
|
206
212
|
"access": "public",
|