contentrain 0.3.1 → 0.3.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.
- package/README.md +56 -2
- package/dist/activity-CtOcAA4X.mjs +88 -0
- package/dist/branches-C_kgqSv9.mjs +142 -0
- package/dist/cdn-build-CDv_yM15.mjs +106 -0
- package/dist/cdn-init-DXQ-3V_c.mjs +90 -0
- package/dist/client-D4xW8aLz.mjs +275 -0
- package/dist/{diff-otz0kGqT.mjs → diff-Cq6iSnaD.mjs} +45 -13
- package/dist/index.mjs +5 -4
- package/dist/login-CO0hxICh.mjs +243 -0
- package/dist/logout-CRM4fCSH.mjs +29 -0
- package/dist/resolve-context-BMV7JwCw.mjs +58 -0
- package/dist/{serve-CaCZMeHF.mjs → serve-DL4LhWxl.mjs} +1 -1
- package/dist/{server-DW0KXB0T.mjs → server-WLDOAZa4.mjs} +94 -36
- package/dist/{status-kF5miVty.mjs → status-Bi9HYgcA.mjs} +38 -5
- package/dist/status-ts5FvDEZ.mjs +128 -0
- package/dist/studio-BmXS_bCq.mjs +23 -0
- package/dist/submissions-6_OCrUNF.mjs +169 -0
- package/dist/usage-BAb4q7Pz.mjs +81 -0
- package/dist/webhooks-C39IItU1.mjs +235 -0
- package/dist/whoami-D8NyFKs_.mjs +65 -0
- package/package.json +6 -6
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { i as pc, n as formatPercent, r as formatTable } from "./ui-B5mXontH.mjs";
|
|
2
|
+
import { n as resolveStudioClient } from "./client-D4xW8aLz.mjs";
|
|
3
|
+
import { t as resolveStudioContext } from "./resolve-context-BMV7JwCw.mjs";
|
|
4
|
+
import { defineCommand } from "citty";
|
|
5
|
+
import { intro, log, outro, spinner } from "@clack/prompts";
|
|
6
|
+
//#region src/studio/commands/usage.ts
|
|
7
|
+
var usage_default = defineCommand({
|
|
8
|
+
meta: {
|
|
9
|
+
name: "usage",
|
|
10
|
+
description: "Show workspace usage metrics"
|
|
11
|
+
},
|
|
12
|
+
args: {
|
|
13
|
+
workspace: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "Workspace ID",
|
|
16
|
+
required: false
|
|
17
|
+
},
|
|
18
|
+
project: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Project ID (for context resolution)",
|
|
21
|
+
required: false
|
|
22
|
+
},
|
|
23
|
+
json: {
|
|
24
|
+
type: "boolean",
|
|
25
|
+
description: "JSON output",
|
|
26
|
+
required: false
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async run({ args }) {
|
|
30
|
+
if (!args.json) intro(pc.bold("contentrain studio usage"));
|
|
31
|
+
try {
|
|
32
|
+
const client = await resolveStudioClient();
|
|
33
|
+
const ctx = await resolveStudioContext(client, args);
|
|
34
|
+
if (!ctx) {
|
|
35
|
+
log.warning("No workspace found.");
|
|
36
|
+
outro("");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const s = args.json ? null : spinner();
|
|
40
|
+
s?.start("Fetching usage metrics...");
|
|
41
|
+
const usage = await client.getWorkspaceUsage(ctx.workspaceId);
|
|
42
|
+
s?.stop("Usage loaded");
|
|
43
|
+
if (args.json) {
|
|
44
|
+
process.stdout.write(JSON.stringify(usage, null, 2));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const rows = [
|
|
48
|
+
metricRow("AI messages", usage.aiMessages),
|
|
49
|
+
metricRow("Form submissions", usage.formSubmissions),
|
|
50
|
+
metricRow("CDN bandwidth", usage.cdnBandwidthGb, "GB"),
|
|
51
|
+
metricRow("Media storage", usage.mediaStorageGb, "GB")
|
|
52
|
+
];
|
|
53
|
+
log.message(formatTable([
|
|
54
|
+
"Metric",
|
|
55
|
+
"Used",
|
|
56
|
+
"Limit",
|
|
57
|
+
"%"
|
|
58
|
+
], rows));
|
|
59
|
+
} catch (error) {
|
|
60
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
}
|
|
63
|
+
if (!args.json) outro("");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
function metricRow(name, metric, unit) {
|
|
67
|
+
const suffix = unit ? ` ${unit}` : "";
|
|
68
|
+
return [
|
|
69
|
+
name,
|
|
70
|
+
formatNumber(metric.current) + suffix,
|
|
71
|
+
metric.limit < 0 ? "unlimited" : formatNumber(metric.limit) + suffix,
|
|
72
|
+
metric.limit < 0 ? pc.dim("—") : formatPercent(metric.current, metric.limit)
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
function formatNumber(n) {
|
|
76
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
77
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
78
|
+
return String(n);
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { usage_default as default };
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { i as pc, o as statusIcon, r as formatTable } from "./ui-B5mXontH.mjs";
|
|
2
|
+
import { n as resolveStudioClient } from "./client-D4xW8aLz.mjs";
|
|
3
|
+
import { t as resolveStudioContext } from "./resolve-context-BMV7JwCw.mjs";
|
|
4
|
+
import { defineCommand } from "citty";
|
|
5
|
+
import { confirm, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
|
|
6
|
+
//#region src/studio/commands/webhooks.ts
|
|
7
|
+
const ALL_EVENTS = [
|
|
8
|
+
{
|
|
9
|
+
value: "content.saved",
|
|
10
|
+
label: "Content saved"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
value: "content.deleted",
|
|
14
|
+
label: "Content deleted"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
value: "model.saved",
|
|
18
|
+
label: "Model updated"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
value: "branch.merged",
|
|
22
|
+
label: "Branch merged"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: "branch.rejected",
|
|
26
|
+
label: "Branch rejected"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
value: "cdn.build_complete",
|
|
30
|
+
label: "CDN build complete"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
value: "media.uploaded",
|
|
34
|
+
label: "Media uploaded"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
value: "form.submitted",
|
|
38
|
+
label: "Form submitted"
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
var webhooks_default = defineCommand({
|
|
42
|
+
meta: {
|
|
43
|
+
name: "webhooks",
|
|
44
|
+
description: "Manage webhooks on Studio"
|
|
45
|
+
},
|
|
46
|
+
args: {
|
|
47
|
+
workspace: {
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "Workspace ID",
|
|
50
|
+
required: false
|
|
51
|
+
},
|
|
52
|
+
project: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "Project ID",
|
|
55
|
+
required: false
|
|
56
|
+
},
|
|
57
|
+
json: {
|
|
58
|
+
type: "boolean",
|
|
59
|
+
description: "JSON output",
|
|
60
|
+
required: false
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
async run({ args }) {
|
|
64
|
+
if (!args.json) intro(pc.bold("contentrain studio webhooks"));
|
|
65
|
+
try {
|
|
66
|
+
const client = await resolveStudioClient();
|
|
67
|
+
const ctx = await resolveStudioContext(client, args);
|
|
68
|
+
if (!ctx) {
|
|
69
|
+
log.warning("No workspace or project found.");
|
|
70
|
+
outro("");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const { workspaceId, projectId } = ctx;
|
|
74
|
+
const s = args.json ? null : spinner();
|
|
75
|
+
s?.start("Fetching webhooks...");
|
|
76
|
+
const webhooks = await client.listWebhooks(workspaceId, projectId);
|
|
77
|
+
s?.stop(`${webhooks.length} webhook(s)`);
|
|
78
|
+
if (args.json) {
|
|
79
|
+
process.stdout.write(JSON.stringify(webhooks, null, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (webhooks.length > 0) {
|
|
83
|
+
const rows = webhooks.map((w) => [
|
|
84
|
+
w.name,
|
|
85
|
+
w.url.length > 40 ? w.url.slice(0, 37) + "..." : w.url,
|
|
86
|
+
w.events.join(", "),
|
|
87
|
+
statusIcon(w.active)
|
|
88
|
+
]);
|
|
89
|
+
log.message(formatTable([
|
|
90
|
+
"Name",
|
|
91
|
+
"URL",
|
|
92
|
+
"Events",
|
|
93
|
+
"Active"
|
|
94
|
+
], rows));
|
|
95
|
+
} else log.info("No webhooks configured.");
|
|
96
|
+
const action = await select({
|
|
97
|
+
message: "What would you like to do?",
|
|
98
|
+
options: [
|
|
99
|
+
{
|
|
100
|
+
value: "create",
|
|
101
|
+
label: "Create new webhook"
|
|
102
|
+
},
|
|
103
|
+
...webhooks.length > 0 ? [
|
|
104
|
+
{
|
|
105
|
+
value: "test",
|
|
106
|
+
label: "Test a webhook"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
value: "delete",
|
|
110
|
+
label: "Delete a webhook"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
value: "deliveries",
|
|
114
|
+
label: "View delivery history"
|
|
115
|
+
}
|
|
116
|
+
] : [],
|
|
117
|
+
{
|
|
118
|
+
value: "cancel",
|
|
119
|
+
label: pc.dim("Done")
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
});
|
|
123
|
+
if (isCancel(action) || action === "cancel") {
|
|
124
|
+
outro("");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (action === "create") await createWebhook(client, workspaceId, projectId);
|
|
128
|
+
else if (action === "test") await testWebhook(client, workspaceId, projectId, webhooks);
|
|
129
|
+
else if (action === "delete") await deleteWebhook(client, workspaceId, projectId, webhooks);
|
|
130
|
+
else if (action === "deliveries") await viewDeliveries(client, workspaceId, projectId, webhooks);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
}
|
|
135
|
+
if (!args.json) outro("");
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
async function createWebhook(client, wid, pid) {
|
|
139
|
+
const name = await text({ message: "Webhook name" });
|
|
140
|
+
if (isCancel(name)) return;
|
|
141
|
+
const url = await text({
|
|
142
|
+
message: "Endpoint URL",
|
|
143
|
+
validate: (v) => {
|
|
144
|
+
if (!v.startsWith("https://")) return "URL must start with https://";
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
if (isCancel(url)) return;
|
|
148
|
+
const events = await multiselect({
|
|
149
|
+
message: "Events to subscribe to",
|
|
150
|
+
options: ALL_EVENTS,
|
|
151
|
+
required: true
|
|
152
|
+
});
|
|
153
|
+
if (isCancel(events)) return;
|
|
154
|
+
const s = spinner();
|
|
155
|
+
s.start("Creating webhook...");
|
|
156
|
+
const webhook = await client.createWebhook(wid, pid, {
|
|
157
|
+
name,
|
|
158
|
+
url,
|
|
159
|
+
events
|
|
160
|
+
});
|
|
161
|
+
s.stop("Webhook created");
|
|
162
|
+
log.success(`${pc.cyan(webhook.name)} → ${webhook.url}`);
|
|
163
|
+
}
|
|
164
|
+
async function testWebhook(client, wid, pid, webhooks) {
|
|
165
|
+
const choice = await select({
|
|
166
|
+
message: "Select webhook to test",
|
|
167
|
+
options: webhooks.map((w) => ({
|
|
168
|
+
value: w.id,
|
|
169
|
+
label: w.name
|
|
170
|
+
}))
|
|
171
|
+
});
|
|
172
|
+
if (isCancel(choice)) return;
|
|
173
|
+
const s = spinner();
|
|
174
|
+
s.start("Sending test event...");
|
|
175
|
+
const result = await client.testWebhook(wid, pid, choice);
|
|
176
|
+
s.stop(result.success ? "Test succeeded" : "Test failed");
|
|
177
|
+
if (result.httpStatus) log.message(` HTTP status: ${result.httpStatus}`);
|
|
178
|
+
}
|
|
179
|
+
async function deleteWebhook(client, wid, pid, webhooks) {
|
|
180
|
+
const choice = await select({
|
|
181
|
+
message: "Select webhook to delete",
|
|
182
|
+
options: webhooks.map((w) => ({
|
|
183
|
+
value: w.id,
|
|
184
|
+
label: w.name
|
|
185
|
+
}))
|
|
186
|
+
});
|
|
187
|
+
if (isCancel(choice)) return;
|
|
188
|
+
const confirmed = await confirm({ message: `Delete webhook "${webhooks.find((w) => w.id === choice)?.name}"? This cannot be undone.` });
|
|
189
|
+
if (isCancel(confirmed) || !confirmed) return;
|
|
190
|
+
const s = spinner();
|
|
191
|
+
s.start("Deleting...");
|
|
192
|
+
await client.deleteWebhook(wid, pid, choice);
|
|
193
|
+
s.stop("Webhook deleted");
|
|
194
|
+
}
|
|
195
|
+
async function viewDeliveries(client, wid, pid, webhooks) {
|
|
196
|
+
const choice = await select({
|
|
197
|
+
message: "Select webhook",
|
|
198
|
+
options: webhooks.map((w) => ({
|
|
199
|
+
value: w.id,
|
|
200
|
+
label: w.name
|
|
201
|
+
}))
|
|
202
|
+
});
|
|
203
|
+
if (isCancel(choice)) return;
|
|
204
|
+
const s = spinner();
|
|
205
|
+
s.start("Fetching deliveries...");
|
|
206
|
+
const result = await client.listWebhookDeliveries(wid, pid, choice);
|
|
207
|
+
s.stop(`${result.data.length} deliveries`);
|
|
208
|
+
if (result.data.length === 0) {
|
|
209
|
+
log.info("No deliveries yet.");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const rows = result.data.map((d) => [
|
|
213
|
+
d.event,
|
|
214
|
+
d.status === "success" ? pc.green(d.status) : d.status === "failed" ? pc.red(d.status) : pc.yellow(d.status),
|
|
215
|
+
d.httpStatus ? String(d.httpStatus) : "—",
|
|
216
|
+
timeAgo(d.createdAt)
|
|
217
|
+
]);
|
|
218
|
+
log.message(formatTable([
|
|
219
|
+
"Event",
|
|
220
|
+
"Status",
|
|
221
|
+
"HTTP",
|
|
222
|
+
"Time"
|
|
223
|
+
], rows));
|
|
224
|
+
}
|
|
225
|
+
function timeAgo(dateStr) {
|
|
226
|
+
const diff = Date.now() - new Date(dateStr).getTime();
|
|
227
|
+
const minutes = Math.floor(diff / 6e4);
|
|
228
|
+
if (minutes < 1) return "just now";
|
|
229
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
230
|
+
const hours = Math.floor(minutes / 60);
|
|
231
|
+
if (hours < 24) return `${hours}h ago`;
|
|
232
|
+
return `${Math.floor(hours / 24)}d ago`;
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
235
|
+
export { webhooks_default as default };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { i as pc } from "./ui-B5mXontH.mjs";
|
|
2
|
+
import { i as checkPermissions, n as resolveStudioClient, r as AuthExpiredError } from "./client-D4xW8aLz.mjs";
|
|
3
|
+
import { defineCommand } from "citty";
|
|
4
|
+
import { intro, log, outro } from "@clack/prompts";
|
|
5
|
+
//#region src/studio/commands/whoami.ts
|
|
6
|
+
var whoami_default = defineCommand({
|
|
7
|
+
meta: {
|
|
8
|
+
name: "whoami",
|
|
9
|
+
description: "Show current Studio authentication status"
|
|
10
|
+
},
|
|
11
|
+
args: { json: {
|
|
12
|
+
type: "boolean",
|
|
13
|
+
description: "JSON output",
|
|
14
|
+
required: false
|
|
15
|
+
} },
|
|
16
|
+
async run({ args }) {
|
|
17
|
+
if (!args.json) intro(pc.bold("contentrain studio whoami"));
|
|
18
|
+
try {
|
|
19
|
+
const client = await resolveStudioClient();
|
|
20
|
+
const user = await client.me();
|
|
21
|
+
if (args.json) {
|
|
22
|
+
const workspaces = await client.listWorkspaces();
|
|
23
|
+
process.stdout.write(JSON.stringify({
|
|
24
|
+
user: {
|
|
25
|
+
id: user.id,
|
|
26
|
+
email: user.email,
|
|
27
|
+
name: user.name,
|
|
28
|
+
provider: user.provider
|
|
29
|
+
},
|
|
30
|
+
workspaces: workspaces.map((w) => ({
|
|
31
|
+
id: w.id,
|
|
32
|
+
name: w.name,
|
|
33
|
+
slug: w.slug,
|
|
34
|
+
plan: w.plan,
|
|
35
|
+
role: w.role
|
|
36
|
+
}))
|
|
37
|
+
}, null, 2));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
log.info(pc.bold("Profile"));
|
|
41
|
+
log.message(` Email: ${pc.cyan(user.email)}`);
|
|
42
|
+
if (user.name) log.message(` Name: ${user.name}`);
|
|
43
|
+
log.message(` Provider: ${user.provider}`);
|
|
44
|
+
const workspaces = await client.listWorkspaces();
|
|
45
|
+
if (workspaces.length > 0) {
|
|
46
|
+
log.info(pc.bold(`\nWorkspaces (${workspaces.length})`));
|
|
47
|
+
for (const ws of workspaces) log.message(` ${pc.cyan(ws.name)} (${ws.plan}) — ${pc.dim(ws.role)}`);
|
|
48
|
+
}
|
|
49
|
+
const permWarning = await checkPermissions();
|
|
50
|
+
if (permWarning) log.warning(`\n${permWarning}`);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error instanceof AuthExpiredError) {
|
|
53
|
+
if (args.json) process.stdout.write(JSON.stringify({ error: "not_authenticated" }));
|
|
54
|
+
else log.error(error.message);
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
} else {
|
|
57
|
+
if (!args.json) log.error(error instanceof Error ? error.message : String(error));
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!args.json) outro("");
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
//#endregion
|
|
65
|
+
export { whoami_default as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "contentrain",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "CLI for Contentrain — AI content governance infrastructure",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"url": "git+https://github.com/Contentrain/ai.git",
|
|
10
10
|
"directory": "packages/cli"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://
|
|
12
|
+
"homepage": "https://ai.contentrain.io/packages/cli",
|
|
13
13
|
"bugs": {
|
|
14
14
|
"url": "https://github.com/Contentrain/ai/issues"
|
|
15
15
|
},
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"picocolors": "^1.1.0",
|
|
47
47
|
"simple-git": "^3.27.0",
|
|
48
48
|
"ws": "^8.18.0",
|
|
49
|
-
"@contentrain/
|
|
50
|
-
"@contentrain/
|
|
51
|
-
"@contentrain/
|
|
52
|
-
"@contentrain/
|
|
49
|
+
"@contentrain/query": "5.1.2",
|
|
50
|
+
"@contentrain/types": "0.4.0",
|
|
51
|
+
"@contentrain/rules": "0.3.1",
|
|
52
|
+
"@contentrain/mcp": "1.1.1"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/ws": "^8.5.0",
|