@calimero-network/agent-skills 0.3.0 → 0.4.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/README.md +137 -17
- package/SKILL.md +31 -28
- package/package.json +1 -1
- package/scripts/install.js +3 -3
- package/scripts/test.js +6 -15
- package/skills/calimero-abi-codegen/SKILL.md +121 -22
- package/skills/calimero-abi-codegen/references/abi-format.md +3 -5
- package/skills/calimero-abi-codegen/references/generated-output.md +12 -4
- package/skills/calimero-abi-codegen/rules/schema-version.md +11 -4
- package/skills/calimero-abi-codegen/rules/unique-names.md +2 -6
- package/skills/calimero-client-js/SKILL.md +126 -31
- package/skills/calimero-client-js/references/auth.md +18 -10
- package/skills/calimero-client-js/references/rpc-calls.md +15 -21
- package/skills/calimero-client-js/references/sso.md +9 -9
- package/skills/calimero-client-js/references/websocket-events.md +73 -92
- package/skills/calimero-client-js/rules/camelcase-api.md +10 -7
- package/skills/calimero-client-js/rules/token-refresh.md +11 -11
- package/skills/calimero-client-py/SKILL.md +25 -13
- package/skills/calimero-client-py/references/api.md +41 -43
- package/skills/calimero-client-py/references/auth.md +7 -7
- package/skills/calimero-client-py/rules/async-usage.md +27 -31
- package/skills/calimero-client-py/rules/stable-node-name.md +7 -7
- package/skills/calimero-core/SKILL.md +135 -0
- package/skills/calimero-core/references/architecture.md +101 -0
- package/skills/calimero-core/references/jsonrpc-protocol.md +192 -0
- package/skills/calimero-core/references/namespaces-groups.md +94 -0
- package/skills/calimero-core/references/storage-types.md +118 -0
- package/skills/calimero-core/references/websocket-events.md +142 -0
- package/skills/calimero-core/rules/context-is-not-app.md +35 -0
- package/skills/calimero-core/rules/crdt-types-only.md +55 -0
- package/skills/calimero-desktop/SKILL.md +24 -19
- package/skills/calimero-desktop/references/sso-integration.md +2 -2
- package/skills/calimero-desktop/rules/sso-fallback.md +3 -2
- package/skills/calimero-merobox/SKILL.md +255 -28
- package/skills/calimero-merobox/references/ci-integration.md +3 -2
- package/skills/calimero-merobox/references/workflow-files.md +7 -5
- package/skills/calimero-merobox/rules/docker-required.md +7 -6
- package/skills/calimero-meroctl/SKILL.md +68 -0
- package/skills/calimero-meroctl/references/commands.md +177 -0
- package/skills/calimero-meroctl/references/scripting.md +80 -0
- package/skills/calimero-meroctl/rules/call-view-flag.md +28 -0
- package/skills/calimero-meroctl/rules/register-node-once.md +34 -0
- package/skills/calimero-merod/SKILL.md +49 -0
- package/skills/calimero-merod/references/health-endpoints.md +90 -0
- package/skills/calimero-merod/references/init-flags.md +84 -0
- package/skills/calimero-merod/rules/init-before-run.md +40 -0
- package/skills/calimero-merod/rules/port-assignments.md +33 -0
- package/skills/calimero-node/SKILL.md +50 -39
- package/skills/calimero-node/references/context-lifecycle.md +34 -17
- package/skills/calimero-node/references/meroctl-commands.md +89 -99
- package/skills/calimero-node/rules/app-vs-context.md +4 -4
- package/skills/calimero-registry/SKILL.md +110 -31
- package/skills/calimero-registry/references/bundle-and-push.md +99 -34
- package/skills/calimero-registry/references/manifest-format.md +56 -35
- package/skills/calimero-registry/references/mero-sign.md +10 -9
- package/skills/calimero-registry/rules/key-security.md +3 -2
- package/skills/calimero-registry/rules/sign-before-pack.md +5 -5
- package/skills/calimero-rust-sdk/SKILL.md +154 -44
- package/skills/calimero-rust-sdk/references/blob-api.md +119 -0
- package/skills/calimero-rust-sdk/references/event-handlers.md +122 -0
- package/skills/calimero-rust-sdk/references/events.md +2 -1
- package/skills/calimero-rust-sdk/references/examples.md +81 -29
- package/skills/calimero-rust-sdk/references/migrations.md +123 -0
- package/skills/calimero-rust-sdk/references/nested-crdts.md +113 -0
- package/skills/calimero-rust-sdk/references/private-storage.md +76 -34
- package/skills/calimero-rust-sdk/references/state-collections.md +106 -21
- package/skills/calimero-rust-sdk/references/user-and-frozen-storage.md +169 -0
- package/skills/calimero-rust-sdk/rules/app-macro-placement.md +5 -2
- package/skills/calimero-rust-sdk/rules/no-std-collections.md +5 -2
- package/skills/calimero-rust-sdk/rules/state-derives.md +9 -10
- package/skills/calimero-rust-sdk/rules/wasm-constraints.md +12 -10
- package/skills/calimero-sdk-js/SKILL.md +34 -26
- package/skills/calimero-sdk-js/references/build-pipeline.md +6 -6
- package/skills/calimero-sdk-js/references/collections.md +11 -11
- package/skills/calimero-sdk-js/references/events.md +7 -3
- package/skills/calimero-sdk-js/rules/crdt-only-state.md +18 -18
- package/skills/calimero-sdk-js/rules/no-console-log.md +6 -6
- package/skills/calimero-sdk-js/rules/view-decorator.md +6 -4
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Running `calimero-abi-codegen -i abi.json -o src/generated --client-name KvStoreClient` produces:
|
|
4
4
|
|
|
5
|
-
```
|
|
5
|
+
```text
|
|
6
6
|
src/generated/
|
|
7
7
|
├── types.ts
|
|
8
8
|
└── KvStoreClient.ts
|
|
@@ -36,7 +36,10 @@ import { CalimeroApp, Context } from '@calimero-network/calimero-client';
|
|
|
36
36
|
import type { Entry } from './types';
|
|
37
37
|
|
|
38
38
|
export class KvStoreClient {
|
|
39
|
-
constructor(
|
|
39
|
+
constructor(
|
|
40
|
+
private app: CalimeroApp,
|
|
41
|
+
private context: Context
|
|
42
|
+
) {}
|
|
40
43
|
|
|
41
44
|
async set(key: string, value: string): Promise<void> {
|
|
42
45
|
await this.app.mutate(this.context, 'set', { key, value });
|
|
@@ -55,7 +58,12 @@ export class KvStoreClient {
|
|
|
55
58
|
## Using the generated client
|
|
56
59
|
|
|
57
60
|
```typescript
|
|
58
|
-
import {
|
|
61
|
+
import {
|
|
62
|
+
CalimeroApp,
|
|
63
|
+
Context,
|
|
64
|
+
getJWTObject,
|
|
65
|
+
getStorageAppEndpointKey,
|
|
66
|
+
} from '@calimero-network/calimero-client';
|
|
59
67
|
import { KvStoreClient } from './generated/KvStoreClient';
|
|
60
68
|
|
|
61
69
|
const app = new CalimeroApp(getStorageAppEndpointKey(), getJWTObject()?.access_token);
|
|
@@ -65,7 +73,7 @@ const client = new KvStoreClient(app, context);
|
|
|
65
73
|
// Fully typed — IDE autocomplete works
|
|
66
74
|
await client.set('hello', 'world');
|
|
67
75
|
const value = await client.get('hello'); // string | null
|
|
68
|
-
const all = await client.entries();
|
|
76
|
+
const all = await client.entries(); // Entry[]
|
|
69
77
|
```
|
|
70
78
|
|
|
71
79
|
## Regenerating after app changes
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
# Rule: ABI manifest must have schema_version "wasm-abi/1"
|
|
2
2
|
|
|
3
|
-
The codegen tool rejects any ABI file that doesn't have the exact string `"wasm-abi/1"` as its
|
|
3
|
+
The codegen tool rejects any ABI file that doesn't have the exact string `"wasm-abi/1"` as its
|
|
4
|
+
`schema_version`.
|
|
4
5
|
|
|
5
6
|
## WRONG:
|
|
6
7
|
|
|
7
8
|
```json
|
|
8
9
|
{
|
|
9
10
|
"schema_version": "1",
|
|
10
|
-
"types": {},
|
|
11
|
+
"types": {},
|
|
12
|
+
"methods": [],
|
|
13
|
+
"events": []
|
|
11
14
|
}
|
|
12
15
|
```
|
|
13
16
|
|
|
14
17
|
```json
|
|
15
18
|
{
|
|
16
|
-
"types": {},
|
|
19
|
+
"types": {},
|
|
20
|
+
"methods": [],
|
|
21
|
+
"events": []
|
|
17
22
|
}
|
|
18
23
|
```
|
|
19
24
|
|
|
@@ -22,7 +27,9 @@ The codegen tool rejects any ABI file that doesn't have the exact string `"wasm-
|
|
|
22
27
|
```json
|
|
23
28
|
{
|
|
24
29
|
"schema_version": "wasm-abi/1",
|
|
25
|
-
"types": {},
|
|
30
|
+
"types": {},
|
|
31
|
+
"methods": [],
|
|
32
|
+
"events": []
|
|
26
33
|
}
|
|
27
34
|
```
|
|
28
35
|
|
|
@@ -20,9 +20,7 @@ The ABI validator rejects manifests where names clash — across methods, events
|
|
|
20
20
|
"types": {
|
|
21
21
|
"set": { "kind": "record", "fields": [] }
|
|
22
22
|
},
|
|
23
|
-
"methods": [
|
|
24
|
-
{ "name": "set", "params": [] }
|
|
25
|
-
]
|
|
23
|
+
"methods": [{ "name": "set", "params": [] }]
|
|
26
24
|
}
|
|
27
25
|
```
|
|
28
26
|
|
|
@@ -43,9 +41,7 @@ Map keys **must** be `{ "kind": "string" }`.
|
|
|
43
41
|
```json
|
|
44
42
|
{
|
|
45
43
|
"types": {},
|
|
46
|
-
"methods": [
|
|
47
|
-
{ "name": "get", "returns": { "$ref": "NonExistentType" } }
|
|
48
|
-
]
|
|
44
|
+
"methods": [{ "name": "get", "returns": { "$ref": "NonExistentType" } }]
|
|
49
45
|
}
|
|
50
46
|
```
|
|
51
47
|
|
|
@@ -1,54 +1,135 @@
|
|
|
1
1
|
# calimero-client-js — Agent Instructions
|
|
2
2
|
|
|
3
|
-
You are helping a developer connect a **browser or Node.js frontend** to a Calimero node
|
|
4
|
-
|
|
3
|
+
You are helping a developer connect a **browser or Node.js frontend** to a Calimero node using
|
|
4
|
+
`@calimero-network/mero-react` (preferred) or `@calimero-network/calimero-client`.
|
|
5
5
|
|
|
6
|
-
> **NOT this skill** if the developer is building the application logic that
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
> a browser or backend service.
|
|
6
|
+
> **NOT this skill** if the developer is building the application logic that _runs on the node_ in
|
|
7
|
+
> TypeScript — that is `calimero-sdk-js`. This skill is for the _client_ side: auth, RPC calls, and
|
|
8
|
+
> WebSocket subscriptions from a browser or React app.
|
|
10
9
|
|
|
11
10
|
## Package versions
|
|
12
11
|
|
|
13
|
-
| Package
|
|
14
|
-
|
|
|
15
|
-
| `@calimero-network/
|
|
16
|
-
| `@calimero-network/mero-js`
|
|
17
|
-
|
|
18
|
-
**Which to use:** new projects should prefer `@calimero-network/mero-js` v2. If you are
|
|
19
|
-
maintaining an existing codebase that uses `calimero-client`, check for snake_case field
|
|
20
|
-
names before migrating — do not mix both packages in the same project.
|
|
12
|
+
| Package | Notes |
|
|
13
|
+
| ----------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
14
|
+
| `@calimero-network/mero-react` | **Preferred for React apps.** Exports `MeroJs`, `useSubscription`, `MeroProvider`, hooks. |
|
|
15
|
+
| `@calimero-network/mero-js` | Core SDK. Zero deps. Used standalone in non-React contexts. |
|
|
16
|
+
| `@calimero-network/calimero-client` | Legacy client. Still works; new projects should prefer mero-react/mero-js. |
|
|
21
17
|
|
|
22
18
|
## Critical: mero-js v2 uses camelCase
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
All request field names changed from `snake_case` to `camelCase` in v2.
|
|
25
21
|
|
|
26
22
|
```typescript
|
|
27
|
-
// WRONG (v1
|
|
23
|
+
// WRONG (v1):
|
|
28
24
|
{ context_id: '...', context_identity: '...' }
|
|
29
25
|
|
|
30
|
-
// CORRECT (v2):
|
|
26
|
+
// CORRECT (v2 / mero-react):
|
|
31
27
|
{ contextId: '...', contextIdentity: '...' }
|
|
32
28
|
```
|
|
33
29
|
|
|
34
|
-
|
|
30
|
+
## React app pattern (mero-react) — recommended
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
### Install
|
|
37
33
|
|
|
38
34
|
```bash
|
|
39
|
-
pnpm add @calimero-network/
|
|
40
|
-
# or
|
|
41
|
-
pnpm add @calimero-network/mero-js
|
|
35
|
+
pnpm add @calimero-network/mero-react
|
|
42
36
|
```
|
|
43
37
|
|
|
44
|
-
|
|
38
|
+
### Setup provider
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { MeroProvider } from '@calimero-network/mero-react';
|
|
42
|
+
|
|
43
|
+
function App() {
|
|
44
|
+
return (
|
|
45
|
+
<MeroProvider nodeUrl="http://localhost:2428">
|
|
46
|
+
<YourApp />
|
|
47
|
+
</MeroProvider>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
2. Store tokens using the provided storage helpers (`setAppEndpointKey`, `setAccessToken`, etc.)
|
|
48
|
-
3. Call app methods via the `rpcClient` singleton using `rpcClient.execute()`
|
|
49
|
-
4. Subscribe to events via `WsSubscriptionsClient`
|
|
52
|
+
### Call app methods
|
|
50
53
|
|
|
51
|
-
|
|
54
|
+
Generated clients (from abi-codegen) import `MeroJs` from `@calimero-network/mero-react`:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { MeroJs } from '@calimero-network/mero-react';
|
|
58
|
+
|
|
59
|
+
export class KvClient {
|
|
60
|
+
constructor(
|
|
61
|
+
private mero: MeroJs,
|
|
62
|
+
private contextId: string,
|
|
63
|
+
private executorPublicKey: string
|
|
64
|
+
) {}
|
|
65
|
+
|
|
66
|
+
async set(key: string, value: string): Promise<void> {
|
|
67
|
+
await this.mero.rpc.execute({
|
|
68
|
+
contextId: this.contextId,
|
|
69
|
+
method: 'set',
|
|
70
|
+
argsJson: { key, value },
|
|
71
|
+
executorPublicKey: this.executorPublicKey,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async get(key: string): Promise<string | null> {
|
|
76
|
+
const response = await this.mero.rpc.execute({
|
|
77
|
+
contextId: this.contextId,
|
|
78
|
+
method: 'get',
|
|
79
|
+
argsJson: { key },
|
|
80
|
+
executorPublicKey: this.executorPublicKey,
|
|
81
|
+
});
|
|
82
|
+
return response as string | null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Subscribe to events (mero-react hook)
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { useSubscription } from '@calimero-network/mero-react';
|
|
91
|
+
|
|
92
|
+
// Subscribe to one or more contexts
|
|
93
|
+
useSubscription([contextId], (event: { contextId: string; data: unknown }) => {
|
|
94
|
+
// event.data may be:
|
|
95
|
+
// - { type: 'EventName', ...payload } for direct events
|
|
96
|
+
// - { events: [{ kind: string, data: unknown }] } for execution event batches
|
|
97
|
+
console.log('event:', event.data);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Subscribe to multiple contexts (e.g. game + lobby simultaneously)
|
|
101
|
+
useSubscription([gameContextId, lobbyContextId], (event) => {
|
|
102
|
+
console.log('from context:', event.contextId);
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Parsing execution event payloads
|
|
107
|
+
|
|
108
|
+
Events from `app::emit!()` arrive batched in an `events` array. Each entry has `kind` (the variant
|
|
109
|
+
name) and `data` (the payload, possibly as a byte array):
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
function decodeEventData(data: unknown): unknown {
|
|
113
|
+
// If data is a number array, it's JSON-encoded bytes
|
|
114
|
+
if (Array.isArray(data) && data.every((n) => typeof n === 'number')) {
|
|
115
|
+
return JSON.parse(new TextDecoder().decode(new Uint8Array(data)));
|
|
116
|
+
}
|
|
117
|
+
return data;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// In the subscription callback:
|
|
121
|
+
useSubscription([contextId], (event) => {
|
|
122
|
+
const payload = event.data as any;
|
|
123
|
+
if (Array.isArray(payload?.events)) {
|
|
124
|
+
for (const e of payload.events) {
|
|
125
|
+
const decoded = decodeEventData(e.data);
|
|
126
|
+
console.log(e.kind, decoded); // e.g. "Inserted", { key: "...", value: "..." }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Legacy calimero-client pattern
|
|
52
133
|
|
|
53
134
|
```typescript
|
|
54
135
|
import {
|
|
@@ -78,7 +159,7 @@ const response = await rpcClient.execute<{ key: string }, string | null>({
|
|
|
78
159
|
});
|
|
79
160
|
console.log(response.result?.output);
|
|
80
161
|
|
|
81
|
-
// 3. Subscribe to
|
|
162
|
+
// 3. Subscribe to events
|
|
82
163
|
const ws = new WsSubscriptionsClient(getAppEndpointKey()!, '/ws');
|
|
83
164
|
await ws.connect();
|
|
84
165
|
ws.subscribe([getContextId()!]);
|
|
@@ -91,7 +172,21 @@ ws.addCallback((event) => {
|
|
|
91
172
|
});
|
|
92
173
|
```
|
|
93
174
|
|
|
175
|
+
## Core workflow
|
|
176
|
+
|
|
177
|
+
1. On startup: read SSO tokens from URL hash (if opened by Desktop), otherwise check `localStorage`,
|
|
178
|
+
otherwise show login
|
|
179
|
+
2. Store tokens using storage helpers or the `MeroProvider` (handles this automatically)
|
|
180
|
+
3. Call app methods via the generated typed client or `mero.rpc.execute()`
|
|
181
|
+
4. Subscribe to events via `useSubscription` (React) or `WsSubscriptionsClient`
|
|
182
|
+
|
|
183
|
+
## Related skills
|
|
184
|
+
|
|
185
|
+
- **`calimero-core`** — JSON-RPC protocol spec, WebSocket event schemas, context/app/identity model
|
|
186
|
+
- **`calimero-desktop`** — SSO token passing from Calimero Desktop (URL hash flow)
|
|
187
|
+
- **`calimero-node`** — node setup if the developer is also running the node
|
|
188
|
+
|
|
94
189
|
## References
|
|
95
190
|
|
|
96
|
-
See `references/` for auth flow, RPC calls, WebSocket events, and SSO.
|
|
97
|
-
|
|
191
|
+
See `references/` for auth flow, RPC calls, WebSocket events, and SSO. See `rules/` for camelCase
|
|
192
|
+
API and token refresh requirements.
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
getContextId,
|
|
20
20
|
setExecutorPublicKey,
|
|
21
21
|
getExecutorPublicKey,
|
|
22
|
-
setContextAndIdentityFromJWT,
|
|
22
|
+
setContextAndIdentityFromJWT, // extracts + stores contextId + executorPublicKey from JWT
|
|
23
23
|
// App ID
|
|
24
24
|
setApplicationId,
|
|
25
25
|
getApplicationId,
|
|
@@ -27,9 +27,9 @@ import {
|
|
|
27
27
|
setAuthEndpointURL,
|
|
28
28
|
getAuthEndpointURL,
|
|
29
29
|
// Decoded JWT payload
|
|
30
|
-
getJWTObject,
|
|
30
|
+
getJWTObject, // returns { context_id, context_identity, exp, permissions, ... }
|
|
31
31
|
// Full auth config (throws if required fields are missing)
|
|
32
|
-
getAuthConfig,
|
|
32
|
+
getAuthConfig, // returns { appEndpointKey, contextId, executorPublicKey, jwtToken }
|
|
33
33
|
// Logout (clears all tokens + reloads)
|
|
34
34
|
clientLogout,
|
|
35
35
|
} from '@calimero-network/calimero-client';
|
|
@@ -44,10 +44,10 @@ When the app is opened by Desktop, tokens arrive in the URL hash. Store them:
|
|
|
44
44
|
```typescript
|
|
45
45
|
function initFromDesktopSSO(): boolean {
|
|
46
46
|
const hash = new URLSearchParams(window.location.hash.slice(1));
|
|
47
|
-
const accessToken
|
|
47
|
+
const accessToken = hash.get('access_token');
|
|
48
48
|
const refreshToken = hash.get('refresh_token');
|
|
49
|
-
const nodeUrl
|
|
50
|
-
const appId
|
|
49
|
+
const nodeUrl = hash.get('node_url');
|
|
50
|
+
const appId = hash.get('application_id');
|
|
51
51
|
|
|
52
52
|
if (!accessToken || !nodeUrl) return false;
|
|
53
53
|
|
|
@@ -72,7 +72,12 @@ function initFromDesktopSSO(): boolean {
|
|
|
72
72
|
## Manual login (no Desktop)
|
|
73
73
|
|
|
74
74
|
```typescript
|
|
75
|
-
import {
|
|
75
|
+
import {
|
|
76
|
+
setAppEndpointKey,
|
|
77
|
+
setAccessToken,
|
|
78
|
+
setRefreshToken,
|
|
79
|
+
setContextAndIdentityFromJWT,
|
|
80
|
+
} from '@calimero-network/calimero-client';
|
|
76
81
|
|
|
77
82
|
async function login(nodeUrl: string, accessToken: string, refreshToken: string) {
|
|
78
83
|
setAppEndpointKey(nodeUrl);
|
|
@@ -126,8 +131,11 @@ clientLogout();
|
|
|
126
131
|
|
|
127
132
|
```typescript
|
|
128
133
|
import {
|
|
129
|
-
setAppEndpointKey,
|
|
130
|
-
|
|
134
|
+
setAppEndpointKey,
|
|
135
|
+
setAccessToken,
|
|
136
|
+
setRefreshToken,
|
|
137
|
+
setApplicationId,
|
|
138
|
+
setContextAndIdentityFromJWT,
|
|
131
139
|
getAuthConfig,
|
|
132
140
|
} from '@calimero-network/calimero-client';
|
|
133
141
|
|
|
@@ -135,7 +143,7 @@ async function bootstrap() {
|
|
|
135
143
|
// Try SSO from Desktop hash
|
|
136
144
|
const hash = new URLSearchParams(window.location.hash.slice(1));
|
|
137
145
|
const accessToken = hash.get('access_token');
|
|
138
|
-
const nodeUrl
|
|
146
|
+
const nodeUrl = hash.get('node_url');
|
|
139
147
|
|
|
140
148
|
if (accessToken && nodeUrl) {
|
|
141
149
|
setAppEndpointKey(nodeUrl);
|
|
@@ -4,15 +4,11 @@ Calling application methods on a Calimero node.
|
|
|
4
4
|
|
|
5
5
|
## The `rpcClient` singleton
|
|
6
6
|
|
|
7
|
-
`@calimero-network/calimero-client` exports a pre-configured `rpcClient` singleton.
|
|
8
|
-
|
|
7
|
+
`@calimero-network/calimero-client` exports a pre-configured `rpcClient` singleton. Import it
|
|
8
|
+
directly — do not construct `JsonRpcClient` manually.
|
|
9
9
|
|
|
10
10
|
```typescript
|
|
11
|
-
import {
|
|
12
|
-
rpcClient,
|
|
13
|
-
getContextId,
|
|
14
|
-
getExecutorPublicKey,
|
|
15
|
-
} from '@calimero-network/calimero-client';
|
|
11
|
+
import { rpcClient, getContextId, getExecutorPublicKey } from '@calimero-network/calimero-client';
|
|
16
12
|
```
|
|
17
13
|
|
|
18
14
|
---
|
|
@@ -23,7 +19,9 @@ import {
|
|
|
23
19
|
const response = await rpcClient.execute<ArgsType, OutputType>({
|
|
24
20
|
contextId: getContextId()!,
|
|
25
21
|
method: 'methodName',
|
|
26
|
-
argsJson: {
|
|
22
|
+
argsJson: {
|
|
23
|
+
/* your args */
|
|
24
|
+
},
|
|
27
25
|
executorPublicKey: getExecutorPublicKey()!,
|
|
28
26
|
});
|
|
29
27
|
|
|
@@ -96,20 +94,20 @@ if (!response.error) {
|
|
|
96
94
|
|
|
97
95
|
## Error names
|
|
98
96
|
|
|
99
|
-
| name
|
|
100
|
-
|
|
101
|
-
| `FunctionCallError`
|
|
102
|
-
| `RpcExecutionError`
|
|
97
|
+
| name | meaning |
|
|
98
|
+
| --------------------- | -------------------------------------------------- |
|
|
99
|
+
| `FunctionCallError` | App method returned an error |
|
|
100
|
+
| `RpcExecutionError` | Node couldn't execute the method |
|
|
103
101
|
| `InvalidRequestError` | Malformed request (wrong context-id, missing args) |
|
|
104
|
-
| `AuthenticationError` | JWT expired, revoked, or missing
|
|
105
|
-
| `UnknownServerError`
|
|
102
|
+
| `AuthenticationError` | JWT expired, revoked, or missing |
|
|
103
|
+
| `UnknownServerError` | Unexpected server error |
|
|
106
104
|
|
|
107
105
|
---
|
|
108
106
|
|
|
109
107
|
## Error handling with 401
|
|
110
108
|
|
|
111
|
-
The HTTP client inside `rpcClient` automatically refreshes tokens on `401 token_expired`.
|
|
112
|
-
|
|
109
|
+
The HTTP client inside `rpcClient` automatically refreshes tokens on `401 token_expired`. For
|
|
110
|
+
`token_revoked` or `invalid_token` you need to re-authenticate:
|
|
113
111
|
|
|
114
112
|
```typescript
|
|
115
113
|
const response = await rpcClient.execute({ ... });
|
|
@@ -133,11 +131,7 @@ if (response.error) {
|
|
|
133
131
|
## Complete example: typed wrapper
|
|
134
132
|
|
|
135
133
|
```typescript
|
|
136
|
-
import {
|
|
137
|
-
rpcClient,
|
|
138
|
-
getContextId,
|
|
139
|
-
getExecutorPublicKey,
|
|
140
|
-
} from '@calimero-network/calimero-client';
|
|
134
|
+
import { rpcClient, getContextId, getExecutorPublicKey } from '@calimero-network/calimero-client';
|
|
141
135
|
|
|
142
136
|
async function setItem(key: string, value: string): Promise<void> {
|
|
143
137
|
const response = await rpcClient.execute<{ key: string; value: string }, void>({
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# SSO — Calimero Desktop Integration
|
|
2
2
|
|
|
3
|
-
When a user opens your app from Calimero Desktop, auth tokens are passed via the URL hash.
|
|
4
|
-
|
|
3
|
+
When a user opens your app from Calimero Desktop, auth tokens are passed via the URL hash. Reading
|
|
4
|
+
them lets users skip the manual login flow entirely.
|
|
5
5
|
|
|
6
6
|
## Hash parameters
|
|
7
7
|
|
|
8
|
-
| Parameter
|
|
9
|
-
|
|
|
10
|
-
| `access_token`
|
|
11
|
-
| `refresh_token`
|
|
12
|
-
| `node_url`
|
|
8
|
+
| Parameter | Description |
|
|
9
|
+
| ---------------- | ------------------------------------------ |
|
|
10
|
+
| `access_token` | JWT for authenticated node calls |
|
|
11
|
+
| `refresh_token` | Token to obtain a new access token |
|
|
12
|
+
| `node_url` | URL of the local node to connect to |
|
|
13
13
|
| `application_id` | The installed application's ID on the node |
|
|
14
14
|
|
|
15
15
|
## Reading from hash
|
|
@@ -68,5 +68,5 @@ async function initApp() {
|
|
|
68
68
|
|
|
69
69
|
## Important
|
|
70
70
|
|
|
71
|
-
Always fall back to manual login if the hash params are absent — the app must work
|
|
72
|
-
|
|
71
|
+
Always fall back to manual login if the hash params are absent — the app must work when opened
|
|
72
|
+
directly in a browser, not only from Desktop.
|