@rogatio/cli 5.5.5 → 5.7.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/node/index.js +220 -52
  2. package/package.json +2 -2
@@ -23813,7 +23813,7 @@ function createRoutes(context) {
23813
23813
  return;
23814
23814
  }
23815
23815
  try {
23816
- await context.writeProject(context.filePath, body);
23816
+ await context.storage.update(context.filePath, body);
23817
23817
  context.project = body;
23818
23818
  res.writeHead(200, { "Content-Type": "application/json" });
23819
23819
  res.end(JSON.stringify({ ok: true }));
@@ -24135,28 +24135,78 @@ async function launchBrowser(url) {
24135
24135
  });
24136
24136
  }
24137
24137
 
24138
- // packages/cli/src/utils/file.ts
24138
+ // packages/cli/src/utils/project-storage.ts
24139
24139
  import { randomBytes as randomBytes4 } from "node:crypto";
24140
- import { mkdir as mkdir3, readFile as readFile5, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
24140
+ import {
24141
+ access,
24142
+ mkdir as mkdir3,
24143
+ readdir,
24144
+ readFile as readFile5,
24145
+ rename as rename2,
24146
+ stat as stat3,
24147
+ unlink as unlink2,
24148
+ writeFile as writeFile3
24149
+ } from "node:fs/promises";
24141
24150
  import { basename as basename3, dirname as dirname6, join as join5 } from "node:path";
24142
- var ProjectFileError = class extends Error {
24151
+ var ProjectStorageError = class extends Error {
24143
24152
  code;
24144
- path;
24145
- constructor(code, path2, message, cause) {
24153
+ id;
24154
+ constructor(code, id2, message, cause) {
24146
24155
  super(message, { cause });
24147
- this.name = "ProjectFileError";
24156
+ this.name = "ProjectStorageError";
24148
24157
  this.code = code;
24149
- this.path = path2;
24158
+ this.id = id2;
24159
+ }
24160
+ };
24161
+ var ProjectFileError = class extends ProjectStorageError {
24162
+ constructor(code, path2, message, cause) {
24163
+ super(code, path2, message, cause);
24164
+ this.name = "ProjectFileError";
24165
+ }
24166
+ get path() {
24167
+ return this.id;
24150
24168
  }
24151
24169
  };
24152
- async function readProject(path2) {
24170
+ function emptyProjectDocument() {
24171
+ return { version: 1, name: "", groups: [] };
24172
+ }
24173
+ function isRogatioProjectFilename(name) {
24174
+ return name === ".rogatio.json" || name.endsWith(".rogatio.json");
24175
+ }
24176
+ function projectName(data) {
24177
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
24178
+ return "";
24179
+ }
24180
+ if (!Object.hasOwn(data, "name")) {
24181
+ return "";
24182
+ }
24183
+ const name = data.name;
24184
+ return typeof name === "string" ? name : "";
24185
+ }
24186
+ async function pathExists(path2) {
24187
+ try {
24188
+ await access(path2);
24189
+ return true;
24190
+ } catch (e) {
24191
+ if (e.code === "ENOENT") {
24192
+ return false;
24193
+ }
24194
+ throw new ProjectFileError(
24195
+ "read-failed",
24196
+ path2,
24197
+ "Failed to access project path",
24198
+ e
24199
+ );
24200
+ }
24201
+ }
24202
+ async function readDocument(id2) {
24153
24203
  try {
24154
- const content = await readFile5(path2, "utf-8");
24204
+ const content = await readFile5(id2, "utf-8");
24155
24205
  const parsed = JSON.parse(content);
24156
24206
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
24157
24207
  throw new ProjectFileError(
24158
24208
  "invalid-format",
24159
- path2,
24209
+ id2,
24160
24210
  "Project file must contain a JSON object"
24161
24211
  );
24162
24212
  }
@@ -24166,7 +24216,7 @@ async function readProject(path2) {
24166
24216
  if (e.code === "ENOENT") {
24167
24217
  throw new ProjectFileError(
24168
24218
  "not-found",
24169
- path2,
24219
+ id2,
24170
24220
  "Project file not found",
24171
24221
  e
24172
24222
  );
@@ -24174,51 +24224,170 @@ async function readProject(path2) {
24174
24224
  if (e instanceof SyntaxError) {
24175
24225
  throw new ProjectFileError(
24176
24226
  "invalid-json",
24177
- path2,
24227
+ id2,
24178
24228
  "Project file contains invalid JSON",
24179
24229
  e
24180
24230
  );
24181
24231
  }
24182
24232
  throw new ProjectFileError(
24183
24233
  "read-failed",
24184
- path2,
24234
+ id2,
24185
24235
  "Failed to read project file",
24186
24236
  e
24187
24237
  );
24188
24238
  }
24189
24239
  }
24190
- async function writeProject(path2, data) {
24191
- const tempName = `.${basename3(dirname6(path2))}.${randomBytes4(8).toString("hex")}.tmp`;
24192
- const tempPath = join5(dirname6(path2), tempName);
24240
+ async function isDirectoryPath(id2) {
24241
+ try {
24242
+ return (await stat3(id2)).isDirectory();
24243
+ } catch {
24244
+ return false;
24245
+ }
24246
+ }
24247
+ async function writeDocument(id2, data) {
24248
+ const tempName = `.${basename3(dirname6(id2))}.${randomBytes4(8).toString("hex")}.tmp`;
24249
+ const tempPath = join5(dirname6(id2), tempName);
24193
24250
  try {
24194
- await mkdir3(dirname6(path2), { recursive: true });
24251
+ if (await isDirectoryPath(id2)) {
24252
+ throw new ProjectFileError(
24253
+ "is-directory",
24254
+ id2,
24255
+ "Target path is a directory"
24256
+ );
24257
+ }
24258
+ await mkdir3(dirname6(id2), { recursive: true });
24195
24259
  await writeFile3(tempPath, JSON.stringify(data, null, 2), "utf-8");
24196
- await rename2(tempPath, path2);
24260
+ await rename2(tempPath, id2);
24197
24261
  } catch (e) {
24262
+ if (e instanceof ProjectFileError) throw e;
24198
24263
  try {
24199
- await import("node:fs/promises").then((fs2) => fs2.unlink(tempPath));
24264
+ await unlink2(tempPath);
24200
24265
  } catch {
24201
24266
  }
24202
- if (e.code === "EISDIR") {
24267
+ if (e.code === "EISDIR" || await isDirectoryPath(id2)) {
24203
24268
  throw new ProjectFileError(
24204
24269
  "is-directory",
24205
- path2,
24270
+ id2,
24206
24271
  "Target path is a directory",
24207
24272
  e
24208
24273
  );
24209
24274
  }
24210
24275
  throw new ProjectFileError(
24211
24276
  "write-failed",
24212
- path2,
24277
+ id2,
24213
24278
  "Failed to write project file",
24214
24279
  e
24215
24280
  );
24216
24281
  }
24217
24282
  }
24283
+ function createJsonFileProjectStorage() {
24284
+ return {
24285
+ async list(scope) {
24286
+ if (scope === void 0 || scope === "") {
24287
+ return [];
24288
+ }
24289
+ let entries;
24290
+ try {
24291
+ entries = await readdir(scope, { withFileTypes: true });
24292
+ } catch (e) {
24293
+ if (e.code === "ENOENT") {
24294
+ return [];
24295
+ }
24296
+ throw new ProjectFileError(
24297
+ "read-failed",
24298
+ scope,
24299
+ "Failed to list project files",
24300
+ e
24301
+ );
24302
+ }
24303
+ const refs = [];
24304
+ for (const entry of entries) {
24305
+ if (entry.isDirectory() || !isRogatioProjectFilename(entry.name)) {
24306
+ continue;
24307
+ }
24308
+ const id2 = join5(scope, entry.name);
24309
+ let name = "";
24310
+ try {
24311
+ const data = await readDocument(id2);
24312
+ name = projectName(data);
24313
+ } catch {
24314
+ name = "";
24315
+ }
24316
+ refs.push({ id: id2, name });
24317
+ }
24318
+ return refs;
24319
+ },
24320
+ async get(id2) {
24321
+ return readDocument(id2);
24322
+ },
24323
+ async create(options) {
24324
+ const id2 = options?.id;
24325
+ if (id2 === void 0 || id2 === "") {
24326
+ throw new ProjectFileError(
24327
+ "invalid-id",
24328
+ id2 ?? "",
24329
+ "JSON-file project storage requires an id (filesystem path)"
24330
+ );
24331
+ }
24332
+ if (await pathExists(id2)) {
24333
+ throw new ProjectFileError(
24334
+ "already-exists",
24335
+ id2,
24336
+ "Project already exists"
24337
+ );
24338
+ }
24339
+ const data = options?.data === void 0 ? emptyProjectDocument() : options.data;
24340
+ await writeDocument(id2, data);
24341
+ return { id: id2, name: projectName(data) };
24342
+ },
24343
+ async import(data, options) {
24344
+ const id2 = options?.id;
24345
+ if (id2 === void 0 || id2 === "") {
24346
+ throw new ProjectFileError(
24347
+ "invalid-id",
24348
+ id2 ?? "",
24349
+ "JSON-file project storage requires an id (filesystem path)"
24350
+ );
24351
+ }
24352
+ await writeDocument(id2, data);
24353
+ return { id: id2, name: projectName(data) };
24354
+ },
24355
+ async update(id2, data) {
24356
+ if (!await pathExists(id2)) {
24357
+ throw new ProjectFileError("not-found", id2, "Project file not found");
24358
+ }
24359
+ await writeDocument(id2, data);
24360
+ },
24361
+ async delete(id2) {
24362
+ try {
24363
+ await unlink2(id2);
24364
+ } catch (e) {
24365
+ if (e.code === "ENOENT") {
24366
+ throw new ProjectFileError(
24367
+ "not-found",
24368
+ id2,
24369
+ "Project file not found",
24370
+ e
24371
+ );
24372
+ }
24373
+ throw new ProjectFileError(
24374
+ "delete-failed",
24375
+ id2,
24376
+ "Failed to delete project file",
24377
+ e
24378
+ );
24379
+ }
24380
+ }
24381
+ };
24382
+ }
24383
+
24384
+ // packages/cli/src/utils/file.ts
24385
+ var defaultStorage = createJsonFileProjectStorage();
24218
24386
 
24219
24387
  // packages/cli/src/commands/edit.ts
24220
24388
  async function editCommand(args, options = {}) {
24221
24389
  const customLaunchBrowser = options.launchBrowser;
24390
+ const storage = options.storage ?? createJsonFileProjectStorage();
24222
24391
  const positionalArgs = [];
24223
24392
  let port;
24224
24393
  for (let i = 0; i < args.length; i++) {
@@ -24254,10 +24423,10 @@ async function editCommand(args, options = {}) {
24254
24423
  filePath = resolve4(process.cwd(), ".rogatio.json");
24255
24424
  }
24256
24425
  try {
24257
- const stat3 = await import("node:fs/promises").then(
24426
+ const stat4 = await import("node:fs/promises").then(
24258
24427
  (fs2) => fs2.stat(filePath)
24259
24428
  );
24260
- if (stat3.isDirectory()) {
24429
+ if (stat4.isDirectory()) {
24261
24430
  console.error("Error: Path is a directory");
24262
24431
  return { exitCode: Promise.resolve(2), shutdown: () => {
24263
24432
  } };
@@ -24270,28 +24439,24 @@ async function editCommand(args, options = {}) {
24270
24439
  }
24271
24440
  }
24272
24441
  let projectData;
24273
- let isNewFile = false;
24274
24442
  try {
24275
- projectData = await readProject(filePath);
24443
+ projectData = await storage.get(filePath);
24276
24444
  } catch (e) {
24277
- if (e instanceof ProjectFileError && e.code === "not-found") {
24278
- projectData = { version: 1, name: "", groups: [] };
24279
- isNewFile = true;
24445
+ if (e instanceof ProjectStorageError && e.code === "not-found") {
24446
+ try {
24447
+ await storage.create({ id: filePath });
24448
+ projectData = emptyProjectDocument();
24449
+ } catch (createError) {
24450
+ console.error(`Error writing initial project: ${createError}`);
24451
+ return { exitCode: Promise.resolve(2), shutdown: () => {
24452
+ } };
24453
+ }
24280
24454
  } else {
24281
24455
  console.error(`Error: ${e}`);
24282
24456
  return { exitCode: Promise.resolve(2), shutdown: () => {
24283
24457
  } };
24284
24458
  }
24285
24459
  }
24286
- if (isNewFile) {
24287
- try {
24288
- await writeProject(filePath, projectData);
24289
- } catch (e) {
24290
- console.error(`Error writing initial project: ${e}`);
24291
- return { exitCode: Promise.resolve(2), shutdown: () => {
24292
- } };
24293
- }
24294
- }
24295
24460
  let aiClient;
24296
24461
  const providerConfig = await readProviderConfig();
24297
24462
  if (providerConfig) {
@@ -24302,7 +24467,7 @@ async function editCommand(args, options = {}) {
24302
24467
  project: projectData,
24303
24468
  filePath,
24304
24469
  csrfToken,
24305
- writeProject,
24470
+ storage,
24306
24471
  shutdown: () => {
24307
24472
  shutdown();
24308
24473
  },
@@ -24766,7 +24931,7 @@ async function trustRuntimeCommand(args) {
24766
24931
  return 2;
24767
24932
  }
24768
24933
  }
24769
- async function runtimeHostCommand(args) {
24934
+ async function runtimeHostCommand(args, options = {}) {
24770
24935
  let root;
24771
24936
  let mockPort;
24772
24937
  const positional = [];
@@ -24811,6 +24976,7 @@ async function runtimeHostCommand(args) {
24811
24976
  }
24812
24977
  let filePath;
24813
24978
  let projectData;
24979
+ const storage = options.storage ?? createJsonFileProjectStorage();
24814
24980
  try {
24815
24981
  if (inputPath === "-") {
24816
24982
  const chunks = [];
@@ -24821,7 +24987,7 @@ async function runtimeHostCommand(args) {
24821
24987
  projectData = JSON.parse(chunks.join(""));
24822
24988
  } else {
24823
24989
  filePath = resolve5(inputPath);
24824
- projectData = await readProject(filePath);
24990
+ projectData = await storage.get(filePath);
24825
24991
  }
24826
24992
  } catch (error) {
24827
24993
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -24873,7 +25039,7 @@ async function runtimeHostCommand(args) {
24873
25039
  });
24874
25040
  return 0;
24875
25041
  }
24876
- async function runtimeCommand(args, _options = {}) {
25042
+ async function runtimeCommand(args, options = {}) {
24877
25043
  if (args.includes("--help") || args.includes("-h")) {
24878
25044
  showRuntimeHelp();
24879
25045
  return 0;
@@ -24881,7 +25047,7 @@ async function runtimeCommand(args, _options = {}) {
24881
25047
  const first = args[0];
24882
25048
  if (first === "install" || first === "trust" || first === "uninstall" || first === "verify")
24883
25049
  return trustRuntimeCommand(args);
24884
- if (first === "host") return runtimeHostCommand(args.slice(1));
25050
+ if (first === "host") return runtimeHostCommand(args.slice(1), options);
24885
25051
  console.error(
24886
25052
  `Error: 'rogatio runtime' no longer starts or stops the runtime. Use 'rogatio runtime install|uninstall' to manage the host manifest and request-body trust, or 'rogatio runtime host [path]' to run the native-messaging host. Start/stop is driven from the extension's controls.`
24887
25053
  );
@@ -25016,7 +25182,7 @@ function testCommandNeedsStdin(args) {
25016
25182
  const positionalUrls = isUrl(positional[0] ?? "") ? positional : positional.slice(1);
25017
25183
  return !hasUrlSource && positionalUrls.length === 0;
25018
25184
  }
25019
- async function testCommandImpl(args, stdinInput, captureOutput) {
25185
+ async function testCommandImpl(args, stdinInput, captureOutput, options = {}) {
25020
25186
  let filePath = resolve6(process.cwd(), ".rogatio.json");
25021
25187
  let jsonMode = false;
25022
25188
  let maxCases;
@@ -25025,6 +25191,7 @@ async function testCommandImpl(args, stdinInput, captureOutput) {
25025
25191
  let defaultMethod;
25026
25192
  let defaultResourceType;
25027
25193
  let argumentError;
25194
+ const storage = options.storage ?? createJsonFileProjectStorage();
25028
25195
  const positionalArgs = [];
25029
25196
  for (let i = 0; i < args.length; i++) {
25030
25197
  const arg = args[i];
@@ -25118,7 +25285,7 @@ async function testCommandImpl(args, stdinInput, captureOutput) {
25118
25285
  if (!stdinInput) throw new Error("No stdin input provided");
25119
25286
  projectData = JSON.parse(stdinInput);
25120
25287
  } else {
25121
- projectData = await readProject(filePath);
25288
+ projectData = await storage.get(filePath);
25122
25289
  }
25123
25290
  } catch (e) {
25124
25291
  const message = e instanceof Error ? e.message : "Unknown error";
@@ -25213,15 +25380,16 @@ ${error.code}: ${error.message}${error.index === void 0 ? "" : ` (case ${error.i
25213
25380
  }
25214
25381
  return dryRunResult.errors.length === 0 ? 0 : 1;
25215
25382
  }
25216
- async function testCommand(args, stdinInput, captureOutput = false) {
25217
- return testCommandImpl(args, stdinInput, captureOutput);
25383
+ async function testCommand(args, stdinInput, captureOutput = false, options = {}) {
25384
+ return testCommandImpl(args, stdinInput, captureOutput, options);
25218
25385
  }
25219
25386
 
25220
25387
  // packages/cli/src/commands/verify.ts
25221
25388
  import { resolve as resolve7 } from "node:path";
25222
- async function verifyCommandImpl(args, stdinInput, captureOutput) {
25389
+ async function verifyCommandImpl(args, stdinInput, captureOutput, options = {}) {
25223
25390
  let filePath;
25224
25391
  let jsonOutput2 = false;
25392
+ const storage = options.storage ?? createJsonFileProjectStorage();
25225
25393
  const positionalArgs = [];
25226
25394
  for (const arg of args) {
25227
25395
  if (arg === "--json") {
@@ -25254,7 +25422,7 @@ async function verifyCommandImpl(args, stdinInput, captureOutput) {
25254
25422
  if (!stdinInput) throw new Error("No stdin input provided");
25255
25423
  projectData = JSON.parse(stdinInput);
25256
25424
  } else {
25257
- projectData = await readProject(filePath);
25425
+ projectData = await storage.get(filePath);
25258
25426
  }
25259
25427
  } catch (e) {
25260
25428
  const message = e instanceof Error ? e.message : "Unknown error";
@@ -25309,8 +25477,8 @@ async function verifyCommandImpl(args, stdinInput, captureOutput) {
25309
25477
  if (diagnostics.length === 0) return 0;
25310
25478
  return 1;
25311
25479
  }
25312
- async function verifyCommand(args, stdinInput, captureOutput = false) {
25313
- return verifyCommandImpl(args, stdinInput, captureOutput);
25480
+ async function verifyCommand(args, stdinInput, captureOutput = false, options = {}) {
25481
+ return verifyCommandImpl(args, stdinInput, captureOutput, options);
25314
25482
  }
25315
25483
 
25316
25484
  // packages/cli/src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "5.5.5",
3
+ "version": "5.7.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local-first browser request and response rules — editor host, file verification, test runner, and runtime dispatch.",
@@ -45,7 +45,7 @@
45
45
  "@rogatio/editor": "workspace:*",
46
46
  "@rogatio/runtime": "workspace:*",
47
47
  "@rogatio/schema": "workspace:*",
48
- "@types/node": "26.4.1",
48
+ "@types/node": "26.5.0",
49
49
  "typescript": "7.0.2",
50
50
  "vitest": "4.1.11"
51
51
  }