@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.
- package/agent-loader.js +36 -25
- package/agent-loader.js.map +4 -4
- package/main.js +4 -4
- package/main.js.map +1 -1
- package/package.json +3 -3
- package/resources/pi-agent/extensions/docyrus-web-browser.ts +31 -0
- package/resources/pi-agent/shared/docyrusWebBrowserProtocol.ts +169 -0
- package/resources/pi-agent/skills/diffity-diff/SKILL.md +1 -1
- package/resources/pi-agent/skills/diffity-resolve/SKILL.md +4 -4
- package/resources/pi-agent/skills/diffity-review/SKILL.md +5 -4
- package/resources/pi-agent/skills/docyrus-api-dev/SKILL.md +197 -0
- package/resources/pi-agent/skills/docyrus-api-dev/references/acl-endpoints-frontend.md +295 -0
- package/resources/pi-agent/skills/docyrus-api-dev/references/api-client.md +349 -0
- package/resources/pi-agent/skills/docyrus-api-dev/references/authentication.md +298 -0
- package/resources/pi-agent/skills/docyrus-api-dev/references/data-source-query-guide.md +2063 -0
- package/resources/pi-agent/skills/docyrus-api-dev/references/formula-design-guide-llm.md +312 -0
- package/resources/pi-agent/skills/docyrus-api-dev/references/query-and-formulas.md +592 -0
- package/resources/pi-agent/skills/docyrus-app-dev-react/SKILL.md +361 -0
- package/resources/pi-agent/skills/docyrus-app-dev-react/references/README.md +29 -0
- package/resources/pi-agent/skills/docyrus-app-dev-react/references/api-client-and-auth.md +326 -0
- package/resources/pi-agent/skills/docyrus-app-dev-react/references/collections-and-patterns.md +353 -0
- package/resources/pi-agent/skills/docyrus-app-dev-react/references/component-selection-guide.md +619 -0
- package/resources/pi-agent/skills/docyrus-app-dev-react/references/icon-usage-guide.md +463 -0
- package/resources/pi-agent/skills/docyrus-app-dev-react/references/preferred-components-catalog.md +242 -0
- package/resources/pi-agent/skills/docyrus-platform/SKILL.md +2 -2
- package/resources/pi-agent/skills/docyrus-platform/references/auth-and-multi-tenancy.md +9 -1
- package/resources/pi-agent/skills/docyrus-platform/references/developer-tools.md +3 -2
- package/server-loader.js +328 -87
- package/server-loader.js.map +4 -4
- 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.
|
|
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.
|
|
14
|
-
"@mariozechner/pi-coding-agent": "0.63.
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
57
|
+
1. **Get the unified diff** directly from diffity — this handles merge-base resolution, untracked files, and all ref types automatically:
|
|
57
58
|
```
|
|
58
|
-
|
|
59
|
+
diffity agent diff
|
|
59
60
|
```
|
|
60
|
-
|
|
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
|