@manfred-kunze-dev/backbone-cli 2.7.0-dev.1 → 2.7.0-dev.3

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.
@@ -32,7 +32,7 @@ export function makeConvertCommand() {
32
32
  const buffer = readFileSync(p);
33
33
  const filename = basename(p);
34
34
  return {
35
- kind: "Base64Source",
35
+ kind: "base64",
36
36
  content: buffer.toString("base64"),
37
37
  filename,
38
38
  mimeType: getMimeType(filename),
@@ -67,7 +67,7 @@ export function makeConvertCommand() {
67
67
  await runAction(command, async () => {
68
68
  const client = getClient(command);
69
69
  const sources = urls.map((url) => ({
70
- kind: "HttpSource",
70
+ kind: "http",
71
71
  url,
72
72
  }));
73
73
  const toFormats = opts.format.map(mapFormat);
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function makeDocsCommand(): Command;
3
+ //# sourceMappingURL=docs.d.ts.map
@@ -0,0 +1,166 @@
1
+ import { Command } from "commander";
2
+ import { runAction } from "../lib/client.js";
3
+ import { resolveConfig, isJsonOutput } from "../lib/config.js";
4
+ /* eslint-enable @typescript-eslint/no-explicit-any */
5
+ /**
6
+ * Fetch the OpenAPI spec from the backend.
7
+ */
8
+ async function fetchSpec(baseUrl, apiKey) {
9
+ const specUrl = `${baseUrl.replace(/\/+$/, "")}/v3/api-docs`;
10
+ const res = await fetch(specUrl, {
11
+ headers: { Authorization: `Bearer ${apiKey}` },
12
+ });
13
+ if (!res.ok) {
14
+ if (res.status === 404 || res.status === 403) {
15
+ throw new Error("API documentation is not available. Enable it by setting SPRINGDOC_ENABLED=true on the backend.");
16
+ }
17
+ throw new Error(`Failed to fetch API docs: ${res.status} ${res.statusText}`);
18
+ }
19
+ return (await res.json());
20
+ }
21
+ /**
22
+ * Collect all $ref strings from an object tree.
23
+ */
24
+ function collectRefs(obj, refs) {
25
+ if (obj === null || obj === undefined || typeof obj !== "object")
26
+ return;
27
+ if (Array.isArray(obj)) {
28
+ for (const item of obj)
29
+ collectRefs(item, refs);
30
+ return;
31
+ }
32
+ const record = obj;
33
+ if (typeof record["$ref"] === "string") {
34
+ refs.add(record["$ref"]);
35
+ }
36
+ for (const value of Object.values(record)) {
37
+ collectRefs(value, refs);
38
+ }
39
+ }
40
+ /**
41
+ * Recursively resolve schema refs — the initial set plus any schemas they reference.
42
+ */
43
+ function resolveSchemas(refPaths, allSchemas) {
44
+ const resolved = {};
45
+ const queue = [...refPaths];
46
+ const visited = new Set();
47
+ while (queue.length > 0) {
48
+ const ref = queue.pop();
49
+ if (visited.has(ref))
50
+ continue;
51
+ visited.add(ref);
52
+ const prefix = "#/components/schemas/";
53
+ if (!ref.startsWith(prefix))
54
+ continue;
55
+ const name = ref.slice(prefix.length);
56
+ const schema = allSchemas[name];
57
+ if (!schema)
58
+ continue;
59
+ resolved[name] = schema;
60
+ const nested = new Set();
61
+ collectRefs(schema, nested);
62
+ for (const n of nested) {
63
+ if (!visited.has(n))
64
+ queue.push(n);
65
+ }
66
+ }
67
+ return resolved;
68
+ }
69
+ export function makeDocsCommand() {
70
+ const cmd = new Command("docs").description("Browse API documentation");
71
+ cmd
72
+ .command("sections")
73
+ .description("List available API documentation sections")
74
+ .action(async (_opts, command) => {
75
+ await runAction(command, async () => {
76
+ const config = resolveConfig(command);
77
+ const spec = await fetchSpec(config.baseUrl, config.apiKey);
78
+ const tags = spec.tags ?? [];
79
+ const paths = spec.paths ?? {};
80
+ // Count endpoints per tag
81
+ const tagCounts = new Map();
82
+ for (const methods of Object.values(paths)) {
83
+ for (const operation of Object.values(methods)) {
84
+ if (operation?.tags) {
85
+ for (const tag of operation.tags) {
86
+ tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ if (isJsonOutput(command)) {
92
+ const data = tags.map((t) => ({
93
+ name: t.name,
94
+ description: t.description ?? null,
95
+ endpoints: tagCounts.get(t.name) ?? 0,
96
+ }));
97
+ console.log(JSON.stringify(data, null, 2));
98
+ }
99
+ else {
100
+ const info = spec.info ?? {};
101
+ console.log(`${info.title ?? "API"} v${info.version ?? "?"}\n`);
102
+ console.log("Available sections:");
103
+ for (const t of tags) {
104
+ const count = tagCounts.get(t.name) ?? 0;
105
+ const desc = t.description ? ` — ${t.description}` : "";
106
+ console.log(` ${t.name} (${count} endpoint${count !== 1 ? "s" : ""})${desc}`);
107
+ }
108
+ }
109
+ });
110
+ });
111
+ cmd
112
+ .command("get")
113
+ .description("Get API documentation for a section")
114
+ .argument("<section>", "Section/tag name (e.g. Projects, Extractions)")
115
+ .action(async (section, _opts, command) => {
116
+ await runAction(command, async () => {
117
+ const config = resolveConfig(command);
118
+ const spec = await fetchSpec(config.baseUrl, config.apiKey);
119
+ const allTags = spec.tags ?? [];
120
+ const tagNames = allTags.map((t) => t.name);
121
+ if (!tagNames.includes(section)) {
122
+ throw new Error(`Section "${section}" not found. Available: ${tagNames.join(", ")}`);
123
+ }
124
+ const allPaths = spec.paths ?? {};
125
+ const allSchemas = spec.components?.schemas ?? {};
126
+ // Filter paths to only operations matching the requested tag
127
+ const filteredPaths = {};
128
+ const refs = new Set();
129
+ for (const [path, methods] of Object.entries(allPaths)) {
130
+ const filteredMethods = {};
131
+ for (const [method, operation] of Object.entries(methods)) {
132
+ const op = operation;
133
+ if (op?.tags?.includes(section)) {
134
+ filteredMethods[method] = operation;
135
+ collectRefs(operation, refs);
136
+ }
137
+ }
138
+ if (Object.keys(filteredMethods).length > 0) {
139
+ filteredPaths[path] = filteredMethods;
140
+ }
141
+ }
142
+ // Resolve referenced schemas (including transitive refs)
143
+ const referencedSchemas = resolveSchemas(refs, allSchemas);
144
+ if (isJsonOutput(command)) {
145
+ console.log(JSON.stringify({ paths: filteredPaths, schemas: referencedSchemas }, null, 2));
146
+ }
147
+ else {
148
+ // Human-readable summary
149
+ console.log(`\n${section}\n${"=".repeat(section.length)}\n`);
150
+ for (const [path, methods] of Object.entries(filteredPaths)) {
151
+ for (const [method, operation] of Object.entries(methods)) {
152
+ const op = operation;
153
+ const label = op.summary ?? op.operationId ?? "";
154
+ console.log(` ${method.toUpperCase().padEnd(7)} ${path}`);
155
+ if (label)
156
+ console.log(` ${label}`);
157
+ }
158
+ }
159
+ console.log(`\nReferenced schemas: ${Object.keys(referencedSchemas).join(", ") || "(none)"}`);
160
+ console.log("\nUse --json for full endpoint and schema details.");
161
+ }
162
+ });
163
+ });
164
+ return cmd;
165
+ }
166
+ //# sourceMappingURL=docs.js.map
@@ -44,7 +44,7 @@ export function makePromptsCommand() {
44
44
  .description("Create a new prompt")
45
45
  .requiredOption("-n, --name <name>", "Prompt name")
46
46
  .option("-d, --description <text>", "Prompt description")
47
- .option("--type <type>", "Prompt type")
47
+ .option("--type <type>", "Prompt type (TEXT or CHAT)", "TEXT")
48
48
  .action(async (opts, command) => {
49
49
  await runAction(command, async () => {
50
50
  const projectId = requireProjectId(command);
@@ -4,6 +4,7 @@ import { isJsonOutput } from "../lib/config.js";
4
4
  import { requireProjectId } from "../lib/context.js";
5
5
  import { formatPage, formatDetail, formatSuccess } from "../lib/output.js";
6
6
  import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
7
+ import { ensureOpenAiCompatible } from "../lib/schema-compat.js";
7
8
  import { makeSchemaVersionsCommand } from "./schema-versions.js";
8
9
  import { makeSchemaLabelsCommand } from "./schema-labels.js";
9
10
  export function makeSchemasCommand() {
@@ -127,15 +128,33 @@ export function makeSchemasCommand() {
127
128
  .argument("<schemaId>", "Schema ID")
128
129
  .requiredOption("-t, --text <text>", "Sample text to extract from")
129
130
  .requiredOption("-m, --model <model>", "Model to use (provider/model)")
130
- .option("--version-id <id>", "Schema version ID")
131
- .option("-l, --label <name>", "Schema label")
131
+ .option("-c, --content <json>", "JSON Schema content (overrides stored version)")
132
+ .option("-l, --label <name>", "Schema label to resolve")
132
133
  .action(async (schemaId, opts, command) => {
133
134
  await runAction(command, async () => {
134
135
  const projectId = requireProjectId(command);
135
136
  const client = getClient(command);
137
+ let jsonSchema;
138
+ if (opts.content) {
139
+ jsonSchema = JSON.parse(opts.content);
140
+ }
141
+ else {
142
+ // Resolve the schema content from the active version (or by label)
143
+ const { data: resolved } = await client.GET("/v1/projects/{projectId}/schemas/{schemaId}/resolve", {
144
+ params: {
145
+ path: { projectId, schemaId },
146
+ query: { label: opts.label },
147
+ },
148
+ });
149
+ jsonSchema = resolved?.jsonSchema;
150
+ if (!jsonSchema) {
151
+ throw new Error("No active schema version found. Create a version first or pass --content.");
152
+ }
153
+ }
136
154
  const { data } = await client.POST("/v1/projects/{projectId}/schemas/{schemaId}/test", {
137
155
  params: { path: { projectId, schemaId } },
138
156
  body: {
157
+ jsonSchema: ensureOpenAiCompatible(jsonSchema),
139
158
  sampleText: opts.text,
140
159
  model: opts.model,
141
160
  },
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import { makeTranscribeCommand } from "./commands/transcribe.js";
12
12
  import { makeProvidersCommand } from "./commands/providers.js";
13
13
  import { makeAnalyticsCommand } from "./commands/analytics.js";
14
14
  import { makeBillingCommand } from "./commands/billing.js";
15
+ import { makeDocsCommand } from "./commands/docs.js";
15
16
  const program = new Command();
16
17
  program
17
18
  .name("backbone")
@@ -34,5 +35,6 @@ program.addCommand(makeTranscribeCommand());
34
35
  program.addCommand(makeProvidersCommand());
35
36
  program.addCommand(makeAnalyticsCommand());
36
37
  program.addCommand(makeBillingCommand());
38
+ program.addCommand(makeDocsCommand());
37
39
  program.parse();
38
40
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Sanitize a JSON Schema to be compatible with OpenAI's structured output requirements.
3
+ *
4
+ * OpenAI requires:
5
+ * - Every object type must have `additionalProperties: false`
6
+ * - Every object must list all properties in `required`
7
+ *
8
+ * This function recursively walks the schema and applies these constraints.
9
+ */
10
+ export declare function ensureOpenAiCompatible(schema: unknown): unknown;
11
+ //# sourceMappingURL=schema-compat.d.ts.map
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Sanitize a JSON Schema to be compatible with OpenAI's structured output requirements.
3
+ *
4
+ * OpenAI requires:
5
+ * - Every object type must have `additionalProperties: false`
6
+ * - Every object must list all properties in `required`
7
+ *
8
+ * This function recursively walks the schema and applies these constraints.
9
+ */
10
+ export function ensureOpenAiCompatible(schema) {
11
+ if (schema === null || schema === undefined || typeof schema !== "object") {
12
+ return schema;
13
+ }
14
+ if (Array.isArray(schema)) {
15
+ return schema.map(ensureOpenAiCompatible);
16
+ }
17
+ const obj = schema;
18
+ const result = {};
19
+ for (const [key, value] of Object.entries(obj)) {
20
+ if (key === "properties" && typeof value === "object" && value !== null) {
21
+ // Recurse into each property definition
22
+ const props = {};
23
+ for (const [propName, propValue] of Object.entries(value)) {
24
+ props[propName] = ensureOpenAiCompatible(propValue);
25
+ }
26
+ result[key] = props;
27
+ }
28
+ else if (key === "items") {
29
+ // Recurse into array items
30
+ result[key] = ensureOpenAiCompatible(value);
31
+ }
32
+ else if (key === "anyOf" || key === "oneOf" || key === "allOf") {
33
+ // Recurse into combinators
34
+ result[key] = Array.isArray(value) ? value.map(ensureOpenAiCompatible) : value;
35
+ }
36
+ else {
37
+ result[key] = value;
38
+ }
39
+ }
40
+ // If this node is an object type with properties, enforce OpenAI constraints
41
+ if (obj.type === "object" && obj.properties && typeof obj.properties === "object") {
42
+ result.additionalProperties = false;
43
+ // Ensure all properties are in required
44
+ const propNames = Object.keys(obj.properties);
45
+ const existing = Array.isArray(obj.required) ? obj.required : [];
46
+ const merged = [...new Set([...existing, ...propNames])];
47
+ result.required = merged;
48
+ }
49
+ return result;
50
+ }
51
+ //# sourceMappingURL=schema-compat.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manfred-kunze-dev/backbone-cli",
3
- "version": "2.7.0-dev.1",
3
+ "version": "2.7.0-dev.3",
4
4
  "description": "CLI for the Backbone AI platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",