howone 0.2.3 → 0.2.6
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/package.json +1 -1
- package/templates/vite/.howone/skills/howone/01-architect/01-app-generation.md +8 -7
- package/templates/vite/.howone/skills/howone/01-architect/02-manifest-codegen.md +121 -436
- package/templates/vite/.howone/skills/howone/03-ai-capabilities/04-workflow-operations.md +13 -4
- package/templates/vite/.howone/skills/howone/04-app-sdk/01-client-setup.md +94 -261
- package/templates/vite/.howone/skills/howone/04-app-sdk/02-entity-operations.md +85 -465
- package/templates/vite/.howone/skills/howone/04-app-sdk/03-auth.md +11 -7
- package/templates/vite/.howone/skills/howone/04-app-sdk/04-react-integration.md +84 -137
- package/templates/vite/.howone/skills/howone/04-app-sdk/05-file-upload.md +66 -273
- package/templates/vite/.howone/skills/howone/04-app-sdk/06-raw-http.md +72 -249
- package/templates/vite/.howone/skills/howone/04-app-sdk/07-ai-action-calls.md +135 -499
- package/templates/vite/.howone/skills/howone/04-app-sdk/08-ai-manifest-handoff.md +49 -196
- package/templates/vite/.howone/skills/howone/04-app-sdk/09-extension-boundaries.md +4 -4
- package/templates/vite/.howone/skills/howone/04-app-sdk/10-workflow-execute-sse.md +94 -61
- package/templates/vite/.howone/skills/howone/04-app-sdk/11-entity-data-access-patterns.md +4 -3
- package/templates/vite/.howone/skills/howone/SKILL.md +48 -8
- package/templates/vite/.howone/skills/howone/references/common-errors.md +27 -0
- package/templates/vite/.howone/skills/howone/references/version-evidence.md +47 -0
- package/templates/vite/.howone/skills/howone/scripts/verify-project.mjs +151 -0
- package/templates/vite/package.json +1 -1
- package/templates/vite/src/App.tsx +9 -5
- package/templates/vite/src/lib/sdk.ts +7 -5
- package/templates/vite/bun.lock +0 -1478
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Common SDK Errors
|
|
2
|
+
|
|
3
|
+
Use this reference when typecheck, build, authentication, entity access, upload, or AI execution
|
|
4
|
+
fails.
|
|
5
|
+
|
|
6
|
+
| Symptom | Cause | Fix |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| `AI_WORKFLOW_CONFIGURATION_ERROR` | Missing or non-UUID `workflowId` | Re-read `.howone/ai/manifest.json`; pass its exact workflow config UUID. There is no action-name fallback. |
|
|
9
|
+
| `AI_SCHEMA_CONFIGURATION_ERROR` | JSON Schema passed to `defineAiAction` | Convert manifest JSON Schema to a Zod schema. |
|
|
10
|
+
| `AI_SCHEMA_VALIDATION_ERROR` on output | Workflow `finalResult` disagrees with manifest output schema | Fix the workflow or contract. Do not add `.passthrough()` or make required fields optional. |
|
|
11
|
+
| `WorkflowExecutionError` | `run_error` or `credit_insufficient` terminal event | Inspect `error.outcome`, `error.result.errors`, and `error.runId`; show app-owned UI or retry only when appropriate. |
|
|
12
|
+
| `aiUrl` / `aiBaseUrl` is not accepted | EAX routing is environment-owned | Remove the URL override and set `env` to `local`, `dev`, or `prod`. |
|
|
13
|
+
| Request hits `/api/api/...` | Raw path included `/api` even though the base already ends in `/api` | Use a path such as `/custom/stats`, not `/api/custom/stats`. |
|
|
14
|
+
| `response.data` is undefined | `howone.raw.*` already returns response data | Use `const data = await howone.raw.get<T>(...)`. |
|
|
15
|
+
| Provider renders blank forever in a public app | Provider guard conflicts with client auth or no client was passed | Pass `<HowOneProvider client={howone}>`; its guard defaults to `howone.auth.guard`. |
|
|
16
|
+
| `useHowoneContext must be used within HowOneProvider` | Hook called outside the Provider | Keep one root Provider and move the hook below it. |
|
|
17
|
+
| `ElementSelectorProvider` import fails from `/react` | Devtools moved to a separate entry | Import it from `@howone/sdk/devtools`. |
|
|
18
|
+
| Entity method is absent at typecheck/runtime | Manifest access forbids that capability and contract wrappers pruned it | Use the allowed namespace/operation or change and re-sync the platform contract. Do not cast the method back. |
|
|
19
|
+
| Public query rejects a filter/sort/scope | Query violates `access.public` | Put filters under `where`; use only `allowedFilters`, `allowedSorts`, and every `requiredScopes` field. |
|
|
20
|
+
| Nested JSON keys unexpectedly changed | `caseDepth: 'deep'` was enabled | Prefer the default `caseDepth: 'top-level'` for business JSON. |
|
|
21
|
+
| `HowOneProtocolError` key collision | snake_case and camelCase keys normalize to the same key | Fix the backend response contract; the SDK refuses silent overwrite. |
|
|
22
|
+
| Upload has no progress updates | Transport did not provide total bytes | Keep a loading state; `onProgress` fires only when total size is available. |
|
|
23
|
+
| Batch upload loops/fails immediately | `concurrent` is not a positive integer | Use `concurrent: 1` or higher. |
|
|
24
|
+
| Core import pulls React or React is missing | Framework code imported from the wrong entry | Use `@howone/sdk` for core, `@howone/sdk/react` for React, and `@howone/sdk/devtools` for selector tooling. |
|
|
25
|
+
|
|
26
|
+
After a fix, run the project verifier, typecheck, and production build. A successful build alone does
|
|
27
|
+
not prove that workflow IDs or manifest access rules are correct.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# SDK Version Evidence
|
|
2
|
+
|
|
3
|
+
Use this reference before writing or repairing `@howone/sdk` bindings. HowOne app contracts and
|
|
4
|
+
SDK APIs change independently; verify both.
|
|
5
|
+
|
|
6
|
+
## Evidence Order
|
|
7
|
+
|
|
8
|
+
1. Read `.howone/database/manifest.json` and `.howone/ai/manifest.json` for project-specific names,
|
|
9
|
+
fields, access rules, schemas, and workflow UUIDs.
|
|
10
|
+
2. Read the app `package.json` for the requested `@howone/sdk` version.
|
|
11
|
+
3. Read `node_modules/@howone/sdk/package.json` for the version actually installed.
|
|
12
|
+
4. Inspect the installed version's public typings:
|
|
13
|
+
- `node_modules/@howone/sdk/dist/index.d.ts`
|
|
14
|
+
- `node_modules/@howone/sdk/dist/react.d.ts`
|
|
15
|
+
- `node_modules/@howone/sdk/dist/devtools.d.ts`
|
|
16
|
+
5. Use this Skill for the workflow and code-generation recipe.
|
|
17
|
+
6. Use official remote documentation only as supplemental evidence. Never let latest remote docs
|
|
18
|
+
override installed typings for an older app.
|
|
19
|
+
|
|
20
|
+
If steps 2 and 3 disagree, run the project's package manager install before coding. Do not repair
|
|
21
|
+
types by casting around a stale installation.
|
|
22
|
+
|
|
23
|
+
## Deterministic Check
|
|
24
|
+
|
|
25
|
+
From the app root, run:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
node .howone/skills/howone/scripts/verify-project.mjs
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Fix every reported error before final validation. Warnings identify missing evidence or code that
|
|
32
|
+
still relies on compatibility behavior.
|
|
33
|
+
|
|
34
|
+
## Lookup Patterns
|
|
35
|
+
|
|
36
|
+
Use `rg` against installed declarations instead of relying on remembered signatures:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
rg -n "CreateClientOptions|HowOneClientRuntime" node_modules/@howone/sdk/dist/index.d.ts
|
|
40
|
+
rg -n "defineAiAction|AiActionClient|WorkflowExecutionError" node_modules/@howone/sdk/dist/index.d.ts
|
|
41
|
+
rg -n "EntityClient|withEntityContract|withPublicEntities" node_modules/@howone/sdk/dist/index.d.ts
|
|
42
|
+
rg -n "HowOneProviderProps" node_modules/@howone/sdk/dist/react.d.ts
|
|
43
|
+
rg -n "ElementSelectorProviderProps" node_modules/@howone/sdk/dist/devtools.d.ts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
When declarations and this Skill disagree, use the installed declarations for signatures and this
|
|
47
|
+
Skill for platform ordering. Report the mismatch instead of silently inventing an adapter.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
const appRoot = path.resolve(process.argv[2] || process.cwd())
|
|
7
|
+
const errors = []
|
|
8
|
+
const warnings = []
|
|
9
|
+
const facts = []
|
|
10
|
+
|
|
11
|
+
function relative(filePath) {
|
|
12
|
+
return path.relative(appRoot, filePath) || '.'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function readText(filePath) {
|
|
16
|
+
return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readJson(filePath, required = false) {
|
|
20
|
+
const source = readText(filePath)
|
|
21
|
+
if (source === null) {
|
|
22
|
+
if (required) errors.push(`Missing ${relative(filePath)}`)
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(source)
|
|
27
|
+
} catch (error) {
|
|
28
|
+
errors.push(`Invalid JSON in ${relative(filePath)}: ${error.message}`)
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function dependencySpec(pkg, name) {
|
|
34
|
+
return (
|
|
35
|
+
pkg?.dependencies?.[name] ??
|
|
36
|
+
pkg?.devDependencies?.[name] ??
|
|
37
|
+
pkg?.peerDependencies?.[name] ??
|
|
38
|
+
null
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function collectWorkflowIds(value, output = []) {
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
for (const entry of value) collectWorkflowIds(entry, output)
|
|
45
|
+
return output
|
|
46
|
+
}
|
|
47
|
+
if (!value || typeof value !== 'object') return output
|
|
48
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
49
|
+
if (key === 'workflowId' && typeof entry === 'string') output.push(entry)
|
|
50
|
+
collectWorkflowIds(entry, output)
|
|
51
|
+
}
|
|
52
|
+
return output
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const packagePath = path.join(appRoot, 'package.json')
|
|
56
|
+
const pkg = readJson(packagePath, true)
|
|
57
|
+
const requested = dependencySpec(pkg, '@howone/sdk')
|
|
58
|
+
if (!requested) errors.push('package.json does not declare @howone/sdk')
|
|
59
|
+
else facts.push(`requested @howone/sdk: ${requested}`)
|
|
60
|
+
|
|
61
|
+
const installedPackagePath = path.join(appRoot, 'node_modules/@howone/sdk/package.json')
|
|
62
|
+
const installed = readJson(installedPackagePath)
|
|
63
|
+
if (!installed) {
|
|
64
|
+
errors.push('node_modules/@howone/sdk is missing; run the project package manager install')
|
|
65
|
+
} else {
|
|
66
|
+
facts.push(`installed @howone/sdk: ${installed.version}`)
|
|
67
|
+
const normalizedRequest = String(requested || '').replace(/^[~^]/, '')
|
|
68
|
+
if (/^\d/.test(normalizedRequest) && normalizedRequest !== installed.version) {
|
|
69
|
+
errors.push(`SDK version mismatch: package.json requests ${requested}, installed ${installed.version}`)
|
|
70
|
+
}
|
|
71
|
+
for (const declaration of ['index.d.ts', 'react.d.ts', 'devtools.d.ts']) {
|
|
72
|
+
const declarationPath = path.join(appRoot, 'node_modules/@howone/sdk/dist', declaration)
|
|
73
|
+
if (!fs.existsSync(declarationPath)) {
|
|
74
|
+
errors.push(`Installed SDK is missing dist/${declaration}`)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const databaseManifestPath = path.join(appRoot, '.howone/database/manifest.json')
|
|
80
|
+
const aiManifestPath = path.join(appRoot, '.howone/ai/manifest.json')
|
|
81
|
+
const databaseManifest = readJson(databaseManifestPath)
|
|
82
|
+
const aiManifest = readJson(aiManifestPath)
|
|
83
|
+
if (databaseManifest) facts.push(`database manifest: ${relative(databaseManifestPath)}`)
|
|
84
|
+
else warnings.push('No synced database manifest found')
|
|
85
|
+
if (aiManifest) {
|
|
86
|
+
facts.push(`AI manifest: ${relative(aiManifestPath)}`)
|
|
87
|
+
const workflowIds = collectWorkflowIds(aiManifest)
|
|
88
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
|
89
|
+
for (const workflowId of workflowIds) {
|
|
90
|
+
if (!uuid.test(workflowId)) errors.push(`AI manifest contains invalid workflowId: ${workflowId}`)
|
|
91
|
+
}
|
|
92
|
+
if (workflowIds.length === 0) warnings.push('AI manifest contains no workflowId values')
|
|
93
|
+
} else {
|
|
94
|
+
warnings.push('No synced AI manifest found')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const sdkSourcePath = path.join(appRoot, 'src/lib/sdk.ts')
|
|
98
|
+
const sdkSource = readText(sdkSourcePath)
|
|
99
|
+
if (sdkSource === null) {
|
|
100
|
+
warnings.push('src/lib/sdk.ts is missing')
|
|
101
|
+
} else {
|
|
102
|
+
facts.push(`SDK binding: ${relative(sdkSourcePath)}`)
|
|
103
|
+
if (/\b(?:aiUrl|aiBaseUrl)\s*:/.test(sdkSource)) {
|
|
104
|
+
errors.push('src/lib/sdk.ts overrides the EAX URL; use createClient env only')
|
|
105
|
+
}
|
|
106
|
+
if (/\bmode\s*:\s*['"](?:auto|standalone|embedded)['"]/.test(sdkSource)) {
|
|
107
|
+
errors.push('src/lib/sdk.ts uses the removed createClient mode option')
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const sourceRoot = path.join(appRoot, 'src')
|
|
112
|
+
if (fs.existsSync(sourceRoot)) {
|
|
113
|
+
const sourceFiles = []
|
|
114
|
+
const visit = (directory) => {
|
|
115
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
116
|
+
const entryPath = path.join(directory, entry.name)
|
|
117
|
+
if (entry.isDirectory()) visit(entryPath)
|
|
118
|
+
else if (/\.[cm]?[jt]sx?$/.test(entry.name)) sourceFiles.push(entryPath)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
visit(sourceRoot)
|
|
122
|
+
|
|
123
|
+
for (const filePath of sourceFiles) {
|
|
124
|
+
const source = readText(filePath) || ''
|
|
125
|
+
if (
|
|
126
|
+
/import\s*\{[^}]*\bElementSelectorProvider\b[^}]*\}\s*from\s*['"]@howone\/sdk\/react['"]/.test(
|
|
127
|
+
source,
|
|
128
|
+
)
|
|
129
|
+
) {
|
|
130
|
+
errors.push(`${relative(filePath)} imports ElementSelectorProvider from /react; use /devtools`)
|
|
131
|
+
}
|
|
132
|
+
if (/\burl\s*:\s*[`'"]\/api\//.test(source)) {
|
|
133
|
+
warnings.push(`${relative(filePath)} contains a raw /api/... path; the SDK REST base already includes /api`)
|
|
134
|
+
}
|
|
135
|
+
if (/<HowOneProvider\b(?![^>]*\bclient=)[^>]*>/.test(source)) {
|
|
136
|
+
errors.push(`${relative(filePath)} renders HowOneProvider without client={howone}`)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(`HowOne project verification: ${appRoot}`)
|
|
142
|
+
for (const fact of facts) console.log(` ok: ${fact}`)
|
|
143
|
+
for (const warning of [...new Set(warnings)]) console.warn(` warning: ${warning}`)
|
|
144
|
+
for (const error of [...new Set(errors)]) console.error(` error: ${error}`)
|
|
145
|
+
|
|
146
|
+
if (errors.length > 0) {
|
|
147
|
+
console.error(`Verification failed with ${new Set(errors).size} error(s).`)
|
|
148
|
+
process.exit(1)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log('Verification passed.')
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@base-ui/react": "^1.4.1",
|
|
16
16
|
"@fontsource-variable/inter": "^5.2.8",
|
|
17
|
-
"@howone/sdk": "2.0.0-beta.
|
|
17
|
+
"@howone/sdk": "2.0.0-beta.33",
|
|
18
18
|
"@tailwindcss/vite": "^4.2.1",
|
|
19
19
|
"class-variance-authority": "^0.7.1",
|
|
20
20
|
"clsx": "^2.1.1",
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import howone from '@/lib/sdk'
|
|
2
|
+
import { ElementSelectorProvider } from '@howone/sdk/devtools'
|
|
1
3
|
import { HowOneProvider } from '@howone/sdk/react'
|
|
2
4
|
|
|
3
5
|
function App() {
|
|
4
6
|
return (
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
<ElementSelectorProvider>
|
|
8
|
+
<HowOneProvider client={howone} brand="visible">
|
|
9
|
+
<div className="App">
|
|
10
|
+
<h1>Hello HowOne</h1>
|
|
11
|
+
</div>
|
|
12
|
+
</HowOneProvider>
|
|
13
|
+
</ElementSelectorProvider>
|
|
10
14
|
)
|
|
11
15
|
}
|
|
12
16
|
|
|
@@ -12,17 +12,19 @@ const client = createClient({
|
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
export const entities = defineEntities({
|
|
15
|
-
//
|
|
16
|
-
//
|
|
15
|
+
// Generate bindings from .howone/database/manifest.json.
|
|
16
|
+
// Wrap each client.entity(...) with withEntityContract(..., entityDefinition).
|
|
17
|
+
// Generate manifest-approved public bindings separately with
|
|
18
|
+
// definePublicEntities + withPublicEntityContract + withPublicEntities.
|
|
17
19
|
})
|
|
18
20
|
|
|
19
21
|
export const ai = defineAiActions({
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
+
// Generate bindings from .howone/ai/manifest.json.
|
|
23
|
+
// Import z from "zod" and convert manifest JSON Schema before using defineAiAction.
|
|
22
24
|
// Do not paste JSON Schema objects from .howone/ai/manifest.json here directly.
|
|
23
25
|
// With outputSchema configured, howone.ai.<action>.run() returns the validated finalResult payload.
|
|
24
26
|
// generateImage: defineAiAction("generateImage", {
|
|
25
|
-
// workflowId: "<
|
|
27
|
+
// workflowId: "<exact-manifest-uuid>", // required; there is no action-name fallback
|
|
26
28
|
// inputSchema: generateImageInputSchema,
|
|
27
29
|
// outputSchema: generateImageOutputSchema,
|
|
28
30
|
// }),
|