@openma/cli 0.1.0-beta.2 → 0.1.0-beta.4

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 +110 -26
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12,12 +12,39 @@ function credentialsPath() {
12
12
  const base = xdg && xdg.length > 0 ? xdg : join(homedir(), ".config");
13
13
  return join(base, "oma", "credentials.json");
14
14
  }
15
+ function migrateV1ToV2(v1) {
16
+ return {
17
+ version: 2,
18
+ base_url: v1.base_url,
19
+ user: v1.user,
20
+ active_tenant_id: v1.tenant.id,
21
+ tenants: {
22
+ [v1.tenant.id]: {
23
+ name: v1.tenant.name,
24
+ role: v1.tenants.find((t) => t.id === v1.tenant.id)?.role ?? "owner",
25
+ token: v1.token,
26
+ key_id: v1.key_id,
27
+ created_at: v1.created_at
28
+ }
29
+ }
30
+ };
31
+ }
15
32
  function readCredentials() {
16
33
  const path = credentialsPath();
17
34
  try {
18
35
  if (!existsSync(path)) return null;
19
36
  const raw = readFileSync(path, "utf8");
20
- return JSON.parse(raw);
37
+ const parsed = JSON.parse(raw);
38
+ if (parsed.version === 2) return parsed;
39
+ if (parsed.version === 1) {
40
+ const v2 = migrateV1ToV2(parsed);
41
+ try {
42
+ writeCredentials(v2);
43
+ } catch {
44
+ }
45
+ return v2;
46
+ }
47
+ return null;
21
48
  } catch {
22
49
  return null;
23
50
  }
@@ -40,6 +67,7 @@ function clearCredentials() {
40
67
  function loadConfig() {
41
68
  const envBase = process.env.OMA_BASE_URL;
42
69
  const envKey = process.env.OMA_API_KEY;
70
+ const envTenant = process.env.OMA_TENANT_ID;
43
71
  const stored = readCredentials();
44
72
  if (envKey) {
45
73
  return {
@@ -50,9 +78,15 @@ function loadConfig() {
50
78
  };
51
79
  }
52
80
  if (stored) {
81
+ const activeId = envTenant || stored.active_tenant_id;
82
+ const profile = stored.tenants[activeId];
83
+ if (!profile) {
84
+ console.error(`Error: tenant ${activeId} not in credentials. Run: oma auth tenant ls`);
85
+ process.exit(1);
86
+ }
53
87
  return {
54
88
  baseUrl: envBase || stored.base_url,
55
- apiKey: stored.token,
89
+ apiKey: profile.token,
56
90
  json: false,
57
91
  source: "stored"
58
92
  };
@@ -65,12 +99,17 @@ function loadConfig() {
65
99
  function loadConfigOptional() {
66
100
  const envBase = process.env.OMA_BASE_URL;
67
101
  const envKey = process.env.OMA_API_KEY;
102
+ const envTenant = process.env.OMA_TENANT_ID;
68
103
  const stored = readCredentials();
69
104
  if (envKey) {
70
105
  return { baseUrl: envBase || stored?.base_url || "https://openma.dev", apiKey: envKey, json: false, source: "env" };
71
106
  }
72
107
  if (stored) {
73
- return { baseUrl: envBase || stored.base_url, apiKey: stored.token, json: false, source: "stored" };
108
+ const activeId = envTenant || stored.active_tenant_id;
109
+ const profile = stored.tenants[activeId];
110
+ if (profile) {
111
+ return { baseUrl: envBase || stored.base_url, apiKey: profile.token, json: false, source: "stored" };
112
+ }
74
113
  }
75
114
  return { baseUrl: envBase || "https://openma.dev", apiKey: "", json: false, source: "missing" };
76
115
  }
@@ -175,6 +214,9 @@ async function authLogin(baseUrl, requestedTenant) {
175
214
  res.writeHead(200, { "Content-Type": "text/html" }).end(approvalPage("Signed in", "You can close this tab and return to your terminal."));
176
215
  clearTimeout(timeout);
177
216
  server.close();
217
+ if (typeof server.closeAllConnections === "function") {
218
+ server.closeAllConnections();
219
+ }
178
220
  resolve({ token, tenant, user, key_id });
179
221
  } catch (err) {
180
222
  res.writeHead(500).end();
@@ -192,23 +234,42 @@ async function authLogin(baseUrl, requestedTenant) {
192
234
  });
193
235
  const tempConfig = { baseUrl, apiKey: result.token, json: false, source: "stored" };
194
236
  const me = await apiFetch(tempConfig, "/v1/me");
195
- writeCredentials({
196
- version: 1,
237
+ const existing = readCredentials();
238
+ const role = me.tenants.find((t) => t.id === me.tenant.id)?.role ?? "owner";
239
+ const updated = {
240
+ version: 2,
197
241
  base_url: baseUrl,
198
242
  user: me.user ?? { id: result.user, email: "", name: null },
199
- tenant: me.tenant,
200
- tenants: me.tenants,
201
- token: result.token,
202
- key_id: result.key_id,
203
- created_at: (/* @__PURE__ */ new Date()).toISOString()
204
- });
243
+ active_tenant_id: me.tenant.id,
244
+ tenants: {
245
+ ...existing && existing.user.id === (me.user?.id ?? result.user) ? existing.tenants : {},
246
+ [me.tenant.id]: {
247
+ name: me.tenant.name,
248
+ role,
249
+ token: result.token,
250
+ key_id: result.key_id,
251
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
252
+ }
253
+ }
254
+ };
255
+ writeCredentials(updated);
205
256
  console.log(`\u2713 Signed in as ${me.user?.email ?? me.user?.id}`);
206
- console.log(` Tenant : ${me.tenant.name || me.tenant.id} (${me.tenant.id})`);
257
+ console.log(` Tenant : ${displayTenantName(me.tenant)} (${me.tenant.id})`);
207
258
  console.log(` Stored : ${credentialsPath()}`);
208
- if (me.tenants.length > 1) {
209
- console.log(` ${me.tenants.length} tenants available \u2014 switch with: oma auth tenant use <id>`);
259
+ const knownCount = Object.keys(updated.tenants).length;
260
+ const totalAvailable = me.tenants.length;
261
+ if (totalAvailable > knownCount) {
262
+ const missing = totalAvailable - knownCount;
263
+ console.log(` ${missing} more tenant${missing === 1 ? "" : "s"} available \u2014 add with: oma auth login --tenant <id>`);
264
+ } else if (knownCount > 1) {
265
+ console.log(` ${knownCount} tenants stored \u2014 switch with: oma auth tenant use <id>`);
210
266
  }
211
267
  }
268
+ function displayTenantName(t) {
269
+ const trimmed = (t.name ?? "").trim();
270
+ if (!trimmed || trimmed === "'s workspace" || trimmed.startsWith("'s ")) return t.id;
271
+ return trimmed;
272
+ }
212
273
  function openBrowser(url) {
213
274
  try {
214
275
  const p = process.platform;
@@ -230,12 +291,13 @@ var commands = [
230
291
  {
231
292
  group: "Auth",
232
293
  match: ["auth", "login"],
233
- usage: "oma auth login [--base-url <url>]",
234
- desc: "Open browser to authenticate; stores ~/.config/oma/credentials.json",
294
+ usage: "oma auth login [--base-url <url>] [--tenant <id>]",
295
+ desc: "Open browser to authenticate; --tenant pre-picks a workspace",
235
296
  http: "POST /v1/me/cli-tokens (browser handoff via /cli/login)",
236
297
  async run(_config, args) {
237
298
  const baseUrl = (flag(args, "--base-url") ?? process.env.OMA_BASE_URL ?? "https://openma.dev").replace(/\/+$/, "");
238
- await authLogin(baseUrl);
299
+ const tenant = flag(args, "--tenant");
300
+ await authLogin(baseUrl, tenant);
239
301
  }
240
302
  },
241
303
  {
@@ -253,7 +315,7 @@ var commands = [
253
315
  group: "Auth",
254
316
  match: ["whoami"],
255
317
  usage: "oma whoami",
256
- desc: "Show current user, tenant, and base URL",
318
+ desc: "Show current user, active tenant, and base URL",
257
319
  http: "GET /v1/me",
258
320
  async run(config) {
259
321
  try {
@@ -265,9 +327,12 @@ var commands = [
265
327
  console.log(`Base URL : ${config.baseUrl}`);
266
328
  console.log(`Source : ${config.source === "env" ? "OMA_API_KEY env var" : "stored credentials"}`);
267
329
  console.log(`User : ${me.user?.email ?? me.user?.id ?? "(unknown \u2014 legacy key without user_id)"}`);
268
- console.log(`Tenant : ${me.tenant.name || me.tenant.id} (${me.tenant.id})`);
269
330
  if (me.tenants.length > 1) {
270
- console.log(`Available: ${me.tenants.map((t) => t.id).join(", ")}`);
331
+ console.log(`Tenant : ${displayTenantName(me.tenant)} (${me.tenant.id})`);
332
+ const others = me.tenants.filter((t) => t.id !== me.tenant.id);
333
+ if (others.length) {
334
+ console.log(`Available: ${others.map((t) => t.id).join(", ")} \u2014 switch with: oma auth tenant use <id>`);
335
+ }
271
336
  }
272
337
  } catch (err) {
273
338
  console.error(`whoami failed: ${err.message}`);
@@ -291,8 +356,20 @@ var commands = [
291
356
  return;
292
357
  }
293
358
  const stored = readCredentials();
294
- const current = stored?.tenant.id;
295
- table([["", "ID", "NAME", "ROLE"], ...data.map((t) => [t.id === current ? "*" : " ", t.id, t.name || "\u2014", t.role])]);
359
+ const active = stored?.active_tenant_id;
360
+ table([
361
+ ["", "ID", "NAME", "ROLE", "TOKEN"],
362
+ ...data.map((t) => [
363
+ t.id === active ? "*" : " ",
364
+ t.id,
365
+ t.name || "\u2014",
366
+ t.role,
367
+ stored?.tenants[t.id] ? "stored" : "\u2014"
368
+ ])
369
+ ]);
370
+ console.log(`
371
+ * = active. "stored" = local token cached. To switch active: oma auth tenant use <id>`);
372
+ console.log(`To add a tenant the CLI hasn't seen yet: oma auth login --tenant <id>`);
296
373
  }
297
374
  },
298
375
  {
@@ -300,9 +377,9 @@ var commands = [
300
377
  match: ["auth", "tenant", "use"],
301
378
  needsArg: true,
302
379
  usage: "oma auth tenant use <tenant-id>",
303
- desc: "Switch active tenant (mints a new CLI token for that tenant)",
304
- http: "POST /v1/me/cli-tokens {tenant_id}",
305
- async run(config, args) {
380
+ desc: "Switch active tenant; if not yet authenticated for it, opens browser to mint",
381
+ http: "(local file update \u2014 or POST /v1/me/cli-tokens via browser if no cached token)",
382
+ async run(_config, args) {
306
383
  const tenantId = args[0];
307
384
  if (!tenantId) {
308
385
  console.error("Usage: oma auth tenant use <tenant-id>");
@@ -313,7 +390,14 @@ var commands = [
313
390
  console.error("No stored credentials. Run: oma auth login");
314
391
  process.exit(1);
315
392
  }
316
- console.log(`Switching to tenant ${tenantId} \u2014 opening browser to confirm\u2026`);
393
+ if (stored.tenants[tenantId]) {
394
+ const updated = { ...stored, active_tenant_id: tenantId };
395
+ writeCredentials(updated);
396
+ const profile = stored.tenants[tenantId];
397
+ console.log(`\u2713 Active tenant: ${profile.name || tenantId} (${tenantId})`);
398
+ return;
399
+ }
400
+ console.log(`No cached token for ${tenantId}. Opening browser to authorize\u2026`);
317
401
  await authLogin(stored.base_url, tenantId);
318
402
  }
319
403
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openma/cli",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.4",
4
4
  "description": "CLI for the openma managed agents platform",
5
5
  "type": "module",
6
6
  "bin": {