@pebblehouse/odin-cli 0.10.2 → 0.12.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.
- package/dist/index.js +133 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command20 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/auth/login.ts
|
|
7
7
|
import { createServer } from "http";
|
|
@@ -281,8 +281,15 @@ docsCmd.command("create <slug> <type>").description("Create a document").require
|
|
|
281
281
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
282
282
|
if (res.error) process.exit(1);
|
|
283
283
|
});
|
|
284
|
-
docsCmd.command("delete <slug>
|
|
285
|
-
|
|
284
|
+
docsCmd.command("delete <slug> [type]").description("Delete a document by type, or by --id (unambiguous)").option("--id <id>", "Delete a specific document by its UUID").action(async (slug, type, opts) => {
|
|
285
|
+
if (!opts.id && !type) {
|
|
286
|
+
process.stdout.write(
|
|
287
|
+
JSON.stringify({ data: null, error: "Provide a <type> or --id <uuid> to delete" }) + "\n"
|
|
288
|
+
);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
const path = opts.id ? `/pebbles/${slug}/documents/id/${opts.id}` : `/pebbles/${slug}/documents/${type}`;
|
|
292
|
+
const res = await apiRequest(path, {
|
|
286
293
|
method: "DELETE"
|
|
287
294
|
});
|
|
288
295
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
@@ -858,12 +865,21 @@ blueprintsCmd.command("deprecate <id>").description("Deprecate a blueprint").act
|
|
|
858
865
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
859
866
|
if (res.error) process.exit(1);
|
|
860
867
|
});
|
|
861
|
-
blueprintsCmd.command("add-asset <id>").description("Add an asset reference to a blueprint").requiredOption("--title <title>", "Asset title").
|
|
868
|
+
blueprintsCmd.command("add-asset <id>").description("Add an asset reference or inline content to a blueprint").requiredOption("--title <title>", "Asset title").option("--url <url>", "Asset URL (optional if content provided)").option("--content <content>", "Inline content string").option("--content-file <path>", "Read content from file").option("--type <type>", "Asset type (screenshot|diagram|code-snippet|figma-link|github-link|raw-text|visual-spec|other)", "other").option("--description <description>", "Asset description").action(async (id, opts) => {
|
|
869
|
+
let content = opts.content;
|
|
870
|
+
if (opts.contentFile) {
|
|
871
|
+
content = readFileSync9(opts.contentFile, "utf-8");
|
|
872
|
+
}
|
|
873
|
+
if (!opts.url && !content) {
|
|
874
|
+
process.stderr.write("Error: at least one of --url, --content, or --content-file is required\n");
|
|
875
|
+
process.exit(1);
|
|
876
|
+
}
|
|
862
877
|
const res = await apiRequest(`/blueprints/${id}/assets`, {
|
|
863
878
|
method: "POST",
|
|
864
879
|
body: {
|
|
865
880
|
title: opts.title,
|
|
866
|
-
asset_url: opts.url,
|
|
881
|
+
asset_url: opts.url || null,
|
|
882
|
+
content: content || null,
|
|
867
883
|
type: opts.type,
|
|
868
884
|
description: opts.description ?? ""
|
|
869
885
|
}
|
|
@@ -1151,12 +1167,119 @@ personaRunsCmd.command("capture").description("Read marker + transcript, compute
|
|
|
1151
1167
|
process.stdout.write(JSON.stringify(res.data) + "\n");
|
|
1152
1168
|
});
|
|
1153
1169
|
|
|
1154
|
-
// src/
|
|
1170
|
+
// src/commands/blog.ts
|
|
1171
|
+
import { Command as Command18 } from "commander";
|
|
1155
1172
|
import { readFileSync as readFileSync13 } from "fs";
|
|
1173
|
+
var blogCmd = new Command18("blog").description("Manage blog posts");
|
|
1174
|
+
blogCmd.command("list <slug>").description("List blog posts for a pebble").option("--status <status>", "Filter by status (draft, review, published, archived)").option("--tag <tag>", "Filter by tag").option("--page <page>", "Page number", "1").option("--per-page <perPage>", "Results per page", "20").action(async (slug, opts) => {
|
|
1175
|
+
const params = {};
|
|
1176
|
+
if (opts.status) params.status = opts.status;
|
|
1177
|
+
if (opts.tag) params.tag = opts.tag;
|
|
1178
|
+
params.page = opts.page;
|
|
1179
|
+
params.per_page = opts.perPage;
|
|
1180
|
+
const res = await apiRequest(`/pebbles/${slug}/blog`, { params });
|
|
1181
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1182
|
+
if (res.error) process.exit(1);
|
|
1183
|
+
});
|
|
1184
|
+
blogCmd.command("get <slug> <idOrSlug>").description("Get a blog post by UUID or slug").action(async (slug, idOrSlug) => {
|
|
1185
|
+
const res = await apiRequest(`/pebbles/${slug}/blog/${idOrSlug}`);
|
|
1186
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1187
|
+
if (res.error) process.exit(1);
|
|
1188
|
+
});
|
|
1189
|
+
blogCmd.command("create <slug> <title>").description("Create a blog post").requiredOption("--post-slug <postSlug>", "URL slug for the post").option("--blocks <blocks>", "Content blocks as JSON string").option("--file <path>", "Read content blocks from JSON file").option("--status <status>", "Initial status (draft, review)", "draft").option("--metadata <metadata>", "Post metadata as JSON string").option("--campaign-id <campaignId>", "Link to a marketing campaign").action(async (slug, title, opts) => {
|
|
1190
|
+
let blocks = [];
|
|
1191
|
+
if (opts.file) {
|
|
1192
|
+
blocks = JSON.parse(readFileSync13(opts.file, "utf-8"));
|
|
1193
|
+
} else if (opts.blocks) {
|
|
1194
|
+
blocks = JSON.parse(opts.blocks);
|
|
1195
|
+
}
|
|
1196
|
+
const body = {
|
|
1197
|
+
title,
|
|
1198
|
+
slug: opts.postSlug,
|
|
1199
|
+
blocks,
|
|
1200
|
+
status: opts.status
|
|
1201
|
+
};
|
|
1202
|
+
if (opts.metadata) body.metadata = JSON.parse(opts.metadata);
|
|
1203
|
+
if (opts.campaignId) body.campaign_id = opts.campaignId;
|
|
1204
|
+
const res = await apiRequest(`/pebbles/${slug}/blog`, {
|
|
1205
|
+
method: "POST",
|
|
1206
|
+
body
|
|
1207
|
+
});
|
|
1208
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1209
|
+
if (res.error) process.exit(1);
|
|
1210
|
+
});
|
|
1211
|
+
blogCmd.command("update <slug> <id>").description("Update a blog post").option("--title <title>", "New title").option("--post-slug <postSlug>", "New URL slug").option("--blocks <blocks>", "Content blocks as JSON string").option("--file <path>", "Read content blocks from JSON file").option("--status <status>", "New status (draft, review, published, archived)").option("--metadata <metadata>", "Post metadata as JSON string").action(async (slug, id, opts) => {
|
|
1212
|
+
const body = {};
|
|
1213
|
+
if (opts.title) body.title = opts.title;
|
|
1214
|
+
if (opts.postSlug) body.slug = opts.postSlug;
|
|
1215
|
+
if (opts.file) body.blocks = JSON.parse(readFileSync13(opts.file, "utf-8"));
|
|
1216
|
+
else if (opts.blocks) body.blocks = JSON.parse(opts.blocks);
|
|
1217
|
+
if (opts.status) body.status = opts.status;
|
|
1218
|
+
if (opts.metadata) body.metadata = JSON.parse(opts.metadata);
|
|
1219
|
+
const res = await apiRequest(`/pebbles/${slug}/blog/${id}`, {
|
|
1220
|
+
method: "PUT",
|
|
1221
|
+
body
|
|
1222
|
+
});
|
|
1223
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1224
|
+
if (res.error) process.exit(1);
|
|
1225
|
+
});
|
|
1226
|
+
blogCmd.command("delete <slug> <id>").description("Delete a blog post").action(async (slug, id) => {
|
|
1227
|
+
const res = await apiRequest(`/pebbles/${slug}/blog/${id}`, {
|
|
1228
|
+
method: "DELETE"
|
|
1229
|
+
});
|
|
1230
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1231
|
+
if (res.error) process.exit(1);
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
// src/commands/api-keys.ts
|
|
1235
|
+
import { Command as Command19 } from "commander";
|
|
1236
|
+
var apiKeysCmd = new Command19("api-keys").description("Manage API keys for server-to-server access");
|
|
1237
|
+
apiKeysCmd.command("list <slug>").description("List API keys for a pebble").action(async (slug) => {
|
|
1238
|
+
const res = await apiRequest(`/pebbles/${slug}/api-keys`);
|
|
1239
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1240
|
+
if (res.error) process.exit(1);
|
|
1241
|
+
});
|
|
1242
|
+
apiKeysCmd.command("create <slug>").description("Create a new API key").requiredOption("--name <name>", "Human-readable key name").requiredOption("--scopes <scopes>", "Comma-separated scopes (e.g. blog:read,marketing_content:read)").option("--expires-at <expiresAt>", "Expiration timestamp (ISO 8601)").action(async (slug, opts) => {
|
|
1243
|
+
const scopes = opts.scopes.split(",").map((s) => s.trim());
|
|
1244
|
+
const body = { name: opts.name, scopes };
|
|
1245
|
+
if (opts.expiresAt) body.expires_at = opts.expiresAt;
|
|
1246
|
+
const res = await apiRequest(`/pebbles/${slug}/api-keys`, {
|
|
1247
|
+
method: "POST",
|
|
1248
|
+
body
|
|
1249
|
+
});
|
|
1250
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1251
|
+
if (res.error) process.exit(1);
|
|
1252
|
+
if (res.data?.key) {
|
|
1253
|
+
process.stderr.write(
|
|
1254
|
+
`
|
|
1255
|
+
Store this key securely -- it cannot be retrieved again:
|
|
1256
|
+
${res.data.key}
|
|
1257
|
+
|
|
1258
|
+
`
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
apiKeysCmd.command("revoke <slug> <id>").description("Revoke an API key").action(async (slug, id) => {
|
|
1263
|
+
const res = await apiRequest(`/pebbles/${slug}/api-keys/${id}/revoke`, {
|
|
1264
|
+
method: "POST"
|
|
1265
|
+
});
|
|
1266
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1267
|
+
if (res.error) process.exit(1);
|
|
1268
|
+
});
|
|
1269
|
+
apiKeysCmd.command("delete <slug> <id>").description("Permanently delete an API key").action(async (slug, id) => {
|
|
1270
|
+
const res = await apiRequest(`/pebbles/${slug}/api-keys/${id}`, {
|
|
1271
|
+
method: "DELETE"
|
|
1272
|
+
});
|
|
1273
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1274
|
+
if (res.error) process.exit(1);
|
|
1275
|
+
});
|
|
1276
|
+
|
|
1277
|
+
// src/index.ts
|
|
1278
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
1156
1279
|
import { fileURLToPath } from "url";
|
|
1157
1280
|
import { dirname as dirname2, resolve } from "path";
|
|
1158
1281
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
1159
|
-
var pkg = JSON.parse(
|
|
1282
|
+
var pkg = JSON.parse(readFileSync14(resolve(__dirname, "../package.json"), "utf-8"));
|
|
1160
1283
|
var GOLD = "\x1B[33m";
|
|
1161
1284
|
var DIM = "\x1B[2m";
|
|
1162
1285
|
var RESET = "\x1B[0m";
|
|
@@ -1174,7 +1297,7 @@ ${RESET}
|
|
|
1174
1297
|
${DIM}${cwd}${RESET}
|
|
1175
1298
|
`);
|
|
1176
1299
|
}
|
|
1177
|
-
var program = new
|
|
1300
|
+
var program = new Command20();
|
|
1178
1301
|
program.name("odin").description("CLI for Odin \u2014 the knowledge backbone for Pebble House").version(VERSION);
|
|
1179
1302
|
program.command("login").description("Authenticate with Odin via browser").action(async () => {
|
|
1180
1303
|
printBanner();
|
|
@@ -1206,6 +1329,8 @@ program.addCommand(betsCmd);
|
|
|
1206
1329
|
program.addCommand(tasksCmd);
|
|
1207
1330
|
program.addCommand(roadmapsCmd);
|
|
1208
1331
|
program.addCommand(personaRunsCmd);
|
|
1332
|
+
program.addCommand(blogCmd);
|
|
1333
|
+
program.addCommand(apiKeysCmd);
|
|
1209
1334
|
process.on("exit", () => {
|
|
1210
1335
|
process.stderr.write("\n\n\n\n\n");
|
|
1211
1336
|
});
|