@spec0/cli 0.1.0

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 (220) hide show
  1. package/AGENTS.md +67 -0
  2. package/LICENSE +21 -0
  3. package/README.md +235 -0
  4. package/dist/commands/api/changelog.d.ts +9 -0
  5. package/dist/commands/api/changelog.d.ts.map +1 -0
  6. package/dist/commands/api/changelog.js +144 -0
  7. package/dist/commands/api/changelog.js.map +1 -0
  8. package/dist/commands/api/index.d.ts +6 -0
  9. package/dist/commands/api/index.d.ts.map +1 -0
  10. package/dist/commands/api/index.js +13 -0
  11. package/dist/commands/api/index.js.map +1 -0
  12. package/dist/commands/api/list.d.ts +10 -0
  13. package/dist/commands/api/list.d.ts.map +1 -0
  14. package/dist/commands/api/list.js +79 -0
  15. package/dist/commands/api/list.js.map +1 -0
  16. package/dist/commands/api/show.d.ts +9 -0
  17. package/dist/commands/api/show.d.ts.map +1 -0
  18. package/dist/commands/api/show.js +88 -0
  19. package/dist/commands/api/show.js.map +1 -0
  20. package/dist/commands/auth.d.ts +6 -0
  21. package/dist/commands/auth.d.ts.map +1 -0
  22. package/dist/commands/auth.js +166 -0
  23. package/dist/commands/auth.js.map +1 -0
  24. package/dist/commands/ci/generate.d.ts +10 -0
  25. package/dist/commands/ci/generate.d.ts.map +1 -0
  26. package/dist/commands/ci/generate.js +72 -0
  27. package/dist/commands/ci/generate.js.map +1 -0
  28. package/dist/commands/ci/index.d.ts +3 -0
  29. package/dist/commands/ci/index.d.ts.map +1 -0
  30. package/dist/commands/ci/index.js +6 -0
  31. package/dist/commands/ci/index.js.map +1 -0
  32. package/dist/commands/commands.d.ts +19 -0
  33. package/dist/commands/commands.d.ts.map +1 -0
  34. package/dist/commands/commands.js +127 -0
  35. package/dist/commands/commands.js.map +1 -0
  36. package/dist/commands/diff.d.ts +6 -0
  37. package/dist/commands/diff.d.ts.map +1 -0
  38. package/dist/commands/diff.js +102 -0
  39. package/dist/commands/diff.js.map +1 -0
  40. package/dist/commands/doctor.d.ts +11 -0
  41. package/dist/commands/doctor.d.ts.map +1 -0
  42. package/dist/commands/doctor.js +54 -0
  43. package/dist/commands/doctor.js.map +1 -0
  44. package/dist/commands/init.d.ts +6 -0
  45. package/dist/commands/init.d.ts.map +1 -0
  46. package/dist/commands/init.js +67 -0
  47. package/dist/commands/init.js.map +1 -0
  48. package/dist/commands/lint.d.ts +6 -0
  49. package/dist/commands/lint.d.ts.map +1 -0
  50. package/dist/commands/lint.js +109 -0
  51. package/dist/commands/lint.js.map +1 -0
  52. package/dist/commands/log.d.ts +6 -0
  53. package/dist/commands/log.d.ts.map +1 -0
  54. package/dist/commands/log.js +95 -0
  55. package/dist/commands/log.js.map +1 -0
  56. package/dist/commands/mcp.d.ts +6 -0
  57. package/dist/commands/mcp.d.ts.map +1 -0
  58. package/dist/commands/mcp.js +59 -0
  59. package/dist/commands/mcp.js.map +1 -0
  60. package/dist/commands/mock/create.d.ts +14 -0
  61. package/dist/commands/mock/create.d.ts.map +1 -0
  62. package/dist/commands/mock/create.js +75 -0
  63. package/dist/commands/mock/create.js.map +1 -0
  64. package/dist/commands/mock/index.d.ts +9 -0
  65. package/dist/commands/mock/index.d.ts.map +1 -0
  66. package/dist/commands/mock/index.js +18 -0
  67. package/dist/commands/mock/index.js.map +1 -0
  68. package/dist/commands/mock/list.d.ts +8 -0
  69. package/dist/commands/mock/list.d.ts.map +1 -0
  70. package/dist/commands/mock/list.js +52 -0
  71. package/dist/commands/mock/list.js.map +1 -0
  72. package/dist/commands/mock/show.d.ts +9 -0
  73. package/dist/commands/mock/show.d.ts.map +1 -0
  74. package/dist/commands/mock/show.js +68 -0
  75. package/dist/commands/mock/show.js.map +1 -0
  76. package/dist/commands/mock/url.d.ts +10 -0
  77. package/dist/commands/mock/url.d.ts.map +1 -0
  78. package/dist/commands/mock/url.js +42 -0
  79. package/dist/commands/mock/url.js.map +1 -0
  80. package/dist/commands/mock.d.ts +6 -0
  81. package/dist/commands/mock.d.ts.map +1 -0
  82. package/dist/commands/mock.js +116 -0
  83. package/dist/commands/mock.js.map +1 -0
  84. package/dist/commands/publish.d.ts +11 -0
  85. package/dist/commands/publish.d.ts.map +1 -0
  86. package/dist/commands/publish.js +242 -0
  87. package/dist/commands/publish.js.map +1 -0
  88. package/dist/commands/pull.d.ts +6 -0
  89. package/dist/commands/pull.d.ts.map +1 -0
  90. package/dist/commands/pull.js +62 -0
  91. package/dist/commands/pull.js.map +1 -0
  92. package/dist/commands/push.d.ts +18 -0
  93. package/dist/commands/push.d.ts.map +1 -0
  94. package/dist/commands/push.js +302 -0
  95. package/dist/commands/push.js.map +1 -0
  96. package/dist/commands/register.d.ts +6 -0
  97. package/dist/commands/register.d.ts.map +1 -0
  98. package/dist/commands/register.js +213 -0
  99. package/dist/commands/register.js.map +1 -0
  100. package/dist/commands/search.d.ts +6 -0
  101. package/dist/commands/search.d.ts.map +1 -0
  102. package/dist/commands/search.js +58 -0
  103. package/dist/commands/search.js.map +1 -0
  104. package/dist/commands/status.d.ts +6 -0
  105. package/dist/commands/status.d.ts.map +1 -0
  106. package/dist/commands/status.js +86 -0
  107. package/dist/commands/status.js.map +1 -0
  108. package/dist/commands/sync-status.d.ts +14 -0
  109. package/dist/commands/sync-status.d.ts.map +1 -0
  110. package/dist/commands/sync-status.js +119 -0
  111. package/dist/commands/sync-status.js.map +1 -0
  112. package/dist/commands/team.d.ts +6 -0
  113. package/dist/commands/team.d.ts.map +1 -0
  114. package/dist/commands/team.js +30 -0
  115. package/dist/commands/team.js.map +1 -0
  116. package/dist/commands/version.d.ts +6 -0
  117. package/dist/commands/version.d.ts.map +1 -0
  118. package/dist/commands/version.js +31 -0
  119. package/dist/commands/version.js.map +1 -0
  120. package/dist/index.d.ts +3 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +61 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/lib/agent-mode.d.ts +25 -0
  125. package/dist/lib/agent-mode.d.ts.map +1 -0
  126. package/dist/lib/agent-mode.js +36 -0
  127. package/dist/lib/agent-mode.js.map +1 -0
  128. package/dist/lib/api-client.d.ts +37 -0
  129. package/dist/lib/api-client.d.ts.map +1 -0
  130. package/dist/lib/api-client.js +144 -0
  131. package/dist/lib/api-client.js.map +1 -0
  132. package/dist/lib/auth-context.d.ts +18 -0
  133. package/dist/lib/auth-context.d.ts.map +1 -0
  134. package/dist/lib/auth-context.js +48 -0
  135. package/dist/lib/auth-context.js.map +1 -0
  136. package/dist/lib/breaking-change.d.ts +12 -0
  137. package/dist/lib/breaking-change.d.ts.map +1 -0
  138. package/dist/lib/breaking-change.js +10 -0
  139. package/dist/lib/breaking-change.js.map +1 -0
  140. package/dist/lib/ci-detect.d.ts +33 -0
  141. package/dist/lib/ci-detect.d.ts.map +1 -0
  142. package/dist/lib/ci-detect.js +136 -0
  143. package/dist/lib/ci-detect.js.map +1 -0
  144. package/dist/lib/cli-spec-path.d.ts +6 -0
  145. package/dist/lib/cli-spec-path.d.ts.map +1 -0
  146. package/dist/lib/cli-spec-path.js +34 -0
  147. package/dist/lib/cli-spec-path.js.map +1 -0
  148. package/dist/lib/codeowners.d.ts +11 -0
  149. package/dist/lib/codeowners.d.ts.map +1 -0
  150. package/dist/lib/codeowners.js +43 -0
  151. package/dist/lib/codeowners.js.map +1 -0
  152. package/dist/lib/config.d.ts +24 -0
  153. package/dist/lib/config.d.ts.map +1 -0
  154. package/dist/lib/config.js +49 -0
  155. package/dist/lib/config.js.map +1 -0
  156. package/dist/lib/deprecation.d.ts +17 -0
  157. package/dist/lib/deprecation.d.ts.map +1 -0
  158. package/dist/lib/deprecation.js +22 -0
  159. package/dist/lib/deprecation.js.map +1 -0
  160. package/dist/lib/doctor.d.ts +40 -0
  161. package/dist/lib/doctor.d.ts.map +1 -0
  162. package/dist/lib/doctor.js +107 -0
  163. package/dist/lib/doctor.js.map +1 -0
  164. package/dist/lib/exit-codes.d.ts +51 -0
  165. package/dist/lib/exit-codes.d.ts.map +1 -0
  166. package/dist/lib/exit-codes.js +78 -0
  167. package/dist/lib/exit-codes.js.map +1 -0
  168. package/dist/lib/lint.d.ts +18 -0
  169. package/dist/lib/lint.d.ts.map +1 -0
  170. package/dist/lib/lint.js +41 -0
  171. package/dist/lib/lint.js.map +1 -0
  172. package/dist/lib/local-config-yaml.d.ts +14 -0
  173. package/dist/lib/local-config-yaml.d.ts.map +1 -0
  174. package/dist/lib/local-config-yaml.js +29 -0
  175. package/dist/lib/local-config-yaml.js.map +1 -0
  176. package/dist/lib/output/index.d.ts +69 -0
  177. package/dist/lib/output/index.d.ts.map +1 -0
  178. package/dist/lib/output/index.js +120 -0
  179. package/dist/lib/output/index.js.map +1 -0
  180. package/dist/lib/output/table.d.ts +19 -0
  181. package/dist/lib/output/table.d.ts.map +1 -0
  182. package/dist/lib/output/table.js +48 -0
  183. package/dist/lib/output/table.js.map +1 -0
  184. package/dist/lib/output.d.ts +32 -0
  185. package/dist/lib/output.d.ts.map +1 -0
  186. package/dist/lib/output.js +64 -0
  187. package/dist/lib/output.js.map +1 -0
  188. package/dist/lib/platform-defaults.d.ts +27 -0
  189. package/dist/lib/platform-defaults.d.ts.map +1 -0
  190. package/dist/lib/platform-defaults.js +36 -0
  191. package/dist/lib/platform-defaults.js.map +1 -0
  192. package/dist/lib/ref-resolver.d.ts +37 -0
  193. package/dist/lib/ref-resolver.d.ts.map +1 -0
  194. package/dist/lib/ref-resolver.js +56 -0
  195. package/dist/lib/ref-resolver.js.map +1 -0
  196. package/dist/lib/registry-ref.d.ts +10 -0
  197. package/dist/lib/registry-ref.d.ts.map +1 -0
  198. package/dist/lib/registry-ref.js +18 -0
  199. package/dist/lib/registry-ref.js.map +1 -0
  200. package/dist/lib/spec-finder.d.ts +6 -0
  201. package/dist/lib/spec-finder.d.ts.map +1 -0
  202. package/dist/lib/spec-finder.js +42 -0
  203. package/dist/lib/spec-finder.js.map +1 -0
  204. package/dist/lib/update-check.d.ts +12 -0
  205. package/dist/lib/update-check.d.ts.map +1 -0
  206. package/dist/lib/update-check.js +62 -0
  207. package/dist/lib/update-check.js.map +1 -0
  208. package/dist/lib/version.d.ts +17 -0
  209. package/dist/lib/version.d.ts.map +1 -0
  210. package/dist/lib/version.js +33 -0
  211. package/dist/lib/version.js.map +1 -0
  212. package/dist/lib/winspect-yaml.d.ts +14 -0
  213. package/dist/lib/winspect-yaml.d.ts.map +1 -0
  214. package/dist/lib/winspect-yaml.js +28 -0
  215. package/dist/lib/winspect-yaml.js.map +1 -0
  216. package/dist/types.d.ts +664 -0
  217. package/dist/types.d.ts.map +1 -0
  218. package/dist/types.js +6 -0
  219. package/dist/types.js.map +1 -0
  220. package/package.json +87 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * spec0 publish — publish an OpenAPI spec to the public API registry.
