minutework 0.1.32 → 0.1.34

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 (91) hide show
  1. package/assets/claude-local/skills/README.md +2 -0
  2. package/assets/claude-local/skills/app-pack-authoring/SKILL.md +24 -1
  3. package/assets/claude-local/skills/contract-first-public-intake/SKILL.md +11 -3
  4. package/assets/claude-local/skills/generated-workspace-architecture/SKILL.md +19 -3
  5. package/assets/claude-local/skills/published-web-and-mw-core-site/SKILL.md +9 -6
  6. package/assets/claude-local/skills/standalone-mobile-client/SKILL.md +5 -4
  7. package/assets/templates/next-tenant-app/README.md +37 -135
  8. package/assets/templates/next-tenant-app/package.json +1 -0
  9. package/assets/templates/next-tenant-app/src/app/app/demo/page.tsx +15 -0
  10. package/assets/templates/next-tenant-app/src/app/app/layout.tsx +1 -4
  11. package/assets/templates/next-tenant-app/src/app/app/page.tsx +2 -17
  12. package/assets/templates/next-tenant-app/src/app/blog/[slug]/page.tsx +9 -67
  13. package/assets/templates/next-tenant-app/src/app/blog/page.tsx +10 -46
  14. package/assets/templates/next-tenant-app/src/app/docs/[...slug]/page.tsx +9 -65
  15. package/assets/templates/next-tenant-app/src/app/docs/page.tsx +10 -46
  16. package/assets/templates/next-tenant-app/src/app/layout.tsx +8 -10
  17. package/assets/templates/next-tenant-app/src/app/login/page.tsx +3 -23
  18. package/assets/templates/next-tenant-app/src/app/page.tsx +11 -44
  19. package/assets/templates/next-tenant-app/src/app/pricing/page.tsx +10 -44
  20. package/assets/templates/next-tenant-app/src/app/providers.tsx +2 -1
  21. package/assets/templates/next-tenant-app/src/app/robots.ts +7 -18
  22. package/assets/templates/next-tenant-app/src/app/sitemap.ts +4 -39
  23. package/assets/templates/next-tenant-app/src/features/auth/components/login-screen.tsx +97 -98
  24. package/assets/templates/next-tenant-app/src/features/dashboard/components/tenant-dashboard.tsx +43 -78
  25. package/assets/templates/next-tenant-app/src/features/demo/components/manifest-demo.tsx +89 -0
  26. package/assets/templates/next-tenant-app/src/features/public-shell/components/static-public-page.tsx +58 -0
  27. package/assets/templates/next-tenant-app/src/features/shell/components/private-app-shell.tsx +48 -552
  28. package/assets/templates/next-tenant-app/src/lib/app-routes.ts +2 -2
  29. package/assets/templates/next-tenant-app/src/lib/public-site.ts +5 -30
  30. package/assets/templates/next-tenant-app/src/mw/client.ts +18 -0
  31. package/assets/templates/next-tenant-app/src/mw/mock.test.ts +21 -0
  32. package/assets/templates/next-tenant-app/src/mw/mock.ts +35 -0
  33. package/assets/templates/next-tenant-app/src/mw/provider.tsx +17 -0
  34. package/assets/templates/next-tenant-app/template.json +3 -3
  35. package/assets/templates/next-tenant-app/template.schema.json +1 -0
  36. package/assets/templates/next-tenant-app/tools/template/validate-route-contract.mjs +4 -5
  37. package/package.json +2 -2
  38. package/vendor/workspace-mcp/types.d.ts +4 -0
  39. package/assets/templates/next-tenant-app/src/app/(cms)/[...path]/page.tsx +0 -89
  40. package/assets/templates/next-tenant-app/src/app/api/auth/context/route.test.ts +0 -90
  41. package/assets/templates/next-tenant-app/src/app/api/auth/context/route.ts +0 -78
  42. package/assets/templates/next-tenant-app/src/app/api/auth/login/route.ts +0 -31
  43. package/assets/templates/next-tenant-app/src/app/api/auth/logout/route.ts +0 -16
  44. package/assets/templates/next-tenant-app/src/app/api/auth/password-change/route.test.ts +0 -79
  45. package/assets/templates/next-tenant-app/src/app/api/auth/password-change/route.ts +0 -40
  46. package/assets/templates/next-tenant-app/src/app/api/auth/password-status/route.test.ts +0 -42
  47. package/assets/templates/next-tenant-app/src/app/api/auth/password-status/route.ts +0 -29
  48. package/assets/templates/next-tenant-app/src/app/api/auth/session/route.ts +0 -26
  49. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/[runId]/route.test.ts +0 -40
  50. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/[runId]/route.ts +0 -47
  51. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/route.test.ts +0 -43
  52. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/route.ts +0 -45
  53. package/assets/templates/next-tenant-app/src/app/app/examples/runtime-commands/page.test.ts +0 -83
  54. package/assets/templates/next-tenant-app/src/app/app/examples/runtime-commands/page.tsx +0 -30
  55. package/assets/templates/next-tenant-app/src/app/app/page.test.ts +0 -62
  56. package/assets/templates/next-tenant-app/src/app/app/private-content-source.test.ts +0 -88
  57. package/assets/templates/next-tenant-app/src/app/blog/[slug]/page.test.ts +0 -70
  58. package/assets/templates/next-tenant-app/src/app/blog/page.test.ts +0 -46
  59. package/assets/templates/next-tenant-app/src/app/docs/[...slug]/page.test.ts +0 -70
  60. package/assets/templates/next-tenant-app/src/app/docs/page.test.ts +0 -46
  61. package/assets/templates/next-tenant-app/src/app/login/page.test.ts +0 -55
  62. package/assets/templates/next-tenant-app/src/app/page.test.ts +0 -90
  63. package/assets/templates/next-tenant-app/src/app/pricing/page.test.ts +0 -59
  64. package/assets/templates/next-tenant-app/src/app/robots.test.ts +0 -40
  65. package/assets/templates/next-tenant-app/src/app/sitemap.test.ts +0 -63
  66. package/assets/templates/next-tenant-app/src/features/examples/runtime-command-demo/components/runtime-command-demo.tsx +0 -342
  67. package/assets/templates/next-tenant-app/src/features/public-shell/components/content-article.tsx +0 -66
  68. package/assets/templates/next-tenant-app/src/features/public-shell/components/content-collection.tsx +0 -108
  69. package/assets/templates/next-tenant-app/src/features/public-shell/components/marketing-page-canvas.tsx +0 -111
  70. package/assets/templates/next-tenant-app/src/features/public-shell/components/public-site-shell.tsx +0 -111
  71. package/assets/templates/next-tenant-app/src/features/public-shell/components/section-renderer.test.ts +0 -38
  72. package/assets/templates/next-tenant-app/src/features/public-shell/components/section-renderer.tsx +0 -145
  73. package/assets/templates/next-tenant-app/src/lib/content/__fixtures__/public-site-snapshot.ts +0 -189
  74. package/assets/templates/next-tenant-app/src/lib/content/adapter.server.test.ts +0 -444
  75. package/assets/templates/next-tenant-app/src/lib/content/adapter.server.ts +0 -383
  76. package/assets/templates/next-tenant-app/src/lib/content/contracts.test.ts +0 -138
  77. package/assets/templates/next-tenant-app/src/lib/content/contracts.ts +0 -399
  78. package/assets/templates/next-tenant-app/src/lib/content/custom-adapter.ts +0 -5
  79. package/assets/templates/next-tenant-app/src/lib/content/empty-state.ts +0 -96
  80. package/assets/templates/next-tenant-app/src/lib/content/release-manifest.test.ts +0 -93
  81. package/assets/templates/next-tenant-app/src/lib/content/release-manifest.ts +0 -123
  82. package/assets/templates/next-tenant-app/src/lib/platform/auth.server.test.ts +0 -75
  83. package/assets/templates/next-tenant-app/src/lib/platform/auth.server.ts +0 -25
  84. package/assets/templates/next-tenant-app/src/lib/platform/client.server.test.ts +0 -170
  85. package/assets/templates/next-tenant-app/src/lib/platform/client.server.ts +0 -661
  86. package/assets/templates/next-tenant-app/src/lib/platform/contracts.ts +0 -131
  87. package/assets/templates/next-tenant-app/src/lib/platform/endpoints.server.ts +0 -34
  88. package/assets/templates/next-tenant-app/src/lib/platform/env.server.test.ts +0 -211
  89. package/assets/templates/next-tenant-app/src/lib/platform/env.server.ts +0 -151
  90. package/assets/templates/next-tenant-app/src/lib/platform/route-response.ts +0 -33
  91. package/assets/templates/next-tenant-app/src/lib/platform/session.server.ts +0 -108
