@dirxai/cli 0.2.1 → 0.3.1

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 (4) hide show
  1. package/dist/index.js +167 -175
  2. package/package.json +5 -11
  3. package/LICENSE +0 -21
  4. package/README.md +0 -91
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, existsSync, mkdirSync, writeFileSync, readdirSync, statSync, chmodSync } from 'fs';
3
- import { dirname, resolve, join, extname, relative } from 'path';
2
+ import { readFileSync, existsSync, mkdirSync, writeFileSync, chmodSync } from 'fs';
3
+ import { dirname, resolve, join } from 'path';
4
4
  import { homedir } from 'os';
5
+ import { detectFramework, scanRoutes, getAuthHint } from '@dirxai/core';
5
6
  import { readFile } from 'fs/promises';
6
7
  import { fileURLToPath } from 'url';
7
8
  import { Command } from 'commander';
@@ -184,24 +185,6 @@ Next steps:`);
184
185
  console.log(` 2. Run 'dirx generate' to scan routes`);
185
186
  console.log(` 3. Run 'dirx register --domain <your-domain>' to publish`);
186
187
  }
187
- function detectFramework(target) {
188
- if (existsSync(join(target, "Cargo.toml"))) {
189
- return { lang: "rust", framework: "axum" };
190
- }
191
- if (existsSync(join(target, "go.mod"))) {
192
- return { lang: "go", framework: "net/http" };
193
- }
194
- if (existsSync(join(target, "package.json"))) {
195
- if (existsSync(join(target, "next.config.js")) || existsSync(join(target, "next.config.mjs"))) {
196
- return { lang: "node", framework: "nextjs" };
197
- }
198
- return { lang: "node", framework: "express" };
199
- }
200
- if (existsSync(join(target, "requirements.txt")) || existsSync(join(target, "pyproject.toml"))) {
201
- return { lang: "python", framework: "fastapi" };
202
- }
203
- return { lang: "unknown", framework: "generic" };
204
- }
205
188
  var init_init = __esm({
206
189
  "src/commands/init.ts"() {
207
190
  }
@@ -310,12 +293,8 @@ async function runGenerate(dir, opts) {
310
293
  process.exitCode = 1;
311
294
  return;
312
295
  }
313
- const { framework } = detectFrameworkFromPackage(target);
314
- const patterns = getRoutePatterns(framework);
315
- const routes = [];
316
- if (patterns.length > 0) {
317
- scanDir(target, target, patterns, framework, routes, 0);
318
- }
296
+ const { framework } = detectFramework(target);
297
+ const routes = scanRoutes(target, framework);
319
298
  console.log(`Scanned ${target} (${framework})`);
320
299
  if (routes.length > 0) {
321
300
  console.log(`