3
+ *
4
+ * Unlike `spec0 register` (which is team-scoped and private), `publish` creates
5
+ * org-scoped public APIs accessible via a shareable URL with no authentication.
6
+ *
7
+ * URL convention: {platform}/public/registry/{org-slug}/{api-slug}
8
+ */
9
+ import chalk from "chalk";
10
+ import ora from "ora";
11
+ import { existsSync, readFileSync } from "fs";
12
+ import { runSpectral } from "../lib/lint.js";
13
+ import { formatLintText, formatGitHubAnnotation } from "../lib/output.js";
14
+ import { createOrgApiClient, is401, is402 } from "../lib/api-client.js";
15
+ import { requireOrgContext } from "../lib/auth-context.js";
16
+ import { ExitCode, exit, exitCodeForHttpStatus } from "../lib/exit-codes.js";
17
+ import { resolveCliSpecPathFromFlags } from "../lib/cli-spec-path.js";
18
+ import { resolvedPlatformApiUrl } from "../lib/platform-defaults.js";
19
+ const API_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
20
+ function toSlug(input) {
21
+ return input
22
+ .trim()
23
+ .toLowerCase()
24
+ .replace(/[^a-z0-9]+/g, "-")
25
+ .replace(/^-+|-+$/g, "");
26
+ }
27
+ function publishUsageExamples() {
28
+ return [
29
+ "Examples:",
30
+ " spec0 publish --spec-file ./openapi.yaml --name payments-api --version v1.0.0",
31
+ " spec0 publish ./openapi.yaml --name payments-api --version v1.0.0 --visibility published",
32
+ " spec0 publish --spec-file ./openapi.yaml --public-api-id <uuid> --version v1.1.0",
33
+ " spec0 publish ./openapi.yaml --version v1.0.0 # slug inferred from spec info.title",
34
+ ].join("\n");
35
+ }
36
+ function formatPublishPublicText(output) {
37
+ const lines = [];
38
+ lines.push(chalk.green("Published!"));
39
+ lines.push(` Public URL: ${output.apiUrl}${output.publicUrl}`);
40
+ lines.push(` Version: ${output.version}`);
41
+ lines.push(` Visibility: ${output.visibility}`);
42
+ lines.push(` API ID: ${output.publicApiId}`);
43
+ return lines.join("\n");
44
+ }
45
+ export function registerPublishCommand(program) {
46
+ program
47
+ .command("publish")
48
+ .description("Publish an API spec to the public registry (org-scoped, shareable URL, no team required)")
49
+ .argument("[spec-file]", "Path to OpenAPI spec (or use --spec-file / global --spec-file)")
50
+ .option("--spec-file <path>", "Path to OpenAPI spec file")
51
+ .option("--public-api-id <id>", "Existing public API ID to update")
52
+ .option("--name <slug>", "URL-safe API slug (e.g. payments-api). Inferred from spec info.title if omitted.")
53
+ .option("--title <title>", "Human-readable display title. Defaults to --name if omitted.")
54
+ .option("--description <text>", "Short description of the API")
55
+ .requiredOption("--version <version>", "Version tag (e.g. v1.0.0)")
56
+ .option("--visibility <state>", "Visibility: draft | published | unlisted (default: published)", "published")
57
+ .option("--release-notes <text>", "Release notes for this version")
58
+ .option("--git-sha <sha>", "Git commit SHA for provenance")
59
+ .option("--strict", "Fail on any Spectral lint warning")
60
+ .option("--skip-lint", "Skip lint gate")
61
+ .option("--dry-run", "Validate only, do not call the API")
62
+ .option("--format <fmt>", "Output format: text | json | github", "text")
63
+ .option("--org <org>", "Override default org (UUID)")
64
+ .action(async (specArg, opts, command) => {
65
+ const cwd = process.cwd();
66
+ const specPath = resolveCliSpecPathFromFlags(command, cwd, opts.specFile, specArg);
67
+ if (!specPath || !existsSync(specPath)) {
68
+ console.error(chalk.red("Spec file not found. Pass a path via --spec-file, global spec0 --spec-file, or positional [spec-file].\n"));
69
+ console.error(chalk.yellow(publishUsageExamples()));
70
+ exit(ExitCode.USAGE);
71
+ }
72
+ const publicApiId = opts["publicApiId"];
73
+ const nameOpt = opts.name;
74
+ const titleOpt = opts.title;
75
+ const description = opts.description;
76
+ const version = opts.version;
77
+ const visibilityRaw = opts.visibility ?? "published";
78
+ const releaseNotes = opts["releaseNotes"];
79
+ const gitSha = opts["gitSha"] ?? "";
80
+ const strict = !!opts.strict;
81
+ const skipLint = !!opts.skipLint;
82
+ const format = opts.format ?? "text";
83
+ // Validate visibility
84
+ const validVisibilities = ["draft", "published", "unlisted"];
85
+ if (!validVisibilities.includes(visibilityRaw.toLowerCase())) {
86
+ console.error(chalk.red(`Invalid --visibility '${visibilityRaw}'. Must be: draft | published | unlisted.`));
87
+ exit(ExitCode.USAGE);
88
+ }
89
+ const visibility = visibilityRaw.toUpperCase();
90
+ const openapiSpec = readFileSync(specPath, "utf-8");
91
+ // Infer slug from spec info.title if --name not provided and no --public-api-id
92
+ let apiSlug = nameOpt?.trim();
93
+ if (!apiSlug && !publicApiId) {
94
+ try {
95
+ const titleMatch = openapiSpec.match(/^\s*title:\s*(.+)$/m);
96
+ if (titleMatch) {
97
+ const inferred = toSlug(titleMatch[1].trim().replace(/['"]/g, ""));
98
+ if (inferred && API_SLUG_PATTERN.test(inferred)) {
99
+ if (format === "text") {
100
+ console.log(chalk.yellow(`No --name provided. Inferred slug: "${inferred}"`));
101
+ }
102
+ apiSlug = inferred;
103
+ }
104
+ }
105
+ }
106
+ catch {
107
+ // ignore parse errors
108
+ }
109
+ }
110
+ if (!publicApiId && (!apiSlug || !API_SLUG_PATTERN.test(apiSlug))) {
111
+ console.error(chalk.red(`--name is required (or provide --public-api-id to update existing). ` +
112
+ `Use a slug like 'payments-api' (lowercase letters, digits, hyphens).\n`));
113
+ console.error(chalk.yellow(publishUsageExamples()));
114
+ exit(ExitCode.USAGE);
115
+ }
116
+ const title = titleOpt?.trim() || apiSlug;
117
+ // -----------------------------------------------------------------------
118
+ // Lint gate
119
+ // -----------------------------------------------------------------------
120
+ const spinner = ora("Linting...").start();
121
+ if (!skipLint) {
122
+ try {
123
+ const lintResult = await runSpectral(specPath);
124
+ spinner.succeed("Lint passed");
125
+ if (format === "github") {
126
+ for (const e of lintResult.errors) {
127
+ console.log(formatGitHubAnnotation(specPath, e.line ?? 0, "error", e.message));
128
+ }
129
+ for (const w of lintResult.warnings) {
130
+ console.log(formatGitHubAnnotation(specPath, w.line ?? 0, "warning", w.message));
131
+ }
132
+ }
133
+ else if (format !== "json") {
134
+ console.log(formatLintText(lintResult));
135
+ }
136
+ if (lintResult.errors.length > 0) {
137
+ console.error(chalk.red("Lint errors block publish. Fix them or use --skip-lint."));
138
+ exit(ExitCode.VALIDATION);
139
+ }
140
+ if (strict && lintResult.warnings.length > 0) {
141
+ console.error(chalk.red("Strict mode: warnings block publish."));
142
+ exit(ExitCode.VALIDATION);
143
+ }
144
+ }
145
+ catch (e) {
146
+ spinner.fail("Lint failed");
147
+ console.error(e);
148
+ exit(ExitCode.GENERIC);
149
+ }
150
+ }
151
+ else {
152
+ spinner.succeed("Lint skipped");
153
+ }
154
+ // -----------------------------------------------------------------------
155
+ // Org context
156
+ // -----------------------------------------------------------------------
157
+ let ctx;
158
+ try {
159
+ ctx = requireOrgContext(opts.org);
160
+ }
161
+ catch (e) {
162
+ console.error(chalk.red(e.message));
163
+ exit(ExitCode.AUTH_MISSING);
164
+ }
165
+ if (opts.dryRun) {
166
+ console.log(chalk.green(`Dry run — would publish apiSlug=${apiSlug ?? "-"} version=${version} visibility=${visibility}`));
167
+ return;
168
+ }
169
+ // -----------------------------------------------------------------------
170
+ // API call
171
+ // -----------------------------------------------------------------------
172
+ spinner.start("Publishing to public registry...");
173
+ const client = createOrgApiClient(ctx);
174
+ const body = {
175
+ publicApiId,
176
+ apiSlug,
177
+ title,
178
+ description,
179
+ visibility,
180
+ version,
181
+ openapiSpec,
182
+ gitSha: gitSha || undefined,
183
+ releaseNotes: releaseNotes || undefined,
184
+ };
185
+ try {
186
+ const reg = (await client.postJson("/api-management/cli/v1/public-publish", body));
187
+ spinner.stop();
188
+ const resolvedApiId = reg.publicApiId ?? "";
189
+ const resolvedPublicUrl = reg.publicUrl ?? `/public/registry/-/${apiSlug ?? ""}`;
190
+ const apiBaseUrl = resolvedPlatformApiUrl();
191
+ if (format === "json") {
192
+ console.log(JSON.stringify({
193
+ publicApiId: resolvedApiId,
194
+ apiSlug: reg.apiSlug ?? apiSlug,
195
+ orgSlug: reg.orgSlug,
196
+ version,
197
+ visibility: reg.visibility ?? visibility,
198
+ created: reg.created ?? false,
199
+ versionCreated: reg.versionCreated ?? false,
200
+ publicUrl: `${apiBaseUrl}${resolvedPublicUrl}`,
201
+ }, null, 2));
202
+ }
203
+ else {
204
+ console.log(formatPublishPublicText({
205
+ publicApiId: resolvedApiId,
206
+ version,
207
+ visibility: reg.visibility ?? visibility,
208
+ publicUrl: resolvedPublicUrl,
209
+ apiUrl: apiBaseUrl,
210
+ }));
211
+ }
212
+ }
213
+ catch (err) {
214
+ spinner.stop();
215
+ if (is401(err)) {
216
+ console.error(chalk.red("Token invalid. Run 'spec0 auth login' to re-authenticate."));
217
+ exit(ExitCode.AUTH_MISSING);
218
+ }
219
+ if (is402(err)) {
220
+ const msg = err?.response?.body?.detail ??
221
+ "Plan limit exceeded. Upgrade your plan to publish more public APIs or use PUBLISHED visibility.";
222
+ console.error(chalk.red(`Plan limit: ${msg}`));
223
+ // 402 isn't in the stable table; PERMISSION_DENIED is closest semantic.
224
+ exit(ExitCode.PERMISSION_DENIED);
225
+ }
226
+ const statusCode = err?.response?.statusCode;
227
+ if (statusCode === 409) {
228
+ console.error(chalk.red(`Version conflict: version tag '${version}' already exists with different content.`));
229
+ console.error(chalk.yellow("Use a new version tag (e.g. bump the patch version)."));
230
+ exit(ExitCode.CONFLICT);
231
+ }
232
+ const msg = err?.response?.body
233
+ ?.detail ??
234
+ err.message ??
235
+ String(err);
236
+ console.error(chalk.red(`Publish failed: ${msg}\n`));
237
+ console.error(chalk.yellow(publishUsageExamples()));
238
+ exit(exitCodeForHttpStatus(statusCode));
239
+ }
240
+ });
241
+ }
242
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * spec0 pull <org>/<name> — download spec from registry
3
+ */
4
+ import { Command } from "commander";
5
+ export declare function registerPullCommand(program: Command): void;
6
+ //# sourceMappingURL=pull.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QAsCnD"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * spec0 pull <org>/<name> — download spec from registry
3
+ */
4
+ import chalk from "chalk";
5
+ import { writeFileSync } from "fs";
6
+ import { createOrgApiClient, is401 } from "../lib/api-client.js";
7
+ import { requireOrgContext } from "../lib/auth-context.js";
8
+ import { resolveRef } from "../lib/ref-resolver.js";
9
+ import { ExitCode, exit } from "../lib/exit-codes.js";
10
+ export function registerPullCommand(program) {
11
+ program
12
+ .command("pull <ref>")
13
+ .description("Download spec from registry (e.g. acme/order-service or acme/order-service@v1.2.0)")
14
+ .option("-o, --output <file>", "Write to file instead of stdout")
15
+ .option("--public", "Reserved for public registry (same endpoint when API is public)")
16
+ .option("--org <uuid>", "Auth org id (defaults to logged-in org)")
17
+ .action(async (ref, opts) => {
18
+ const parsed = resolveRefForPull(ref);
19
+ let ctx;
20
+ try {
21
+ ctx = requireOrgContext(opts.org);
22
+ }
23
+ catch (e) {
24
+ exit(ExitCode.AUTH_MISSING, e.message);
25
+ }
26
+ const client = createOrgApiClient(ctx);
27
+ const path = parsed.tag
28
+ ? `/registry/${encodeURIComponent(parsed.org)}/${encodeURIComponent(parsed.api)}/versions/${encodeURIComponent(parsed.tag)}`
29
+ : `/registry/${encodeURIComponent(parsed.org)}/${encodeURIComponent(parsed.api)}?format=yaml`;
30
+ try {
31
+ const yaml = await client.getText(path);
32
+ if (opts.output) {
33
+ writeFileSync(opts.output, yaml, "utf-8");
34
+ console.log(chalk.green(`Wrote ${opts.output}`));
35
+ }
36
+ else {
37
+ process.stdout.write(yaml);
38
+ }
39
+ }
40
+ catch (err) {
41
+ if (is401(err)) {
42
+ exit(ExitCode.AUTH_MISSING, "Token invalid or expired. Run 'spec0 auth login'.");
43
+ }
44
+ exit(ExitCode.GENERIC, `Pull failed: ${err.message}`);
45
+ }
46
+ });
47
+ }
48
+ /** Pull only accepts <org>/<api>[@<tag>]; UUIDs and bare names aren't useful. */
49
+ function resolveRefForPull(ref) {
50
+ let parsed;
51
+ try {
52
+ parsed = resolveRef(ref);
53
+ }
54
+ catch (e) {
55
+ exit(ExitCode.USAGE, e.message);
56
+ }
57
+ if (parsed.kind !== "name" || !parsed.org) {
58
+ exit(ExitCode.USAGE, `Pull requires '<org>/<api>[@<tag>]'. Got '${ref}'. UUIDs aren't supported by the registry endpoint.`);
59
+ }
60
+ return { org: parsed.org, api: parsed.api, tag: parsed.tag };
61
+ }
62
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * spec0 push — zero-config push of an OpenAPI spec to the platform (team-scoped, private).
3
+ *
4
+ * The primary publish command for the GitOps workflow:
5
+ * spec0 push openapi.yaml
6
+ * spec0 push openapi.yaml --name payment-api
7
+ * spec0 push openapi.yaml --semver
8
+ *
9
+ * Name resolution order: --name > basename(specFilePath) > error
10
+ * Version resolution order: --version > info.version in spec > default 0.1.0
11
+ * Team resolution order: --team > "Unassigned APIs" (backend default, reassign later)
12
+ * Git SHA: --git-sha > CI env > local `git log -1 -- <file>`
13
+ *
14
+ * `spec0 register` is a backward-compat alias for this command.
15
+ */
16
+ import { Command } from "commander";
17
+ export declare function registerPushCommand(program: Command): void;
18
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgUpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QAuBnD"}
@@ -0,0 +1,302 @@
1
+ /**
2
+ * spec0 push — zero-config push of an OpenAPI spec to the platform (team-scoped, private).
3
+ *
4
+ * The primary publish command for the GitOps workflow:
5
+ * spec0 push openapi.yaml
6
+ * spec0 push openapi.yaml --name payment-api
7
+ * spec0 push openapi.yaml --semver
8
+ *
9
+ * Name resolution order: --name > basename(specFilePath) > error
10
+ * Version resolution order: --version > info.version in spec > default 0.1.0
11
+ * Team resolution order: --team > "Unassigned APIs" (backend default, reassign later)
12
+ * Git SHA: --git-sha > CI env > local `git log -1 -- <file>`
13
+ *
14
+ * `spec0 register` is a backward-compat alias for this command.
15
+ */
16
+ import chalk from "chalk";
17
+ import ora from "ora";
18
+ import { existsSync, readFileSync } from "fs";
19
+ import { basename, extname, relative, resolve } from "path";
20
+ import { runSpectral } from "../lib/lint.js";
21
+ import { formatLintText, formatPublishText, formatGitHubAnnotation } from "../lib/output.js";
22
+ import { createOrgApiClient, is401, is402, extractErrorMessage } from "../lib/api-client.js";
23
+ import { requireOrgContext } from "../lib/auth-context.js";
24
+ import { ExitCode, exit, exitCodeForHttpStatus } from "../lib/exit-codes.js";
25
+ import { resolveCliSpecPathFromFlags } from "../lib/cli-spec-path.js";
26
+ import { resolvedPlatformAppUrl } from "../lib/platform-defaults.js";
27
+ import { detectCI, getGitShaForFile, getGitBranch } from "../lib/ci-detect.js";
28
+ const TEAM_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
29
+ function isUuid(v) {
30
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(v);
31
+ }
32
+ function validateTeamArg(team) {
33
+ const t = team.trim();
34
+ if (!t)
35
+ return "Missing --team value.";
36
+ if (isUuid(t))
37
+ return null;
38
+ if (!TEAM_SLUG_PATTERN.test(t)) {
39
+ return `Invalid --team '${team}'. Use a UUID or slug like 'platform-team' (lowercase, hyphens, no spaces).`;
40
+ }
41
+ return null;
42
+ }
43
+ /** Derive API name from spec filename: "api/payment-api.yaml" → "payment-api" */
44
+ function nameFromSpecPath(specPath) {
45
+ const base = basename(specPath);
46
+ const ext = extname(base);
47
+ const stem = ext ? base.slice(0, -ext.length) : base;
48
+ // Only return if it already looks like a valid slug
49
+ const slug = stem
50
+ .toLowerCase()
51
+ .replace(/[^a-z0-9]+/g, "-")
52
+ .replace(/^-+|-+$/g, "");
53
+ return slug || null;
54
+ }
55
+ function usageExamples(cmd) {
56
+ return [
57
+ "Examples:",
58
+ ` spec0 ${cmd} openapi.yaml`,
59
+ ` spec0 ${cmd} openapi.yaml --name payment-api`,
60
+ ` spec0 ${cmd} openapi.yaml --name payment-api --team platform-team`,
61
+ ` spec0 ${cmd} openapi.yaml --semver`,
62
+ ` spec0 ${cmd} openapi.yaml --version 1.2.0 --git-sha $(git rev-parse HEAD)`,
63
+ ].join("\n");
64
+ }
65
+ async function runPush(cmdName, specArg, opts, command) {
66
+ const cwd = process.cwd();
67
+ const specPath = resolveCliSpecPathFromFlags(command, cwd, opts.specFile, specArg);
68
+ if (!specPath || !existsSync(specPath)) {
69
+ console.error(chalk.red("Spec file not found. Pass a path as the first argument or via --spec-file.\n"));
70
+ console.error(chalk.yellow(usageExamples(cmdName)));
71
+ exit(ExitCode.USAGE);
72
+ }
73
+ const apiId = opts["apiId"];
74
+ const nameOpt = opts.name;
75
+ const team = opts.team;
76
+ const versionOpt = opts.version;
77
+ const semver = !!opts.semver;
78
+ const strict = !!opts.strict;
79
+ const skipLint = !!opts.skipLint;
80
+ const verbose = !!opts.verbose;
81
+ const format = opts.format ?? "text";
82
+ const dryRun = !!opts.dryRun;
83
+ const vLog = (msg) => {
84
+ if (verbose)
85
+ console.error(chalk.gray(`[verbose] ${msg}`));
86
+ };
87
+ // ── Name resolution ────────────────────────────────────────────────────────
88
+ // --name > basename(specFilePath) — never from info.title
89
+ const resolvedName = nameOpt?.trim() ?? nameFromSpecPath(specPath);
90
+ if (!apiId && !resolvedName) {
91
+ console.error(chalk.red("Cannot derive API name from spec path. Use --name payment-api.\n"));
92
+ console.error(chalk.yellow(usageExamples(cmdName)));
93
+ exit(ExitCode.USAGE);
94
+ }
95
+ // ── Team validation (optional) ─────────────────────────────────────────────
96
+ if (team) {
97
+ const teamErr = validateTeamArg(team);
98
+ if (teamErr) {
99
+ console.error(chalk.red(`${teamErr}\n`));
100
+ exit(ExitCode.USAGE);
101
+ }
102
+ }
103
+ // ── Git SHA resolution ─────────────────────────────────────────────────────
104
+ // Priority: --git-sha flag > CI env > local git log
105
+ let gitSha = opts["gitSha"] ?? "";
106
+ let githubRepo = opts["githubRepo"] ?? "";
107
+ let githubBranch = opts["githubBranch"] ?? "";
108
+ if (!gitSha) {
109
+ const ci = detectCI();
110
+ if (ci?.gitSha) {
111
+ gitSha = ci.gitSha;
112
+ githubRepo = githubRepo || (ci.githubRepo ?? "");
113
+ githubBranch = githubBranch || ci.branch;
114
+ vLog(`git sha from CI: ${gitSha.slice(0, 8)}…`);
115
+ }
116
+ else {
117
+ // Local git: SHA of last commit that touched this spec file
118
+ const localSha = getGitShaForFile(specPath);
119
+ if (localSha) {
120
+ gitSha = localSha;
121
+ githubBranch = githubBranch || (getGitBranch() ?? "");
122
+ vLog(`git sha from local git: ${gitSha.slice(0, 8)}…`);
123
+ }
124
+ }
125
+ }
126
+ // ── Lint gate ─────────────────────────────────────────────────────────────
127
+ const spinner = ora("Linting…").start();
128
+ if (!skipLint) {
129
+ try {
130
+ const lintResult = await runSpectral(specPath);
131
+ if (lintResult.errors.length === 0) {
132
+ spinner.succeed("Lint passed");
133
+ }
134
+ else {
135
+ spinner.fail("Lint failed");
136
+ }
137
+ if (format === "github") {
138
+ for (const e of lintResult.errors)
139
+ console.log(formatGitHubAnnotation(specPath, e.line ?? 0, "error", e.message));
140
+ for (const w of lintResult.warnings)
141
+ console.log(formatGitHubAnnotation(specPath, w.line ?? 0, "warning", w.message));
142
+ }
143
+ else if (format !== "json") {
144
+ console.log(formatLintText(lintResult));
145
+ }
146
+ if (lintResult.errors.length > 0) {
147
+ console.error(chalk.red("Lint errors block push. Fix them or use --skip-lint."));
148
+ exit(ExitCode.VALIDATION);
149
+ }
150
+ if (strict && lintResult.warnings.length > 0) {
151
+ console.error(chalk.red("Strict mode: lint warnings block push."));
152
+ exit(ExitCode.VALIDATION);
153
+ }
154
+ }
155
+ catch (e) {
156
+ spinner.fail("Lint check failed");
157
+ console.error(e);
158
+ exit(ExitCode.GENERIC);
159
+ }
160
+ }
161
+ else {
162
+ spinner.succeed("Lint skipped");
163
+ }
164
+ // ── Auth ──────────────────────────────────────────────────────────────────
165
+ let ctx;
166
+ try {
167
+ ctx = requireOrgContext(opts.org);
168
+ }
169
+ catch (e) {
170
+ console.error(chalk.red(e.message));
171
+ exit(ExitCode.AUTH_MISSING);
172
+ }
173
+ const openapiSpec = readFileSync(specPath, "utf-8");
174
+ const specFilePath = relative(cwd, resolve(specPath));
175
+ if (dryRun) {
176
+ console.log(chalk.green(`Dry run — would push: name=${resolvedName ?? apiId ?? "new"} team=${team ?? "(unassigned)"} version=${versionOpt ?? "(from spec)"} sha=${gitSha ? gitSha.slice(0, 8) + "…" : "none"}`));
177
+ return;
178
+ }
179
+ // ── API call ─────────────────────────────────────────────────────────────
180
+ const client = createOrgApiClient(ctx);
181
+ const body = {
182
+ apiId,
183
+ name: resolvedName ?? undefined,
184
+ team: team || undefined,
185
+ version: versionOpt || undefined,
186
+ openapiSpec,
187
+ gitSha: gitSha || undefined,
188
+ githubRepo: githubRepo || undefined,
189
+ githubBranch: githubBranch || undefined,
190
+ specFilePath,
191
+ semver,
192
+ };
193
+ vLog(`POST ${ctx.apiUrl}/api-management/cli/v1/publish`);
194
+ vLog(`payload: name=${resolvedName} team=${team ?? "(unassigned)"} version=${versionOpt ?? "(from spec)"} sha=${gitSha ? gitSha.slice(0, 8) + "…" : "none"} semver=${semver}`);
195
+ const pushSpinner = ora("Pushing…").start();
196
+ try {
197
+ const reg = (await client.postJson("/api-management/cli/v1/publish", body));
198
+ pushSpinner.stop();
199
+ vLog(`response: apiId=${reg.apiId} apiName=${reg.apiName ?? "-"} version=${reg.version ?? "-"} created=${reg.created} noChanges=${reg.noChanges} versionUnchanged=${reg.versionUnchanged}`);
200
+ const resolvedApiId = reg.apiId ?? "";
201
+ const appBase = resolvedPlatformAppUrl();
202
+ const specUrl = `${appBase}/apis/${resolvedApiId}`;
203
+ let mockUrl;
204
+ try {
205
+ const mocks = (await client.getJson("/api-management/cli/v1/mocks"));
206
+ const hit = mocks.find((m) => m.apiId === resolvedApiId);
207
+ if (hit?.mockBaseUrl) {
208
+ mockUrl = `${ctx.apiUrl}${hit.mockBaseUrl}`;
209
+ }
210
+ }
211
+ catch {
212
+ /* best-effort */
213
+ }
214
+ const displayVersion = reg.version ?? versionOpt ?? "?";
215
+ const displayName = reg.apiName ?? resolvedName ?? resolvedApiId;
216
+ if (format === "json") {
217
+ console.log(JSON.stringify({
218
+ apiId: resolvedApiId,
219
+ apiName: reg.apiName ?? null,
220
+ version: displayVersion,
221
+ created: reg.created ?? false,
222
+ versionCreated: reg.versionCreated ?? false,
223
+ noChanges: reg.noChanges ?? false,
224
+ versionUnchanged: reg.versionUnchanged ?? false,
225
+ versionUnchangedHint: reg.versionUnchangedHint ?? null,
226
+ teamName: reg.teamName ?? null,
227
+ orgSlug: reg.orgSlug ?? null,
228
+ registryUrl: reg.registryUrl ?? null,
229
+ specUrl,
230
+ mockUrl: mockUrl ?? null,
231
+ }, null, 2));
232
+ }
233
+ else {
234
+ console.log(formatPublishText({
235
+ apiId: resolvedApiId,
236
+ apiName: displayName,
237
+ version: displayVersion,
238
+ specUrl,
239
+ mockUrl,
240
+ teamName: reg.teamName,
241
+ registryUrl: reg.registryUrl,
242
+ noChanges: reg.noChanges,
243
+ versionUnchanged: reg.versionUnchanged,
244
+ versionUnchangedHint: reg.versionUnchangedHint,
245
+ created: reg.created,
246
+ }));
247
+ }
248
+ }
249
+ catch (err) {
250
+ pushSpinner.fail("Push failed");
251
+ if (is401(err)) {
252
+ const errBody = err?.response?.body;
253
+ const errCode = typeof errBody === "object" && errBody !== null
254
+ ? errBody["error"]
255
+ : undefined;
256
+ if (errCode === "API_KEY_EXPIRED") {
257
+ console.error(chalk.red("Your API key has expired. Run 'spec0 auth login' to re-authenticate."));
258
+ }
259
+ else {
260
+ console.error(chalk.red("Authentication failed. Run 'spec0 auth login' to re-authenticate."));
261
+ }
262
+ exit(ExitCode.AUTH_MISSING);
263
+ }
264
+ if (is402(err)) {
265
+ const msg = extractErrorMessage(err) ?? "You've reached your plan limit.";
266
+ console.error(chalk.red(`Plan limit reached: ${msg}`));
267
+ console.error(chalk.yellow("Upgrade your plan at your org settings."));
268
+ // 402 isn't in the stable exit-code table; closest semantic is permission-denied.
269
+ exit(ExitCode.PERMISSION_DENIED);
270
+ }
271
+ const status = err?.response?.statusCode;
272
+ const detail = extractErrorMessage(err) ?? err.message ?? String(err);
273
+ console.error(chalk.red(`Push failed: ${detail}\n`));
274
+ console.error(chalk.yellow(usageExamples(cmdName)));
275
+ exit(exitCodeForHttpStatus(status));
276
+ }
277
+ }
278
+ export function registerPushCommand(program) {
279
+ program
280
+ .command("push")
281
+ .description("Push an OpenAPI spec to the platform (team-scoped, private)")
282
+ .argument("[spec-file]", "Path to OpenAPI spec file (or use --spec-file)")
283
+ .option("--spec-file <path>", "Path to OpenAPI spec file")
284
+ .option("--name <name>", "API name (kebab-case). Defaults to spec filename if omitted.")
285
+ .option("--team <team>", "Team UUID or slug. Defaults to 'Unassigned APIs' if omitted.")
286
+ .option("--version <version>", "Version tag (e.g. 1.2.0). Defaults to info.version in spec.")
287
+ .option("--api-id <id>", "Existing API UUID to update (backward compat; prefer --name)")
288
+ .option("--semver", "Auto-compute next semver via oasdiff diff classification")
289
+ .option("--git-sha <sha>", "Git commit SHA (auto-detected if .git is present)")
290
+ .option("--github-repo <repo>", "GitHub repository URL for provenance")
291
+ .option("--github-branch <branch>", "GitHub branch for provenance")
292
+ .option("--strict", "Fail on any Spectral lint warning")
293
+ .option("--skip-lint", "Skip lint gate")
294
+ .option("--dry-run", "Validate and print what would be sent, without pushing")
295
+ .option("--verbose", "Print verbose request/response logs")
296
+ .option("--format <fmt>", "Output format: text, json, github", "text")
297
+ .option("--org <org>", "Override default org (UUID)")
298
+ .action(async (specArg, opts, command) => {
299
+ await runPush("push", specArg, opts, command);
300
+ });
301
+ }
302
+ //# sourceMappingURL=push.js.map