howone 0.2.2 → 0.2.5

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/package.json +1 -1
  2. package/templates/vite/.howone/skills/howone/01-architect/01-app-generation.md +36 -7
  3. package/templates/vite/.howone/skills/howone/01-architect/02-manifest-codegen.md +121 -436
  4. package/templates/vite/.howone/skills/howone/03-ai-capabilities/03-service-capability-catalog.md +37 -19
  5. package/templates/vite/.howone/skills/howone/03-ai-capabilities/04-workflow-operations.md +14 -5
  6. package/templates/vite/.howone/skills/howone/04-app-sdk/01-client-setup.md +94 -261
  7. package/templates/vite/.howone/skills/howone/04-app-sdk/02-entity-operations.md +85 -465
  8. package/templates/vite/.howone/skills/howone/04-app-sdk/03-auth.md +11 -7
  9. package/templates/vite/.howone/skills/howone/04-app-sdk/04-react-integration.md +84 -137
  10. package/templates/vite/.howone/skills/howone/04-app-sdk/05-file-upload.md +66 -273
  11. package/templates/vite/.howone/skills/howone/04-app-sdk/06-raw-http.md +72 -249
  12. package/templates/vite/.howone/skills/howone/04-app-sdk/07-ai-action-calls.md +135 -499
  13. package/templates/vite/.howone/skills/howone/04-app-sdk/08-ai-manifest-handoff.md +49 -196
  14. package/templates/vite/.howone/skills/howone/04-app-sdk/09-extension-boundaries.md +4 -4
  15. package/templates/vite/.howone/skills/howone/04-app-sdk/10-workflow-execute-sse.md +94 -61
  16. package/templates/vite/.howone/skills/howone/04-app-sdk/11-entity-data-access-patterns.md +4 -3
  17. package/templates/vite/.howone/skills/howone/SKILL.md +110 -4
  18. package/templates/vite/.howone/skills/howone/references/audio-generation.md +30 -0
  19. package/templates/vite/.howone/skills/howone/references/audio-recognition.md +30 -0
  20. package/templates/vite/.howone/skills/howone/references/common-errors.md +27 -0
  21. package/templates/vite/.howone/skills/howone/references/finance.md +28 -0
  22. package/templates/vite/.howone/skills/howone/references/image-editing.md +30 -0
  23. package/templates/vite/.howone/skills/howone/references/image-generation.md +30 -0
  24. package/templates/vite/.howone/skills/howone/references/version-evidence.md +47 -0
  25. package/templates/vite/.howone/skills/howone/references/video-generation.md +35 -0
  26. package/templates/vite/.howone/skills/howone/scripts/verify-project.mjs +151 -0
  27. package/templates/vite/package.json +1 -1
  28. package/templates/vite/src/App.tsx +9 -5
  29. package/templates/vite/src/lib/sdk.ts +7 -5
  30. package/templates/vite/bun.lock +0 -1478
@@ -64,12 +64,14 @@ That wires:
64
64
  Pair with React:
65
65
 
66
66
  ```tsx
67
- <HowOneProvider auth="none" brand="visible">
67
+ <HowOneProvider client={howone} brand="visible">
68
68
  <App />
69
69
  </HowOneProvider>
70
70
  ```
71
71
 
72
- `auth="none"` on the provider means **no automatic redirect**; route guards call `howone.me()` and `navigate('/login')` yourself. Keep `brand="visible"` unless the user explicitly asks to hide the bottom-right HowOne logo.
72
+ The Provider derives its route guard from `client.auth.guard`; custom/headless clients default to no
73
+ hosted redirect. Override `auth="none"` only when the app deliberately owns route guards. Keep
74
+ `brand="visible"` unless the user explicitly asks to hide the bottom-right HowOne logo.
73
75
 
74
76
  | `createClient({ auth })` | Login UI | `auth.login()` | `auth.logout()` default redirect |
75
77
  |--------------------------|----------|----------------|----------------------------------|
@@ -88,7 +90,7 @@ auth: {
88
90
  mode: 'custom',
89
91
  loginPath: '/sign-in',
90
92
  logoutPath: '/sign-in', // optional; defaults to loginPath
91
- guard: 'required', // optional; use with HowOneProvider auth="required" for auto-redirect to loginPath
93
+ guard: 'required', // optional; Provider inherits this when passed client={howone}
92
94
  getToken: async () => null, // only for headless
93
95
  }
94
96
  ```
@@ -192,6 +194,8 @@ import { createClient, defineEntities, withEntities } from '@howone/sdk'
192
194
  const client = createClient({
193
195
  projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
194
196
  env: import.meta.env.VITE_HOWONE_ENV,
197
+ auth: 'custom',
198
+ loginPath: '/login',
195
199
  })
196
200
 
197
201
  export default withEntities(client, defineEntities({ /* ... */ }))
@@ -201,9 +205,9 @@ export default withEntities(client, defineEntities({ /* ... */ }))
201
205
 
202
206
  ```tsx