@@ -342,106 +321,8 @@ Registering with gateway...`);
342
321
  });
343
322
  }
344
323
  }
345
- function detectFrameworkFromPackage(target) {
346
- const pkgPath = join(target, "package.json");
347
- if (existsSync(pkgPath)) {
348
- try {
349
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
350
- const deps = {
351
- ...pkg.dependencies,
352
- ...pkg.devDependencies
353
- };
354
- if (deps.next) return { framework: "nextjs" };
355
- if (deps.hono) return { framework: "hono" };
356
- if (deps.fastify) return { framework: "fastify" };
357
- if (deps.express) return { framework: "express" };
358
- } catch {
359
- }
360
- }
361
- if (existsSync(join(target, "go.mod")))
362
- return { framework: "go" };
363
- if (existsSync(join(target, "Cargo.toml")))
364
- return { framework: "axum" };
365
- if (existsSync(join(target, "requirements.txt")) || existsSync(join(target, "pyproject.toml")))
366
- return { framework: "fastapi" };
367
- return { framework: "generic" };
368
- }
369
- function getRoutePatterns(framework) {
370
- switch (framework) {
371
- case "express":
372
- case "hono":
373
- case "fastify":
374
- return [".get(", ".post(", ".put(", ".delete(", ".patch(", "router."];
375
- case "nextjs":
376
- return ["export default", "export async function"];
377
- case "fastapi":
378
- return ["@app.get", "@app.post", "@app.put", "@app.delete", "@router."];
379
- case "go":
380
- return ["HandleFunc(", "Get(", "Post(", "Put(", "Delete(", "r.Route("];
381
- case "axum":
382
- return [".route(", ".get(", ".post(", ".put(", ".delete("];
383
- default:
384
- return [];
385
- }
386
- }
387
- function scanDir(root, dir, patterns, framework, routes, depth) {
388
- if (depth > MAX_SCAN_DEPTH) return;
389
- let entries;
390
- try {
391
- entries = readdirSync(dir);
392
- } catch {
393
- return;
394
- }
395
- for (const name of entries) {
396
- if (SKIP_DIRS.has(name)) continue;
397
- const fullPath = join(dir, name);
398
- let stat;
399
- try {
400
- stat = statSync(fullPath);
401
- } catch {
402
- continue;
403
- }
404
- if (stat.isDirectory()) {
405
- scanDir(root, fullPath, patterns, framework, routes, depth + 1);
406
- continue;
407
- }
408
- if (!stat.isFile()) continue;
409
- if (!SCAN_EXTENSIONS.has(extname(name))) continue;
410
- try {
411
- const content = readFileSync(fullPath, "utf-8");
412
- if (patterns.some((p) => content.includes(p))) {
413
- routes.push({
414
- file: relative(root, fullPath),
415
- framework
416
- });
417
- }
418
- } catch {
419
- }
420
- }
421
- }
422
- var MAX_SCAN_DEPTH, SKIP_DIRS, SCAN_EXTENSIONS;
423
324
  var init_generate = __esm({
424
325
  "src/commands/generate.ts"() {
425
- MAX_SCAN_DEPTH = 8;
426
- SKIP_DIRS = /* @__PURE__ */ new Set([
427
- "node_modules",
428
- ".git",
429
- "target",
430
- "__pycache__",
431
- "vendor",
432
- "dist",
433
- "build",
434
- ".next"
435
- ]);
436
- SCAN_EXTENSIONS = /* @__PURE__ */ new Set([
437
- ".ts",
438
- ".tsx",
439
- ".js",
440
- ".jsx",
441
- ".py",
442
- ".go",
443
- ".rs"
444
- ]);
445
326
  }
446
327
  });
447
328
 
@@ -643,35 +524,130 @@ var init_client = __esm({
643
524
  }
644
525
  });
645
526
 
646
- // src/keys/provider-map.ts
647
- function getAuthHint(domain) {
648
- return providers[domain] ?? null;
649
- }
650
- var providers;
651
- var init_provider_map = __esm({
652
- "src/keys/provider-map.ts"() {
653
- providers = {
654
- "api.github.com": {
655
- envVar: "GITHUB_TOKEN",
656
- guideUrl: "https://github.com/settings/tokens"
657
- },
658
- "api.openai.com": {
659
- envVar: "OPENAI_API_KEY",
660
- guideUrl: "https://platform.openai.com/api-keys"
661
- },
662
- "api.anthropic.com": {
663
- envVar: "ANTHROPIC_API_KEY",
664
- guideUrl: "https://console.anthropic.com/settings/keys"
665
- },
666
- "generativelanguage.googleapis.com": {
667
- envVar: "GOOGLE_API_KEY",
668
- guideUrl: "https://aistudio.google.com/apikey"
669
- },
670
- "api.stripe.com": {
671
- envVar: "STRIPE_SECRET_KEY",
672
- guideUrl: "https://dashboard.stripe.com/apikeys"
527
+ // src/format.ts
528
+ function isSearchResults(data) {
529
+ if (!Array.isArray(data) || data.length === 0) return false;
530
+ const first = data[0];
531
+ return typeof first === "object" && first !== null && "domain" in first && ("title" in first || "name" in first);
532
+ }
533
+ function isEndpointResults(data) {
534
+ if (!Array.isArray(data) || data.length === 0) return false;
535
+ const first = data[0];
536
+ return typeof first === "object" && first !== null && "method" in first && "path" in first;
537
+ }
538
+ function extractPayload(result) {
539
+ if (typeof result !== "object" || result === null) return result;
540
+ const obj = result;
541
+ if ("ok" in obj && "data" in obj) {
542
+ const data = obj.data;
543
+ if (typeof data === "object" && data !== null && "results" in data) {
544
+ return data.results;
545
+ }
546
+ return data;
547
+ }
548
+ if ("results" in obj && "query" in obj) return obj.results;
549
+ return result;
550
+ }
551
+ function formatLs(result) {
552
+ const payload = extractPayload(result);
553
+ if (Array.isArray(payload)) {
554
+ if (payload.length === 0) return "(empty)";
555
+ return payload.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join("\n");
556
+ }
557
+ if (typeof payload === "object" && payload !== null && "items" in payload) {
558
+ const items = payload.items;
559
+ if (!Array.isArray(items) || items.length === 0) return "(empty)";
560
+ const rich = items.some(
561
+ (item) => typeof item === "object" && item !== null && "path" in item
562
+ );
563
+ if (!rich) {
564
+ return items.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join("\n");
565
+ }
566
+ const entries = items;
567
+ const maxPath = Math.min(
568
+ 40,
569
+ Math.max(8, ...entries.map((e) => (e.path ?? "").length))
570
+ );
571
+ return entries.map((entry) => {
572
+ const path = entry.path ?? "<unknown>";
573
+ const title = entry.title ?? "";
574
+ if (title) {
575
+ return `${path.padEnd(maxPath)} ${title}`;
673
576
  }
674
- };
577
+ return path;
578
+ }).join("\n");
579
+ }
580
+ return JSON.stringify(payload);
581
+ }
582
+ function formatGrep(result) {
583
+ const payload = extractPayload(result);
584
+ if (isSearchResults(payload)) {
585
+ if (payload.length === 0) return "(no matches)";
586
+ let domainCount = 0;
587
+ let endpointCount = 0;
588
+ const blocks = payload.map((s) => {
589
+ domainCount++;
590
+ const name = s.title || s.name || "";
591
+ const header = name ? `${s.domain} ${name}` : s.domain;
592
+ const lines = [header];
593
+ if (s.description) {
594
+ const desc = s.description.length > 80 ? s.description.slice(0, 77) + "..." : s.description;
595
+ lines.push(` ${desc}`);
596
+ }
597
+ const endpoints = s.matched_endpoints;
598
+ if (endpoints && endpoints.length > 0) {
599
+ lines[0] += ` \xB7 ${endpoints.length} matched`;
600
+ for (const ep of endpoints) {
601
+ endpointCount++;
602
+ const method = (ep.method ?? "").padEnd(6);
603
+ const desc = ep.description ? ` -- ${ep.description}` : "";
604
+ lines.push(` ${method} ${ep.path}${desc}`);
605
+ }
606
+ }
607
+ return lines.join("\n");
608
+ });
609
+ const output2 = blocks.join("\n\n");
610
+ if (domainCount > 0 || endpointCount > 0) {
611
+ const parts = [];
612
+ if (domainCount > 0) parts.push(`${domainCount} service${domainCount === 1 ? "" : "s"}`);
613
+ if (endpointCount > 0) parts.push(`${endpointCount} endpoint${endpointCount === 1 ? "" : "s"}`);
614
+ return `${output2}
615
+
616
+ -- ${parts.join(", ")} matched`;
617
+ }
618
+ return output2;
619
+ }
620
+ if (isEndpointResults(payload)) {
621
+ if (payload.length === 0) return "(no matches)";
622
+ return payload.map((ep) => {
623
+ const method = (ep.method ?? "").padEnd(6);
624
+ const desc = ep.description ? ` -- ${ep.description}` : "";
625
+ return `${method} ${ep.path}${desc}`;
626
+ }).join("\n");
627
+ }
628
+ return JSON.stringify(payload);
629
+ }
630
+ function formatCat(result) {
631
+ const payload = extractPayload(result);
632
+ if (typeof payload === "string") return payload;
633
+ return JSON.stringify(payload);
634
+ }
635
+ function formatMutate(result, verb, path) {
636
+ const payload = extractPayload(result);
637
+ if (payload === null || payload === void 0) {
638
+ return `${verb}: ${path}`;
639
+ }
640
+ if (typeof payload === "object" && Object.keys(payload).length === 0) {
641
+ return `${verb}: ${path}`;
642
+ }
643
+ return `${verb}: ${path}
644
+ ${JSON.stringify(payload)}`;
645
+ }
646
+ function formatJson(result) {
647
+ return JSON.stringify(result);
648
+ }
649
+ var init_format = __esm({
650
+ "src/format.ts"() {
675
651
  }
676
652
  });
677
653
 
@@ -680,8 +656,8 @@ var fs_exports = {};
680
656
  __export(fs_exports, {
681
657
  registerFsCommands: () => registerFsCommands
682
658
  });
683
- function output(result) {
684
- console.log(JSON.stringify(result, null, 2));
659
+ function output(result, jsonMode) {
660
+ console.log(jsonMode ? formatJson(result) : formatCat(result));
685
661
  }
686
662
  function extractDomain(path) {
687
663
  const cleaned = path.replace(/^\/+/, "");
@@ -719,33 +695,40 @@ function handleError(err, cmdPath) {
719
695
  process.exit(1);
720
696
  }
721
697
  }
722
- console.error(JSON.stringify({ error: message }));
698
+ console.error(message);
723
699
  process.exit(1);
724
700
  }
725
701
  function registerFsCommands(program2) {
726
- program2.command("ls").description("List directory contents in the DirX path space").argument("<path>", "Directory path (e.g. /net/)").action(async (path) => {
702
+ program2.command("ls").description("List directory contents in the DirX path space").argument("[path]", "Directory path (e.g. /net/)", "/").action(async (path) => {
727
703
  try {
704
+ let normalizedPath = path.trim();
705
+ if (normalizedPath.length > 1 && normalizedPath.endsWith("/")) {
706
+ normalizedPath = normalizedPath.replace(/\/+$/, "");
707
+ }
708
+ const jsonMode = program2.opts().json === true;
728
709
  const client = await createClient();
729
- const result = await client.execute(`ls ${path}`);
730
- output(result);
710
+ const result = await client.execute(`ls ${normalizedPath}`);
711
+ console.log(jsonMode ? formatJson(result) : formatLs(result));
731
712
  } catch (err) {
732
713
  handleError(err, path);
733
714
  }
734
715
  });
735
716
  program2.command("cat").description("Read file contents from a DirX path").argument("<path>", "File path").action(async (path) => {
736
717
  try {
718
+ const jsonMode = program2.opts().json === true;
737
719
  const client = await createClient();
738
720
  const result = await client.execute(`cat ${path}`);
739
- output(result);
721
+ output(result, jsonMode);
740
722
  } catch (err) {
741
723
  handleError(err, path);
742
724
  }
743
725
  });
744
726
  program2.command("read").description("Read file contents (alias for cat)").argument("<path>", "File path").action(async (path) => {
745
727
  try {
728
+ const jsonMode = program2.opts().json === true;
746
729
  const client = await createClient();
747
730
  const result = await client.execute(`cat ${path}`);
748
- output(result);
731
+ output(result, jsonMode);
749
732
  } catch (err) {
750
733
  handleError(err, path);
751
734
  }
@@ -755,12 +738,13 @@ function registerFsCommands(program2) {
755
738
  try {
756
739
  let payload = opts.data ?? "";
757
740
  if (opts.file) {
758
- const { readFileSync: readFileSync4 } = await import('fs');
759
- payload = readFileSync4(opts.file, "utf-8");
741
+ const { readFileSync: readFileSync3 } = await import('fs');
742
+ payload = readFileSync3(opts.file, "utf-8");
760
743
  }
744
+ const jsonMode = program2.opts().json === true;
761
745
  const client = await createClient();
762
746
  const result = await client.execute(`write ${path} ${payload}`);
763
- output(result);
747
+ console.log(jsonMode ? formatJson(result) : formatMutate(result, "Created", path));
764
748
  } catch (err) {
765
749
  handleError(err, path);
766
750
  }
@@ -771,12 +755,13 @@ function registerFsCommands(program2) {
771
755
  try {
772
756
  let payload = opts.data ?? "";
773
757
  if (opts.file) {
774
- const { readFileSync: readFileSync4 } = await import('fs');
775
- payload = readFileSync4(opts.file, "utf-8");
758
+ const { readFileSync: readFileSync3 } = await import('fs');
759
+ payload = readFileSync3(opts.file, "utf-8");
776
760
  }
761
+ const jsonMode = program2.opts().json === true;
777
762
  const client = await createClient();
778
763
  const result = await client.execute(`edit ${path} ${payload}`);
779
- output(result);
764
+ console.log(jsonMode ? formatJson(result) : formatMutate(result, "Updated", path));
780
765
  } catch (err) {
781
766
  handleError(err, path);
782
767
  }
@@ -784,27 +769,30 @@ function registerFsCommands(program2) {
784
769
  );
785
770
  program2.command("rm").description("Remove a resource at a DirX path").argument("<path>", "Resource path").action(async (path) => {
786
771
  try {
772
+ const jsonMode = program2.opts().json === true;
787
773
  const client = await createClient();
788
774
  const result = await client.execute(`rm ${path}`);
789
- output(result);
775
+ console.log(jsonMode ? formatJson(result) : formatMutate(result, "Removed", path));
790
776
  } catch (err) {
791
777
  handleError(err, path);
792
778
  }
793
779
  });
794
780
  program2.command("bash").description("Execute a multi-step pipeline on the gateway").argument("<pipeline>", "Pipeline expression").action(async (pipeline) => {
795
781
  try {
782
+ const jsonMode = program2.opts().json === true;
796
783
  const client = await createClient();
797
784
  const result = await client.execute(`bash ${pipeline}`);
798
- output(result);
785
+ output(result, jsonMode);
799
786
  } catch (err) {
800
787
  handleError(err);
801
788
  }
802
789
  });
803
- program2.command("grep").description("Search across services in the DirX path space").argument("<pattern>", "Search pattern").argument("<path>", "Search scope path").action(async (pattern, path) => {
790
+ program2.command("grep").description("Search across services in the DirX path space").argument("<pattern>", "Search pattern").argument("[path]", "Search scope path", "/").action(async (pattern, path) => {
804
791
  try {
792
+ const jsonMode = program2.opts().json === true;
805
793
  const client = await createClient();
806
794
  const result = await client.execute(`grep ${pattern} ${path}`);
807
- output(result);
795
+ console.log(jsonMode ? formatJson(result) : formatGrep(result));
808
796
  } catch (err) {
809
797
  handleError(err, path);
810
798
  }
@@ -813,7 +801,7 @@ function registerFsCommands(program2) {
813
801
  var init_fs = __esm({
814
802
  "src/commands/fs.ts"() {
815
803
  init_client();
816
- init_provider_map();
804
+ init_format();
817
805
  }
818
806
  });
819
807
 
@@ -823,13 +811,13 @@ __export(keys_exports, {
823
811
  registerKeysCommand: () => registerKeysCommand
824
812
  });
825
813
  async function loadKeyStore() {
826
- const { readFileSync: readFileSync4 } = await import('fs');
814
+ const { readFileSync: readFileSync3 } = await import('fs');
827
815
  const { join: join5 } = await import('path');
828
816
  const { homedir: homedir2 } = await import('os');
829
817
  const dirxHome = process.env.DIRX_HOME ?? join5(homedir2(), ".dirx");
830
818
  const filePath = join5(dirxHome, "keys.json");
831
819
  try {
832
- const text = readFileSync4(filePath, "utf-8");
820
+ const text = readFileSync3(filePath, "utf-8");
833
821
  return JSON.parse(text);
834
822
  } catch {
835
823
  return { keys: {} };
@@ -880,7 +868,7 @@ function registerKeysCommand(program2) {
880
868
  try {
881
869
  const client = await createClient();
882
870
  const result = await client.listByok();
883
- console.log(JSON.stringify(result, null, 2));
871
+ console.log(JSON.stringify(result));
884
872
  } catch (err) {
885
873
  const msg = err instanceof Error ? err.message : String(err);
886
874
  console.error(`Error: ${msg}`);
@@ -936,7 +924,7 @@ try {
936
924
  } catch {
937
925
  }
938
926
  var program = new Command();
939
- program.name("dirx").description("DirX \u2014 Unified Gateway & CLI for Agents").version(version);
927
+ program.name("dirx").description("DirX \u2014 Unified Gateway & CLI for Agents").version(version).option("--json", "Output raw JSON instead of human-readable text");
940
928
  program.command("auth").description("Authenticate with the DirX gateway").option("--gateway-url <url>", "Gateway URL (default: https://api.dirx.ai)").action(async (opts) => {
941
929
  const { runAuth: runAuth2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
942
930
  await runAuth2({ gatewayUrl: opts.gatewayUrl });
@@ -982,4 +970,8 @@ var { registerFsCommands: registerFsCommands2 } = await Promise.resolve().then((
982
970
  registerFsCommands2(program);
983
971
  var { registerKeysCommand: registerKeysCommand2 } = await Promise.resolve().then(() => (init_keys(), keys_exports));
984
972
  registerKeysCommand2(program);
973
+ if (process.argv.length <= 2) {
974
+ program.outputHelp();
975
+ process.exit(0);
976
+ }
985
977
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dirxai/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "DirX — Unified Gateway & CLI for Agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,22 +9,16 @@
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsup",
12
- "dev": "tsup --watch",
13
- "lint": "tsc --noEmit",
14
- "test": "vitest run"
12
+ "dev": "tsup --watch"
15
13
  },
16
14
  "dependencies": {
15
+ "@dirxai/core": "^0.3.1",
17
16
  "commander": "^13.0.0"
18
17
  },
19
- "devDependencies": {
20
- "@types/node": "^22.0.0",
21
- "tsup": "^8.0.0",
22
- "typescript": "^5.7.0",
23
- "vitest": "^4.0.0"
24
- },
25
18
  "repository": {
26
19
  "type": "git",
27
- "url": "https://github.com/dirxai/dirx.git"
20
+ "url": "https://github.com/dirxai/dirx.git",
21
+ "directory": "packages/cli"
28
22
  },
29
23
  "homepage": "https://dirx.ai",
30
24
  "keywords": [
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 DirX AI
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/README.md DELETED
@@ -1,91 +0,0 @@
1
- # DirX — Unified Gateway & CLI for Agents
2
-
3
- [![npm version](https://img.shields.io/npm/v/@dirxai/cli.svg)](https://www.npmjs.com/package/@dirxai/cli)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
-
6
- DirX gives AI agents a unified, file-system-like interface to discover and interact with internet APIs — with built-in governance, access control, and auditing.
7
-
8
- ## Install
9
-
10
- ```bash
11
- npm install -g @dirxai/cli
12
- ```
13
-
14
- ## Quick Start
15
-
16
- ```bash
17
- # Authenticate with the gateway
18
- dirx auth
19
-
20
- # Browse the API directory
21
- dirx ls /
22
- dirx ls /net/
23
-
24
- # Read service descriptions
25
- dirx cat /net/api.github.com/DIR.md
26
-
27
- # Search across services
28
- dirx grep "weather" /net/
29
-
30
- # Write data
31
- dirx write /net/api.example.com/data -d '{"key": "value"}'
32
-
33
- # Manage API keys (BYOK)
34
- dirx keys set api.github.com --token ghp_xxxx --sync
35
- ```
36
-
37
- ## Commands
38
-
39
- ### Agent Commands
40
-
41
- | Command | Description |
42
- |---------|-------------|
43
- | `dirx ls <path>` | List directory contents |
44
- | `dirx cat <path>` | Read file contents |
45
- | `dirx write <path>` | Write data |
46
- | `dirx edit <path>` | Partial update |
47
- | `dirx rm <path>` | Remove a resource |
48
- | `dirx grep <pattern> <path>` | Search across services |
49
- | `dirx bash <pipeline>` | Execute multi-step pipeline |
50
-
51
- ### Developer Tools
52
-
53
- | Command | Description |
54
- |---------|-------------|
55
- | `dirx init [dir]` | Initialize DirX in a project |
56
- | `dirx generate [dir]` | Scan and detect route definitions |
57
- | `dirx generate --register` | Scan and auto-register with the gateway |
58
- | `dirx claim <domain>` | Claim domain via DNS verification |
59
- | `dirx register` | Register DIR.md with the gateway |
60
-
61
- ### Configuration
62
-
63
- | Command | Description |
64
- |---------|-------------|
65
- | `dirx auth` | Authenticate with the gateway |
66
- | `dirx keys set <domain>` | Set an API key |
67
- | `dirx keys list` | List stored keys |
68
- | `dirx keys remove <domain>` | Remove a key |
69
- | `dirx status` | Show CLI config and auth status |
70
-
71
- ## Environment Variables
72
-
73
- | Variable | Description | Default |
74
- |----------|-------------|---------|
75
- | `DIRX_GATEWAY_URL` | Gateway URL | `https://api.dirx.ai` |
76
- | `DIRX_TOKEN` | Agent token | — |
77
- | `DIRX_HOME` | Config directory | `~/.dirx` |
78
-
79
- ## Development
80
-
81
- ```bash
82
- git clone https://github.com/dirxai/dirx.git
83
- cd dirx
84
- npm install
85
- npm run build
86
- npm run lint
87
- ```
88
-
89
- ## License
90
-
91
- MIT