@frontera-sdk/cli 1.53.23 → 1.53.24
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 +4 -4
- package/src/api/knowledge-base-api.ts +230 -0
- package/src/commands/knowledge/index-commands.ts +6 -6
- package/src/commands/knowledge-base/index-commands.ts +328 -0
- package/src/commands/local/check.ts +12 -0
- package/src/commands/local/dev.ts +64 -2
- package/src/commands/local/index-commands.ts +34 -10
- package/src/commands/local/init.ts +11 -2
- package/src/commands/local/schema.ts +1 -1
- package/src/commands/local/sync-request.ts +135 -29
- package/src/commands/local/sync.ts +40 -25
- package/src/commands/registry.ts +4 -1
- package/src/commands/types.ts +6 -0
- package/src/flag-help.ts +10 -4
- package/src/forge/touches.ts +29 -0
- package/src/local/local-applied.ts +28 -0
- package/src/local/local-build-summary.ts +1 -0
- package/src/local/local-component-health.ts +7 -2
- package/src/local/local-dev-references.ts +6 -2
- package/src/local/local-dev-sync.ts +43 -4
- package/src/local/local-docker.ts +16 -1
- package/src/local/local-env.ts +19 -0
- package/src/local/local-file-checks.ts +56 -8
- package/src/local/local-knowledge-base-apply.ts +714 -0
- package/src/local/local-knowledge-base-files.ts +195 -0
- package/src/local/local-knowledge-base-pull.ts +458 -0
- package/src/local/local-knowledge-base-sources-off.ts +67 -0
- package/src/local/local-person-steps.ts +2 -1
- package/src/local/local-personas.ts +64 -7
- package/src/local/local-portability.ts +27 -0
- package/src/local/local-ports.ts +4 -2
- package/src/local/local-project-secrets.ts +30 -5
- package/src/local/local-scaffold.ts +2 -0
- package/src/local/local-schema-check.ts +3 -1
- package/src/local/local-schemas.ts +11 -1
- package/src/local/local-search-index.ts +92 -0
- package/src/local/local-secrets.ts +7 -2
- package/src/local/local-seed.ts +17 -0
- package/src/local/local-state.ts +7 -5
- package/src/local/local-studio-server.ts +3 -3
- package/src/local/local-supervisor.ts +45 -8
- package/src/local/local-sync-link.ts +16 -0
- package/src/local/local-sync-names.ts +10 -1
- package/src/local/local-sync-plan.ts +44 -5
- package/src/local/local-sync-push.ts +165 -34
- package/src/local/local-sync-refs.ts +3 -1
- package/src/local/local-sync-remote.ts +44 -12
- package/src/local/local-sync-snapshot.ts +156 -12
- package/src/main.ts +13 -0
- package/src/mcp/mcp-server.ts +3 -0
- package/src/mcp/tools/docs-tools.ts +1 -1
- package/src/mcp/tools/knowledge-base-tools.ts +85 -0
|
@@ -65,6 +65,17 @@ export function dockerAvailable(): { ok: true } | { ok: false; reason: string }
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/** The memory Docker's engine has for all its containers, in bytes; null when it does not say. */
|
|
69
|
+
export function dockerMemoryBytes(): number | null {
|
|
70
|
+
try {
|
|
71
|
+
const r = docker(['info', '--format', '{{.MemTotal}}'])
|
|
72
|
+
const bytes = Number(r.out)
|
|
73
|
+
return r.ok && r.out && Number.isFinite(bytes) && bytes > 0 ? bytes : null
|
|
74
|
+
} catch {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
68
79
|
export function imagePresent(image: string): boolean {
|
|
69
80
|
return docker(['image', 'inspect', image, '--format', '{{.Id}}']).ok
|
|
70
81
|
}
|
|
@@ -242,13 +253,17 @@ export const dbVolumeName = (projectId: string) => `frontera-${projectId}-db`
|
|
|
242
253
|
/** A local runtime's file storage volume, by its id. */
|
|
243
254
|
export const storageVolumeName = (projectId: string) => `frontera-${projectId}-storage`
|
|
244
255
|
|
|
256
|
+
/** A local runtime's search index volume, by its id. */
|
|
257
|
+
export const searchIndexVolumeName = (projectId: string) => `frontera-${projectId}-search-index`
|
|
258
|
+
|
|
245
259
|
/**
|
|
246
260
|
* Delete a local runtime's containers and volumes, by its id: the containers
|
|
247
|
-
* labelled for it, running or not, and its database and
|
|
261
|
+
* labelled for it, running or not, and its database, storage and search index volumes. Its
|
|
248
262
|
* data is gone afterwards.
|
|
249
263
|
*/
|
|
250
264
|
export function removeRuntimeResources(projectId: string): void {
|
|
251
265
|
for (const name of containersForProject(projectId)) removeContainer(name)
|
|
252
266
|
removeVolume(dbVolumeName(projectId))
|
|
253
267
|
removeVolume(storageVolumeName(projectId))
|
|
268
|
+
removeVolume(searchIndexVolumeName(projectId))
|
|
254
269
|
}
|
package/src/local/local-env.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface LocalConfig {
|
|
|
37
37
|
/** Where image mode pulls the platform images from: a registry host and namespace. */
|
|
38
38
|
registry?: string
|
|
39
39
|
exclude: string[]
|
|
40
|
+
/** Components that run only when asked (`--with`): the search index, in a project without `knowledge-base/`. */
|
|
41
|
+
with?: string[]
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
const secretsFile = (project: LocalProject) => join(project.stateDir, 'secrets.json')
|
|
@@ -116,6 +118,7 @@ export const SERVICE_OUTBOUND = [
|
|
|
116
118
|
'S3_ENDPOINT',
|
|
117
119
|
'INNGEST_BASE_URL',
|
|
118
120
|
'AUTOMATION_RUNNER_URL',
|
|
121
|
+
'KNOWLEDGE_SEARCH_URL',
|
|
119
122
|
] as const
|
|
120
123
|
|
|
121
124
|
/** The service's settings for a hosted key custodian (OpenBao / Vault transit), blanked on the local runtime. */
|
|
@@ -150,6 +153,8 @@ export function serviceEnv(args: {
|
|
|
150
153
|
runnerEnabled: boolean
|
|
151
154
|
jobsEnabled: boolean
|
|
152
155
|
storageEnabled: boolean
|
|
156
|
+
/** The search index runs: the Knowledge app is on. */
|
|
157
|
+
searchIndexEnabled?: boolean
|
|
153
158
|
/** Point the service's OpenAI client at the keyless local embeddings (no OpenAI key stored). */
|
|
154
159
|
keylessEmbeddings?: boolean
|
|
155
160
|
/** The customer documentation publication the service serves to coding agents. */
|
|
@@ -225,9 +230,23 @@ export function serviceEnv(args: {
|
|
|
225
230
|
if (args.keylessEmbeddings) env.OPENAI_BASE_URL = `http://127.0.0.1:${ports.embeddings}/v1`
|
|
226
231
|
if (args.runnerEnabled) env.AUTOMATION_RUNNER_URL = `http://127.0.0.1:${ports.runner}`
|
|
227
232
|
if (args.storageEnabled) Object.assign(env, storageEnv(secrets, ports))
|
|
233
|
+
if (args.searchIndexEnabled) Object.assign(env, searchIndexEnv(ports))
|
|
228
234
|
return env
|
|
229
235
|
}
|
|
230
236
|
|
|
237
|
+
/**
|
|
238
|
+
* The Knowledge app: its search index, and its own bucket in the runtime's
|
|
239
|
+
* object storage, created on first use. The service refuses a Knowledge bucket
|
|
240
|
+
* that is the public uploads bucket (`S3_BUCKET`).
|
|
241
|
+
*/
|
|
242
|
+
export function searchIndexEnv(ports: PortBlock): Record<string, string> {
|
|
243
|
+
return {
|
|
244
|
+
KNOWLEDGE_SEARCH_URL: `http://127.0.0.1:${ports['search-index']}`,
|
|
245
|
+
KNOWLEDGE_BUCKET: 'frontera-local-knowledge',
|
|
246
|
+
KNOWLEDGE_BUCKET_CREATE: 'true',
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
231
250
|
/**
|
|
232
251
|
* Object storage (RustFS): Function bundles, uploads, managed datasets. Buckets
|
|
233
252
|
* are created on first write. Managed datasets also need their catalog
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
1
|
+
import { readdirSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
3
3
|
|
|
4
4
|
import matter from 'gray-matter'
|
|
5
5
|
|
|
6
|
+
import { KNOWLEDGE_BASE_SOURCES_DIR } from './local-knowledge-base-files'
|
|
7
|
+
import { agentPicks, hasKnowledgeBaseFolder } from './local-knowledge-base-apply'
|
|
6
8
|
import { checkAgainstSchema, type SchemaProblem } from './local-schema-check'
|
|
7
9
|
import { cliSchema, type SchemaKind } from './local-schemas'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
|
-
* A Chat App file, a knowledge base's `knowledge.json
|
|
11
|
-
*
|
|
12
|
-
* before `local dev` applies them.
|
|
12
|
+
* A Chat App file, a knowledge base's `knowledge.json`, a skill's `SKILL.md`
|
|
13
|
+
* front matter, and the Knowledge app's files (`knowledge-base/`), checked
|
|
14
|
+
* against `frontera local schema <kind>` before `local dev` applies them.
|
|
13
15
|
*
|
|
14
16
|
* The platform keeps only the keys it knows and drops the rest without a word
|
|
15
17
|
* (a Chat App's `experience` block applied "✓" and was gone), so a key the
|
|
@@ -82,10 +84,53 @@ export function skillFileChecks(file: string, text: string): FileChecks {
|
|
|
82
84
|
return { errors, warnings }
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
type JsonSchema = Record<string, unknown>
|
|
88
|
+
|
|
89
|
+
const refused = (file: string, problems: SchemaProblem[]) => problems.map((p) => `${file} ${p.path || '(the file)'}: ${p.message}`)
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A Knowledge app base: its knowledge-base.json, and each source file against
|
|
93
|
+
* the service's format when `local start` kept it. Every key in these files
|
|
94
|
+
* means something (a workspace base's `sharing` would share nothing), so any
|
|
95
|
+
* problem refuses the base.
|
|
96
|
+
*/
|
|
97
|
+
export function knowledgeBaseFileChecks(root: string, file: string, doc: unknown, sourceSchema: JsonSchema | null): FileChecks {
|
|
98
|
+
const errors = refused(file, checkAgainstSchema(doc, cliSchema('knowledge-base')!))
|
|
99
|
+
const sources = join(dirname(file), KNOWLEDGE_BASE_SOURCES_DIR)
|
|
100
|
+
let names: string[] = []
|
|
101
|
+
try {
|
|
102
|
+
names = readdirSync(join(root, sources)).filter((n) => n.endsWith('.json')).sort()
|
|
103
|
+
} catch {
|
|
104
|
+
// No sources/ folder: a base without API sources.
|
|
105
|
+
}
|
|
106
|
+
for (const name of names) {
|
|
107
|
+
const rel = join(sources, name)
|
|
108
|
+
try {
|
|
109
|
+
const spec = JSON.parse(readFileSync(join(root, rel), 'utf8')) as unknown
|
|
110
|
+
if (sourceSchema) errors.push(...refused(rel, checkAgainstSchema(spec, sourceSchema)))
|
|
111
|
+
} catch (err) {
|
|
112
|
+
errors.push(`${rel}: is not valid JSON: ${(err as Error).message}`)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { errors, warnings: [] }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** An Agent's picks (`knowledgeBases`): each names a base folder the project has, or the Agent goes without it. */
|
|
119
|
+
export function agentPickChecks(root: string, file: string, doc: unknown): FileChecks {
|
|
120
|
+
const warnings = (agentPicks(doc) ?? [])
|
|
121
|
+
.filter((ref) => !hasKnowledgeBaseFolder(root, ref))
|
|
122
|
+
.map((ref) => ` warning: ${file} knowledgeBases: ${ref} is not a knowledge base in this project (${ref}/knowledge-base.json), so the Agent goes without it`)
|
|
123
|
+
return { errors: [], warnings }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The checks for one target's file, read from the project; nothing for a kind
|
|
128
|
+
* these do not cover. `keptSchema` reads a service-owned format `local start`
|
|
129
|
+
* kept (a knowledge base's source); without it, the service checks those.
|
|
130
|
+
*/
|
|
131
|
+
export function projectFileChecks(root: string, target: { kind: string; file?: string }, keptSchema: (kind: SchemaKind) => JsonSchema | null = () => null): FileChecks {
|
|
87
132
|
const none = { errors: [], warnings: [] }
|
|
88
|
-
if (!target.file || !['chat-app', 'knowledge', 'skill'].includes(target.kind)) return none
|
|
133
|
+
if (!target.file || !['chat-app', 'knowledge', 'skill', 'knowledge-base', 'knowledge-base-settings', 'agent'].includes(target.kind)) return none
|
|
89
134
|
let text: string
|
|
90
135
|
try {
|
|
91
136
|
text = readFileSync(join(root, target.file), 'utf8')
|
|
@@ -100,5 +145,8 @@ export function projectFileChecks(root: string, target: { kind: string; file?: s
|
|
|
100
145
|
// Unreadable: the apply says so with the file's own error.
|
|
101
146
|
return none
|
|
102
147
|
}
|
|
148
|
+
if (target.kind === 'knowledge-base') return knowledgeBaseFileChecks(root, target.file, doc, keptSchema('knowledge-base-source'))
|
|
149
|
+
if (target.kind === 'knowledge-base-settings') return { errors: refused(target.file, checkAgainstSchema(doc, cliSchema('knowledge-base-settings')!)), warnings: [] }
|
|
150
|
+
if (target.kind === 'agent') return agentPickChecks(root, target.file, doc)
|
|
103
151
|
return target.kind === 'chat-app' ? chatAppFileChecks(target.file, doc) : knowledgeFileChecks(target.file, doc)
|
|
104
152
|
}
|