203
207
  import { HowOneProvider } from '@howone/sdk/react'
204
- import './lib/sdk' // registers auth config
208
+ import howone from './lib/sdk'
205
209
 
206
- <HowOneProvider auth="none" brand="visible">
210
+ <HowOneProvider client={howone} brand="visible">
207
211
  <App />
208
212
  </HowOneProvider>
209
213
  ```
@@ -285,7 +289,7 @@ createClient({ projectId, env })
285
289
  ```
286
290
 
287
291
  ```tsx
288
- <HowOneProvider auth="required">
292
+ <HowOneProvider client={howone}>
289
293
  <App />
290
294
  </HowOneProvider>
291
295
  ```
@@ -392,7 +396,7 @@ try {
392
396
  | Mistake | Fix |
393
397
  |---------|-----|
394
398
  | Custom `/login` page with HowOne OTP/OAuth | Add `auth: 'custom'` in `createClient` |
395
- | `HowOneProvider auth="required"` + custom login | Use `auth="none"`; guard with `howone.me()` |
399
+ | Provider without `client={howone}` | Pass the composed client so auth strategy and guard are instance-consistent |
396
400
  | `howone.auth.logout()` expecting no redirect before this change | Now respects `auth: 'custom'` |
397
401
  | `auth.isAuthenticated()` on first paint | Use `await howone.me()` |
398
402
  | Phone without country code | E.164 `+86...` |
@@ -1,191 +1,138 @@
1
1
  # React Integration
2
2
 
3
- ## What `@howone/sdk/react` Provides
3
+ Use this track for the application shell, auth context, and optional element-selector devtools.
4
+ Read `01-client-setup.md` first and initialize `src/lib/sdk.ts` before rendering the Provider.
4
5
 
5
- Thin integration layer: auth context plus the HowOne floating brand button. **No** entity hooks,
6
- AI hooks, toast system, redirect overlay, or app-owned UI.
6
+ ## Package boundaries
7
7
 
8
- Exports:
9
-
10
- - `HowOneProvider`
11
- - `useHowoneContext`
12
- - `FloatingButton`
13
-
14
- ---
15
-
16
- ## Auth: one SDK config + one Provider flag
17
-
18
- **Step 1 — `src/lib/sdk.ts` (required):**
8
+ Keep imports on the smallest entry point:
19
9
 
20
10
  ```ts
21
- const client = createClient({
22
- projectId: import.meta.env.VITE_HOWONE_PROJECT_ID,
23
- env: import.meta.env.VITE_HOWONE_ENV,
24
- })
11
+ import { createClient } from '@howone/sdk' // framework-neutral core
12
+ import { HowOneProvider, useHowoneContext } from '@howone/sdk/react'
13
+ import { ElementSelectorProvider } from '@howone/sdk/devtools' // optional editor tooling
25
14
  ```
26
15
 
27
- **Step 2 Provider:**
16
+ The core package must remain usable in workers, tests, and non-React apps. Do not import React
17
+ components from `@howone/sdk` or selector tooling from `/react`.
18
+
19
+ ## Root wiring
28
20
 
29
21
  ```tsx
22
+ import howone from '@/lib/sdk'
23
+ import { ElementSelectorProvider } from '@howone/sdk/devtools'
30
24
  import { HowOneProvider } from '@howone/sdk/react'
31
- import './lib/sdk' // must import before Provider so auth config is registered
32
-
33
- <HowOneProvider auth="none" brand="visible">
34
- <App />
35
- </HowOneProvider>
36
- ```
37
-
38
- | Layer | Setting | Meaning |
39
- |-------|---------|---------|
40
- | `createClient` | `{ projectId, env }` (default **hosted**) | Login/logout → HowOne `/auth` |
41
- | `createClient` | `auth: 'custom'` | Login/logout → your `loginPath`; APIs still HowOne |
42
- | `HowOneProvider` | `auth="required"` | Default with hosted — redirect to HowOne login |
43
- | `HowOneProvider` | `auth="none"` | Use with `auth: 'custom'`; guard routes yourself |
44
-
45
- Default (HowOne hosted login):
46
25
 
47
- ```tsx
48
- createClient({ projectId, env })
49
- <HowOneProvider auth="required" />
26
+ export function AppRoot() {
27
+ return (
28
+ <ElementSelectorProvider>
29
+ <HowOneProvider client={howone} brand="visible">
30
+ <App />
31
+ </HowOneProvider>
32
+ </ElementSelectorProvider>
33
+ )
34
+ }
50
35
  ```
51
36
 
52
- Custom login page (your UI, HowOne auth APIs, keep HowOne logo unless product asks to hide it):
37
+ `ElementSelectorProvider` is optional and should be present when the app is edited through the
38
+ HowOne iframe/editor integration. It is independent from auth. `HowOneProvider` is required when
39
+ the app uses any HowOne runtime surface.
53
40
 
54
- ```tsx
55
- createClient({ projectId, env, auth: 'custom', loginPath: '/login' })
56
- <HowOneProvider auth="none" brand="visible" />
57
- ```
41
+ The Provider waits until its auth URL/cookie check completes. With `auth="required"`, protected
42
+ children are not briefly rendered before redirect. By default it derives the guard from
43
+ `client.auth.guard`:
58
44
 
59
- ---
45
+ | Client auth | Default Provider guard | Use |
46
+ |---|---|---|
47
+ | hosted | required | hosted login protects the app |
48
+ | custom/headless | none | app or adapter owns login UI |
49
+ | none | none | public app |
60
50
 
61
- ## HowOneProvider
51
+ Override deliberately when a single app has a public shell and protected routes:
62
52
 
63
53
  ```tsx
64
- <HowOneProvider
65
- auth="none"
66
- brand="visible"
67
- onAuthRedirect={({ mode, returnUrl }) => {
68
- // App may set its own loading/redirect state here.
69
- }}
70
- onAuthStateChange={(state) => {
71
- // App may update analytics or local UI state here.
72
- }}
73
- >
54
+ <HowOneProvider client={howone} auth="optional" brand="visible">
74
55
  <App />
75
56
  </HowOneProvider>
76
57
  ```
77
58
 
78
- ### HowOneProviderProps
59
+ `auth` is only a route guard. It does not change the client's login strategy. Login/logout
60
+ destinations come from `createClient({ auth, loginPath, logoutPath })`.
79
61
 
80
- ```ts
81
- type HowOneProviderAuth = 'required' | 'optional' | 'none'
62
+ ## Provider props
82
63
 
83
- interface HowOneProviderProps {
64
+ ```ts
65
+ type HowOneProviderProps = {
84
66
  children: React.ReactNode
85
- projectId?: string // prefer createClient projectId
86
- auth?: HowOneProviderAuth
67
+ client?: HowOneClient
68
+ auth?: 'required' | 'optional' | 'none'
87
69
  brand?: 'visible' | 'hidden'
88
70
  showBrandButton?: boolean
89
- theme?: 'dark' | 'light' | 'system' | 'inherit'
90
71
  onAuthStateChange?: (state: AuthState) => void
91
72
  onAuthRedirect?: (info: { mode: 'hosted' | 'custom'; returnUrl: string }) => void
92
73
  }
93
74
  ```
94
75
 
95
- **Important:** Provider `auth` is only a **route guard**. Login/logout URLs come from `createClient({ auth: 'custom' })`.
76
+ `projectId` is retained only as a deprecated compatibility path when no client is passed. New app
77
+ code must pass `client`. Theme ownership belongs to the app's ThemeProvider; the HowOne Provider
78
+ does not accept or silently ignore theme props.
96
79
 
97
- The provider must not render app-owned UI. It does not own toasts, dialogs, pages, custom login UI,
98
- or redirect overlays. It does keep the bottom-right HowOne logo by default through `FloatingButton`.
99
- Use `brand="hidden"` or `showBrandButton={false}` only when the product explicitly asks to hide it.
100
-
101
- ---
102
-
103
- ## useHowoneContext
104
-
105
- ```ts
106
- const { user, token, isAuthenticated, logout } = useHowoneContext()
107
- ```
108
-
109
- ### Logout
80
+ The floating HowOne brand button is visible by default. Hide it only when product requirements say
81
+ so:
110
82
 
111
83
  ```tsx
112
- <button onClick={() => void logout()}>Sign out</button>
84
+ <HowOneProvider client={howone} brand="hidden">
85
+ <App />
86
+ </HowOneProvider>
113
87
  ```
114
88
 
115
- With `auth: 'custom'`, `logout()` clears session and navigates to `loginPath` **not** howone.dev.
116
-
117
- Equivalent:
89
+ The Provider does not own toasts, dialogs, loading overlays, route components, or app-specific login
90
+ screens. Render those in the app and use SDK callbacks/results as signals.
118
91
 
119
- ```ts
120
- await howone.auth.logout()
121
- ```
122
-
123
- ### Custom login page link
92
+ ## Context and logout
124
93
 
125
94
  ```tsx
126
- import { useNavigate } from 'react-router-dom'
127
- import howone from '@/lib/sdk'
128
-
129
- function Header() {
130
- const navigate = useNavigate()
131
- const { isAuthenticated, logout } = useHowoneContext()
95
+ import { useHowoneContext } from '@howone/sdk/react'
132
96
 
133
- if (!isAuthenticated) {
134
- return <button onClick={() => navigate(howone.auth.loginPath)}>Sign in</button>
135
- }
136
-
137
- return <button onClick={() => void logout()}>Sign out</button>
97
+ function AccountButton() {
98
+ const { user, isAuthenticated, logout } = useHowoneContext()
99
+ if (!isAuthenticated) return <a href="/login">Sign in</a>
100
+ return <button onClick={() => void logout()}>{user?.name || 'Sign out'}</button>
138
101
  }
139
102
  ```
140
103
 
141
- ---
104
+ `useHowoneContext()` intentionally throws when called outside the Provider. This catches broken
105
+ tree wiring immediately; do not add a global-token fallback.
142
106
 
143
- ## FloatingButton
107
+ For non-React code, use `howone.auth.*`, `howone.me()`, and `howone.requireMe()` from the singleton.
108
+ `useAuth` remains a compatibility hook on `/react`, but `useHowoneContext` is preferred because it
109
+ is bound to the client instance.
144
110
 
145
- The bottom-right HowOne logo is part of the SDK React integration and should remain visible by
146
- default. It does not replace your login page and does not perform app auth. Hide it only with
147
- `brand="hidden"` or `showBrandButton={false}`.
111
+ ## Auth state and routes
148
112
 
149
- ---
113
+ - Hosted auth: let `HowOneProvider client={howone}` redirect.
114
+ - Custom auth: use `auth: 'custom'`, render the app's login route, obtain OTP/OAuth tokens through
115
+ the SDK or adapter, and call `howone.auth.setToken(token)`.
116
+ - Headless auth: adapter owns token refresh; an opaque token is accepted, while JWT expiration is
117
+ checked by the SDK.
118
+ - Public app: use `auth: 'none'` and do not add a fake login screen.
150
119
 
151
- ## Protected route pattern
120
+ If a custom login page needs a route guard, use `howone.auth.isAuthenticated()` or
121
+ `howone.me()` rather than manually parsing ownership fields.
152
122
 
153
- ```tsx
154
- function ProtectedPage() {
155
- const [user, setUser] = useState(null)
156
- const navigate = useNavigate()
157
-
158
- useEffect(() => {
159
- howone.me()
160
- .then(setUser)
161
- .catch(() => navigate(howone.auth.loginPath, { replace: true }))
162
- }, [navigate])
163
-
164
- if (!user) return null
165
- return <div>Welcome {user.name}</div>
166
- }
167
- ```
123
+ ## Element selector security
168
124
 
169
- ---
125
+ `ElementSelectorProvider` accepts `allowedOrigins` and `targetOrigin` for cross-window messages.
126
+ The default trusts same-origin and the `document.referrer` origin. Use explicit origins in embedded
127
+ multi-tenant deployments; never reintroduce `'*'` for messages carrying selected element data.
170
128
 
171
129
  ## Common mistakes
172
130
 
173
- | Mistake | Fix |
174
- |---------|-----|
175
- | Custom UI but no `auth: 'custom'` | Add to `createClient` |
176
- | `HowOneProvider auth="required"` without custom SDK auth | Hosted redirect to howone.ai |
177
- | `useHowoneContext` without Provider | Wrap app in `HowOneProvider` |
178
- | Import Provider before `./lib/sdk` | Import sdk module first |
179
- | Manual redirect to howone.dev on logout | Use `howone.auth.logout()` |
180
- | Deleting the bottom-right HowOne logo by default | Keep `brand="visible"` unless explicitly asked to hide it |
181
- | Expecting SDK toast APIs | Implement visible feedback in the frontend app from callbacks/results |
182
-
183
- ---
184
-
185
- ## Import map
186
-
187
- | Need | Import |
188
- |------|--------|
189
- | Provider, context | `@howone/sdk/react` |
190
- | Client, OTP, OAuth | `@howone/sdk` |
191
- | App singleton | `@/lib/sdk` default export |
131
+ | Mistake | Correct pattern |
132
+ |---|---|
133
+ | Provider without `client` | Pass the exported composed singleton. |
134
+ | `auth="required"` on a public client | Let the client guard default to `none`, or explicitly use `auth="optional"`. |
135
+ | Custom login plus hosted redirect | `auth: 'custom'` and Provider `auth="none"`/derived guard. |
136
+ | Importing `ElementSelectorProvider` from `/react` | Import it from `@howone/sdk/devtools`. |
137
+ | Calling `useHowoneContext` above the Provider | Move the component below the root Provider. |
138
+ | Passing `theme`, `themeStorageKey`, or `forceTheme` | Configure the app's own ThemeProvider. |