@dirxai/cli 0.1.0 → 0.2.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
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 CHANGED
@@ -1,101 +1,91 @@
1
1
  # DirX — Unified Gateway & CLI for Agents
2
2
 
3
- DirX provides a single CLI that lets AI agents **discover**, **navigate**, and **execute** across any API — GitHub, OpenAI, custom services, and more.
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)
4
5
 
5
- ```
6
- dirx ls / # discover registered services
7
- dirx cat /net/api.github.com # read service info
8
- dirx grep "issues" api.github.com # search endpoints
9
- dirx write /net/api.github.com/owner/repo/issues --json '{"title":"Bug"}'
10
- dirx bash 'cat /net | cat /net/api.github.com'
11
- ```
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.
12
7
 
13
8
  ## Install
14
9
 
15
- ### npm (recommended)
16
-
17
10
  ```bash
18
11
  npm install -g @dirxai/cli
19
12
  ```
20
13
 
21
- ### Homebrew (macOS/Linux)
14
+ ## Quick Start
22
15
 
23
16
  ```bash
24
- brew tap dirxai/dirx
25
- brew install dirx
26
- ```
17
+ # Authenticate with the gateway
18
+ dirx auth
27
19
 
28
- ### Binary download
20
+ # Browse the API directory
21
+ dirx ls /
22
+ dirx ls /net/
29
23
 
