@docyrus/docyrus 0.0.30 → 0.0.32

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 (30) hide show
  1. package/agent-loader.js +36 -25
  2. package/agent-loader.js.map +4 -4
  3. package/main.js +4 -4
  4. package/main.js.map +1 -1
  5. package/package.json +3 -3
  6. package/resources/pi-agent/extensions/docyrus-web-browser.ts +31 -0
  7. package/resources/pi-agent/shared/docyrusWebBrowserProtocol.ts +169 -0
  8. package/resources/pi-agent/skills/diffity-diff/SKILL.md +1 -1
  9. package/resources/pi-agent/skills/diffity-resolve/SKILL.md +4 -4
  10. package/resources/pi-agent/skills/diffity-review/SKILL.md +5 -4
  11. package/resources/pi-agent/skills/docyrus-api-dev/SKILL.md +197 -0
  12. package/resources/pi-agent/skills/docyrus-api-dev/references/acl-endpoints-frontend.md +295 -0
  13. package/resources/pi-agent/skills/docyrus-api-dev/references/api-client.md +349 -0
  14. package/resources/pi-agent/skills/docyrus-api-dev/references/authentication.md +298 -0
  15. package/resources/pi-agent/skills/docyrus-api-dev/references/data-source-query-guide.md +2063 -0
  16. package/resources/pi-agent/skills/docyrus-api-dev/references/formula-design-guide-llm.md +312 -0
  17. package/resources/pi-agent/skills/docyrus-api-dev/references/query-and-formulas.md +592 -0
  18. package/resources/pi-agent/skills/docyrus-app-dev-react/SKILL.md +361 -0
  19. package/resources/pi-agent/skills/docyrus-app-dev-react/references/README.md +29 -0
  20. package/resources/pi-agent/skills/docyrus-app-dev-react/references/api-client-and-auth.md +326 -0
  21. package/resources/pi-agent/skills/docyrus-app-dev-react/references/collections-and-patterns.md +353 -0
  22. package/resources/pi-agent/skills/docyrus-app-dev-react/references/component-selection-guide.md +619 -0
  23. package/resources/pi-agent/skills/docyrus-app-dev-react/references/icon-usage-guide.md +463 -0
  24. package/resources/pi-agent/skills/docyrus-app-dev-react/references/preferred-components-catalog.md +242 -0
  25. package/resources/pi-agent/skills/docyrus-platform/SKILL.md +2 -2
  26. package/resources/pi-agent/skills/docyrus-platform/references/auth-and-multi-tenancy.md +9 -1
  27. package/resources/pi-agent/skills/docyrus-platform/references/developer-tools.md +3 -2
  28. package/server-loader.js +328 -87
  29. package/server-loader.js.map +4 -4
  30. package/resources/pi-agent/extensions/multi-edit.ts +0 -835
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docyrus/docyrus",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "private": false,
5
5
  "description": "Docyrus API CLI",
6
6
  "main": "./main.js",
