@jini-ai/cms 0.3.10 → 0.4.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/CHANGELOG.md +59 -0
- package/README.md +47 -19
- package/dist/content-types/agent-tools.d.ts +3 -56
- package/dist/content-types/agent-tools.js +22 -6
- package/dist/content-types/cleanup.d.ts +18 -13
- package/dist/content-types/cleanup.js +16 -11
- package/dist/content-types/errors.d.ts +57 -24
- package/dist/content-types/errors.js +56 -39
- package/dist/content-types/field-defs.d.ts +8 -4
- package/dist/content-types/field-defs.js +19 -14
- package/dist/content-types/index-provisioning.d.ts +61 -47
- package/dist/content-types/index-provisioning.js +90 -66
- package/dist/content-types/index.d.ts +4 -3
- package/dist/content-types/lifecycle-dispatch.d.ts +11 -5
- package/dist/content-types/lifecycle-dispatch.js +5 -4
- package/dist/content-types/lifecycle.d.ts +23 -18
- package/dist/content-types/lifecycle.js +71 -64
- package/dist/content-types/repo.memory.d.ts +5 -4
- package/dist/content-types/repo.memory.js +3 -2
- package/dist/content-types/tool-registrations.d.ts +6 -8
- package/dist/content-types/tool-registrations.js +24 -28
- package/dist/content-types/types.d.ts +17 -13
- package/dist/content-types/types.js +11 -5
- package/dist/content-types/write-service.d.ts +20 -22
- package/dist/content-types/write-service.js +78 -75
- package/dist/core/authorization.d.ts +15 -0
- package/dist/core/authorization.js +2 -0
- package/dist/core/commands/change-set.d.ts +15 -7
- package/dist/core/commands/command.d.ts +45 -26
- package/dist/core/commands/command.js +73 -63
- package/dist/core/entity-liveness.d.ts +11 -7
- package/dist/core/entity-liveness.js +5 -4
- package/dist/core/index.d.ts +12 -5
- package/dist/core/index.js +10 -4
- package/dist/core/ports.d.ts +25 -24
- package/dist/core/ports.js +1 -0
- package/dist/core/tools/args.d.ts +49 -0
- package/dist/core/tools/args.js +46 -0
- package/dist/core/tools/index.d.ts +3 -0
- package/dist/core/tools/index.js +3 -0
- package/dist/entries/agent-tools.d.ts +1 -73
- package/dist/entries/agent-tools.js +19 -13
- package/dist/entries/errors.d.ts +28 -14
- package/dist/entries/errors.js +17 -11
- package/dist/entries/field-validation.d.ts +5 -3
- package/dist/entries/field-validation.js +7 -5
- package/dist/entries/index.d.ts +2 -2
- package/dist/entries/lifecycle-dispatch.d.ts +5 -2
- package/dist/entries/lifecycle-dispatch.js +2 -1
- package/dist/entries/list.d.ts +4 -2
- package/dist/entries/list.js +4 -3
- package/dist/entries/repo.memory.d.ts +12 -10
- package/dist/entries/repo.memory.js +11 -10
- package/dist/entries/tool-registrations.d.ts +6 -8
- package/dist/entries/tool-registrations.js +42 -41
- package/dist/entries/types.d.ts +0 -7
- package/dist/entries/write-service.d.ts +28 -29
- package/dist/entries/write-service.js +114 -105
- package/dist/http/settings/cms-adapter.d.ts +20 -0
- package/dist/http/settings/cms-adapter.js +124 -0
- package/dist/http/settings/contracts.d.ts +135 -0
- package/dist/http/settings/contracts.js +2 -0
- package/dist/http/settings/http.d.ts +10 -0
- package/dist/http/settings/http.js +211 -0
- package/dist/http/settings/index.d.ts +5 -0
- package/dist/http/settings/index.js +4 -0
- package/dist/http/settings/shared.d.ts +68 -0
- package/dist/http/settings/shared.js +54 -0
- package/dist/http/settings/stream.d.ts +24 -0
- package/dist/http/settings/stream.js +147 -0
- package/dist/media/agent-tools.d.ts +1 -66
- package/dist/media/agent-tools.js +5 -1
- package/dist/media/blob-gc-lock.d.ts +6 -2
- package/dist/media/blob-gc-lock.js +4 -2
- package/dist/media/blob-gc.d.ts +64 -51
- package/dist/media/blob-gc.js +62 -52
- package/dist/media/blob-key.d.ts +1 -1
- package/dist/media/content-type-sniffer.d.ts +3 -1
- package/dist/media/content-type-sniffer.js +1 -1
- package/dist/media/html-attributes.d.ts +9 -5
- package/dist/media/html-attributes.js +8 -6
- package/dist/media/image-transformer.js +1 -1
- package/dist/media/image-transformer.sharp.d.ts +6 -0
- package/dist/media/image-transformer.sharp.js +12 -6
- package/dist/media/import/fetch-image.d.ts +24 -0
- package/dist/media/import/fetch-image.js +137 -0
- package/dist/media/import/index.d.ts +3 -0
- package/dist/media/import/index.js +3 -0
- package/dist/media/import/ports.d.ts +38 -0
- package/dist/media/index.d.ts +7 -1
- package/dist/media/index.js +1 -0
- package/dist/media/media-service.d.ts +14 -10
- package/dist/media/media-service.js +57 -52
- package/dist/media/ports.d.ts +3 -2
- package/dist/media/rendition-service.d.ts +3 -3
- package/dist/media/rendition-service.js +38 -37
- package/dist/media/repo.memory.d.ts +18 -7
- package/dist/media/repo.memory.js +12 -6
- package/dist/media/tool-registrations.d.ts +16 -15
- package/dist/media/tool-registrations.js +84 -72
- package/dist/media/transform-lock.d.ts +22 -12
- package/dist/media/transform-lock.js +14 -13
- package/dist/media/transform-registry.d.ts +3 -3
- package/dist/media/transform-registry.js +19 -18
- package/dist/media/transform-types.d.ts +8 -3
- package/dist/media/transform-types.js +10 -5
- package/dist/media/types.d.ts +21 -4
- package/dist/media/types.js +15 -2
- package/dist/navigation/agent-tools.d.ts +1 -80
- package/dist/navigation/agent-tools.js +15 -3
- package/dist/navigation/contracts.d.ts +1 -1
- package/dist/navigation/index.d.ts +2 -2
- package/dist/navigation/menu-service.d.ts +57 -40
- package/dist/navigation/menu-service.js +80 -69
- package/dist/navigation/ports.d.ts +1 -1
- package/dist/navigation/read-model.d.ts +3 -1
- package/dist/navigation/read-model.js +2 -2
- package/dist/navigation/reconcile.d.ts +2 -2
- package/dist/navigation/reconcile.js +2 -1
- package/dist/navigation/repo.memory.d.ts +7 -3
- package/dist/navigation/repo.memory.js +4 -2
- package/dist/navigation/resolver.d.ts +4 -1
- package/dist/navigation/resolver.js +1 -1
- package/dist/navigation/tool-registrations.d.ts +6 -8
- package/dist/navigation/tool-registrations.js +44 -42
- package/dist/navigation/types.d.ts +1 -1
- package/dist/presentation/presentation.d.ts +11 -3
- package/dist/presentation/presentation.js +13 -6
- package/dist/presentation/repo.memory.d.ts +6 -2
- package/dist/presentation/repo.memory.js +3 -2
- package/dist/settings/agent-tools.d.ts +2 -75
- package/dist/settings/agent-tools.js +49 -45
- package/dist/settings/agent-value-write-tools.d.ts +22 -0
- package/dist/settings/agent-value-write-tools.js +154 -0
- package/dist/settings/agent-writable-preferences.js +8 -20
- package/dist/settings/agent-write-denylist.d.ts +19 -0
- package/dist/settings/agent-write-denylist.js +15 -0
- package/dist/settings/change-feed.d.ts +11 -2
- package/dist/settings/change-feed.js +4 -4
- package/dist/settings/definitions-dispatch.d.ts +8 -3
- package/dist/settings/definitions-dispatch.js +5 -5
- package/dist/settings/dictionaries/index.d.ts +5 -2
- package/dist/settings/dictionaries/index.js +2 -2
- package/dist/settings/dictionaries/settings-dialog.ar.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.bn.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.de.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.es.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.fa.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.fr.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.hi.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.hu.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.id.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.it.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.ja.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.ko.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.pl.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.pt-BR.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.ru.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.th.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.tr.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.uk.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.ur.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.zh-CN.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.zh-TW.js +0 -1
- package/dist/settings/ensure-definitions.d.ts +5 -5
- package/dist/settings/errors.d.ts +7 -1
- package/dist/settings/errors.js +7 -1
- package/dist/settings/index.d.ts +4 -1
- package/dist/settings/index.js +1 -0
- package/dist/settings/ports.d.ts +6 -5
- package/dist/settings/principal-lookup.d.ts +9 -0
- package/dist/settings/principal-lookup.js +2 -0
- package/dist/settings/purge-service.d.ts +5 -4
- package/dist/settings/purge-service.js +3 -2
- package/dist/settings/settings.d.ts +22 -7
- package/dist/settings/settings.js +47 -26
- package/dist/settings/tool-registrations.d.ts +25 -32
- package/dist/settings/tool-registrations.js +66 -56
- package/dist/settings/types.d.ts +1 -1
- package/dist/settings/ui-tab-definitions.d.ts +1 -1
- package/dist/settings/write-service.d.ts +32 -26
- package/dist/settings/write-service.js +26 -20
- package/dist/taxonomy/agent-tools.d.ts +1 -66
- package/dist/taxonomy/agent-tools.js +25 -15
- package/dist/taxonomy/content-lookup.js +1 -1
- package/dist/taxonomy/index.d.ts +2 -2
- package/dist/taxonomy/merge-term.d.ts +7 -4
- package/dist/taxonomy/merge-term.js +7 -5
- package/dist/taxonomy/repo.memory.d.ts +42 -19
- package/dist/taxonomy/repo.memory.js +14 -12
- package/dist/taxonomy/validation-chain.d.ts +39 -18
- package/dist/taxonomy/validation-chain.js +35 -25
- package/dist/taxonomy/write-service.d.ts +106 -45
- package/dist/taxonomy/write-service.js +221 -179
- package/dist/trash/adapters/record-store.d.ts +52 -0
- package/dist/trash/adapters/record-store.js +36 -0
- package/dist/trash/cursor.d.ts +20 -0
- package/dist/trash/cursor.js +36 -0
- package/dist/trash/follow-ups.d.ts +76 -0
- package/dist/trash/follow-ups.js +46 -0
- package/dist/trash/index.d.ts +8 -0
- package/dist/trash/index.js +8 -0
- package/dist/trash/ports.d.ts +308 -0
- package/dist/trash/ports.js +2 -0
- package/dist/trash/repo.memory.d.ts +50 -0
- package/dist/trash/repo.memory.js +114 -0
- package/dist/trash/sweeper.d.ts +77 -0
- package/dist/trash/sweeper.js +127 -0
- package/dist/trash/write-service.d.ts +100 -0
- package/dist/trash/write-service.js +242 -0
- package/dist/workspace/agent-tools.d.ts +1 -59
- package/dist/workspace/agent-tools.js +27 -12
- package/dist/workspace/create.d.ts +31 -6
- package/dist/workspace/create.js +18 -5
- package/dist/workspace/delete.d.ts +18 -13
- package/dist/workspace/delete.js +20 -12
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/repo.memory.d.ts +19 -4
- package/dist/workspace/repo.memory.js +26 -4
- package/dist/workspace/tool-registrations.d.ts +2 -2
- package/dist/workspace/tool-registrations.js +13 -13
- package/dist/workspace/update.d.ts +6 -5
- package/dist/workspace/update.js +9 -8
- package/package.json +44 -38
- package/dist/identity/admin-crud-service.d.ts +0 -280
- package/dist/identity/admin-crud-service.js +0 -468
- package/dist/identity/agent-tool-input.d.ts +0 -58
- package/dist/identity/agent-tool-input.js +0 -126
- package/dist/identity/agent-tools.d.ts +0 -101
- package/dist/identity/agent-tools.js +0 -290
- package/dist/identity/auth-service.d.ts +0 -118
- package/dist/identity/auth-service.js +0 -170
- package/dist/identity/authorize.d.ts +0 -90
- package/dist/identity/authorize.js +0 -94
- package/dist/identity/grant-service.d.ts +0 -198
- package/dist/identity/grant-service.js +0 -361
- package/dist/identity/hasher.d.ts +0 -39
- package/dist/identity/hasher.js +0 -39
- package/dist/identity/index.d.ts +0 -62
- package/dist/identity/index.js +0 -54
- package/dist/identity/password-policy.d.ts +0 -57
- package/dist/identity/password-policy.js +0 -66
- package/dist/identity/permission-migrations.d.ts +0 -86
- package/dist/identity/permission-migrations.js +0 -67
- package/dist/identity/permissions.d.ts +0 -35
- package/dist/identity/permissions.js +0 -603
- package/dist/identity/ports.d.ts +0 -201
- package/dist/identity/repo.memory.d.ts +0 -164
- package/dist/identity/repo.memory.js +0 -224
- package/dist/identity/seed.d.ts +0 -54
- package/dist/identity/seed.js +0 -429
- package/dist/identity/tool-registrations.d.ts +0 -50
- package/dist/identity/tool-registrations.js +0 -419
- package/dist/identity/types.d.ts +0 -185
- package/dist/identity/types.js +0 -64
- package/dist/identity/username.d.ts +0 -12
- package/dist/identity/username.js +0 -14
- /package/dist/{identity → media/import}/ports.js +0 -0
|
@@ -1,62 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* @file The Workspace domain's agent-tool catalog, instantiating this package's shared
|
|
3
|
-
* naming/callability convention (the same shape every other domain catalog in this package
|
|
4
|
-
* already uses).
|
|
5
|
-
*
|
|
6
|
-
* Purpose:
|
|
7
|
-
* A static, in-process catalog describing every agent-callable tool this domain exposes and the
|
|
8
|
-
* permission each one carries. This is the highest-stakes domain wired so far: a host's
|
|
9
|
-
* `workspaceId` is fixed at process composition (a v1 install always has exactly one addressable
|
|
10
|
-
* workspace row), so `create`/`delete` are deliberately excluded
|
|
11
|
-
* outright rather than treated as ordinary CRUD:
|
|
12
|
-
* - `CREATE_WORKSPACE` inserts a row no route can ever address afterwards (every other workspace
|
|
13
|
-
* route resolves strictly against the boot-wired `workspaceId`, never a caller-supplied id)
|
|
14
|
-
* — wiring it would let an agent mint permanently orphaned, unreferenceable rows with no
|
|
15
|
-
* corresponding read/delete path back to them. Zero utility, non-zero clutter/spam surface.
|
|
16
|
-
* - `DELETE_WORKSPACE` is INV-03-guarded to always refuse in v1 (there is only ever one row), but
|
|
17
|
-
* the moment that guard's precondition changes (a second addressable workspace exists), the same
|
|
18
|
-
* tool becomes a lever that can remove the only workspace scope a request can reach — whole-scope,
|
|
19
|
-
* no per-domain undo, the same blast-radius class as a forward database migration or a backup
|
|
20
|
-
* restore. Excluded by design, mirroring those two.
|
|
21
|
-
* `UPDATE_WORKSPACE` is the one write kept: a narrow rename of `name`/`slug` on the single existing,
|
|
22
|
-
* already-addressable workspace, no cascading effects (`id`/`createdAt` immutable by convention —
|
|
23
|
-
* see `update.ts`), the same risk class as `identity_role_rename`.
|
|
24
|
-
*
|
|
25
|
-
* How it relates to the package:
|
|
26
|
-
* A host's server-side tool filter consumes this catalog to decide which tool names an
|
|
27
|
-
* agent session may even see; `authorize()` (the single-evaluator rule below) enforces the actual
|
|
28
|
-
* permission checks at call time — this module only declares the catalog shape, it performs no
|
|
29
|
-
* I/O and no enforcement itself.
|
|
30
|
-
*
|
|
31
|
-
* Architectural role:
|
|
32
|
-
* `workspace` domain logic. No dependencies.
|
|
33
|
-
*/
|
|
34
|
-
export type AgentToolSideEffect = "none" | "mutates-durable-state" | "mints-token";
|
|
35
|
-
export type AgentToolActorClassRule = "confirmer-must-equal-own-delegatedBy" | "user-only" | "none";
|
|
36
|
-
export interface AgentToolDefinition {
|
|
37
|
-
name: string;
|
|
38
|
-
description: string;
|
|
39
|
-
sideEffects: AgentToolSideEffect;
|
|
40
|
-
authorization: {
|
|
41
|
-
permission: string;
|
|
42
|
-
};
|
|
43
|
-
actorClassRule?: AgentToolActorClassRule;
|
|
44
|
-
/**
|
|
45
|
-
* JSON Schema for this tool's `input`, published to the model via `ToolDescriptor.inputSchema`
|
|
46
|
-
* by whichever host layer wires these tools (any such layer should refuse to wire a tool lacking
|
|
47
|
-
* one). Optional, matching this package's other domain catalogs — the two entries this dispatch
|
|
48
|
-
* leaves unwired (see file header) carry no schema at all, since one is never published for a
|
|
49
|
-
* tool the model never sees.
|
|
50
|
-
*
|
|
51
|
-
* Deliberately not widened to `| undefined` (unlike most optional fields in this port) — this
|
|
52
|
-
* type flows into `core/tools/registration-kit.ts`'s `WirableToolDefinition`, which declares both
|
|
53
|
-
* this field and `actorClassRule` above without `| undefined`. Widening only this catalog's copy
|
|
54
|
-
* would make it structurally incompatible with that shared interface under
|
|
55
|
-
* `exactOptionalPropertyTypes`, the same reason `navigation`'s and `identity`'s catalogs leave
|
|
56
|
-
* these two fields alone.
|
|
57
|
-
*/
|
|
58
|
-
inputSchema?: Readonly<Record<string, unknown>>;
|
|
59
|
-
}
|
|
1
|
+
import type { AgentToolDefinition } from "@jini-ai/core";
|
|
60
2
|
/**
|
|
61
3
|
* The Workspace domain's fixed agent-tool catalog.
|
|
62
4
|
*
|
|
@@ -10,28 +10,43 @@
|
|
|
10
10
|
* workspace row), so `create`/`delete` are deliberately excluded
|
|
11
11
|
* outright rather than treated as ordinary CRUD:
|
|
12
12
|
* - `CREATE_WORKSPACE` inserts a row no route can ever address afterwards (every other workspace
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* - `DELETE_WORKSPACE` is
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
13
|
+
* route resolves strictly against the boot-wired `workspaceId`, never a caller-supplied id)
|
|
14
|
+
* — wiring it would let an agent mint permanently orphaned, unreferenceable rows with no
|
|
15
|
+
* corresponding read/delete path back to them. Zero utility, non-zero clutter/spam surface.
|
|
16
|
+
* - `DELETE_WORKSPACE` is -guarded to always refuse in v1 (there is only ever one row), but
|
|
17
|
+
* the moment that guard's precondition changes (a second addressable workspace exists), the same
|
|
18
|
+
* tool becomes a lever that can remove the only workspace scope a request can reach — whole-scope,
|
|
19
|
+
* no per-domain undo, the same blast-radius class as a forward database migration or a backup
|
|
20
|
+
* restore. Excluded by design, mirroring those two.
|
|
21
21
|
* `UPDATE_WORKSPACE` is the one write kept: a narrow rename of `name`/`slug` on the single existing,
|
|
22
22
|
* already-addressable workspace, no cascading effects (`id`/`createdAt` immutable by convention —
|
|
23
23
|
* see `update.ts`), the same risk class as `identity_role_rename`.
|
|
24
24
|
*
|
|
25
25
|
* How it relates to the package:
|
|
26
26
|
* A host's server-side tool filter consumes this catalog to decide which tool names an
|
|
27
|
-
* agent session may even see; `authorize
|
|
27
|
+
* agent session may even see; `authorize` (the single-evaluator rule below) enforces the actual
|
|
28
28
|
* permission checks at call time — this module only declares the catalog shape, it performs no
|
|
29
29
|
* I/O and no enforcement itself.
|
|
30
30
|
*
|
|
31
31
|
* Architectural role:
|
|
32
32
|
* `workspace` domain logic. No dependencies.
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
* See docs/decisions/DR-007-workspace-and-owner-floors.md.
|
|
34
|
+
*
|
|
35
|
+
*
|
|
36
|
+
* JSON Schema for this tool's `input`, published to the model via `ToolDescriptor.inputSchema`
|
|
37
|
+
* by whichever host layer wires these tools (any such layer should refuse to wire a tool lacking
|
|
38
|
+
* one). Optional, matching this package's other domain catalogs — the two entries this dispatch
|
|
39
|
+
* leaves unwired (see file header) carry no schema at all, since one is never published for a
|
|
40
|
+
* tool the model never sees.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately not widened to `| undefined` (unlike most optional fields in this port) — this
|
|
43
|
+
* type flows into `@jini-ai/core`'s `registration-kit.ts`'s `AgentToolDefinition`, which declares both
|
|
44
|
+
* this field and `actorClassRule` above without `| undefined`. Widening only this catalog's copy
|
|
45
|
+
* would make it structurally incompatible with that shared interface under
|
|
46
|
+
* `exactOptionalPropertyTypes`, the same reason `navigation`'s and `identity`'s catalogs leave
|
|
47
|
+
* these two fields alone.
|
|
48
|
+
*
|
|
49
|
+
* No arguments — `workspace_get` takes none; it always reports the caller's own boot-wired workspace. */
|
|
35
50
|
const NO_INPUT_SCHEMA = {
|
|
36
51
|
type: "object",
|
|
37
52
|
additionalProperties: false,
|
|
@@ -88,7 +103,7 @@ export function getWorkspaceAgentToolCatalog() {
|
|
|
88
103
|
authorization: { permission: "workspace.manage" },
|
|
89
104
|
},
|
|
90
105
|
{
|
|
91
|
-
// EXCLUDED BY DESIGN, never wired: see this file's header.
|
|
106
|
+
// EXCLUDED BY DESIGN, never wired: see this file's header. -guarded to always refuse. See docs/decisions/DR-007-workspace-and-owner-floors.md.
|
|
92
107
|
// today (v1 has exactly one workspace row), but the lever itself removes the only addressable
|
|
93
108
|
// workspace scope the moment that guard's precondition ever changes — whole-scope,
|
|
94
109
|
// irreversible, no per-domain undo. Same exclusion class as a forward database migration or a
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Clock, IdGenerator, UUID } from "@jini-ai/core/primitives";
|
|
2
|
+
import type { OutboxPort } from "../core/ports.js";
|
|
2
3
|
/**
|
|
3
4
|
* @file Workspace creation vertical slice (domain logic).
|
|
4
5
|
*
|
|
@@ -33,8 +34,12 @@ export interface WorkspaceRecord {
|
|
|
33
34
|
*/
|
|
34
35
|
export interface WorkspaceRepoPort {
|
|
35
36
|
insert(record: WorkspaceRecord): Promise<void>;
|
|
36
|
-
findBySlug(slug
|
|
37
|
-
|
|
37
|
+
findBySlug({ slug }: {
|
|
38
|
+
slug: string;
|
|
39
|
+
}): Promise<WorkspaceRecord | null>;
|
|
40
|
+
findById({ id }: {
|
|
41
|
+
id: UUID;
|
|
42
|
+
}): Promise<WorkspaceRecord | null>;
|
|
38
43
|
/** A v1 install always has exactly one row (see `delete.ts`'s header);
|
|
39
44
|
* the port itself is not limited to one. */
|
|
40
45
|
list(): Promise<WorkspaceRecord[]>;
|
|
@@ -43,7 +48,15 @@ export interface WorkspaceRepoPort {
|
|
|
43
48
|
update(record: WorkspaceRecord): Promise<void>;
|
|
44
49
|
/** Callers (see `delete.ts`) must apply the last-workspace guard
|
|
45
50
|
* themselves — this port method performs the row deletion only, no business rule. */
|
|
46
|
-
delete(id
|
|
51
|
+
delete({ id }: {
|
|
52
|
+
id: UUID;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
/** Optional for existing adapters; deletion needs this or an injected transaction.
|
|
55
|
+
* Serializes the entire count/guard/delete operation against competing deletions,
|
|
56
|
+
* rolling back on rejection. Durable adapters must cover all workspace writers. */
|
|
57
|
+
transaction?<T>(required: {
|
|
58
|
+
fn: () => Promise<T>;
|
|
59
|
+
}): Promise<T>;
|
|
47
60
|
}
|
|
48
61
|
/**
|
|
49
62
|
* Shared `name`/`slug` validation — used by both `createWorkspace` below
|
|
@@ -68,8 +81,8 @@ export interface CreateWorkspaceInput {
|
|
|
68
81
|
}
|
|
69
82
|
/** Dependencies required by the create-workspace slice. */
|
|
70
83
|
export interface CreateWorkspaceDeps {
|
|
71
|
-
idGen:
|
|
72
|
-
clock:
|
|
84
|
+
idGen: IdGenerator;
|
|
85
|
+
clock: Clock;
|
|
73
86
|
repo: WorkspaceRepoPort;
|
|
74
87
|
outbox: OutboxPort;
|
|
75
88
|
}
|
|
@@ -83,12 +96,21 @@ export interface CreateWorkspaceOptional {
|
|
|
83
96
|
}
|
|
84
97
|
/** Thrown when incoming command data is invalid. */
|
|
85
98
|
export declare class WorkspaceValidationError extends Error {
|
|
99
|
+
constructor({ message }: {
|
|
100
|
+
message: string;
|
|
101
|
+
}, optionalArgs?: ErrorOptions);
|
|
86
102
|
}
|
|
87
103
|
/** Thrown when uniqueness constraints are violated. */
|
|
88
104
|
export declare class WorkspaceConflictError extends Error {
|
|
105
|
+
constructor({ message }: {
|
|
106
|
+
message: string;
|
|
107
|
+
}, optionalArgs?: ErrorOptions);
|
|
89
108
|
}
|
|
90
109
|
/** Thrown when a `:workspaceId` does not resolve to a row. */
|
|
91
110
|
export declare class WorkspaceNotFoundError extends Error {
|
|
111
|
+
constructor({ message }: {
|
|
112
|
+
message: string;
|
|
113
|
+
}, optionalArgs?: ErrorOptions);
|
|
92
114
|
}
|
|
93
115
|
/**
|
|
94
116
|
* Thrown when `deleteWorkspace` (`delete.ts`) would remove the install's
|
|
@@ -98,6 +120,9 @@ export declare class WorkspaceNotFoundError extends Error {
|
|
|
98
120
|
* from `IdentityForbiddenError` for the identical reason (typed-reason clarity).
|
|
99
121
|
*/
|
|
100
122
|
export declare class WorkspaceLastRemainingError extends Error {
|
|
123
|
+
constructor({ message }: {
|
|
124
|
+
message: string;
|
|
125
|
+
}, optionalArgs?: ErrorOptions);
|
|
101
126
|
}
|
|
102
127
|
/**
|
|
103
128
|
* Execute create-workspace command.
|
package/dist/workspace/create.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nowIso as kernelNowIso } from "@jini-ai/core/primitives";
|
|
1
2
|
/**
|
|
2
3
|
* Shared `name`/`slug` validation — used by both `createWorkspace` below
|
|
3
4
|
* and `updateWorkspace` (`update.ts`) so the two transitions can never drift on what counts as a
|
|
@@ -11,20 +12,29 @@ export function validateWorkspaceNameAndSlug(input) {
|
|
|
11
12
|
const name = input.name.trim();
|
|
12
13
|
const slug = input.slug.trim().toLowerCase();
|
|
13
14
|
if (!name)
|
|
14
|
-
throw new WorkspaceValidationError("name is required");
|
|
15
|
+
throw new WorkspaceValidationError({ message: "name is required" });
|
|
15
16
|
if (!slug.match(/^[a-z0-9-]+$/)) {
|
|
16
|
-
throw new WorkspaceValidationError("slug must use lowercase letters, numbers, and dashes");
|
|
17
|
+
throw new WorkspaceValidationError({ message: "slug must use lowercase letters, numbers, and dashes" });
|
|
17
18
|
}
|
|
18
19
|
return { name, slug };
|
|
19
20
|
}
|
|
20
21
|
/** Thrown when incoming command data is invalid. */
|
|
21
22
|
export class WorkspaceValidationError extends Error {
|
|
23
|
+
constructor({ message }, optionalArgs = {}) {
|
|
24
|
+
super(message, optionalArgs);
|
|
25
|
+
}
|
|
22
26
|
}
|
|
23
27
|
/** Thrown when uniqueness constraints are violated. */
|
|
24
28
|
export class WorkspaceConflictError extends Error {
|
|
29
|
+
constructor({ message }, optionalArgs = {}) {
|
|
30
|
+
super(message, optionalArgs);
|
|
31
|
+
}
|
|
25
32
|
}
|
|
26
33
|
/** Thrown when a `:workspaceId` does not resolve to a row. */
|
|
27
34
|
export class WorkspaceNotFoundError extends Error {
|
|
35
|
+
constructor({ message }, optionalArgs = {}) {
|
|
36
|
+
super(message, optionalArgs);
|
|
37
|
+
}
|
|
28
38
|
}
|
|
29
39
|
/**
|
|
30
40
|
* Thrown when `deleteWorkspace` (`delete.ts`) would remove the install's
|
|
@@ -34,6 +44,9 @@ export class WorkspaceNotFoundError extends Error {
|
|
|
34
44
|
* from `IdentityForbiddenError` for the identical reason (typed-reason clarity).
|
|
35
45
|
*/
|
|
36
46
|
export class WorkspaceLastRemainingError extends Error {
|
|
47
|
+
constructor({ message }, optionalArgs = {}) {
|
|
48
|
+
super(message, optionalArgs);
|
|
49
|
+
}
|
|
37
50
|
}
|
|
38
51
|
/**
|
|
39
52
|
* Execute create-workspace command.
|
|
@@ -49,10 +62,10 @@ export class WorkspaceLastRemainingError extends Error {
|
|
|
49
62
|
export async function createWorkspace(required, _optional = {}) {
|
|
50
63
|
const { deps, input } = required;
|
|
51
64
|
const { name, slug } = validateWorkspaceNameAndSlug(input);
|
|
52
|
-
const existing = await deps.repo.findBySlug(slug);
|
|
65
|
+
const existing = await deps.repo.findBySlug({ slug: slug });
|
|
53
66
|
if (existing)
|
|
54
|
-
throw new WorkspaceConflictError(`slug '${slug}' already exists`);
|
|
55
|
-
const now = deps.clock
|
|
67
|
+
throw new WorkspaceConflictError({ message: `slug '${slug}' already exists` });
|
|
68
|
+
const now = kernelNowIso({ clock: deps.clock });
|
|
56
69
|
const id = deps.idGen.newId();
|
|
57
70
|
await deps.repo.insert({ id, name, slug, createdAt: now });
|
|
58
71
|
const event = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TransactionalRepoPort } from "../taxonomy/write-service.js";
|
|
2
|
+
import type { UUID } from "@jini-ai/core/primitives";
|
|
2
3
|
import { type WorkspaceRepoPort } from "./create.js";
|
|
3
4
|
/**
|
|
4
5
|
* @file `DELETE_WORKSPACE` — hard-delete a workspace, guarded so the
|
|
@@ -13,14 +14,15 @@ import { type WorkspaceRepoPort } from "./create.js";
|
|
|
13
14
|
* works without further design.
|
|
14
15
|
*
|
|
15
16
|
* Architectural role:
|
|
16
|
-
* Ordinary slice function — not a port. `deps.repo.list
|
|
17
|
-
* check and the delete
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* Ordinary slice function — not a port. `deps.repo.list` is the count source; the guard
|
|
18
|
+
* check and the delete run in one transaction. The earlier single-Node-event-loop,
|
|
19
|
+
* "atomic by construction" reasoning (also used for `identity/grant-service.ts`'s transitions
|
|
20
|
+
* and `DISABLE_PRINCIPAL`'s count-check-then-disable) was insufficient: awaits can
|
|
21
|
+
* interleave even in one process. needs serialization across the count read and delete.
|
|
22
|
+
* The memory adapter queues transactions; a multi-process/multi-connection deployment needs
|
|
23
|
+
* a DB-level transaction that serializes all workspace writers, supplied by the host.
|
|
24
|
+
* Without the required transaction port, deletion fails closed before repository reads.
|
|
25
|
+
* See docs/decisions/DR-007-workspace-and-owner-floors.md.
|
|
24
26
|
*/
|
|
25
27
|
/** Command payload for `DELETE_WORKSPACE`. */
|
|
26
28
|
export interface DeleteWorkspaceInput {
|
|
@@ -29,6 +31,8 @@ export interface DeleteWorkspaceInput {
|
|
|
29
31
|
/** Dependencies required by the delete-workspace slice. */
|
|
30
32
|
export interface DeleteWorkspaceDeps {
|
|
31
33
|
repo: WorkspaceRepoPort;
|
|
34
|
+
/** Required transaction; must serialize workspace writers on its store. */
|
|
35
|
+
transaction: TransactionalRepoPort["transaction"];
|
|
32
36
|
}
|
|
33
37
|
/** Required parameters for `deleteWorkspace`. */
|
|
34
38
|
export interface DeleteWorkspaceRequired {
|
|
@@ -36,15 +40,16 @@ export interface DeleteWorkspaceRequired {
|
|
|
36
40
|
input: DeleteWorkspaceInput;
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
39
|
-
* Execute `DELETE_WORKSPACE
|
|
40
|
-
* resolve to a row, else `WorkspaceLastRemainingError`
|
|
43
|
+
* Execute `DELETE_WORKSPACE`. Throws `WorkspaceNotFoundError` if `input.id` does not
|
|
44
|
+
* resolve to a row, else `WorkspaceLastRemainingError` if it is the only workspace row —
|
|
41
45
|
* which is always true in v1, so this transition always refuses today. No row is deleted on either
|
|
42
46
|
* rejection.
|
|
43
47
|
*
|
|
44
|
-
* @complexity O(n) in the total workspace count (`repo.list
|
|
45
|
-
* operator-managed-roster assumption `identity`'s `PrincipalRepoPort.list
|
|
48
|
+
* @complexity O(n) in the total workspace count (`repo.list`) — bounded by the same
|
|
49
|
+
* operator-managed-roster assumption `identity`'s `PrincipalRepoPort.list` already makes; not a
|
|
46
50
|
* caller-controlled collection.
|
|
47
51
|
* @overallScore 100
|
|
52
|
+
* See docs/decisions/DR-007-workspace-and-owner-floors.md.
|
|
48
53
|
*/
|
|
49
54
|
export declare function deleteWorkspace(required: DeleteWorkspaceRequired): Promise<void>;
|
|
50
55
|
//# sourceMappingURL=delete.d.ts.map
|
package/dist/workspace/delete.js
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
import { WorkspaceLastRemainingError, WorkspaceNotFoundError } from "./create.js";
|
|
2
2
|
/**
|
|
3
|
-
* Execute `DELETE_WORKSPACE
|
|
4
|
-
* resolve to a row, else `WorkspaceLastRemainingError`
|
|
3
|
+
* Execute `DELETE_WORKSPACE`. Throws `WorkspaceNotFoundError` if `input.id` does not
|
|
4
|
+
* resolve to a row, else `WorkspaceLastRemainingError` if it is the only workspace row —
|
|
5
5
|
* which is always true in v1, so this transition always refuses today. No row is deleted on either
|
|
6
6
|
* rejection.
|
|
7
7
|
*
|
|
8
|
-
* @complexity O(n) in the total workspace count (`repo.list
|
|
9
|
-
* operator-managed-roster assumption `identity`'s `PrincipalRepoPort.list
|
|
8
|
+
* @complexity O(n) in the total workspace count (`repo.list`) — bounded by the same
|
|
9
|
+
* operator-managed-roster assumption `identity`'s `PrincipalRepoPort.list` already makes; not a
|
|
10
10
|
* caller-controlled collection.
|
|
11
11
|
* @overallScore 100
|
|
12
|
+
* See docs/decisions/DR-007-workspace-and-owner-floors.md.
|
|
12
13
|
*/
|
|
13
14
|
export async function deleteWorkspace(required) {
|
|
14
15
|
const { deps, input } = required;
|
|
15
|
-
const
|
|
16
|
-
if (!
|
|
17
|
-
throw new
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const transaction = deps.transaction;
|
|
17
|
+
if (!transaction)
|
|
18
|
+
throw new Error("workspace deletion requires a transaction port");
|
|
19
|
+
const guardedDelete = async () => {
|
|
20
|
+
const existing = await deps.repo.findById({ id: input.id });
|
|
21
|
+
if (!existing)
|
|
22
|
+
throw new WorkspaceNotFoundError({ message: `workspace '${input.id}' was not found` });
|
|
23
|
+
const all = await deps.repo.list();
|
|
24
|
+
if (all.length <= 1) {
|
|
25
|
+
// See docs/decisions/DR-007-workspace-and-owner-floors.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
|
|
26
|
+
throw new WorkspaceLastRemainingError({ message: "the install's last remaining workspace cannot be deleted" });
|
|
27
|
+
}
|
|
28
|
+
await deps.repo.delete({ id: input.id });
|
|
29
|
+
};
|
|
30
|
+
await transaction({ fn: guardedDelete });
|
|
23
31
|
}
|
|
24
32
|
//# sourceMappingURL=delete.js.map
|
|
@@ -9,7 +9,7 @@ export { updateWorkspace, type UpdateWorkspaceDeps, type UpdateWorkspaceInput, t
|
|
|
9
9
|
export { deleteWorkspace, type DeleteWorkspaceDeps, type DeleteWorkspaceInput, type DeleteWorkspaceRequired, } from "./delete.js";
|
|
10
10
|
export { InMemoryWorkspaceRepo } from "./repo.memory.js";
|
|
11
11
|
/** The agent-tool surface for this domain (see `agent-tools.ts` for what is deliberately omitted). */
|
|
12
|
-
export { getWorkspaceAgentToolCatalog,
|
|
12
|
+
export { getWorkspaceAgentToolCatalog, } from "./agent-tools.js";
|
|
13
13
|
/**
|
|
14
14
|
* The agent-tool wiring for this domain.
|
|
15
15
|
*
|
|
@@ -17,18 +17,33 @@ import type { WorkspaceRecord, WorkspaceRepoPort } from "./create.js";
|
|
|
17
17
|
export declare class InMemoryWorkspaceRepo implements WorkspaceRepoPort {
|
|
18
18
|
/** Internal record storage. */
|
|
19
19
|
private rows;
|
|
20
|
-
|
|
20
|
+
private transactionTail;
|
|
21
|
+
constructor(requiredArgs: Record<string, never>, optionalArgs?: {
|
|
22
|
+
initialRows?: WorkspaceRecord[];
|
|
23
|
+
});
|
|
21
24
|
/** Insert one workspace record. */
|
|
22
25
|
insert(record: WorkspaceRecord): Promise<void>;
|
|
23
26
|
/** Find workspace by unique slug. */
|
|
24
|
-
findBySlug(slug
|
|
27
|
+
findBySlug({ slug }: {
|
|
28
|
+
slug: string;
|
|
29
|
+
}): Promise<WorkspaceRecord | null>;
|
|
25
30
|
/** Find workspace by id. */
|
|
26
|
-
findById(id
|
|
31
|
+
findById({ id }: {
|
|
32
|
+
id: string;
|
|
33
|
+
}): Promise<WorkspaceRecord | null>;
|
|
27
34
|
/** All workspace rows (v1 always has exactly one — see `delete.ts`'s header). */
|
|
28
35
|
list(): Promise<WorkspaceRecord[]>;
|
|
29
36
|
/** Replace the row matching `record.id` in place. */
|
|
30
37
|
update(record: WorkspaceRecord): Promise<void>;
|
|
31
38
|
/** Remove the row matching `id`, if present (idempotent). */
|
|
32
|
-
delete(id
|
|
39
|
+
delete({ id }: {
|
|
40
|
+
id: string;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
/** Serializes transactions on this instance, restoring rows on failure. Non-reentrant:
|
|
43
|
+
* callers must put every competing guarded deletion through this seam. Direct repo writes
|
|
44
|
+
* do not join or wait for it; this local adapter supplies no cross-process isolation. */
|
|
45
|
+
transaction<T>({ fn }: {
|
|
46
|
+
fn: () => Promise<T>;
|
|
47
|
+
}): Promise<T>;
|
|
33
48
|
}
|
|
34
49
|
//# sourceMappingURL=repo.memory.d.ts.map
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
export class InMemoryWorkspaceRepo {
|
|
17
17
|
/** Internal record storage. */
|
|
18
18
|
rows;
|
|
19
|
-
|
|
19
|
+
transactionTail = Promise.resolve();
|
|
20
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
21
|
+
const { initialRows = [] } = optionalArgs;
|
|
20
22
|
this.rows = [...initialRows];
|
|
21
23
|
}
|
|
22
24
|
/** Insert one workspace record. */
|
|
@@ -24,11 +26,11 @@ export class InMemoryWorkspaceRepo {
|
|
|
24
26
|
this.rows.push(record);
|
|
25
27
|
}
|
|
26
28
|
/** Find workspace by unique slug. */
|
|
27
|
-
async findBySlug(slug) {
|
|
29
|
+
async findBySlug({ slug }) {
|
|
28
30
|
return this.rows.find((row) => row.slug === slug) ?? null;
|
|
29
31
|
}
|
|
30
32
|
/** Find workspace by id. */
|
|
31
|
-
async findById(id) {
|
|
33
|
+
async findById({ id }) {
|
|
32
34
|
return this.rows.find((row) => row.id === id) ?? null;
|
|
33
35
|
}
|
|
34
36
|
/** All workspace rows (v1 always has exactly one — see `delete.ts`'s header). */
|
|
@@ -43,8 +45,28 @@ export class InMemoryWorkspaceRepo {
|
|
|
43
45
|
this.rows[index] = record;
|
|
44
46
|
}
|
|
45
47
|
/** Remove the row matching `id`, if present (idempotent). */
|
|
46
|
-
async delete(id) {
|
|
48
|
+
async delete({ id }) {
|
|
47
49
|
this.rows = this.rows.filter((row) => row.id !== id);
|
|
48
50
|
}
|
|
51
|
+
/** Serializes transactions on this instance, restoring rows on failure. Non-reentrant:
|
|
52
|
+
* callers must put every competing guarded deletion through this seam. Direct repo writes
|
|
53
|
+
* do not join or wait for it; this local adapter supplies no cross-process isolation. */
|
|
54
|
+
async transaction({ fn }) {
|
|
55
|
+
const previous = this.transactionTail;
|
|
56
|
+
let release;
|
|
57
|
+
this.transactionTail = new Promise((resolve) => { release = resolve; });
|
|
58
|
+
await previous;
|
|
59
|
+
const before = this.rows.map((row) => ({ ...row }));
|
|
60
|
+
try {
|
|
61
|
+
return await fn();
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
this.rows = before;
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
release();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
49
71
|
}
|
|
50
72
|
//# sourceMappingURL=repo.memory.js.map
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
* `workspace.manage` check.
|
|
15
15
|
*/
|
|
16
16
|
import type { AuthorizeFn } from "../core/commands/command.js";
|
|
17
|
-
import { type DerivedRiskByToolId, type ToolRegistration } from "
|
|
17
|
+
import { type DerivedRiskByToolId, type ToolRegistration } from "@jini-ai/core";
|
|
18
18
|
import type { WorkspaceRepoPort } from "./create.js";
|
|
19
19
|
/**
|
|
20
20
|
* The exact slice of a host's route-deps bag Workspace's tool handlers read. Declared structurally
|
|
21
21
|
* (rather than importing any host's `RouteDeps`) so this module carries no back-edge into a
|
|
22
22
|
* composition root. A host satisfies this structurally by passing its existing deps object;
|
|
23
|
-
*
|
|
23
|
+
* hosts bind the declared repository and authorization ports.
|
|
24
24
|
*/
|
|
25
25
|
export interface WorkspaceToolDeps {
|
|
26
26
|
authorize: AuthorizeFn;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { buildDomainRegistrations, indexCatalogById, optionalString, requireInputRecord, requireNoInput
|
|
1
|
+
import { buildDomainRegistrations, indexCatalogById, optionalString, requireInputRecord, requireNoInput } from "@jini-ai/core";
|
|
2
|
+
import { adaptLegacyAuthorize, requireToolPermission } from "../core/tools/index.js";
|
|
2
3
|
import { getWorkspaceAgentToolCatalog } from "./agent-tools.js";
|
|
3
4
|
import { updateWorkspace } from "./update.js";
|
|
4
|
-
const CATALOG_BY_ID = indexCatalogById(getWorkspaceAgentToolCatalog());
|
|
5
|
+
const CATALOG_BY_ID = indexCatalogById({ catalog: getWorkspaceAgentToolCatalog() });
|
|
5
6
|
/**
|
|
6
7
|
* This wiring layer's OWN risk classification, authored from what each handler below actually
|
|
7
8
|
* calls. See `DerivedRiskByToolId` in the kit for why it is independent of the catalog's own
|
|
@@ -14,7 +15,7 @@ export const workspaceDerivedRisk = new Map([
|
|
|
14
15
|
["workspace_get", "none"],
|
|
15
16
|
// -> updateWorkspace (update.ts) -> repo.findById + repo.findBySlug + repo.update: validates,
|
|
16
17
|
// checks slug uniqueness excluding the row itself, writes. No domain event enqueued (matches
|
|
17
|
-
//
|
|
18
|
+
// update.ts's own header: doesn't ask for one). See docs/decisions/DR-007-workspace-and-owner-floors.md.
|
|
18
19
|
["workspace_update", "mutates-durable-state"],
|
|
19
20
|
]);
|
|
20
21
|
/** Workspace catalog entries this pass does not wire, and why — see `./agent-tools.ts`'s own per-entry comments for the full reasoning. */
|
|
@@ -23,7 +24,7 @@ const UNWIRED_WORKSPACE_TOOL_IDS = new Set([
|
|
|
23
24
|
// resolves strictly against the boot-wired `workspaceId`) — zero utility, non-zero
|
|
24
25
|
// orphaned-row/clutter surface.
|
|
25
26
|
"workspace_create",
|
|
26
|
-
// EXCLUDED BY DESIGN:
|
|
27
|
+
// EXCLUDED BY DESIGN: -guarded to always refuse today, but the lever removes the only. See docs/decisions/DR-007-workspace-and-owner-floors.md.
|
|
27
28
|
// addressable workspace scope the moment that guard's precondition ever changes — whole-scope,
|
|
28
29
|
// no per-domain undo, same exclusion class as a forward database migration or a backup restore.
|
|
29
30
|
"workspace_delete",
|
|
@@ -31,9 +32,9 @@ const UNWIRED_WORKSPACE_TOOL_IDS = new Set([
|
|
|
31
32
|
export function buildWorkspaceRegistrations(routeDeps) {
|
|
32
33
|
const handlers = {
|
|
33
34
|
workspace_get: async (ctx) => {
|
|
34
|
-
requireNoInput(ctx.input);
|
|
35
|
-
await requireToolPermission(routeDeps,
|
|
36
|
-
const workspace = await routeDeps.workspaceRepo.findById(routeDeps.workspaceId);
|
|
35
|
+
requireNoInput({ input: ctx.input });
|
|
36
|
+
await requireToolPermission({ authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }), workspaceId: routeDeps.workspaceId, principalId: ctx.principal.id, permission: "workspace.manage" }, { entityType: "workspace" });
|
|
37
|
+
const workspace = await routeDeps.workspaceRepo.findById({ id: routeDeps.workspaceId });
|
|
37
38
|
if (!workspace) {
|
|
38
39
|
// Defensive: the boot-wired workspaceId always resolves to a real row by construction.
|
|
39
40
|
// Not an expected runtime path.
|
|
@@ -42,8 +43,8 @@ export function buildWorkspaceRegistrations(routeDeps) {
|
|
|
42
43
|
return { workspace };
|
|
43
44
|
},
|
|
44
45
|
workspace_update: async (ctx) => {
|
|
45
|
-
const input = requireInputRecord(ctx.input);
|
|
46
|
-
await requireToolPermission(routeDeps,
|
|
46
|
+
const input = requireInputRecord({ input: ctx.input });
|
|
47
|
+
await requireToolPermission({ authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }), workspaceId: routeDeps.workspaceId, principalId: ctx.principal.id, permission: "workspace.manage" }, { entityType: "workspace" });
|
|
47
48
|
// WorkspaceValidationError/WorkspaceConflictError/WorkspaceNotFoundError (all real `Error`
|
|
48
49
|
// subclasses, see `create.ts`) propagate as-is — a `ToolExecutor` treats any thrown error as
|
|
49
50
|
// a failed execution, matching every other domain's handler convention of not rewrapping
|
|
@@ -52,8 +53,8 @@ export function buildWorkspaceRegistrations(routeDeps) {
|
|
|
52
53
|
deps: { repo: routeDeps.workspaceRepo },
|
|
53
54
|
input: {
|
|
54
55
|
id: routeDeps.workspaceId,
|
|
55
|
-
name: optionalString(input, "name"),
|
|
56
|
-
slug: optionalString(input, "slug"),
|
|
56
|
+
name: optionalString({ input, key: "name" }),
|
|
57
|
+
slug: optionalString({ input, key: "slug" }),
|
|
57
58
|
},
|
|
58
59
|
});
|
|
59
60
|
return { workspace };
|
|
@@ -65,7 +66,6 @@ export function buildWorkspaceRegistrations(routeDeps) {
|
|
|
65
66
|
catalog: CATALOG_BY_ID,
|
|
66
67
|
handlers,
|
|
67
68
|
derivedRisk: workspaceDerivedRisk,
|
|
68
|
-
|
|
69
|
-
});
|
|
69
|
+
}, { unwiredToolIds: UNWIRED_WORKSPACE_TOOL_IDS });
|
|
70
70
|
}
|
|
71
71
|
//# sourceMappingURL=tool-registrations.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UUID } from "
|
|
1
|
+
import type { UUID } from "@jini-ai/core/primitives";
|
|
2
2
|
import { type WorkspaceRecord, type WorkspaceRepoPort } from "./create.js";
|
|
3
3
|
/**
|
|
4
4
|
* @file `UPDATE_WORKSPACE` — rename a workspace's `name` and/or `slug`.
|
|
@@ -14,7 +14,7 @@ import { type WorkspaceRecord, type WorkspaceRepoPort } from "./create.js";
|
|
|
14
14
|
* Ordinary slice function, not a port — one implementation, the rule-of-two doesn't
|
|
15
15
|
* apply to business logic, only to the repo/outbox seams it calls through `WorkspaceRepoPort`.
|
|
16
16
|
*/
|
|
17
|
-
/** Command payload for `UPDATE_WORKSPACE`. At least one of `name`/`slug` must be present
|
|
17
|
+
/** Command payload for `UPDATE_WORKSPACE`. At least one of `name`/`slug` must be present. See docs/decisions/DR-007-workspace-and-owner-floors.md. */
|
|
18
18
|
export interface UpdateWorkspaceInput {
|
|
19
19
|
id: UUID;
|
|
20
20
|
name?: string | undefined;
|
|
@@ -30,8 +30,8 @@ export interface UpdateWorkspaceRequired {
|
|
|
30
30
|
input: UpdateWorkspaceInput;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* Execute `UPDATE_WORKSPACE
|
|
34
|
-
* (
|
|
33
|
+
* Execute `UPDATE_WORKSPACE`. At least one of `name`/`slug` must be present in the input
|
|
34
|
+
* ( — an empty-object update is rejected, not a silent no-op, so a client-side bug that drops
|
|
35
35
|
* both fields fails loudly instead of masquerading as a successful save). When only one field is
|
|
36
36
|
* given, the other keeps its current stored value; both are re-validated together through
|
|
37
37
|
* `validateWorkspaceNameAndSlug` either way, so a `slug`-only update still catches a blank stored
|
|
@@ -39,12 +39,13 @@ export interface UpdateWorkspaceRequired {
|
|
|
39
39
|
* rejects that, but re-validating both together — rather than only the changed field — keeps this
|
|
40
40
|
* function's invariant identical to `createWorkspace`'s with no special-cased partial path).
|
|
41
41
|
*
|
|
42
|
-
* The slug-uniqueness check excludes the row being updated (
|
|
42
|
+
* The slug-uniqueness check excludes the row being updated ( — a workspace is never "in
|
|
43
43
|
* conflict with itself"): `findBySlug` naturally excludes it whenever the row's own `id` matches,
|
|
44
44
|
* since that's a no-op rename, not a collision with a *different* row.
|
|
45
45
|
*
|
|
46
46
|
* @complexity O(1) — one lookup by id, one lookup by slug, one write.
|
|
47
47
|
* @overallScore 100
|
|
48
|
+
* See docs/decisions/DR-007-workspace-and-owner-floors.md.
|
|
48
49
|
*/
|
|
49
50
|
export declare function updateWorkspace(required: UpdateWorkspaceRequired): Promise<{
|
|
50
51
|
workspace: WorkspaceRecord;
|