vessels 0.2.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 (2) hide show
  1. package/dist/index.js +287 -0
  2. package/package.json +26 -0
package/dist/index.js ADDED
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { createClient } from "@supabase/supabase-js";
5
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
6
+ import { homedir } from "os";
7
+ import { join } from "path";
8
+ import * as readline from "readline/promises";
9
+ var BASE_URL = "https://vessels-two.vercel.app";
10
+ var SUPABASE_URL = "https://vnlrstpwkizhidvwhoom.supabase.co";
11
+ var SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZubHJzdHB3a2l6aGlkdndob29tIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzU1ODIzMDgsImV4cCI6MjA5MTE1ODMwOH0.tz1k8MH_1G8M0qZncfFV8eA8NmGKZXvzUk3r1dylAhs";
12
+ var CONFIG_DIR = join(homedir(), ".vessels");
13
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
14
+ function readConfig() {
15
+ if (!existsSync(CONFIG_FILE)) return {};
16
+ try {
17
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
18
+ } catch {
19
+ return {};
20
+ }
21
+ }
22
+ function writeConfig(config) {
23
+ if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
24
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
25
+ }
26
+ function getToken() {
27
+ return readConfig().access_token ?? null;
28
+ }
29
+ async function api(path, options = {}) {
30
+ const token = getToken();
31
+ const res = await fetch(`${BASE_URL}${path}`, {
32
+ ...options,
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ ...token ? { Authorization: `Bearer ${token}` } : {},
36
+ ...options.headers ?? {}
37
+ }
38
+ });
39
+ const data = await res.json();
40
+ if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
41
+ return data;
42
+ }
43
+ function prompt(question) {
44
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
45
+ return rl.question(question).then((ans) => {
46
+ rl.close();
47
+ return ans.trim();
48
+ });
49
+ }
50
+ function parseFlags(args) {
51
+ const flags = {};
52
+ for (let i = 0; i < args.length; i++) {
53
+ if (args[i].startsWith("--")) flags[args[i].slice(2)] = args[i + 1] ?? "";
54
+ }
55
+ return flags;
56
+ }
57
+ async function cmdLogin(args) {
58
+ const flags = parseFlags(args);
59
+ const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
60
+ const email = flags.email || await prompt("Email: ");
61
+ if (!flags.otp) {
62
+ const { error: otpError } = await supabase.auth.signInWithOtp({ email });
63
+ if (otpError) {
64
+ console.error("Failed to send code:", otpError.message);
65
+ process.exit(1);
66
+ }
67
+ console.log(`OTP sent to ${email}.`);
68
+ if (flags.email) {
69
+ console.log(`Run: vessels login --email ${email} --otp <code>`);
70
+ return;
71
+ }
72
+ }
73
+ const otp = flags.otp || await prompt("Code: ");
74
+ const { data, error } = await supabase.auth.verifyOtp({ email, token: otp, type: "email" });
75
+ if (error || !data.session) {
76
+ console.error("Invalid or expired code.");
77
+ process.exit(1);
78
+ }
79
+ writeConfig({
80
+ access_token: data.session.access_token,
81
+ refresh_token: data.session.refresh_token,
82
+ email
83
+ });
84
+ await api("/api/v1/me");
85
+ console.log(`Logged in as ${email}`);
86
+ }
87
+ async function cmdLogout() {
88
+ writeConfig({});
89
+ console.log("Logged out.");
90
+ }
91
+ async function cmdWhoami() {
92
+ var _a, _b;
93
+ const data = await api("/api/v1/me");
94
+ console.log(`Email: ${readConfig().email ?? "unknown"}`);
95
+ console.log(`Workspace: ${(_a = data.workspace) == null ? void 0 : _a.name} (${(_b = data.workspace) == null ? void 0 : _b.plan})`);
96
+ console.log(`User ID: ${data.userId}`);
97
+ }
98
+ async function cmdKeysList() {
99
+ const data = await api("/api/v1/keys");
100
+ const keys = data.keys;
101
+ if (!keys.length) {
102
+ console.log("No API keys. Run: vessels keys create");
103
+ return;
104
+ }
105
+ for (const k of keys) {
106
+ const name = k.name ? ` (${k.name})` : "";
107
+ const used = k.last_used_at ? `last used ${new Date(k.last_used_at).toLocaleDateString()}` : "never used";
108
+ console.log(`${k.prefix}\u2026 ${used}${name} [${k.id}]`);
109
+ }
110
+ }
111
+ async function cmdKeysCreate(name) {
112
+ if (!name) name = await prompt("Key name (optional): ");
113
+ const data = await api("/api/v1/keys", {
114
+ method: "POST",
115
+ body: JSON.stringify({ name: name || null })
116
+ });
117
+ console.log(`
118
+ API key created. Copy it now \u2014 it won't be shown again.
119
+ `);
120
+ console.log(` ${data.key}
121
+ `);
122
+ if (data.name) console.log(`Name: ${data.name}`);
123
+ }
124
+ async function cmdKeysRevoke(id) {
125
+ await api(`/api/v1/keys/${id}`, { method: "DELETE" });
126
+ console.log(`Key ${id} revoked.`);
127
+ }
128
+ async function cmdWebhooksList() {
129
+ const data = await api("/api/v1/webhooks");
130
+ const endpoints = data.endpoints;
131
+ if (!endpoints.length) {
132
+ console.log("No webhook endpoints. Run: vessels webhooks create");
133
+ return;
134
+ }
135
+ for (const e of endpoints) {
136
+ const status = e.active ? "active" : "disabled";
137
+ console.log(`[${e.id}] ${e.url} ${status} events: ${e.events.join(", ")}`);
138
+ }
139
+ }
140
+ async function cmdWebhooksCreate(args) {
141
+ const flags = parseFlags(args);
142
+ const url = flags.url || await prompt("Endpoint URL (https://): ");
143
+ if (!url.startsWith("https://")) {
144
+ console.error("URL must start with https://");
145
+ process.exit(1);
146
+ }
147
+ let events;
148
+ if (flags.events) {
149
+ events = flags.events.split(/[,\s]+/).filter(Boolean);
150
+ } else {
151
+ console.log("Events (comma-separated, or press enter for both): interaction.response, message.user");
152
+ const eventsInput = await prompt("Events: ");
153
+ events = eventsInput.trim() ? eventsInput.trim().split(/[,\s]+/).filter(Boolean) : ["interaction.response", "message.user"];
154
+ }
155
+ const data = await api("/api/v1/webhooks", {
156
+ method: "POST",
157
+ body: JSON.stringify({ url, events })
158
+ });
159
+ console.log(`
160
+ Webhook created. Copy the secret now \u2014 it won't be shown again.
161
+ `);
162
+ console.log(` ID: ${data.endpoint.id}`);
163
+ console.log(` URL: ${data.endpoint.url}`);
164
+ console.log(` Events: ${data.endpoint.events.join(", ")}`);
165
+ console.log(` Secret: ${data.endpoint.secret}
166
+ `);
167
+ console.log(`Set VESSELS_WEBHOOK_SECRET=${data.endpoint.secret} in your environment.`);
168
+ }
169
+ async function cmdWebhooksDelete(id) {
170
+ await api(`/api/v1/webhooks/${id}`, { method: "DELETE" });
171
+ console.log(`Webhook ${id} deleted.`);
172
+ }
173
+ async function cmdWebhooksToggle(id, active) {
174
+ const data = await api(`/api/v1/webhooks/${id}`, {
175
+ method: "PATCH",
176
+ body: JSON.stringify({ active })
177
+ });
178
+ console.log(`Webhook ${data.endpoint.id} ${data.endpoint.active ? "enabled" : "disabled"}.`);
179
+ }
180
+ async function cmdPush(args) {
181
+ const flags = {};
182
+ for (let i = 0; i < args.length; i++) {
183
+ if (args[i].startsWith("--")) flags[args[i].slice(2)] = args[i + 1] ?? "";
184
+ }
185
+ const apiKey = flags.key ?? process.env.VESSELS_API_KEY;
186
+ if (!apiKey) {
187
+ console.error("Provide an API key via --key or VESSELS_API_KEY env var");
188
+ process.exit(1);
189
+ }
190
+ const message = flags.message ?? await prompt("Message: ");
191
+ const vessel = flags.vessel ?? await prompt("Vessel ID (or press enter for default): ");
192
+ const res = await fetch(`${BASE_URL}/api/v1/push`, {
193
+ method: "POST",
194
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
195
+ body: JSON.stringify({ message, vessel: vessel || void 0 })
196
+ });
197
+ const data = await res.json();
198
+ if (!res.ok) {
199
+ console.error("Push failed:", data.error);
200
+ process.exit(1);
201
+ }
202
+ console.log(`Message sent. vessel_id=${data.vessel_id} message_id=${data.message_id}`);
203
+ }
204
+ var [, , cmd, sub, ...rest] = process.argv;
205
+ var HELP = `
206
+ vessels \u2014 CLI for Vessels (vessels-two.vercel.app)
207
+
208
+ Commands:
209
+ vessels login [--email <email>] [--otp <code>]
210
+ Log in or sign up. Run without --otp to send the code, then re-run with --otp to verify.
211
+
212
+ vessels logout
213
+ vessels whoami
214
+
215
+ vessels keys list
216
+ vessels keys create [name]
217
+ vessels keys revoke <id>
218
+
219
+ vessels webhooks list
220
+ vessels webhooks create --url <https://...> [--events interaction.response,message.user]
221
+ vessels webhooks delete <id>
222
+ vessels webhooks enable <id>
223
+ vessels webhooks disable <id>
224
+
225
+ vessels push --vessel <id> --message <text> --key <api_key>
226
+ (--key can be omitted if VESSELS_API_KEY is set)
227
+
228
+ Agent/Claude Code setup (fully non-interactive):
229
+ vessels login --email me@example.com
230
+ # tell your agent the OTP from the email
231
+ vessels login --email me@example.com --otp 847293
232
+ vessels keys create my-project
233
+ vessels webhooks create --url https://myapp.com/hooks/vessels
234
+ `;
235
+ async function main() {
236
+ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
237
+ console.log(HELP);
238
+ return;
239
+ }
240
+ if (cmd === "login" || cmd === "signup") return cmdLogin([sub, ...rest].filter(Boolean));
241
+ if (cmd === "logout") return cmdLogout();
242
+ if (cmd === "whoami") return cmdWhoami();
243
+ if (cmd === "keys") {
244
+ if (sub === "list" || !sub) return cmdKeysList();
245
+ if (sub === "create") return cmdKeysCreate(rest[0]);
246
+ if (sub === "revoke") {
247
+ if (!rest[0]) {
248
+ console.error("Usage: vessels keys revoke <id>");
249
+ process.exit(1);
250
+ }
251
+ return cmdKeysRevoke(rest[0]);
252
+ }
253
+ }
254
+ if (cmd === "webhooks") {
255
+ if (sub === "list" || !sub) return cmdWebhooksList();
256
+ if (sub === "create") return cmdWebhooksCreate(rest);
257
+ if (sub === "delete" || sub === "remove") {
258
+ if (!rest[0]) {
259
+ console.error("Usage: vessels webhooks delete <id>");
260
+ process.exit(1);
261
+ }
262
+ return cmdWebhooksDelete(rest[0]);
263
+ }
264
+ if (sub === "enable") {
265
+ if (!rest[0]) {
266
+ console.error("Usage: vessels webhooks enable <id>");
267
+ process.exit(1);
268
+ }
269
+ return cmdWebhooksToggle(rest[0], true);
270
+ }
271
+ if (sub === "disable") {
272
+ if (!rest[0]) {
273
+ console.error("Usage: vessels webhooks disable <id>");
274
+ process.exit(1);
275
+ }
276
+ return cmdWebhooksToggle(rest[0], false);
277
+ }
278
+ }
279
+ if (cmd === "push") return cmdPush([sub, ...rest].filter(Boolean));
280
+ console.error(`Unknown command: ${cmd}
281
+ Run: vessels help`);
282
+ process.exit(1);
283
+ }
284
+ main().catch((err) => {
285
+ console.error(err.message ?? err);
286
+ process.exit(1);
287
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "vessels",
3
+ "version": "0.2.0",
4
+ "description": "Vessels CLI — manage your agent communication layer from the terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "vessels": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "tsup --watch"
15
+ },
16
+ "license": "MIT",
17
+ "keywords": ["ai", "agents", "vessels", "cli"],
18
+ "devDependencies": {
19
+ "tsup": "^8.5.1",
20
+ "typescript": "^5",
21
+ "@types/node": "^25.5.2"
22
+ },
23
+ "dependencies": {
24
+ "@supabase/supabase-js": "^2.102.1"
25
+ }
26
+ }