@rooaak/cli 0.1.0-beta.1 → 0.1.0-beta.2

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 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/init/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA4XzD,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgctF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/init/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgTzD,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA8WtF"}
@@ -41,7 +41,7 @@ function noTtyUsageError(message, details = {}) {
41
41
  details,
42
42
  });
43
43
  }
44
- async function requireAdminTransport(ctx, r, argvApiKey) {
44
+ async function requireInitTransport(ctx, r, argvApiKey) {
45
45
  const store = createProfileStore({ configPath: ctx.profileConfigPath });
46
46
  const cfgRes = store.readConfig();
47
47
  if (cfgRes.kind === "corrupt") {
@@ -55,19 +55,27 @@ async function requireAdminTransport(ctx, r, argvApiKey) {
55
55
  env: ctx.env.ROOAAK_BASE_URL,
56
56
  profile: profile?.baseUrl,
57
57
  });
58
- async function validateAdminKey(candidate) {
58
+ async function validateInitKey(candidate) {
59
59
  const transport = createTransport({ baseUrl, apiKey: candidate, fetchFn: ctx.fetchFn });
60
- const res = await transport.getJson("/api/v1/projects?limit=1");
60
+ const res = await transport.getJson("/api/v1/agents?limit=1");
61
61
  if (!res.ok) {
62
- return { ok: false, http: { status: res.status, requestId: res.requestId, err: res.error } };
62
+ return { ok: false, status: res.status, requestId: res.requestId, err: res.error };
63
63
  }
64
64
  return { ok: true, transport, meta: { status: res.status, requestId: res.requestId } };
65
65
  }
66
66
  if (typeof argvApiKey === "string" && argvApiKey.length > 0) {
67
- const v = await validateAdminKey(argvApiKey);
68
- if (v.ok)
69
- return { ok: true, transport: v.transport, baseUrl, adminApiKey: argvApiKey, requestMeta: v.meta };
70
- const status = v.http?.status;
67
+ const v = await validateInitKey(argvApiKey);
68
+ if (v.ok) {
69
+ return {
70
+ ok: true,
71
+ transport: v.transport,
72
+ baseUrl,
73
+ apiKey: argvApiKey,
74
+ keySource: "argv",
75
+ requestMeta: v.meta,
76
+ };
77
+ }
78
+ const status = v.status;
71
79
  if (status === 401) {
72
80
  return {
73
81
  ok: false,
@@ -75,7 +83,7 @@ async function requireAdminTransport(ctx, r, argvApiKey) {
75
83
  err: normalizeInitError({
76
84
  code: "auth.invalid_api_key",
77
85
  message: "Invalid API key",
78
- meta: { status: v.http?.status, requestId: v.http?.requestId },
86
+ meta: { status: v.status, requestId: v.requestId },
79
87
  }),
80
88
  };
81
89
  }
@@ -84,21 +92,30 @@ async function requireAdminTransport(ctx, r, argvApiKey) {
84
92
  ok: false,
85
93
  exit: ExitCode.Permission,
86
94
  err: normalizeInitError({
87
- code: "permission.admin_key_required",
88
- message: "Admin API key required to create/select projects.",
89
- details: { next: "Use an admin API key with scope admin:project (project_id must be NULL)." },
90
- meta: { status: v.http?.status, requestId: v.http?.requestId },
95
+ code: "permission.forbidden",
96
+ message: "API key lacks required scope: project:agent",
97
+ details: { next: "Use a project API key with scope project:agent." },
98
+ meta: { status: v.status, requestId: v.requestId },
91
99
  }),
92
100
  };
93
101
  }
94
- return { ok: false, exit: exitCodeForError(v.http?.err ?? normalizeInitError({ code: "server.error", message: "Server error" })), err: v.http?.err ?? normalizeInitError({ code: "server.error", message: "Server error" }) };
102
+ return { ok: false, exit: exitCodeForError(v.err), err: v.err };
95
103
  }
96
104
  if (profile?.apiKey) {
97
- const v = await validateAdminKey(profile.apiKey);
98
- if (v.ok)
99
- return { ok: true, transport: v.transport, baseUrl, adminApiKey: profile.apiKey, requestMeta: v.meta };
100
- const status = v.http?.status;
101
- const err = v.http?.err ?? normalizeInitError({ code: "server.error", message: "Server error" });
105
+ const v = await validateInitKey(profile.apiKey);
106
+ if (v.ok) {
107
+ return {
108
+ ok: true,
109
+ transport: v.transport,
110
+ baseUrl,
111
+ apiKey: profile.apiKey,
112
+ keySource: "profile",
113
+ sourceProfileName: config?.currentProfile,
114
+ requestMeta: v.meta,
115
+ };
116
+ }
117
+ const status = v.status;
118
+ const err = v.err;
102
119
  if (ctx.flags.nonInteractive) {
103
120
  if (status === 401) {
104
121
  return {
@@ -107,7 +124,7 @@ async function requireAdminTransport(ctx, r, argvApiKey) {
107
124
  err: normalizeInitError({
108
125
  code: "auth.invalid_api_key",
109
126
  message: "Invalid API key",
110
- meta: { status: v.http?.status, requestId: v.http?.requestId },
127
+ meta: { status: v.status, requestId: v.requestId },
111
128
  }),
112
129
  };
113
130
  }
@@ -116,23 +133,21 @@ async function requireAdminTransport(ctx, r, argvApiKey) {
116
133
  ok: false,
117
134
  exit: ExitCode.Permission,
118
135
  err: normalizeInitError({
119
- code: "permission.admin_key_required",
120
- message: err.message || "Admin API key required to create/select projects.",
121
- details: {
122
- next: "Pass --api-key with an admin API key (scope admin:project), or switch to an admin profile.",
123
- },
124
- meta: { status: v.http?.status, requestId: v.http?.requestId },
136
+ code: "permission.forbidden",
137
+ message: "API key lacks required scope: project:agent",
138
+ details: { next: "Use a project API key with scope project:agent." },
139
+ meta: { status: v.status, requestId: v.requestId },
125
140
  }),
126
141
  };
127
142
  }
128
143
  return { ok: false, exit: exitCodeForError(err), err };
129
144
  }
130
- // Interactive: allow recovery by prompting for an admin key on auth/permission failures.
145
+ // Interactive: allow recovery by prompting for another key on auth/permission failures.
131
146
  if (status === 401) {
132
- r.warn("Current profile API key is invalid. You'll be prompted for an admin API key.");
147
+ r.warn("Current profile API key is invalid. You'll be prompted for an API key.");
133
148
  }
134
149
  else if (status === 403) {
135
- r.warn("Current profile API key cannot manage projects. You'll be prompted for an admin API key.");
150
+ r.warn("Current profile API key cannot run init. You'll be prompted for a key with project:agent scope.");
136
151
  }
137
152
  else {
138
153
  // Network/transient failures won't be fixed by prompting.
@@ -156,107 +171,32 @@ async function requireAdminTransport(ctx, r, argvApiKey) {
156
171
  err: noTtyUsageError("Cannot prompt for API key because stdin is not a TTY. Re-run with --non-interactive and provide --api-key, or run `rooaak auth login --api-key ...` from an interactive terminal."),
157
172
  };
158
173
  }
159
- // Interactive: prompt until valid admin key is provided.
174
+ // Interactive: prompt until a key that can run init is provided.
160
175
  while (true) {
161
- const entered = await promptSecret({ stderr: ctx.stderr, stdin: ctx.stdin, stdinIsTTY: ctx.stdinIsTTY }, "Admin API key", { allowEmpty: false });
162
- const v = await validateAdminKey(entered);
163
- if (v.ok)
164
- return { ok: true, transport: v.transport, baseUrl, adminApiKey: entered, requestMeta: v.meta };
165
- const status = v.http?.status;
176
+ const entered = await promptSecret({ stderr: ctx.stderr, stdin: ctx.stdin, stdinIsTTY: ctx.stdinIsTTY }, "API key", { allowEmpty: false });
177
+ const v = await validateInitKey(entered);
178
+ if (v.ok) {
179
+ return {
180
+ ok: true,
181
+ transport: v.transport,
182
+ baseUrl,
183
+ apiKey: entered,
184
+ keySource: "prompt",
185
+ requestMeta: v.meta,
186
+ };
187
+ }
188
+ const status = v.status;
166
189
  if (status === 401) {
167
190
  r.error("Invalid API key. Try again.");
168
191
  continue;
169
192
  }
170
193
  if (status === 403) {
171
- r.error("That key cannot manage projects. Use an admin API key with scope admin:project.");
194
+ r.error("That key lacks required scope for init. Use a key with project:agent.");
172
195
  continue;
173
196
  }
174
- r.error(v.http?.err.message ?? "Request failed. Try again.");
197
+ r.error(v.err.message ?? "Request failed. Try again.");
175
198
  }
176
199
  }
177
- async function findProjectsByExactName(adminTransport, projectName) {
178
- const matches = [];
179
- let cursor = null;
180
- let meta = null;
181
- while (true) {
182
- const qs = new URLSearchParams();
183
- qs.set("limit", "500");
184
- if (cursor)
185
- qs.set("cursor", cursor);
186
- const httpRes = await adminTransport.getJson(`/api/v1/projects?${qs.toString()}`);
187
- if (!httpRes.ok)
188
- return { ok: false, err: httpRes.error };
189
- meta = { status: httpRes.status, requestId: httpRes.requestId };
190
- const items = Array.isArray(httpRes.data.items) ? httpRes.data.items : [];
191
- for (const p of items) {
192
- if (p.name === projectName)
193
- matches.push(p);
194
- if (matches.length >= 2) {
195
- return { ok: true, matches, meta };
196
- }
197
- }
198
- const next = httpRes.data.nextCursor ?? null;
199
- if (!next)
200
- return { ok: true, matches, meta };
201
- cursor = next;
202
- }
203
- }
204
- async function ensureProjectByName(input) {
205
- const findRes = await findProjectsByExactName(input.adminTransport, input.projectName);
206
- if (!findRes.ok)
207
- return { ok: false, err: findRes.err };
208
- const matches = findRes.matches;
209
- if (matches.length === 1)
210
- return { ok: true, project: matches[0], created: false, meta: findRes.meta };
211
- if (matches.length > 1) {
212
- return {
213
- ok: false,
214
- err: normalizeInitError({
215
- code: "conflict.duplicate_project_name",
216
- message: `Multiple projects named '${input.projectName}' exist.`,
217
- details: { next: "Run `rooaak init` in interactive mode to select a project." },
218
- meta: findRes.meta,
219
- }),
220
- };
221
- }
222
- const createRes = await input.adminTransport.requestJson({
223
- method: "POST",
224
- path: "/api/v1/projects",
225
- body: { name: input.projectName },
226
- });
227
- if (createRes.ok) {
228
- return { ok: true, project: { id: createRes.data.id, name: createRes.data.name }, created: true, meta: { status: createRes.status, requestId: createRes.requestId } };
229
- }
230
- // Best-effort recovery: if the create failed but the project exists now (race, duplicate name), select it.
231
- const findAgain = await findProjectsByExactName(input.adminTransport, input.projectName);
232
- if (findAgain.ok) {
233
- const againMatches = findAgain.matches;
234
- if (againMatches.length === 1)
235
- return { ok: true, project: againMatches[0], created: false, meta: findAgain.meta };
236
- }
237
- return { ok: false, err: createRes.error };
238
- }
239
- async function createProjectApiKey(input) {
240
- const httpRes = await input.adminTransport.requestJson({
241
- method: "POST",
242
- path: `/api/v1/projects/${input.projectId}/api-keys`,
243
- body: { name: input.name },
244
- });
245
- if (!httpRes.ok)
246
- return { ok: false, err: httpRes.error };
247
- const key = isPlainObject(httpRes.data) && typeof httpRes.data.key === "string" ? httpRes.data.key : null;
248
- if (!key) {
249
- return {
250
- ok: false,
251
- err: normalizeInitError({
252
- code: "server.invalid_response",
253
- message: "Server returned an unexpected API key response.",
254
- meta: { status: httpRes.status, requestId: httpRes.requestId },
255
- }),
256
- };
257
- }
258
- return { ok: true, apiKey: key, meta: { status: httpRes.status, requestId: httpRes.requestId } };
259
- }
260
200
  function writeProjectProfile(input) {
261
201
  const store = createProfileStore({ configPath: input.ctx.profileConfigPath });
262
202
  const existingRes = store.readConfig();
@@ -307,7 +247,7 @@ export async function initCommand(ctx, argv) {
307
247
  const r = createRenderer(ctx);
308
248
  const parsed = parseOptions(argv, [
309
249
  { name: "--help", takesValue: false },
310
- { name: "--api-key", takesValue: true }, // admin key
250
+ { name: "--api-key", takesValue: true }, // must include project:agent
311
251
  { name: "--project-name", takesValue: true },
312
252
  { name: "--agent-name", takesValue: true },
313
253
  { name: "--test-message", takesValue: true },
@@ -347,14 +287,14 @@ export async function initCommand(ctx, argv) {
347
287
  command: "init",
348
288
  usage: "rooaak init [--api-key <key>] [--project-name <name>] [--agent-name <name>] [--test-message <text>] [--wait-timeout-ms <ms>] [--profile <name>] [--start|--no-start] [--json] [--non-interactive]",
349
289
  flags: [
350
- { name: "--api-key <key>", description: "Admin API key (required when not logged in as admin)" },
351
- { name: "--project-name <name>", description: "Project name to create/select" },
290
+ { name: "--api-key <key>", description: "Project API key (must include project:agent scope)" },
291
+ { name: "--project-name <name>", description: "Optional project label for local profile naming/output" },
352
292
  { name: "--agent-name <name>", description: "Agent name to create" },
353
293
  { name: "--test-message <text>", description: "Message to send after creation" },
354
294
  { name: "--wait-timeout-ms <ms>", description: "How long to wait for the first response (default 60000ms; overrides ROOAAK_SYNC_MESSAGE_TIMEOUT_MS)" },
355
295
  { name: "--start", description: "Start the agent (default)" },
356
296
  { name: "--no-start", description: "Skip starting the agent" },
357
- { name: "--profile <name>", description: "Profile name to save the created project API key under" },
297
+ { name: "--profile <name>", description: "Profile name to save the API key under" },
358
298
  ],
359
299
  }));
360
300
  return ExitCode.Success;
@@ -364,14 +304,14 @@ export async function initCommand(ctx, argv) {
364
304
  " rooaak init [flags]",
365
305
  "",
366
306
  "Flags:",
367
- " --api-key <key> Admin API key (required when not logged in as admin)",
368
- " --project-name <name> Project name to create/select",
307
+ " --api-key <key> Project API key (must include project:agent scope)",
308
+ " --project-name <name> Optional project label for local profile naming/output",
369
309
  " --agent-name <name> Agent name to create",
370
310
  " --test-message <text> Message to send after creation",
371
311
  " --wait-timeout-ms <ms> How long to wait for the first response (default 60000ms; overrides ROOAAK_SYNC_MESSAGE_TIMEOUT_MS)",
372
312
  " --start Start the agent (default)",
373
313
  " --no-start Skip starting the agent",
374
- " --profile <name> Profile name to save created project API key under",
314
+ " --profile <name> Profile name to save API key under",
375
315
  "",
376
316
  "Global flags:",
377
317
  " --json Machine output (single JSON object on stdout)",
@@ -411,24 +351,7 @@ export async function initCommand(ctx, argv) {
411
351
  else
412
352
  startAgent = await promptConfirm({ stderr: ctx.stderr, stdin: ctx.stdin, stdinIsTTY: ctx.stdinIsTTY }, "Start the agent now?", { defaultYes: true });
413
353
  if (!projectName) {
414
- if (!interactive) {
415
- const err = normalizeLocalUsageError("Missing required flag: --project-name");
416
- if (ctx.flags.json)
417
- r.json(toJsonErrorEnvelope(err));
418
- else
419
- r.error(err.message);
420
- return ExitCode.Usage;
421
- }
422
- if (ctx.stdinIsTTY !== true) {
423
- const err = noTtyUsageError("Cannot prompt for --project-name because stdin is not a TTY. Re-run with --non-interactive and provide --project-name.");
424
- if (ctx.flags.json)
425
- r.json(toJsonErrorEnvelope(err));
426
- else
427
- r.error(err.message);
428
- return ExitCode.Usage;
429
- }
430
- projectName = await promptText({ stderr: ctx.stderr, stdin: ctx.stdin, stdinIsTTY: ctx.stdinIsTTY }, "Project name", { defaultValue: safeBasename(ctx.cwd) });
431
- projectName = projectName.trim();
354
+ projectName = safeBasename(ctx.cwd);
432
355
  }
433
356
  if (!agentName) {
434
357
  if (!interactive) {
@@ -492,110 +415,42 @@ export async function initCommand(ctx, argv) {
492
415
  }
493
416
  waitTimeoutMs = Math.floor(n);
494
417
  }
495
- const adminApiKey = typeof parsed.values["--api-key"] === "string" ? parsed.values["--api-key"] : undefined;
496
- const adminRes = await requireAdminTransport(ctx, r, adminApiKey);
497
- if (!adminRes.ok) {
418
+ const inputApiKey = typeof parsed.values["--api-key"] === "string" ? parsed.values["--api-key"] : undefined;
419
+ const initAuthRes = await requireInitTransport(ctx, r, inputApiKey);
420
+ if (!initAuthRes.ok) {
498
421
  if (ctx.flags.json)
499
- r.json(toJsonErrorEnvelope(adminRes.err));
422
+ r.json(toJsonErrorEnvelope(initAuthRes.err));
500
423
  else
501
- r.error(adminRes.err.message);
502
- return adminRes.exit;
503
- }
504
- const { transport: adminTransport, baseUrl } = adminRes;
505
- if (interactive) {
506
- r.warn("This will create/select a project, generate a project API key, create an agent, and send a first message.");
424
+ r.error(initAuthRes.err.message);
425
+ return initAuthRes.exit;
507
426
  }
508
- // Create/select project.
509
- let projectRes;
427
+ const { baseUrl } = initAuthRes;
428
+ const project = { id: null, name: projectName, created: false };
429
+ let projectApiKey = initAuthRes.apiKey;
430
+ let profileName = initAuthRes.sourceProfileName ?? "default";
510
431
  if (interactive) {
511
- const findRes = await findProjectsByExactName(adminTransport, projectName);
512
- if (!findRes.ok) {
513
- const err = findRes.err;
432
+ r.warn("Using your project API key. This will create an agent and send a first message.");
433
+ }
434
+ const shouldPersistProfile = initAuthRes.keySource !== "profile" || typeof flagProfileName === "string";
435
+ if (shouldPersistProfile) {
436
+ const writeRes = writeProjectProfile({
437
+ ctx,
438
+ r,
439
+ baseUrl,
440
+ apiKey: projectApiKey,
441
+ projectName: project.name,
442
+ profileName: typeof flagProfileName === "string" ? flagProfileName : undefined,
443
+ });
444
+ if (!writeRes.ok) {
445
+ const err = writeRes.err;
514
446
  if (ctx.flags.json)
515
447
  r.json(toJsonErrorEnvelope(err));
516
448
  else
517
449
  r.error(err.message);
518
- return exitCodeForError(err);
519
- }
520
- if (findRes.matches.length === 1) {
521
- const shouldPrompt = ctx.stdinIsTTY === true;
522
- const useExisting = shouldPrompt
523
- ? await promptConfirm({ stderr: ctx.stderr, stdin: ctx.stdin, stdinIsTTY: ctx.stdinIsTTY }, `Project '${projectName}' exists. Use it?`, { defaultYes: true })
524
- : true;
525
- if (useExisting) {
526
- projectRes = { ok: true, project: findRes.matches[0], created: false, meta: findRes.meta };
527
- }
528
- else {
529
- projectName = await promptText({ stderr: ctx.stderr, stdin: ctx.stdin, stdinIsTTY: ctx.stdinIsTTY }, "Project name", { defaultValue: `${projectName}-2` });
530
- projectName = projectName.trim();
531
- projectRes = await ensureProjectByName({ adminTransport, projectName });
532
- }
450
+ return writeRes.exit;
533
451
  }
534
- else if (findRes.matches.length > 1) {
535
- projectRes = {
536
- ok: false,
537
- err: normalizeInitError({
538
- code: "conflict.duplicate_project_name",
539
- message: `Multiple projects named '${projectName}' exist.`,
540
- details: { next: "Specify a unique --project-name or clean up duplicates." },
541
- meta: findRes.meta,
542
- }),
543
- };
544
- }
545
- else {
546
- projectRes = await ensureProjectByName({ adminTransport, projectName });
547
- }
548
- }
549
- else {
550
- projectRes = await ensureProjectByName({ adminTransport, projectName });
452
+ profileName = writeRes.profileName;
551
453
  }
552
- if (!projectRes.ok) {
553
- const err = projectRes.err;
554
- if (ctx.flags.json)
555
- r.json(toJsonErrorEnvelope(err));
556
- else {
557
- r.error(err.message);
558
- if (err.code === "permission.forbidden" || err.code.startsWith("permission.")) {
559
- r.warn("Project management requires an admin API key with scope admin:project.");
560
- }
561
- }
562
- return exitCodeForError(err);
563
- }
564
- const project = projectRes.project;
565
- // Create a project-scoped API key and store it locally.
566
- const apiKeyRes = await createProjectApiKey({
567
- adminTransport,
568
- projectId: project.id,
569
- name: "rooaak-cli-init",
570
- });
571
- if (!apiKeyRes.ok) {
572
- const err = apiKeyRes.err;
573
- if (ctx.flags.json)
574
- r.json(toJsonErrorEnvelope(err));
575
- else {
576
- r.error(err.message);
577
- r.warn("Creating project API keys requires admin:project (admin key) or project:api-keys (project key).");
578
- }
579
- return exitCodeForError(err);
580
- }
581
- const projectApiKey = apiKeyRes.apiKey;
582
- const writeRes = writeProjectProfile({
583
- ctx,
584
- r,
585
- baseUrl,
586
- apiKey: projectApiKey,
587
- projectName: project.name,
588
- profileName: typeof flagProfileName === "string" ? flagProfileName : undefined,
589
- });
590
- if (!writeRes.ok) {
591
- const err = writeRes.err;
592
- if (ctx.flags.json)
593
- r.json(toJsonErrorEnvelope(err));
594
- else
595
- r.error(err.message);
596
- return writeRes.exit;
597
- }
598
- const profileName = writeRes.profileName;
599
454
  const projectTransport = createTransport({ baseUrl, apiKey: projectApiKey, fetchFn: ctx.fetchFn });
600
455
  // Create agent.
601
456
  const agentRes = await projectTransport.requestJson({
@@ -710,7 +565,7 @@ export async function initCommand(ctx, argv) {
710
565
  const output = {
711
566
  baseUrl,
712
567
  profile: profileName,
713
- project: { id: project.id, name: project.name, created: projectRes.created },
568
+ project: { id: project.id, name: project.name, created: project.created },
714
569
  agent: { id: agent.id, name: agent.name, started: startAgent },
715
570
  message: {
716
571
  id: messageId,
@@ -727,7 +582,10 @@ export async function initCommand(ctx, argv) {
727
582
  r.json(toJsonOkEnvelope(output, meta));
728
583
  return ExitCode.Success;
729
584
  }
730
- r.text(`Project: ${project.name} (${project.id})`);
585
+ if (project.id)
586
+ r.text(`Project: ${project.name} (${project.id})`);
587
+ else
588
+ r.text(`Project: ${project.name}`);
731
589
  r.text(`Profile saved: '${profileName}'`);
732
590
  r.text(`Agent: ${agent.name} (${agent.id})`);
733
591
  if (messageStatus === "responded") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rooaak/cli",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Rooaak CLI (companion to the Rooaak SDK)",
5
5
  "private": false,
6
6
  "type": "module",