@astrale-domains/shell 0.1.0-beta.17 → 0.1.0-beta.19
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 +20 -0
- package/README.md +7 -2
- package/dist/application.js +33 -7
- package/dist/mutations/group/membership.d.ts +6 -2
- package/dist/mutations/group/membership.js +18 -5
- package/dist/mutations/user/repair.d.ts +5 -2
- package/dist/mutations/user/repair.js +21 -0
- package/dist/queries/user/groups.d.ts +3 -2
- package/dist/queries/user/groups.js +27 -3
- package/dist/schema/group/relationships.js +1 -1
- package/dist/workflows/group/change.js +16 -6
- package/dist/workflows/setup/plan.js +13 -1
- package/dist/workflows/setup/provision.js +10 -0
- package/dist/workflows/setup/run.js +5 -4
- package/dist/workflows/space/access.js +3 -3
- package/dist/workflows/space/default.js +3 -3
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.0-beta.19](https://github.com/astrale-os/shell/compare/shell-domain-v0.1.0-beta.18...shell-domain-v0.1.0-beta.19) (2026-08-31)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **domain:** preserve caller-owned Shell authority ([#143](https://github.com/astrale-os/shell/issues/143)) ([7150ce0](https://github.com/astrale-os/shell/commit/7150ce0890546b6c605f9192aa78177b95af3615))
|
|
9
|
+
|
|
10
|
+
## [0.1.0-beta.18](https://github.com/astrale-os/shell/compare/shell-domain-v0.1.0-beta.17...shell-domain-v0.1.0-beta.18) (2026-08-30)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* grant trusted Shell exchange authority ([#140](https://github.com/astrale-os/shell/issues/140)) ([349c4b5](https://github.com/astrale-os/shell/commit/349c4b593b20245a8cb836f2370807b372faf23f))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **deps:** consume Zod-coherent SDK cohort ([#145](https://github.com/astrale-os/shell/issues/145)) ([2d2027c](https://github.com/astrale-os/shell/commit/2d2027c749f83c1e35a5b4a28696761b1f8e71e1))
|
|
21
|
+
* **domain:** adopt function and authority requirements ([#144](https://github.com/astrale-os/shell/issues/144)) ([016e306](https://github.com/astrale-os/shell/commit/016e30603591c32a2c8cf0073fe75995c67ae004))
|
|
22
|
+
|
|
3
23
|
## [0.1.0-beta.17](https://github.com/astrale-os/shell/compare/shell-domain-v0.1.0-beta.16...shell-domain-v0.1.0-beta.17) (2026-08-30)
|
|
4
24
|
|
|
5
25
|
|
package/README.md
CHANGED
|
@@ -22,8 +22,9 @@ converge the first administrator, using the exact registered issuer and subject
|
|
|
22
22
|
`setupAdmin` for subsequent registered Users. Both setup Functions are authorized by
|
|
23
23
|
`maySetupUsers` against membership in the Core Administrators Group.
|
|
24
24
|
They converge the User profile, personal Space, ownership/default facts, Members membership, and—only
|
|
25
|
-
for `setupAdmin`—Administrators membership.
|
|
26
|
-
`
|
|
25
|
+
for `setupAdmin`—Administrators membership. Every membership mutation also changes the matching
|
|
26
|
+
Kernel `extends_with` composition edge atomically. They do not create credentials, author dynamic
|
|
27
|
+
`can_*` edges, infer identity from a local CLI name, or replace self-proven registration.
|
|
27
28
|
|
|
28
29
|
The Methods are:
|
|
29
30
|
|
|
@@ -33,6 +34,10 @@ The Methods are:
|
|
|
33
34
|
|
|
34
35
|
Installation authors the `admin`, `member`, and `default-space` Core nodes. Shell infers no authority
|
|
35
36
|
from email, claims, path spelling, creation order, or a successful handler-local boolean.
|
|
37
|
+
The signed Publication declares installation-owned authority profiles: the Shell principal and
|
|
38
|
+
Core `admin` receive the intended Kernel application-plane surface, excluding only the ability to
|
|
39
|
+
grant `grant` or revoke `revoke`; Core `member` receives Query and View resolution. Kernel install,
|
|
40
|
+
upgrade, introspection, and uninstall own those authority edges as one generation.
|
|
36
41
|
|
|
37
42
|
## Source ownership
|
|
38
43
|
|
package/dist/application.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { defineApplication, defineFrontend, external, requirements, } from '@astrale-os/sdk/application';
|
|
2
|
-
import { KernelRootFunctions } from '@astrale-os/sdk/schema';
|
|
2
|
+
import { K, KernelRootFunctions } from '@astrale-os/sdk/schema';
|
|
3
3
|
import runtime from './runtime.js';
|
|
4
|
-
import { ShellSchema } from './schema/index.js';
|
|
4
|
+
import { S, ShellSchema } from './schema/index.js';
|
|
5
|
+
const classOperations = ['create', 'read', 'traverse', 'update', 'delete'];
|
|
6
|
+
const authority = {
|
|
7
|
+
grant: ['create', 'read', 'update', 'delete', 'traverse', 'use', 'revoke'],
|
|
8
|
+
revoke: ['create', 'read', 'update', 'delete', 'traverse', 'use', 'grant'],
|
|
9
|
+
};
|
|
5
10
|
export const frontend = defineFrontend({
|
|
6
11
|
schema: ShellSchema,
|
|
7
12
|
source: external('https://app.astrale.ai'),
|
|
@@ -14,11 +19,32 @@ export const frontend = defineFrontend({
|
|
|
14
19
|
entrypoint: 'home',
|
|
15
20
|
});
|
|
16
21
|
const trustedKernel = requirements({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
functions: KernelRootFunctions,
|
|
23
|
+
classes: [
|
|
24
|
+
{
|
|
25
|
+
class: K.classes.extends_with,
|
|
26
|
+
operations: ['create', 'read', 'traverse', 'delete'],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
authority,
|
|
30
|
+
profiles: [
|
|
31
|
+
{
|
|
32
|
+
subject: S.core.nodes.admin,
|
|
33
|
+
functions: KernelRootFunctions,
|
|
34
|
+
classes: [
|
|
35
|
+
...Object.values(S.classes).map((value) => ({ class: value, operations: classOperations })),
|
|
36
|
+
{
|
|
37
|
+
class: K.classes.extends_with,
|
|
38
|
+
operations: ['create', 'read', 'traverse', 'delete'],
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
authority,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
subject: S.core.nodes.member,
|
|
45
|
+
functions: [K.classes.View.static.methods.resolve, K.functions.query],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
22
48
|
});
|
|
23
49
|
export const application = defineApplication({
|
|
24
50
|
schema: ShellSchema,
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { NodeId } from '@astrale-os/sdk/graph/node';
|
|
2
2
|
import type { QueryPrecondition } from '@astrale-os/sdk/mutation';
|
|
3
|
-
import type {
|
|
3
|
+
import type { QueryEdgeAST } from '@astrale-os/sdk/query';
|
|
4
4
|
import { type Mutation } from '@astrale-os/sdk/mutation';
|
|
5
5
|
import type { Shell } from '#schema';
|
|
6
6
|
export interface GroupMembershipChange {
|
|
7
7
|
readonly user: string;
|
|
8
8
|
readonly group: NodeId;
|
|
9
9
|
readonly present: boolean;
|
|
10
|
-
readonly
|
|
10
|
+
readonly current: {
|
|
11
|
+
readonly membership: boolean;
|
|
12
|
+
readonly authority: boolean;
|
|
13
|
+
};
|
|
14
|
+
readonly guard: QueryPrecondition<QueryEdgeAST>;
|
|
11
15
|
}
|
|
12
16
|
export type GroupMembershipChangeResult = {
|
|
13
17
|
readonly ok: true;
|
|
@@ -4,15 +4,28 @@ export const changeGroupMembership = defineMutation()((domain) => ({
|
|
|
4
4
|
id: 'shell.group.membership',
|
|
5
5
|
build(change, mutation) {
|
|
6
6
|
mutation.expect.query(change.guard);
|
|
7
|
-
const
|
|
7
|
+
const membership = {
|
|
8
8
|
class: domain.classes.member_of_group,
|
|
9
9
|
source: Path.parse(change.user),
|
|
10
10
|
target: Path.id(change.group),
|
|
11
11
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const authority = {
|
|
13
|
+
class: domain.dependencies.kernel.classes.extends_with,
|
|
14
|
+
source: Path.parse(change.user),
|
|
15
|
+
target: Path.id(change.group),
|
|
16
|
+
};
|
|
17
|
+
if (change.current.membership !== change.present) {
|
|
18
|
+
if (change.present)
|
|
19
|
+
mutation.createEdge(membership);
|
|
20
|
+
else
|
|
21
|
+
mutation.deleteEdge(membership);
|
|
22
|
+
}
|
|
23
|
+
if (change.current.authority !== change.present) {
|
|
24
|
+
if (change.present)
|
|
25
|
+
mutation.createEdge(authority);
|
|
26
|
+
else
|
|
27
|
+
mutation.deleteEdge(authority);
|
|
28
|
+
}
|
|
16
29
|
},
|
|
17
30
|
project: () => Object.freeze({ ok: true }),
|
|
18
31
|
reject: (failure) => Object.freeze({ ok: false, code: failure.code, outcomeKnown: failure.outcomeKnown }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NodeId } from '@astrale-os/sdk/graph/node';
|
|
2
2
|
import type { QueryPrecondition } from '@astrale-os/sdk/mutation';
|
|
3
|
-
import type { QueryNodeAST } from '@astrale-os/sdk/query';
|
|
3
|
+
import type { QueryEdgeAST, QueryNodeAST } from '@astrale-os/sdk/query';
|
|
4
4
|
import { type Mutation } from '@astrale-os/sdk/mutation';
|
|
5
5
|
import type { Shell } from '#schema';
|
|
6
6
|
export interface UserRepair {
|
|
@@ -19,11 +19,14 @@ export interface UserRepair {
|
|
|
19
19
|
readonly addDefault: boolean;
|
|
20
20
|
readonly addOwnership: boolean;
|
|
21
21
|
readonly addMember: boolean;
|
|
22
|
+
readonly addMemberAuthority: boolean;
|
|
22
23
|
readonly addAdmin: boolean;
|
|
24
|
+
readonly addAdminAuthority: boolean;
|
|
25
|
+
readonly removeAdminAuthority: boolean;
|
|
23
26
|
readonly guards: {
|
|
24
27
|
readonly home: QueryPrecondition<QueryNodeAST>;
|
|
25
28
|
readonly ownedSpaces: QueryPrecondition<QueryNodeAST>;
|
|
26
|
-
readonly groups: QueryPrecondition<
|
|
29
|
+
readonly groups: QueryPrecondition<QueryEdgeAST>;
|
|
27
30
|
};
|
|
28
31
|
}
|
|
29
32
|
export type UserRepairResult = {
|
|
@@ -62,6 +62,13 @@ export const repairUser = defineMutation()((domain) => ({
|
|
|
62
62
|
target: Path.project(domain.core.nodes.member.ref),
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
|
+
if (repair.addMemberAuthority) {
|
|
66
|
+
mutation.createEdge({
|
|
67
|
+
class: domain.dependencies.kernel.classes.extends_with,
|
|
68
|
+
source: Path.id(repair.userId),
|
|
69
|
+
target: Path.project(domain.core.nodes.member.ref),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
65
72
|
if (repair.addAdmin) {
|
|
66
73
|
mutation.createEdge({
|
|
67
74
|
class: domain.classes.member_of_group,
|
|
@@ -69,6 +76,20 @@ export const repairUser = defineMutation()((domain) => ({
|
|
|
69
76
|
target: Path.project(domain.core.nodes.admin.ref),
|
|
70
77
|
});
|
|
71
78
|
}
|
|
79
|
+
if (repair.addAdminAuthority) {
|
|
80
|
+
mutation.createEdge({
|
|
81
|
+
class: domain.dependencies.kernel.classes.extends_with,
|
|
82
|
+
source: Path.id(repair.userId),
|
|
83
|
+
target: Path.project(domain.core.nodes.admin.ref),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (repair.removeAdminAuthority) {
|
|
87
|
+
mutation.deleteEdge({
|
|
88
|
+
class: domain.dependencies.kernel.classes.extends_with,
|
|
89
|
+
source: Path.id(repair.userId),
|
|
90
|
+
target: Path.project(domain.core.nodes.admin.ref),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
72
93
|
},
|
|
73
94
|
project(result, repair) {
|
|
74
95
|
const personalSpaceId = repair.home.kind === 'existing' ? repair.home.id : result.createdNodes[personalSpace];
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Node } from '@astrale-os/sdk/graph/node';
|
|
2
2
|
import type { QueryPrecondition } from '@astrale-os/sdk/mutation';
|
|
3
|
-
import type {
|
|
3
|
+
import type { QueryEdgeAST, SingleQueryDefinition } from '@astrale-os/sdk/query';
|
|
4
4
|
import type { Shell } from '#schema';
|
|
5
5
|
export interface UserGroupsObservation {
|
|
6
6
|
readonly groups: readonly Node[];
|
|
7
|
-
readonly
|
|
7
|
+
readonly authorities: readonly Node[];
|
|
8
|
+
readonly guard: QueryPrecondition<QueryEdgeAST>;
|
|
8
9
|
}
|
|
9
10
|
export declare const observeUserGroups: SingleQueryDefinition<Shell, string, UserGroupsObservation>;
|
|
@@ -4,13 +4,37 @@ export const observeUserGroups = defineQuery()((domain) => ({
|
|
|
4
4
|
id: 'shell.user.groups',
|
|
5
5
|
build(user) {
|
|
6
6
|
const source = Query.from({ nodes: [Path.parse(user)] });
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const relations = source.expand({
|
|
8
|
+
via: [domain.classes.member_of_group, domain.dependencies.kernel.classes.extends_with],
|
|
9
|
+
direction: 'outgoing',
|
|
10
|
+
});
|
|
11
|
+
return relations.select({
|
|
12
|
+
kind: 'edges',
|
|
13
|
+
binding: relations.edge,
|
|
14
|
+
projection: { edge: 'value', source: 'reference', target: 'value' },
|
|
15
|
+
});
|
|
9
16
|
},
|
|
10
17
|
pagination: { maximumPages: 64 },
|
|
11
18
|
project(pages, _user, { ast, complete }) {
|
|
19
|
+
const groups = [];
|
|
20
|
+
const authorities = [];
|
|
21
|
+
for (const item of pages.flatMap((page) => queryResult.edgeItems(page, 'Shell User Groups'))) {
|
|
22
|
+
if (item.edge.kind !== 'value' || item.target.kind !== 'value') {
|
|
23
|
+
throw new TypeError('Shell User Groups must project Edge and target values.');
|
|
24
|
+
}
|
|
25
|
+
if (item.edge.value.class === domain.classes.member_of_group.key) {
|
|
26
|
+
groups.push(item.target.value);
|
|
27
|
+
}
|
|
28
|
+
else if (item.edge.value.class === domain.dependencies.kernel.classes.extends_with.key) {
|
|
29
|
+
authorities.push(item.target.value);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
throw new TypeError('Shell User Groups returned an unexpected relationship Class.');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
12
35
|
return Object.freeze({
|
|
13
|
-
groups:
|
|
36
|
+
groups: Object.freeze(groups),
|
|
37
|
+
authorities: Object.freeze(authorities),
|
|
14
38
|
guard: Object.freeze({ query: ast, equals: complete }),
|
|
15
39
|
});
|
|
16
40
|
},
|
|
@@ -4,5 +4,5 @@ import { Group } from './class.js';
|
|
|
4
4
|
export const member_of_group = edgeClass.directed({
|
|
5
5
|
source: { as: 'user', accepts: [User], outgoing: '0..*' },
|
|
6
6
|
target: { as: 'group', accepts: [() => Group], incoming: '0..*' },
|
|
7
|
-
description: 'Shell
|
|
7
|
+
description: 'Shell role membership paired atomically with Kernel authority composition.',
|
|
8
8
|
});
|
|
@@ -2,25 +2,35 @@ import { InvocationError } from '@astrale-os/sdk/execution';
|
|
|
2
2
|
import { changeGroupMembership } from '#mutations/group';
|
|
3
3
|
import { observeUserGroups } from '#queries/user';
|
|
4
4
|
export async function changeMembership(context, present) {
|
|
5
|
-
const observed = await context.step.run('observe-membership', () => context.query(observeUserGroups, context.input.user));
|
|
6
|
-
const
|
|
7
|
-
if (
|
|
5
|
+
const observed = await context.step.run('observe-membership', () => context.graph.self.query(observeUserGroups, context.input.user));
|
|
6
|
+
const current = membershipState(observed, context.self);
|
|
7
|
+
if (matches(current, present))
|
|
8
8
|
return Object.freeze({});
|
|
9
|
-
const result = await context.step.run('change-membership', () => context.mutate(changeGroupMembership, {
|
|
9
|
+
const result = await context.step.run('change-membership', () => context.graph.self.mutate(changeGroupMembership, {
|
|
10
10
|
user: context.input.user,
|
|
11
11
|
group: context.self,
|
|
12
12
|
present,
|
|
13
|
+
current,
|
|
13
14
|
guard: observed.guard,
|
|
14
15
|
}));
|
|
15
16
|
if (result.ok)
|
|
16
17
|
return Object.freeze({});
|
|
17
18
|
if (result.outcomeKnown)
|
|
18
19
|
return membershipConflict(result.code);
|
|
19
|
-
const recovered = await context.step.run('recover-membership', () => context.query(observeUserGroups, context.input.user));
|
|
20
|
-
if (
|
|
20
|
+
const recovered = await context.step.run('recover-membership', () => context.graph.self.query(observeUserGroups, context.input.user));
|
|
21
|
+
if (matches(membershipState(recovered, context.self), present))
|
|
21
22
|
return Object.freeze({});
|
|
22
23
|
throw new InvocationError('BACKEND_UNAVAILABLE', 'Group membership outcome remains unknown after exact observation.', { reason: { code: result.code, details: {} } });
|
|
23
24
|
}
|
|
25
|
+
function membershipState(observation, group) {
|
|
26
|
+
return Object.freeze({
|
|
27
|
+
membership: includes(observation.groups, group),
|
|
28
|
+
authority: includes(observation.authorities, group),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function matches(state, present) {
|
|
32
|
+
return state.membership === present && state.authority === present;
|
|
33
|
+
}
|
|
24
34
|
function includes(groups, group) {
|
|
25
35
|
return groups.some(({ id }) => id === group);
|
|
26
36
|
}
|
|
@@ -28,7 +28,9 @@ export function planUserSetup(domain, input, role, observation) {
|
|
|
28
28
|
throw new TypeError('Shell User displayName is not a string.');
|
|
29
29
|
}
|
|
30
30
|
const member = hasGroup(domain, observation.groups.groups, 'core.member');
|
|
31
|
+
const memberAuthority = hasGroup(domain, observation.groups.authorities, 'core.member');
|
|
31
32
|
const admin = hasGroup(domain, observation.groups.groups, 'core.admin');
|
|
33
|
+
const adminAuthority = hasGroup(domain, observation.groups.authorities, 'core.admin');
|
|
32
34
|
const ownsHome = home !== undefined && observation.ownedSpaces.spaces.some((space) => space.id === home.id);
|
|
33
35
|
let homeName;
|
|
34
36
|
if (home !== undefined) {
|
|
@@ -43,13 +45,20 @@ export function planUserSetup(domain, input, role, observation) {
|
|
|
43
45
|
const addDefault = currentHome === undefined;
|
|
44
46
|
const addOwnership = home === undefined || !ownsHome;
|
|
45
47
|
const addMember = !member;
|
|
48
|
+
const addMemberAuthority = !memberAuthority;
|
|
46
49
|
const addAdmin = role === 'admin' && !admin;
|
|
50
|
+
const preserveAdmin = role === 'admin' || admin;
|
|
51
|
+
const addAdminAuthority = preserveAdmin && !adminAuthority;
|
|
52
|
+
const removeAdminAuthority = !preserveAdmin && adminAuthority;
|
|
47
53
|
if (displayName === undefined &&
|
|
48
54
|
desiredHomeName === undefined &&
|
|
49
55
|
!addDefault &&
|
|
50
56
|
!addOwnership &&
|
|
51
57
|
!addMember &&
|
|
52
|
-
!
|
|
58
|
+
!addMemberAuthority &&
|
|
59
|
+
!addAdmin &&
|
|
60
|
+
!addAdminAuthority &&
|
|
61
|
+
!removeAdminAuthority) {
|
|
53
62
|
return Object.freeze({ kind: 'unchanged', personalSpaceId: home.id });
|
|
54
63
|
}
|
|
55
64
|
return Object.freeze({
|
|
@@ -69,7 +78,10 @@ export function planUserSetup(domain, input, role, observation) {
|
|
|
69
78
|
addDefault,
|
|
70
79
|
addOwnership,
|
|
71
80
|
addMember,
|
|
81
|
+
addMemberAuthority,
|
|
72
82
|
addAdmin,
|
|
83
|
+
addAdminAuthority,
|
|
84
|
+
removeAdminAuthority,
|
|
73
85
|
guards: Object.freeze({
|
|
74
86
|
home: observation.home.guard,
|
|
75
87
|
ownedSpaces: observation.ownedSpaces.guard,
|
|
@@ -31,12 +31,22 @@ export function provisioningRequest(domain, input, role) {
|
|
|
31
31
|
source: createdUser,
|
|
32
32
|
target: Path.project(domain.core.nodes.member.ref),
|
|
33
33
|
});
|
|
34
|
+
change.createEdge({
|
|
35
|
+
class: domain.dependencies.kernel.classes.extends_with.key,
|
|
36
|
+
source: createdUser,
|
|
37
|
+
target: Path.project(domain.core.nodes.member.ref),
|
|
38
|
+
});
|
|
34
39
|
if (role === 'admin') {
|
|
35
40
|
change.createEdge({
|
|
36
41
|
class: domain.classes.member_of_group.key,
|
|
37
42
|
source: createdUser,
|
|
38
43
|
target: Path.project(domain.core.nodes.admin.ref),
|
|
39
44
|
});
|
|
45
|
+
change.createEdge({
|
|
46
|
+
class: domain.dependencies.kernel.classes.extends_with.key,
|
|
47
|
+
source: createdUser,
|
|
48
|
+
target: Path.project(domain.core.nodes.admin.ref),
|
|
49
|
+
});
|
|
40
50
|
}
|
|
41
51
|
});
|
|
42
52
|
return Object.freeze({
|
|
@@ -7,7 +7,8 @@ import { observeUserSetup } from '#queries/user';
|
|
|
7
7
|
import { planUserSetup } from './plan.js';
|
|
8
8
|
import { provisioningRequest, provisioningResult } from './provision.js';
|
|
9
9
|
export async function runSetup(context, role) {
|
|
10
|
-
const {
|
|
10
|
+
const { input, step } = context;
|
|
11
|
+
const client = context.kernel.self;
|
|
11
12
|
const identity = Object.freeze({
|
|
12
13
|
iss: issuer.accept(input.issuer),
|
|
13
14
|
sub: input.subject,
|
|
@@ -30,7 +31,7 @@ export async function runSetup(context, role) {
|
|
|
30
31
|
return convergeExisting(context, role, existing.id, false);
|
|
31
32
|
}
|
|
32
33
|
async function convergeExisting(context, role, userId, changedBeforeObservation) {
|
|
33
|
-
const observed = await context.step.run('observe-user-setup', () => context.query(observeUserSetup, Path.id(userId).raw));
|
|
34
|
+
const observed = await context.step.run('observe-user-setup', () => context.graph.self.query(observeUserSetup, Path.id(userId).raw));
|
|
34
35
|
const plan = planUserSetup(context.domain, context.input, role, observed);
|
|
35
36
|
if (plan.kind === 'conflict')
|
|
36
37
|
return setupConflict(plan);
|
|
@@ -41,7 +42,7 @@ async function convergeExisting(context, role, userId, changedBeforeObservation)
|
|
|
41
42
|
changed: changedBeforeObservation,
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
|
-
const repaired = await context.step.run('repair-user-setup', () => context.mutate(repairUser, plan.repair));
|
|
45
|
+
const repaired = await context.step.run('repair-user-setup', () => context.graph.self.mutate(repairUser, plan.repair));
|
|
45
46
|
if (repaired.ok) {
|
|
46
47
|
return Object.freeze({ userId, personalSpaceId: repaired.personalSpaceId, changed: true });
|
|
47
48
|
}
|
|
@@ -50,7 +51,7 @@ async function convergeExisting(context, role, userId, changedBeforeObservation)
|
|
|
50
51
|
reason: { code: repaired.code, details: {} },
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
|
-
const recovered = await context.step.run('recover-user-repair', () => context.query(observeUserSetup, Path.id(userId).raw));
|
|
54
|
+
const recovered = await context.step.run('recover-user-repair', () => context.graph.self.query(observeUserSetup, Path.id(userId).raw));
|
|
54
55
|
const recoveredPlan = planUserSetup(context.domain, context.input, role, recovered);
|
|
55
56
|
if (recoveredPlan.kind === 'conflict')
|
|
56
57
|
return setupConflict(recoveredPlan);
|
|
@@ -2,11 +2,11 @@ import { InvocationError } from '@astrale-os/sdk/execution';
|
|
|
2
2
|
import { changeSpaceAccess } from '#mutations/space';
|
|
3
3
|
import { observeDirectSpaceAccess } from '#queries/space';
|
|
4
4
|
export async function setUserAccess(context, desired) {
|
|
5
|
-
const observed = await context.step.run('observe-space-access', () => context.query(observeDirectSpaceAccess, context.input.user));
|
|
5
|
+
const observed = await context.step.run('observe-space-access', () => context.graph.self.query(observeDirectSpaceAccess, context.input.user));
|
|
6
6
|
const current = state(observed, context.self);
|
|
7
7
|
if (current === desired)
|
|
8
8
|
return Object.freeze({});
|
|
9
|
-
const result = await context.step.run('change-space-access', () => context.mutate(changeSpaceAccess, {
|
|
9
|
+
const result = await context.step.run('change-space-access', () => context.graph.self.mutate(changeSpaceAccess, {
|
|
10
10
|
user: context.input.user,
|
|
11
11
|
space: context.self,
|
|
12
12
|
addMember: desired === 'member' && current !== 'member',
|
|
@@ -19,7 +19,7 @@ export async function setUserAccess(context, desired) {
|
|
|
19
19
|
return Object.freeze({});
|
|
20
20
|
if (result.outcomeKnown)
|
|
21
21
|
return accessConflict(result.code);
|
|
22
|
-
const recovered = await context.step.run('recover-space-access', () => context.query(observeDirectSpaceAccess, context.input.user));
|
|
22
|
+
const recovered = await context.step.run('recover-space-access', () => context.graph.self.query(observeDirectSpaceAccess, context.input.user));
|
|
23
23
|
if (state(recovered, context.self) === desired)
|
|
24
24
|
return Object.freeze({});
|
|
25
25
|
throw new InvocationError('BACKEND_UNAVAILABLE', 'Space access outcome remains unknown after exact observation.', { reason: { code: result.code, details: {} } });
|
|
@@ -5,7 +5,7 @@ import { changeDefaultSpace } from '#mutations/space';
|
|
|
5
5
|
import { observeHome } from '#queries/user';
|
|
6
6
|
async function setDefault(context) {
|
|
7
7
|
const user = context.caller.principal;
|
|
8
|
-
const observed = await context.step.run('observe-default-space', () => context.query(observeHome, Path.id(user).raw));
|
|
8
|
+
const observed = await context.step.run('observe-default-space', () => context.graph.self.query(observeHome, Path.id(user).raw));
|
|
9
9
|
if (observed.spaces.length > 1) {
|
|
10
10
|
throw new InvocationError('CONFLICT', 'The Shell User has more than one default Space.', {
|
|
11
11
|
reason: { code: 'MULTIPLE_DEFAULT_SPACES', details: {} },
|
|
@@ -13,7 +13,7 @@ async function setDefault(context) {
|
|
|
13
13
|
}
|
|
14
14
|
if (observed.spaces[0]?.id === context.self)
|
|
15
15
|
return Object.freeze({});
|
|
16
|
-
const result = await context.step.run('change-default-space', () => context.mutate(changeDefaultSpace, {
|
|
16
|
+
const result = await context.step.run('change-default-space', () => context.graph.self.mutate(changeDefaultSpace, {
|
|
17
17
|
user,
|
|
18
18
|
...(observed.spaces[0] === undefined ? {} : { previous: observed.spaces[0].id }),
|
|
19
19
|
next: context.self,
|
|
@@ -23,7 +23,7 @@ async function setDefault(context) {
|
|
|
23
23
|
return Object.freeze({});
|
|
24
24
|
if (result.outcomeKnown)
|
|
25
25
|
return defaultConflict(result.code);
|
|
26
|
-
const recovered = await context.step.run('recover-default-space', () => context.query(observeHome, Path.id(user).raw));
|
|
26
|
+
const recovered = await context.step.run('recover-default-space', () => context.graph.self.query(observeHome, Path.id(user).raw));
|
|
27
27
|
if (recovered.spaces.length === 1 && recovered.spaces[0]?.id === context.self) {
|
|
28
28
|
return Object.freeze({});
|
|
29
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-domains/shell",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.19",
|
|
4
4
|
"description": "Astrale Shell Domain schema and remote product implementation",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"zod": "4.4.3"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@astrale-os/sdk": "0.5.0-beta.
|
|
50
|
+
"@astrale-os/sdk": "0.5.0-beta.96",
|
|
51
51
|
"@astrale/typescript-config": "npm:@jsr/astrale__typescript-config@~1.1.3",
|
|
52
52
|
"@types/node": "^22.0.0",
|
|
53
53
|
"typescript": "7.0.2",
|
|
54
54
|
"vitest": "4.1.10"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@astrale-os/sdk": ">=0.5.0-beta.
|
|
57
|
+
"@astrale-os/sdk": ">=0.5.0-beta.96 <1.0.0"
|
|
58
58
|
},
|
|
59
59
|
"devEngines": {
|
|
60
60
|
"runtime": {
|