@beignet/cli 0.0.43 → 0.0.45
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/CHANGELOG.md +24 -0
- package/README.md +61 -8
- package/dist/choices.d.ts +4 -0
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +9 -0
- package/dist/choices.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +8 -4
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -13
- package/dist/index.js.map +1 -1
- package/dist/inspect.js +78 -21
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +4 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -1
- package/dist/lib.js.map +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +354 -3
- package/dist/mcp.js.map +1 -1
- package/dist/operational-lifecycle.d.ts +12 -0
- package/dist/operational-lifecycle.d.ts.map +1 -0
- package/dist/operational-lifecycle.js +39 -0
- package/dist/operational-lifecycle.js.map +1 -0
- package/dist/operational-path.d.ts +3 -0
- package/dist/operational-path.d.ts.map +1 -0
- package/dist/operational-path.js +26 -0
- package/dist/operational-path.js.map +1 -0
- package/dist/operational-process.d.ts +78 -0
- package/dist/operational-process.d.ts.map +1 -0
- package/dist/operational-process.js +228 -0
- package/dist/operational-process.js.map +1 -0
- package/dist/operational-runner.d.ts +2 -0
- package/dist/operational-runner.d.ts.map +1 -0
- package/dist/operational-runner.js +168 -0
- package/dist/operational-runner.js.map +1 -0
- package/dist/outbox.d.ts +14 -6
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +140 -124
- package/dist/outbox.js.map +1 -1
- package/dist/schedule.d.ts +2 -1
- package/dist/schedule.d.ts.map +1 -1
- package/dist/schedule.js +79 -81
- package/dist/schedule.js.map +1 -1
- package/dist/task.d.ts +2 -1
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +57 -61
- package/dist/task.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +21 -8
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +2 -0
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +9 -2
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shadcn.d.ts.map +1 -1
- package/dist/templates/shadcn.js +3 -1
- package/dist/templates/shadcn.js.map +1 -1
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +19 -6
- package/src/choices.ts +19 -0
- package/src/db.ts +8 -4
- package/src/index.ts +19 -16
- package/src/inspect.ts +114 -26
- package/src/lib.ts +27 -1
- package/src/mcp.ts +457 -2
- package/src/operational-lifecycle.ts +56 -0
- package/src/operational-path.ts +35 -0
- package/src/operational-process.ts +454 -0
- package/src/operational-runner.ts +221 -0
- package/src/outbox.ts +154 -128
- package/src/schedule.ts +84 -89
- package/src/task.ts +58 -62
- package/src/templates/agents.ts +21 -8
- package/src/templates/base.ts +2 -0
- package/src/templates/server.ts +9 -2
- package/src/templates/shadcn.ts +3 -1
package/src/task.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import type { TaskDef, TaskRunContextArgs } from "@beignet/core/tasks";
|
|
4
4
|
import { createJiti } from "jiti";
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
type OperationalErrorReportingContext,
|
|
9
9
|
reportOperationalFailure,
|
|
10
10
|
} from "./operational-error-reporting.js";
|
|
11
|
+
import { runWithOperationalCleanup } from "./operational-lifecycle.js";
|
|
12
|
+
import { resolveAppOperationalModulePath } from "./operational-path.js";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Options for running an app-owned operational task.
|
|
@@ -15,7 +17,8 @@ import {
|
|
|
15
17
|
export type RunAppTaskOptions = {
|
|
16
18
|
name: string;
|
|
17
19
|
cwd?: string;
|
|
18
|
-
input
|
|
20
|
+
/** Pre-parsed input validated by the registered task schema. */
|
|
21
|
+
input?: unknown;
|
|
19
22
|
tenant?: string;
|
|
20
23
|
modulePath?: string;
|
|
21
24
|
};
|
|
@@ -49,7 +52,7 @@ export async function runAppTask(
|
|
|
49
52
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
50
53
|
const config = await loadBeignetConfig(cwd);
|
|
51
54
|
const modulePath = normalizePath(options.modulePath ?? config.paths.tasks);
|
|
52
|
-
const rawInput =
|
|
55
|
+
const rawInput = options.input === undefined ? {} : options.input;
|
|
53
56
|
const startedAt = performance.now();
|
|
54
57
|
const taskModule = await loadTaskModule(cwd, modulePath);
|
|
55
58
|
const task = findTask(taskModule.tasks, options.name, modulePath);
|
|
@@ -67,51 +70,56 @@ export async function runAppTask(
|
|
|
67
70
|
? await taskModule.createTaskContext(contextArgs)
|
|
68
71
|
: {};
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"beignet.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
73
|
+
return runWithOperationalCleanup({
|
|
74
|
+
operation: `Task "${task.name}"`,
|
|
75
|
+
cleanupLabel: "stopTaskContext",
|
|
76
|
+
run: async () => {
|
|
77
|
+
let output: unknown;
|
|
78
|
+
try {
|
|
79
|
+
output = await runTask(task, {
|
|
80
|
+
input: parsedInput,
|
|
81
|
+
ctx,
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
await reportOperationalFailure({
|
|
85
|
+
ctx: toOperationalContext(ctx),
|
|
86
|
+
error,
|
|
87
|
+
reportOptions: {
|
|
88
|
+
level: "error",
|
|
89
|
+
mechanism: "beignet.task.cli",
|
|
90
|
+
handled: false,
|
|
91
|
+
tags: {
|
|
92
|
+
"beignet.kind": "task",
|
|
93
|
+
"beignet.task": task.name,
|
|
94
|
+
},
|
|
95
|
+
contexts: {
|
|
96
|
+
task: {
|
|
97
|
+
name: task.name,
|
|
98
|
+
tenant: options.tenant ?? null,
|
|
99
|
+
source: "beignet-cli",
|
|
100
|
+
},
|
|
94
101
|
},
|
|
95
102
|
},
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
103
|
+
});
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
schemaVersion: 1,
|
|
109
|
+
name: task.name,
|
|
110
|
+
cwd,
|
|
111
|
+
modulePath,
|
|
112
|
+
input: parsedInput,
|
|
113
|
+
...(options.tenant !== undefined ? { tenant: options.tenant } : {}),
|
|
114
|
+
output,
|
|
115
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
cleanup: async () => {
|
|
119
|
+
await flushOperationalErrorReporter(toOperationalContext(ctx));
|
|
120
|
+
await taskModule.stopTaskContext?.(ctx, contextArgs);
|
|
121
|
+
},
|
|
122
|
+
});
|
|
115
123
|
}
|
|
116
124
|
|
|
117
125
|
function toOperationalContext(ctx: unknown): OperationalErrorReportingContext {
|
|
@@ -120,27 +128,15 @@ function toOperationalContext(ctx: unknown): OperationalErrorReportingContext {
|
|
|
120
128
|
: {};
|
|
121
129
|
}
|
|
122
130
|
|
|
123
|
-
function parseTaskInputFlag(input: RunAppTaskOptions["input"]): unknown {
|
|
124
|
-
if (input === undefined) return {};
|
|
125
|
-
if (typeof input !== "string") return input;
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
return JSON.parse(input) as unknown;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
throw new Error(
|
|
131
|
-
`Invalid --input JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
131
|
async function loadTaskModule(
|
|
137
132
|
cwd: string,
|
|
138
133
|
modulePath: string,
|
|
139
134
|
): Promise<TaskModule> {
|
|
140
|
-
|
|
135
|
+
let absolutePath: string;
|
|
141
136
|
try {
|
|
142
|
-
await
|
|
143
|
-
} catch {
|
|
137
|
+
absolutePath = await resolveAppOperationalModulePath(cwd, modulePath);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
144
140
|
throw new Error(
|
|
145
141
|
`Could not find app task registry at ${modulePath}. Create it with beignet make task <feature.name> or configure paths.tasks in beignet.config.ts.`,
|
|
146
142
|
);
|
package/src/templates/agents.ts
CHANGED
|
@@ -31,8 +31,8 @@ export function agentsMd(ctx: TemplateContext): string {
|
|
|
31
31
|
"\n| Server Component request context | Use an app-owned server-only helper such as `lib/server-context.ts` that wraps `server.createContextFromNext()` in React `cache(...)`. Layouts may read `ctx.auth`, `ctx.tenant`, and request metadata for redirects and shell state; feature data still belongs behind use cases. |" +
|
|
32
32
|
"\n| Server Component React Query prefetch | Use an app-owned helper such as `lib/server-react-query.ts` on top of the request context: keep the `rq(contract).queryOptions(...)` key, replace only the server `queryFn` with the direct use case call, and do not create a parallel `rqServer` abstraction. |";
|
|
33
33
|
const specificSkills = ctx.api
|
|
34
|
-
? "`@beignet/next#routes-server`, `@beignet/provider-db-drizzle#database-provider`, `@beignet/provider-auth-better-auth#auth-provider`, or `@beignet/cli#app-structure`"
|
|
35
|
-
: "`@beignet/next#routes-server`, `@beignet/react-query#client`, `@beignet/react-hook-form#forms`, `@beignet/provider-db-drizzle#database-provider`, `@beignet/provider-auth-better-auth#auth-provider`, or `@beignet/cli#app-structure`";
|
|
34
|
+
? "`@beignet/next#routes-server`, `@beignet/web#fetch-server`, `@beignet/devtools#runtime-safety`, `@beignet/provider-db-drizzle#database-provider`, `@beignet/provider-auth-better-auth#auth-provider`, or `@beignet/cli#app-structure`"
|
|
35
|
+
: "`@beignet/next#routes-server`, `@beignet/web#fetch-server`, `@beignet/devtools#runtime-safety`, `@beignet/react-query#client`, `@beignet/react-hook-form#forms`, `@beignet/provider-db-drizzle#database-provider`, `@beignet/provider-auth-better-auth#auth-provider`, or `@beignet/cli#app-structure`";
|
|
36
36
|
|
|
37
37
|
return `# ${ctx.name} — agent guide
|
|
38
38
|
|
|
@@ -81,7 +81,9 @@ events, listener registration, jobs, and outbox wiring. After changing the Drizz
|
|
|
81
81
|
\`infra/db/schema/\`, run \`${cli} db generate\` then \`${cli} db migrate\`.
|
|
82
82
|
When MCP is available, use \`db_schema_sync\` for Beignet provider table
|
|
83
83
|
re-exports and \`db\` for \`generate\`, \`migrate\`, \`seed\`, or \`reset\`
|
|
84
|
-
instead of falling back to a shell.
|
|
84
|
+
instead of falling back to a shell. Use \`task_run\`, \`schedule_run\`,
|
|
85
|
+
\`outbox_inspect\`, and \`outbox_run\` for registered operational workflows
|
|
86
|
+
and outbox recovery.
|
|
85
87
|
|
|
86
88
|
## The framework already solves these
|
|
87
89
|
|
|
@@ -94,7 +96,7 @@ apps have reimplemented by accident:
|
|
|
94
96
|
| Ports outside a request — auth callbacks, module-level helpers | \`const { ports } = await getServer()\` (dynamic \`import("@/server")\` breaks module cycles). Do not construct parallel provider clients or fall back to \`console.*\` when \`ports.logger\` exists. |
|
|
95
97
|
| Routes that cannot be contracts — webhooks, third-party callbacks, streaming | \`createWebhookRoute\`, \`createPaymentWebhookRoute\`, \`createScheduleRoute\`, \`createOutboxDrainRoute\` from \`@beignet/next\`; \`server.rawRoute(...)\` for anything else. All run the hooks pipeline — never hand-enforce rate limits in a route body. |
|
|
96
98
|
| Rate limiting or idempotency on a route | Declare \`metadata.rateLimit\` / \`metadata.idempotency\` on the contract (or the \`pipeline\` option on raw routes); hooks enforce it. |
|
|
97
|
-
| Tenant-owned repository access | Pass \`TenantScope\` through app-facing repository methods and unwrap it with \`tenantScopeId(scope)\` in adapters. Raw provider-correlation lookups such as webhook customer IDs are not tenant authorization. |
|
|
99
|
+
| Tenant-owned repository access | Resolve the request tenant from membership-backed app state or provider claims the app explicitly treats as authoritative and current; never trust caller-supplied tenant IDs or unverified session fields. Pass \`TenantScope\` through app-facing repository methods and unwrap it with \`tenantScopeId(scope)\` in adapters. Raw provider-correlation lookups such as webhook customer IDs are not tenant authorization. |
|
|
98
100
|
| Route-level tests that exercise real hooks | \`createTestApp\` / \`createTestRequester\` from \`@beignet/web/testing\`. Bind the rate-limit and idempotency ports in the test app when asserting 429s or replay. |
|
|
99
101
|
| Environment configuration | \`lib/env.ts\` (\`createEnv\`), never ad-hoc \`process.env\` reads in app code. |
|
|
100
102
|
| The same lookup runs several times in one request — context, policy, use case | Wrap the repository read in \`createMemo(...)\` from \`@beignet/core/memo\` where the adapter is wired in infra. The cache lives for exactly one request; pair mutations with \`.invalidate(...)\`. Never hand-roll per-request caches. |
|
|
@@ -159,10 +161,13 @@ skill-loading block.
|
|
|
159
161
|
## MCP server
|
|
160
162
|
|
|
161
163
|
\`.mcp.json\` registers the app-local \`@beignet/cli\` bin at
|
|
162
|
-
\`./node_modules/.bin/beignet mcp\`, which exposes
|
|
163
|
-
|
|
164
|
-
\`check\`, \`db\`, \`db_schema_sync\`, \`
|
|
164
|
+
\`./node_modules/.bin/beignet mcp\`, which exposes app context, validation,
|
|
165
|
+
generation, and operations as structured tools named exactly: \`app_map\`,
|
|
166
|
+
\`explain\`, \`check\`, \`db\`, \`db_schema_sync\`, \`task_run\`,
|
|
167
|
+
\`schedule_run\`, \`outbox_inspect\`, \`outbox_run\`, \`routes\`, \`doctor\`,
|
|
165
168
|
\`doctor_fix_plan\`, \`doctor_fix\`, \`lint\`, \`make\`, \`provider_add\`.
|
|
169
|
+
It also publishes \`beignet://app/guidance\` and focused
|
|
170
|
+
\`beignet://app/features/{feature}\` resources.
|
|
166
171
|
Use \`doctor_fix_plan\` to inspect
|
|
167
172
|
stable operation IDs, hashes, exact patches, and current diagnostics before
|
|
168
173
|
passing its \`planId\` and optional \`fixIds\` to \`doctor_fix\`. Guarded
|
|
@@ -180,7 +185,15 @@ with \`generate\` and \`migrate\`; database output is bounded, commands time
|
|
|
180
185
|
out, and cancellation stops the active process tree. Treat \`seed\` and
|
|
181
186
|
especially \`reset\` as app-owned mutations. Lifecycle \`dryRun\` validates
|
|
182
187
|
and reports the script without executing it; it does not simulate SQL or data
|
|
183
|
-
changes.
|
|
188
|
+
changes. Use \`task_run\` and \`schedule_run\` for registered operational
|
|
189
|
+
workflows, \`outbox_inspect\` for read-only \`list\` and \`show\`, and
|
|
190
|
+
\`outbox_run\` for \`drain\`, \`requeue\`, \`purge\`, or \`prune\`; purge and
|
|
191
|
+
prune support \`dryRun\`. Operational commands run in isolated process trees
|
|
192
|
+
with bounded results, cancellation, and timeouts. Optional \`module\` overrides
|
|
193
|
+
must remain inside the app root where the MCP server started. Cancellation and
|
|
194
|
+
timeouts cannot roll back side effects that already completed. Inspect app
|
|
195
|
+
state before retrying an interrupted operation or one that reports a cleanup
|
|
196
|
+
failure. Clients that do not read \`.mcp.json\` can use the same command from
|
|
184
197
|
the app root; use \`${cli} mcp\` only for terminal debugging.
|
|
185
198
|
`;
|
|
186
199
|
}
|
package/src/templates/base.ts
CHANGED
|
@@ -122,7 +122,9 @@ export function packageJson(ctx: TemplateContext): string {
|
|
|
122
122
|
function intentSkillPackages(ctx: TemplateContext): string[] {
|
|
123
123
|
const packages = [
|
|
124
124
|
"@beignet/core",
|
|
125
|
+
"@beignet/devtools",
|
|
125
126
|
"@beignet/next",
|
|
127
|
+
"@beignet/web",
|
|
126
128
|
"@beignet/cli",
|
|
127
129
|
"@beignet/provider-auth-better-auth",
|
|
128
130
|
"@beignet/provider-db-drizzle",
|
package/src/templates/server.ts
CHANGED
|
@@ -601,7 +601,9 @@ export type TenantResolutionInput = {
|
|
|
601
601
|
export function resolveRequestTenant({
|
|
602
602
|
auth,
|
|
603
603
|
}: TenantResolutionInput): ActivityTenant | undefined {
|
|
604
|
-
const tenantId = auth
|
|
604
|
+
const tenantId = auth
|
|
605
|
+
? tenantIdFromAuthoritativeAuthClaims(auth)
|
|
606
|
+
: undefined;
|
|
605
607
|
|
|
606
608
|
return tenantId ? createTenant(tenantId) : undefined;
|
|
607
609
|
}
|
|
@@ -614,7 +616,12 @@ export function resolveServiceTenant(
|
|
|
614
616
|
return normalizedTenantId ? createTenant(normalizedTenantId) : undefined;
|
|
615
617
|
}
|
|
616
618
|
|
|
617
|
-
|
|
619
|
+
/**
|
|
620
|
+
* Resolve only provider-issued claims that the application treats as
|
|
621
|
+
* authoritative. Multi-tenant apps must replace this starter seam with a
|
|
622
|
+
* membership-backed lookup before authorizing tenant-owned resources.
|
|
623
|
+
*/
|
|
624
|
+
function tenantIdFromAuthoritativeAuthClaims(auth: AuthSession) {
|
|
618
625
|
return (
|
|
619
626
|
stringProperty(auth.session, "tenantId") ??
|
|
620
627
|
stringProperty(auth.session, "organizationId") ??
|
package/src/templates/shadcn.ts
CHANGED
|
@@ -186,7 +186,9 @@ const appGlobalsCss = `@import "tailwindcss";
|
|
|
186
186
|
}
|
|
187
187
|
`;
|
|
188
188
|
|
|
189
|
-
const uiButton = `
|
|
189
|
+
const uiButton = `"use client";
|
|
190
|
+
|
|
191
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
190
192
|
import { Slot } from "radix-ui";
|
|
191
193
|
import type * as React from "react";
|
|
192
194
|
|