@oriro/orirocli 0.1.3 → 0.1.4

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 (3) hide show
  1. package/README.md +16 -12
  2. package/dist/cli.js +48 -4
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -2,21 +2,25 @@
2
2
 
3
3
  # ORIRO‑Terminal - **“Head | Memory | Eyeball for AI”**
4
4
 
5
- # **FREE TIER | BYOK** | **Works in 100 Languages | Live Security Gaurdian-V3 (MCP Watch)**
5
+ # **FREE · KEYLESS · BYOK** | **Works in 100 Languages | Deterministic Security Guardian V3**
6
6
 
7
- A terminal coder that **sees the web**, **speaks and listens in 100 languages**, **guards itself**, and can wear a **floating avatar that talks back in its own voice** — all on‑device.
8
-
9
- A free, on-device-friendly terminal AI agent built on the Pi agent harness (used as a library).
7
+ A free, keyless terminal AI coder built on the Pi agent harness (used as a library).
8
+ It **writes and runs code** from a pool of free routers, works in **your language**, **guards every action**,
9
+ can **inspect a live site's structure**, and greets you with an **avatar in its own on-device voice**.
10
10
  Your language, your machine, no paid keys required.
11
11
 
12
- ## What's inside
13
- - **Keyless free-router Mux** — best-router selection + invisible failover across free providers, with an on-device floor. Never a paid key.
14
- - **100 languages** — type in your language; the model reasons in English; replies come back translated (on-device NLLB).
15
- - **Guardian V3** — a security gate on every tool call, default-on, fail-closed.
16
- - **Head** — goes out, inspects a live site, and reverse-engineers it to code.
17
- - **Scriber** — a consent-gated, self-healing local work journal (never leaves your machine).
18
- - **323 skills**, **multi-agent orchestration** on the free pool, and **MCP connectors**.
19
- - **Channels** — drive ORIRO from Telegram/Discord/WhatsApp with your own bot.
12
+ ## What's inside (this release)
13
+ - **Keyless free-router Mux** — best-router selection + invisible failover across free providers, with an on-device floor. **Never a paid key.** BYOK optional (live-validated).
14
+ - **100 languages** — pick yours at first run; the model works in English. On-device NLLB translation is an optional add-on (without it, your text passes through as-is).
15
+ - **Guardian V3 (Lite)** — a **deterministic** security gate on every tool call (default-on, fail-closed): blocks `curl|sh` remote-exec, destructive wipes, reverse shells, and env/secret exfil. No weights, no tokenizer, no download.
16
+ - **Head** — fetches a live site, detects its **sections/structure**, and reports the gaps to build from (the coder writes the code from that report).
17
+ - **Scriber (memory)** — a consent-gated local work journal, **off by default**; turns are recalled across sessions and never leave your machine.
18
+ - **323 skills** (CORE/TAIL tiered) + **multi-agent orchestration** on the free pool.
19
+ - **MCP connector catalog** (59) and **Channels** — run ORIRO from Telegram/Discord/WhatsApp with **your own** bot.
20
+ - **Avatar** — pick a face at onboarding; it greets you aloud in its paired on-device voice.
21
+
22
+ ## On the roadmap (not in this release)
23
+ Full-page **screenshot → code** Head (Playwright), the **two-way voice loop** (speak + listen/STT), in-REPL **permission modes**, and **`oriro mcp`** guided setup. Today the Head is fetch/structure-based and voice is the avatar's spoken greeting.
20
24
 
21
25
  ## Install
22
26
 
package/dist/cli.js CHANGED
@@ -475,6 +475,10 @@ var REVERSE_SHELL = [
475
475
  ];
476
476
  var SECRET_PATHS = /(\.ssh\/id_|\.ssh\/.*_rsa|\.aws\/credentials|\.oriro\/credentials|\.config\/gcloud|\.env(\.|\b)|\.netrc|id_ed25519|\.kube\/config|wallet\.dat|\.gnupg\/)/i;
