@jskit-ai/workspaces-core 0.1.14 → 0.1.16

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.
Files changed (83) hide show
  1. package/package.descriptor.mjs +2 -2
  2. package/package.json +18 -3
  3. package/src/server/WorkspacesCoreServiceProvider.js +41 -2
  4. package/src/server/common/contributors/workspaceActionContextContributor.js +88 -0
  5. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +34 -0
  6. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +78 -0
  7. package/src/server/common/formatters/workspaceFormatter.js +53 -0
  8. package/src/server/common/repositories/repositoryUtils.js +59 -0
  9. package/src/server/common/repositories/workspaceInvitesRepository.js +208 -0
  10. package/src/server/common/repositories/workspaceMembershipsRepository.js +190 -0
  11. package/src/server/common/repositories/workspacesRepository.js +202 -0
  12. package/src/server/common/services/workspaceContextService.js +281 -0
  13. package/src/server/common/support/deepFreeze.js +1 -0
  14. package/src/server/common/support/realtimeServiceEvents.js +91 -0
  15. package/src/server/common/support/resolveActionUser.js +9 -0
  16. package/src/server/common/support/workspaceRoutePaths.js +18 -0
  17. package/src/server/common/validators/authenticatedUserValidator.js +43 -0
  18. package/src/server/common/validators/routeParamsValidator.js +62 -0
  19. package/src/server/registerWorkspaceBootstrap.js +27 -0
  20. package/src/server/registerWorkspaceCore.js +100 -0
  21. package/src/server/registerWorkspaceRepositories.js +26 -0
  22. package/src/server/support/resolveWorkspace.js +16 -0
  23. package/src/server/support/workspaceActionSurfaces.js +118 -0
  24. package/src/server/support/workspaceInvitationsPolicy.js +45 -0
  25. package/src/server/support/workspaceRouteInput.js +22 -0
  26. package/src/server/workspaceBootstrapContributor.js +233 -0
  27. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +133 -0
  28. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +19 -0
  29. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +133 -0
  30. package/src/server/workspaceMembers/bootWorkspaceMembers.js +236 -0
  31. package/src/server/workspaceMembers/registerWorkspaceMembers.js +108 -0
  32. package/src/server/workspaceMembers/workspaceMembersActions.js +186 -0
  33. package/src/server/workspaceMembers/workspaceMembersService.js +222 -0
  34. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +62 -0
  35. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +119 -0
  36. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +74 -0
  37. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +138 -0
  38. package/src/server/workspaceSettings/bootWorkspaceSettings.js +76 -0
  39. package/src/server/workspaceSettings/registerWorkspaceSettings.js +62 -0
  40. package/src/server/workspaceSettings/workspaceSettingsActions.js +72 -0
  41. package/src/server/workspaceSettings/workspaceSettingsRepository.js +154 -0
  42. package/src/server/workspaceSettings/workspaceSettingsService.js +66 -0
  43. package/src/shared/operationMessages.js +16 -0
  44. package/src/shared/resources/resolveGlobalArrayRegistry.js +6 -0
  45. package/src/shared/resources/workspaceMembersResource.js +354 -0
  46. package/src/shared/resources/workspacePendingInvitationsResource.js +82 -0
  47. package/src/shared/resources/workspaceResource.js +176 -0
  48. package/src/shared/resources/workspaceSettingsFields.js +59 -0
  49. package/src/shared/resources/workspaceSettingsResource.js +169 -0
  50. package/src/shared/roles.js +161 -0
  51. package/src/shared/settings.js +119 -0
  52. package/src/shared/support/workspacePathModel.js +145 -0
  53. package/src/shared/tenancyMode.js +35 -0
  54. package/src/shared/tenancyProfile.js +73 -0
  55. package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +2 -2
  56. package/test/registerServiceRealtimeEvents.test.js +116 -0
  57. package/test/registerWorkspaceDirectory.test.js +31 -0
  58. package/test/registerWorkspaceSettings.test.js +40 -0
  59. package/test/repositoryContracts.test.js +34 -0
  60. package/test/resourcesCanonical.test.js +74 -0
  61. package/test/roles.test.js +159 -0
  62. package/test/routeParamsValidator.test.js +49 -0
  63. package/test/settingsFieldRegistriesSingleton.test.js +14 -0
  64. package/test/tenancyProfile.test.js +67 -0
  65. package/test/usersRouteResources.test.js +97 -0
  66. package/test/workspaceActionContextContributor.test.js +344 -0
  67. package/test/workspaceActionSurfaces.test.js +85 -0
  68. package/test/workspaceAuthPolicyContextResolver.test.js +119 -0
  69. package/test/workspaceBootstrapContributor.test.js +169 -0
  70. package/test/workspaceInvitationsPolicy.test.js +71 -0
  71. package/test/workspaceInvitesRepository.test.js +111 -0
  72. package/test/workspaceMembersService.test.js +398 -0
  73. package/test/workspacePathModel.test.js +93 -0
  74. package/test/workspacePendingInvitationsResource.test.js +38 -0
  75. package/test/workspacePendingInvitationsService.test.js +151 -0
  76. package/test/workspaceRouteVisibilityResolver.test.js +83 -0
  77. package/test/workspaceService.test.js +546 -0
  78. package/test/workspaceSettingsActions.test.js +52 -0
  79. package/test/workspaceSettingsRepository.test.js +202 -0
  80. package/test/workspaceSettingsResource.test.js +169 -0
  81. package/test/workspaceSettingsService.test.js +140 -0
  82. package/test/workspacesRouteRequestInputValidator.test.js +5 -5
  83. package/test-support/registerDefaultSettingsFields.js +1 -0
