@pikku/cli 0.12.43 → 0.12.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.
Files changed (114) hide show
  1. package/cli.schema.json +1 -1
  2. package/console-app/assets/index-CRLT8CXr.js +254 -0
  3. package/console-app/assets/{index-VleHndkw.css → index-DwyRdRuZ.css} +1 -1
  4. package/console-app/index.html +2 -2
  5. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  6. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  7. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  8. package/dist/.pikku/cli/pikku-cli-channel.js +11 -1
  9. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  10. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  11. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +5 -0
  12. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +5 -0
  13. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +474 -0
  14. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  15. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  16. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  17. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +55 -0
  18. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  19. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  20. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  21. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  22. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  23. package/dist/.pikku/function/pikku-function-types.gen.d.ts +14 -1
  24. package/dist/.pikku/function/pikku-function-types.gen.js +17 -1
  25. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  26. package/dist/.pikku/function/pikku-functions-meta.gen.json +174 -136
  27. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  28. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  29. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  30. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  31. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  32. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  33. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  34. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  35. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  36. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  37. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  38. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  39. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  40. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  41. package/dist/.pikku/pikku-types.gen.js +1 -1
  42. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  43. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  45. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.json +8 -8
  46. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  47. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  48. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  49. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +6 -4
  50. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  51. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  52. package/dist/.pikku/schemas/register.gen.js +13 -5
  53. package/dist/.pikku/schemas/schemas/FabricAddInput.schema.json +1 -0
  54. package/dist/.pikku/schemas/schemas/FabricAddOutput.schema.json +1 -0
  55. package/dist/.pikku/schemas/schemas/FabricPublishInput.schema.json +1 -0
  56. package/dist/.pikku/schemas/schemas/FabricPublishOutput.schema.json +1 -0
  57. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  58. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  59. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  60. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  61. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  62. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  63. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  64. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  65. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  66. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  67. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  68. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +15 -15
  69. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  70. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  71. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  72. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  73. package/dist/bin/pikku-bin.mjs +2 -2
  74. package/dist/src/cli.wiring.js +12 -0
  75. package/dist/src/fabric/fabric-commands.d.ts +61 -3
  76. package/dist/src/fabric/fabric-commands.js +27 -1
  77. package/dist/src/fabric/functions/add.function.d.ts +50 -0
  78. package/dist/src/fabric/functions/add.function.js +144 -0
  79. package/dist/src/fabric/functions/publish.function.d.ts +45 -0
  80. package/dist/src/fabric/functions/publish.function.js +85 -0
  81. package/dist/src/fabric/functions/validate-core.d.ts +1 -1
  82. package/dist/src/fabric/functions/validate.function.d.ts +4 -4
  83. package/dist/src/fabric/functions/validate.function.js +119 -0
  84. package/dist/src/functions/commands/pikku-command-summary.js +3 -2
  85. package/dist/src/functions/commands/versions-update.js +10 -4
  86. package/dist/src/functions/commands/workspace-validate.d.ts +3 -3
  87. package/dist/src/functions/db/better-auth-schema.js +15 -2
  88. package/dist/src/functions/db/sqlite/sqlite-kysely.js +11 -3
  89. package/dist/src/functions/validate/workspace-validate.d.ts +2 -2
  90. package/dist/src/functions/validate/workspace-validate.js +4 -0
  91. package/dist/src/functions/wirings/channels/pikku-channels.js +20 -11
  92. package/dist/src/functions/wirings/channels/pikku-command-channels-map.js +2 -2
  93. package/dist/src/functions/wirings/channels/pikku-command-channels.js +20 -6
  94. package/dist/src/functions/wirings/channels/serialize-typed-channel-map.d.ts +1 -1
  95. package/dist/src/functions/wirings/channels/serialize-typed-channel-map.js +59 -11
  96. package/dist/src/functions/wirings/cli/pikku-command-cli.js +20 -6
  97. package/dist/src/functions/wirings/emails/pikku-command-emails.js +48 -19
  98. package/dist/src/functions/wirings/functions/pikku-command-function-types-split.js +7 -1
  99. package/dist/src/functions/wirings/functions/serialize-addon-refs.d.ts +14 -0
  100. package/dist/src/functions/wirings/functions/serialize-addon-refs.js +129 -0
  101. package/dist/src/functions/wirings/http/pikku-command-http-routes.js +21 -6
  102. package/dist/src/functions/wirings/http/serialize-typed-http-map.js +12 -0
  103. package/dist/src/functions/workflows/all.workflow.js +7 -0
  104. package/dist/src/scaffold/rpc-remote.gen.js +1 -1
  105. package/dist/src/services/cli-logger-forwarder.service.d.ts +4 -1
  106. package/dist/src/services/cli-logger-forwarder.service.js +20 -2
  107. package/dist/src/services/cli-logger.service.d.ts +16 -2
  108. package/dist/src/services/cli-logger.service.js +33 -5
  109. package/dist/src/services.js +7 -0
  110. package/dist/src/utils/pikku-cli-config.js +18 -0
  111. package/dist/tsconfig.tsbuildinfo +1 -1
  112. package/package.json +3 -3
  113. package/skills/pikku-emails/SKILL.md +157 -0
  114. package/console-app/assets/index-AwGnKyWe.js +0 -254