477
477
  var NET_SINK = /\b(curl|wget|nc|ncat|socat|scp|rsync|ftp|tftp|invoke-webrequest|invoke-restmethod)\b/i;
478
+ var ENV_EXFIL = [
479
+ /\$\(\s*(printenv|env)\b/i,
480
+ /\$\(\s*cat\b[^)]*(\.ssh|\.aws|\.env|\.netrc|credential|secret|token|id_rsa|id_ed25519)/i
481
+ ];
478
482
  var PERSISTENCE = [
479
483
  /\bcrontab\b\s+(-|\S+)/i,
480
484
  // crontab install
@@ -542,6 +546,14 @@ var DEFAULT_RULES = [
542
546
  return null;
543
547
  }
544
548
  },
549
+ {
550
+ id: "env-exfiltration",
551
+ description: "Block dumping env vars / secret files into a network request (curl \u2026$(printenv SECRET)).",
552
+ match: (c) => {
553
+ const cmd = norm(cmdOf(c));
554
+ return cmd && NET_SINK.test(cmd) && anyMatch(ENV_EXFIL, cmd) ? block("env-exfiltration", "Sending environment variables / secret files off the machine") : null;
555
+ }
556
+ },
545
557
  {
546
558
  id: "persistence",
547
559
  description: "Flag cron/rc/startup/service edits used for Trojan persistence.",
@@ -2813,6 +2825,17 @@ function setupNllbTranslator(opts) {
2813
2825
  }
2814
2826
 
2815
2827
  // src/repl.ts
2828
+ function replHelp() {
2829
+ return `
2830
+ ${accent("ORIRO terminal \u2014 help")}
2831
+ ${dim("Just type to chat; ORIRO writes and runs code for you (keyless, free).")}
2832
+
2833
+ ${accent("/help")} this help ${accent("/exit")} or ${accent("/quit")} leave ${dim("Ctrl-D / Ctrl-C also exit")}
2834
+ ${dim("Run these OUTSIDE the chat (in your shell):")}
2835
+ ${dim("oriro skills \xB7 routers \xB7 connectors \xB7 channels \xB7 scribe \xB7 language \xB7 avatar")}
2836
+
2837
+ `;
2838
+ }
2816
2839
  async function runRepl() {
2817
2840
  if (isFirstRun()) await runOnboarding();
2818
2841
  else stdout4.write(banner());
@@ -2831,6 +2854,10 @@ async function runRepl() {
2831
2854
  }
2832
2855
  if (!line) continue;
2833
2856
  if (line === "/exit" || line === "/quit") break;
2857
+ if (line === "/help" || line === "/?") {
2858
+ stdout4.write(replHelp());
2859
+ continue;
2860
+ }
2834
2861
  const english = await translateForCoder(line, lang);
2835
2862
  let out = "";
2836
2863
  const unsub = session.subscribe((e) => {
@@ -4284,6 +4311,9 @@ function writeAdded(slugs) {
4284
4311
  function listConnectors(category) {
4285
4312
  return category ? CONNECTOR_CATALOG.filter((c) => c.category === category) : CONNECTOR_CATALOG;
4286
4313
  }
4314
+ function connectorCategories() {
4315
+ return [...new Set(CONNECTOR_CATALOG.map((c) => c.category))].sort();
4316
+ }
4287
4317
  function addConnector(slug) {
4288
4318
  const entry = connectorBySlug(slug);
4289
4319
  if (!entry) return { ok: false, error: `unknown connector '${slug}' \u2014 run \`oriro connectors list\`` };
@@ -4297,13 +4327,20 @@ function addedConnectors() {
4297
4327
  return CONNECTOR_CATALOG.filter((c) => added.has(c.slug));
4298
4328
  }
4299
4329
  function removeConnector(slug) {
4300
- writeAdded(readAdded().filter((s) => s !== slug));
4330
+ const before = readAdded();
4331
+ if (!before.includes(slug)) return false;
4332
+ writeAdded(before.filter((s) => s !== slug));
4333
+ return true;
4301
4334
  }
4302
4335
 
4303
4336
  // src/commands/connectors.ts
4304
4337
  function registerConnectorsCommand(program2) {
4305
4338
  const connectors = program2.command("connectors").description("MCP connectors \u2014 add external tools/services (inert until used)");
4306
4339
  connectors.command("list [category]").description("list the connector catalog (optionally filtered by category)").action((category) => {
4340
+ if (category && !connectorCategories().includes(category)) {
4341
+ info(`unknown category '${category}' \u2014 categories: ${connectorCategories().join(", ")}`);
4342
+ return;
4343
+ }
4307
4344
  const entries = listConnectors(category);
4308
4345
  const added = new Set(addedConnectors().map((c) => c.slug));
4309
4346
  heading(category ? `Connectors \xB7 ${category}` : "Connectors");
@@ -4320,8 +4357,8 @@ function registerConnectorsCommand(program2) {
4320
4357
  ok(`added ${accent(slug)} \u2014 inert until a session uses it`);
4321
4358
  });
4322
4359
  connectors.command("remove <slug>").description("remove a connector").action((slug) => {
4323
- removeConnector(slug);
4324
- ok(`removed ${accent(slug)}`);
4360
+ if (removeConnector(slug)) ok(`removed ${accent(slug)}`);
4361
+ else info(`'${slug}' is not in your added list \u2014 nothing to remove`);
4325
4362
  });
4326
4363
  }
4327
4364
 
@@ -4571,6 +4608,10 @@ function registerChannelsCommand(program2) {
4571
4608
  });
4572
4609
  channels.command("remove <kind>").description("remove a configured channel").action((kind) => {
4573
4610
  if (!isKind(kind)) die(`unknown channel '${kind}' \u2014 one of: ${KINDS.join(", ")}`);
4611
+ if (!readChannels().some((c) => c.kind === kind)) {
4612
+ info(`no ${kind} channel configured \u2014 nothing to remove`);
4613
+ return;
4614
+ }
4574
4615
  removeChannel(kind);
4575
4616
  ok(`removed ${accent(kind)}`);
4576
4617
  });
@@ -4595,6 +4636,9 @@ function registerSkillsCommand(program2) {
4595
4636
 
4596
4637
  // src/commands/language.ts
4597
4638
  import { stdin as stdin5 } from "process";
4639
+ function resolveLanguage(input) {
4640
+ return languageByCode(input) ?? LANGUAGES.find((l) => l.name.toLowerCase() === input.trim().toLowerCase());
4641
+ }
4598
4642
  function registerLanguageCommand(program2) {
4599
4643
  program2.command("language").description("show or change your terminal language").argument("[code]", "switch directly to this language (ISO code or name, e.g. es)").option("-a, --all", "list every available language").action(async (code, opts) => {
4600
4644
  if (opts.all) {
@@ -4607,7 +4651,7 @@ function registerLanguageCommand(program2) {
4607
4651
  return;
4608
4652
  }
4609
4653
  if (code) {
4610
- const lang = languageByCode(code);
4654
+ const lang = resolveLanguage(code);
4611
4655
  if (!lang) die(`unknown language '${code}' \u2014 run \`oriro language --all\` to see the list`);
4612
4656
  setTerminalLanguage(lang);
4613
4657
  ok(`${accent(lang.name)} is now your terminal language.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oriro/orirocli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "ORIRO — a free, on-device-friendly terminal AI agent. Built on the Pi agent harness (used as a library).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  "dev": "tsx src/cli.ts",
23
23
  "build": "tsup",
24
24
  "typecheck": "tsc --noEmit",
25
- "test:unit": "tsx scripts/test-tool-sanitize.ts",
25
+ "test:unit": "tsx scripts/test-tool-sanitize.ts && tsx scripts/test-guardian.ts",
26
26
  "smoke": "npm run build && node scripts/smoke.mjs",
27
27
  "prepublishOnly": "npm run build && npm run test:unit && node scripts/smoke.mjs && node scripts/prepublish-check.mjs",
28
28
  "start": "node dist/cli.js"