@@ -0,0 +1,233 @@
1
+ import { requireServiceMethod } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
2
+ import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
3
+ import { normalizeLowerText } from "@jskit-ai/kernel/shared/actions/textNormalization";
4
+ import {
5
+ TENANCY_MODE_NONE,
6
+ resolveTenancyProfile
7
+ } from "../shared/tenancyProfile.js";
8
+ import { workspacePendingInvitationsResource } from "../shared/resources/workspacePendingInvitationsResource.js";
9
+ import {
10
+ mapMembershipSummary,
11
+ mapWorkspaceSettingsPublic,
12
+ mapWorkspaceSummary
13
+ } from "./common/formatters/workspaceFormatter.js";
14
+
15
+ const REQUESTED_WORKSPACE_STATUS_RESOLVED = "resolved";
16
+ const REQUESTED_WORKSPACE_STATUS_NOT_FOUND = "not_found";
17
+ const REQUESTED_WORKSPACE_STATUS_FORBIDDEN = "forbidden";
18
+ const REQUESTED_WORKSPACE_STATUS_UNAUTHENTICATED = "unauthenticated";
19
+
20
+ function normalizePendingInvites(invites) {
21
+ return workspacePendingInvitationsResource.operations.list.outputValidator.normalize({
22
+ pendingInvites: invites
23
+ }).pendingInvites;
24
+ }
25
+
26
+ function normalizeQueryPayload(value = {}) {
27
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
28
+ return {};
29
+ }
30
+ return value;
31
+ }
32
+
33
+ function resolveBootstrapWorkspaceSlug({ query = {}, request = null } = {}) {
34
+ const normalizedQuery = normalizeQueryPayload(query);
35
+ if (Object.hasOwn(normalizedQuery, "workspaceSlug")) {
36
+ return normalizeLowerText(normalizedQuery.workspaceSlug);
37
+ }
38
+
39
+ const normalizedInputQuery = normalizeQueryPayload(request?.input?.query);
40
+ if (Object.hasOwn(normalizedInputQuery, "workspaceSlug")) {
41
+ return normalizeLowerText(normalizedInputQuery.workspaceSlug);
42
+ }
43
+
44
+ const normalizedRequestQuery = normalizeQueryPayload(request?.query);
45
+ if (Object.hasOwn(normalizedRequestQuery, "workspaceSlug")) {
46
+ return normalizeLowerText(normalizedRequestQuery.workspaceSlug);
47
+ }
48
+
49
+ return "";
50
+ }
51
+
52
+ function normalizeRequestedWorkspaceStatus(value = "") {
53
+ const normalizedValue = normalizeLowerText(value);
54
+ if (
55
+ normalizedValue === REQUESTED_WORKSPACE_STATUS_RESOLVED ||
56
+ normalizedValue === REQUESTED_WORKSPACE_STATUS_NOT_FOUND ||
57
+ normalizedValue === REQUESTED_WORKSPACE_STATUS_FORBIDDEN ||
58
+ normalizedValue === REQUESTED_WORKSPACE_STATUS_UNAUTHENTICATED
59
+ ) {
60
+ return normalizedValue;
61
+ }
62
+ return "";
63
+ }
64
+
65
+ function createRequestedWorkspacePayload(workspaceSlug = "", status = "") {
66
+ const normalizedWorkspaceSlug = normalizeLowerText(workspaceSlug);
67
+ const normalizedStatus = normalizeRequestedWorkspaceStatus(status);
68
+ if (!normalizedWorkspaceSlug || !normalizedStatus) {
69
+ return null;
70
+ }
71
+ return {
72
+ slug: normalizedWorkspaceSlug,
73
+ status: normalizedStatus
74
+ };
75
+ }
76
+
77
+ function resolveRequestedWorkspaceStatusFromError(error) {
78
+ const statusCode = Number(error?.statusCode || error?.status || 0);
79
+ if (statusCode === 404) {
80
+ return REQUESTED_WORKSPACE_STATUS_NOT_FOUND;
81
+ }
82
+ if (statusCode === 403) {
83
+ return REQUESTED_WORKSPACE_STATUS_FORBIDDEN;
84
+ }
85
+ if (statusCode === 401) {
86
+ return REQUESTED_WORKSPACE_STATUS_UNAUTHENTICATED;
87
+ }
88
+ return "";
89
+ }
90
+
91
+ function resolveBootstrapTenancyProfile(tenancyProfile = null, appConfig = {}) {
92
+ const fallback = resolveTenancyProfile(appConfig);
93
+ return Object.freeze({
94
+ mode: fallback.mode,
95
+ workspace: Object.freeze({
96
+ enabled: fallback.workspace.enabled === true,
97
+ autoProvision: fallback.workspace.autoProvision === true,
98
+ allowSelfCreate: fallback.workspace.allowSelfCreate === true,
99
+ slugPolicy: fallback.workspace.slugPolicy
100
+ })
101
+ });
102
+ }
103
+
104
+ function createWorkspaceBootstrapContributor({
105
+ workspaceService,
106
+ workspacePendingInvitationsService,
107
+ usersRepository,
108
+ workspaceInvitationsEnabled = false,
109
+ appConfig = {},
110
+ tenancyProfile = null
111
+ } = {}) {
112
+ const contributorId = "users.workspace.bootstrap";
113
+ const resolvedTenancyProfile = resolveBootstrapTenancyProfile(tenancyProfile, appConfig);
114
+
115
+ requireServiceMethod(workspaceService, "listWorkspacesForUser", contributorId, {
116
+ serviceLabel: "workspaceService"
117
+ });
118
+ requireServiceMethod(workspaceService, "resolveWorkspaceContextForUserBySlug", contributorId, {
119
+ serviceLabel: "workspaceService"
120
+ });
121
+ if (workspaceInvitationsEnabled) {
122
+ requireServiceMethod(workspacePendingInvitationsService, "listPendingInvitesForUser", contributorId, {
123
+ serviceLabel: "workspacePendingInvitationsService"
124
+ });
125
+ }
126
+ requireServiceMethod(usersRepository, "findById", contributorId, {
127
+ serviceLabel: "usersRepository"
128
+ });
129
+
130
+ return Object.freeze({
131
+ contributorId,
132
+ order: 200,
133
+ async contribute({ request = null, query = {}, payload = {} } = {}) {
134
+ const inheritedAppState = payload?.app && typeof payload.app === "object" ? payload.app : {};
135
+ const inheritedFeatures =
136
+ inheritedAppState.features && typeof inheritedAppState.features === "object" ? inheritedAppState.features : {};
137
+ const normalizedUserId = normalizeRecordId(
138
+ payload?.session?.authenticated === true ? payload?.session?.userId : null,
139
+ { fallback: null }
140
+ );
141
+ const normalizedWorkspaceSlug = resolveBootstrapWorkspaceSlug({ query, request });
142
+ if (!normalizedUserId) {
143
+ if (!normalizedWorkspaceSlug || resolvedTenancyProfile.mode === TENANCY_MODE_NONE) {
144
+ return {};
145
+ }
146
+
147
+ return {
148
+ tenancy: resolvedTenancyProfile,
149
+ app: {
150
+ ...inheritedAppState,
151
+ features: {
152
+ ...inheritedFeatures,
153
+ workspaceSwitching: normalizeLowerText(resolvedTenancyProfile.mode) !== TENANCY_MODE_NONE,
154
+ workspaceInvites: workspaceInvitationsEnabled === true
155
+ }
156
+ },
157
+ requestedWorkspace: createRequestedWorkspacePayload(
158
+ normalizedWorkspaceSlug,
159
+ REQUESTED_WORKSPACE_STATUS_UNAUTHENTICATED
160
+ )
161
+ };
162
+ }
163
+
164
+ const latestProfile = await usersRepository.findById(normalizedUserId);
165
+ if (!latestProfile) {
166
+ return {};
167
+ }
168
+
169
+ const pendingInvites =
170
+ workspaceInvitationsEnabled
171
+ ? normalizePendingInvites(
172
+ await workspacePendingInvitationsService.listPendingInvitesForUser(latestProfile, {
173
+ context: {
174
+ actor: latestProfile
175
+ }
176
+ })
177
+ )
178
+ : [];
179
+ const workspaces = await workspaceService.listWorkspacesForUser(latestProfile, { request });
180
+ let workspaceContext = null;
181
+ let requestedWorkspace = null;
182
+ if (normalizedWorkspaceSlug && resolvedTenancyProfile.mode !== TENANCY_MODE_NONE) {
183
+ try {
184
+ workspaceContext = await workspaceService.resolveWorkspaceContextForUserBySlug(
185
+ latestProfile,
186
+ normalizedWorkspaceSlug,
187
+ { request }
188
+ );
189
+ requestedWorkspace = createRequestedWorkspacePayload(
190
+ normalizedWorkspaceSlug,
191
+ REQUESTED_WORKSPACE_STATUS_RESOLVED
192
+ );
193
+ } catch (error) {
194
+ const requestedWorkspaceStatus = resolveRequestedWorkspaceStatusFromError(error);
195
+ if (!requestedWorkspaceStatus) {
196
+ throw error;
197
+ }
198
+ requestedWorkspace = createRequestedWorkspacePayload(normalizedWorkspaceSlug, requestedWorkspaceStatus);
199
+ }
200
+ }
201
+
202
+ return {
203
+ tenancy: resolvedTenancyProfile,
204
+ app: {
205
+ ...inheritedAppState,
206
+ features: {
207
+ ...inheritedFeatures,
208
+ workspaceSwitching: normalizeLowerText(resolvedTenancyProfile.mode) !== TENANCY_MODE_NONE,
209
+ workspaceInvites: workspaceInvitationsEnabled === true
210
+ }
211
+ },
212
+ workspaces: [...workspaces],
213
+ pendingInvites,
214
+ activeWorkspace: workspaceContext
215
+ ? mapWorkspaceSummary(workspaceContext.workspace, {
216
+ roleSid: workspaceContext.membership?.roleSid,
217
+ status: workspaceContext.membership?.status
218
+ })
219
+ : null,
220
+ membership: mapMembershipSummary(workspaceContext?.membership, workspaceContext?.workspace),
221
+ requestedWorkspace,
222
+ permissions: workspaceContext ? [...workspaceContext.permissions] : [],
223
+ workspaceSettings: workspaceContext
224
+ ? mapWorkspaceSettingsPublic(workspaceContext.workspaceSettings, {
225
+ workspaceInvitationsEnabled
226
+ })
227
+ : null
228
+ };
229
+ }
230
+ });
231
+ }
232
+
233
+ export { createWorkspaceBootstrapContributor };
@@ -0,0 +1,133 @@
1
+ import { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
2
+ import { workspaceResource } from "../../shared/resources/workspaceResource.js";
3
+ import { resolveWorkspaceRoutePath } from "../common/support/workspaceRoutePaths.js";
4
+ import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
5
+ import { resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig } from "../support/workspaceActionSurfaces.js";
6
+
7
+ function bootWorkspaceDirectoryRoutes(app) {
8
+ if (!app || typeof app.make !== "function" || typeof app.has !== "function") {
9
+ throw new Error("bootWorkspaceDirectoryRoutes requires application make()/has().");
10
+ }
11
+
12
+ const router = app.make("jskit.http.router");
13
+ const appConfig = app.has("appConfig") ? app.make("appConfig") : {};
14
+ const workspaceRouteSurfaceId = resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig(appConfig);
15
+ const workspaceSelfCreateEnabled = app.has("workspaces.self-create.enabled")
16
+ ? app.make("workspaces.self-create.enabled") === true
17
+ : false;
18
+
19
+ if (workspaceSelfCreateEnabled) {
20
+ router.register(
21
+ "POST",
22
+ "/api/workspaces",
23
+ {
24
+ auth: "required",
25
+ meta: {
26
+ tags: ["workspace"],
27
+ summary: "Create a workspace for the authenticated user"
28
+ },
29
+ bodyValidator: workspaceResource.operations.create.bodyValidator,
30
+ responseValidators: withStandardErrorResponses(
31
+ {
32
+ 200: workspaceResource.operations.create.outputValidator
33
+ },
34
+ { includeValidation400: true }
35
+ )
36
+ },
37
+ async function (request, reply) {
38
+ const body = request.input.body || {};
39
+ const response = await request.executeAction({
40
+ actionId: "workspace.workspaces.create",
41
+ input: {
42
+ name: body.name,
43
+ slug: body.slug
44
+ }
45
+ });
46
+ reply.code(200).send(response);
47
+ }
48
+ );
49
+ }
50
+
51
+ router.register(
52
+ "GET",
53
+ "/api/workspaces",
54
+ {
55
+ auth: "required",
56
+ meta: {
57
+ tags: ["workspace"],
58
+ summary: "List workspaces visible to authenticated user"
59
+ },
60
+ responseValidators: withStandardErrorResponses({
61
+ 200: workspaceResource.operations.list.outputValidator
62
+ })
63
+ },
64
+ async function (request, reply) {
65
+ const response = await request.executeAction({
66
+ actionId: "workspace.workspaces.list",
67
+ input: {}
68
+ });
69
+ reply.code(200).send(response);
70
+ }
71
+ );
72
+
73
+ router.register(
74
+ "GET",
75
+ resolveWorkspaceRoutePath("/"),
76
+ {
77
+ auth: "required",
78
+ surface: workspaceRouteSurfaceId,
79
+ visibility: "workspace",
80
+ meta: {
81
+ tags: ["workspace"],
82
+ summary: "Get workspace profile by workspace slug"
83
+ },
84
+ paramsValidator: workspaceSlugParamsValidator,
85
+ responseValidators: withStandardErrorResponses({
86
+ 200: workspaceResource.operations.view.outputValidator
87
+ })
88
+ },
89
+ async function (request, reply) {
90
+ const response = await request.executeAction({
91
+ actionId: "workspace.workspaces.read",
92
+ input: {
93
+ workspaceSlug: request.input.params.workspaceSlug
94
+ }
95
+ });
96
+ reply.code(200).send(response);
97
+ }
98
+ );
99
+
100
+ router.register(
101
+ "PATCH",
102
+ resolveWorkspaceRoutePath("/"),
103
+ {
104
+ auth: "required",
105
+ surface: workspaceRouteSurfaceId,
106
+ visibility: "workspace",
107
+ meta: {
108
+ tags: ["workspace"],
109
+ summary: "Update workspace profile by workspace slug"
110
+ },
111
+ paramsValidator: workspaceSlugParamsValidator,
112
+ bodyValidator: workspaceResource.operations.patch.bodyValidator,
113
+ responseValidators: withStandardErrorResponses(
114
+ {
115
+ 200: workspaceResource.operations.patch.outputValidator
116
+ },
117
+ { includeValidation400: true }
118
+ )
119
+ },
120
+ async function (request, reply) {
121
+ const response = await request.executeAction({
122
+ actionId: "workspace.workspaces.update",
123
+ input: {
124
+ workspaceSlug: request.input.params.workspaceSlug,
125
+ patch: request.input.body
126
+ }
127
+ });
128
+ reply.code(200).send(response);
129
+ }
130
+ );
131
+ }
132
+
133
+ export { bootWorkspaceDirectoryRoutes };
@@ -0,0 +1,19 @@
1
+ import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
2
+ import { workspaceDirectoryActions } from "./workspaceDirectoryActions.js";
3
+
4
+ function registerWorkspaceDirectory(app) {
5
+ if (!app || typeof app.singleton !== "function" || typeof app.actions !== "function") {
6
+ throw new Error("registerWorkspaceDirectory requires application singleton()/actions().");
7
+ }
8
+
9
+ app.actions(
10
+ withActionDefaults(workspaceDirectoryActions, {
11
+ domain: "workspace",
12
+ dependencies: {
13
+ workspaceService: "workspaces.service"
14
+ }
15
+ })
16
+ );
17
+ }
18
+
19
+ export { registerWorkspaceDirectory };
@@ -0,0 +1,133 @@
1
+ import {
2
+ EMPTY_INPUT_VALIDATOR,
3
+ resolveRequest
4
+ } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
5
+ import { workspaceResource } from "../../shared/resources/workspaceResource.js";
6
+ import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
7
+ import { resolveActionUser } from "../common/support/resolveActionUser.js";
8
+
9
+ const workspaceDirectoryActions = Object.freeze([
10
+ {
11
+ id: "workspace.workspaces.create",
12
+ version: 1,
13
+ kind: "command",
14
+ channels: ["api", "assistant_tool", "automation", "internal"],
15
+ surfacesFrom: "enabled",
16
+ permission: {
17
+ require: "authenticated"
18
+ },
19
+ inputValidator: workspaceResource.operations.create.bodyValidator,
20
+ outputValidator: workspaceResource.operations.create.outputValidator,
21
+ idempotency: "none",
22
+ audit: {
23
+ actionName: "workspace.workspaces.create"
24
+ },
25
+ observability: {},
26
+ extensions: {
27
+ assistant: {
28
+ description: "Create a workspace for the authenticated user."
29
+ }
30
+ },
31
+ async execute(input, context, deps) {
32
+ return deps.workspaceService.createWorkspaceForAuthenticatedUser(resolveActionUser(context, input), input, {
33
+ request: resolveRequest(context),
34
+ context
35
+ });
36
+ }
37
+ },
38
+ {
39
+ id: "workspace.workspaces.list",
40
+ version: 1,
41
+ kind: "query",
42
+ channels: ["api", "automation", "internal"],
43
+ surfacesFrom: "enabled",
44
+ permission: {
45
+ require: "authenticated"
46
+ },
47
+ inputValidator: EMPTY_INPUT_VALIDATOR,
48
+ outputValidator: workspaceResource.operations.list.outputValidator,
49
+ idempotency: "none",
50
+ audit: {
51
+ actionName: "workspace.workspaces.list"
52
+ },
53
+ observability: {},
54
+ async execute(input, context, deps) {
55
+ return {
56
+ items: await deps.workspaceService.listWorkspacesForAuthenticatedUser(resolveActionUser(context, input), {
57
+ request: resolveRequest(context),
58
+ context
59
+ }),
60
+ nextCursor: null
61
+ };
62
+ }
63
+ },
64
+ {
65
+ id: "workspace.workspaces.read",
66
+ version: 1,
67
+ kind: "query",
68
+ channels: ["api", "automation", "internal"],
69
+ surfacesFrom: "workspace",
70
+ permission: {
71
+ require: "any",
72
+ permissions: ["workspace.settings.view", "workspace.settings.update"]
73
+ },
74
+ inputValidator: workspaceSlugParamsValidator,
75
+ outputValidator: workspaceResource.operations.view.outputValidator,
76
+ idempotency: "none",
77
+ audit: {
78
+ actionName: "workspace.workspaces.read"
79
+ },
80
+ observability: {},
81
+ async execute(input, context, deps) {
82
+ return deps.workspaceService.getWorkspaceForAuthenticatedUser(
83
+ resolveActionUser(context, input),
84
+ input.workspaceSlug,
85
+ {
86
+ request: resolveRequest(context),
87
+ context
88
+ }
89
+ );
90
+ }
91
+ },
92
+ {
93
+ id: "workspace.workspaces.update",
94
+ version: 1,
95
+ kind: "command",
96
+ channels: ["api", "assistant_tool", "automation", "internal"],
97
+ surfacesFrom: "workspace",
98
+ permission: {
99
+ require: "all",
100
+ permissions: ["workspace.settings.update"]
101
+ },
102
+ inputValidator: [
103
+ workspaceSlugParamsValidator,
104
+ {
105
+ patch: workspaceResource.operations.patch.bodyValidator
106
+ }
107
+ ],
108
+ outputValidator: workspaceResource.operations.patch.outputValidator,
109
+ idempotency: "optional",
110
+ audit: {
111
+ actionName: "workspace.workspaces.update"
112
+ },
113
+ observability: {},
114
+ extensions: {
115
+ assistant: {
116
+ description: "Update workspace profile fields."
117
+ }
118
+ },
119
+ async execute(input, context, deps) {
120
+ return deps.workspaceService.updateWorkspaceForAuthenticatedUser(
121
+ resolveActionUser(context, input),
122
+ input.workspaceSlug,
123
+ input.patch,
124
+ {
125
+ request: resolveRequest(context),
126
+ context
127
+ }
128
+ );
129
+ }
130
+ }
131
+ ]);
132
+
133
+ export { workspaceDirectoryActions };