30
- Download from [Releases](https://github.com/dirxai/dirx/releases) and add to your PATH.
24
+ # Read service descriptions
25
+ dirx cat /net/api.github.com/DIR.md
31
26
 
32
- | Platform | Archive |
33
- |----------|---------|
34
- | macOS ARM64 | `dirx-aarch64-apple-darwin.tar.gz` |
35
- | macOS x86_64 | `dirx-x86_64-apple-darwin.tar.gz` |
36
- | Linux x86_64 | `dirx-x86_64-unknown-linux-gnu.tar.gz` |
27
+ # Search across services
28
+ dirx grep "weather" /net/
37
29
 
38
- Install:
30
+ # Write data
31
+ dirx write /net/api.example.com/data -d '{"key": "value"}'
39
32
 
40
- ```bash
41
- tar xzf dirx-*.tar.gz
42
- sudo mv dirx /usr/local/bin/
33
+ # Manage API keys (BYOK)
34
+ dirx keys set api.github.com --token ghp_xxxx --sync
43
35
  ```
44
36
 
45
- ## Quick Start
46
-
47
- ```bash
48
- # 1. Authenticate
49
- dirx auth --server https://api.dirx.ai --json '{}'
37
+ ## Commands
50
38
 
51
- # 2. Browse
52
- dirx ls /
53
- dirx ls /net
39
+ ### Agent Commands
54
40
 
55
- # 3. Read
56
- dirx cat /net/api.github.com
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 |
57
50
 
58
- # 4. Search
59
- dirx grep "pull requests"
51
+ ### Developer Tools
60
52
 
61
- # 5. Manage API keys (BYOK)
62
- dirx keys set api.github.com --token ghp_your_token
63
- dirx keys list
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 |
64
60
 
65
- # 6. Pipeline
66
- dirx bash 'cat /net/api.github.com | write /log'
67
- ```
61
+ ### Configuration
68
62
 
69
- ## For Service Developers
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
70
 
71
- Register your API with DirX:
71
+ ## Environment Variables
72
72
 
73
- ```bash
74
- dirx init my-service/ # generate DIR.md + dir.json
75
- dirx claim api.example.com # start domain verification
76
- dirx claim api.example.com --verify # complete verification
77
- dirx register --domain api.example.com # publish to registry
78
- ```
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` |
79
78
 
80
- ## Architecture
79
+ ## Development
81
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
82
87
  ```
83
- CLI (Rust) ──HTTP──▶ DirX Server (Go) ──▶ Adapters ──▶ External APIs
84
- │ Registry │ GitHub
85
- │ Router │ OpenAI
86
- │ Auth (JWKS) │ Custom...
87
- │ Policy
88
- │ Audit
89
- │ BYOK
90
- │ Search
91
- ```
92
-
93
- ## Documentation
94
-
95
- - [dirx.ai](https://dirx.ai) — official site
96
- - [API Reference](https://dirx.ai/docs/api)
97
- - [Developer Guide](https://dirx.ai/docs/developers)
98
88
 
99
89
  ## License
100
90
 
101
- MIT
91
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,985 @@
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';
4
+ import { homedir } from 'os';
5
+ import { readFile } from 'fs/promises';
6
+ import { fileURLToPath } from 'url';
7
+ import { Command } from 'commander';
8
+
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+
19
+ // src/credentials.ts
20
+ var credentials_exports = {};
21
+ __export(credentials_exports, {
22
+ getAgentToken: () => getAgentToken,
23
+ getToken: () => getToken,
24
+ saveAgentToken: () => saveAgentToken,
25
+ saveToken: () => saveToken
26
+ });
27
+ function credentialsPath() {
28
+ const dirxHome = process.env.DIRX_HOME ?? join(homedir(), ".dirx");
29
+ return join(dirxHome, "credentials.json");
30
+ }
31
+ function loadStore() {
32
+ try {
33
+ const text = readFileSync(credentialsPath(), "utf-8");
34
+ return JSON.parse(text);
35
+ } catch {
36
+ return {};
37
+ }
38
+ }
39
+ function saveStore(store) {
40
+ const filePath = credentialsPath();
41
+ const dir = join(filePath, "..");
42
+ mkdirSync(dir, { recursive: true });
43
+ writeFileSync(filePath, JSON.stringify(store, null, 2));
44
+ try {
45
+ chmodSync(filePath, 384);
46
+ } catch {
47
+ }
48
+ }
49
+ function decodeJwtPayload(token) {
50
+ try {
51
+ const parts = token.split(".");
52
+ if (parts.length !== 3) return {};
53
+ const payload = JSON.parse(
54
+ Buffer.from(parts[1], "base64url").toString("utf-8")
55
+ );
56
+ return { iat: payload.iat, exp: payload.exp };
57
+ } catch {
58
+ return {};
59
+ }
60
+ }
61
+ async function saveAgentToken(gatewayUrl, token) {
62
+ const store = loadStore();
63
+ const { iat, exp } = decodeJwtPayload(token);
64
+ store.agentToken = {
65
+ gatewayUrl,
66
+ token,
67
+ issuedAt: iat,
68
+ expiresAt: exp
69
+ };
70
+ saveStore(store);
71
+ }
72
+ async function getAgentToken() {
73
+ const store = loadStore();
74
+ if (!store.agentToken) return null;
75
+ if (store.agentToken.expiresAt) {
76
+ const now = Math.floor(Date.now() / 1e3);
77
+ if (now >= store.agentToken.expiresAt) return null;
78
+ }
79
+ return store.agentToken;
80
+ }
81
+ async function saveToken(domain, token) {
82
+ const store = loadStore();
83
+ const { iat, exp } = decodeJwtPayload(token);
84
+ if (!store.tokens) store.tokens = {};
85
+ store.tokens[domain] = { token, issuedAt: iat, expiresAt: exp };
86
+ saveStore(store);
87
+ }
88
+ async function getToken(domain) {
89
+ const store = loadStore();
90
+ const entry = store.tokens?.[domain];
91
+ if (!entry) return null;
92
+ if (entry.expiresAt) {
93
+ const now = Math.floor(Date.now() / 1e3);
94
+ if (now >= entry.expiresAt) return null;
95
+ }
96
+ return entry.token;
97
+ }
98
+ var init_credentials = __esm({
99
+ "src/credentials.ts"() {
100
+ }
101
+ });
102
+
103
+ // src/commands/auth.ts
104
+ var auth_exports = {};
105
+ __export(auth_exports, {
106
+ runAuth: () => runAuth
107
+ });
108
+ async function runAuth(opts) {
109
+ const gatewayUrl = opts.gatewayUrl ?? process.env.DIRX_GATEWAY_URL ?? "https://api.dirx.ai";
110
+ const sub = `agent-${Date.now()}`;
111
+ const res = await fetch(`${gatewayUrl.replace(/\/$/, "")}/auth/token`, {
112
+ method: "POST",
113
+ headers: { "Content-Type": "application/json" },
114
+ body: JSON.stringify({
115
+ sub,
116
+ svc: "gateway",
117
+ roles: ["agent"],
118
+ expiresIn: "24h"
119
+ })
120
+ });
121
+ if (!res.ok) {
122
+ const body = await res.text();
123
+ throw new Error(`Auth failed (${res.status}): ${body}`);
124
+ }
125
+ const { token } = await res.json();
126
+ await saveAgentToken(gatewayUrl, token);
127
+ console.log("Authenticated with gateway");
128
+ console.log(` Token saved to ~/.dirx/credentials.json`);
129
+ console.log(` Gateway: ${gatewayUrl}`);
130
+ }
131
+ var init_auth = __esm({
132
+ "src/commands/auth.ts"() {
133
+ init_credentials();
134
+ }
135
+ });
136
+
137
+ // src/commands/init.ts
138
+ var init_exports = {};
139
+ __export(init_exports, {
140
+ runInit: () => runInit
141
+ });
142
+ async function runInit(dir) {
143
+ const target = dir === "." ? process.cwd() : dir;
144
+ const dirMdPath = join(target, "DIR.md");
145
+ const dirJsonPath = join(target, "dir.json");
146
+ if (existsSync(dirMdPath)) {
147
+ console.log(`Already initialized: ${dirMdPath} exists`);
148
+ return;
149
+ }
150
+ const { lang, framework } = detectFramework(target);
151
+ mkdirSync(target, { recursive: true });
152
+ const mdContent = [
153
+ "# My Service",
154
+ "",
155
+ "Describe your service here.",
156
+ "",
157
+ "## Stack",
158
+ "",
159
+ `- Language: ${lang}`,
160
+ `- Framework: ${framework}`,
161
+ "",
162
+ "## Endpoints",
163
+ "",
164
+ "- `GET /` \u2014 health check",
165
+ ""
166
+ ].join("\n");
167
+ const jsonContent = {
168
+ title: "My Service",
169
+ description: "A DirX-compatible service",
170
+ base_url: "https://api.example.com",
171
+ actions: ["ls", "read"],
172
+ endpoints: [
173
+ { path: "/", method: "GET", description: "Health check" }
174
+ ]
175
+ };
176
+ writeFileSync(dirMdPath, mdContent);
177
+ writeFileSync(dirJsonPath, JSON.stringify(jsonContent, null, 2));
178
+ console.log(`Initialized DirX project in ${target}`);
179
+ console.log(` Detected: ${lang} / ${framework}`);
180
+ console.log(` Created: DIR.md, dir.json`);
181
+ console.log(`
182
+ Next steps:`);
183
+ console.log(` 1. Edit DIR.md to describe your service`);
184
+ console.log(` 2. Run 'dirx generate' to scan routes`);
185
+ console.log(` 3. Run 'dirx register --domain <your-domain>' to publish`);
186
+ }
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
+ var init_init = __esm({
206
+ "src/commands/init.ts"() {
207
+ }
208
+ });
209
+
210
+ // src/commands/register.ts
211
+ var register_exports = {};
212
+ __export(register_exports, {
213
+ runRegister: () => runRegister
214
+ });
215
+ async function runRegister(options) {
216
+ const projectDir = options.dir ?? process.cwd();
217
+ const gatewayUrl = options.gatewayUrl ?? process.env.DIRX_GATEWAY_URL ?? "https://api.dirx.ai";
218
+ const domain = options.domain ?? process.env.DIRX_DOMAIN;
219
+ if (!domain) {
220
+ throw new Error(
221
+ "Domain is required. Use --domain or DIRX_DOMAIN env var."
222
+ );
223
+ }
224
+ const token = await resolveToken({
225
+ token: options.token,
226
+ domain,
227
+ gatewayUrl
228
+ });
229
+ const dirMdPath = join(projectDir, "DIR.md");
230
+ let dirMd;
231
+ try {
232
+ dirMd = await readFile(dirMdPath, "utf-8");
233
+ } catch {
234
+ throw new Error(
235
+ `DIR.md not found at ${dirMdPath}. Run 'dirx init' first.`
236
+ );
237
+ }
238
+ if (!dirMd.trim()) {
239
+ throw new Error(`DIR.md is empty at ${dirMdPath}`);
240
+ }
241
+ const baseUrl = gatewayUrl.replace(/\/$/, "");
242
+ const url = `${baseUrl}/registry/services?domain=${encodeURIComponent(domain)}`;
243
+ const res = await fetch(url, {
244
+ method: "POST",
245
+ headers: {
246
+ Authorization: `Bearer ${token}`,
247
+ "Content-Type": "text/markdown"
248
+ },
249
+ body: dirMd
250
+ });
251
+ if (!res.ok) {
252
+ const body = await res.text();
253
+ throw new Error(`Registration failed (${res.status}): ${body}`);
254
+ }
255
+ const result = await res.json();
256
+ console.log(`Registered ${result.name} as ${result.domain}`);
257
+ }
258
+ async function resolveToken(options) {
259
+ if (options.token) return options.token;
260
+ if (process.env.DIRX_PUBLISH_TOKEN) return process.env.DIRX_PUBLISH_TOKEN;
261
+ if (options.domain) {
262
+ const stored = await getToken(options.domain);
263
+ if (stored) return stored;
264
+ const gw = options.gatewayUrl ?? process.env.DIRX_GATEWAY_URL;
265
+ if (gw) {
266
+ const renewed = await renewToken(gw, options.domain);
267
+ if (renewed) return renewed;
268
+ }
269
+ }
270
+ throw new Error(
271
+ "No auth token found. Use `dirx claim <domain>` to obtain a publish token, or provide --token."
272
+ );
273
+ }
274
+ async function renewToken(gatewayUrl, domain) {
275
+ const baseUrl = gatewayUrl.replace(/\/$/, "");
276
+ try {
277
+ const res = await fetch(`${baseUrl}/domains/verify`, {
278
+ method: "POST",
279
+ headers: { "Content-Type": "application/json" },
280
+ body: JSON.stringify({ domain })
281
+ });
282
+ if (!res.ok) return null;
283
+ const data = await res.json();
284
+ if (!data.publishToken) return null;
285
+ await saveToken(domain, data.publishToken);
286
+ console.log(`Token renewed for ${domain}`);
287
+ return data.publishToken;
288
+ } catch {
289
+ return null;
290
+ }
291
+ }
292
+ var init_register = __esm({
293
+ "src/commands/register.ts"() {
294
+ init_credentials();
295
+ }
296
+ });
297
+
298
+ // src/commands/generate.ts
299
+ var generate_exports = {};
300
+ __export(generate_exports, {
301
+ runGenerate: () => runGenerate
302
+ });
303
+ async function runGenerate(dir, opts) {
304
+ const target = dir === "." ? process.cwd() : dir;
305
+ const dirJsonPath = join(target, "dir.json");
306
+ if (!existsSync(dirJsonPath)) {
307
+ console.error(
308
+ `dir.json not found in ${target}. Run 'dirx init' first.`
309
+ );
310
+ process.exitCode = 1;
311
+ return;
312
+ }
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
+ }
319
+ console.log(`Scanned ${target} (${framework})`);
320
+ if (routes.length > 0) {
321
+ console.log(`
322
+ Found ${routes.length} file(s) with route definitions:
323
+ `);
324
+ for (const r of routes) {
325
+ console.log(` ${r.file} (${r.framework})`);
326
+ }
327
+ } else {
328
+ console.log(`
329
+ No route definitions detected.`);
330
+ }
331
+ console.log(`
332
+ Edit DIR.md and dir.json to describe your API.`);
333
+ if (opts?.register) {
334
+ console.log(`
335
+ Registering with gateway...`);
336
+ const { runRegister: runRegister2 } = await Promise.resolve().then(() => (init_register(), register_exports));
337
+ await runRegister2({
338
+ gatewayUrl: opts.gatewayUrl,
339
+ token: opts.token,
340
+ domain: opts.domain,
341
+ dir: target
342
+ });
343
+ }
344
+ }
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
+ var init_generate = __esm({
424
+ "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
+ }
446
+ });
447
+
448
+ // src/commands/claim.ts
449
+ var claim_exports = {};
450
+ __export(claim_exports, {
451
+ runClaim: () => runClaim
452
+ });
453
+ async function runClaim(options) {
454
+ const { gatewayUrl, domain, verify } = options;
455
+ const baseUrl = gatewayUrl.replace(/\/$/, "");
456
+ if (verify) {
457
+ await verifyClaim(baseUrl, domain);
458
+ } else {
459
+ await requestChallenge(baseUrl, domain);
460
+ }
461
+ }
462
+ async function requestChallenge(baseUrl, domain) {
463
+ const res = await fetch(`${baseUrl}/domains/challenge`, {
464
+ method: "POST",
465
+ headers: { "Content-Type": "application/json" },
466
+ body: JSON.stringify({ domain })
467
+ });
468
+ if (!res.ok) {
469
+ const body = await res.text();
470
+ throw new Error(`Challenge request failed (${res.status}): ${body}`);
471
+ }
472
+ const data = await res.json();
473
+ const expires = new Date(data.expiresAt).toISOString();
474
+ console.log(`
475
+ Domain claim initiated for ${data.domain}
476
+ `);
477
+ console.log(`Add the following DNS TXT record:
478
+ `);
479
+ console.log(` Name: ${data.txtRecord}`);
480
+ console.log(` Value: ${data.txtValue}
481
+ `);
482
+ console.log(`This challenge expires at ${expires}.
483
+ `);
484
+ console.log(`After adding the record, verify with:`);
485
+ console.log(` dirx claim ${domain} --verify
486
+ `);
487
+ console.log(`Tip: You can check propagation with:`);
488
+ console.log(` dig TXT ${data.txtRecord}`);
489
+ }
490
+ async function verifyClaim(baseUrl, domain) {
491
+ const res = await fetch(`${baseUrl}/domains/verify`, {
492
+ method: "POST",
493
+ headers: { "Content-Type": "application/json" },
494
+ body: JSON.stringify({ domain })
495
+ });
496
+ const data = await res.json();
497
+ if (!res.ok) {
498
+ const error = data.error || "Verification failed";
499
+ console.error(`
500
+ Verification failed: ${error}
501
+ `);
502
+ console.log(`Troubleshooting:`);
503
+ console.log(
504
+ ` 1. Check the record exists: dig TXT _dirx.${domain}`
505
+ );
506
+ console.log(` 2. DNS propagation can take up to 5 minutes`);
507
+ console.log(
508
+ ` 3. Ensure the TXT value matches exactly (including "dirx-verify=" prefix)
509
+ `
510
+ );
511
+ process.exitCode = 1;
512
+ return;
513
+ }
514
+ const publishToken = data.publishToken;
515
+ await saveToken(domain, publishToken);
516
+ console.log(`
517
+ Domain ${domain} verified successfully!`);
518
+ console.log(`Publish token saved to ~/.dirx/credentials.json
519
+ `);
520
+ console.log(`You can now register services:`);
521
+ console.log(` dirx register --domain ${domain}`);
522
+ }
523
+ var init_claim = __esm({
524
+ "src/commands/claim.ts"() {
525
+ init_credentials();
526
+ }
527
+ });
528
+
529
+ // src/commands/status.ts
530
+ var status_exports = {};
531
+ __export(status_exports, {
532
+ runStatus: () => runStatus
533
+ });
534
+ async function runStatus() {
535
+ const gatewayUrl = process.env.DIRX_GATEWAY_URL ?? "https://api.dirx.ai";
536
+ console.log(`DirX CLI Status
537
+ `);
538
+ console.log(` Gateway: ${gatewayUrl}`);
539
+ const stored = await getAgentToken();
540
+ if (stored) {
541
+ const hint = stored.token.length > 8 ? `${stored.token.slice(0, 4)}...${stored.token.slice(-4)}` : "********";
542
+ console.log(` Token: ${hint}`);
543
+ console.log(` Server: ${stored.gatewayUrl}`);
544
+ if (stored.expiresAt) {
545
+ const expDate = new Date(stored.expiresAt * 1e3);
546
+ const now = /* @__PURE__ */ new Date();
547
+ if (expDate > now) {
548
+ console.log(` Expires: ${expDate.toISOString()}`);
549
+ } else {
550
+ console.log(` Expires: EXPIRED (${expDate.toISOString()})`);
551
+ }
552
+ }
553
+ } else {
554
+ console.log(` Token: not set (run 'dirx auth')`);
555
+ }
556
+ }
557
+ var init_status = __esm({
558
+ "src/commands/status.ts"() {
559
+ init_credentials();
560
+ }
561
+ });
562
+
563
+ // src/gateway/client.ts
564
+ async function createClient() {
565
+ const { getAgentToken: getAgentToken2 } = await Promise.resolve().then(() => (init_credentials(), credentials_exports));
566
+ const stored = await getAgentToken2();
567
+ const gatewayUrl = process.env.DIRX_GATEWAY_URL ?? stored?.gatewayUrl ?? "https://api.dirx.ai";
568
+ const token = process.env.DIRX_TOKEN ?? stored?.token ?? null;
569
+ if (!token) {
570
+ throw new Error(
571
+ "No token found. Run 'dirx auth' first, or set DIRX_TOKEN."
572
+ );
573
+ }
574
+ return new GatewayClient(gatewayUrl, token);
575
+ }
576
+ var GatewayClient;
577
+ var init_client = __esm({
578
+ "src/gateway/client.ts"() {
579
+ GatewayClient = class {
580
+ constructor(gatewayUrl, token) {
581
+ this.gatewayUrl = gatewayUrl;
582
+ this.token = token;
583
+ }
584
+ baseUrl() {
585
+ return this.gatewayUrl.replace(/\/$/, "");
586
+ }
587
+ /** Send a command string to the gateway /execute endpoint. */
588
+ async execute(command) {
589
+ const url = `${this.baseUrl()}/execute`;
590
+ const res = await fetch(url, {
591
+ method: "POST",
592
+ headers: {
593
+ Authorization: `Bearer ${this.token}`,
594
+ "Content-Type": "application/json"
595
+ },
596
+ body: JSON.stringify({ command })
597
+ });
598
+ if (!res.ok) {
599
+ const body = await res.text();
600
+ throw new Error(`Gateway error ${res.status}: ${body}`);
601
+ }
602
+ return res.json();
603
+ }
604
+ // --- BYOK methods ---
605
+ async uploadByok(domain, auth) {
606
+ const url = `${this.baseUrl()}/byok/${encodeURIComponent(domain)}`;
607
+ const res = await fetch(url, {
608
+ method: "PUT",
609
+ headers: {
610
+ Authorization: `Bearer ${this.token}`,
611
+ "Content-Type": "application/json"
612
+ },
613
+ body: JSON.stringify({ auth })
614
+ });
615
+ if (!res.ok) {
616
+ const body = await res.text();
617
+ throw new Error(`BYOK upload failed (${res.status}): ${body}`);
618
+ }
619
+ }
620
+ async listByok() {
621
+ const url = `${this.baseUrl()}/byok`;
622
+ const res = await fetch(url, {
623
+ headers: { Authorization: `Bearer ${this.token}` }
624
+ });
625
+ if (!res.ok) {
626
+ const body = await res.text();
627
+ throw new Error(`BYOK list failed (${res.status}): ${body}`);
628
+ }
629
+ return res.json();
630
+ }
631
+ async deleteByok(domain) {
632
+ const url = `${this.baseUrl()}/byok/${encodeURIComponent(domain)}`;
633
+ const res = await fetch(url, {
634
+ method: "DELETE",
635
+ headers: { Authorization: `Bearer ${this.token}` }
636
+ });
637
+ if (!res.ok) {
638
+ const body = await res.text();
639
+ throw new Error(`BYOK delete failed (${res.status}): ${body}`);
640
+ }
641
+ }
642
+ };
643
+ }
644
+ });
645
+
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"
673
+ }
674
+ };
675
+ }
676
+ });
677
+
678
+ // src/commands/fs.ts
679
+ var fs_exports = {};
680
+ __export(fs_exports, {
681
+ registerFsCommands: () => registerFsCommands
682
+ });
683
+ function output(result) {
684
+ console.log(JSON.stringify(result, null, 2));
685
+ }
686
+ function extractDomain(path) {
687
+ const cleaned = path.replace(/^\/+/, "");
688
+ const segments = cleaned.split("/");
689
+ const candidate = segments.length > 1 ? segments[1] : segments[0];
690
+ if (!candidate) return null;
691
+ return candidate.includes(".") ? candidate : null;
692
+ }
693
+ function isAuthError(message) {
694
+ if (/Gateway error (401|403):/.test(message)) return true;
695
+ if (/Gateway error 500:/.test(message) && /\b(Unauthorized|Unauthenticated|authenticate|API key|api[_-]?key)\b/i.test(
696
+ message
697
+ ))
698
+ return true;
699
+ return false;
700
+ }
701
+ function handleError(err, cmdPath) {
702
+ const message = err instanceof Error ? err.message : String(err);
703
+ if (isAuthError(message)) {
704
+ const domain = cmdPath && extractDomain(cmdPath) || message.match(/([a-z0-9-]+(?:\.[a-z0-9-]+){1,})/i)?.[1] || null;
705
+ if (domain) {
706
+ const hint = getAuthHint(domain);
707
+ console.error(`Error: Authentication required for ${domain}`);
708
+ if (hint?.guideUrl) {
709
+ console.error(` Get your key: ${hint.guideUrl}`);
710
+ } else {
711
+ console.error(
712
+ ` Get your key: https://${domain} (check the developer/API settings)`
713
+ );
714
+ }
715
+ console.error(
716
+ ` Set your key: dirx keys set ${domain} --token <YOUR_KEY> --sync`
717
+ );
718
+ console.error(` Then retry your command.`);
719
+ process.exit(1);
720
+ }
721
+ }
722
+ console.error(JSON.stringify({ error: message }));
723
+ process.exit(1);
724
+ }
725
+ 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) => {
727
+ try {
728
+ const client = await createClient();
729
+ const result = await client.execute(`ls ${path}`);
730
+ output(result);
731
+ } catch (err) {
732
+ handleError(err, path);
733
+ }
734
+ });
735
+ program2.command("cat").description("Read file contents from a DirX path").argument("<path>", "File path").action(async (path) => {
736
+ try {
737
+ const client = await createClient();
738
+ const result = await client.execute(`cat ${path}`);
739
+ output(result);
740
+ } catch (err) {
741
+ handleError(err, path);
742
+ }
743
+ });
744
+ program2.command("read").description("Read file contents (alias for cat)").argument("<path>", "File path").action(async (path) => {
745
+ try {
746
+ const client = await createClient();
747
+ const result = await client.execute(`cat ${path}`);
748
+ output(result);
749
+ } catch (err) {
750
+ handleError(err, path);
751
+ }
752
+ });
753
+ program2.command("write").description("Write data to a DirX path").argument("<path>", "File path").option("-d, --data <json>", "JSON data to write").option("-f, --file <file>", "Read data from file").action(
754
+ async (path, opts) => {
755
+ try {
756
+ let payload = opts.data ?? "";
757
+ if (opts.file) {
758
+ const { readFileSync: readFileSync4 } = await import('fs');
759
+ payload = readFileSync4(opts.file, "utf-8");
760
+ }
761
+ const client = await createClient();
762
+ const result = await client.execute(`write ${path} ${payload}`);
763
+ output(result);
764
+ } catch (err) {
765
+ handleError(err, path);
766
+ }
767
+ }
768
+ );
769
+ program2.command("edit").description("Edit (partial update) data at a DirX path").argument("<path>", "File path").option("-d, --data <json>", "JSON data for partial update").option("-f, --file <file>", "Read data from file").action(
770
+ async (path, opts) => {
771
+ try {
772
+ let payload = opts.data ?? "";
773
+ if (opts.file) {
774
+ const { readFileSync: readFileSync4 } = await import('fs');
775
+ payload = readFileSync4(opts.file, "utf-8");
776
+ }
777
+ const client = await createClient();
778
+ const result = await client.execute(`edit ${path} ${payload}`);
779
+ output(result);
780
+ } catch (err) {
781
+ handleError(err, path);
782
+ }
783
+ }
784
+ );
785
+ program2.command("rm").description("Remove a resource at a DirX path").argument("<path>", "Resource path").action(async (path) => {
786
+ try {
787
+ const client = await createClient();
788
+ const result = await client.execute(`rm ${path}`);
789
+ output(result);
790
+ } catch (err) {
791
+ handleError(err, path);
792
+ }
793
+ });
794
+ program2.command("bash").description("Execute a multi-step pipeline on the gateway").argument("<pipeline>", "Pipeline expression").action(async (pipeline) => {
795
+ try {
796
+ const client = await createClient();
797
+ const result = await client.execute(`bash ${pipeline}`);
798
+ output(result);
799
+ } catch (err) {
800
+ handleError(err);
801
+ }
802
+ });
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) => {
804
+ try {
805
+ const client = await createClient();
806
+ const result = await client.execute(`grep ${pattern} ${path}`);
807
+ output(result);
808
+ } catch (err) {
809
+ handleError(err, path);
810
+ }
811
+ });
812
+ }
813
+ var init_fs = __esm({
814
+ "src/commands/fs.ts"() {
815
+ init_client();
816
+ init_provider_map();
817
+ }
818
+ });
819
+
820
+ // src/commands/keys.ts
821
+ var keys_exports = {};
822
+ __export(keys_exports, {
823
+ registerKeysCommand: () => registerKeysCommand
824
+ });
825
+ async function loadKeyStore() {
826
+ const { readFileSync: readFileSync4 } = await import('fs');
827
+ const { join: join5 } = await import('path');
828
+ const { homedir: homedir2 } = await import('os');
829
+ const dirxHome = process.env.DIRX_HOME ?? join5(homedir2(), ".dirx");
830
+ const filePath = join5(dirxHome, "keys.json");
831
+ try {
832
+ const text = readFileSync4(filePath, "utf-8");
833
+ return JSON.parse(text);
834
+ } catch {
835
+ return { keys: {} };
836
+ }
837
+ }
838
+ async function saveKeyStore(store) {
839
+ const { writeFileSync: writeFileSync3, mkdirSync: mkdirSync3, chmodSync: chmodSync2 } = await import('fs');
840
+ const { join: join5 } = await import('path');
841
+ const { homedir: homedir2 } = await import('os');
842
+ const dirxHome = process.env.DIRX_HOME ?? join5(homedir2(), ".dirx");
843
+ const filePath = join5(dirxHome, "keys.json");
844
+ mkdirSync3(dirxHome, { recursive: true });
845
+ writeFileSync3(filePath, JSON.stringify(store, null, 2));
846
+ try {
847
+ chmodSync2(filePath, 384);
848
+ } catch {
849
+ }
850
+ }
851
+ function registerKeysCommand(program2) {
852
+ const keys = program2.command("keys").description("Manage external API keys (BYOK)");
853
+ keys.command("set").description("Set an API key for a domain").argument("<domain>", "API domain (e.g. api.github.com)").requiredOption("--token <token>", "API token/key").option("--type <type>", "Auth type (bearer, header, query)", "bearer").option("--sync", "Also upload to the gateway").action(
854
+ async (domain, opts) => {
855
+ const store = await loadKeyStore();
856
+ store.keys[domain] = {
857
+ type: opts.type,
858
+ token: opts.token,
859
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
860
+ };
861
+ await saveKeyStore(store);
862
+ console.log(`Key saved for ${domain}`);
863
+ if (opts.sync) {
864
+ try {
865
+ const client = await createClient();
866
+ await client.uploadByok(domain, {
867
+ type: opts.type,
868
+ token: opts.token
869
+ });
870
+ console.log(`Key synced to gateway`);
871
+ } catch (err) {
872
+ const msg = err instanceof Error ? err.message : String(err);
873
+ console.error(`Sync failed: ${msg}`);
874
+ }
875
+ }
876
+ }
877
+ );
878
+ keys.command("list").description("List stored API keys").option("--remote", "List keys stored on the gateway").action(async (opts) => {
879
+ if (opts.remote) {
880
+ try {
881
+ const client = await createClient();
882
+ const result = await client.listByok();
883
+ console.log(JSON.stringify(result, null, 2));
884
+ } catch (err) {
885
+ const msg = err instanceof Error ? err.message : String(err);
886
+ console.error(`Error: ${msg}`);
887
+ process.exit(1);
888
+ }
889
+ } else {
890
+ const store = await loadKeyStore();
891
+ const domains = Object.keys(store.keys);
892
+ if (domains.length === 0) {
893
+ console.log("No keys stored locally.");
894
+ return;
895
+ }
896
+ for (const domain of domains) {
897
+ const entry = store.keys[domain];
898
+ const hint = entry.token ? `${entry.token.slice(0, 4)}...${entry.token.slice(-4)}` : "(set)";
899
+ console.log(` ${domain} ${entry.type} ${hint} ${entry.updatedAt}`);
900
+ }
901
+ }
902
+ });
903
+ keys.command("remove").description("Remove an API key for a domain").argument("<domain>", "API domain").option("--remote", "Also delete from the gateway").action(async (domain, opts) => {
904
+ const store = await loadKeyStore();
905
+ if (store.keys[domain]) {
906
+ delete store.keys[domain];
907
+ await saveKeyStore(store);
908
+ console.log(`Key removed for ${domain}`);
909
+ } else {
910
+ console.log(`No key found for ${domain}`);
911
+ }
912
+ if (opts.remote) {
913
+ try {
914
+ const client = await createClient();
915
+ await client.deleteByok(domain);
916
+ console.log(`Key deleted from gateway`);
917
+ } catch (err) {
918
+ const msg = err instanceof Error ? err.message : String(err);
919
+ console.error(`Remote delete failed: ${msg}`);
920
+ }
921
+ }
922
+ });
923
+ }
924
+ var init_keys = __esm({
925
+ "src/commands/keys.ts"() {
926
+ init_client();
927
+ }
928
+ });
929
+ var __dirname$1 = dirname(fileURLToPath(import.meta.url));
930
+ var version = "0.0.0";
931
+ try {
932
+ const pkg = JSON.parse(
933
+ readFileSync(resolve(__dirname$1, "../package.json"), "utf-8")
934
+ );
935
+ version = pkg.version;
936
+ } catch {
937
+ }
938
+ var program = new Command();
939
+ program.name("dirx").description("DirX \u2014 Unified Gateway & CLI for Agents").version(version);
940
+ program.command("auth").description("Authenticate with the DirX gateway").option("--gateway-url <url>", "Gateway URL (default: https://api.dirx.ai)").action(async (opts) => {
941
+ const { runAuth: runAuth2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
942
+ await runAuth2({ gatewayUrl: opts.gatewayUrl });
943
+ });
944
+ program.command("init").description("Initialize DirX in the current project").argument("[dir]", "Project directory", ".").action(async (dir) => {
945
+ const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
946
+ await runInit2(dir);
947
+ });
948
+ program.command("generate").description("Scan project and generate DIR.md, optionally register").argument("[dir]", "Project directory", ".").option("--register", "Register the service with the gateway after generating").option("--gateway-url <url>", "Gateway URL for registration").option("--token <token>", "Auth token for registration").option("--domain <domain>", "Domain name for the service").action(async (dir, opts) => {
949
+ const { runGenerate: runGenerate2 } = await Promise.resolve().then(() => (init_generate(), generate_exports));
950
+ await runGenerate2(dir, {
951
+ register: opts.register,
952
+ gatewayUrl: opts.gatewayUrl,
953
+ token: opts.token,
954
+ domain: opts.domain
955
+ });
956
+ });
957
+ program.command("claim").description("Claim domain ownership via DNS verification").argument("<domain>", "Domain to claim").option("--verify", "Verify DNS record and obtain publish token").option("--gateway-url <url>", "Gateway URL").action(
958
+ async (domain, opts) => {
959
+ const { runClaim: runClaim2 } = await Promise.resolve().then(() => (init_claim(), claim_exports));
960
+ const gatewayUrl = opts.gatewayUrl ?? process.env.DIRX_GATEWAY_URL ?? "https://api.dirx.ai";
961
+ await runClaim2({
962
+ gatewayUrl,
963
+ domain,
964
+ verify: opts.verify
965
+ });
966
+ }
967
+ );
968
+ program.command("register").description("Register DIR.md with the gateway").option("--gateway-url <url>", "Gateway URL").option("--token <token>", "Auth token (publish token)").option("--domain <domain>", "Domain name for the service").option("--dir <dir>", "Project directory", ".").action(async (opts) => {
969
+ const { runRegister: runRegister2 } = await Promise.resolve().then(() => (init_register(), register_exports));
970
+ await runRegister2({
971
+ gatewayUrl: opts.gatewayUrl,
972
+ token: opts.token,
973
+ domain: opts.domain,
974
+ dir: opts.dir === "." ? process.cwd() : opts.dir
975
+ });
976
+ });
977
+ program.command("status").description("Show current CLI configuration and auth status").action(async () => {
978
+ const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
979
+ await runStatus2();
980
+ });
981
+ var { registerFsCommands: registerFsCommands2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
982
+ registerFsCommands2(program);
983
+ var { registerKeysCommand: registerKeysCommand2 } = await Promise.resolve().then(() => (init_keys(), keys_exports));
984
+ registerKeysCommand2(program);
985
+ program.parse();
package/package.json CHANGED
@@ -1,40 +1,47 @@
1
1
  {
2
- "name": "@dirxai/cli",
3
- "version": "0.1.0",
4
- "description": "DirX — Unified Gateway & CLI for Agents",
5
- "license": "MIT",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/dirxai/dirx"
9
- },
10
- "homepage": "https://dirx.ai",
11
- "keywords": [
12
- "dirx",
13
- "agent",
14
- "cli",
15
- "gateway",
16
- "api"
17
- ],
18
- "bin": {
19
- "dirx": "bin/dirx"
20
- },
21
- "scripts": {
22
- "postinstall": "node install.js"
23
- },
24
- "files": [
25
- "bin/",
26
- "install.js",
27
- "README.md"
28
- ],
29
- "os": [
30
- "darwin",
31
- "linux"
32
- ],
33
- "cpu": [
34
- "x64",
35
- "arm64"
36
- ],
37
- "engines": {
38
- "node": ">=16"
39
- }
2
+ "name": "@dirxai/cli",
3
+ "version": "0.2.1",
4
+ "description": "DirX — Unified Gateway & CLI for Agents",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "dirx": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsup",
12
+ "dev": "tsup --watch",
13
+ "lint": "tsc --noEmit",
14
+ "test": "vitest run"
15
+ },
16
+ "dependencies": {
17
+ "commander": "^13.0.0"
18
+ },
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
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/dirxai/dirx.git"
28
+ },
29
+ "homepage": "https://dirx.ai",
30
+ "keywords": [
31
+ "dirx",
32
+ "agent",
33
+ "cli",
34
+ "gateway",
35
+ "api",
36
+ "mcp"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "engines": {
45
+ "node": ">=18"
46
+ }
40
47
  }
