@frontera-sdk/cli 1.52.39 → 1.52.41
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 +9 -0
- package/package.json +6 -4
- package/src/adopt.ts +6 -2
- package/src/api/apps-api.ts +86 -4
- package/src/api/blueprint-authoring-api.ts +35 -11
- package/src/api/dataset-api.ts +29 -0
- package/src/api/governed-action-api.ts +90 -2
- package/src/api/platform-api.ts +87 -2
- package/src/api/workflow-api.ts +33 -2
- package/src/app-contract.ts +156 -0
- package/src/app-lock.ts +125 -0
- package/src/args.ts +14 -3
- package/src/auth-verify.ts +10 -0
- package/src/automation-template.ts +12 -1
- package/src/blueprint/action-rules.ts +516 -0
- package/src/blueprint/action-semantic-fit.ts +205 -0
- package/src/blueprint/compile.ts +51 -2
- package/src/blueprint/diff.ts +34 -2
- package/src/blueprint/metric-query.ts +215 -0
- package/src/blueprint/metric-rules.ts +122 -0
- package/src/blueprint/object-sets.ts +116 -0
- package/src/blueprint/ontology-edit-plan.ts +49 -5
- package/src/blueprint/projection.ts +24 -1
- package/src/blueprint/scaffold.ts +84 -42
- package/src/commands/action/available.ts +1 -1
- package/src/commands/action/criteria-refusal.ts +84 -0
- package/src/commands/action/decide.ts +5 -2
- package/src/commands/action/deploy.ts +13 -4
- package/src/commands/action/list.ts +134 -19
- package/src/commands/action/requests.ts +34 -6
- package/src/commands/action/submit.ts +28 -13
- package/src/commands/agent/draft-changes.ts +1 -1
- package/src/commands/agent/index-commands.ts +69 -7
- package/src/commands/agent/resolve.ts +2 -0
- package/src/commands/app/checks.ts +44 -0
- package/src/commands/app/deploy.ts +243 -60
- package/src/commands/app/deployments.ts +97 -0
- package/src/commands/app/dev.ts +103 -11
- package/src/commands/app/init.ts +36 -3
- package/src/commands/app/promote.ts +45 -19
- package/src/commands/app/pull.ts +3 -1
- package/src/commands/app/rollback.ts +46 -0
- package/src/commands/app/sdk.ts +54 -8
- package/src/commands/app/unpin.ts +27 -0
- package/src/commands/app/versions.ts +1 -1
- package/src/commands/automation/bindings.ts +4 -2
- package/src/commands/automation/build-entry.ts +4 -1
- package/src/commands/automation/index-commands.ts +3 -1
- package/src/commands/automation/pull.ts +6 -1
- package/src/commands/automation/run.ts +34 -9
- package/src/commands/blueprint/bind.ts +2 -1
- package/src/commands/blueprint/declarative.ts +332 -32
- package/src/commands/blueprint/editable.ts +3 -3
- package/src/commands/blueprint/get.ts +6 -2
- package/src/commands/blueprint/grants.ts +64 -10
- package/src/commands/blueprint/metric.ts +108 -0
- package/src/commands/blueprint/object-sets.ts +71 -0
- package/src/commands/blueprint/query.ts +155 -31
- package/src/commands/dataset/index-commands.ts +156 -7
- package/src/commands/knowledge/index-commands.ts +122 -20
- package/src/commands/local/check.ts +657 -44
- package/src/commands/local/data.ts +33 -12
- package/src/commands/local/dev.ts +876 -89
- package/src/commands/local/docs-mcp.ts +10 -18
- package/src/commands/local/index-commands.ts +300 -63
- package/src/commands/local/init.ts +18 -5
- package/src/commands/local/key.ts +0 -3
- package/src/commands/local/providers.ts +0 -7
- package/src/commands/local/remove.ts +116 -0
- package/src/commands/local/schema.ts +3 -4
- package/src/commands/local/sdk-refresh.ts +59 -0
- package/src/commands/local/snapshot.ts +100 -0
- package/src/commands/local/sync-request.ts +321 -14
- package/src/commands/local/sync.ts +103 -6
- package/src/commands/mcp/serve.ts +34 -0
- package/src/commands/registry.ts +54 -10
- package/src/commands/types.ts +29 -0
- package/src/commands/workflow/archive.ts +46 -0
- package/src/commands/workflow/get.ts +2 -1
- package/src/commands/workflow/index-commands.ts +5 -0
- package/src/commands/workflow/push.ts +49 -2
- package/src/commands/workflow/run.ts +65 -7
- package/src/commands/workflow/runs.ts +9 -6
- package/src/commands/workflow/webhook-test.ts +166 -0
- package/src/config.ts +43 -6
- package/src/dev-broker.ts +52 -0
- package/src/exit.ts +103 -4
- package/src/flag-help.ts +77 -17
- package/src/forge/claude-commands.ts +11 -6
- package/src/forge/claude-skills.ts +4 -4
- package/src/forge/touches.ts +15 -12
- package/src/function-outside-imports.ts +89 -0
- package/src/harness.ts +42 -24
- package/src/heal.ts +113 -2
- package/src/kit.ts +1 -1
- package/src/local/fix-words.ts +12 -0
- package/src/local/function-bundle-recovery.ts +775 -0
- package/src/local/local-actions.ts +103 -10
- package/src/local/local-agent-action-grants.ts +210 -0
- package/src/local/local-agent-checks.ts +232 -0
- package/src/local/local-agent-grants.ts +29 -7
- package/src/local/local-agent-lean-plugins.ts +68 -0
- package/src/local/local-agent-missing-tools.ts +68 -0
- package/src/local/local-agent-name.ts +27 -0
- package/src/local/local-app-code.ts +191 -0
- package/src/local/local-app-dev-files.ts +186 -0
- package/src/local/local-app-dev-session.ts +88 -0
- package/src/local/local-app-lock-install.ts +114 -0
- package/src/local/local-app-pulled.ts +167 -0
- package/src/local/local-app-restore.ts +111 -0
- package/src/local/local-app-runtime.ts +68 -0
- package/src/local/local-app-sdk-paths.ts +149 -0
- package/src/local/local-app-sdk.ts +509 -0
- package/src/local/local-applied.ts +212 -22
- package/src/local/local-apply-history.ts +84 -0
- package/src/local/local-apply-summary.ts +86 -0
- package/src/local/local-apply-watchdog.ts +95 -0
- package/src/local/local-apps.ts +181 -8
- package/src/local/local-blueprint-errors.ts +198 -0
- package/src/local/local-blueprint-outline.ts +31 -12
- package/src/local/local-blueprint-schemas.ts +582 -0
- package/src/local/local-build-inputs.ts +48 -0
- package/src/local/local-build-summary.ts +495 -28
- package/src/local/local-case-evidence.ts +120 -0
- package/src/local/local-case-not-run.ts +153 -0
- package/src/local/local-chat-apps.ts +93 -16
- package/src/local/local-check-inherited.ts +83 -0
- package/src/local/local-cities.ts +89 -0
- package/src/local/local-component-health.ts +119 -0
- package/src/local/local-control.ts +34 -7
- package/src/local/local-db-snapshot.ts +348 -0
- package/src/local/local-dev-references.ts +347 -36
- package/src/local/local-dev-sync.ts +141 -12
- package/src/local/local-docker.ts +35 -1
- package/src/local/local-docs-search.ts +1 -1
- package/src/local/local-docs.ts +281 -2
- package/src/local/local-empty-workflows.ts +92 -0
- package/src/local/local-env.ts +16 -1
- package/src/local/local-file-checks.ts +104 -0
- package/src/local/local-function-ids.ts +141 -0
- package/src/local/local-function-recovery.ts +141 -0
- package/src/local/local-function-state.ts +87 -0
- package/src/local/local-keys.ts +7 -5
- package/src/local/local-knowledge-documents.ts +1 -1
- package/src/local/local-knowledge-ingestion.ts +225 -0
- package/src/local/local-knowledge-uploads.ts +217 -0
- package/src/local/local-lessons.ts +1 -1
- package/src/local/local-library.ts +1 -1
- package/src/local/local-media-placeholders.ts +182 -0
- package/src/local/local-media-sets.ts +90 -10
- package/src/local/local-model-parameters.ts +89 -0
- package/src/local/local-object-sets.ts +18 -6
- package/src/local/local-operating.ts +35 -1
- package/src/local/local-org-settings.ts +206 -8
- package/src/local/local-orphans.ts +321 -0
- package/src/local/local-personas.ts +296 -0
- package/src/local/local-portability.ts +143 -18
- package/src/local/local-ports.ts +99 -7
- package/src/local/local-project-notices.ts +234 -0
- package/src/local/local-project-secrets.ts +133 -0
- package/src/local/local-project.ts +12 -0
- package/src/local/local-provider-refusal.ts +66 -0
- package/src/local/local-request-evidence.ts +112 -0
- package/src/local/local-runtime-health.ts +123 -0
- package/src/local/local-runtime-history.ts +50 -0
- package/src/local/local-runtime-target.ts +42 -0
- package/src/local/local-sample-data.ts +141 -0
- package/src/local/local-sample-remake.ts +138 -0
- package/src/local/local-scaffold.ts +336 -53
- package/src/local/local-schema-check.ts +148 -0
- package/src/local/local-schemas.ts +742 -25
- package/src/local/local-secret-carry.ts +107 -0
- package/src/local/local-secrets.ts +41 -11
- package/src/local/local-seed.ts +102 -26
- package/src/local/local-stand-ins.ts +3 -3
- package/src/local/local-state.ts +43 -2
- package/src/local/local-studio-server.ts +170 -7
- package/src/local/local-supervisor.ts +44 -6
- package/src/local/local-sync-merge.ts +1 -1
- package/src/local/local-sync-names.ts +2 -0
- package/src/local/local-sync-plan.ts +19 -8
- package/src/local/local-sync-push.ts +15 -12
- package/src/local/local-sync-refs.ts +49 -3
- package/src/local/local-sync-remote.ts +349 -38
- package/src/local/local-sync-snapshot.ts +18 -13
- package/src/local/local-synthetic-data.ts +2116 -99
- package/src/local/local-test-baseline.ts +113 -0
- package/src/local/local-test-cases.ts +76 -1
- package/src/local/local-test-runner.ts +582 -91
- package/src/local/local-test-runs.ts +13 -5
- package/src/local/local-vendored-sdk.ts +183 -0
- package/src/local/local-workflow-state.ts +16 -3
- package/src/local/local-workflow-tests.ts +482 -0
- package/src/local/local-workspace-grants.ts +81 -0
- package/src/main.ts +50 -9
- package/src/mcp/a11y-scan.ts +55 -0
- package/src/mcp/browser/ax-snapshot.ts +181 -0
- package/src/mcp/browser/credentials.ts +78 -0
- package/src/mcp/browser/frames.ts +336 -0
- package/src/mcp/browser/headless-session.ts +413 -0
- package/src/mcp/browser/headless-tab.ts +575 -0
- package/src/mcp/browser/keys.ts +70 -0
- package/src/mcp/browser/page-scripts.ts +129 -0
- package/src/mcp/browser/wait.ts +73 -0
- package/src/mcp/forge-link.ts +126 -0
- package/src/mcp/headless.ts +199 -0
- package/src/mcp/mcp-apps.ts +435 -0
- package/src/mcp/mcp-chat.ts +226 -0
- package/src/mcp/mcp-cli.ts +53 -0
- package/src/mcp/mcp-protocol.ts +122 -0
- package/src/mcp/mcp-runtime.ts +113 -0
- package/src/mcp/mcp-server.ts +108 -0
- package/src/mcp/mcp-traces.ts +409 -0
- package/src/mcp/page-observation.ts +193 -0
- package/src/mcp/tools/agent-tools.ts +700 -0
- package/src/mcp/tools/app-tools.ts +241 -0
- package/src/mcp/tools/browser-tools.ts +310 -0
- package/src/mcp/tools/docs-tools.ts +32 -0
- package/src/mcp/tools/project-tools.ts +205 -0
- package/src/mcp/tools/test-tools.ts +339 -0
- package/src/mcp/tools/workflow-tools.ts +611 -0
- package/src/output.ts +1 -1
- package/src/pack.ts +8 -3
- package/src/packaging.ts +1 -1
- package/src/project-bootstrap.ts +107 -4
- package/src/project-context.ts +18 -1
- package/src/project.ts +27 -2
- package/src/provenance.ts +25 -0
- package/src/render-evidence.ts +65 -7
- package/src/scopes.ts +1 -1
- package/src/template.ts +14 -8
- package/src/templates/next-app-files.ts +35 -6
- package/src/templates/next-skills.ts +61 -27
- package/src/vendor/kit-assets.json +6 -6
- package/src/vendor/sdk-sources.json +17 -14
package/src/harness.ts
CHANGED
|
@@ -25,6 +25,16 @@ description: Use when reading or changing anything on the Frontera platform from
|
|
|
25
25
|
never out of date — read it instead of guessing, and instead of trusting this
|
|
26
26
|
file for syntax.
|
|
27
27
|
|
|
28
|
+
## In a project with the Frontera MCP server
|
|
29
|
+
|
|
30
|
+
A project made by \`frontera local\` registers the \`frontera\` MCP server
|
|
31
|
+
(\`.mcp.json\`). When its tools are offered (\`mcp__frontera__*\` in Claude
|
|
32
|
+
Code), use them for the local loop instead of the shell: \`status\`,
|
|
33
|
+
\`apply\`, \`test_run\`, \`logs\`, \`read_file_schema\` and the documentation
|
|
34
|
+
tools. The person approves the server once, where every shell command can
|
|
35
|
+
ask again. Run \`frontera local …\` only when the tools aren't offered, or
|
|
36
|
+
for a step that has no tool (\`frontera local start\`, \`frontera local check\`).
|
|
37
|
+
|
|
28
38
|
## Preflight: know which customer you are about to change
|
|
29
39
|
|
|
30
40
|
\`\`\`bash
|
|
@@ -63,7 +73,10 @@ Two fields decide whether a call can work at all:
|
|
|
63
73
|
key: set \`FRONTERA_TOKEN\` to the session token and \`FRONTERA_API_URL\` (or
|
|
64
74
|
pass \`--api-url\`). The CLI refuses an \`sk-ws-\`/\`sk-org-\` key on those
|
|
65
75
|
before sending anything; \`frontera login\` stores keys only, so it cannot
|
|
66
|
-
supply the session.
|
|
76
|
+
supply the session. On a project's local runtime, never fetch a person's
|
|
77
|
+
session: \`--as <persona>\` runs \`action available\`, \`submit\`,
|
|
78
|
+
\`approve\` and \`reject\` as a local persona (\`--as submitter\`,
|
|
79
|
+
\`--as <approver>\`; \`frontera local status\` lists them).
|
|
67
80
|
- \`available\` — \`false\` means the verb is named but unbuilt, and
|
|
68
81
|
\`unavailableReason\` says why. Pick another route rather than retrying.
|
|
69
82
|
|
|
@@ -126,7 +139,7 @@ Pass \`--json\` for machine-readable output. The contract:
|
|
|
126
139
|
| 0 | success | continue |
|
|
127
140
|
| 1 | transient or remote failure | retry, then report |
|
|
128
141
|
| 2 | usage, input, or profile selection | fix the command |
|
|
129
|
-
| 3 | conflict — someone changed it first | re-
|
|
142
|
+
| 3 | conflict or refusal — someone changed it first, or the record's state refuses it | re-read, change the input or the record, then send it again; never retry it unchanged |
|
|
130
143
|
| 4 | auth or permission | different credential, or ask an admin |
|
|
131
144
|
|
|
132
145
|
Errors carry a \`hint\` naming the next command. Read it.
|
|
@@ -192,8 +205,9 @@ frontera blueprint instance <apiName> <pk>
|
|
|
192
205
|
app or agent sees at runtime. Anything absent here is absent for them too.
|
|
193
206
|
|
|
194
207
|
\`--where\` takes \`<property>:<value>\` or \`<property>:<op>:<value>\` for one
|
|
195
|
-
condition
|
|
196
|
-
|
|
208
|
+
condition, and \`<property>:in:<a>,<b>\` (also \`notIn\`, \`between\`) for a list;
|
|
209
|
+
repeat it for conditions that must all hold. Pass a JSON where-node for an
|
|
210
|
+
or/not, or \`--file\` a whole query document for unions and link traversals.
|
|
197
211
|
|
|
198
212
|
## Configuring an agent
|
|
199
213
|
|
|
@@ -239,14 +253,13 @@ means someone else changed it: re-read and retry, never force.
|
|
|
239
253
|
## Building an app
|
|
240
254
|
|
|
241
255
|
\`\`\`bash
|
|
242
|
-
frontera app init <name> #
|
|
243
|
-
|
|
256
|
+
frontera app init <name> # Next.js static export — installs with public npm only
|
|
257
|
+
cd <name>
|
|
244
258
|
frontera blueprint list # what data this workspace actually has
|
|
245
|
-
frontera app
|
|
246
|
-
bun run
|
|
247
|
-
|
|
248
|
-
frontera app
|
|
249
|
-
frontera app promote <version>
|
|
259
|
+
frontera app dev # the App against real data, with a short-lived token
|
|
260
|
+
bun run check # typecheck, tests and build: the gate
|
|
261
|
+
frontera app deploy --build # build, then a preview deployment with its link
|
|
262
|
+
frontera app promote <deployment>
|
|
250
263
|
\`\`\`
|
|
251
264
|
|
|
252
265
|
Read \`frontera blueprint list\` and \`frontera blueprint get <apiName>\` before
|
|
@@ -254,19 +267,24 @@ designing against data. What they return is the slice this workspace is granted,
|
|
|
254
267
|
which is exactly what the app will be able to query at runtime — so anything
|
|
255
268
|
absent there will be absent for the app too.
|
|
256
269
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
The SDK depends on how the App was made:
|
|
271
|
+
|
|
272
|
+
- **Next.js (the default).** \`@frontera-sdk/core\` and \`@frontera-sdk/blueprint\`
|
|
273
|
+
(and \`@frontera-sdk/agents\` when used) are public npm packages in
|
|
274
|
+
\`package.json\`, kept at the release \`frontera app init\` wrote and updated
|
|
275
|
+
with the package manager. Never copy SDK source into \`src/\`. Open the App
|
|
276
|
+
through \`frontera app dev\`: it serves on \`http://127.0.0.1:<port>\` and
|
|
277
|
+
hands the page its session; never write that session into \`.env\`.
|
|
278
|
+
- **Vite (legacy, \`--framework react\`).** \`src/frontera/\` is the SDK, copied
|
|
279
|
+
in as source. Read it; never edit it, never rewrite an \`@frontera-sdk/…\`
|
|
280
|
+
import to a relative path, and never add \`@frontera-sdk/*\` to
|
|
281
|
+
\`package.json\` there. \`frontera app sdk sync\` refreshes the copy. Opening
|
|
282
|
+
\`/\` renders "This app runs inside Frontera" (the handshake refuses an
|
|
283
|
+
unknown parent); open **\`/dev-host.html\`** instead, which frames the app and
|
|
284
|
+
plays the host side of the bridge.
|
|
285
|
+
|
|
286
|
+
The project's own skills (\`.agents/skills/\`) have the detail for its
|
|
287
|
+
framework.
|
|
270
288
|
`
|
|
271
289
|
|
|
272
290
|
export const AGENTS_MD = `# Frontera
|
package/src/heal.ts
CHANGED
|
@@ -45,9 +45,10 @@
|
|
|
45
45
|
* CLI (`src/vendor/sdk-sources.json`).
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'
|
|
48
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs'
|
|
49
49
|
import { dirname, join } from 'node:path'
|
|
50
50
|
|
|
51
|
+
import { lockedSdkVersions, lockedSpec, recordLockedRanges } from './app-lock'
|
|
51
52
|
import {
|
|
52
53
|
APP_SDK_PACKAGES,
|
|
53
54
|
LEGACY_SDK_ALIASES,
|
|
@@ -102,6 +103,10 @@ export function healProject(dir: string): HealResult {
|
|
|
102
103
|
|
|
103
104
|
const name = typeof manifest.name === 'string' && manifest.name ? manifest.name : 'app'
|
|
104
105
|
let manifestChanged = false
|
|
106
|
+
// What the App's own lockfile installed from the registry: the SDK its code was written against.
|
|
107
|
+
const locked = lockedSdkVersions(dir)
|
|
108
|
+
// The specs restored to a locked release, recorded in the lockfile too so the two agree.
|
|
109
|
+
const restored: Record<string, string> = {}
|
|
105
110
|
|
|
106
111
|
// Packages a legacy tree needs declared under their CURRENT name as well, so
|
|
107
112
|
// the vendored copy resolves the siblings it imports internally.
|
|
@@ -131,6 +136,18 @@ export function healProject(dir: string): HealResult {
|
|
|
131
136
|
if (typeof spec !== 'string' || !UNRESOLVABLE.test(spec)) continue
|
|
132
137
|
|
|
133
138
|
const pkg = currentSdkPackage(dep)
|
|
139
|
+
// The lockfile installed a published version: that is the SDK the code was checked against, so
|
|
140
|
+
// the App goes back to it rather than to a copy of another SDK (a pulled App's typecheck depends on it).
|
|
141
|
+
const lockedAt = pkg && dep === pkg ? locked.get(pkg) : undefined
|
|
142
|
+
if (lockedAt) {
|
|
143
|
+
deps[dep] = lockedSpec(lockedAt)
|
|
144
|
+
restored[dep] = deps[dep]
|
|
145
|
+
repairs.push(`restored ${dep} to ${deps[dep]}, the published release its bun.lock installed (was ${truncate(spec)})`)
|
|
146
|
+
manifestChanged = true
|
|
147
|
+
// A copy left under src/frontera/ would still win through tsconfig paths: it goes.
|
|
148
|
+
if (unvendorSdkPackage(dir, pkg!)) repairs.push(`removed the vendored copy in ${APP_SDK_PACKAGES[pkg!].dir} and its tsconfig path`)
|
|
149
|
+
continue
|
|
150
|
+
}
|
|
134
151
|
if (pkg) {
|
|
135
152
|
// Already pointing at the copy in this tree — that is the shape this
|
|
136
153
|
// function produces, so re-pulling must not rewrite it back over
|
|
@@ -188,6 +205,10 @@ export function healProject(dir: string): HealResult {
|
|
|
188
205
|
// still carry a stale copy, which is the ordinary state of every app
|
|
189
206
|
// scaffolded before the SDK grew a module.
|
|
190
207
|
if (vendorCurrent(dir, pkg) && (declares(manifest, specifier) || specifier === pkg)) continue
|
|
208
|
+
// Declared with a published range (`^1.43.10`): the package manager installs it, and the code was written against
|
|
209
|
+
// that release. Vendoring this CLI's copy over it swaps the SDK under the code (a pulled Next.js App's case).
|
|
210
|
+
const spec = manifest.dependencies?.[specifier] ?? manifest.devDependencies?.[specifier]
|
|
211
|
+
if (typeof spec === 'string' && !UNRESOLVABLE.test(spec)) continue
|
|
191
212
|
vendorSdkPackage(dir, pkg)
|
|
192
213
|
manifest.dependencies ??= {}
|
|
193
214
|
manifest.dependencies[specifier] = `file:./${APP_SDK_PACKAGES[pkg].dir}`
|
|
@@ -225,6 +246,8 @@ export function healProject(dir: string): HealResult {
|
|
|
225
246
|
}
|
|
226
247
|
|
|
227
248
|
if (manifestChanged) writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
|
|
249
|
+
// The lockfile's record of what package.json asks for follows, so a frozen install takes the restored package.json.
|
|
250
|
+
if (recordLockedRanges(dir, restored)) repairs.push(`recorded ${Object.entries(restored).map(([d, s]) => `${d} ${s}`).join(' and ')} in the lockfile, which already installed that release`)
|
|
228
251
|
|
|
229
252
|
// Point the editor at the vendored copy directly. Without this TypeScript
|
|
230
253
|
// reaches it through the `file:` link instead and reports every diagnostic
|
|
@@ -246,6 +269,94 @@ export function healProject(dir: string): HealResult {
|
|
|
246
269
|
return { repairs }
|
|
247
270
|
}
|
|
248
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Take a vendored SDK package out of an App that installs the published one:
|
|
274
|
+
* its directory under src/frontera/ and the tsconfig `paths` entries that
|
|
275
|
+
* point at it (which would otherwise resolve imports to the copy instead of
|
|
276
|
+
* node_modules). True when anything was removed. A Vite App whose config
|
|
277
|
+
* aliases the copy keeps it: the alias is how it resolves the SDK.
|
|
278
|
+
*/
|
|
279
|
+
/** Does the App's Vite config alias the vendored copy (a scaffolded Vite App resolves the SDK that way)? */
|
|
280
|
+
export function viteAliasesSdk(dir: string, pkg: AppSdkPackage): boolean {
|
|
281
|
+
const rel = APP_SDK_PACKAGES[pkg].dir
|
|
282
|
+
return ['vite.config.ts', 'vite.config.js', 'vite.config.mts'].some((f) => {
|
|
283
|
+
try {
|
|
284
|
+
return readFileSync(join(dir, f), 'utf8').includes(rel)
|
|
285
|
+
} catch {
|
|
286
|
+
return false
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function unvendorSdkPackage(dir: string, pkg: AppSdkPackage): boolean {
|
|
292
|
+
const removed = unvendorSdkPackageDetail(dir, pkg)
|
|
293
|
+
return !!removed && (removed.files.length > 0 || removed.tsconfigPaths.length > 0)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** What taking a vendored SDK package out of an App removed: the copy's files, and the tsconfig paths to it. */
|
|
297
|
+
export interface Unvendored {
|
|
298
|
+
/** The copy's directory, relative to the App (`src/frontera/blueprint`). */
|
|
299
|
+
dir: string
|
|
300
|
+
/** Each file deleted with it, relative to that directory, sorted. */
|
|
301
|
+
files: string[]
|
|
302
|
+
/** The directory above it, when it was left empty and deleted too (`src/frontera`). */
|
|
303
|
+
parentRemoved?: string
|
|
304
|
+
/** Each `compilerOptions.paths` key removed from tsconfig.json because it pointed at the copy. */
|
|
305
|
+
tsconfigPaths: string[]
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** `unvendorSdkPackage`, saying what it removed; null when the App's Vite config aliases the copy, so it is kept. */
|
|
309
|
+
export function unvendorSdkPackageDetail(dir: string, pkg: AppSdkPackage): Unvendored | null {
|
|
310
|
+
const rel = APP_SDK_PACKAGES[pkg].dir
|
|
311
|
+
if (viteAliasesSdk(dir, pkg)) return null
|
|
312
|
+
const out: Unvendored = { dir: rel, files: [], tsconfigPaths: [] }
|
|
313
|
+
if (existsSync(join(dir, rel))) {
|
|
314
|
+
out.files = filesUnder(join(dir, rel))
|
|
315
|
+
rmSync(join(dir, rel), { recursive: true, force: true })
|
|
316
|
+
const parent = dirname(join(dir, rel))
|
|
317
|
+
try {
|
|
318
|
+
if (readdirSync(parent).length === 0) {
|
|
319
|
+
rmSync(parent, { recursive: true })
|
|
320
|
+
out.parentRemoved = dirname(rel)
|
|
321
|
+
}
|
|
322
|
+
} catch {
|
|
323
|
+
// Gone already.
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const tsconfig = join(dir, 'tsconfig.json')
|
|
327
|
+
try {
|
|
328
|
+
const config = JSON.parse(readFileSync(tsconfig, 'utf8')) as { compilerOptions?: { paths?: Record<string, string[]> } }
|
|
329
|
+
const paths = config.compilerOptions?.paths
|
|
330
|
+
if (paths) {
|
|
331
|
+
for (const [key, targets] of Object.entries(paths)) {
|
|
332
|
+
if (Array.isArray(targets) && targets.some((t) => t.replace(/^\.\//, '').startsWith(`${rel}/`) || t.replace(/^\.\//, '') === rel)) {
|
|
333
|
+
delete paths[key]
|
|
334
|
+
out.tsconfigPaths.push(key)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (out.tsconfigPaths.length) writeFileSync(tsconfig, `${JSON.stringify(config, null, 2)}\n`)
|
|
338
|
+
}
|
|
339
|
+
} catch {
|
|
340
|
+
// No tsconfig, or JSON with comments: left as it is.
|
|
341
|
+
}
|
|
342
|
+
return out
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Every file under a directory, by its path relative to it, sorted. */
|
|
346
|
+
function filesUnder(root: string): string[] {
|
|
347
|
+
const out: string[] = []
|
|
348
|
+
const walk = (at: string, rel: string) => {
|
|
349
|
+
for (const name of readdirSync(at).sort()) {
|
|
350
|
+
const path = join(at, name)
|
|
351
|
+
const r = rel ? `${rel}/${name}` : name
|
|
352
|
+
if (statSync(path).isDirectory()) walk(path, r)
|
|
353
|
+
else out.push(r)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
walk(root, '')
|
|
357
|
+
return out
|
|
358
|
+
}
|
|
359
|
+
|
|
249
360
|
function isSdkPackage(dep: string): dep is AppSdkPackage {
|
|
250
361
|
return (SDK_PACKAGE_NAMES as string[]).includes(dep)
|
|
251
362
|
}
|
|
@@ -380,7 +491,7 @@ function vendorSdkPackage(dir: string, pkg: AppSdkPackage): void {
|
|
|
380
491
|
*/
|
|
381
492
|
export function vendoredManifest(pkg: AppSdkPackage, files: string[]): Record<string, unknown> {
|
|
382
493
|
const exports: Record<string, unknown> = { './package.json': './package.json' }
|
|
383
|
-
for (const rel of files.sort()) {
|
|
494
|
+
for (const rel of files.sort((a, b) => (String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0))) {
|
|
384
495
|
if (!/\.tsx?$/.test(rel)) continue
|
|
385
496
|
exports[`./${rel.replace(/\.tsx?$/, '')}`] = { types: `./${rel}`, import: `./${rel}` }
|
|
386
497
|
}
|
package/src/kit.ts
CHANGED
|
@@ -135,7 +135,7 @@ export interface RenderedProject {
|
|
|
135
135
|
/** One source, two native projections — Codex reads neither host's other tree. */
|
|
136
136
|
export function renderKit(kit: KitAssets = KIT): RenderedProject {
|
|
137
137
|
const generated: Record<string, string> = {}
|
|
138
|
-
for (const rel of Object.keys(kit.assets).sort()) {
|
|
138
|
+
for (const rel of Object.keys(kit.assets).sort((a, b) => (String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0))) {
|
|
139
139
|
const content = kit.assets[rel]!
|
|
140
140
|
if (kit.hosts.codex) generated[join('.agents', 'skills', rel)] = content
|
|
141
141
|
if (kit.hosts.claudeCode) generated[join('.claude', 'skills', rel)] = content
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A message and what fixes it, said as one sentence pair: "… the type
|
|
3
|
+
* allows. Fix: change …". The message keeps its own end mark when it has
|
|
4
|
+
* one (a runtime's error often ends with a period), so the join never says
|
|
5
|
+
* "allows.. Fix:"; a trailing comma, colon or semicolon gives way to the
|
|
6
|
+
* period. Without a fix, the message as it is.
|
|
7
|
+
*/
|
|
8
|
+
export function withFix(message: string, fix: string | null | undefined): string {
|
|
9
|
+
const said = message.trim().replace(/[\s,;:]+$/, '')
|
|
10
|
+
if (!fix?.trim()) return said
|
|
11
|
+
return `${said}${/[.!?…]$/.test(said) ? '' : '.'} Fix: ${fix.trim()}`
|
|
12
|
+
}
|