@@ -11,8 +11,8 @@ async function checkForUpdate() {
11
11
  })
12
12
  if (!res.ok) return
13
13
  const { version: latest } = await res.json()
14
- if (latest !== '0.12.43') {
15
- process.stderr.write(`\n Update available 0.12.43 → ${latest}\n brew upgrade pikku or npm install -g @pikku/cli\n\n`)
14
+ if (latest !== '0.12.45') {
15
+ process.stderr.write(`\n Update available 0.12.45 → ${latest}\n brew upgrade pikku or npm install -g @pikku/cli\n\n`)
16
16
  }
17
17
  } catch {}
18
18
  }
@@ -67,6 +67,18 @@ wireCLI({
67
67
  default: false,
68
68
  short: 'j',
69
69
  },
70
+ failOnError: {
71
+ description: 'Fail the build on error-severity diagnostics (e.g. data-classification leaks). Default: only critical diagnostics fail.',
72
+ default: false,
73
+ },
74
+ failOnWarn: {
75
+ description: 'Fail the build on warn-severity diagnostics (implies --fail-on-error).',
76
+ default: false,
77
+ },
78
+ failOnCritical: {
79
+ description: 'Fail the build on critical diagnostics. Always on; accepted for symmetry with --fail-on-error/--fail-on-warn.',
80
+ default: true,
81
+ },
70
82
  userSessionType: {
71
83
  description: 'Specify which UserSession type to use (when multiple exist)',
72
84
  },
@@ -4,7 +4,7 @@ export declare const fabricCommands: {
4
4
  root: string;
5
5
  findings: {
6
6
  id: string;
7
- severity: "info" | "warn" | "error";
7
+ severity: "warn" | "error" | "info";
8
8
  message: string;
9
9
  path: string;
10
10
  fixHint: string;
@@ -14,7 +14,7 @@ export declare const fabricCommands: {
14
14
  root: string;
15
15
  findings: {
16
16
  id: string;
17
- severity: "info" | "warn" | "error";
17
+ severity: "warn" | "error" | "info";
18
18
  message: string;
19
19
  path: string;
20
20
  fixHint: string;
@@ -24,7 +24,7 @@ export declare const fabricCommands: {
24
24
  root: string;
25
25
  findings: {
26
26
  id: string;
27
- severity: "info" | "warn" | "error";
27
+ severity: "warn" | "error" | "info";
28
28
  message: string;
29
29
  path: string;
30
30
  fixHint: string;
@@ -172,6 +172,64 @@ export declare const fabricCommands: {
172
172
  deploymentId: string;
173
173
  stageId: string;
174
174
  }, "session" | "rpc", import("../../types/application-types.js").Services>, undefined, undefined>, import("../../.pikku/pikku-types.gen.js").PikkuMiddleware, (services: import("../../types/application-types.js").SingletonServices, data: any, session?: import("../../types/application-types.js").UserSession | undefined) => void | Promise<void>, string>;
175
+ addon: {
176
+ description: string;
177
+ subcommands: {
178
+ publish: import("@pikku/core/cli").CoreCLICommandConfig<import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
179
+ dir?: string | undefined;
180
+ apiUrl?: string | undefined;
181
+ }, {
182
+ id: string;
183
+ name: string;
184
+ version: string;
185
+ publisher: string | null;
186
+ }, "session" | "rpc", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
187
+ dir?: string | undefined;
188
+ apiUrl?: string | undefined;
189
+ }, {
190
+ id: string;
191
+ name: string;
192
+ version: string;
193
+ publisher: string | null;
194
+ }, "session" | "rpc", import("../../types/application-types.js").Services> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
195
+ dir?: string | undefined;
196
+ apiUrl?: string | undefined;
197
+ }, {
198
+ id: string;
199
+ name: string;
200
+ version: string;
201
+ publisher: string | null;
202
+ }, "session" | "rpc", import("../../types/application-types.js").Services>, undefined, undefined>, import("../../.pikku/pikku-types.gen.js").PikkuMiddleware, (services: import("../../types/application-types.js").SingletonServices, data: any, session?: import("../../types/application-types.js").UserSession | undefined) => void | Promise<void>, string>;
203
+ add: import("@pikku/core/cli").CoreCLICommandConfig<import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
204
+ id: string;
205
+ dir?: string | undefined;
206
+ apiUrl?: string | undefined;
207
+ }, {
208
+ id: string;
209
+ name: string;
210
+ version: string;
211
+ path: string;
212
+ }, "session" | "rpc", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
213
+ id: string;
214
+ dir?: string | undefined;
215
+ apiUrl?: string | undefined;
216
+ }, {
217
+ id: string;
218
+ name: string;
219
+ version: string;
220
+ path: string;
221
+ }, "session" | "rpc", import("../../types/application-types.js").Services> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
222
+ id: string;
223
+ dir?: string | undefined;
224
+ apiUrl?: string | undefined;
225
+ }, {
226
+ id: string;
227
+ name: string;
228
+ version: string;
229
+ path: string;
230
+ }, "session" | "rpc", import("../../types/application-types.js").Services>, undefined, undefined>, import("../../.pikku/pikku-types.gen.js").PikkuMiddleware, (services: import("../../types/application-types.js").SingletonServices, data: any, session?: import("../../types/application-types.js").UserSession | undefined) => void | Promise<void>, string>;
231
+ };
232
+ };
175
233
  deploy: {
176
234
  description: string;
177
235
  subcommands: {
@@ -39,7 +39,9 @@ import { FabricDomainsAdd } from './functions/domains-add.function.js';
39
39
  import { FabricDomainsRemove } from './functions/domains-remove.function.js';
40
40
  import { FabricLLMKey, renderLLMKey } from './functions/llm-key.function.js';
41
41
  import { FabricValidate, renderValidate, } from './functions/validate.function.js';
42
- import { FabricSmoke, renderSmoke, } from './functions/smoke.function.js';
42
+ import { FabricSmoke, renderSmoke } from './functions/smoke.function.js';
43
+ import { FabricPublish } from './functions/publish.function.js';
44
+ import { FabricAdd } from './functions/add.function.js';
43
45
  export const fabricCommands = defineCLICommands({
44
46
  validate: pikkuCLICommand({
45
47
  func: FabricValidate,
@@ -103,6 +105,30 @@ export const fabricCommands = defineCLICommands({
103
105
  },
104
106
  },
105
107
  }),
108
+ addon: {
109
+ description: 'Publish and install Fabric community-registry addons',
110
+ subcommands: {
111
+ publish: pikkuCLICommand({
112
+ parameters: '[dir]',
113
+ func: FabricPublish,
114
+ description: 'Publish the addon in this directory to the community registry (pack + upload)',
115
+ options: {
116
+ apiUrl: { description: 'Override the fabric-api URL for this call' },
117
+ },
118
+ }),
119
+ add: pikkuCLICommand({
120
+ parameters: '<id>',
121
+ func: FabricAdd,
122
+ description: 'Install an addon from the community registry into addons/ (shadcn-style)',
123
+ options: {
124
+ dir: {
125
+ description: 'Addon dir (overrides pikku.config.json addons.addonDir, default addons/)',
126
+ },
127
+ apiUrl: { description: 'Override the fabric-api URL for this call' },
128
+ },
129
+ }),
130
+ },
131
+ },
106
132
  deploy: {
107
133
  description: 'Plan and apply deploys for a named branch or production',
108
134
  subcommands: {
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod';
2
+ export declare const FabricAddInput: z.ZodObject<{
3
+ id: z.ZodString;
4
+ dir: z.ZodOptional<z.ZodString>;
5
+ apiUrl: z.ZodOptional<z.ZodString>;
6
+ }, z.core.$strip>;
7
+ export declare const FabricAddOutput: z.ZodObject<{
8
+ id: z.ZodString;
9
+ name: z.ZodString;
10
+ version: z.ZodString;
11
+ path: z.ZodString;
12
+ }, z.core.$strip>;
13
+ /**
14
+ * Install a community-registry addon shadcn-style: the source is copied into
15
+ * `<addonDir>/<name>/` (default `addons/`, top-level so it sits outside the
16
+ * app's TS scan and never collides with the project's own CoreConfig). The dir
17
+ * is registered as a yarn workspace, so `yarn install` symlinks it into
18
+ * node_modules and `wireAddon({ package })` resolves it by name unchanged.
19
+ *
20
+ * Provenance (registry id + version) is recorded in pikku-addons.json, which is
21
+ * CLI-owned and survives the user editing/forking the copied source.
22
+ */
23
+ export declare const FabricAdd: import("../../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
24
+ id: string;
25
+ dir?: string | undefined;
26
+ apiUrl?: string | undefined;
27
+ }, {
28
+ id: string;
29
+ name: string;
30
+ version: string;
31
+ path: string;
32
+ }, "session" | "rpc", import("../../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
33
+ id: string;
34
+ dir?: string | undefined;
35
+ apiUrl?: string | undefined;
36
+ }, {
37
+ id: string;
38
+ name: string;
39
+ version: string;
40
+ path: string;
41
+ }, "session" | "rpc", import("../../../types/application-types.js").Services> | import("../../../.pikku/pikku-types.gen.js").PikkuFunction<{
42
+ id: string;
43
+ dir?: string | undefined;
44
+ apiUrl?: string | undefined;
45
+ }, {
46
+ id: string;
47
+ name: string;
48
+ version: string;
49
+ path: string;
50
+ }, "session" | "rpc", import("../../../types/application-types.js").Services>, undefined, undefined>;
@@ -0,0 +1,144 @@
1
+ import { z } from 'zod';
2
+ import { readFile, mkdir, rm, writeFile, rename } from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+ import { dirname, isAbsolute, join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ import { execFileSync } from 'node:child_process';
7
+ import { pikkuSessionlessFunc } from '../../../.pikku/pikku-types.gen.js';
8
+ import { resolveApiContext } from '../lib/config.js';
9
+ export const FabricAddInput = z.object({
10
+ id: z.string(),
11
+ dir: z.string().optional(),
12
+ apiUrl: z.string().optional(),
13
+ });
14
+ export const FabricAddOutput = z.object({
15
+ id: z.string(),
16
+ name: z.string(),
17
+ version: z.string(),
18
+ path: z.string(),
19
+ });
20
+ /** Walk up from cwd to find the project root (the dir with package.json). */
21
+ function resolveProjectRoot() {
22
+ let dir = process.cwd();
23
+ while (true) {
24
+ if (existsSync(join(dir, 'package.json')))
25
+ return dir;
26
+ const parent = dirname(dir);
27
+ if (parent === dir)
28
+ return process.cwd();
29
+ dir = parent;
30
+ }
31
+ }
32
+ /** Read `addons.addonDir` from pikku.config.json if present (json only). */
33
+ async function readAddonDirFromConfig(root) {
34
+ const cfgPath = join(root, 'pikku.config.json');
35
+ if (!existsSync(cfgPath))
36
+ return undefined;
37
+ try {
38
+ const cfg = JSON.parse(await readFile(cfgPath, 'utf8'));
39
+ return cfg.addons?.addonDir;
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
45
+ /** Ensure the root package.json `workspaces` glob covers `<addonDir>/*`, so a
46
+ * later `yarn install` symlinks the addon into node_modules and `wireAddon`
47
+ * resolves it by package name. */
48
+ async function ensureWorkspaceGlob(root, addonDir) {
49
+ const pkgPath = join(root, 'package.json');
50
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
51
+ const glob = `${addonDir}/*`;
52
+ const objectForm = !Array.isArray(pkg.workspaces) && pkg.workspaces != null;
53
+ const list = Array.isArray(pkg.workspaces)
54
+ ? pkg.workspaces
55
+ : (pkg.workspaces?.packages ?? []);
56
+ if (list.includes(glob))
57
+ return;
58
+ list.push(glob);
59
+ if (objectForm)
60
+ pkg.workspaces.packages = list;
61
+ else
62
+ pkg.workspaces = list;
63
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
64
+ }
65
+ /** Record install provenance in pikku-addons.json — CLI-owned, so we know which
66
+ * registry package + version a folder came from even after the user forks it. */
67
+ async function recordInstall(root, name, rec) {
68
+ const p = join(root, 'pikku-addons.json');
69
+ let data = {};
70
+ if (existsSync(p)) {
71
+ try {
72
+ data = JSON.parse(await readFile(p, 'utf8'));
73
+ }
74
+ catch {
75
+ data = {};
76
+ }
77
+ }
78
+ data[name] = rec;
79
+ await writeFile(p, JSON.stringify(data, null, 2) + '\n');
80
+ }
81
+ /**
82
+ * Install a community-registry addon shadcn-style: the source is copied into
83
+ * `<addonDir>/<name>/` (default `addons/`, top-level so it sits outside the
84
+ * app's TS scan and never collides with the project's own CoreConfig). The dir
85
+ * is registered as a yarn workspace, so `yarn install` symlinks it into
86
+ * node_modules and `wireAddon({ package })` resolves it by name unchanged.
87
+ *
88
+ * Provenance (registry id + version) is recorded in pikku-addons.json, which is
89
+ * CLI-owned and survives the user editing/forking the copied source.
90
+ */
91
+ export const FabricAdd = pikkuSessionlessFunc({
92
+ description: 'Install an addon from the Fabric community registry into addons/ (shadcn-style).',
93
+ input: FabricAddInput,
94
+ output: FabricAddOutput,
95
+ func: async (_services, { id, dir, apiUrl: apiUrlOverride }) => {
96
+ const ctx = await resolveApiContext({ apiUrlOverride });
97
+ // 1. resolve a presigned download URL (public read)
98
+ const metaRes = await fetch(`${ctx.apiUrl}/registry/packages/${encodeURIComponent(id)}/download`);
99
+ if (!metaRes.ok)
100
+ throw new Error(`download lookup failed → ${metaRes.status}: ${await metaRes.text()}`);
101
+ const { url } = (await metaRes.json());
102
+ // 2. fetch the artifact
103
+ const dl = await fetch(url);
104
+ if (!dl.ok)
105
+ throw new Error(`artifact fetch failed → ${dl.status}`);
106
+ const artifact = Buffer.from(await dl.arrayBuffer());
107
+ const root = resolveProjectRoot();
108
+ const addonDir = dir ?? (await readAddonDirFromConfig(root)) ?? 'addons';
109
+ const addonRoot = isAbsolute(addonDir) ? addonDir : join(root, addonDir);
110
+ // 3. stage inside addonRoot (same filesystem — no EXDEV on the final move)
111
+ // and strip npm-pack's `package/` prefix. The dir name isn't known until
112
+ // we read the artifact's package.json.
113
+ await mkdir(addonRoot, { recursive: true });
114
+ const staging = join(addonRoot, `.pikku-add-${id}-${Date.now()}`);
115
+ await mkdir(staging, { recursive: true });
116
+ const tmp = join(tmpdir(), `pikku-add-${id}.tgz`);
117
+ await writeFile(tmp, artifact);
118
+ try {
119
+ execFileSync('tar', ['-xzf', tmp, '-C', staging, '--strip-components=1']);
120
+ await rm(tmp, { force: true });
121
+ const pkg = JSON.parse(await readFile(join(staging, 'package.json'), 'utf8'));
122
+ if (!pkg.name)
123
+ throw new Error('artifact package.json is missing a "name" field');
124
+ const version = pkg.version ?? '0.0.0';
125
+ // shadcn copy: folder is the last segment of the (scoped) package name
126
+ const folder = pkg.name.split('/').pop();
127
+ const target = join(addonRoot, folder);
128
+ await rm(target, { recursive: true, force: true });
129
+ await rename(staging, target);
130
+ // 4. register the workspace glob + record provenance (skip glob for an
131
+ // absolute --dir override — it can't be a relative workspace pattern)
132
+ if (!isAbsolute(addonDir))
133
+ await ensureWorkspaceGlob(root, addonDir);
134
+ await recordInstall(root, pkg.name, { id, version });
135
+ console.log(`[fabric] installed ${pkg.name}@${version} → ${target}`);
136
+ console.log('[fabric] run `yarn install` to link it into node_modules');
137
+ return { id, name: pkg.name, version, path: target };
138
+ }
139
+ finally {
140
+ await rm(staging, { recursive: true, force: true });
141
+ await rm(tmp, { force: true });
142
+ }
143
+ },
144
+ });
@@ -0,0 +1,45 @@
1
+ import { z } from 'zod';
2
+ export declare const FabricPublishInput: z.ZodObject<{
3
+ dir: z.ZodOptional<z.ZodString>;
4
+ apiUrl: z.ZodOptional<z.ZodString>;
5
+ }, z.core.$strip>;
6
+ export declare const FabricPublishOutput: z.ZodObject<{
7
+ id: z.ZodString;
8
+ name: z.ZodString;
9
+ version: z.ZodString;
10
+ publisher: z.ZodNullable<z.ZodString>;
11
+ }, z.core.$strip>;
12
+ /**
13
+ * Publish a package to the Fabric community registry. Packs the directory into
14
+ * a gzipped tar, requests a short-lived presigned upload URL, PUTs the artifact
15
+ * to R2, then finalizes the publish so the catalogue indexes it. Authenticated
16
+ * as the logged-in user (the package is attributed to their org or person).
17
+ *
18
+ * Generating the package contents (`.pikku/` meta etc.) is a separate step;
19
+ * this command only packages + uploads what's already in the directory.
20
+ */
21
+ export declare const FabricPublish: import("../../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
22
+ dir?: string | undefined;
23
+ apiUrl?: string | undefined;
24
+ }, {
25
+ id: string;
26
+ name: string;
27
+ version: string;
28
+ publisher: string | null;
29
+ }, "session" | "rpc", import("../../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
30
+ dir?: string | undefined;
31
+ apiUrl?: string | undefined;
32
+ }, {
33
+ id: string;
34
+ name: string;
35
+ version: string;
36
+ publisher: string | null;
37
+ }, "session" | "rpc", import("../../../types/application-types.js").Services> | import("../../../.pikku/pikku-types.gen.js").PikkuFunction<{
38
+ dir?: string | undefined;
39
+ apiUrl?: string | undefined;
40
+ }, {
41
+ id: string;
42
+ name: string;
43
+ version: string;
44
+ publisher: string | null;
45
+ }, "session" | "rpc", import("../../../types/application-types.js").Services>, undefined, undefined>;
@@ -0,0 +1,85 @@
1
+ import { z } from 'zod';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { existsSync, readFileSync, mkdirSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ import { execFileSync } from 'node:child_process';
7
+ import { pikkuSessionlessFunc } from '../../../.pikku/pikku-types.gen.js';
8
+ import { resolveApiContext } from '../lib/config.js';
9
+ export const FabricPublishInput = z.object({
10
+ dir: z.string().optional(),
11
+ apiUrl: z.string().optional(),
12
+ });
13
+ export const FabricPublishOutput = z.object({
14
+ id: z.string(),
15
+ name: z.string(),
16
+ version: z.string(),
17
+ publisher: z.string().nullable(),
18
+ });
19
+ /**
20
+ * Publish a package to the Fabric community registry. Packs the directory into
21
+ * a gzipped tar, requests a short-lived presigned upload URL, PUTs the artifact
22
+ * to R2, then finalizes the publish so the catalogue indexes it. Authenticated
23
+ * as the logged-in user (the package is attributed to their org or person).
24
+ *
25
+ * Generating the package contents (`.pikku/` meta etc.) is a separate step;
26
+ * this command only packages + uploads what's already in the directory.
27
+ */
28
+ export const FabricPublish = pikkuSessionlessFunc({
29
+ description: 'Publish a package directory to the Fabric community registry.',
30
+ input: FabricPublishInput,
31
+ output: FabricPublishOutput,
32
+ func: async (_services, { dir, apiUrl: apiUrlOverride }) => {
33
+ const ctx = await resolveApiContext({ apiUrlOverride });
34
+ if (!ctx.token)
35
+ throw new Error('Not logged in. Run `pikku fabric login` first.');
36
+ const packageDir = dir ?? process.cwd();
37
+ const pkgPath = join(packageDir, 'package.json');
38
+ if (!existsSync(pkgPath))
39
+ throw new Error(`No package.json found in ${packageDir}`);
40
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
41
+ if (!pkg.name || !pkg.version)
42
+ throw new Error('package.json must have a name and version');
43
+ // Pack via `npm pack` so the artifact honours the package's `files` field
44
+ // (ship src/.pikku/types, not build/VCS noise) and matches the layout a
45
+ // normal install produces. npm nests contents under `package/`; the
46
+ // registry ingestion and `pikku fabric add` both handle that prefix.
47
+ const packDir = join(tmpdir(), `pikku-publish-${Date.now()}`);
48
+ mkdirSync(packDir, { recursive: true });
49
+ const packOut = execFileSync('npm', ['pack', '--json', '--pack-destination', packDir], { cwd: packageDir, encoding: 'utf8' });
50
+ const tgzName = JSON.parse(packOut)[0].filename;
51
+ const artifact = readFileSync(join(packDir, tgzName));
52
+ const headers = {
53
+ authorization: `Bearer ${ctx.token}`,
54
+ 'content-type': 'application/json',
55
+ };
56
+ const post = async (path, body) => {
57
+ const r = await fetch(`${ctx.apiUrl}${path}`, {
58
+ method: 'POST',
59
+ headers,
60
+ body: JSON.stringify(body),
61
+ });
62
+ if (!r.ok)
63
+ throw new Error(`POST ${path} → ${r.status}: ${await r.text()}`);
64
+ return r.json();
65
+ };
66
+ // 1. presigned upload URL (short-lived)
67
+ const { uploadUrl, artifactKey } = await post('/registry/packages/publish-url', { packageName: pkg.name, version: pkg.version });
68
+ // 2. PUT the artifact to the exact signed URL (no extra headers — the URL
69
+ // is signed over host only; mismatched headers break the signature).
70
+ const put = await fetch(uploadUrl, { method: 'PUT', body: artifact });
71
+ if (!put.ok)
72
+ throw new Error(`upload failed → ${put.status}: ${await put.text()}`);
73
+ // 3. finalize — server reads the artifact back, extracts meta, indexes it
74
+ const entry = await post('/registry/packages/publish', { artifactKey });
75
+ const publisher = entry.publisher?.name ?? null;
76
+ console.log(`[fabric] published ${entry.name}@${entry.version} (id=${entry.id})` +
77
+ (publisher ? ` as ${publisher}` : ''));
78
+ return {
79
+ id: entry.id,
80
+ name: entry.name,
81
+ version: entry.version,
82
+ publisher,
83
+ };
84
+ },
85
+ });
@@ -6,9 +6,9 @@ export declare const FabricValidateOutput: z.ZodObject<{
6
6
  findings: z.ZodArray<z.ZodObject<{
7
7
  id: z.ZodString;
8
8
  severity: z.ZodEnum<{
9
- info: "info";
10
9
  warn: "warn";
11
10
  error: "error";
11
+ info: "info";
12
12
  }>;
13
13
  message: z.ZodString;
14
14
  path: z.ZodString;
@@ -6,9 +6,9 @@ export declare const FabricValidateOutput: z.ZodObject<{
6
6
  findings: z.ZodArray<z.ZodObject<{
7
7
  id: z.ZodString;
8
8
  severity: z.ZodEnum<{
9
- info: "info";
10
9
  warn: "warn";
11
10
  error: "error";
11
+ info: "info";
12
12
  }>;
13
13
  message: z.ZodString;
14
14
  path: z.ZodString;
@@ -21,7 +21,7 @@ export declare const FabricValidate: import("../../../.pikku/pikku-types.gen.js"
21
21
  root: string;
22
22
  findings: {
23
23
  id: string;
24
- severity: "info" | "warn" | "error";
24
+ severity: "warn" | "error" | "info";
25
25
  message: string;
26
26
  path: string;
27
27
  fixHint: string;
@@ -31,7 +31,7 @@ export declare const FabricValidate: import("../../../.pikku/pikku-types.gen.js"
31
31
  root: string;
32
32
  findings: {
33
33
  id: string;
34
- severity: "info" | "warn" | "error";
34
+ severity: "warn" | "error" | "info";
35
35
  message: string;
36
36
  path: string;
37
37
  fixHint: string;
@@ -41,7 +41,7 @@ export declare const FabricValidate: import("../../../.pikku/pikku-types.gen.js"
41
41
  root: string;
42
42
  findings: {
43
43
  id: string;
44
- severity: "info" | "warn" | "error";
44
+ severity: "warn" | "error" | "info";
45
45
  message: string;
46
46
  path: string;
47
47
  fixHint: string;