@agent-native/core 0.162.0 → 0.163.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/clips/app/lib/camera-composite.ts +15 -1
- package/corpus/templates/clips/changelog/2026-08-18-clips-no-longer-crashes-when-a-camera-bubble-starts-at-a-tin.md +6 -0
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/content/actions/create-content-database.ts +1 -1
- package/corpus/templates/content/actions/create-document.ts +1 -1
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +6 -1
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +27 -2
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +3 -3
- package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +3 -3
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +3 -1
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +1 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +1 -1
- package/corpus/templates/slides/server/handlers/request-auth-context.ts +1 -0
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +27 -2
- package/dist/app-config/schema.d.ts +1 -0
- package/dist/app-config/workspace.d.ts +1 -0
- package/dist/app-config/workspace.js +8 -0
- package/dist/chat-threads/schema.d.ts +2 -2
- package/dist/client/org/TeamPage.js +15 -2
- package/dist/client/org/hooks.d.ts +2 -0
- package/dist/client/org/hooks.js +12 -0
- package/dist/client/org/index.d.ts +2 -2
- package/dist/client/org/index.js +1 -1
- package/dist/client/sharing/ShareButton.js +43 -18
- package/dist/client/sharing/useShareButtonController.d.ts +12 -2
- package/dist/client/sharing/useShareButtonController.js +38 -8
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/dashboard-storage/schema.d.ts +2 -2
- package/dist/data-programs/schema.d.ts +2 -2
- package/dist/extensions/schema.d.ts +2 -2
- package/dist/extensions/store.js +3 -0
- package/dist/localization/core-messages/ar-SA.js +1 -0
- package/dist/localization/core-messages/de-DE.js +1 -0
- package/dist/localization/core-messages/en-US.d.ts +1 -0
- package/dist/localization/core-messages/en-US.js +1 -0
- package/dist/localization/core-messages/es-ES.js +1 -0
- package/dist/localization/core-messages/fr-FR.js +1 -0
- package/dist/localization/core-messages/hi-IN.js +1 -0
- package/dist/localization/core-messages/ja-JP.js +1 -0
- package/dist/localization/core-messages/ko-KR.js +1 -0
- package/dist/localization/core-messages/pt-BR.js +1 -0
- package/dist/localization/core-messages/zh-CN.js +1 -0
- package/dist/localization/core-messages/zh-TW.js +1 -0
- package/dist/localization/core-messages.d.ts +1 -0
- package/dist/localization/default-messages.d.ts +4 -0
- package/dist/localization/default-messages.js +4 -0
- package/dist/org/handlers.d.ts +6 -1
- package/dist/org/handlers.js +43 -0
- package/dist/org/index.d.ts +3 -3
- package/dist/org/index.js +2 -2
- package/dist/org/migrations.js +33 -0
- package/dist/org/plugin.js +10 -1
- package/dist/org/schema.d.ts +315 -0
- package/dist/org/schema.js +20 -1
- package/dist/org/types.d.ts +3 -0
- package/dist/org/workspace-app-access.d.ts +10 -0
- package/dist/org/workspace-app-access.js +179 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +14 -1
- package/dist/server/poll.js +6 -0
- package/dist/sharing/access.d.ts +2 -2
- package/dist/sharing/access.js +56 -3
- package/dist/sharing/actions/list-resource-shares.d.ts +2 -0
- package/dist/sharing/actions/list-resource-shares.js +26 -1
- package/dist/sharing/actions/share-resource.d.ts +1 -1
- package/dist/sharing/actions/share-resource.js +23 -4
- package/dist/sharing/actions/unshare-resource.d.ts +1 -1
- package/dist/sharing/actions/unshare-resource.js +1 -1
- package/dist/sharing/registry.d.ts +12 -0
- package/dist/sharing/schema.d.ts +4 -4
- package/dist/sharing/schema.js +2 -2
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
- package/dist/templates/default/.agents/skills/sharing/SKILL.md +27 -2
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +27 -2
- package/dist/workspace-connections/actions/list-workspace-user-groups.d.ts +9 -1
- package/dist/workspace-connections/actions/list-workspace-user-groups.js +9 -4
- package/dist/workspace-connections/groups.d.ts +2 -0
- package/dist/workspace-connections/groups.js +16 -5
- package/package.json +1 -1
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +27 -2
- package/src/templates/default/.agents/skills/sharing/SKILL.md +27 -2
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +27 -2
|
@@ -3,7 +3,7 @@ export declare function resolveShareNotificationUrl(explicitUrl: string | undefi
|
|
|
3
3
|
declare const _default: import("../../action.js").ActionDefinition<{
|
|
4
4
|
resourceType: string;
|
|
5
5
|
resourceId: string;
|
|
6
|
-
principalType: "org" | "user";
|
|
6
|
+
principalType: "group" | "org" | "user";
|
|
7
7
|
principalId: string;
|
|
8
8
|
role?: "admin" | "commenter" | "editor" | "viewer";
|
|
9
9
|
notify?: boolean;
|
|
@@ -8,6 +8,7 @@ import { sendEmail, isEmailConfigured } from "../../server/email.js";
|
|
|
8
8
|
import { invalidateCollabAccessCache } from "../../server/poll.js";
|
|
9
9
|
import { getRequestUserEmail } from "../../server/request-context.js";
|
|
10
10
|
import { getUserProfile } from "../../user-profile/store.js";
|
|
11
|
+
import { assertWorkspaceUserGroupIds } from "../../workspace-connections/groups.js";
|
|
11
12
|
import { assertAccess, ForbiddenError } from "../access.js";
|
|
12
13
|
import { requireShareableResource } from "../registry.js";
|
|
13
14
|
import { getExtensionShareChangeTargets, notifyExtensionShareChanged, } from "./extension-change.js";
|
|
@@ -122,7 +123,7 @@ async function isOrgMemberOrInvited(orgId, email) {
|
|
|
122
123
|
return invited.rows.length > 0;
|
|
123
124
|
}
|
|
124
125
|
export default defineAction({
|
|
125
|
-
description: "Grant a user or org access to a shareable resource. Owner or admin role required.",
|
|
126
|
+
description: "Grant a user, group, or org access to a shareable resource. Owner or admin role required.",
|
|
126
127
|
// (audit H5) Sharing-grant operations are admin-tier and let a caller
|
|
127
128
|
// expand who can read/write a resource. Refuse from the tools iframe
|
|
128
129
|
// bridge so a malicious shared tool can't silently re-share its
|
|
@@ -134,11 +135,11 @@ export default defineAction({
|
|
|
134
135
|
.describe("Registered resource type, e.g. 'document', 'form'."),
|
|
135
136
|
resourceId: z.string().describe("Id of the resource to share."),
|
|
136
137
|
principalType: z
|
|
137
|
-
.enum(["user", "org"])
|
|
138
|
-
.describe("'user' for an individual, 'org' for
|
|
138
|
+
.enum(["user", "group", "org"])
|
|
139
|
+
.describe("'user' for an individual, 'group' for an organization group, or 'org' for the whole organization."),
|
|
139
140
|
principalId: z
|
|
140
141
|
.string()
|
|
141
|
-
.describe("Email (user) or org id (org) of the principal."),
|
|
142
|
+
.describe("Email (user), group id (group), or org id (org) of the principal."),
|
|
142
143
|
role: z
|
|
143
144
|
.enum(["viewer", "commenter", "editor", "admin"])
|
|
144
145
|
.default("viewer")
|
|
@@ -165,9 +166,24 @@ export default defineAction({
|
|
|
165
166
|
if (!actor)
|
|
166
167
|
throw new ForbiddenError("Not signed in");
|
|
167
168
|
const principalId = normalizePrincipalId(args.principalType, args.principalId);
|
|
169
|
+
if (args.principalType === "group" && reg.supportsGroupShares !== true) {
|
|
170
|
+
throw new ForbiddenError(`${reg.displayName} does not support organization groups yet.`);
|
|
171
|
+
}
|
|
168
172
|
if (args.principalType === "user" && !isEmailPrincipalId(principalId)) {
|
|
169
173
|
throw new Error("User shares must use an email address, not an internal user id.");
|
|
170
174
|
}
|
|
175
|
+
if (args.principalType === "group") {
|
|
176
|
+
const resourceOrgId = access.resource?.orgId;
|
|
177
|
+
if (!resourceOrgId) {
|
|
178
|
+
throw new ForbiddenError(`${reg.displayName} can only be shared with a group from within an organization.`);
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
await assertWorkspaceUserGroupIds([principalId], resourceOrgId);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
throw new ForbiddenError(`${reg.displayName} can only be shared with a group from its own organization.`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
171
187
|
const beforeExtensionTargets = await getExtensionShareChangeTargets(args.resourceType, args.resourceId);
|
|
172
188
|
if (reg.requireOrgMemberForUserShares) {
|
|
173
189
|
const resourceOrgId = access.resource?.orgId;
|
|
@@ -189,6 +205,9 @@ export default defineAction({
|
|
|
189
205
|
throw new ForbiddenError(`${reg.displayName} can only be shared with its own organization, not a different one.`);
|
|
190
206
|
}
|
|
191
207
|
}
|
|
208
|
+
else if (args.principalType === "group") {
|
|
209
|
+
// Group ownership was validated above against the resource org.
|
|
210
|
+
}
|
|
192
211
|
}
|
|
193
212
|
const db = reg.getDb();
|
|
194
213
|
const [existing] = await db
|
|
@@ -22,7 +22,7 @@ export default defineAction({
|
|
|
22
22
|
schema: z.object({
|
|
23
23
|
resourceType: z.string(),
|
|
24
24
|
resourceId: z.string(),
|
|
25
|
-
principalType: z.enum(["user", "org"]),
|
|
25
|
+
principalType: z.enum(["user", "group", "org"]),
|
|
26
26
|
principalId: z.string(),
|
|
27
27
|
}),
|
|
28
28
|
run: async (args) => {
|
|
@@ -168,6 +168,18 @@ export interface ShareableResourceRegistration {
|
|
|
168
168
|
* Default: `false` (matches the historical behavior — any email can be granted).
|
|
169
169
|
*/
|
|
170
170
|
requireOrgMemberForUserShares?: boolean;
|
|
171
|
+
/** Set true when this resource's DB is mounted with the org group tables. */
|
|
172
|
+
supportsGroupShares?: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* Optional resource-specific manager check. Returning true grants the
|
|
175
|
+
* caller admin capability even when the row is private or they have no
|
|
176
|
+
* explicit share. Workspace apps use this for organization owners/admins;
|
|
177
|
+
* the owner is still recognized by the normal owner_email check.
|
|
178
|
+
*/
|
|
179
|
+
canManageAccess?: (resource: any, ctx: {
|
|
180
|
+
userEmail?: string;
|
|
181
|
+
orgId?: string;
|
|
182
|
+
}) => boolean | Promise<boolean>;
|
|
171
183
|
/**
|
|
172
184
|
* Optional per-resource access-context adapter. Most resources should use the
|
|
173
185
|
* request user/org unchanged. Templates with an intentional alternate local
|
package/dist/sharing/schema.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare function ownableColumns(): {
|
|
|
42
42
|
* Columns:
|
|
43
43
|
* - `id` — share row id (generated by the share action)
|
|
44
44
|
* - `resourceId` — id of the parent resource
|
|
45
|
-
* - `principalType` — `'user' | 'org'`
|
|
45
|
+
* - `principalType` — `'user' | 'group' | 'org'`
|
|
46
46
|
* - `principalId` — email (for user) or org id (for org)
|
|
47
47
|
* - `role` — `'viewer' | 'commenter' | 'editor' | 'admin'`
|
|
48
48
|
* - `createdBy` — email of the user who created the share
|
|
@@ -100,14 +100,14 @@ export declare function createSharesTable(tableName: string): import("drizzle-or
|
|
|
100
100
|
tableName: string;
|
|
101
101
|
dataType: "string";
|
|
102
102
|
columnType: "SQLiteText";
|
|
103
|
-
data: "org" | "user";
|
|
103
|
+
data: "group" | "org" | "user";
|
|
104
104
|
driverParam: string;
|
|
105
105
|
notNull: true;
|
|
106
106
|
hasDefault: false;
|
|
107
107
|
isPrimaryKey: false;
|
|
108
108
|
isAutoincrement: false;
|
|
109
109
|
hasRuntimeDefault: false;
|
|
110
|
-
enumValues: ["user", "org"];
|
|
110
|
+
enumValues: ["user", "group", "org"];
|
|
111
111
|
baseColumn: never;
|
|
112
112
|
identity: undefined;
|
|
113
113
|
generated: undefined;
|
|
@@ -195,7 +195,7 @@ export declare function createSharesTable(tableName: string): import("drizzle-or
|
|
|
195
195
|
}>;
|
|
196
196
|
export type Visibility = "private" | "org" | "public";
|
|
197
197
|
export type ShareRole = "viewer" | "commenter" | "editor" | "admin";
|
|
198
|
-
export type PrincipalType = "user" | "org";
|
|
198
|
+
export type PrincipalType = "user" | "group" | "org";
|
|
199
199
|
/** Role precedence — higher number = stronger capability. */
|
|
200
200
|
export declare const ROLE_RANK: Record<ShareRole | "owner", number>;
|
|
201
201
|
export declare function roleSatisfies(actual: ShareRole | "owner", minimum: ShareRole | "owner"): boolean;
|
package/dist/sharing/schema.js
CHANGED
|
@@ -49,7 +49,7 @@ export function ownableColumns() {
|
|
|
49
49
|
* Columns:
|
|
50
50
|
* - `id` — share row id (generated by the share action)
|
|
51
51
|
* - `resourceId` — id of the parent resource
|
|
52
|
-
* - `principalType` — `'user' | 'org'`
|
|
52
|
+
* - `principalType` — `'user' | 'group' | 'org'`
|
|
53
53
|
* - `principalId` — email (for user) or org id (for org)
|
|
54
54
|
* - `role` — `'viewer' | 'commenter' | 'editor' | 'admin'`
|
|
55
55
|
* - `createdBy` — email of the user who created the share
|
|
@@ -65,7 +65,7 @@ export function createSharesTable(tableName) {
|
|
|
65
65
|
id: text("id").primaryKey(),
|
|
66
66
|
resourceId: text("resource_id").notNull(),
|
|
67
67
|
principalType: text("principal_type", {
|
|
68
|
-
enum: ["user", "org"],
|
|
68
|
+
enum: ["user", "group", "org"],
|
|
69
69
|
}).notNull(),
|
|
70
70
|
principalId: text("principal_id").notNull(),
|
|
71
71
|
role: text("role", {
|
|
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
|
|
|
18
18
|
|
|
19
19
|
This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
|
|
20
20
|
|
|
21
|
+
Workspace apps are the intentional organization-scoped exception: their default
|
|
22
|
+
visibility is `org`, configurable by an organization owner/admin. The creator is
|
|
23
|
+
stored as the app owner, and the creator or an organization owner/admin can
|
|
24
|
+
change the app's visibility or manage its share grants.
|
|
25
|
+
|
|
21
26
|
## Concepts
|
|
22
27
|
|
|
23
28
|
### Three visibility levels
|
|
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
|
|
|
26
31
|
- **`org`** — owner + explicit grants + anyone in the same org (read-only).
|
|
27
32
|
- **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
|
|
28
33
|
|
|
29
|
-
Visibility is coarse. Explicit share grants are fine-grained (per user
|
|
34
|
+
Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
|
|
35
|
+
organization group, or per org).
|
|
30
36
|
|
|
31
37
|
### Roles on a share grant
|
|
32
38
|
|
|
@@ -101,6 +107,7 @@ registerShareableResource({
|
|
|
101
107
|
// ...
|
|
102
108
|
allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
|
|
103
109
|
requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
|
|
110
|
+
supportsGroupShares: true, // enable reusable organization groups for this resource
|
|
104
111
|
});
|
|
105
112
|
```
|
|
106
113
|
|
|
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
|
|
|
111
118
|
|
|
112
119
|
Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
|
|
113
120
|
|
|
121
|
+
### Reusable organization groups
|
|
122
|
+
|
|
123
|
+
The workspace organization model stores reusable groups in
|
|
124
|
+
`workspace_user_groups`. Organization owners/admins manage the groups and their
|
|
125
|
+
members through the existing group actions. Set `supportsGroupShares: true` on a
|
|
126
|
+
resource registration to enable `principalType: "group"` in the generic share
|
|
127
|
+
actions and the shared popover's autocomplete. Group ids are always checked
|
|
128
|
+
against the resource's organization, and membership is evaluated at access time
|
|
129
|
+
so group changes apply to existing shares.
|
|
130
|
+
|
|
131
|
+
Workspace apps opt into this flag and use the organization default visibility
|
|
132
|
+
setting. The app creator is the durable `owner_email`, while organization
|
|
133
|
+
owners/admins receive admin access through the registration's
|
|
134
|
+
`canManageAccess` callback.
|
|
135
|
+
|
|
114
136
|
## Filter list/read queries
|
|
115
137
|
|
|
116
138
|
```ts
|
|
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
|
|
|
197
219
|
- Add `ShareAgentsSection` only when the resource has a real agent-readable
|
|
198
220
|
link or prompt. Keep it collapsed by default and supply domain-specific
|
|
199
221
|
content through the shared section shell.
|
|
222
|
+
- When `supportsGroupShares` is enabled, keep the existing people input as the
|
|
223
|
+
only entry point and include organization groups in its autocomplete. Do not
|
|
224
|
+
add a separate group-management surface to each resource.
|
|
200
225
|
- App-specific tabs or controls may remain when they represent a real domain
|
|
201
226
|
action (for example, an embed-code editor), but they should retain the same
|
|
202
227
|
trigger, copy-row, access, and spacing language.
|
|
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
211
236
|
|
|
212
237
|
| Action | Args | Purpose |
|
|
213
238
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
214
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
239
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
215
240
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
216
241
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
217
242
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|
|
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
|
|
|
18
18
|
|
|
19
19
|
This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
|
|
20
20
|
|
|
21
|
+
Workspace apps are the intentional organization-scoped exception: their default
|
|
22
|
+
visibility is `org`, configurable by an organization owner/admin. The creator is
|
|
23
|
+
stored as the app owner, and the creator or an organization owner/admin can
|
|
24
|
+
change the app's visibility or manage its share grants.
|
|
25
|
+
|
|
21
26
|
## Concepts
|
|
22
27
|
|
|
23
28
|
### Three visibility levels
|
|
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
|
|
|
26
31
|
- **`org`** — owner + explicit grants + anyone in the same org (read-only).
|
|
27
32
|
- **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
|
|
28
33
|
|
|
29
|
-
Visibility is coarse. Explicit share grants are fine-grained (per user
|
|
34
|
+
Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
|
|
35
|
+
organization group, or per org).
|
|
30
36
|
|
|
31
37
|
### Roles on a share grant
|
|
32
38
|
|
|
@@ -101,6 +107,7 @@ registerShareableResource({
|
|
|
101
107
|
// ...
|
|
102
108
|
allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
|
|
103
109
|
requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
|
|
110
|
+
supportsGroupShares: true, // enable reusable organization groups for this resource
|
|
104
111
|
});
|
|
105
112
|
```
|
|
106
113
|
|
|
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
|
|
|
111
118
|
|
|
112
119
|
Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
|
|
113
120
|
|
|
121
|
+
### Reusable organization groups
|
|
122
|
+
|
|
123
|
+
The workspace organization model stores reusable groups in
|
|
124
|
+
`workspace_user_groups`. Organization owners/admins manage the groups and their
|
|
125
|
+
members through the existing group actions. Set `supportsGroupShares: true` on a
|
|
126
|
+
resource registration to enable `principalType: "group"` in the generic share
|
|
127
|
+
actions and the shared popover's autocomplete. Group ids are always checked
|
|
128
|
+
against the resource's organization, and membership is evaluated at access time
|
|
129
|
+
so group changes apply to existing shares.
|
|
130
|
+
|
|
131
|
+
Workspace apps opt into this flag and use the organization default visibility
|
|
132
|
+
setting. The app creator is the durable `owner_email`, while organization
|
|
133
|
+
owners/admins receive admin access through the registration's
|
|
134
|
+
`canManageAccess` callback.
|
|
135
|
+
|
|
114
136
|
## Filter list/read queries
|
|
115
137
|
|
|
116
138
|
```ts
|
|
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
|
|
|
197
219
|
- Add `ShareAgentsSection` only when the resource has a real agent-readable
|
|
198
220
|
link or prompt. Keep it collapsed by default and supply domain-specific
|
|
199
221
|
content through the shared section shell.
|
|
222
|
+
- When `supportsGroupShares` is enabled, keep the existing people input as the
|
|
223
|
+
only entry point and include organization groups in its autocomplete. Do not
|
|
224
|
+
add a separate group-management surface to each resource.
|
|
200
225
|
- App-specific tabs or controls may remain when they represent a real domain
|
|
201
226
|
action (for example, an embed-code editor), but they should retain the same
|
|
202
227
|
trigger, copy-row, access, and spacing language.
|
|
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
211
236
|
|
|
212
237
|
| Action | Args | Purpose |
|
|
213
238
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
214
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
239
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
215
240
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
216
241
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
217
242
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|
|
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
|
|
|
18
18
|
|
|
19
19
|
This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
|
|
20
20
|
|
|
21
|
+
Workspace apps are the intentional organization-scoped exception: their default
|
|
22
|
+
visibility is `org`, configurable by an organization owner/admin. The creator is
|
|
23
|
+
stored as the app owner, and the creator or an organization owner/admin can
|
|
24
|
+
change the app's visibility or manage its share grants.
|
|
25
|
+
|
|
21
26
|
## Concepts
|
|
22
27
|
|
|
23
28
|
### Three visibility levels
|
|
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
|
|
|
26
31
|
- **`org`** — owner + explicit grants + anyone in the same org (read-only).
|
|
27
32
|
- **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
|
|
28
33
|
|
|
29
|
-
Visibility is coarse. Explicit share grants are fine-grained (per user
|
|
34
|
+
Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
|
|
35
|
+
organization group, or per org).
|
|
30
36
|
|
|
31
37
|
### Roles on a share grant
|
|
32
38
|
|
|
@@ -101,6 +107,7 @@ registerShareableResource({
|
|
|
101
107
|
// ...
|
|
102
108
|
allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
|
|
103
109
|
requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
|
|
110
|
+
supportsGroupShares: true, // enable reusable organization groups for this resource
|
|
104
111
|
});
|
|
105
112
|
```
|
|
106
113
|
|
|
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
|
|
|
111
118
|
|
|
112
119
|
Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
|
|
113
120
|
|
|
121
|
+
### Reusable organization groups
|
|
122
|
+
|
|
123
|
+
The workspace organization model stores reusable groups in
|
|
124
|
+
`workspace_user_groups`. Organization owners/admins manage the groups and their
|
|
125
|
+
members through the existing group actions. Set `supportsGroupShares: true` on a
|
|
126
|
+
resource registration to enable `principalType: "group"` in the generic share
|
|
127
|
+
actions and the shared popover's autocomplete. Group ids are always checked
|
|
128
|
+
against the resource's organization, and membership is evaluated at access time
|
|
129
|
+
so group changes apply to existing shares.
|
|
130
|
+
|
|
131
|
+
Workspace apps opt into this flag and use the organization default visibility
|
|
132
|
+
setting. The app creator is the durable `owner_email`, while organization
|
|
133
|
+
owners/admins receive admin access through the registration's
|
|
134
|
+
`canManageAccess` callback.
|
|
135
|
+
|
|
114
136
|
## Filter list/read queries
|
|
115
137
|
|
|
116
138
|
```ts
|
|
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
|
|
|
197
219
|
- Add `ShareAgentsSection` only when the resource has a real agent-readable
|
|
198
220
|
link or prompt. Keep it collapsed by default and supply domain-specific
|
|
199
221
|
content through the shared section shell.
|
|
222
|
+
- When `supportsGroupShares` is enabled, keep the existing people input as the
|
|
223
|
+
only entry point and include organization groups in its autocomplete. Do not
|
|
224
|
+
add a separate group-management surface to each resource.
|
|
200
225
|
- App-specific tabs or controls may remain when they represent a real domain
|
|
201
226
|
action (for example, an embed-code editor), but they should retain the same
|
|
202
227
|
trigger, copy-row, access, and spacing language.
|
|
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
211
236
|
|
|
212
237
|
| Action | Args | Purpose |
|
|
213
238
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
214
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
239
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
215
240
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
216
241
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
217
242
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
declare const _default: import("../../action.js").ActionDefinition<Record<string, never>,
|
|
1
|
+
declare const _default: import("../../action.js").ActionDefinition<Record<string, never>, {
|
|
2
|
+
id: string;
|
|
3
|
+
orgId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
createdByEmail: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
updatedAt: string;
|
|
8
|
+
memberEmails: any[];
|
|
9
|
+
}[]>;
|
|
2
10
|
export default _default;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { defineAction } from "../../action.js";
|
|
3
|
-
import {
|
|
3
|
+
import { listWorkspaceUserGroupsForOrg, workspaceUserGroupRole, } from "../groups.js";
|
|
4
4
|
export default defineAction({
|
|
5
|
-
description: "List reusable workspace user groups.
|
|
5
|
+
description: "List reusable workspace user groups. Workspace members can see group names for sharing; only owners and admins receive member details and can manage groups.",
|
|
6
6
|
schema: z.object({}),
|
|
7
7
|
http: { method: "GET" },
|
|
8
8
|
readOnly: true,
|
|
9
9
|
run: async (_args, ctx) => {
|
|
10
|
-
await
|
|
11
|
-
|
|
10
|
+
const role = await workspaceUserGroupRole(ctx?.orgId, ctx?.userEmail);
|
|
11
|
+
if (!role)
|
|
12
|
+
throw new Error("Workspace membership is required.");
|
|
13
|
+
const groups = await listWorkspaceUserGroupsForOrg(ctx?.orgId ?? "");
|
|
14
|
+
if (role === "owner" || role === "admin")
|
|
15
|
+
return groups;
|
|
16
|
+
return groups.map((group) => ({ ...group, memberEmails: [] }));
|
|
12
17
|
},
|
|
13
18
|
});
|
|
@@ -20,12 +20,14 @@ export interface UpdateWorkspaceUserGroupMembersInput {
|
|
|
20
20
|
operation: "add" | "remove";
|
|
21
21
|
orgId?: string | null;
|
|
22
22
|
}
|
|
23
|
+
export declare function workspaceUserGroupsTable(): string;
|
|
23
24
|
export declare function normalizeWorkspaceUserGroupIds(value: unknown): string[];
|
|
24
25
|
export declare function ensureWorkspaceUserGroupsTable(): Promise<void>;
|
|
25
26
|
export declare function listWorkspaceUserGroupsForOrg(orgId: string, groupIds?: string[]): Promise<WorkspaceUserGroup[]>;
|
|
26
27
|
export declare function listWorkspaceUserGroups(): Promise<WorkspaceUserGroup[]>;
|
|
27
28
|
export declare function assertWorkspaceUserGroupIds(groupIds: string[] | undefined, orgId: string | null | undefined): Promise<string[] | undefined>;
|
|
28
29
|
export declare function assertWorkspaceUserGroupManager(orgId: string | null | undefined, userEmail: string | undefined): Promise<void>;
|
|
30
|
+
export declare function workspaceUserGroupRole(orgId: string | null | undefined, userEmail: string | undefined): Promise<"owner" | "admin" | "member" | null>;
|
|
29
31
|
export declare function upsertWorkspaceUserGroup(input: UpsertWorkspaceUserGroupInput): Promise<WorkspaceUserGroup>;
|
|
30
32
|
export declare function updateWorkspaceUserGroupMembers(input: UpdateWorkspaceUserGroupMembersInput): Promise<WorkspaceUserGroup>;
|
|
31
33
|
export declare function deleteWorkspaceUserGroup(id: string, orgId?: string | null): Promise<boolean>;
|
|
@@ -3,7 +3,7 @@ import { getDbExec, intType, isPostgres, retryOnDdlRace, safeJsonParse, } from "
|
|
|
3
3
|
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
4
4
|
import { isOrgMember } from "../org/membership.js";
|
|
5
5
|
import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
|
|
6
|
-
function workspaceUserGroupsTable() {
|
|
6
|
+
export function workspaceUserGroupsTable() {
|
|
7
7
|
return isPostgres()
|
|
8
8
|
? "public.workspace_user_groups"
|
|
9
9
|
: "workspace_user_groups";
|
|
@@ -171,19 +171,25 @@ export async function assertWorkspaceUserGroupIds(groupIds, orgId) {
|
|
|
171
171
|
return normalized;
|
|
172
172
|
}
|
|
173
173
|
export async function assertWorkspaceUserGroupManager(orgId, userEmail) {
|
|
174
|
+
const role = await workspaceUserGroupRole(orgId, userEmail);
|
|
175
|
+
if (role === "owner" || role === "admin")
|
|
176
|
+
return;
|
|
177
|
+
throw new Error("Only workspace admins can manage user groups.");
|
|
178
|
+
}
|
|
179
|
+
export async function workspaceUserGroupRole(orgId, userEmail) {
|
|
174
180
|
const normalizedOrgId = orgId?.trim();
|
|
175
181
|
const normalizedEmail = userEmail?.trim().toLowerCase();
|
|
176
182
|
if (!normalizedOrgId || !normalizedEmail) {
|
|
177
|
-
|
|
183
|
+
return null;
|
|
178
184
|
}
|
|
179
185
|
const { rows } = await getDbExec().execute({
|
|
180
186
|
sql: `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,
|
|
181
187
|
args: [normalizedOrgId, normalizedEmail],
|
|
182
188
|
});
|
|
183
189
|
const role = String(rows[0]?.role ?? "");
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
190
|
+
return role === "owner" || role === "admin" || role === "member"
|
|
191
|
+
? role
|
|
192
|
+
: null;
|
|
187
193
|
}
|
|
188
194
|
export async function upsertWorkspaceUserGroup(input) {
|
|
189
195
|
const requestScope = requireWorkspaceUserGroupScope();
|
|
@@ -283,6 +289,11 @@ export async function workspaceUserGroupsIncludeUser(orgId, groupIds, userEmail)
|
|
|
283
289
|
if (!normalizedOrgId || !normalizedEmail || normalizedIds.length === 0) {
|
|
284
290
|
return false;
|
|
285
291
|
}
|
|
292
|
+
// Group rows can outlive roster membership. A stale email in the JSON
|
|
293
|
+
// member list must not keep granting access after that person leaves the
|
|
294
|
+
// organization.
|
|
295
|
+
if (!(await isOrgMember(normalizedOrgId, normalizedEmail)))
|
|
296
|
+
return false;
|
|
286
297
|
const groups = await listWorkspaceUserGroupsForOrg(normalizedOrgId, normalizedIds);
|
|
287
298
|
return groups.some((group) => group.memberEmails.includes(normalizedEmail));
|
|
288
299
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.163.0",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
|
|
|
18
18
|
|
|
19
19
|
This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
|
|
20
20
|
|
|
21
|
+
Workspace apps are the intentional organization-scoped exception: their default
|
|
22
|
+
visibility is `org`, configurable by an organization owner/admin. The creator is
|
|
23
|
+
stored as the app owner, and the creator or an organization owner/admin can
|
|
24
|
+
change the app's visibility or manage its share grants.
|
|
25
|
+
|
|
21
26
|
## Concepts
|
|
22
27
|
|
|
23
28
|
### Three visibility levels
|
|
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
|
|
|
26
31
|
- **`org`** — owner + explicit grants + anyone in the same org (read-only).
|
|
27
32
|
- **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
|
|
28
33
|
|
|
29
|
-
Visibility is coarse. Explicit share grants are fine-grained (per user
|
|
34
|
+
Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
|
|
35
|
+
organization group, or per org).
|
|
30
36
|
|
|
31
37
|
### Roles on a share grant
|
|
32
38
|
|
|
@@ -101,6 +107,7 @@ registerShareableResource({
|
|
|
101
107
|
// ...
|
|
102
108
|
allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
|
|
103
109
|
requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
|
|
110
|
+
supportsGroupShares: true, // enable reusable organization groups for this resource
|
|
104
111
|
});
|
|
105
112
|
```
|
|
106
113
|
|
|
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
|
|
|
111
118
|
|
|
112
119
|
Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
|
|
113
120
|
|
|
121
|
+
### Reusable organization groups
|
|
122
|
+
|
|
123
|
+
The workspace organization model stores reusable groups in
|
|
124
|
+
`workspace_user_groups`. Organization owners/admins manage the groups and their
|
|
125
|
+
members through the existing group actions. Set `supportsGroupShares: true` on a
|
|
126
|
+
resource registration to enable `principalType: "group"` in the generic share
|
|
127
|
+
actions and the shared popover's autocomplete. Group ids are always checked
|
|
128
|
+
against the resource's organization, and membership is evaluated at access time
|
|
129
|
+
so group changes apply to existing shares.
|
|
130
|
+
|
|
131
|
+
Workspace apps opt into this flag and use the organization default visibility
|
|
132
|
+
setting. The app creator is the durable `owner_email`, while organization
|
|
133
|
+
owners/admins receive admin access through the registration's
|
|
134
|
+
`canManageAccess` callback.
|
|
135
|
+
|
|
114
136
|
## Filter list/read queries
|
|
115
137
|
|
|
116
138
|
```ts
|
|
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
|
|
|
197
219
|
- Add `ShareAgentsSection` only when the resource has a real agent-readable
|
|
198
220
|
link or prompt. Keep it collapsed by default and supply domain-specific
|
|
199
221
|
content through the shared section shell.
|
|
222
|
+
- When `supportsGroupShares` is enabled, keep the existing people input as the
|
|
223
|
+
only entry point and include organization groups in its autocomplete. Do not
|
|
224
|
+
add a separate group-management surface to each resource.
|
|
200
225
|
- App-specific tabs or controls may remain when they represent a real domain
|
|
201
226
|
action (for example, an embed-code editor), but they should retain the same
|
|
202
227
|
trigger, copy-row, access, and spacing language.
|
|
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
211
236
|
|
|
212
237
|
| Action | Args | Purpose |
|
|
213
238
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
214
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
239
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
215
240
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
216
241
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
217
242
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|
|
@@ -18,6 +18,11 @@ Any resource a user **creates** (dashboards, documents, forms, decks, compositio
|
|
|
18
18
|
|
|
19
19
|
This is the framework-level primitive. Every ownable resource gets it for free — same API, same UI, same skill.
|
|
20
20
|
|
|
21
|
+
Workspace apps are the intentional organization-scoped exception: their default
|
|
22
|
+
visibility is `org`, configurable by an organization owner/admin. The creator is
|
|
23
|
+
stored as the app owner, and the creator or an organization owner/admin can
|
|
24
|
+
change the app's visibility or manage its share grants.
|
|
25
|
+
|
|
21
26
|
## Concepts
|
|
22
27
|
|
|
23
28
|
### Three visibility levels
|
|
@@ -26,7 +31,8 @@ This is the framework-level primitive. Every ownable resource gets it for free
|
|
|
26
31
|
- **`org`** — owner + explicit grants + anyone in the same org (read-only).
|
|
27
32
|
- **`public`** — owner + explicit grants + **anyone with the link** (read-only). Public docs do NOT appear in other users' list/sidebar/search results — `accessFilter` omits them by default. They're reachable by id (`resolveAccess` admits them) so direct links and SSR routes like `/p/:id` keep working. If a list endpoint legitimately needs cross-user public discovery (a template gallery, etc.), pass `accessFilter(table, shares, ctx, minRole, { includePublic: true })`.
|
|
28
33
|
|
|
29
|
-
Visibility is coarse. Explicit share grants are fine-grained (per user
|
|
34
|
+
Visibility is coarse. Explicit share grants are fine-grained (per user, reusable
|
|
35
|
+
organization group, or per org).
|
|
30
36
|
|
|
31
37
|
### Roles on a share grant
|
|
32
38
|
|
|
@@ -101,6 +107,7 @@ registerShareableResource({
|
|
|
101
107
|
// ...
|
|
102
108
|
allowPublic: false, // hides "Public" in the share dialog and rejects it server-side
|
|
103
109
|
requireOrgMemberForUserShares: true, // user shares must target an org member or pending invitee
|
|
110
|
+
supportsGroupShares: true, // enable reusable organization groups for this resource
|
|
104
111
|
});
|
|
105
112
|
```
|
|
106
113
|
|
|
@@ -111,6 +118,21 @@ Use both for resources that execute code or expose privileged data with the *vie
|
|
|
111
118
|
|
|
112
119
|
Defaults match historical behaviour: `allowPublic: true`, `requireOrgMemberForUserShares: false`. Resources that don't set the flags work as before.
|
|
113
120
|
|
|
121
|
+
### Reusable organization groups
|
|
122
|
+
|
|
123
|
+
The workspace organization model stores reusable groups in
|
|
124
|
+
`workspace_user_groups`. Organization owners/admins manage the groups and their
|
|
125
|
+
members through the existing group actions. Set `supportsGroupShares: true` on a
|
|
126
|
+
resource registration to enable `principalType: "group"` in the generic share
|
|
127
|
+
actions and the shared popover's autocomplete. Group ids are always checked
|
|
128
|
+
against the resource's organization, and membership is evaluated at access time
|
|
129
|
+
so group changes apply to existing shares.
|
|
130
|
+
|
|
131
|
+
Workspace apps opt into this flag and use the organization default visibility
|
|
132
|
+
setting. The app creator is the durable `owner_email`, while organization
|
|
133
|
+
owners/admins receive admin access through the registration's
|
|
134
|
+
`canManageAccess` callback.
|
|
135
|
+
|
|
114
136
|
## Filter list/read queries
|
|
115
137
|
|
|
116
138
|
```ts
|
|
@@ -197,6 +219,9 @@ All app share popovers should use the same compact surface contract:
|
|
|
197
219
|
- Add `ShareAgentsSection` only when the resource has a real agent-readable
|
|
198
220
|
link or prompt. Keep it collapsed by default and supply domain-specific
|
|
199
221
|
content through the shared section shell.
|
|
222
|
+
- When `supportsGroupShares` is enabled, keep the existing people input as the
|
|
223
|
+
only entry point and include organization groups in its autocomplete. Do not
|
|
224
|
+
add a separate group-management surface to each resource.
|
|
200
225
|
- App-specific tabs or controls may remain when they represent a real domain
|
|
201
226
|
action (for example, an embed-code editor), but they should retain the same
|
|
202
227
|
trigger, copy-row, access, and spacing language.
|
|
@@ -211,7 +236,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
211
236
|
|
|
212
237
|
| Action | Args | Purpose |
|
|
213
238
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
214
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
239
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user, group, or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
215
240
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
216
241
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
217
242
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|