@@ -10,8 +10,8 @@
10
10
  "dependencies": {
11
11
  "@clack/prompts": "^0.11.0",
12
12
  "@hono/node-server": "^1.14.1",
13
- "@mariozechner/pi-ai": "0.63.1",
14
- "@mariozechner/pi-coding-agent": "0.63.1",
13
+ "@mariozechner/pi-ai": "0.63.2",
14
+ "@mariozechner/pi-coding-agent": "0.63.2",
15
15
  "@modelcontextprotocol/ext-apps": "^1.2.2",
16
16
  "@modelcontextprotocol/sdk": "^1.25.1",
17
17
  "@mozilla/readability": "^0.6.0",
@@ -0,0 +1,31 @@
1
+ import { isToolCallEventType, type ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { buildDocyrusWebBrowserProtocolInstructions } from "../shared/docyrusWebBrowserProtocol";
3
+
4
+ const DOCYRUS_CHROME_COMMAND_PATTERN = /\bdocyrus\b(?:\s+-g|\s+--global)?\s+chrome\b/;
5
+ const DOCYRUS_CHROME_BLOCK_REASON =
6
+ "Docyrus Server sessions must use the docyrus-web-browser preview tools instead of `docyrus chrome`.";
7
+
8
+ export default function docyrusWebBrowserExtension(pi: ExtensionAPI) {
9
+ pi.on("before_agent_start", async(event) => {
10
+ const protocolInstructions = buildDocyrusWebBrowserProtocolInstructions();
11
+ return {
12
+ systemPrompt: [event.systemPrompt, protocolInstructions].filter(Boolean).join("\n\n"),
13
+ };
14
+ });
15
+
16
+ pi.on("tool_call", async(event) => {
17
+ if (!isToolCallEventType("bash", event)) {
18
+ return;
19
+ }
20
+
21
+ const command = typeof event.input.command === "string" ? event.input.command.trim() : "";
22
+ if (!command || !DOCYRUS_CHROME_COMMAND_PATTERN.test(command)) {
23
+ return;
24
+ }
25
+
26
+ return {
27
+ block: true,
28
+ reason: DOCYRUS_CHROME_BLOCK_REASON,
29
+ };
30
+ });
31
+ }
@@ -0,0 +1,169 @@
1
+ export const DOCYRUS_WEB_BROWSER_EXTENSION_NAME = "docyrus-web-browser";
2
+ export const DOCYRUS_WEB_BROWSER_TAG = "docyrus_web_browser";
3
+ export const DOCYRUS_WEB_BROWSER_OPEN = `<${DOCYRUS_WEB_BROWSER_TAG}>`;
4
+ export const DOCYRUS_WEB_BROWSER_CLOSE = `</${DOCYRUS_WEB_BROWSER_TAG}>`;
5
+ export const DOCYRUS_WEB_BROWSER_RESULT_TAG = "docyrus_web_browser_result";
6
+ export const DOCYRUS_WEB_BROWSER_RESULT_OPEN = `<${DOCYRUS_WEB_BROWSER_RESULT_TAG}>`;
7
+ export const DOCYRUS_WEB_BROWSER_RESULT_CLOSE = `</${DOCYRUS_WEB_BROWSER_RESULT_TAG}>`;
8
+ export const WEB_PREVIEW_CONTEXT_TOOL = "web_preview_context";
9
+ export const WEB_PREVIEW_PLAYWRIGHT_TOOL = "web_preview_playwright";
10
+
11
+ export const DOCYRUS_WEB_BROWSER_TOOL_NAMES = [
12
+ WEB_PREVIEW_CONTEXT_TOOL,
13
+ WEB_PREVIEW_PLAYWRIGHT_TOOL,
14
+ ] as const;
15
+
16
+ export type IDocyrusWebBrowserToolName = typeof DOCYRUS_WEB_BROWSER_TOOL_NAMES[number];
17
+
18
+ export interface IDocyrusWebBrowserToolRequest {
19
+ tool: IDocyrusWebBrowserToolName;
20
+ input?: Record<string, unknown>;
21
+ }
22
+
23
+ export interface IDocyrusWebBrowserToolResponsePrompt {
24
+ tool: IDocyrusWebBrowserToolName;
25
+ request?: Record<string, unknown>;
26
+ status: "success" | "error";
27
+ output?: unknown;
28
+ errorText?: string;
29
+ }
30
+
31
+ export interface IDocyrusWebBrowserClientToolInfo {
32
+ name: IDocyrusWebBrowserToolName;
33
+ description: string;
34
+ inputSchema?: Record<string, unknown>;
35
+ }
36
+
37
+ export const DOCYRUS_WEB_BROWSER_CLIENT_TOOLS: IDocyrusWebBrowserClientToolInfo[] = [
38
+ {
39
+ name: WEB_PREVIEW_CONTEXT_TOOL,
40
+ description:
41
+ "Inspect the current Docyrus web preview state before automation. Prefer this first when preview availability or bridge state is unknown.",
42
+ inputSchema: {
43
+ type: "object",
44
+ properties: {
45
+ includeSnapshot: { type: "boolean" },
46
+ },
47
+ additionalProperties: false,
48
+ },
49
+ },
50
+ {
51
+ name: WEB_PREVIEW_PLAYWRIGHT_TOOL,
52
+ description:
53
+ "Run Playwright-style actions against the Docyrus web preview. Prefer structured steps over raw scripts.",
54
+ inputSchema: {
55
+ type: "object",
56
+ properties: {
57
+ script: { type: "string" },
58
+ steps: {
59
+ type: "array",
60
+ items: {
61
+ type: "object",
62
+ properties: {
63
+ action: { type: "string" },
64
+ selector: { type: "string" },
65
+ url: { type: "string" },
66
+ value: { type: "string" },
67
+ values: {
68
+ type: "array",
69
+ items: { type: "string" },
70
+ },
71
+ key: { type: "string" },
72
+ attribute: { type: "string" },
73
+ timeoutMs: { type: "number" },
74
+ state: { type: "string" },
75
+ },
76
+ additionalProperties: true,
77
+ },
78
+ },
79
+ timeoutMs: { type: "number" },
80
+ stopOnError: { type: "boolean" },
81
+ },
82
+ additionalProperties: true,
83
+ },
84
+ },
85
+ ];
86
+
87
+ function hashString(value: string): string {
88
+ let hash = 0;
89
+ for (let index = 0; index < value.length; index += 1) {
90
+ hash = ((hash << 5) - hash) + value.charCodeAt(index);
91
+ hash |= 0;
92
+ }
93
+ return Math.abs(hash).toString(36);
94
+ }
95
+
96
+ function isRecord(value: unknown): value is Record<string, unknown> {
97
+ return typeof value === "object" && value !== null && !Array.isArray(value);
98
+ }
99
+
100
+ export function isDocyrusWebBrowserToolName(value: unknown): value is IDocyrusWebBrowserToolName {
101
+ return DOCYRUS_WEB_BROWSER_TOOL_NAMES.some((toolName) => toolName === value);
102
+ }
103
+
104
+ export function normalizeDocyrusWebBrowserToolRequest(value: unknown): IDocyrusWebBrowserToolRequest | undefined {
105
+ if (!isRecord(value) || !isDocyrusWebBrowserToolName(value.tool)) {
106
+ return undefined;
107
+ }
108
+
109
+ return {
110
+ tool: value.tool,
111
+ input: isRecord(value.input) ? value.input : undefined,
112
+ };
113
+ }
114
+
115
+ export function serializeDocyrusWebBrowserToolRequest(request: IDocyrusWebBrowserToolRequest): string {
116
+ return `${DOCYRUS_WEB_BROWSER_OPEN}\n${JSON.stringify(request, null, 2)}\n${DOCYRUS_WEB_BROWSER_CLOSE}`;
117
+ }
118
+
119
+ export function createDocyrusWebBrowserToolCallId(request: IDocyrusWebBrowserToolRequest): string {
120
+ return `docyrus_web_browser_${hashString(JSON.stringify(request))}`;
121
+ }
122
+
123
+ export function parseDocyrusWebBrowserRequestFromText(text: string): IDocyrusWebBrowserToolRequest | undefined {
124
+ const trimmed = text.trim();
125
+ if (!trimmed.startsWith(DOCYRUS_WEB_BROWSER_OPEN) || !trimmed.endsWith(DOCYRUS_WEB_BROWSER_CLOSE)) {
126
+ return undefined;
127
+ }
128
+
129
+ const body = trimmed.slice(DOCYRUS_WEB_BROWSER_OPEN.length, trimmed.length - DOCYRUS_WEB_BROWSER_CLOSE.length).trim();
130
+ if (!body) {
131
+ return undefined;
132
+ }
133
+
134
+ try {
135
+ return normalizeDocyrusWebBrowserToolRequest(JSON.parse(body));
136
+ } catch {
137
+ return undefined;
138
+ }
139
+ }
140
+
141
+ export function buildDocyrusWebBrowserProtocolInstructions(): string {
142
+ return [
143
+ "When you need to inspect or automate the Docyrus web preview in server-backed sessions, use the docyrus-web-browser client tools.",
144
+ "Do not use `docyrus chrome` or any visible Chrome DevTools workflow in this environment.",
145
+ `Instead, output only a single ${DOCYRUS_WEB_BROWSER_OPEN}...${DOCYRUS_WEB_BROWSER_CLOSE} block and nothing else in the assistant message.`,
146
+ "Inside that block, emit strict JSON with this shape:",
147
+ "{\"tool\":\"web_preview_context\",\"input\":{\"includeSnapshot\":true}}",
148
+ "or",
149
+ "{\"tool\":\"web_preview_playwright\",\"input\":{\"steps\":[{\"action\":\"goto\",\"url\":\"/login\"},{\"action\":\"fill\",\"selector\":\"input[name='email']\",\"value\":\"demo@example.com\"}],\"timeoutMs\":10000,\"stopOnError\":true}}",
150
+ "Call `web_preview_context` first when preview state is unknown.",
151
+ "Prefer `steps` over `script` for `web_preview_playwright`.",
152
+ "Supported playwright step actions are: goto, click, dblclick, hover, fill, press, select, check, uncheck, waitForSelector, waitForTimeout, textContent, getAttribute, title, url, snapshot.",
153
+ "If the tool reports that the preview is unavailable or blocked by bridge/cross-origin constraints, stop and explain the exact blocker to the user.",
154
+ ].join("\n");
155
+ }
156
+
157
+ export function formatDocyrusWebBrowserToolResponsePrompt(
158
+ response: IDocyrusWebBrowserToolResponsePrompt,
159
+ ): string {
160
+ return [
161
+ "The docyrus-web-browser client tool returned a result.",
162
+ "",
163
+ `${DOCYRUS_WEB_BROWSER_RESULT_OPEN}`,
164
+ JSON.stringify(response, null, 2),
165
+ `${DOCYRUS_WEB_BROWSER_RESULT_CLOSE}`,
166
+ "",
167
+ "Continue the task using this result. If the tool reported an availability or bridge blocker, explain that blocker exactly and do not claim success.",
168
+ ].join("\n");
169
+ }
@@ -14,7 +14,7 @@ You are opening the diffity diff viewer so the user can see their changes in the
14
14
 
15
15
  ## Instructions
16
16
 
17
- 1. Check that `diffity` is available: run `which diffity`. If not found, tell the user to restart `docyrus coder` so the launcher can install the managed `diffity` CLI.
17
+ 1. Check that `diffity` is available: run `which diffity`. If not found, install it with `npm install -g diffity`.
18
18
  2. Run `diffity <ref>` (or just `diffity` if no ref) using the Bash tool with `run_in_background: true`:
19
19
  - The CLI handles everything: if an instance is already running for this repo it reuses it and opens the browser, otherwise it starts a new server and opens the browser.
20
20
  - Do NOT use `&` or `--quiet` — let the Bash tool handle backgrounding.
@@ -15,6 +15,7 @@ You are reading open review comments and resolving them by making the requested
15
15
  ## CLI Reference
16
16
 
17
17
  ```
18
+ diffity agent diff
18
19
  diffity agent list [--status open|resolved|dismissed] [--json]
19
20
  diffity agent comment --file <path> --line <n> [--end-line <n>] [--side new|old] --body "<text>"
20
21
  diffity agent general-comment --body "<text>"
@@ -31,7 +32,7 @@ diffity agent reply <id> --body "<text>"
31
32
 
32
33
  ## Prerequisites
33
34
 
34
- 1. Check that `diffity` is available: run `which diffity`. If not found, tell the user to restart `docyrus coder` so the launcher can install the managed `diffity` CLI.
35
+ 1. Check that `diffity` is available: run `which diffity`. If not found, install it with `npm install -g diffity`.
35
36
  2. Check that a review session exists: run `diffity agent list`. If this fails with "No active review session", tell the user to start diffity first (e.g. `diffity` or **/diffity-diff**).
36
37
 
37
38
  ## Instructions
@@ -46,11 +47,10 @@ diffity agent reply <id> --body "<text>"
46
47
  a. **Skip** general comments (filePath `__general__`) — these are summaries, not actionable code changes.
47
48
  b. **Skip** threads where the last comment is an agent reply that asks the user a question (e.g. "Could you clarify...?") and the user hasn't responded yet — the agent is waiting for user input. Still process threads where the agent left the original comment (code suggestion, review feedback, etc.) — those are actionable.
48
49
  c. **`[nit]` comments** — these are minor suggestions but still actionable. Resolve them like any other comment.
49
- d. **`[question]` comments** (from the user) — read the question, examine the relevant code, and reply with an answer:
50
+ d. **`[question]` comments** (from the user) — read the question, examine the relevant code, and resolve the thread with your answer as the summary:
50
51
  ```
51
- diffity agent reply <thread-id> --body "Your answer here"
52
+ diffity agent resolve <thread-id> --summary "Your answer here"
52
53
  ```
53
- Then resolve the thread with a summary of your answer.
54
54
  e. Comments phrased as questions without an explicit `[question]` tag (e.g. "should we add X?" or "can we rename this?") are suggestions — treat them as actionable requests and make the change.
55
55
  f. Read the comment body from the JSON output and understand what change is requested. Interpret the intent:
56
56
  - If the comment suggests a code change, make the change.
@@ -16,6 +16,7 @@ You are reviewing a diff and leaving inline comments using the `diffity agent` C
16
16
  ## CLI Reference
17
17
 
18
18
  ```
19
+ diffity agent diff
19
20
  diffity agent list [--status open|resolved|dismissed] [--json]
20
21
  diffity agent comment --file <path> --line <n> [--end-line <n>] [--side new|old] --body "<text>"
21
22
  diffity agent general-comment --body "<text>"
@@ -32,7 +33,7 @@ diffity agent reply <id> --body "<text>"
32
33
 
33
34
  ## Prerequisites
34
35
 
35
- 1. Check that `diffity` is available: run `which diffity`. If not found, tell the user to restart `docyrus coder` so the launcher can install the managed `diffity` CLI.
36
+ 1. Check that `diffity` is available: run `which diffity`. If not found, install it with `npm install -g diffity`.
36
37
 
37
38
  ## Instructions
38
39
 
@@ -53,11 +54,11 @@ The review needs a running session whose ref matches the requested ref. A ref mi
53
54
 
54
55
  ### Step 2: Review the diff
55
56
 
56
- 1. **Get the resolved diff args from diffity's API**, then run `git diff` yourself do NOT construct the diff ref manually, as diffity uses merge-base resolution:
57
+ 1. **Get the unified diff** directly from diffity — this handles merge-base resolution, untracked files, and all ref types automatically:
57
58
  ```
58
- curl -s 'http://localhost:<port>/api/diff/ref?ref=<ref>'
59
+ diffity agent diff
59
60
  ```
60
- If no ref was provided, omit the `ref` query parameter. The response is JSON with an `args` field (e.g. `"abc123def"`). Run `git diff <args>` to get the unified diff. Line numbers are in the `@@` hunk headers.
61
+ This outputs the full unified diff for the current session. Line numbers are in the `@@` hunk headers.
61
62
  2. Find and read all relevant CLAUDE.md files — the root CLAUDE.md and any CLAUDE.md files in directories containing modified files. These define project-specific rules that the diff must follow.
62
63
 
63
64
  #### Understand the change before reviewing it
@@ -0,0 +1,197 @@
1
+ ---
2
+ name: docyrus-api-dev
3
+ description: Develop applications using the Docyrus API with @docyrus/api-client and @docyrus/signin libraries. Use when building apps that authenticate with Docyrus OAuth2 (PKCE, iframe, client credentials, device code), make REST API calls to Docyrus data source endpoints, or construct query payloads with filters, aggregations, formulas, pivots, and child queries. Triggers on tasks involving Docyrus API integration, @docyrus/api-client usage, @docyrus/signin authentication, data source query building, or Docyrus REST endpoint consumption.
4
+ ---
5
+
6
+ # Docyrus API Developer
7
+
8
+ Integrate with the Docyrus API using `@docyrus/api-client` (REST client) and `@docyrus/signin` (React auth provider). Authenticate via OAuth2 PKCE, query data sources with powerful filtering/aggregation, and consume REST endpoints.
9
+
10
+ ## Authentication Quick Start
11
+
12
+ ### React Apps — Use @docyrus/signin
13
+
14
+ ```tsx
15
+ import { DocyrusAuthProvider, useDocyrusAuth, useDocyrusClient, SignInButton } from '@docyrus/signin'
16
+
17
+ // 1. Wrap root
18
+ <DocyrusAuthProvider
19
+ apiUrl={import.meta.env.VITE_API_BASE_URL}
20
+ clientId={import.meta.env.VITE_OAUTH2_CLIENT_ID}
21
+ redirectUri={import.meta.env.VITE_OAUTH2_REDIRECT_URI}
22
+ scopes={['offline_access', 'Read.All', 'DS.ReadWrite.All', 'Users.Read']}
23
+ callbackPath="/auth/callback"
24
+ >
25
+ <App />
26
+ </DocyrusAuthProvider>
27
+
28
+ // 2. Use hooks
29
+ function App() {
30
+ const { status, signOut } = useDocyrusAuth()
31
+ const client = useDocyrusClient() // RestApiClient | null
32
+
33
+ if (status === 'loading') return <Spinner />
34
+ if (status === 'unauthenticated') return <SignInButton />
35
+
36
+ // client is ready — make API calls
37
+ const user = await client!.get('/v1/users/me')
38
+ }
39
+ ```
40
+
41
+ ### Non-React / Server — Use OAuth2Client Directly
42
+
43
+ ```typescript
44
+ import { RestApiClient, OAuth2Client, OAuth2TokenManagerAdapter, BrowserOAuth2TokenStorage } from '@docyrus/api-client'
45
+
46
+ const tokenStorage = new BrowserOAuth2TokenStorage(localStorage)
47
+ const oauth2 = new OAuth2Client({
48
+ baseURL: 'https://api.docyrus.com',
49
+ clientId: 'your-client-id',
50
+ redirectUri: 'http://localhost:3000/callback',
51
+ usePKCE: true,
52
+ tokenStorage,
53
+ })
54
+
55
+ // Auth Code flow
56
+ const { url } = await oauth2.getAuthorizationUrl({ scope: 'openid offline_access Users.Read' })
57
+ window.location.href = url
58
+ // After redirect:
59
+ const tokens = await oauth2.handleCallback(window.location.href)
60
+
61
+ // Create API client with auto-refresh
62
+ const client = new RestApiClient({
63
+ baseURL: 'https://api.docyrus.com',
64
+ tokenManager: new OAuth2TokenManagerAdapter(tokenStorage, async () => {
65
+ return (await oauth2.refreshAccessToken()).accessToken
66
+ }),
67
+ })
68
+ ```
69
+
70
+ ## API Endpoints
71
+
72
+ ### Data Source Items (Dynamic per tenant)
73
+ ```
74
+ GET /v1/apps/{appSlug}/data-sources/{slug}/items — List with query payload
75
+ GET /v1/apps/{appSlug}/data-sources/{slug}/items/{id} — Get one
76
+ POST /v1/apps/{appSlug}/data-sources/{slug}/items — Create
77
+ PATCH /v1/apps/{appSlug}/data-sources/{slug}/items/{id} — Update
78
+ DELETE /v1/apps/{appSlug}/data-sources/{slug}/items/{id} — Delete one
79
+ DELETE /v1/apps/{appSlug}/data-sources/{slug}/items — Delete many (body: { recordIds })
80
+ ```
81
+
82
+ Endpoints exist only if the data source is defined in the tenant. Check the tenant's OpenAPI spec at `GET /v1/api/openapi.json`.
83
+
84
+ ### System Endpoints (Always Available)
85
+ ```
86
+ GET /v1/users — List users
87
+ POST /v1/users — Create user
88
+ GET /v1/users/me — Current user profile
89
+ PATCH /v1/users/me — Update current user
90
+ ```
91
+
92
+ ### ACL / Role Management Endpoints
93
+ ```
94
+ GET /v1/users/acl?dataSourceId={uuid}&recordId={uuid} — Read record ACL rows
95
+ POST /v1/users/acl/share — Upsert record shares
96
+ DELETE /v1/users/acl/share — Revoke record shares
97
+ PUT /v1/users/acl/owner — Transfer record ownership
98
+
99
+ GET /v1/users/acl/roles — List roles
100
+ GET /v1/users/acl/roles/{roleId} — Get one role
101
+ POST /v1/users/acl/roles — Create role
102
+ PATCH /v1/users/acl/roles/{roleId} — Update role
103
+ DELETE /v1/users/acl/roles/{roleId} — Delete role
104
+
105
+ GET /v1/users/acl/user-roles — List user-role assignments
106
+ GET /v1/users/acl/users/{userId}/roles — List one user's roles
107
+ POST /v1/users/acl/users/{userId}/roles — Add roles to a user
108
+ PUT /v1/users/acl/users/{userId}/roles — Replace a user's full role set
109
+ DELETE /v1/users/acl/users/{userId}/roles/{roleId} — Remove one role assignment
110
+
111
+ GET /v1/users/acl/role-queries — List role queries
112
+ GET /v1/users/acl/role-queries/{roleQueryId} — Get one role query
113
+ POST /v1/users/acl/role-queries — Create role query
114
+ PATCH /v1/users/acl/role-queries/{roleQueryId} — Update role query
115
+ DELETE /v1/users/acl/role-queries/{roleQueryId} — Delete role query
116
+ ```
117
+
118
+ ACL routes require the normal authenticated API session, but they may not appear in generated Swagger/OpenAPI output because the backend currently excludes them from public docs. Integrate them with direct `RestApiClient` calls when you need record sharing, role CRUD, user-role assignment management, or role-query management.
119
+
120
+ For all ACL role operations, prefer using role `uid` values returned by the API. Nested role objects expose both `id` and `uid`, and both map to the role UID value.
121
+
122
+ ### Making API Calls
123
+
124
+ ```typescript
125
+ // List items with query payload
126
+ const items = await client.get('/v1/apps/base/data-sources/project/items', {
127
+ columns: 'name, status, record_owner(firstname,lastname)',
128
+ filters: { rules: [{ field: 'status', operator: '!=', value: 'archived' }] },
129
+ orderBy: 'created_on DESC',
130
+ limit: 50,
131
+ })
132
+
133
+ // Get single item
134
+ const item = await client.get('/v1/apps/base/data-sources/project/items/uuid-here', {
135
+ columns: 'name, description, status',
136
+ })
137
+
138
+ // Create
139
+ const newItem = await client.post('/v1/apps/base/data-sources/project/items', {
140
+ name: 'New Project',
141
+ status: 'status-enum-id',
142
+ })
143
+
144
+ // Update
145
+ await client.patch('/v1/apps/base/data-sources/project/items/uuid-here', {
146
+ name: 'Updated Name',
147
+ })
148
+
149
+ // Delete
150
+ await client.delete('/v1/apps/base/data-sources/project/items/uuid-here')
151
+ ```
152
+
153
+ ## Query Payload Summary
154
+
155
+ The GET items endpoint accepts a powerful query payload:
156
+
157
+ | Feature | Purpose |
158
+ |---------|---------|
159
+ | `columns` | Select fields, expand relations `field(subfields)`, alias `alias:field`, spread `...field()` |
160
+ | `filters` | Nested AND/OR groups with 50+ operators (comparison, date shortcuts, user-related) |
161
+ | `filterKeyword` | Full-text search across all searchable fields |
162
+ | `orderBy` | Sort by fields with direction, including related fields |
163
+ | `limit`/`offset` | Pagination (default limit: 100) |
164
+ | `fullCount` | Return total matching count alongside results |
165
+ | `calculations` | Aggregations: count, sum, avg, min, max with grouping |
166
+ | `formulas` | Computed virtual columns (simple functions, block AST, correlated subqueries) |
167
+ | `childQueries` | Fetch related child records as nested JSON arrays |
168
+ | `pivot` | Cross-tab matrix queries with date range series |
169
+ | `expand` | Return full objects for relation/user/enum fields instead of IDs |
170
+
171
+ **For full query and formula references, read**:
172
+ - `references/data-source-query-guide.md`
173
+ - `references/formula-design-guide-llm.md`
174
+
175
+ ## Critical Rules
176
+
177
+ 1. **Always send `columns`** in list/get calls. Without it, only `id` is returned.
178
+ 2. **Data source endpoints are dynamic** — they exist only for data sources defined in the tenant.
179
+ 3. **Use `id` field** for `count` calculations. Use the actual field slug for `sum`, `avg`, `min`, `max`.
180
+ 4. **Child query keys must appear in `columns`** — if childQuery key is `orders`, include `orders` in columns.
181
+ 5. **Formula keys must appear in `columns`** — if formula key is `total`, include `total` in columns.
182
+ 6. **Filter by related field** using `rel_{{relation_field}}/{{field}}` syntax.
183
+ 7. **ACL routes may be hidden from generated OpenAPI** — call them directly via `RestApiClient` instead of expecting generated collection support.
184
+ 8. **Prefer role `uid` values** for ACL role writes, user-role `roleIds`, and role-query `roleIds`.
185
+ 9. **Treat `PUT /v1/users/acl/users/:userId/roles` as full replacement** and `POST /v1/users/acl/users/:userId/roles` as additive.
186
+ 10. **Send role-query `query` as raw JSON** and let backend derive `tenantAppId` from `dataSourceId` when applicable.
187
+ 11. **After deleting a role, refresh dependent ACL state** — role lists, user-role lists, role-query lists, and any UI showing primary-role labels.
188
+
189
+ ## References
190
+
191
+ Read these files when you need detailed information:
192
+
193
+ - **`references/api-client.md`** — Full RestApiClient API, OAuth2Client (all flows: PKCE, client credentials, device code), token managers, interceptors, error classes, SSE/streaming, file upload/download, HTML to PDF, retry logic
194
+ - **`references/authentication.md`** — @docyrus/signin React provider, useDocyrusAuth/useDocyrusClient hooks, hasRole/hasPermission authorization helpers, SignInButton, standalone vs iframe auth modes, env vars, API client access pattern
195
+ - **`references/data-source-query-guide.md`** — Up-to-date query payload guide: columns, filters, orderBy, pagination, calculations, formulas, child queries, pivots, and operator reference
196
+ - **`references/formula-design-guide-llm.md`** — Up-to-date formula design guide for building and validating `formulas` payloads
197
+ - **`references/acl-endpoints-frontend.md`** — Hidden ACL endpoint reference covering record sharing, roles, user-role assignment flows, role queries, identifier rules, and expected frontend integration behavior