@vellumai/cli 0.4.35 → 0.4.37

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.
@@ -1,241 +0,0 @@
1
- import { loadLatestAssistant } from "../lib/assistant-config";
2
- import { GATEWAY_PORT } from "../lib/constants.js";
3
-
4
- // ---------------------------------------------------------------------------
5
- // Gateway API client
6
- // ---------------------------------------------------------------------------
7
-
8
- function getGatewayUrl(): string {
9
- const entry = loadLatestAssistant();
10
- if (entry?.runtimeUrl) return entry.runtimeUrl;
11
- return `http://localhost:${GATEWAY_PORT}`;
12
- }
13
-
14
- function getBearerToken(): string | undefined {
15
- const entry = loadLatestAssistant();
16
- return entry?.bearerToken;
17
- }
18
-
19
- function buildHeaders(): Record<string, string> {
20
- const headers: Record<string, string> = {
21
- "Content-Type": "application/json",
22
- };
23
- const token = getBearerToken();
24
- if (token) {
25
- headers["Authorization"] = `Bearer ${token}`;
26
- }
27
- return headers;
28
- }
29
-
30
- async function apiGet(path: string): Promise<unknown> {
31
- const url = `${getGatewayUrl()}/v1/${path}`;
32
- const response = await fetch(url, { headers: buildHeaders() });
33
- if (!response.ok) {
34
- const text = await response.text();
35
- throw new Error(`API error ${response.status}: ${text}`);
36
- }
37
- return response.json();
38
- }
39
-
40
- async function apiPost(path: string, body: unknown): Promise<unknown> {
41
- const url = `${getGatewayUrl()}/v1/${path}`;
42
- const response = await fetch(url, {
43
- method: "POST",
44
- headers: buildHeaders(),
45
- body: JSON.stringify(body),
46
- });
47
- if (!response.ok) {
48
- const text = await response.text();
49
- throw new Error(`API error ${response.status}: ${text}`);
50
- }
51
- return response.json();
52
- }
53
-
54
- // ---------------------------------------------------------------------------
55
- // Types
56
- // ---------------------------------------------------------------------------
57
-
58
- interface ContactChannel {
59
- type: string;
60
- address: string;
61
- isPrimary: boolean;
62
- }
63
-
64
- interface Contact {
65
- id: string;
66
- displayName: string;
67
- notes: string | null;
68
- lastInteraction: number | null;
69
- interactionCount: number;
70
- channels: ContactChannel[];
71
- }
72
-
73
- // ---------------------------------------------------------------------------
74
- // Output helpers
75
- // ---------------------------------------------------------------------------
76
-
77
- function hasFlag(args: string[], flag: string): boolean {
78
- return args.includes(flag);
79
- }
80
-
81
- function getFlagValue(args: string[], flag: string): string | undefined {
82
- const idx = args.indexOf(flag);
83
- if (idx === -1 || idx + 1 >= args.length) return undefined;
84
- return args[idx + 1];
85
- }
86
-
87
- function formatContact(c: Contact): string {
88
- const lines = [
89
- ` ID: ${c.id}`,
90
- ` Name: ${c.displayName}`,
91
- ` Notes: ${c.notes ?? "(none)"}`,
92
- ` Interactions: ${c.interactionCount}`,
93
- ];
94
- if (c.lastInteraction) {
95
- lines.push(` Last seen: ${new Date(c.lastInteraction).toISOString()}`);
96
- }
97
- if (c.channels.length > 0) {
98
- lines.push(" Channels:");
99
- for (const ch of c.channels) {
100
- const primary = ch.isPrimary ? " (primary)" : "";
101
- lines.push(` - ${ch.type}: ${ch.address}${primary}`);
102
- }
103
- }
104
- return lines.join("\n");
105
- }
106
-
107
- // ---------------------------------------------------------------------------
108
- // Usage
109
- // ---------------------------------------------------------------------------
110
-
111
- function printUsage(): void {
112
- console.log("Usage: vellum contacts <subcommand> [options]");
113
- console.log("");
114
- console.log("Subcommands:");
115
- console.log(" list [--limit N] [--role ROLE] List all contacts");
116
- console.log(" get <id> Get a contact by ID");
117
- console.log(" merge <keepId> <mergeId> Merge two contacts");
118
- console.log("");
119
- console.log("Options:");
120
- console.log(" --json Machine-readable JSON output");
121
- }
122
-
123
- // ---------------------------------------------------------------------------
124
- // Command entry point
125
- // ---------------------------------------------------------------------------
126
-
127
- export async function contacts(): Promise<void> {
128
- const args = process.argv.slice(3);
129
- const subcommand = args[0];
130
- const json = hasFlag(args, "--json");
131
-
132
- if (!subcommand || subcommand === "--help" || subcommand === "-h") {
133
- printUsage();
134
- return;
135
- }
136
-
137
- switch (subcommand) {
138
- case "list": {
139
- const limit = getFlagValue(args, "--limit") ?? "50";
140
- const role = getFlagValue(args, "--role");
141
- const query = `contacts?limit=${limit}${role ? `&role=${encodeURIComponent(role)}` : ""}`;
142
- const data = (await apiGet(query)) as {
143
- ok: boolean;
144
- contacts: Contact[];
145
- };
146
-
147
- if (json) {
148
- console.log(JSON.stringify(data));
149
- return;
150
- }
151
-
152
- if (data.contacts.length === 0) {
153
- console.log("No contacts found.");
154
- return;
155
- }
156
-
157
- console.log(`Contacts (${data.contacts.length}):\n`);
158
- for (const c of data.contacts) {
159
- console.log(formatContact(c) + "\n");
160
- }
161
- break;
162
- }
163
-
164
- case "get": {
165
- const id = args[1];
166
- if (!id || id.startsWith("--")) {
167
- console.error("Usage: vellum contacts get <id>");
168
- process.exit(1);
169
- }
170
-
171
- try {
172
- const data = (await apiGet(`contacts/${encodeURIComponent(id)}`)) as {
173
- ok: boolean;
174
- contact: Contact;
175
- };
176
-
177
- if (json) {
178
- console.log(JSON.stringify(data));
179
- } else {
180
- console.log(formatContact(data.contact));
181
- }
182
- } catch {
183
- if (json) {
184
- console.log(
185
- JSON.stringify({
186
- ok: false,
187
- error: `Contact "${id}" not found`,
188
- }),
189
- );
190
- } else {
191
- console.error(`Contact "${id}" not found.`);
192
- }
193
- process.exitCode = 1;
194
- }
195
- break;
196
- }
197
-
198
- case "merge": {
199
- const keepId = args[1];
200
- const mergeId = args[2];
201
- if (
202
- !keepId ||
203
- !mergeId ||
204
- keepId.startsWith("--") ||
205
- mergeId.startsWith("--")
206
- ) {
207
- console.error("Usage: vellum contacts merge <keepId> <mergeId>");
208
- process.exit(1);
209
- }
210
-
211
- try {
212
- const data = (await apiPost("contacts/merge", {
213
- keepId,
214
- mergeId,
215
- })) as { ok: boolean; contact: Contact };
216
-
217
- if (json) {
218
- console.log(JSON.stringify(data));
219
- } else {
220
- console.log(`Merged contact "${mergeId}" into "${keepId}".\n`);
221
- console.log(formatContact(data.contact));
222
- }
223
- } catch (err) {
224
- const msg = err instanceof Error ? err.message : String(err);
225
- if (json) {
226
- console.log(JSON.stringify({ ok: false, error: msg }));
227
- } else {
228
- console.error(`Error: ${msg}`);
229
- }
230
- process.exitCode = 1;
231
- }
232
- break;
233
- }
234
-
235
- default: {
236
- console.error(`Unknown contacts subcommand: ${subcommand}`);
237
- printUsage();
238
- process.exit(1);
239
- }
240
- }
241
- }
@@ -1,108 +0,0 @@
1
- /**
2
- * CLI command: `vellum email`
3
- *
4
- * Supports:
5
- * - `vellum email status` — show current email configuration
6
- * - `vellum email create <username>` — provision a new email inbox
7
- */
8
-
9
- import { VellumEmailClient } from "../email/vellum.js";
10
- import { loadLatestAssistant } from "../lib/assistant-config.js";
11
-
12
- // ---------------------------------------------------------------------------
13
- // Helpers
14
- // ---------------------------------------------------------------------------
15
-
16
- function output(data: unknown): void {
17
- process.stdout.write(JSON.stringify(data, null, 2) + "\n");
18
- }
19
-
20
- function exitError(message: string): void {
21
- output({ ok: false, error: message });
22
- process.exitCode = 1;
23
- }
24
-
25
- // ---------------------------------------------------------------------------
26
- // Usage
27
- // ---------------------------------------------------------------------------
28
-
29
- function printUsage(): void {
30
- console.log(`Usage: vellum email <subcommand> [options]
31
-
32
- Subcommands:
33
- status Show email status (address, inboxes, callback URL)
34
- create <username> Create a new email inbox for the given username
35
-
36
- Options:
37
- --assistant <id> Assistant ID (defaults to the most recently hatched)
38
- --help, -h Show this help message
39
- `);
40
- }
41
-
42
- // ---------------------------------------------------------------------------
43
- // Entry point
44
- // ---------------------------------------------------------------------------
45
-
46
- export async function email(): Promise<void> {
47
- const args = process.argv.slice(3); // everything after "email"
48
-
49
- if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
50
- printUsage();
51
- return;
52
- }
53
-
54
- // Resolve assistant ID from --assistant flag or latest assistant
55
- const assistantFlagIdx = args.indexOf("--assistant");
56
- let assistantId: string | undefined;
57
- if (assistantFlagIdx !== -1) {
58
- const value = args[assistantFlagIdx + 1];
59
- if (!value || value.startsWith("-")) {
60
- exitError("--assistant requires a value.");
61
- return;
62
- }
63
- assistantId = value;
64
- args.splice(assistantFlagIdx, 2);
65
- }
66
- if (!assistantId) {
67
- assistantId = loadLatestAssistant()?.assistantId;
68
- }
69
- if (!assistantId) {
70
- exitError(
71
- "No assistant ID available. Pass --assistant <id> or hatch an assistant first.",
72
- );
73
- return;
74
- }
75
-
76
- const subcommand = args[0];
77
-
78
- switch (subcommand) {
79
- case "status": {
80
- try {
81
- const client = new VellumEmailClient(assistantId);
82
- const addresses = await client.status();
83
- output({ ok: true, addresses });
84
- } catch (err) {
85
- exitError(err instanceof Error ? err.message : String(err));
86
- }
87
- break;
88
- }
89
- case "create": {
90
- const username = args[1];
91
- if (!username) {
92
- exitError("Usage: vellum email create <username>");
93
- return;
94
- }
95
- try {
96
- const client = new VellumEmailClient(assistantId);
97
- const inbox = await client.createInbox(username);
98
- output({ ok: true, inbox });
99
- } catch (err) {
100
- exitError(err instanceof Error ? err.message : String(err));
101
- }
102
- break;
103
- }
104
- default:
105
- exitError(`Unknown email subcommand: ${subcommand}`);
106
- printUsage();
107
- }
108
- }
@@ -1,97 +0,0 @@
1
- /**
2
- * Vellum email API client — calls the Vellum platform email endpoints.
3
- */
4
-
5
- // The domain for the Vellum email API is still being finalized and may change.
6
- const DEFAULT_VELLUM_API_URL = "https://api.vellum.ai";
7
-
8
- // ---------------------------------------------------------------------------
9
- // Types
10
- // ---------------------------------------------------------------------------
11
-
12
- export interface AssistantEmailAddress {
13
- id: string;
14
- address: string;
15
- created_at: string;
16
- }
17
-
18
- // ---------------------------------------------------------------------------
19
- // HTTP helper
20
- // ---------------------------------------------------------------------------
21
-
22
- async function vellumFetch(
23
- apiKey: string,
24
- baseUrl: string,
25
- path: string,
26
- opts: { method?: string; body?: unknown } = {},
27
- ): Promise<unknown> {
28
- const url = `${baseUrl}${path}`;
29
- const response = await fetch(url, {
30
- method: opts.method ?? "GET",
31
- headers: {
32
- Authorization: `Bearer ${apiKey}`,
33
- "Content-Type": "application/json",
34
- },
35
- body: opts.body ? JSON.stringify(opts.body) : undefined,
36
- });
37
-
38
- if (!response.ok) {
39
- const text = await response.text().catch(() => "");
40
- throw new Error(
41
- `Vellum email API error: ${response.status} ${response.statusText}${text ? ` — ${text}` : ""}`,
42
- );
43
- }
44
-
45
- const contentType = response.headers.get("content-type") ?? "";
46
- if (contentType.includes("application/json")) {
47
- return response.json();
48
- }
49
- return undefined;
50
- }
51
-
52
- // ---------------------------------------------------------------------------
53
- // Client
54
- // ---------------------------------------------------------------------------
55
-
56
- export class VellumEmailClient {
57
- private apiKey: string;
58
- private baseUrl: string;
59
- private assistantId: string;
60
-
61
- constructor(assistantId: string, apiKey?: string, baseUrl?: string) {
62
- this.assistantId = assistantId;
63
- const resolvedKey = apiKey ?? process.env.VELLUM_API_KEY;
64
- if (!resolvedKey) {
65
- throw new Error(
66
- "No Vellum API key configured. Set the VELLUM_API_KEY environment variable.",
67
- );
68
- }
69
- this.apiKey = resolvedKey;
70
- this.baseUrl =
71
- baseUrl ?? process.env.VELLUM_API_URL ?? DEFAULT_VELLUM_API_URL;
72
- }
73
-
74
- /** List existing email addresses and check connectivity. */
75
- async status(): Promise<AssistantEmailAddress[]> {
76
- const result = await vellumFetch(
77
- this.apiKey,
78
- this.baseUrl,
79
- `/v1/assistants/${this.assistantId}/email-addresses/`,
80
- );
81
- return result as AssistantEmailAddress[];
82
- }
83
-
84
- /** Provision a new email address for the given username. */
85
- async createInbox(username: string): Promise<AssistantEmailAddress> {
86
- const result = await vellumFetch(
87
- this.apiKey,
88
- this.baseUrl,
89
- `/v1/assistants/${this.assistantId}/email-addresses/`,
90
- {
91
- method: "POST",
92
- body: { username },
93
- },
94
- );
95
- return result as AssistantEmailAddress;
96
- }
97
- }
package/src/lib/config.ts DELETED
@@ -1,73 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { dirname, join } from "node:path";
4
-
5
- function getRootDir(): string {
6
- return join(process.env.BASE_DATA_DIR?.trim() || homedir(), ".vellum");
7
- }
8
-
9
- export function getConfigPath(): string {
10
- return join(getRootDir(), "workspace", "config.json");
11
- }
12
-
13
- export function getAllowlistPath(): string {
14
- return join(getRootDir(), "protected", "secret-allowlist.json");
15
- }
16
-
17
- export function loadRawConfig(): Record<string, unknown> {
18
- const configPath = getConfigPath();
19
- if (!existsSync(configPath)) {
20
- return {};
21
- }
22
- const raw = readFileSync(configPath, "utf-8");
23
- return JSON.parse(raw) as Record<string, unknown>;
24
- }
25
-
26
- export function saveRawConfig(config: Record<string, unknown>): void {
27
- const configPath = getConfigPath();
28
- const dir = dirname(configPath);
29
- if (!existsSync(dir)) {
30
- mkdirSync(dir, { recursive: true });
31
- }
32
- writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
33
- }
34
-
35
- export function getNestedValue(
36
- obj: Record<string, unknown>,
37
- path: string,
38
- ): unknown {
39
- const keys = path.split(".");
40
- let current: unknown = obj;
41
- for (const key of keys) {
42
- if (
43
- current === null ||
44
- current === undefined ||
45
- typeof current !== "object"
46
- ) {
47
- return undefined;
48
- }
49
- current = (current as Record<string, unknown>)[key];
50
- }
51
- return current;
52
- }
53
-
54
- export function setNestedValue(
55
- obj: Record<string, unknown>,
56
- path: string,
57
- value: unknown,
58
- ): void {
59
- const keys = path.split(".");
60
- let current = obj;
61
- for (let i = 0; i < keys.length - 1; i++) {
62
- const key = keys[i];
63
- if (
64
- current[key] === undefined ||
65
- current[key] === null ||
66
- typeof current[key] !== "object"
67
- ) {
68
- current[key] = {};
69
- }
70
- current = current[key] as Record<string, unknown>;
71
- }
72
- current[keys[keys.length - 1]] = value;
73
- }