@@ -1,123 +0,0 @@
1
- import { z } from "zod";
2
-
3
- import { contentStructurePageSchema, siteConfigSchema } from "./contracts";
4
-
5
- /**
6
- * Release manifest contract for Builder output.
7
- *
8
- * When Builder composes a site, it emits a manifest that describes what
9
- * was generated. This manifest is inspectable, diffable, and can be
10
- * used to verify that releases match expectations.
11
- *
12
- * The manifest is NOT the release artifact itself — it describes the
13
- * composition that produced the artifact.
14
- */
15
-
16
- export const releaseManifestSchema = z.object({
17
- /** Manifest format version. */
18
- manifest_version: z.literal(1),
19
-
20
- /** The app pack ID that was composed. */
21
- app_pack_id: z.string().min(1),
22
-
23
- /** Snapshot schema version for compatibility gating. */
24
- snapshot_schema_version: z.number().int().positive(),
25
-
26
- /** Release class from the hosted vocabulary. */
27
- release_class: z.enum(["static_export", "ssr_container", "runtime_local_sidecar"]),
28
-
29
- /** Summary of what was composed. */
30
- composition: z.object({
31
- config_updated: z.boolean(),
32
- pages: z.array(
33
- z.object({
34
- page_key: z.string().min(1),
35
- path: z.string().min(1),
36
- title: z.string().min(1),
37
- section_count: z.number().int().min(0),
38
- }),
39
- ),
40
- navigation: z.array(
41
- z.object({
42
- nav_key: z.string().min(1),
43
- slot: z.string().min(1),
44
- item_count: z.number().int().min(0),
45
- }),
46
- ),
47
- forms: z.array(
48
- z.object({
49
- form_key: z.string().min(1),
50
- field_count: z.number().int().min(0),
51
- }),
52
- ),
53
- }),
54
-
55
- /** Optional metadata for attached external renderers that sync against the standard artifact graph. */
56
- attached_site: z
57
- .object({
58
- mode: z.literal("attached_app"),
59
- adapter_module: z.string().min(1),
60
- renderer: z.string().min(1),
61
- publish_target: z.string().min(1),
62
- })
63
- .optional(),
64
-
65
- /** Digest of the composed content for integrity verification. */
66
- content_digest: z.string().min(1),
67
-
68
- /** Timestamp of the manifest generation. */
69
- generated_at: z.string().min(1),
70
- });
71
-
72
- export type ReleaseManifest = z.infer<typeof releaseManifestSchema>;
73
-
74
- /**
75
- * Build a release manifest from a Builder composition result.
76
- *
77
- * This is called by Builder after compose_site_from_builder_payload
78
- * to produce an inspectable record of what was generated.
79
- */
80
- export function buildReleaseManifest(input: {
81
- appPackId: string;
82
- snapshotSchemaVersion: number;
83
- releaseClass: "static_export" | "ssr_container" | "runtime_local_sidecar";
84
- config: Record<string, unknown> | null;
85
- pages: { page_key: string; path: string; title: string; sections: unknown[] }[];
86
- navigation: { nav_key: string; slot: string; items: unknown[] }[];
87
- forms: { form_key: string; schema?: { fields?: unknown[] } }[];
88
- attachedSite?: {
89
- mode: "attached_app";
90
- adapter_module: string;
91
- renderer: string;
92
- publish_target: string;
93
- } | null;
94
- contentDigest: string;
95
- }): ReleaseManifest {
96
- return {
97
- manifest_version: 1,
98
- app_pack_id: input.appPackId,
99
- snapshot_schema_version: input.snapshotSchemaVersion,
100
- release_class: input.releaseClass,
101
- composition: {
102
- config_updated: input.config !== null,
103
- pages: input.pages.map((p) => ({
104
- page_key: p.page_key,
105
- path: p.path,
106
- title: p.title,
107
- section_count: p.sections?.length ?? 0,
108
- })),
109
- navigation: input.navigation.map((n) => ({
110
- nav_key: n.nav_key,
111
- slot: n.slot,
112
- item_count: n.items?.length ?? 0,
113
- })),
114
- forms: input.forms.map((f) => ({
115
- form_key: f.form_key,
116
- field_count: f.schema?.fields?.length ?? 0,
117
- })),
118
- },
119
- attached_site: input.attachedSite ?? undefined,
120
- content_digest: input.contentDigest,
121
- generated_at: new Date().toISOString(),
122
- };
123
- }
@@ -1,75 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
-
3
- const redirect = vi.fn((href: string) => {
4
- throw new Error(`redirect:${href}`);
5
- });
6
- const resolveCurrentSession = vi.fn();
7
- const readPlatformAuthState = vi.fn();
8
-
9
- vi.mock("next/navigation", () => ({
10
- redirect,
11
- }));
12
-
13
- vi.mock("@/lib/platform/client.server", () => ({
14
- resolveCurrentSession,
15
- }));
16
-
17
- vi.mock("@/lib/platform/session.server", () => ({
18
- readPlatformAuthState,
19
- }));
20
-
21
- describe("resolveAuthenticatedSession", () => {
22
- beforeEach(() => {
23
- vi.clearAllMocks();
24
- vi.resetModules();
25
- });
26
-
27
- it("redirects anonymous sessions to /login", async () => {
28
- readPlatformAuthState.mockResolvedValue(null);
29
- resolveCurrentSession.mockResolvedValue({ authenticated: false });
30
-
31
- const auth = await import("./auth.server");
32
-
33
- await expect(auth.resolveAuthenticatedSession()).rejects.toThrow(
34
- "redirect:/login",
35
- );
36
- expect(redirect).toHaveBeenCalledWith("/login");
37
- });
38
-
39
- it("returns the authenticated platform session", async () => {
40
- const session = {
41
- authenticated: true as const,
42
- user: {
43
- id: "user-1",
44
- username: "demo-user",
45
- email: "demo@example.com",
46
- },
47
- active_tenant_id: "tenant-1",
48
- active_tenant: {
49
- tenant_id: "tenant-1",
50
- tenant_slug: "alpha",
51
- tenant_name: "Alpha",
52
- role: "admin",
53
- },
54
- memberships: [
55
- {
56
- tenant_id: "tenant-1",
57
- tenant_slug: "alpha",
58
- tenant_name: "Alpha",
59
- role: "admin",
60
- },
61
- ],
62
- };
63
-
64
- readPlatformAuthState.mockResolvedValue({
65
- sessionId: "session-1",
66
- csrfToken: "csrf-1",
67
- });
68
- resolveCurrentSession.mockResolvedValue(session);
69
-
70
- const auth = await import("./auth.server");
71
-
72
- await expect(auth.resolveAuthenticatedSession()).resolves.toEqual(session);
73
- expect(redirect).not.toHaveBeenCalled();
74
- });
75
- });
@@ -1,25 +0,0 @@
1
- import "server-only";
2
-
3
- import { cache } from "react";
4
- import { redirect } from "next/navigation";
5
-
6
- import { appRoutes } from "@/lib/app-routes";
7
- import { resolveCurrentSession } from "@/lib/platform/client.server";
8
- import type { AuthenticatedPlatformSession } from "@/lib/platform/contracts";
9
- import { readPlatformAuthState } from "@/lib/platform/session.server";
10
-
11
- const loadResolvedPlatformSession = cache(async () =>
12
- resolveCurrentSession(await readPlatformAuthState()),
13
- );
14
-
15
- export const resolvePlatformSession = loadResolvedPlatformSession;
16
-
17
- export async function resolveAuthenticatedSession(): Promise<AuthenticatedPlatformSession> {
18
- const session = await loadResolvedPlatformSession();
19
-
20
- if (!session.authenticated) {
21
- redirect(appRoutes.login);
22
- }
23
-
24
- return session;
25
- }
@@ -1,170 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from "vitest";
2
-
3
- import {
4
- dispatchTenantCommand,
5
- loadCurrentSession,
6
- loginWithPassword,
7
- } from "@/lib/platform/client.server";
8
-
9
- const sessionPayload = {
10
- user: {
11
- id: "user-1",
12
- username: "demo-user",
13
- email: "demo@example.com",
14
- },
15
- active_tenant_id: "tenant-1",
16
- active_tenant: {
17
- tenant_id: "tenant-1",
18
- tenant_slug: "alpha",
19
- tenant_name: "Alpha",
20
- role: "admin",
21
- },
22
- memberships: [
23
- {
24
- tenant_id: "tenant-1",
25
- tenant_slug: "alpha",
26
- tenant_name: "Alpha",
27
- role: "admin",
28
- },
29
- ],
30
- };
31
-
32
- const commandRunPayload = {
33
- run_id: "run-1",
34
- tenant_id: "tenant-1",
35
- runtime_id: "runtime-1",
36
- template_key: "echo_hello",
37
- status: "queued",
38
- safe_summary: "Queued for dispatch.",
39
- exit_code: null,
40
- stdout_tail: "",
41
- stderr_tail: "",
42
- source_ref: "runtime:command:1",
43
- dispatch_error: "",
44
- };
45
-
46
- function jsonResponse(
47
- body: unknown,
48
- init?: {
49
- status?: number;
50
- headers?: Record<string, string>;
51
- },
52
- ) {
53
- return new Response(body === null ? null : JSON.stringify(body), {
54
- status: init?.status ?? 200,
55
- headers: {
56
- "content-type": "application/json",
57
- ...init?.headers,
58
- },
59
- });
60
- }
61
-
62
- describe("platform client", () => {
63
- afterEach(() => {
64
- vi.unstubAllGlobals();
65
- });
66
-
67
- it("bootstraps an anonymous csrf token while resolving the session", async () => {
68
- const fetchMock = vi.fn().mockResolvedValue(
69
- jsonResponse(
70
- { detail: "Authentication required." },
71
- {
72
- status: 401,
73
- headers: {
74
- "set-cookie": "csrftoken=bootstrap-token; Path=/; SameSite=Lax",
75
- },
76
- },
77
- ),
78
- );
79
-
80
- vi.stubGlobal("fetch", fetchMock);
81
-
82
- const result = await loadCurrentSession(null);
83
-
84
- expect(result.data).toEqual({ authenticated: false });
85
- expect(result.authState).toEqual({
86
- sessionId: null,
87
- csrfToken: "bootstrap-token",
88
- });
89
- expect(fetchMock).toHaveBeenCalledTimes(1);
90
- });
91
-
92
- it("bootstraps csrf before login and persists the returned session cookies", async () => {
93
- const fetchMock = vi
94
- .fn()
95
- .mockResolvedValueOnce(
96
- jsonResponse(
97
- { detail: "Authentication required." },
98
- {
99
- status: 401,
100
- headers: {
101
- "set-cookie": "csrftoken=bootstrap-token; Path=/; SameSite=Lax",
102
- },
103
- },
104
- ),
105
- )
106
- .mockResolvedValueOnce(
107
- jsonResponse(sessionPayload, {
108
- status: 200,
109
- headers: {
110
- "set-cookie":
111
- "sessionid=session-1; Path=/; HttpOnly, csrftoken=rotated-token; Path=/; SameSite=Lax",
112
- },
113
- }),
114
- );
115
-
116
- vi.stubGlobal("fetch", fetchMock);
117
-
118
- const result = await loginWithPassword(null, {
119
- username: "demo-user",
120
- password: "demo-password",
121
- });
122
-
123
- const loginHeaders = new Headers(fetchMock.mock.calls[1]?.[1]?.headers);
124
-
125
- expect(loginHeaders.get("x-csrftoken")).toBe("bootstrap-token");
126
- expect(loginHeaders.get("cookie")).toBe("csrftoken=bootstrap-token");
127
- expect(result.authState).toEqual({
128
- sessionId: "session-1",
129
- csrfToken: "rotated-token",
130
- });
131
- expect(result.data.authenticated).toBe(true);
132
- expect(result.data.active_tenant_id).toBe("tenant-1");
133
- });
134
-
135
- it("forwards the stored session and csrf token on unsafe tenant mutations", async () => {
136
- const fetchMock = vi
137
- .fn()
138
- .mockResolvedValueOnce(
139
- jsonResponse(sessionPayload, {
140
- status: 200,
141
- headers: {
142
- "set-cookie": "csrftoken=csrf-1; Path=/; SameSite=Lax",
143
- },
144
- }),
145
- )
146
- .mockResolvedValueOnce(jsonResponse(commandRunPayload));
147
-
148
- vi.stubGlobal("fetch", fetchMock);
149
-
150
- const result = await dispatchTenantCommand(
151
- { sessionId: "session-1", csrfToken: null },
152
- {
153
- tenant_id: "tenant-1",
154
- template_key: "echo_hello",
155
- },
156
- );
157
-
158
- const commandHeaders = new Headers(fetchMock.mock.calls[1]?.[1]?.headers);
159
-
160
- expect(commandHeaders.get("cookie")).toBe(
161
- "sessionid=session-1; csrftoken=csrf-1",
162
- );
163
- expect(commandHeaders.get("x-csrftoken")).toBe("csrf-1");
164
- expect(result.authState).toEqual({
165
- sessionId: "session-1",
166
- csrfToken: "csrf-1",
167
- });
168
- expect(result.data.run_id).toBe("run-1");
169
- });
170
- });