@lunnoa/client 2.0.0 → 2.1.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 +13 -40
- package/dist/cli.js +6 -6
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -31
- package/dist/index.d.ts +22 -31
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +3 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +15 -22
- package/dist/react.d.ts +15 -22
- package/dist/react.js +3 -3
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/skills/lunnoa-client/SKILL.md +32 -32
- package/skills/lunnoa-solution-design/SKILL.md +9 -8
package/README.md
CHANGED
|
@@ -27,49 +27,17 @@ npm install @lunnoa/client
|
|
|
27
27
|
npx @lunnoa/client init my-app --url https://lunnoa.your-company.example
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Scaffolds the [lunnoa-starter](https://github.com/lunnoa-automate/lunnoa-starter) Next.js template (thin, Docker-deployable,
|
|
30
|
+
Scaffolds the [lunnoa-starter](https://github.com/lunnoa-automate/lunnoa-starter) Next.js template (thin, Docker-deployable, Lunnoa user login) and writes `.env`, prompting for an API key for CLI commands when run interactively. `--template <owner/repo[#ref]|./local-folder>` overrides the source. Then `npm install` and open the folder in Claude Code / Cursor / Codex.
|
|
31
31
|
|
|
32
32
|
## Quickstart
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
|
-
import {
|
|
35
|
+
import { createBrowserClient, isLoginRequires2FA } from '@lunnoa/client';
|
|
36
36
|
|
|
37
|
-
const lunnoa =
|
|
37
|
+
const lunnoa = createBrowserClient({
|
|
38
38
|
baseUrl: 'https://lunnoa.your-company.example',
|
|
39
|
-
apiKey: process.env.LUNNOA_API_KEY, // lna_... — server-side only
|
|
40
39
|
});
|
|
41
40
|
|
|
42
|
-
const { data: invoices } = await lunnoa.entities.list({
|
|
43
|
-
objectTypeSlug: 'invoice',
|
|
44
|
-
state: 'pending',
|
|
45
|
-
expansion: ['attributes'],
|
|
46
|
-
});
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
A SuperAdmin creates API keys in **Adminspace → API keys**, choosing the workspace and an RBAC role for the key's service account. The role is the key's entire capability model — grant the minimum permission set, not Admin.
|
|
50
|
-
|
|
51
|
-
## Two auth patterns
|
|
52
|
-
|
|
53
|
-
| Pattern | Credential | Where |
|
|
54
|
-
|---|---|---|
|
|
55
|
-
| **A. Backend holds the key** | `apiKey: 'lna_...'` via `createServerClient` | Your server, background jobs, server-rendered pages. The key acts as a workspace-scoped service account. |
|
|
56
|
-
| **B. End users are Lunnoa users** | User JWT via `createBrowserClient` + `lunnoa.auth.*` | Browser apps. Login, 2FA, refresh, and SSO discovery are wrapped; per-user permissions apply. |
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
import {
|
|
60
|
-
createBrowserClient,
|
|
61
|
-
createServerClient,
|
|
62
|
-
isLoginRequires2FA,
|
|
63
|
-
} from '@lunnoa/client';
|
|
64
|
-
|
|
65
|
-
// Pattern A — server-side
|
|
66
|
-
const server = createServerClient({
|
|
67
|
-
baseUrl,
|
|
68
|
-
apiKey: process.env.LUNNOA_API_KEY!,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Pattern B — browser (tokens in localStorage by default; auto-refresh on 401)
|
|
72
|
-
const lunnoa = createBrowserClient({ baseUrl });
|
|
73
41
|
const result = await lunnoa.auth.login({ email, password });
|
|
74
42
|
if (isLoginRequires2FA(result)) {
|
|
75
43
|
await lunnoa.auth.verify2faLogin({
|
|
@@ -77,12 +45,17 @@ if (isLoginRequires2FA(result)) {
|
|
|
77
45
|
token: totpCode,
|
|
78
46
|
});
|
|
79
47
|
}
|
|
80
|
-
|
|
48
|
+
|
|
49
|
+
const { data: invoices } = await lunnoa.entities.list({
|
|
50
|
+
objectTypeSlug: 'invoice',
|
|
51
|
+
state: 'pending',
|
|
52
|
+
expansion: ['attributes'],
|
|
53
|
+
});
|
|
81
54
|
```
|
|
82
55
|
|
|
83
|
-
|
|
56
|
+
People using the app sign in as Lunnoa users (invite, SSO, or SCIM). Their permissions apply. The deployment must allow your origin via `CORS_ALLOWED_ORIGINS`.
|
|
84
57
|
|
|
85
|
-
|
|
58
|
+
`lna_...` API keys are for the CLI (discovery, codegen, deploy), not for the app session. A SuperAdmin creates them in **Adminspace → API keys**, with a minimal RBAC role. The SDK throws if an `lna_` key is used while `window` exists. See the Authentication guide under `guides/develop/client/auth` in Lunnoa Docs for SSO redirect constraints and token stores.
|
|
86
59
|
|
|
87
60
|
### React (optional)
|
|
88
61
|
|
|
@@ -253,10 +226,10 @@ npx @lunnoa/client codegen --url https://lunnoa.your-company.example --api-key l
|
|
|
253
226
|
This writes `lunnoa.generated.ts`:
|
|
254
227
|
|
|
255
228
|
```typescript
|
|
256
|
-
import {
|
|
229
|
+
import { createBrowserClient } from '@lunnoa/client';
|
|
257
230
|
import { createDeploymentClient } from './lunnoa/lunnoa.generated';
|
|
258
231
|
|
|
259
|
-
const lunnoa = createDeploymentClient(
|
|
232
|
+
const lunnoa = createDeploymentClient(createBrowserClient({ baseUrl }));
|
|
260
233
|
|
|
261
234
|
// Entity attributes and states are compile-time checked:
|
|
262
235
|
const open = await lunnoa.entities.invoice.list({ state: 'pending' });
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ function isBrowserEnvironment() {
|
|
|
12
12
|
function assertApiKeyAllowedInEnvironment(apiKey) {
|
|
13
13
|
if (isBrowserEnvironment()) {
|
|
14
14
|
throw new Error(
|
|
15
|
-
"Refusing to use a Lunnoa API key (lna_...) in a browser environment. API keys are
|
|
15
|
+
"Refusing to use a Lunnoa API key (lna_...) in a browser environment. API keys are for the CLI and operator scripts, not for app login. Anyone opening dev tools could read the key and act as the service account across its whole workspace. Sign the end user in with lunnoa.auth.login and pass their JWT as `accessToken`."
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
18
|
if (!apiKey.startsWith(API_KEY_PREFIX)) {
|
|
@@ -94,7 +94,7 @@ var HttpClient = class {
|
|
|
94
94
|
}
|
|
95
95
|
if (!options.apiKey && !options.accessToken && !options.tokenStore) {
|
|
96
96
|
throw new Error(
|
|
97
|
-
"LunnoaClient requires an
|
|
97
|
+
"LunnoaClient requires an accessToken or tokenStore (Lunnoa user JWT), or an apiKey for operator scripts."
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
if (options.apiKey) {
|
|
@@ -2050,7 +2050,7 @@ var WorkflowsResource = class {
|
|
|
2050
2050
|
var LunnoaClient = class {
|
|
2051
2051
|
/** The underlying HTTP layer, exposed for advanced/escape-hatch requests. */
|
|
2052
2052
|
http;
|
|
2053
|
-
/** Login, refresh, SSO discovery, and session helpers
|
|
2053
|
+
/** Login, refresh, SSO discovery, and session helpers for Lunnoa users. */
|
|
2054
2054
|
auth;
|
|
2055
2055
|
agents;
|
|
2056
2056
|
tasks;
|
|
@@ -3026,7 +3026,7 @@ function generateFactory(entityEntries, workflowEntries, agentEntries) {
|
|
|
3026
3026
|
* entity types, workflows, and agents.
|
|
3027
3027
|
*
|
|
3028
3028
|
* \`\`\`ts
|
|
3029
|
-
* const lunnoa = createDeploymentClient(
|
|
3029
|
+
* const lunnoa = createDeploymentClient(createBrowserClient({ baseUrl }));
|
|
3030
3030
|
* const open = await lunnoa.entities.invoice.list({ state: 'pending' });
|
|
3031
3031
|
* \`\`\`
|
|
3032
3032
|
*/
|
|
@@ -3328,7 +3328,7 @@ async function runInit(args) {
|
|
|
3328
3328
|
if (process.stdin.isTTY && (!url || !apiKey)) {
|
|
3329
3329
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3330
3330
|
url ||= (await rl.question("Lunnoa URL (e.g. https://lunnoa.your-company.example): ")).trim();
|
|
3331
|
-
apiKey ||= (await rl.question("API key lna_... (
|
|
3331
|
+
apiKey ||= (await rl.question("API key lna_... (CLI only: discovery, codegen, deploy. Enter to skip): ")).trim();
|
|
3332
3332
|
rl.close();
|
|
3333
3333
|
}
|
|
3334
3334
|
const example = await readFile(join(dest, ".env.example"), "utf8").catch(() => "");
|
|
@@ -3347,7 +3347,7 @@ Next steps:`);
|
|
|
3347
3347
|
console.log(` cd ${args.path}`);
|
|
3348
3348
|
console.log(" npm install");
|
|
3349
3349
|
if (!apiKey) {
|
|
3350
|
-
console.log(" Add LUNNOA_API_KEY to .env
|
|
3350
|
+
console.log(" The app signs people in as Lunnoa users. Add LUNNOA_API_KEY to .env when you want CLI discovery and deploy");
|
|
3351
3351
|
}
|
|
3352
3352
|
console.log(" npm run dev");
|
|
3353
3353
|
console.log(" Open the folder in Claude Code / Cursor / Codex and describe your idea.");
|
package/dist/index.cjs
CHANGED
|
@@ -8,7 +8,7 @@ function isBrowserEnvironment() {
|
|
|
8
8
|
function assertApiKeyAllowedInEnvironment(apiKey) {
|
|
9
9
|
if (isBrowserEnvironment()) {
|
|
10
10
|
throw new Error(
|
|
11
|
-
"Refusing to use a Lunnoa API key (lna_...) in a browser environment. API keys are
|
|
11
|
+
"Refusing to use a Lunnoa API key (lna_...) in a browser environment. API keys are for the CLI and operator scripts, not for app login. Anyone opening dev tools could read the key and act as the service account across its whole workspace. Sign the end user in with lunnoa.auth.login and pass their JWT as `accessToken`."
|
|
12
12
|
);
|
|
13
13
|
}
|
|
14
14
|
if (!apiKey.startsWith(API_KEY_PREFIX)) {
|
|
@@ -90,7 +90,7 @@ var HttpClient = class {
|
|
|
90
90
|
}
|
|
91
91
|
if (!options.apiKey && !options.accessToken && !options.tokenStore) {
|
|
92
92
|
throw new Error(
|
|
93
|
-
"LunnoaClient requires an
|
|
93
|
+
"LunnoaClient requires an accessToken or tokenStore (Lunnoa user JWT), or an apiKey for operator scripts."
|
|
94
94
|
);
|
|
95
95
|
}
|
|
96
96
|
if (options.apiKey) {
|
|
@@ -2092,7 +2092,7 @@ var WorkflowsResource = class {
|
|
|
2092
2092
|
var LunnoaClient = class {
|
|
2093
2093
|
/** The underlying HTTP layer, exposed for advanced/escape-hatch requests. */
|
|
2094
2094
|
http;
|
|
2095
|
-
/** Login, refresh, SSO discovery, and session helpers
|
|
2095
|
+
/** Login, refresh, SSO discovery, and session helpers for Lunnoa users. */
|
|
2096
2096
|
auth;
|
|
2097
2097
|
agents;
|
|
2098
2098
|
tasks;
|