package/install.js DELETED
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const https = require("https");
4
- const http = require("http");
5
- const fs = require("fs");
6
- const os = require("os");
7
- const path = require("path");
8
- const { execSync } = require("child_process");
9
-
10
- const VERSION = require("./package.json").version;
11
- const REPO = "dirxai/dirx";
12
- const BIN_DIR = path.join(__dirname, "bin");
13
-
14
- const PLATFORM_MAP = {
15
- darwin: "apple-darwin",
16
- linux: "unknown-linux-gnu",
17
- };
18
-
19
- const ARCH_MAP = {
20
- x64: "x86_64",
21
- arm64: "aarch64",
22
- };
23
-
24
- function getArchiveName() {
25
- const platform = PLATFORM_MAP[process.platform];
26
- const arch = ARCH_MAP[process.arch];
27
- if (!platform || !arch) {
28
- throw new Error(
29
- `Unsupported platform: ${process.platform}-${process.arch}`
30
- );
31
- }
32
- return `dirx-${arch}-${platform}.tar.gz`;
33
- }
34
-
35
- function getDownloadUrl(archiveName) {
36
- return `https://github.com/${REPO}/releases/download/v${VERSION}/${archiveName}`;
37
- }
38
-
39
- function download(url) {
40
- return new Promise((resolve, reject) => {
41
- const client = url.startsWith("https") ? https : http;
42
- client.get(url, { headers: { "User-Agent": "dirx-npm-installer" } }, (res) => {
43
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
44
- return download(res.headers.location).then(resolve).catch(reject);
45
- }
46
- if (res.statusCode !== 200) {
47
- return reject(new Error(`Download failed: HTTP ${res.statusCode} from ${url}`));
48
- }
49
- const chunks = [];
50
- res.on("data", (chunk) => chunks.push(chunk));
51
- res.on("end", () => resolve(Buffer.concat(chunks)));
52
- res.on("error", reject);
53
- }).on("error", reject);
54
- });
55
- }
56
-
57
- async function main() {
58
- const archiveName = getArchiveName();
59
- const url = getDownloadUrl(archiveName);
60
- const archivePath = path.join(os.tmpdir(), archiveName);
61
- const binPath = path.join(BIN_DIR, "dirx");
62
-
63
- console.log(`Downloading DirX v${VERSION} for ${process.platform}-${process.arch}...`);
64
- console.log(` ${url}`);
65
-
66
- fs.mkdirSync(BIN_DIR, { recursive: true });
67
-
68
- try {
69
- const data = await download(url);
70
- fs.writeFileSync(archivePath, data);
71
-
72
- execSync(`tar xzf "${archivePath}" -C "${BIN_DIR}"`, { stdio: "inherit" });
73
- fs.chmodSync(binPath, 0o755);
74
- fs.unlinkSync(archivePath);
75
-
76
- console.log(`DirX v${VERSION} installed to ${binPath}`);
77
-
78
- try {
79
- const version = execSync(`"${binPath}" --version`, { encoding: "utf8" }).trim();
80
- console.log(` ${version}`);
81
- } catch {
82
- // binary works but --version might not exist yet
83
- }
84
- } catch (err) {
85
- console.error(`Failed to install DirX: ${err.message}`);
86
- console.error("You can download manually from:");
87
- console.error(` https://github.com/${REPO}/releases/tag/v${VERSION}`);
88
- process.exit(1);
89
- }
90
- }
91
-
92
- main();