@sentropic/artifacts 0.2.4 → 0.2.6
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 +34 -0
- package/README.md +51 -3
- package/dist/adapters/postgres-management.d.ts +4 -0
- package/dist/adapters/postgres-management.js +36 -0
- package/dist/adapters/postgres.js +30 -8
- package/dist/http.d.ts +11 -1
- package/dist/http.js +272 -38
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/mcp-core.d.ts +3 -3
- package/dist/mcp-core.js +29 -10
- package/dist/profile/contracts.d.ts +15 -5
- package/dist/profile/contracts.js +9 -6
- package/dist/profile/index.js +39 -4
- package/dist/profile/radar.js +4 -1
- package/dist/profile/schemas.js +41 -8
- package/dist/profile/validation.d.ts +3 -1
- package/dist/profile/validation.js +36 -13
- package/dist/server/access.d.ts +14 -0
- package/dist/server/command-identity.d.ts +6 -3
- package/dist/server/command-identity.js +8 -8
- package/dist/server/commit/coordinator.js +13 -10
- package/dist/server/commit/types.d.ts +4 -0
- package/dist/server/edits.d.ts +17 -0
- package/dist/server/edits.js +43 -0
- package/dist/server/failures.d.ts +34 -2
- package/dist/server/failures.js +17 -3
- package/dist/server/lifecycle.d.ts +4 -0
- package/dist/server/lifecycle.js +15 -0
- package/dist/server/management.d.ts +46 -0
- package/dist/server/management.js +86 -0
- package/dist/server/outcome-projection.d.ts +2 -2
- package/dist/server/outcome-projection.js +25 -7
- package/dist/server/publication.d.ts +22 -11
- package/dist/server/publication.js +56 -7
- package/dist/server/result-schema.d.ts +14 -0
- package/dist/server/result-schema.js +35 -0
- package/dist/server/service.d.ts +2 -0
- package/dist/server/service.js +2 -0
- package/dist/server/source-read.d.ts +38 -0
- package/dist/server/source-read.js +68 -0
- package/dist/server/summary.d.ts +40 -0
- package/dist/server/summary.js +18 -0
- package/dist/server/templates.js +24 -3
- package/dist/server/versions.d.ts +29 -0
- package/dist/server/versions.js +39 -0
- package/docs/migration-l6.md +8 -1
- package/migrations/0004_terminal_controls.sql +47 -0
- package/migrations/0005_metadata.sql +14 -0
- package/package.json +15 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.6
|
|
4
|
+
|
|
5
|
+
- Integrate part 1 with the source reads, sharing, terminal deletion, masking and
|
|
6
|
+
summaries published in 0.2.5. Apply `0005_metadata.sql` after 0004; it adds
|
|
7
|
+
editorial metadata and nullable release authors/lineage without backfilling history.
|
|
8
|
+
- Add version history and revert as a new release on the same stable URL, preserving
|
|
9
|
+
current metadata and ACLs. The v2 profile uses `targetReleaseId` and paginated history.
|
|
10
|
+
- Add full-bundle or exactly-once text edits, metadata on ensure/publish, and
|
|
11
|
+
transactional release CAS, metadata, references, quota and receipts.
|
|
12
|
+
- Separate trusted `profileRevisionId`, `requireExpectedCurrentReleaseId` and
|
|
13
|
+
`resultEnvelopeVersion`. Compatible bindings keep root results; strict v2 retains
|
|
14
|
+
commit state, replay status and typed failures through mcp-platform 0.4.1.
|
|
15
|
+
- Replay historical receipts before enforcing new preconditions or preparing edits.
|
|
16
|
+
Missing strict preconditions use the R2 `precondition_required` message and details.
|
|
17
|
+
|
|
18
|
+
IMMO activates part 1 only after its qualified input and result switchovers.
|
|
19
|
+
Artifacts 0.2.6 stays inside the existing cluster-mesh `>=0.2.0 <0.3.0` range;
|
|
20
|
+
the API consumes the workspace package and retains its pinned APP-P1 profile.
|
|
21
|
+
|
|
22
|
+
## 0.2.5
|
|
23
|
+
|
|
24
|
+
- D1: Read verified author manifests and file bytes for the current or a selected
|
|
25
|
+
release, with path selection, source authorization, bounded snapshots and
|
|
26
|
+
separate provenance. Reject purged releases and mismatched record digests.
|
|
27
|
+
- D2: Grant or revoke named read access and delete artifacts terminally with
|
|
28
|
+
host-approved confirmation, transactional receipts and idempotent replay.
|
|
29
|
+
Mask artifacts outside the caller's visibility as `not_found`.
|
|
30
|
+
- D4: Enrich get/list results with binding names, dates, current release IDs,
|
|
31
|
+
digests, stable/pinned URLs, permissions and available optional metadata.
|
|
32
|
+
|
|
33
|
+
Apply `0004_terminal_controls.sql` after migrations 0001–0003 before enabling
|
|
34
|
+
the management capabilities.
|
package/README.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @sentropic/artifacts — generic artifact contracts (L-5a)
|
|
2
2
|
|
|
3
|
+
Publication profiles can set `requireExpectedCurrentReleaseId: true` to require an
|
|
4
|
+
explicit UUID or `null` on publish/revert. Strict profiles reject writers without
|
|
5
|
+
CAS. Omission remains supported in generic profiles. Replay the same caller key
|
|
6
|
+
after an unknown commit outcome; changing files, edits, metadata or the precondition
|
|
7
|
+
requires a new key. The `artifacts-outcome/v2` MCP envelope requires
|
|
8
|
+
`@sentropic/mcp-platform >=0.4.1` and preserves commit state and typed error details.
|
|
9
|
+
|
|
3
10
|
## Shared PostgreSQL activation (UP-T, 0.2.3)
|
|
4
11
|
|
|
5
12
|
W1.c/W3.a and Radar S1/A6 use the same imports:
|
|
@@ -314,7 +321,8 @@ The installed package includes SQL resources exported under
|
|
|
314
321
|
```js
|
|
315
322
|
import { readFile } from 'node:fs/promises';
|
|
316
323
|
|
|
317
|
-
const migrations = ['0001_artifacts.sql', '0002_lifecycle.sql'
|
|
324
|
+
const migrations = ['0001_artifacts.sql', '0002_lifecycle.sql', '0003_coordinator.sql',
|
|
325
|
+
'0004_terminal_controls.sql', '0005_metadata.sql'];
|
|
318
326
|
for (const name of migrations) {
|
|
319
327
|
const sql = await readFile(new URL(import.meta.resolve(
|
|
320
328
|
`@sentropic/artifacts/migrations/${name}`,
|
|
@@ -324,8 +332,10 @@ for (const name of migrations) {
|
|
|
324
332
|
```
|
|
325
333
|
|
|
326
334
|
Apply `0001_artifacts.sql` first to create the domain schema, then
|
|
327
|
-
`0002_lifecycle.sql` to add lifecycle, sharing, sources and GC support,
|
|
328
|
-
|
|
335
|
+
`0002_lifecycle.sql` to add lifecycle, sharing, sources and GC support, then
|
|
336
|
+
`0003_coordinator.sql`, `0004_terminal_controls.sql`, and `0005_metadata.sql`
|
|
337
|
+
(artifact display metadata and nullable release lineage),
|
|
338
|
+
before using the Postgres store. The host owns migration credentials, serialization,
|
|
329
339
|
transactions and the migration journal; execute each version through that
|
|
330
340
|
runner rather than running this read loop at application startup.
|
|
331
341
|
|
|
@@ -417,3 +427,41 @@ publication, use the current package version and this bootstrap sequence
|
|
|
417
427
|
3. After that, merges to `main` touching the artifacts package publish
|
|
418
428
|
through the `publish-artifacts` job (`make publish-artifacts`, OIDC +
|
|
419
429
|
provenance), gated by `validate-artifacts`.
|
|
430
|
+
|
|
431
|
+
## Source and management capabilities
|
|
432
|
+
|
|
433
|
+
Version 0.3.0 introduces D1, D2 and D4; see [CHANGELOG.md](./CHANGELOG.md).
|
|
434
|
+
|
|
435
|
+
Hosts may explicitly select `artifacts_get_source`, `artifacts_set_access`, and
|
|
436
|
+
`artifacts_delete` in their compiled profile. Configure `get_source` with empty
|
|
437
|
+
limits/defaults, and `set_access`/`delete` with `idempotencyKeyMaxUnits: 256` and
|
|
438
|
+
empty defaults. Existing profile selections remain explicit.
|
|
439
|
+
|
|
440
|
+
`POST /get_source` accepts `artifactId`, optional `releaseId`, and optional unique
|
|
441
|
+
`paths` (1–32 entries). Bind `publication.getRelease` and `sources.getSource` on
|
|
442
|
+
the HTTP app. It returns the canonical manifest, verified author file bytes,
|
|
443
|
+
and separate nullable `sourceRef`; readers need the distinct source permission.
|
|
444
|
+
The snapshot is limited to 32 files and 262144 decoded bytes. Hosts must qualify
|
|
445
|
+
a 2 MiB serialized MCP result budget before selecting this capability.
|
|
446
|
+
|
|
447
|
+
`POST /set_access` grants or revokes a named principal's read access, requires a
|
|
448
|
+
caller `idempotencyKey`, and keeps the artifact private. Bind
|
|
449
|
+
`isApprovedAccount` to the host's principal policy. `POST /delete` also requires
|
|
450
|
+
`expectedCurrentReleaseId` (nullable) and a trusted `management.confirmation`
|
|
451
|
+
port. Its `request` method issues a challenge for the host management UI;
|
|
452
|
+
`consume` verifies the human-approved proof, expiration and full command context
|
|
453
|
+
on the supplied transaction. A caller boolean is never confirmation. Repeated
|
|
454
|
+
successful deletes return the stored receipt without consuming proof again.
|
|
455
|
+
|
|
456
|
+
Apply `migrations/0004_terminal_controls.sql` after `0001_artifacts.sql`,
|
|
457
|
+
`0002_lifecycle.sql` and `0003_coordinator.sql` before activation.
|
|
458
|
+
The default management writer uses the coordinator's PostgreSQL
|
|
459
|
+
transaction; alternative stores implement `management.createUnit` with the same
|
|
460
|
+
parent/ACL/current lock order and transactional effects. Terminal markers and
|
|
461
|
+
SQL guards prevent restoration and later publication. Deletion reports
|
|
462
|
+
`purgeState: "pending"`; physical purge belongs to host retention policy.
|
|
463
|
+
|
|
464
|
+
Get/list include binding, creation/modification dates, current release, digest,
|
|
465
|
+
host URLs and available metadata. Historical modification dates stay nullable.
|
|
466
|
+
The PostgreSQL projection reads the optional metadata column added by the
|
|
467
|
+
publication parity lane without requiring that column for older stores.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FinalizeTxClient } from '../server/commit/types.js';
|
|
2
|
+
import type { ManagementUnit } from '../server/management.js';
|
|
3
|
+
/** Parent lock serializes ACL inserts, lifecycle changes and publication FKs. */
|
|
4
|
+
export declare function createPostgresManagementUnit(client: FinalizeTxClient): ManagementUnit;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Parent lock serializes ACL inserts, lifecycle changes and publication FKs. */
|
|
2
|
+
export function createPostgresManagementUnit(client) {
|
|
3
|
+
return {
|
|
4
|
+
async lock(id) {
|
|
5
|
+
await client.query('SELECT id FROM artifacts.artifacts WHERE id = $1 FOR UPDATE', [id]);
|
|
6
|
+
await client.query('SELECT principal_id FROM artifacts.artifact_access WHERE artifact_id = $1 ORDER BY principal_id FOR UPDATE', [id]);
|
|
7
|
+
await client.query('SELECT release_id FROM artifacts.artifact_current WHERE artifact_id = $1 FOR UPDATE', [id]);
|
|
8
|
+
},
|
|
9
|
+
async read(id) {
|
|
10
|
+
const { rows } = await client.query(`SELECT a.owner_id, a.storage_scope_id, a.lifecycle_state, a.is_public, a.terminal_deleted_at, c.release_id
|
|
11
|
+
FROM artifacts.artifacts a LEFT JOIN artifacts.artifact_current c ON c.artifact_id = a.id WHERE a.id = $1`, [id]);
|
|
12
|
+
const row = rows[0];
|
|
13
|
+
if (!row)
|
|
14
|
+
return undefined;
|
|
15
|
+
const acl = { ownerId: row.owner_id, storageScopeId: row.storage_scope_id,
|
|
16
|
+
isPublic: row.is_public, withdrawn: row.lifecycle_state === 'withdrawn', grants: [] };
|
|
17
|
+
return { acl, state: row.lifecycle_state, terminal: row.terminal_deleted_at !== null,
|
|
18
|
+
currentReleaseId: row.release_id };
|
|
19
|
+
},
|
|
20
|
+
async setAccess(id, principalId, action, updatedAt) {
|
|
21
|
+
const changed = action === 'grant' ? await client.query(`INSERT INTO artifacts.artifact_access (artifact_id, principal_id, permission) VALUES ($1, $2, 'read')
|
|
22
|
+
ON CONFLICT (artifact_id, principal_id) DO NOTHING`, [id, principalId])
|
|
23
|
+
: await client.query('DELETE FROM artifacts.artifact_access WHERE artifact_id = $1 AND principal_id = $2', [id, principalId]);
|
|
24
|
+
await client.query(`UPDATE artifacts.artifacts SET is_public = false, updated_at = $2
|
|
25
|
+
WHERE id = $1 AND ($3::boolean OR is_public)`, [id, updatedAt, changed.rowCount > 0]);
|
|
26
|
+
const result = await client.query('SELECT principal_id, permission FROM artifacts.artifact_access WHERE artifact_id = $1 ORDER BY principal_id', [id]);
|
|
27
|
+
const stamp = await client.query('SELECT updated_at FROM artifacts.artifacts WHERE id = $1', [id]);
|
|
28
|
+
return { grants: result.rows.map((row) => ({ principalId: row.principal_id, grant: row.permission })),
|
|
29
|
+
updatedAt: stamp.rows[0]?.updated_at?.toISOString() ?? null };
|
|
30
|
+
},
|
|
31
|
+
async delete(id, deletedAt) {
|
|
32
|
+
await client.query(`UPDATE artifacts.artifacts SET lifecycle_state = 'deleted', terminal_deleted_at = $2,
|
|
33
|
+
updated_at = $2, is_public = false WHERE id = $1`, [id, deletedAt]);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ArtifactOperationError } from '../server/failures.js';
|
|
1
2
|
/** Read-only migration inventory. Never promotes a legacy success into an authorized receipt. */
|
|
2
3
|
export function createPostgresLegacyArtifactRecords(pool) {
|
|
3
4
|
return {
|
|
@@ -20,6 +21,9 @@ function toIntent(row) {
|
|
|
20
21
|
}
|
|
21
22
|
function toRelease(row) {
|
|
22
23
|
return {
|
|
24
|
+
authorPrincipalId: row.author_principal_id ?? null,
|
|
25
|
+
parentReleaseId: row.parent_release_id ?? null,
|
|
26
|
+
revertedFromReleaseId: row.reverted_from_release_id ?? null,
|
|
23
27
|
releaseId: row.id,
|
|
24
28
|
artifactId: row.artifact_id,
|
|
25
29
|
manifestDigest: row.manifest_digest,
|
|
@@ -34,8 +38,10 @@ export function createPostgresArtifactStore(pool) {
|
|
|
34
38
|
async ensureArtifact(input) {
|
|
35
39
|
const where = `WHERE storage_scope_id = $1 AND owner_id = $2 AND binding_name = $3`;
|
|
36
40
|
const args = [input.storageScopeId, input.ownerId, input.bindingName];
|
|
37
|
-
const found = await pool.query(`SELECT id FROM artifacts.artifacts ${where}`, args);
|
|
41
|
+
const found = await pool.query(`SELECT id, (to_jsonb(a)->>'terminal_deleted_at') IS NOT NULL AS terminal FROM artifacts.artifacts a ${where}`, args);
|
|
38
42
|
if (found.rowCount !== 0 && found.rows[0] !== undefined) {
|
|
43
|
+
if (found.rows[0].terminal)
|
|
44
|
+
throw new ArtifactOperationError('gone');
|
|
39
45
|
return { artifactId: found.rows[0].id, created: false };
|
|
40
46
|
}
|
|
41
47
|
const inserted = await pool.query(`INSERT INTO artifacts.artifacts (id, storage_scope_id, owner_id, binding_name, profile)
|
|
@@ -44,10 +50,12 @@ export function createPostgresArtifactStore(pool) {
|
|
|
44
50
|
if (inserted.rowCount !== 0 && inserted.rows[0] !== undefined) {
|
|
45
51
|
return { artifactId: inserted.rows[0].id, created: true };
|
|
46
52
|
}
|
|
47
|
-
const raced = await pool.query(`SELECT id FROM artifacts.artifacts ${where}`, args);
|
|
53
|
+
const raced = await pool.query(`SELECT id, (to_jsonb(a)->>'terminal_deleted_at') IS NOT NULL AS terminal FROM artifacts.artifacts a ${where}`, args);
|
|
48
54
|
const row = raced.rows[0];
|
|
49
55
|
if (row === undefined)
|
|
50
56
|
throw new Error(`artifact binding lost for ${input.bindingName}`);
|
|
57
|
+
if (row.terminal)
|
|
58
|
+
throw new ArtifactOperationError('gone');
|
|
51
59
|
return { artifactId: row.id, created: false };
|
|
52
60
|
},
|
|
53
61
|
async record(kind, payload) {
|
|
@@ -138,11 +146,14 @@ export function createPostgresArtifactStore(pool) {
|
|
|
138
146
|
}
|
|
139
147
|
},
|
|
140
148
|
async getRelease(releaseId) {
|
|
141
|
-
const { rows } = await pool.query(`SELECT
|
|
142
|
-
FROM artifacts.releases WHERE id = $1`, [releaseId]);
|
|
149
|
+
const { rows } = await pool.query(`SELECT * FROM artifacts.releases WHERE id = $1`, [releaseId]);
|
|
143
150
|
const row = rows[0];
|
|
144
151
|
return row === undefined ? undefined : toRelease(row);
|
|
145
152
|
},
|
|
153
|
+
async listReleases(artifactId) {
|
|
154
|
+
const { rows } = await pool.query(`SELECT * FROM artifacts.releases WHERE artifact_id = $1 ORDER BY created_at, id`, [artifactId]);
|
|
155
|
+
return rows.map(toRelease);
|
|
156
|
+
},
|
|
146
157
|
async getCurrentReleaseId(artifactId) {
|
|
147
158
|
const { rows } = await pool.query(`SELECT release_id FROM artifacts.artifact_current WHERE artifact_id = $1`, [artifactId]);
|
|
148
159
|
return rows[0]?.release_id;
|
|
@@ -185,14 +196,17 @@ export function createPostgresArtifactStore(pool) {
|
|
|
185
196
|
return rows.map((row) => row.digest);
|
|
186
197
|
},
|
|
187
198
|
async getArtifactAcl(artifactId) {
|
|
188
|
-
const { rows } = await pool.query(`SELECT owner_id, storage_scope_id, is_public, lifecycle_state
|
|
189
|
-
|
|
199
|
+
const { rows } = await pool.query(`SELECT owner_id, storage_scope_id, is_public, lifecycle_state, binding_name, created_at,
|
|
200
|
+
to_jsonb(a)->>'updated_at' AS updated_at, to_jsonb(a)->'metadata' AS metadata
|
|
201
|
+
FROM artifacts.artifacts a WHERE id = $1`, [artifactId]);
|
|
190
202
|
const row = rows[0];
|
|
191
203
|
if (row === undefined)
|
|
192
204
|
return undefined;
|
|
193
205
|
const grants = await pool.query(`SELECT principal_id, permission FROM artifacts.artifact_access
|
|
194
206
|
WHERE artifact_id = $1 ORDER BY principal_id`, [artifactId]);
|
|
195
207
|
return {
|
|
208
|
+
bindingName: row.binding_name, createdAt: row.created_at.toISOString(), updatedAt: row.updated_at,
|
|
209
|
+
...(row.metadata ?? {}),
|
|
196
210
|
ownerId: row.owner_id,
|
|
197
211
|
storageScopeId: row.storage_scope_id,
|
|
198
212
|
isPublic: row.is_public,
|
|
@@ -201,14 +215,15 @@ export function createPostgresArtifactStore(pool) {
|
|
|
201
215
|
principalId: grant.principal_id,
|
|
202
216
|
grant: grant.permission,
|
|
203
217
|
})),
|
|
218
|
+
...(row.metadata === null || Object.keys(row.metadata).length === 0 ? {} : { metadata: row.metadata }),
|
|
204
219
|
};
|
|
205
220
|
},
|
|
206
221
|
async putArtifactAcl(artifactId, acl) {
|
|
207
|
-
await pool.query(`UPDATE artifacts.artifacts SET is_public = $2,
|
|
222
|
+
await pool.query(`UPDATE artifacts.artifacts SET is_public = $2, metadata = $4,
|
|
208
223
|
lifecycle_state = CASE WHEN $3 THEN 'withdrawn'
|
|
209
224
|
WHEN lifecycle_state = 'withdrawn' THEN 'active'
|
|
210
225
|
ELSE lifecycle_state END
|
|
211
|
-
WHERE id = $1`, [artifactId, acl.isPublic, acl.withdrawn]);
|
|
226
|
+
WHERE id = $1`, [artifactId, acl.isPublic, acl.withdrawn, JSON.stringify(acl.metadata ?? {})]);
|
|
212
227
|
await pool.query(`DELETE FROM artifacts.artifact_access WHERE artifact_id = $1`, [
|
|
213
228
|
artifactId,
|
|
214
229
|
]);
|
|
@@ -263,6 +278,13 @@ export function createPostgresArtifactStore(pool) {
|
|
|
263
278
|
const { rows } = await pool.query(`SELECT lifecycle_state FROM artifacts.artifacts WHERE id = $1`, [artifactId]);
|
|
264
279
|
return rows[0]?.lifecycle_state ?? 'active';
|
|
265
280
|
},
|
|
281
|
+
async isTerminal(artifactId) {
|
|
282
|
+
const { rows } = await pool.query("SELECT (to_jsonb(a)->>'terminal_deleted_at') IS NOT NULL AS terminal FROM artifacts.artifacts a WHERE id = $1", [artifactId]);
|
|
283
|
+
return rows[0]?.terminal === true;
|
|
284
|
+
},
|
|
285
|
+
async markTerminal(artifactId) {
|
|
286
|
+
await pool.query("UPDATE artifacts.artifacts SET terminal_deleted_at = coalesce(terminal_deleted_at, now()), lifecycle_state = 'deleted' WHERE id = $1", [artifactId]);
|
|
287
|
+
},
|
|
266
288
|
async setArtifactState(artifactId, state) {
|
|
267
289
|
const updated = await pool.query(`UPDATE artifacts.artifacts SET lifecycle_state = $2 WHERE id = $1`, [artifactId, state]);
|
|
268
290
|
if ((updated.rowCount ?? 0) === 0)
|
package/dist/http.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ import type { AclStore } from './server/access.js';
|
|
|
7
7
|
import type { LifecycleService } from './server/lifecycle.js';
|
|
8
8
|
import { type ArtifactInvocationContext, type ArtifactCommitCoordinator } from './server/commit/types.js';
|
|
9
9
|
import { type ArtifactOutcome, type ArtifactHostFailureMapper } from './server/failures.js';
|
|
10
|
+
import { type ManagementUnit, type ArtifactConfirmationPort } from './server/management.js';
|
|
11
|
+
import type { FinalizeTxClient } from './server/commit/types.js';
|
|
12
|
+
import type { SourceStore } from './server/sources.js';
|
|
10
13
|
export { projectArtifactHttpOutcome } from './server/outcome-projection.js';
|
|
11
14
|
export interface HttpRequest {
|
|
12
15
|
method: string;
|
|
@@ -37,9 +40,16 @@ export interface HttpAppDeps {
|
|
|
37
40
|
ensureHost: GuardedEnsureHost;
|
|
38
41
|
publication: Omit<GuardedPublicationDeps, 'coordinator' | 'limits'> & {
|
|
39
42
|
getCurrent: PublicationService['getCurrent'];
|
|
43
|
+
getRelease: PublicationService['getRelease'];
|
|
44
|
+
listReleases: PublicationService['listReleases'];
|
|
45
|
+
};
|
|
46
|
+
sources?: Pick<SourceStore, 'getSource'>;
|
|
47
|
+
management?: {
|
|
48
|
+
createUnit?: (client: FinalizeTxClient) => ManagementUnit;
|
|
49
|
+
confirmation?: ArtifactConfirmationPort;
|
|
40
50
|
};
|
|
41
51
|
acls: AclStore;
|
|
42
|
-
lifecycle: Pick<LifecycleService, 'stateOf' | 'canRead'>;
|
|
52
|
+
lifecycle: Pick<LifecycleService, 'stateOf' | 'canRead' | 'getReleaseState'>;
|
|
43
53
|
isApprovedAccount?: (principalId: string) => boolean;
|
|
44
54
|
/** One trusted URL policy shared by HTTP and MCP receipts. */
|
|
45
55
|
publicationUrls?: (artifactId: string, releaseId: string) => {
|