@naraya/cli 0.1.0 → 0.4.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 (83) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +184 -93
  3. package/bin/naraya-native.mjs +4 -0
  4. package/bin/naraya.mjs +1 -142
  5. package/bin/undici-timeout.mjs +1 -0
  6. package/dist/assets.pack.gz +0 -0
  7. package/dist/mcp/config-loader.js +32 -0
  8. package/dist/mcp/lifecycle.js +90 -0
  9. package/dist/mcp/tool-mapper.js +31 -0
  10. package/dist/mcp/transport.js +30 -0
  11. package/dist/pentest/catalog/catalog-loader.js +45 -0
  12. package/dist/pentest/catalog/index.js +1 -0
  13. package/dist/pentest/cli.js +117 -0
  14. package/dist/pentest/command-builder/command-builder.js +90 -0
  15. package/dist/pentest/command-builder/index.js +1 -0
  16. package/dist/pentest/index.js +10 -0
  17. package/dist/pentest/installer/index.js +1 -0
  18. package/dist/pentest/installer/tool-installer.js +90 -0
  19. package/dist/pentest/manager.js +125 -0
  20. package/dist/pentest/mode/index.js +1 -0
  21. package/dist/pentest/mode/mode-selector.js +127 -0
  22. package/dist/pentest/selector/index.js +1 -0
  23. package/dist/pentest/selector/tool-selector.js +66 -0
  24. package/dist/pentest/skill-bridge/index.js +1 -0
  25. package/dist/pentest/skill-bridge/skill-bridge.js +66 -0
  26. package/dist/pentest/skills/generator/index.js +1 -0
  27. package/dist/pentest/skills/generator/skill-generator.js +310 -0
  28. package/dist/pentest/skills/index.js +3 -0
  29. package/dist/pentest/skills/loader/index.js +1 -0
  30. package/dist/pentest/skills/loader/skill-loader.js +167 -0
  31. package/dist/pentest/skills/register/index.js +1 -0
  32. package/dist/pentest/skills/register/skill-register.js +162 -0
  33. package/dist/pentest/skills/types.js +1 -0
  34. package/dist/pentest/types.js +90 -0
  35. package/package.json +42 -14
  36. package/src/assets-pack.mjs +1 -0
  37. package/src/banner.mjs +5 -0
  38. package/src/clipboard.mjs +1 -0
  39. package/src/config.mjs +1 -40
  40. package/src/goodbye.mjs +7 -0
  41. package/src/login.mjs +7 -49
  42. package/src/mcp/config-loader.ts +50 -0
  43. package/src/mcp/lifecycle.ts +113 -0
  44. package/src/mcp/tool-mapper.ts +42 -0
  45. package/src/mcp/transport.ts +38 -0
  46. package/src/mcp-cli.mjs +5 -0
  47. package/src/pentest/catalog/catalog-loader.ts +55 -0
  48. package/src/pentest/catalog/index.ts +1 -0
  49. package/src/pentest/cli.ts +130 -0
  50. package/src/pentest/command-builder/command-builder.ts +109 -0
  51. package/src/pentest/command-builder/index.ts +1 -0
  52. package/src/pentest/index.ts +11 -0
  53. package/src/pentest/installer/index.ts +1 -0
  54. package/src/pentest/installer/tool-installer.ts +107 -0
  55. package/src/pentest/manager.ts +167 -0
  56. package/src/pentest/mode/index.ts +1 -0
  57. package/src/pentest/mode/mode-selector.ts +159 -0
  58. package/src/pentest/selector/index.ts +1 -0
  59. package/src/pentest/selector/tool-selector.ts +87 -0
  60. package/src/pentest/skill-bridge/index.ts +1 -0
  61. package/src/pentest/skill-bridge/skill-bridge.ts +86 -0
  62. package/src/pentest/skills/generator/index.ts +1 -0
  63. package/src/pentest/skills/generator/skill-generator.ts +373 -0
  64. package/src/pentest/skills/index.ts +4 -0
  65. package/src/pentest/skills/loader/index.ts +1 -0
  66. package/src/pentest/skills/loader/skill-loader.ts +206 -0
  67. package/src/pentest/skills/register/index.ts +1 -0
  68. package/src/pentest/skills/register/skill-register.ts +196 -0
  69. package/src/pentest/skills/types.ts +66 -0
  70. package/src/pentest/types.ts +341 -0
  71. package/src/seed.mjs +1 -36
  72. package/src/splash.mjs +4 -0
  73. package/src/status.mjs +2 -71
  74. package/assets/APPEND-SYSTEM.md +0 -9
  75. package/assets/extensions/naraya-brand.ts +0 -251
  76. package/assets/extensions/naraya-gate.ts +0 -23
  77. package/assets/naraya-logo.txt +0 -5
  78. package/assets/skills/narabuild/SKILL.md +0 -156
  79. package/assets/skills/naradroid/SKILL.md +0 -118
  80. package/assets/skills/naraexplore/SKILL.md +0 -71
  81. package/assets/skills/narafe/SKILL.md +0 -94
  82. package/assets/skills/naraplan/SKILL.md +0 -47
  83. package/assets/skills/narasearch/SKILL.md +0 -141
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Pentest Types - Full integration from pentest-core
3
+ */
4
+
5
+ // ── Flag Types ──
6
+
7
+ export type FlagType = "boolean" | "string" | "number" | "path" | "choice" | "repeat"
8
+
9
+ export interface FlagDefinition {
10
+ readonly name: string
11
+ readonly type: FlagType
12
+ readonly description: string
13
+ readonly default?: unknown
14
+ readonly required?: boolean
15
+ readonly choices?: readonly string[]
16
+ readonly depends_on?: string
17
+ }
18
+
19
+ export interface PositionalArgument {
20
+ readonly name: string
21
+ readonly type: string
22
+ readonly description: string
23
+ readonly required: boolean
24
+ }
25
+
26
+ // ── Command Structure ──
27
+
28
+ export interface CommandDefinition {
29
+ readonly base: string
30
+ readonly flags: readonly FlagDefinition[]
31
+ readonly positional: readonly PositionalArgument[]
32
+ readonly pipes?: readonly string[]
33
+ }
34
+
35
+ // ── Installation ──
36
+
37
+ export interface PlatformInstallation {
38
+ readonly command: string
39
+ readonly manager: string
40
+ }
41
+
42
+ export interface InstallationConfig {
43
+ readonly linux?: PlatformInstallation
44
+ readonly darwin?: PlatformInstallation
45
+ readonly win32?: PlatformInstallation
46
+ }
47
+
48
+ // ── Check Installed ──
49
+
50
+ export interface CheckInstalledConfig {
51
+ readonly command: string
52
+ readonly exit_code?: number
53
+ readonly parse_version?: string
54
+ }
55
+
56
+ // ── Categories & Phases ──
57
+
58
+ export type ToolCategory = "recon" | "enumeration" | "exploitation" | "reporting" | "utility"
59
+
60
+ export type PentestPhase = "recon" | "enumeration" | "exploitation" | "reporting"
61
+
62
+ export type OutputFormat = "text" | "json" | "xml" | "csv" | "html" | "markdown" | "sarif" | "grepable" | "binary" | "log" | "graph" | "ndjson"
63
+
64
+ // ── Tool Entry ──
65
+
66
+ export interface ToolEntry {
67
+ readonly tools_name: string
68
+ readonly description: string
69
+ readonly category: ToolCategory
70
+ readonly command: CommandDefinition
71
+ readonly installation: InstallationConfig
72
+ readonly check_installed: CheckInstalledConfig
73
+ readonly skills_loader: string
74
+ readonly phase: readonly PentestPhase[]
75
+ readonly tags: readonly string[]
76
+ readonly requires_root: boolean
77
+ readonly output_format: readonly OutputFormat[]
78
+ readonly homepage?: string
79
+ readonly alternatives?: readonly string[]
80
+ readonly min_version?: string
81
+ readonly max_version?: string
82
+ }
83
+
84
+ // ── Catalog Root ──
85
+
86
+ export interface ToolsCatalog {
87
+ readonly $schema: string
88
+ readonly version: string
89
+ readonly categories: readonly ToolCategory[]
90
+ readonly tools: readonly ToolEntry[]
91
+ }
92
+
93
+ // ── Runtime Types ──
94
+
95
+ export interface ToolAvailability {
96
+ readonly tools_name: string
97
+ readonly installed: boolean
98
+ readonly version?: string
99
+ readonly error?: string
100
+ }
101
+
102
+ export interface CommandBuildResult {
103
+ readonly command: string
104
+ readonly args: readonly string[]
105
+ readonly fullCommand: string
106
+ readonly requires_root: boolean
107
+ }
108
+
109
+ export interface SkillBridgeResult {
110
+ readonly skill_name: string
111
+ readonly skill_path?: string
112
+ readonly loaded: boolean
113
+ readonly error?: string
114
+ }
115
+
116
+ // ── Selector Options ──
117
+
118
+ export interface ToolSelectorOptions {
119
+ readonly category?: ToolCategory
120
+ readonly phase?: PentestPhase
121
+ readonly tags?: readonly string[]
122
+ readonly requires_root?: boolean
123
+ readonly installed_only?: boolean
124
+ }
125
+
126
+ // ── Command Builder Options ──
127
+
128
+ export interface CommandBuildOptions {
129
+ readonly flags?: Record<string, unknown>
130
+ readonly positional?: readonly string[]
131
+ readonly pipe_to?: readonly string[]
132
+ }
133
+
134
+ // ── Pentest Mode Types ──
135
+
136
+ export type PentestMode = "auto" | "ctf" | "bug-bounty" | "red-team" | "blue-team" | "offensive" | "grey-hat"
137
+
138
+ export type ScopeEnforcement = "strict" | "moderate" | "none"
139
+
140
+ export type ReportFormat = "standard" | "hackerone" | "bugcrowd" | "executive" | "technical" | "ir" | "flag"
141
+
142
+ export interface ModeConfig {
143
+ readonly mode: PentestMode
144
+ readonly description: string
145
+ readonly scope_enforcement: ScopeEnforcement
146
+ readonly tool_priority: readonly ToolCategory[]
147
+ readonly skill_chain: readonly string[]
148
+ readonly parallelism: number
149
+ readonly stealth: boolean
150
+ readonly report_format: ReportFormat
151
+ readonly loop_config: ModeLoopConfig
152
+ readonly safety_constraints: SafetyConfig
153
+ }
154
+
155
+ export interface ModeLoopConfig {
156
+ readonly max_iterations: number
157
+ readonly iteration_timeout_ms: number
158
+ readonly strategy: "reset" | "continue" | "adaptive"
159
+ readonly auto_continue: boolean
160
+ readonly speed: "fast" | "moderate" | "slow"
161
+ }
162
+
163
+ export interface SafetyConfig {
164
+ readonly scope_strict: boolean
165
+ readonly no_dos: boolean
166
+ readonly no_exfiltration: boolean
167
+ readonly stealth_mode: boolean
168
+ readonly auto_stop_on_scope_violation: boolean
169
+ }
170
+
171
+ // ── Mode Presets ──
172
+
173
+ export const MODE_PRESETS: Record<PentestMode, ModeConfig> = {
174
+ "auto": {
175
+ mode: "auto",
176
+ description: "Auto-detect mode based on target type. Smart tool priority and adaptive strategy.",
177
+ scope_enforcement: "moderate",
178
+ tool_priority: ["recon", "enumeration", "exploitation", "reporting", "utility"],
179
+ skill_chain: ["pentest-recon", "pentest-enum", "pentest-exploit", "pentest-report"],
180
+ parallelism: 4,
181
+ stealth: false,
182
+ report_format: "standard",
183
+ loop_config: { max_iterations: 100, iteration_timeout_ms: 300000, strategy: "adaptive", auto_continue: true, speed: "moderate" },
184
+ safety_constraints: { scope_strict: true, no_dos: true, no_exfiltration: true, stealth_mode: false, auto_stop_on_scope_violation: true }
185
+ },
186
+ "ctf": {
187
+ mode: "ctf",
188
+ description: "CTF challenge mode. Speed-first, parallel everything, flag-focused reporting.",
189
+ scope_enforcement: "none",
190
+ tool_priority: ["exploitation", "enumeration", "recon", "utility"],
191
+ skill_chain: ["ctf-recon", "ctf-exploit", "ctf-crypto", "ctf-forensics"],
192
+ parallelism: 8,
193
+ stealth: false,
194
+ report_format: "flag",
195
+ loop_config: { max_iterations: 200, iteration_timeout_ms: 120000, strategy: "adaptive", auto_continue: true, speed: "fast" },
196
+ safety_constraints: { scope_strict: false, no_dos: false, no_exfiltration: false, stealth_mode: false, auto_stop_on_scope_violation: false }
197
+ },
198
+ "bug-bounty": {
199
+ mode: "bug-bounty",
200
+ description: "Bug bounty mode. Scope-strict, web-focused, HackerOne/Bugcrowd report format.",
201
+ scope_enforcement: "strict",
202
+ tool_priority: ["recon", "enumeration", "exploitation", "reporting"],
203
+ skill_chain: ["pentest-recon", "pentest-enum", "pentest-exploit", "pentest-report"],
204
+ parallelism: 6,
205
+ stealth: false,
206
+ report_format: "hackerone",
207
+ loop_config: { max_iterations: 150, iteration_timeout_ms: 300000, strategy: "continue", auto_continue: true, speed: "moderate" },
208
+ safety_constraints: { scope_strict: true, no_dos: true, no_exfiltration: true, stealth_mode: false, auto_stop_on_scope_violation: true }
209
+ },
210
+ "red-team": {
211
+ mode: "red-team",
212
+ description: "Red team mode. Stealth-first, persistence, lateral movement, executive reporting.",
213
+ scope_enforcement: "strict",
214
+ tool_priority: ["recon", "exploitation", "enumeration", "reporting"],
215
+ skill_chain: ["red-recon", "red-exploit", "red-lateral", "red-persistence"],
216
+ parallelism: 2,
217
+ stealth: true,
218
+ report_format: "executive",
219
+ loop_config: { max_iterations: 50, iteration_timeout_ms: 600000, strategy: "continue", auto_continue: true, speed: "slow" },
220
+ safety_constraints: { scope_strict: true, no_dos: true, no_exfiltration: true, stealth_mode: true, auto_stop_on_scope_violation: true }
221
+ },
222
+ "blue-team": {
223
+ mode: "blue-team",
224
+ description: "Blue team mode. Detection-first, log analysis, incident response, IR reporting.",
225
+ scope_enforcement: "strict",
226
+ tool_priority: ["enumeration", "recon", "reporting", "utility"],
227
+ skill_chain: ["blue-detect", "blue-ir", "blue-forensics", "blue-report"],
228
+ parallelism: 4,
229
+ stealth: false,
230
+ report_format: "ir",
231
+ loop_config: { max_iterations: 100, iteration_timeout_ms: 300000, strategy: "continue", auto_continue: true, speed: "moderate" },
232
+ safety_constraints: { scope_strict: true, no_dos: true, no_exfiltration: true, stealth_mode: false, auto_stop_on_scope_violation: true }
233
+ },
234
+ "offensive": {
235
+ mode: "offensive",
236
+ description: "Offensive mode. Aggressive exploitation, privilege escalation, technical reporting.",
237
+ scope_enforcement: "strict",
238
+ tool_priority: ["exploitation", "enumeration", "recon", "reporting"],
239
+ skill_chain: ["pentest-recon", "pentest-enum", "pentest-exploit", "pentest-privesc"],
240
+ parallelism: 6,
241
+ stealth: false,
242
+ report_format: "technical",
243
+ loop_config: { max_iterations: 100, iteration_timeout_ms: 300000, strategy: "continue", auto_continue: true, speed: "moderate" },
244
+ safety_constraints: { scope_strict: true, no_dos: true, no_exfiltration: true, stealth_mode: false, auto_stop_on_scope_violation: true }
245
+ },
246
+ "grey-hat": {
247
+ mode: "grey-hat",
248
+ description: "Grey hat mode. Balanced offensive/defensive, moderate stealth, hybrid reporting.",
249
+ scope_enforcement: "moderate",
250
+ tool_priority: ["recon", "enumeration", "exploitation", "reporting", "utility"],
251
+ skill_chain: ["pentest-recon", "pentest-enum", "pentest-exploit", "pentest-report"],
252
+ parallelism: 4,
253
+ stealth: true,
254
+ report_format: "technical",
255
+ loop_config: { max_iterations: 100, iteration_timeout_ms: 300000, strategy: "adaptive", auto_continue: true, speed: "moderate" },
256
+ safety_constraints: { scope_strict: true, no_dos: true, no_exfiltration: true, stealth_mode: true, auto_stop_on_scope_violation: true }
257
+ }
258
+ }
259
+
260
+ // ── Mode Selector Options ──
261
+
262
+ export interface ModeSelectorOptions {
263
+ readonly target?: string
264
+ readonly scope_file?: string
265
+ readonly preferred_mode?: PentestMode
266
+ readonly auto_detect?: boolean
267
+ }
268
+
269
+ export interface ModeSelectionResult {
270
+ readonly selected_mode: PentestMode
271
+ readonly config: ModeConfig
272
+ readonly auto_detected: boolean
273
+ readonly detection_reason?: string
274
+ }
275
+
276
+ // ── Pentest Skill Types ──
277
+
278
+ export interface PentestSkill {
279
+ readonly name: string
280
+ readonly description: string
281
+ readonly version: string
282
+ readonly phase: readonly PentestPhase[]
283
+ readonly category: readonly ToolCategory[]
284
+ readonly tools: readonly string[]
285
+ readonly author?: string
286
+ readonly homepage?: string
287
+ readonly tags: readonly string[]
288
+ readonly template: string
289
+ }
290
+
291
+ export interface PentestSkillManifest {
292
+ readonly name: string
293
+ readonly description: string
294
+ readonly version: string
295
+ readonly phase: readonly PentestPhase[]
296
+ readonly category: readonly ToolCategory[]
297
+ readonly tools: readonly string[]
298
+ readonly tags: readonly string[]
299
+ readonly skill_path: string
300
+ readonly loaded: boolean
301
+ }
302
+
303
+ export interface SkillRegisterEntry {
304
+ readonly name: string
305
+ readonly skill: PentestSkill
306
+ readonly registered_at: string
307
+ readonly enabled: boolean
308
+ }
309
+
310
+ export interface SkillRegisterConfig {
311
+ readonly skills_dir: string
312
+ readonly auto_discover: boolean
313
+ readonly search_paths: readonly string[]
314
+ }
315
+
316
+ export interface GeneratedSkill {
317
+ readonly name: string
318
+ readonly content: string
319
+ readonly path: string
320
+ readonly tools_referenced: readonly string[]
321
+ readonly phase: readonly PentestPhase[]
322
+ }
323
+
324
+ export interface SkillGenerationOptions {
325
+ readonly name: string
326
+ readonly description: string
327
+ readonly phase: readonly PentestPhase[]
328
+ readonly category: readonly ToolCategory[]
329
+ readonly tools: readonly string[]
330
+ readonly author?: string
331
+ readonly tags?: readonly string[]
332
+ readonly output_dir?: string
333
+ readonly template?: "basic" | "recon" | "exploitation" | "reporting" | "custom"
334
+ }
335
+
336
+ export interface SkillLoadResult {
337
+ readonly name: string
338
+ readonly loaded: boolean
339
+ readonly skill?: PentestSkill
340
+ readonly error?: string
341
+ }
package/src/seed.mjs CHANGED
@@ -1,36 +1 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
-
4
- const MANIFEST = ".naraya-managed.json";
5
-
6
- /** Recursively list files under dir, relative paths, forward slashes. */
7
- function listFiles(dir, base = dir) {
8
- if (!fs.existsSync(dir)) return [];
9
- return fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => {
10
- const p = path.join(dir, e.name);
11
- return e.isDirectory() ? listFiles(p, base) : [path.relative(base, p).replaceAll("\\", "/")];
12
- });
13
- }
14
-
15
- /**
16
- * Sync assets into the agent config dir. Only files listed in the previous
17
- * manifest are ever overwritten or deleted; user files are untouched.
18
- */
19
- export function seed(assetsDir, agentDir) {
20
- fs.mkdirSync(agentDir, { recursive: true });
21
- const manifestPath = path.join(agentDir, MANIFEST);
22
- const previous = fs.existsSync(manifestPath)
23
- ? JSON.parse(fs.readFileSync(manifestPath, "utf8")).files
24
- : [];
25
-
26
- const current = listFiles(assetsDir);
27
- for (const rel of current) {
28
- const dst = path.join(agentDir, rel);
29
- fs.mkdirSync(path.dirname(dst), { recursive: true });
30
- fs.copyFileSync(path.join(assetsDir, rel), dst);
31
- }
32
- for (const rel of previous) {
33
- if (!current.includes(rel)) fs.rmSync(path.join(agentDir, rel), { force: true });
34
- }
35
- fs.writeFileSync(manifestPath, JSON.stringify({ files: current }, null, 2));
36
- }
1
+ import e from"node:fs";import t from"node:path";const u=".naraya-managed.json";function a(n,r=n){return e.existsSync(n)?e.readdirSync(n,{withFileTypes:!0}).flatMap(i=>{const c=t.join(n,i.name);return i.isDirectory()?a(c,r):[t.relative(r,c).replaceAll("\\","/")]}):[]}function y(n,r){e.mkdirSync(r,{recursive:!0});const i=t.join(r,u),c=e.existsSync(i)?JSON.parse(e.readFileSync(i,"utf8")).files:[],l=n.map(o=>o.rel);for(const o of n){const f=t.join(r,o.rel);e.mkdirSync(t.dirname(f),{recursive:!0}),e.writeFileSync(f,o.buf)}for(const o of c){if(l.includes(o))continue;const f=t.join(r,o);e.rmSync(f,{force:!0});let s=t.dirname(f);for(;s!==r&&s.startsWith(r);){try{if(e.readdirSync(s).length>0)break;e.rmdirSync(s)}catch{break}s=t.dirname(s)}}e.writeFileSync(i,JSON.stringify({files:l},null,2))}function S(n,r){const i=a(n).map(c=>({rel:c,buf:e.readFileSync(t.join(n,c))}));y(i,r)}export{S as seed,y as seedEntries};
package/src/splash.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import b from"node:fs";import x from"node:os";import f from"node:path";const m="\x1B[38;2;45;123;255m",d="\x1B[48;2;45;123;255m",k="\x1B[38;2;10;29;77m",y="\x1B[48;2;10;29;77m",w="\x1B[38;2;111;191;115m",T="\x1B[38;2;224;163;62m",E="\x1B[1m",c="\x1B[2m",s="\x1B[0m",L="\x1B[?25l",S="\x1B[?25h",p="\x1B[2J\x1B[3J\x1B[H",$=B=>new Promise(_=>setTimeout(_,B)),A=B=>B.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g,"").length,l=["_____________BB___","___BBB_______BBB__","_BBBBBB______BBBBB","_BBBBBBBB____BBBBB","B_BBBBBBBB___BBBBB","BB_BBBBBBBB__BBBNN","BBB_BBBBBBB__BBNNN","BBBB__BBB____BNNNN","BBBBBB_____BBBNNNN","BBBBBB___BBBBBNNNN","BBBBB___BBBBBBNNNN","BBBBB___BBBBBBNNNN","BBBBB____BBBBBNNNN","BBBBB______BBBNNN_","BBBBB_______BBNN__","_BBBB________B____","__BBB_____________","____B_____________"],N=B=>B==="B"?m:B==="N"?k:"",I=B=>B==="B"?d:B==="N"?y:"";function O(){const B=[];for(let _=0;_<l.length;_+=2){let t="";for(let n=0;n<l[_].length;n++){const o=l[_][n],e=l[_+1]?.[n]??"_";o==="_"&&e==="_"?t+=" ":o!=="_"&&e==="_"?t+=`${N(o)}\u2580${s}`:o==="_"&&e!=="_"?t+=`${N(e)}\u2584${s}`:o===e?t+=`${N(o)}\u2588${s}`:t+=`${N(o)}${I(e)}\u2580${s}`}B.push(t)}return B}function D(){return process.env.PI_CODING_AGENT_DIR??f.join(x.homedir(),".naraya","agent")}function G(){try{const _=JSON.parse(b.readFileSync(f.join(D(),"models.json"),"utf8")).providers?.naraya;if(_?.baseUrl&&_?.apiKey)return _}catch{}return null}function g(B,_,t){const n=`
2
+ `.repeat(Math.max(0,Math.floor((_-t.length)/2))),o=t.map(e=>" ".repeat(Math.max(0,Math.floor((B-A(e))/2)))+e).join(`
3
+ `);return`${p}${n}${o}
4
+ `}async function P(){const B=G();if(!process.stdout.isTTY)return{ok:!!B,reason:B?"ok":"login"};const _=process.stdout.columns||80,t=process.stdout.rows||24,n=B?fetch(`${B.baseUrl}/me`,{headers:{authorization:`Bearer ${B.apiKey}`},signal:AbortSignal.timeout(3e3)}).then(r=>r.ok?"ok":"expired").catch(()=>"offline"):Promise.resolve("login"),o=O(),e=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];process.stdout.write(L);try{for(let u=0;u<24;u++){const h=`${m}${e[u%e.length]}${s} ${E}Starting Naraya CLI${s}${c}\u2026${s}`;process.stdout.write(g(_,t,[...o,"",h])),await $(105)}const r=await n;let i,a;return r==="login"?(i=`${c}\u2192 Belum login \u2014 mengarahkan ke login\u2026${s}`,a=!1):r==="expired"?(i=`${c}\u2192 Sesi habis \u2014 login ulang\u2026${s}`,a=!1):r==="offline"?(i=`${c}\u2192 Masuk terminal (gateway tak terjangkau)\u2026${s}`,a=!0):(i=`${w}\u2713 Auth sukses${s}${c} \u2014 masuk terminal\u2026${s}`,a=!0),process.stdout.write(g(_,t,[...o,"",i])),await $(a?700:1200),{ok:a,reason:r}}finally{process.stdout.write(p+S)}}export{P as splash};
package/src/status.mjs CHANGED
@@ -1,71 +1,2 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { BASE_URL as DEFAULT_BASE_URL } from "./config.mjs";
4
-
5
- /** Read the saved key from models.json (written by `naraya login`). Null if absent. */
6
- function readKey(agentDir) {
7
- const p = path.join(agentDir, "models.json");
8
- if (!fs.existsSync(p)) return null;
9
- try {
10
- return JSON.parse(fs.readFileSync(p, "utf8")).providers?.naraya?.apiKey ?? null;
11
- } catch {
12
- return null;
13
- }
14
- }
15
-
16
- /** Format the credit. Credits are Rupiah (1 credit = Rp1); `--usd` shows the
17
- * server-provided USD equivalent instead. */
18
- function formatCredit(credit, usd) {
19
- if (usd && credit?.usd_equivalent != null) {
20
- return `$${Number(credit.usd_equivalent).toFixed(2)} available (USD est.)`;
21
- }
22
- const amount = Number(credit?.available ?? 0);
23
- return `Rp ${amount.toLocaleString("id-ID", { maximumFractionDigits: 0 })} available`;
24
- }
25
-
26
- /**
27
- * Format the /v1/me response into a printable block. Pure (no I/O).
28
- * quota.remaining/limit are DAILY TOKENS (unit:"tokens"), never request counts —
29
- * usage.requests_* are the counts, kept separate.
30
- */
31
- export function renderStatus(s, { usd = false } = {}) {
32
- const quota =
33
- s.quota?.limit > 0
34
- ? `${s.quota.remaining.toLocaleString("en-US")} / ${s.quota.limit.toLocaleString("en-US")} ${s.quota.unit ?? "tokens"} today (reset ${String(s.quota.reset_at).slice(0, 10)})`
35
- : "no daily cap (fair-use)";
36
- const models = Array.isArray(s.models) ? s.models : [];
37
- const modelList = models.slice(0, 6).join(", ") + (models.length > 6 ? ` … (${models.length})` : ` (${models.length})`);
38
- const ok = s.usage ? `${s.usage.requests_today} req, ${(s.usage.success_rate * 100).toFixed(1)}% ok` : "";
39
- return [
40
- `Signed in: ${s.account?.email ?? "?"} [${s.account?.plan ?? "?"}]`,
41
- `Credit: ${formatCredit(s.credit, usd)}`,
42
- `Quota: ${quota}`,
43
- `Today: ${ok}`,
44
- `Models: ${modelList}`,
45
- ].join("\n");
46
- }
47
-
48
- /** Fetch /v1/me with the saved key and print the status block. */
49
- export async function status(agentDir, { BASE_URL = DEFAULT_BASE_URL } = {}, { usd = false } = {}) {
50
- const key = readKey(agentDir);
51
- if (!key) {
52
- console.error("Run `naraya login` first.");
53
- process.exit(1);
54
- }
55
- let res;
56
- try {
57
- res = await fetch(`${BASE_URL}/me`, { headers: { authorization: `Bearer ${key}` } });
58
- } catch (err) {
59
- console.error(`status failed: ${err.message}`);
60
- process.exit(1);
61
- }
62
- if (res.status === 401) {
63
- console.error("Key rejected. Run `naraya login` again.");
64
- process.exit(1);
65
- }
66
- if (!res.ok) {
67
- console.error(`status failed: HTTP ${res.status}`);
68
- process.exit(1);
69
- }
70
- console.log(renderStatus(await res.json(), { usd }));
71
- }
1
+ import i from"node:fs";import s from"node:path";import{BASE_URL as l}from"./config.mjs";function u(e){const t=s.join(e,"models.json");if(!i.existsSync(t))return null;try{return JSON.parse(i.readFileSync(t,"utf8")).providers?.naraya?.apiKey??null}catch{return null}}function c(e,t){return t&&e?.usd_equivalent!=null?`$${Number(e.usd_equivalent).toFixed(2)} available (USD est.)`:`Rp ${Number(e?.available??0).toLocaleString("id-ID",{maximumFractionDigits:0})} available`}function d(e,{usd:t=!1}={}){const r=e.quota?.limit>0?`${e.quota.remaining.toLocaleString("en-US")} / ${e.quota.limit.toLocaleString("en-US")} ${e.quota.unit??"tokens"} today (reset ${String(e.quota.reset_at).slice(0,10)})`:"no daily cap (fair-use)",a=Array.isArray(e.models)?e.models:[],o=a.slice(0,6).join(", ")+(a.length>6?` \u2026 (${a.length})`:` (${a.length})`),n=e.usage?`${e.usage.requests_today} req, ${(e.usage.success_rate*100).toFixed(1)}% ok`:"";return[`Signed in: ${e.account?.email??"?"} [${e.account?.plan??"?"}]`,`Credit: ${c(e.credit,t)}`,`Quota: ${r}`,`Today: ${n}`,`Models: ${o}`].join(`
2
+ `)}async function g(e,{BASE_URL:t=l}={},{usd:r=!1}={}){const a=u(e);a||(console.error("Run `naraya login` first."),process.exit(1));let o;try{o=await fetch(`${t}/me`,{headers:{authorization:`Bearer ${a}`}})}catch(n){console.error(`status failed: ${n.message}`),process.exit(1)}o.status===401&&(console.error("Key rejected. Run `naraya login` again."),process.exit(1)),o.ok||(console.error(`status failed: HTTP ${o.status}`),process.exit(1)),console.log(d(await o.json(),{usd:r}))}export{d as renderStatus,g as status};
@@ -1,9 +0,0 @@
1
- You are Naraya, an AI coding agent powered by router.naraya.ai.
2
-
3
- Specialist skills are available on demand — load the matching one before starting a task:
4
- - NaraBuild (`/skill:narabuild`) — implement features, bugfixes, refactors, releases (changes code).
5
- - NaraPlan (`/skill:naraplan`) — investigate and write an implementation plan; never edits code.
6
- - NaraSearch (`/skill:narasearch`) — evidence-first research over docs, libraries, codebases.
7
- - NaraExplore (`/skill:naraexplore`) — fast read-only codebase navigation.
8
- - NaraFE (`/skill:narafe`) — UI/UX, React/Vue/Svelte, CSS/Tailwind, accessibility.
9
- - NaraDroid (`/skill:naradroid`) — native Android: Kotlin/Gradle/Compose, adb, APK/AAB.