@oriro/orirocli 0.3.2 → 0.3.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.
- package/dist/cli.js +390 -96
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -3382,19 +3382,77 @@ import {
|
|
|
3382
3382
|
createAgentSession as createAgentSession2,
|
|
3383
3383
|
AuthStorage as AuthStorage2,
|
|
3384
3384
|
ModelRegistry as ModelRegistry2,
|
|
3385
|
-
SessionManager as SessionManager2,
|
|
3386
3385
|
SettingsManager,
|
|
3387
3386
|
DefaultResourceLoader,
|
|
3388
3387
|
getAgentDir
|
|
3389
3388
|
} from "@earendil-works/pi-coding-agent";
|
|
3390
3389
|
|
|
3390
|
+
// src/sessions/store.ts
|
|
3391
|
+
import { join as join16 } from "path";
|
|
3392
|
+
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
3393
|
+
function sessionsDir() {
|
|
3394
|
+
return join16(oriroDir(), "sessions");
|
|
3395
|
+
}
|
|
3396
|
+
async function findByIdPrefix(cwd, idish, verb) {
|
|
3397
|
+
const infos = await SessionManager.list(cwd, sessionsDir());
|
|
3398
|
+
const exact = infos.find((s) => s.id === idish);
|
|
3399
|
+
if (exact) return exact;
|
|
3400
|
+
const pref = infos.filter((s) => s.id.startsWith(idish));
|
|
3401
|
+
if (pref.length === 1) return pref[0];
|
|
3402
|
+
if (pref.length > 1) throw new Error(`'${idish}' matches ${pref.length} sessions \u2014 use a longer id (oriro sessions)`);
|
|
3403
|
+
throw new Error(`no session '${idish}' to ${verb} here \u2014 see: oriro sessions`);
|
|
3404
|
+
}
|
|
3405
|
+
async function resolveSessionManager(cwd, opts = {}) {
|
|
3406
|
+
const dir = sessionsDir();
|
|
3407
|
+
if (opts.ephemeral) return { sm: SessionManager.inMemory(cwd), note: "ephemeral \u2014 this session is NOT saved" };
|
|
3408
|
+
if (opts.continue) return { sm: SessionManager.continueRecent(cwd, dir), note: "continuing your most recent session" };
|
|
3409
|
+
if (opts.resumeId) {
|
|
3410
|
+
const hit = await findByIdPrefix(cwd, opts.resumeId, "resume");
|
|
3411
|
+
return { sm: SessionManager.open(hit.path, dir), note: `resumed ${hit.id.slice(0, 8)} (${hit.messageCount} msgs)` };
|
|
3412
|
+
}
|
|
3413
|
+
if (opts.forkId) {
|
|
3414
|
+
const hit = await findByIdPrefix(cwd, opts.forkId, "fork");
|
|
3415
|
+
return { sm: SessionManager.forkFrom(hit.path, cwd, dir), note: `forked a new session from ${hit.id.slice(0, 8)}` };
|
|
3416
|
+
}
|
|
3417
|
+
return { sm: SessionManager.create(cwd, dir), note: "new session (saved locally \u2014 resume with `oriro -c`)" };
|
|
3418
|
+
}
|
|
3419
|
+
async function listSessions(cwd = process.cwd()) {
|
|
3420
|
+
const infos = await SessionManager.list(cwd, sessionsDir());
|
|
3421
|
+
return infos.sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
3422
|
+
}
|
|
3423
|
+
function shortWhen(d) {
|
|
3424
|
+
const mon = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][d.getMonth()] ?? "";
|
|
3425
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
3426
|
+
return `${mon} ${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
3427
|
+
}
|
|
3428
|
+
function formatSessionList(infos) {
|
|
3429
|
+
if (!infos.length) return [dim(" no saved sessions yet \u2014 they're created as you chat. Resume the last with `oriro -c`.")];
|
|
3430
|
+
const lines = [];
|
|
3431
|
+
for (const s of infos) {
|
|
3432
|
+
const id = s.id.slice(0, 8);
|
|
3433
|
+
const first = (s.firstMessage ?? s.name ?? "(empty)").replace(/\s+/g, " ").trim().slice(0, 56);
|
|
3434
|
+
lines.push(` ${accent(id)} ${dim(shortWhen(s.modified).padEnd(12))} ${dim(`${String(s.messageCount).padStart(3)} msg`)} ${first}`);
|
|
3435
|
+
}
|
|
3436
|
+
lines.push(dim(` ${infos.length} session${infos.length === 1 ? "" : "s"} \xB7 resume: `) + accent("oriro --resume <id>") + dim(" \xB7 continue last: ") + accent("oriro -c"));
|
|
3437
|
+
return lines;
|
|
3438
|
+
}
|
|
3439
|
+
function sessionRows(infos) {
|
|
3440
|
+
return infos.map((s) => ({
|
|
3441
|
+
id: s.id,
|
|
3442
|
+
messages: s.messageCount,
|
|
3443
|
+
modified: s.modified.toISOString(),
|
|
3444
|
+
cwd: s.cwd,
|
|
3445
|
+
first: (s.firstMessage ?? "").replace(/\s+/g, " ").trim().slice(0, 80)
|
|
3446
|
+
}));
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3391
3449
|
// src/routers/mux-provider.ts
|
|
3392
3450
|
import { streamSimple as piStreamSimple2, createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
3393
3451
|
import { register as registerOpenAICompletions } from "@earendil-works/pi-ai/openai-completions";
|
|
3394
3452
|
|
|
3395
3453
|
// src/routers/mux.ts
|
|
3396
3454
|
import { existsSync as existsSync7, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync12 } from "fs";
|
|
3397
|
-
import { join as
|
|
3455
|
+
import { join as join17 } from "path";
|
|
3398
3456
|
var COOLDOWN_DEFAULT_MS = 6e4;
|
|
3399
3457
|
var UNHEALTHY_AFTER = 3;
|
|
3400
3458
|
var RouterMux = class {
|
|
@@ -3464,11 +3522,11 @@ var RouterMux = class {
|
|
|
3464
3522
|
}
|
|
3465
3523
|
};
|
|
3466
3524
|
function healthStatePath(dir) {
|
|
3467
|
-
return
|
|
3525
|
+
return join17(dir, "routers", "health.json");
|
|
3468
3526
|
}
|
|
3469
3527
|
function saveMuxState(dir, stats) {
|
|
3470
3528
|
const p = healthStatePath(dir);
|
|
3471
|
-
mkdirSync9(
|
|
3529
|
+
mkdirSync9(join17(dir, "routers"), { recursive: true });
|
|
3472
3530
|
writeFileSync12(p, JSON.stringify(stats, null, 2), "utf8");
|
|
3473
3531
|
}
|
|
3474
3532
|
function loadMuxState(dir) {
|
|
@@ -3570,28 +3628,28 @@ import { Type } from "typebox";
|
|
|
3570
3628
|
|
|
3571
3629
|
// src/scribe/capture.ts
|
|
3572
3630
|
import { closeSync as closeSync2, fsyncSync as fsyncSync2, mkdirSync as mkdirSync12, openSync as openSync2, writeSync as writeSync2 } from "fs";
|
|
3573
|
-
import { join as
|
|
3631
|
+
import { join as join19 } from "path";
|
|
3574
3632
|
|
|
3575
3633
|
// src/scribe/digest.ts
|
|
3576
3634
|
import { existsSync as existsSync8, mkdirSync as mkdirSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync13 } from "fs";
|
|
3577
3635
|
|
|
3578
3636
|
// src/scribe/paths.ts
|
|
3579
|
-
import { join as
|
|
3637
|
+
import { join as join18 } from "path";
|
|
3580
3638
|
function scribeDir() {
|
|
3581
3639
|
const override = process.env.ORIRO_SCRIBE_DIR?.trim();
|
|
3582
|
-
return override && override.length > 0 ? override :
|
|
3640
|
+
return override && override.length > 0 ? override : join18(CONFIG_DIR, "scribe");
|
|
3583
3641
|
}
|
|
3584
3642
|
function journalFile(date) {
|
|
3585
|
-
return
|
|
3643
|
+
return join18(scribeDir(), `${date}.md`);
|
|
3586
3644
|
}
|
|
3587
3645
|
function digestFile() {
|
|
3588
|
-
return
|
|
3646
|
+
return join18(scribeDir(), "_digest.md");
|
|
3589
3647
|
}
|
|
3590
3648
|
function timelineFile() {
|
|
3591
|
-
return
|
|
3649
|
+
return join18(scribeDir(), "_timeline.md");
|
|
3592
3650
|
}
|
|
3593
3651
|
function artifactsDir() {
|
|
3594
|
-
return
|
|
3652
|
+
return join18(scribeDir(), "artifacts");
|
|
3595
3653
|
}
|
|
3596
3654
|
|
|
3597
3655
|
// src/scribe/digest.ts
|
|
@@ -3774,7 +3832,7 @@ var INLINE_CAP = 4e3;
|
|
|
3774
3832
|
function sideFile(date, ts, kind, full) {
|
|
3775
3833
|
mkdirSync12(artifactsDir(), { recursive: true });
|
|
3776
3834
|
const name = `${date}_${ts.replace(/[:.]/g, "-")}_${kind}.md`;
|
|
3777
|
-
const p =
|
|
3835
|
+
const p = join19(artifactsDir(), name);
|
|
3778
3836
|
const fd = openSync2(p, "w");
|
|
3779
3837
|
try {
|
|
3780
3838
|
writeSync2(fd, full);
|
|
@@ -3866,12 +3924,12 @@ import {
|
|
|
3866
3924
|
writeFileSync as writeFileSync14,
|
|
3867
3925
|
writeSync as writeSync3
|
|
3868
3926
|
} from "fs";
|
|
3869
|
-
import { join as
|
|
3927
|
+
import { join as join20 } from "path";
|
|
3870
3928
|
function healthFile() {
|
|
3871
|
-
return
|
|
3929
|
+
return join20(scribeDir(), "_health.json");
|
|
3872
3930
|
}
|
|
3873
3931
|
function faultLogFile() {
|
|
3874
|
-
return
|
|
3932
|
+
return join20(scribeDir(), "_faults.log");
|
|
3875
3933
|
}
|
|
3876
3934
|
function read2() {
|
|
3877
3935
|
try {
|
|
@@ -3924,9 +3982,9 @@ import {
|
|
|
3924
3982
|
writeFileSync as writeFileSync15,
|
|
3925
3983
|
writeSync as writeSync4
|
|
3926
3984
|
} from "fs";
|
|
3927
|
-
import { join as
|
|
3985
|
+
import { join as join21 } from "path";
|
|
3928
3986
|
function walFile() {
|
|
3929
|
-
return
|
|
3987
|
+
return join21(scribeDir(), "_wal.jsonl");
|
|
3930
3988
|
}
|
|
3931
3989
|
function appendLine(obj) {
|
|
3932
3990
|
mkdirSync14(scribeDir(), { recursive: true });
|
|
@@ -4138,12 +4196,12 @@ function attachScribe(session) {
|
|
|
4138
4196
|
|
|
4139
4197
|
// src/context/project-md.ts
|
|
4140
4198
|
import { existsSync as existsSync13, readFileSync as readFileSync18, statSync as statSync2 } from "fs";
|
|
4141
|
-
import { join as
|
|
4199
|
+
import { join as join22, dirname as dirname3, parse } from "path";
|
|
4142
4200
|
var NAMES = ["AGENTS.md", "CLAUDE.md", ".oriro/ORIRO.md"];
|
|
4143
4201
|
var MAX_BYTES = 32 * 1024;
|
|
4144
4202
|
var MAX_LEVELS = 24;
|
|
4145
4203
|
function isRoot(dir) {
|
|
4146
|
-
return existsSync13(
|
|
4204
|
+
return existsSync13(join22(dir, ".git")) || existsSync13(join22(dir, ".oriro"));
|
|
4147
4205
|
}
|
|
4148
4206
|
function discoverProjectInstructions(cwd) {
|
|
4149
4207
|
const chain = [];
|
|
@@ -4151,7 +4209,7 @@ function discoverProjectInstructions(cwd) {
|
|
|
4151
4209
|
const rootOfDrive = parse(cwd).root;
|
|
4152
4210
|
for (let i = 0; i < MAX_LEVELS; i++) {
|
|
4153
4211
|
for (const name of NAMES) {
|
|
4154
|
-
const p =
|
|
4212
|
+
const p = join22(dir, name);
|
|
4155
4213
|
try {
|
|
4156
4214
|
if (existsSync13(p) && statSync2(p).isFile()) {
|
|
4157
4215
|
let text = readFileSync18(p, "utf8");
|
|
@@ -4765,7 +4823,7 @@ async function comparePages(opts) {
|
|
|
4765
4823
|
|
|
4766
4824
|
// src/head/run.ts
|
|
4767
4825
|
import { writeFile } from "fs/promises";
|
|
4768
|
-
import { join as
|
|
4826
|
+
import { join as join23 } from "path";
|
|
4769
4827
|
|
|
4770
4828
|
// src/head/inspection-html.ts
|
|
4771
4829
|
var PRIORITY_COLOR = {
|
|
@@ -5267,7 +5325,7 @@ async function runInspect(target, competitors, opts = {}) {
|
|
|
5267
5325
|
const report = await comparePages({ targetUrl: target, competitorUrls: competitors.length ? competitors : [target] });
|
|
5268
5326
|
const files = [];
|
|
5269
5327
|
if (opts.html) {
|
|
5270
|
-
const path =
|
|
5328
|
+
const path = join23(opts.outDir ?? process.cwd(), `oriro-head-${hostSlug(target)}-inspect.html`);
|
|
5271
5329
|
await writeFile(path, buildInspectionHtml(report), "utf8");
|
|
5272
5330
|
files.push(path);
|
|
5273
5331
|
}
|
|
@@ -5283,7 +5341,7 @@ function parseHeadTargets(text, selfOrigin) {
|
|
|
5283
5341
|
async function runUrlToCode(url, opts = {}) {
|
|
5284
5342
|
try {
|
|
5285
5343
|
const res = await urlToCode(url, headModels(), { goal: opts.goal, stack: opts.stack });
|
|
5286
|
-
const codePath =
|
|
5344
|
+
const codePath = join23(opts.outDir ?? process.cwd(), `oriro-head-${hostSlug(url)}${extForStack(opts.stack)}`);
|
|
5287
5345
|
await writeFile(codePath, res.code, "utf8");
|
|
5288
5346
|
return { summary: `Reverse-engineered ${url} into clean code (${res.code.length} chars) \u2192 ${codePath}`, files: [codePath] };
|
|
5289
5347
|
} catch (e) {
|
|
@@ -5293,7 +5351,7 @@ async function runUrlToCode(url, opts = {}) {
|
|
|
5293
5351
|
async function runUrlToSpec(url, opts = {}) {
|
|
5294
5352
|
try {
|
|
5295
5353
|
const res = await urlToSpec(url, headModels(), { goal: opts.goal });
|
|
5296
|
-
const specPath =
|
|
5354
|
+
const specPath = join23(opts.outDir ?? process.cwd(), `oriro-head-${hostSlug(url)}.spec.yaml`);
|
|
5297
5355
|
await writeFile(specPath, res.spec, "utf8");
|
|
5298
5356
|
return { summary: `Reverse-engineered ${url} into a YAML build spec \u2192 ${specPath}`, files: [specPath] };
|
|
5299
5357
|
} catch (e) {
|
|
@@ -5305,7 +5363,7 @@ async function runCapture(urls, opts = {}) {
|
|
|
5305
5363
|
const { captureScreens: captureScreens2, buildScreenshotFlowHtml: buildScreenshotFlowHtml2 } = await Promise.resolve().then(() => (init_screenshot_flow(), screenshot_flow_exports));
|
|
5306
5364
|
const caps = await captureScreens2(urls, { video: opts.video });
|
|
5307
5365
|
const html = buildScreenshotFlowHtml2([{ name: "Captured screens", captures: caps }]);
|
|
5308
|
-
const flowPath =
|
|
5366
|
+
const flowPath = join23(opts.outDir ?? process.cwd(), "oriro-head-flow.html");
|
|
5309
5367
|
await writeFile(flowPath, html, "utf8");
|
|
5310
5368
|
const ok2 = caps.filter((c) => c.ok).length;
|
|
5311
5369
|
return { summary: `Captured ${ok2}/${caps.length} full-page screenshots \u2192 ${flowPath}`, files: [flowPath] };
|
|
@@ -5326,7 +5384,7 @@ async function runVideoToCode(videoPath, opts = {}) {
|
|
|
5326
5384
|
{ videoPath, frames, mimeType: mime, goal: opts.goal, stack: opts.stack },
|
|
5327
5385
|
headVideoModels()
|
|
5328
5386
|
);
|
|
5329
|
-
const codePath =
|
|
5387
|
+
const codePath = join23(opts.outDir ?? process.cwd(), `oriro-head-video${extForStack(opts.stack)}`);
|
|
5330
5388
|
await writeFile(codePath, res.code, "utf8");
|
|
5331
5389
|
return { summary: `Watched ${videoPath} \u2192 built code (${res.code.length} chars) \u2192 ${codePath}
|
|
5332
5390
|
(experimental on the free floor \u2014 add a vision-capable router for pixel-faithful results.)`, files: [codePath] };
|
|
@@ -5419,7 +5477,7 @@ function registerHead(pi) {
|
|
|
5419
5477
|
}
|
|
5420
5478
|
|
|
5421
5479
|
// src/orchestrate.ts
|
|
5422
|
-
import { createAgentSession, AuthStorage, ModelRegistry, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
5480
|
+
import { createAgentSession, AuthStorage, ModelRegistry, SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
|
|
5423
5481
|
import { Type as Type3 } from "typebox";
|
|
5424
5482
|
var MAX_AGENTS = 8;
|
|
5425
5483
|
var MAX_CONCURRENCY = 4;
|
|
@@ -5432,7 +5490,7 @@ async function runOnce(spec) {
|
|
|
5432
5490
|
model,
|
|
5433
5491
|
authStorage,
|
|
5434
5492
|
modelRegistry,
|
|
5435
|
-
sessionManager:
|
|
5493
|
+
sessionManager: SessionManager2.inMemory(),
|
|
5436
5494
|
noTools: "all"
|
|
5437
5495
|
});
|
|
5438
5496
|
let out = "";
|
|
@@ -5509,19 +5567,19 @@ import { Type as Type4 } from "typebox";
|
|
|
5509
5567
|
|
|
5510
5568
|
// src/agents/store.ts
|
|
5511
5569
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync19, writeFileSync as writeFileSync16, readdirSync as readdirSync2, rmSync as rmSync3, existsSync as existsSync14 } from "fs";
|
|
5512
|
-
import { join as
|
|
5570
|
+
import { join as join24 } from "path";
|
|
5513
5571
|
var SLUG = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
5514
5572
|
function isValidAgentName(name) {
|
|
5515
5573
|
return SLUG.test(name);
|
|
5516
5574
|
}
|
|
5517
5575
|
function agentsDir() {
|
|
5518
|
-
return
|
|
5576
|
+
return join24(oriroDir(), "agents");
|
|
5519
5577
|
}
|
|
5520
5578
|
function agentFile(name) {
|
|
5521
|
-
return
|
|
5579
|
+
return join24(agentsDir(), `${name}.json`);
|
|
5522
5580
|
}
|
|
5523
5581
|
function stateFile() {
|
|
5524
|
-
return
|
|
5582
|
+
return join24(agentsDir(), ".state.json");
|
|
5525
5583
|
}
|
|
5526
5584
|
function listAgents() {
|
|
5527
5585
|
const dir = agentsDir();
|
|
@@ -5530,7 +5588,7 @@ function listAgents() {
|
|
|
5530
5588
|
for (const f of readdirSync2(dir)) {
|
|
5531
5589
|
if (!f.endsWith(".json") || f.startsWith(".")) continue;
|
|
5532
5590
|
try {
|
|
5533
|
-
const def = JSON.parse(readFileSync19(
|
|
5591
|
+
const def = JSON.parse(readFileSync19(join24(dir, f), "utf8"));
|
|
5534
5592
|
if (def && typeof def.name === "string" && typeof def.task === "string") out.push(def);
|
|
5535
5593
|
} catch {
|
|
5536
5594
|
}
|
|
@@ -5843,9 +5901,9 @@ function registerToolList(pi, serverName, client, tools, seen = /* @__PURE__ */
|
|
|
5843
5901
|
|
|
5844
5902
|
// src/connectors/custom.ts
|
|
5845
5903
|
import { readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "fs";
|
|
5846
|
-
import { join as
|
|
5904
|
+
import { join as join25 } from "path";
|
|
5847
5905
|
function file3() {
|
|
5848
|
-
return
|
|
5906
|
+
return join25(oriroDir(), "mcp-custom.json");
|
|
5849
5907
|
}
|
|
5850
5908
|
function readCustomServers() {
|
|
5851
5909
|
try {
|
|
@@ -5857,13 +5915,13 @@ function readCustomServers() {
|
|
|
5857
5915
|
}
|
|
5858
5916
|
function saveCustomServer(server) {
|
|
5859
5917
|
const rest = readCustomServers().filter((s) => s.name.toLowerCase() !== server.name.toLowerCase());
|
|
5860
|
-
writeFileSync17(
|
|
5918
|
+
writeFileSync17(join25(ensureOriroDir(), "mcp-custom.json"), JSON.stringify([...rest, server], null, 2), "utf8");
|
|
5861
5919
|
}
|
|
5862
5920
|
function removeCustomServer(name) {
|
|
5863
5921
|
const before = readCustomServers();
|
|
5864
5922
|
const after = before.filter((s) => s.name.toLowerCase() !== name.toLowerCase());
|
|
5865
5923
|
if (after.length === before.length) return false;
|
|
5866
|
-
writeFileSync17(
|
|
5924
|
+
writeFileSync17(join25(ensureOriroDir(), "mcp-custom.json"), JSON.stringify(after, null, 2), "utf8");
|
|
5867
5925
|
return true;
|
|
5868
5926
|
}
|
|
5869
5927
|
function trustedServerNames() {
|
|
@@ -5928,16 +5986,17 @@ async function assembleOriroSession(opts = {}) {
|
|
|
5928
5986
|
]
|
|
5929
5987
|
});
|
|
5930
5988
|
await resourceLoader.reload();
|
|
5989
|
+
const { sm, note: sessionNote } = await resolveSessionManager(cwd, opts.resume);
|
|
5931
5990
|
const { session, extensionsResult } = await createAgentSession2({
|
|
5932
5991
|
model,
|
|
5933
5992
|
authStorage,
|
|
5934
5993
|
modelRegistry,
|
|
5935
5994
|
settingsManager,
|
|
5936
|
-
sessionManager:
|
|
5995
|
+
sessionManager: sm,
|
|
5937
5996
|
resourceLoader
|
|
5938
5997
|
});
|
|
5939
5998
|
attachScribe(session);
|
|
5940
|
-
return { session, extensionsResult };
|
|
5999
|
+
return { session, extensionsResult, sessionNote };
|
|
5941
6000
|
}
|
|
5942
6001
|
|
|
5943
6002
|
// src/language/nllb-translator.ts
|
|
@@ -6403,6 +6462,178 @@ async function handleCompact(session, cmd) {
|
|
|
6403
6462
|
}
|
|
6404
6463
|
}
|
|
6405
6464
|
|
|
6465
|
+
// src/context/init-agents.ts
|
|
6466
|
+
import { existsSync as existsSync17, readFileSync as readFileSync21, readdirSync as readdirSync3, statSync as statSync3, writeFileSync as writeFileSync19 } from "fs";
|
|
6467
|
+
import { join as join26, basename } from "path";
|
|
6468
|
+
var CODE_EXT = {
|
|
6469
|
+
ts: "TypeScript",
|
|
6470
|
+
tsx: "TypeScript",
|
|
6471
|
+
js: "JavaScript",
|
|
6472
|
+
jsx: "JavaScript",
|
|
6473
|
+
mjs: "JavaScript",
|
|
6474
|
+
py: "Python",
|
|
6475
|
+
go: "Go",
|
|
6476
|
+
rs: "Rust",
|
|
6477
|
+
java: "Java",
|
|
6478
|
+
kt: "Kotlin",
|
|
6479
|
+
rb: "Ruby",
|
|
6480
|
+
php: "PHP",
|
|
6481
|
+
c: "C",
|
|
6482
|
+
h: "C",
|
|
6483
|
+
cpp: "C++",
|
|
6484
|
+
cc: "C++",
|
|
6485
|
+
cs: "C#",
|
|
6486
|
+
swift: "Swift",
|
|
6487
|
+
sh: "Shell",
|
|
6488
|
+
sql: "SQL"
|
|
6489
|
+
};
|
|
6490
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "target", "__pycache__", ".venv", "venv", ".oriro"]);
|
|
6491
|
+
function readJson(p) {
|
|
6492
|
+
try {
|
|
6493
|
+
return JSON.parse(readFileSync21(p, "utf8"));
|
|
6494
|
+
} catch {
|
|
6495
|
+
return {};
|
|
6496
|
+
}
|
|
6497
|
+
}
|
|
6498
|
+
function detectProject(cwd) {
|
|
6499
|
+
const facts = { name: basename(cwd) || "project", languages: [], commands: [], topDirs: [] };
|
|
6500
|
+
const pkgPath = join26(cwd, "package.json");
|
|
6501
|
+
if (existsSync17(pkgPath)) {
|
|
6502
|
+
const pkg = readJson(pkgPath);
|
|
6503
|
+
if (typeof pkg.name === "string" && pkg.name) facts.name = pkg.name;
|
|
6504
|
+
if (typeof pkg.description === "string" && pkg.description) facts.description = pkg.description;
|
|
6505
|
+
const scripts = pkg.scripts && typeof pkg.scripts === "object" ? pkg.scripts : {};
|
|
6506
|
+
for (const key of ["dev", "build", "test", "lint", "start"]) {
|
|
6507
|
+
if (scripts[key]) facts.commands.push({ label: key, cmd: `npm run ${key}` });
|
|
6508
|
+
}
|
|
6509
|
+
} else if (existsSync17(join26(cwd, "pyproject.toml")) || existsSync17(join26(cwd, "requirements.txt"))) {
|
|
6510
|
+
if (!facts.description) facts.description = "Python project";
|
|
6511
|
+
} else if (existsSync17(join26(cwd, "Cargo.toml"))) {
|
|
6512
|
+
facts.commands.push({ label: "build", cmd: "cargo build" }, { label: "test", cmd: "cargo test" });
|
|
6513
|
+
} else if (existsSync17(join26(cwd, "go.mod"))) {
|
|
6514
|
+
facts.commands.push({ label: "build", cmd: "go build ./..." }, { label: "test", cmd: "go test ./..." });
|
|
6515
|
+
}
|
|
6516
|
+
const langCount = /* @__PURE__ */ new Map();
|
|
6517
|
+
const tallyExt = (file6) => {
|
|
6518
|
+
const ext = file6.split(".").pop()?.toLowerCase();
|
|
6519
|
+
const lang = ext && CODE_EXT[ext];
|
|
6520
|
+
if (lang) langCount.set(lang, (langCount.get(lang) ?? 0) + 1);
|
|
6521
|
+
};
|
|
6522
|
+
let entries = [];
|
|
6523
|
+
try {
|
|
6524
|
+
entries = readdirSync3(cwd);
|
|
6525
|
+
} catch {
|
|
6526
|
+
}
|
|
6527
|
+
for (const e of entries) {
|
|
6528
|
+
const full = join26(cwd, e);
|
|
6529
|
+
let isDir = false;
|
|
6530
|
+
try {
|
|
6531
|
+
isDir = statSync3(full).isDirectory();
|
|
6532
|
+
} catch {
|
|
6533
|
+
continue;
|
|
6534
|
+
}
|
|
6535
|
+
if (isDir) {
|
|
6536
|
+
if (SKIP_DIRS.has(e) || e.startsWith(".")) continue;
|
|
6537
|
+
facts.topDirs.push(e);
|
|
6538
|
+
try {
|
|
6539
|
+
for (const f of readdirSync3(full)) {
|
|
6540
|
+
try {
|
|
6541
|
+
if (statSync3(join26(full, f)).isFile()) tallyExt(f);
|
|
6542
|
+
} catch {
|
|
6543
|
+
}
|
|
6544
|
+
}
|
|
6545
|
+
} catch {
|
|
6546
|
+
}
|
|
6547
|
+
} else {
|
|
6548
|
+
tallyExt(e);
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
facts.languages = [...langCount.entries()].sort((a, b) => b[1] - a[1]).map(([l]) => l);
|
|
6552
|
+
facts.topDirs.sort();
|
|
6553
|
+
return facts;
|
|
6554
|
+
}
|
|
6555
|
+
function generateAgentsMd(cwd) {
|
|
6556
|
+
const f = detectProject(cwd);
|
|
6557
|
+
const lines = [];
|
|
6558
|
+
lines.push(`# ${f.name}`, "");
|
|
6559
|
+
lines.push(f.description ?? "_One-line description of what this project does._", "");
|
|
6560
|
+
lines.push("## Stack");
|
|
6561
|
+
lines.push(f.languages.length ? `- Languages: ${f.languages.join(", ")}` : "- Languages: _add the main languages_");
|
|
6562
|
+
if (f.topDirs.length) lines.push(`- Layout: ${f.topDirs.map((d) => `\`${d}/\``).join(", ")}`);
|
|
6563
|
+
lines.push("");
|
|
6564
|
+
lines.push("## Commands");
|
|
6565
|
+
if (f.commands.length) for (const c of f.commands) lines.push(`- ${c.label}: \`${c.cmd}\``);
|
|
6566
|
+
else lines.push("- _add build/test/run commands here_");
|
|
6567
|
+
lines.push("");
|
|
6568
|
+
lines.push("## Conventions");
|
|
6569
|
+
lines.push("- _House rules for this repo: style, patterns to follow, things never to touch._");
|
|
6570
|
+
lines.push("- _ORIRO reads this file automatically each session \u2014 keep it short and current._");
|
|
6571
|
+
lines.push("");
|
|
6572
|
+
return lines.join("\n");
|
|
6573
|
+
}
|
|
6574
|
+
function writeAgentsMd(cwd = process.cwd(), force = false) {
|
|
6575
|
+
const path = join26(cwd, "AGENTS.md");
|
|
6576
|
+
const facts = detectProject(cwd);
|
|
6577
|
+
if (existsSync17(path) && !force) return { path, created: false, facts };
|
|
6578
|
+
writeFileSync19(path, generateAgentsMd(cwd), "utf8");
|
|
6579
|
+
return { path, created: true, facts };
|
|
6580
|
+
}
|
|
6581
|
+
|
|
6582
|
+
// src/repl-ui/slash-init.ts
|
|
6583
|
+
function isInitSlash(cmd) {
|
|
6584
|
+
return /^\/init(\s|$)/i.test(cmd.trim());
|
|
6585
|
+
}
|
|
6586
|
+
function handleInit(cmd, cwd = process.cwd()) {
|
|
6587
|
+
const force = /(^|\s)--force(\s|$)/i.test(cmd);
|
|
6588
|
+
let res;
|
|
6589
|
+
try {
|
|
6590
|
+
res = writeAgentsMd(cwd, force);
|
|
6591
|
+
} catch (e) {
|
|
6592
|
+
return [` ${fgHex(PALETTE.error, "init failed")}: ${dim(e instanceof Error ? e.message : String(e))}`];
|
|
6593
|
+
}
|
|
6594
|
+
const lines = [];
|
|
6595
|
+
if (!res.created) {
|
|
6596
|
+
lines.push(` ${dim("AGENTS.md already exists")} ${accent(res.path)} ${dim("\u2014 use /init --force to overwrite.")}`);
|
|
6597
|
+
return lines;
|
|
6598
|
+
}
|
|
6599
|
+
const f = res.facts;
|
|
6600
|
+
lines.push(` ${fgHex(PALETTE.success, "\u2713 wrote")} ${accent(res.path)}`);
|
|
6601
|
+
lines.push(dim(` detected: ${f.languages.length ? f.languages.join(", ") : "no languages"}${f.commands.length ? ` \xB7 ${f.commands.length} command${f.commands.length === 1 ? "" : "s"}` : ""}${f.topDirs.length ? ` \xB7 ${f.topDirs.length} dir${f.topDirs.length === 1 ? "" : "s"}` : ""}`));
|
|
6602
|
+
lines.push(dim(" edit it to add house rules \u2014 ORIRO reads it automatically each session."));
|
|
6603
|
+
return lines;
|
|
6604
|
+
}
|
|
6605
|
+
|
|
6606
|
+
// src/repl-ui/slash-sessions.ts
|
|
6607
|
+
function isSessionsSlash(cmd) {
|
|
6608
|
+
return /^\/sessions?(\s|$)/i.test(cmd.trim());
|
|
6609
|
+
}
|
|
6610
|
+
function isUndoSlash(cmd) {
|
|
6611
|
+
return /^\/undo(\s|$)/i.test(cmd.trim());
|
|
6612
|
+
}
|
|
6613
|
+
async function handleSessions() {
|
|
6614
|
+
try {
|
|
6615
|
+
return formatSessionList(await listSessions());
|
|
6616
|
+
} catch (e) {
|
|
6617
|
+
return [` ${fgHex(PALETTE.error, "sessions failed")}: ${dim(e instanceof Error ? e.message : String(e))}`];
|
|
6618
|
+
}
|
|
6619
|
+
}
|
|
6620
|
+
async function handleUndo(session) {
|
|
6621
|
+
try {
|
|
6622
|
+
const turns2 = session.getUserMessagesForForking();
|
|
6623
|
+
if (turns2.length < 2) {
|
|
6624
|
+
return [dim(" nothing to undo \u2014 this is the first turn of the session.")];
|
|
6625
|
+
}
|
|
6626
|
+
const target = turns2[turns2.length - 2];
|
|
6627
|
+
if (!target) return [dim(" nothing to undo.")];
|
|
6628
|
+
const res = await session.navigateTree(target.entryId, { label: "undo" });
|
|
6629
|
+
if (res.cancelled) return [dim(" undo cancelled.")];
|
|
6630
|
+
const preview = target.text.replace(/\s+/g, " ").trim().slice(0, 48);
|
|
6631
|
+
return [` ${fgHex(PALETTE.success, "\u21BA undone")} ${dim("\u2014 rewound to:")} ${accent(preview || "(prev turn)")}`];
|
|
6632
|
+
} catch (e) {
|
|
6633
|
+
return [` ${fgHex(PALETTE.error, "undo failed")}: ${dim(e instanceof Error ? e.message : String(e))}`];
|
|
6634
|
+
}
|
|
6635
|
+
}
|
|
6636
|
+
|
|
6406
6637
|
// src/repl-ui/tui-repl.ts
|
|
6407
6638
|
var editorTheme = {
|
|
6408
6639
|
borderColor: (s) => dim(s),
|
|
@@ -6485,7 +6716,8 @@ async function runTuiRepl(session) {
|
|
|
6485
6716
|
const help = [
|
|
6486
6717
|
" Just type to chat \u2014 ORIRO writes and runs code for you (keyless, free).",
|
|
6487
6718
|
` ${accent("/routers")} pool add\xB7rotate ${accent("/model")} <id\u2026> switch ${accent("/usage")} health ${accent("/trace")} tool+router activity ${accent("/compact")} free context`,
|
|
6488
|
-
` ${accent("/review")} artifacts
|
|
6719
|
+
` ${accent("/review")} artifacts ${accent("/save")} <n> [path] ${accent("/init")} AGENTS.md ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")}`,
|
|
6720
|
+
` ${accent("/sessions")} list saved ${accent("/undo")} rewind a turn ${dim("resume:")} ${accent("oriro -c")} / ${accent("oriro --resume <id>")}`,
|
|
6489
6721
|
` ${dim("Shift+Tab")} posture ${dim("Alt+Shift+T")} thinking ${accent("/help")} ${accent("/exit")}`
|
|
6490
6722
|
].join("\n");
|
|
6491
6723
|
chat.addChild(new Text(help, 0, 0));
|
|
@@ -6548,6 +6780,24 @@ async function runTuiRepl(session) {
|
|
|
6548
6780
|
})();
|
|
6549
6781
|
return;
|
|
6550
6782
|
}
|
|
6783
|
+
if (isInitSlash(slash)) {
|
|
6784
|
+
chat.addChild(new Text(handleInit(text).join("\n"), 0, 0));
|
|
6785
|
+
editor.setText("");
|
|
6786
|
+
tui.requestRender();
|
|
6787
|
+
return;
|
|
6788
|
+
}
|
|
6789
|
+
if (isSessionsSlash(slash) || isUndoSlash(slash)) {
|
|
6790
|
+
editor.setText("");
|
|
6791
|
+
const pending = new Text(dim(" \u2026"), 0, 0);
|
|
6792
|
+
chat.addChild(pending);
|
|
6793
|
+
tui.requestRender();
|
|
6794
|
+
void (async () => {
|
|
6795
|
+
const lines = isUndoSlash(slash) ? await handleUndo(session) : await handleSessions();
|
|
6796
|
+
pending.setText(lines.join("\n"));
|
|
6797
|
+
tui.requestRender();
|
|
6798
|
+
})();
|
|
6799
|
+
return;
|
|
6800
|
+
}
|
|
6551
6801
|
if (slash === "/voice") {
|
|
6552
6802
|
editor.setText("");
|
|
6553
6803
|
const status = new Text(dim(" \u{1F399} listening\u2026 (needs ffmpeg + the transformers voice peer)"), 0, 0);
|
|
@@ -6639,8 +6889,8 @@ ${english}`;
|
|
|
6639
6889
|
// src/voice/mic.ts
|
|
6640
6890
|
import { spawn as spawn3 } from "child_process";
|
|
6641
6891
|
import { tmpdir as tmpdir3 } from "os";
|
|
6642
|
-
import { join as
|
|
6643
|
-
import { existsSync as
|
|
6892
|
+
import { join as join27 } from "path";
|
|
6893
|
+
import { existsSync as existsSync18, statSync as statSync4 } from "fs";
|
|
6644
6894
|
function recorders(outFile, seconds) {
|
|
6645
6895
|
const dur = String(seconds);
|
|
6646
6896
|
if (process.platform === "darwin") {
|
|
@@ -6661,12 +6911,12 @@ function recorders(outFile, seconds) {
|
|
|
6661
6911
|
];
|
|
6662
6912
|
}
|
|
6663
6913
|
async function recordMic(seconds = 6) {
|
|
6664
|
-
const outFile =
|
|
6914
|
+
const outFile = join27(tmpdir3(), `oriro-voice-${process.pid}-${seconds}.wav`);
|
|
6665
6915
|
for (const r of recorders(outFile, seconds)) {
|
|
6666
6916
|
const okFile = await new Promise((resolve3) => {
|
|
6667
6917
|
const child = spawn3(r.cmd, r.args, { stdio: "ignore" });
|
|
6668
6918
|
child.on("error", () => resolve3(false));
|
|
6669
|
-
child.on("close", (code) => resolve3(code === 0 &&
|
|
6919
|
+
child.on("close", (code) => resolve3(code === 0 && existsSync18(outFile) && statSync4(outFile).size > 44));
|
|
6670
6920
|
});
|
|
6671
6921
|
if (okFile) return outFile;
|
|
6672
6922
|
}
|
|
@@ -6731,8 +6981,10 @@ function replHelp() {
|
|
|
6731
6981
|
${dim("Just type to chat; ORIRO writes and runs code for you (keyless, free).")}
|
|
6732
6982
|
|
|
6733
6983
|
${dim("Models & routers")} ${accent("/routers")} list\xB7add\xB7rotate the racing pool ${accent("/model")} <id\u2026> switch
|
|
6734
|
-
${dim("This session")} ${accent("/usage")} pool health & turns ${accent("/trace")}
|
|
6984
|
+
${dim("This session")} ${accent("/usage")} pool health & turns ${accent("/trace")} activity ${accent("/compact")} free context ${accent("/undo")} rewind a turn
|
|
6985
|
+
${dim("Continuity")} ${accent("/sessions")} list saved sessions ${dim("resume:")} ${accent("oriro -c")} ${dim("or")} ${accent("oriro --resume <id>")}
|
|
6735
6986
|
${dim("Artifacts")} ${accent("/review")} code/SVG from the last reply ${accent("/save")} <n> [path] write one
|
|
6987
|
+
${dim("Project")} ${accent("/init")} write a starter AGENTS.md ORIRO reads each session
|
|
6736
6988
|
${dim("Capabilities")} ${accent("/skills")} ${accent("/connectors")} ${accent("/voice")} speak a turn
|
|
6737
6989
|
${dim("General")} ${accent("/help")} this ${accent("/exit")} / ${accent("/quit")} leave ${dim("(Ctrl-D / Ctrl-C also exit)")}
|
|
6738
6990
|
|
|
@@ -6740,10 +6992,12 @@ function replHelp() {
|
|
|
6740
6992
|
|
|
6741
6993
|
`;
|
|
6742
6994
|
}
|
|
6743
|
-
async function runRepl() {
|
|
6995
|
+
async function runRepl(opts = {}) {
|
|
6744
6996
|
if (isFirstRun()) await runOnboarding();
|
|
6745
6997
|
else stdout7.write(banner());
|
|
6746
|
-
const { session } = await assembleOriroSession();
|
|
6998
|
+
const { session, sessionNote } = await assembleOriroSession({ resume: opts.resume });
|
|
6999
|
+
if (sessionNote) stdout7.write(` ${dim(sessionNote)}
|
|
7000
|
+
`);
|
|
6747
7001
|
setupVoiceInput();
|
|
6748
7002
|
if (stdin6.isTTY && stdout7.isTTY) {
|
|
6749
7003
|
await runTuiRepl(session);
|
|
@@ -6812,6 +7066,18 @@ async function runReadlineRepl(session) {
|
|
|
6812
7066
|
stdout7.write((await handleCompact(session, line)).join("\n") + "\n");
|
|
6813
7067
|
continue;
|
|
6814
7068
|
}
|
|
7069
|
+
if (isInitSlash(slash)) {
|
|
7070
|
+
stdout7.write(handleInit(line).join("\n") + "\n");
|
|
7071
|
+
continue;
|
|
7072
|
+
}
|
|
7073
|
+
if (isSessionsSlash(slash)) {
|
|
7074
|
+
stdout7.write((await handleSessions()).join("\n") + "\n");
|
|
7075
|
+
continue;
|
|
7076
|
+
}
|
|
7077
|
+
if (isUndoSlash(slash)) {
|
|
7078
|
+
stdout7.write((await handleUndo(session)).join("\n") + "\n");
|
|
7079
|
+
continue;
|
|
7080
|
+
}
|
|
6815
7081
|
if (isArtifactSlash(slash)) {
|
|
6816
7082
|
stdout7.write(handleArtifactSlash(line).join("\n") + "\n");
|
|
6817
7083
|
continue;
|
|
@@ -6939,8 +7205,8 @@ async function confirmDestructive(what, opts = {}) {
|
|
|
6939
7205
|
}
|
|
6940
7206
|
|
|
6941
7207
|
// src/config/store.ts
|
|
6942
|
-
import { readFileSync as
|
|
6943
|
-
import { join as
|
|
7208
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync20, mkdirSync as mkdirSync16 } from "fs";
|
|
7209
|
+
import { join as join28 } from "path";
|
|
6944
7210
|
var KEYS = {
|
|
6945
7211
|
output: {
|
|
6946
7212
|
desc: "default output format for list commands: text | json | csv",
|
|
@@ -6962,13 +7228,13 @@ function validateConfig(key, value) {
|
|
|
6962
7228
|
return KEYS[key].validate?.(value) ?? null;
|
|
6963
7229
|
}
|
|
6964
7230
|
function file4() {
|
|
6965
|
-
return
|
|
7231
|
+
return join28(oriroDir(), "config.json");
|
|
6966
7232
|
}
|
|
6967
7233
|
var cache = null;
|
|
6968
7234
|
function readAll() {
|
|
6969
7235
|
if (cache) return cache;
|
|
6970
7236
|
try {
|
|
6971
|
-
const v = JSON.parse(
|
|
7237
|
+
const v = JSON.parse(readFileSync22(file4(), "utf8"));
|
|
6972
7238
|
cache = v && typeof v === "object" ? v : {};
|
|
6973
7239
|
} catch {
|
|
6974
7240
|
cache = {};
|
|
@@ -6984,7 +7250,7 @@ function configAll() {
|
|
|
6984
7250
|
function configSet(key, value) {
|
|
6985
7251
|
const all = { ...readAll(), [key]: value };
|
|
6986
7252
|
mkdirSync16(oriroDir(), { recursive: true });
|
|
6987
|
-
|
|
7253
|
+
writeFileSync20(file4(), JSON.stringify(all, null, 2), "utf8");
|
|
6988
7254
|
cache = all;
|
|
6989
7255
|
}
|
|
6990
7256
|
function configUnset(key) {
|
|
@@ -6992,7 +7258,7 @@ function configUnset(key) {
|
|
|
6992
7258
|
if (!(key in all)) return false;
|
|
6993
7259
|
const rest = { ...all };
|
|
6994
7260
|
delete rest[key];
|
|
6995
|
-
|
|
7261
|
+
writeFileSync20(file4(), JSON.stringify(rest, null, 2), "utf8");
|
|
6996
7262
|
cache = rest;
|
|
6997
7263
|
return true;
|
|
6998
7264
|
}
|
|
@@ -7046,6 +7312,27 @@ function outputError(opts) {
|
|
|
7046
7312
|
return f === "json" || f === "csv" || f === "text" ? null : `invalid --output '${opts.output}' \u2014 use text | json | csv`;
|
|
7047
7313
|
}
|
|
7048
7314
|
|
|
7315
|
+
// src/commands/sessions.ts
|
|
7316
|
+
function registerSessionsCommand(program2) {
|
|
7317
|
+
program2.command("sessions").description("list your saved chat sessions (resume with `oriro -c` or `oriro --resume <id>`)").option("-o, --output <fmt>", "output format: text (default) | json | csv").option("-q, --query <expr>", "filter/select: 'field', 'field=value', or 'field=value:selectField'").action(async (opts) => {
|
|
7318
|
+
const oerr = outputError(opts);
|
|
7319
|
+
if (oerr) die(oerr);
|
|
7320
|
+
const infos = await listSessions();
|
|
7321
|
+
if (isMachineOutput(opts) || opts.query) {
|
|
7322
|
+
process.stdout.write(
|
|
7323
|
+
renderList2(sessionRows(infos), {
|
|
7324
|
+
output: opts.output,
|
|
7325
|
+
query: opts.query,
|
|
7326
|
+
columns: ["id", "messages", "modified", "first", "cwd"]
|
|
7327
|
+
}) + "\n"
|
|
7328
|
+
);
|
|
7329
|
+
return;
|
|
7330
|
+
}
|
|
7331
|
+
heading("Sessions");
|
|
7332
|
+
process.stdout.write(formatSessionList(infos).join("\n") + "\n");
|
|
7333
|
+
});
|
|
7334
|
+
}
|
|
7335
|
+
|
|
7049
7336
|
// src/commands/routers.ts
|
|
7050
7337
|
function registerRoutersCommand(program2) {
|
|
7051
7338
|
const routers = program2.command("routers").description("manage the free-router pool the model runs on");
|
|
@@ -7135,10 +7422,10 @@ function registerRoutersCommand(program2) {
|
|
|
7135
7422
|
}
|
|
7136
7423
|
|
|
7137
7424
|
// src/commands/scribe.ts
|
|
7138
|
-
import { readFileSync as
|
|
7425
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
7139
7426
|
|
|
7140
7427
|
// src/scribe/transcript.ts
|
|
7141
|
-
import { existsSync as
|
|
7428
|
+
import { existsSync as existsSync20, readFileSync as readFileSync23 } from "fs";
|
|
7142
7429
|
function parseHookStdin(raw) {
|
|
7143
7430
|
try {
|
|
7144
7431
|
const j = JSON.parse(raw);
|
|
@@ -7171,8 +7458,8 @@ function isHumanUser(e) {
|
|
|
7171
7458
|
}
|
|
7172
7459
|
var FILE_KEYS = ["file_path", "path", "notebook_path", "filePath"];
|
|
7173
7460
|
function lastTurnFromTranscript(path) {
|
|
7174
|
-
if (!
|
|
7175
|
-
const raw =
|
|
7461
|
+
if (!existsSync20(path)) return null;
|
|
7462
|
+
const raw = readFileSync23(path, "utf8");
|
|
7176
7463
|
const entries = [];
|
|
7177
7464
|
for (const line of raw.split("\n")) {
|
|
7178
7465
|
if (!line.trim()) continue;
|
|
@@ -7233,7 +7520,7 @@ function lastTurnFromTranscript(path) {
|
|
|
7233
7520
|
// src/commands/scribe.ts
|
|
7234
7521
|
function readStdin() {
|
|
7235
7522
|
try {
|
|
7236
|
-
return
|
|
7523
|
+
return readFileSync24(0, "utf8");
|
|
7237
7524
|
} catch {
|
|
7238
7525
|
return "";
|
|
7239
7526
|
}
|
|
@@ -7543,14 +7830,14 @@ function registerConnectorsCommand(program2) {
|
|
|
7543
7830
|
}
|
|
7544
7831
|
|
|
7545
7832
|
// src/channels/config.ts
|
|
7546
|
-
import { readFileSync as
|
|
7547
|
-
import { join as
|
|
7833
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync21 } from "fs";
|
|
7834
|
+
import { join as join29 } from "path";
|
|
7548
7835
|
function file5() {
|
|
7549
|
-
return
|
|
7836
|
+
return join29(oriroDir(), "channels.json");
|
|
7550
7837
|
}
|
|
7551
7838
|
function readChannels() {
|
|
7552
7839
|
try {
|
|
7553
|
-
const v = JSON.parse(
|
|
7840
|
+
const v = JSON.parse(readFileSync25(file5(), "utf8"));
|
|
7554
7841
|
return Array.isArray(v) ? v : [];
|
|
7555
7842
|
} catch {
|
|
7556
7843
|
return [];
|
|
@@ -7559,10 +7846,10 @@ function readChannels() {
|
|
|
7559
7846
|
function saveChannel(cfg) {
|
|
7560
7847
|
const all = readChannels().filter((c) => c.kind !== cfg.kind);
|
|
7561
7848
|
all.push(cfg);
|
|
7562
|
-
|
|
7849
|
+
writeFileSync21(join29(ensureOriroDir(), "channels.json"), JSON.stringify(all, null, 2), "utf8");
|
|
7563
7850
|
}
|
|
7564
7851
|
function removeChannel(kind) {
|
|
7565
|
-
|
|
7852
|
+
writeFileSync21(join29(ensureOriroDir(), "channels.json"), JSON.stringify(readChannels().filter((c) => c.kind !== kind), null, 2), "utf8");
|
|
7566
7853
|
}
|
|
7567
7854
|
|
|
7568
7855
|
// src/channels/telegram.ts
|
|
@@ -7679,9 +7966,9 @@ async function startDiscord(token) {
|
|
|
7679
7966
|
}
|
|
7680
7967
|
|
|
7681
7968
|
// src/channels/whatsapp.ts
|
|
7682
|
-
import { join as
|
|
7969
|
+
import { join as join30 } from "path";
|
|
7683
7970
|
function whatsappAuthDir() {
|
|
7684
|
-
return
|
|
7971
|
+
return join30(oriroDir(), "whatsapp-auth");
|
|
7685
7972
|
}
|
|
7686
7973
|
async function startWhatsApp() {
|
|
7687
7974
|
let baileys;
|
|
@@ -7799,8 +8086,8 @@ function registerChannelsCommand(program2) {
|
|
|
7799
8086
|
}
|
|
7800
8087
|
|
|
7801
8088
|
// src/commands/skills.ts
|
|
7802
|
-
import { existsSync as
|
|
7803
|
-
import { resolve as resolve2, join as
|
|
8089
|
+
import { existsSync as existsSync21, statSync as statSync5, mkdirSync as mkdirSync17, cpSync, rmSync as rmSync4 } from "fs";
|
|
8090
|
+
import { resolve as resolve2, join as join31, basename as basename2, dirname as dirname4 } from "path";
|
|
7804
8091
|
function registerSkillsCommand(program2) {
|
|
7805
8092
|
const skills = program2.command("skills").description("the ORIRO skill library \u2014 bundled + your own");
|
|
7806
8093
|
skills.command("list").description("show CORE / TAIL skill counts (use --all to list names)").option("-a, --all", "list every skill name").option("-o, --output <fmt>", "output format: text (default) | json | csv").option("-q, --query <expr>", "filter/select: 'field', 'field=value', or 'field=value:selectField'").action(async (opts) => {
|
|
@@ -7832,28 +8119,28 @@ function registerSkillsCommand(program2) {
|
|
|
7832
8119
|
});
|
|
7833
8120
|
skills.command("add <path>").description("add your own skill \u2014 a folder containing SKILL.md, or a SKILL.md file").action((p) => {
|
|
7834
8121
|
const src = resolve2(p);
|
|
7835
|
-
if (!
|
|
8122
|
+
if (!existsSync21(src)) die(`not found: ${src}`);
|
|
7836
8123
|
const dest = userSkillsDir();
|
|
7837
8124
|
mkdirSync17(dest, { recursive: true });
|
|
7838
|
-
const st =
|
|
8125
|
+
const st = statSync5(src);
|
|
7839
8126
|
if (st.isDirectory()) {
|
|
7840
|
-
if (!
|
|
7841
|
-
const name =
|
|
7842
|
-
cpSync(src,
|
|
7843
|
-
ok(`added skill ${accent(name)} \u2192 ${
|
|
7844
|
-
} else if (
|
|
7845
|
-
const name =
|
|
7846
|
-
mkdirSync17(
|
|
7847
|
-
cpSync(src,
|
|
7848
|
-
ok(`added skill ${accent(name)} \u2192 ${
|
|
8127
|
+
if (!existsSync21(join31(src, "SKILL.md"))) die(`no SKILL.md in ${src} \u2014 a skill folder must contain SKILL.md`);
|
|
8128
|
+
const name = basename2(src);
|
|
8129
|
+
cpSync(src, join31(dest, name), { recursive: true });
|
|
8130
|
+
ok(`added skill ${accent(name)} \u2192 ${join31(dest, name)}`);
|
|
8131
|
+
} else if (basename2(src).toLowerCase() === "skill.md") {
|
|
8132
|
+
const name = basename2(dirname4(src)) || "custom-skill";
|
|
8133
|
+
mkdirSync17(join31(dest, name), { recursive: true });
|
|
8134
|
+
cpSync(src, join31(dest, name, "SKILL.md"));
|
|
8135
|
+
ok(`added skill ${accent(name)} \u2192 ${join31(dest, name)}`);
|
|
7849
8136
|
} else {
|
|
7850
8137
|
die("expected a folder containing SKILL.md, or a SKILL.md file");
|
|
7851
8138
|
}
|
|
7852
8139
|
info("It loads on next launch \u2014 and is available in chat via /skill.");
|
|
7853
8140
|
});
|
|
7854
8141
|
skills.command("remove <name>").description("remove a skill you added").option("-f, --force", "skip the confirmation prompt").action(async (name, opts) => {
|
|
7855
|
-
const target =
|
|
7856
|
-
if (!
|
|
8142
|
+
const target = join31(userSkillsDir(), name);
|
|
8143
|
+
if (!existsSync21(target)) {
|
|
7857
8144
|
info(`'${name}' is not a user-added skill \u2014 nothing to remove`);
|
|
7858
8145
|
return;
|
|
7859
8146
|
}
|
|
@@ -8036,7 +8323,7 @@ function registerVoiceCommand(program2) {
|
|
|
8036
8323
|
}
|
|
8037
8324
|
|
|
8038
8325
|
// src/agents/catalog.ts
|
|
8039
|
-
import { readFileSync as
|
|
8326
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
8040
8327
|
function parseAgentDef(raw, now) {
|
|
8041
8328
|
if (!raw || typeof raw !== "object") return { ok: false, error: "not a JSON object" };
|
|
8042
8329
|
const o = raw;
|
|
@@ -8063,7 +8350,7 @@ async function fetchAgentSource(pathOrUrl) {
|
|
|
8063
8350
|
if (!res.ok) throw new Error(`fetch failed: HTTP ${res.status}`);
|
|
8064
8351
|
return await res.json();
|
|
8065
8352
|
}
|
|
8066
|
-
return JSON.parse(
|
|
8353
|
+
return JSON.parse(readFileSync26(pathOrUrl, "utf8"));
|
|
8067
8354
|
}
|
|
8068
8355
|
async function addAgentFromSource(pathOrUrl, now) {
|
|
8069
8356
|
let raw;
|
|
@@ -8441,7 +8728,7 @@ function registerConfigCommand(program2) {
|
|
|
8441
8728
|
|
|
8442
8729
|
// src/commands/setup.ts
|
|
8443
8730
|
import { rmSync as rmSync5 } from "fs";
|
|
8444
|
-
import { join as
|
|
8731
|
+
import { join as join32 } from "path";
|
|
8445
8732
|
import { stdin as stdin12, stdout as stdout11 } from "process";
|
|
8446
8733
|
var MARKERS = [
|
|
8447
8734
|
"language.json",
|
|
@@ -8449,14 +8736,14 @@ var MARKERS = [
|
|
|
8449
8736
|
"skills-onboarded.json",
|
|
8450
8737
|
"connectors-onboarded.json",
|
|
8451
8738
|
"models-onboarded.json",
|
|
8452
|
-
|
|
8739
|
+
join32("routers", "onboarded.json")
|
|
8453
8740
|
];
|
|
8454
8741
|
function registerSetupCommand(program2) {
|
|
8455
8742
|
program2.command("setup").description("run the guided setup wizard (language \xB7 routers \xB7 connectors \xB7 skills \xB7 avatar)").option("--reset", "clear your settled choices and re-ask every step").action(async (opts) => {
|
|
8456
8743
|
if (opts.reset) {
|
|
8457
8744
|
for (const m of MARKERS) {
|
|
8458
8745
|
try {
|
|
8459
|
-
rmSync5(
|
|
8746
|
+
rmSync5(join32(oriroDir(), m), { force: true });
|
|
8460
8747
|
} catch {
|
|
8461
8748
|
}
|
|
8462
8749
|
}
|
|
@@ -8473,15 +8760,15 @@ function registerSetupCommand(program2) {
|
|
|
8473
8760
|
}
|
|
8474
8761
|
|
|
8475
8762
|
// src/commands/import.ts
|
|
8476
|
-
import { existsSync as
|
|
8477
|
-
import { join as
|
|
8763
|
+
import { existsSync as existsSync22, readFileSync as readFileSync27, readdirSync as readdirSync4, statSync as statSync6, cpSync as cpSync2, mkdirSync as mkdirSync18 } from "fs";
|
|
8764
|
+
import { join as join33, basename as basename3 } from "path";
|
|
8478
8765
|
function registerImportCommand(program2) {
|
|
8479
8766
|
const imp = program2.command("import").description("migrate from another CLI (MCP servers, skills)");
|
|
8480
8767
|
imp.command("mcp <file>").description("import MCP servers from a Claude-compatible mcp.json (Guardian-vetted)").action((file6) => {
|
|
8481
|
-
if (!
|
|
8768
|
+
if (!existsSync22(file6)) die(`no such file: ${file6}`);
|
|
8482
8769
|
let servers;
|
|
8483
8770
|
try {
|
|
8484
|
-
const j = JSON.parse(
|
|
8771
|
+
const j = JSON.parse(readFileSync27(file6, "utf8"));
|
|
8485
8772
|
servers = j.mcpServers ?? j.servers ?? {};
|
|
8486
8773
|
} catch (e) {
|
|
8487
8774
|
die(`could not parse ${file6}: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -8527,15 +8814,15 @@ function registerImportCommand(program2) {
|
|
|
8527
8814
|
info(`${imported} imported \xB7 ${blocked2} blocked${imported ? ` \u2014 they connect in-session; see \`oriro connectors custom\`` : ""}`);
|
|
8528
8815
|
});
|
|
8529
8816
|
imp.command("skills <dir>").description("import SKILL.md skill folders from another CLI's skills directory").action((dir) => {
|
|
8530
|
-
if (!
|
|
8817
|
+
if (!existsSync22(dir) || !statSync6(dir).isDirectory()) die(`no such directory: ${dir}`);
|
|
8531
8818
|
const dest = userSkillsDir();
|
|
8532
8819
|
mkdirSync18(dest, { recursive: true });
|
|
8533
8820
|
heading("Import skills");
|
|
8534
|
-
const sources =
|
|
8821
|
+
const sources = existsSync22(join33(dir, "SKILL.md")) ? [dir] : readdirSync4(dir).map((e) => join33(dir, e)).filter((p) => statSync6(p).isDirectory() && existsSync22(join33(p, "SKILL.md")));
|
|
8535
8822
|
let n = 0;
|
|
8536
8823
|
for (const src of sources) {
|
|
8537
|
-
cpSync2(src,
|
|
8538
|
-
process.stdout.write(` ${fgHex(PALETTE.success, "\u2713")} ${accent(
|
|
8824
|
+
cpSync2(src, join33(dest, basename3(src)), { recursive: true });
|
|
8825
|
+
process.stdout.write(` ${fgHex(PALETTE.success, "\u2713")} ${accent(basename3(src))}
|
|
8539
8826
|
`);
|
|
8540
8827
|
n++;
|
|
8541
8828
|
}
|
|
@@ -8595,7 +8882,7 @@ function enableHelpOnError(program2) {
|
|
|
8595
8882
|
// src/cli.ts
|
|
8596
8883
|
var version = createRequire(import.meta.url)("../package.json").version;
|
|
8597
8884
|
var program = new Command();
|
|
8598
|
-
program.name("oriro").description("ORIRO \u2014 a free, on-device-friendly terminal AI agent.").version(version, "-v, --version").option("-p, --print <prompt>", "headless one-shot: run a single prompt, print the answer, exit (CI-friendly)").option("--output-format <fmt>", "with --print: text | json | stream-json", "text").action(async (options, command) => {
|
|
8885
|
+
program.name("oriro").description("ORIRO \u2014 a free, on-device-friendly terminal AI agent.").version(version, "-v, --version").option("-p, --print <prompt>", "headless one-shot: run a single prompt, print the answer, exit (CI-friendly)").option("--output-format <fmt>", "with --print: text | json | stream-json", "text").option("-c, --continue", "resume your most recent session in this folder").option("--resume <id>", "resume a specific saved session (id or unique prefix \u2014 see: oriro sessions)").option("--fork <id>", "start a new session branched from an existing one").option("--no-session", "don't save this session to disk (ephemeral)").action(async (options, command) => {
|
|
8599
8886
|
if (options.print !== void 0) {
|
|
8600
8887
|
const fmt = options.outputFormat ?? "text";
|
|
8601
8888
|
if (!isOutputFormatMode(fmt)) {
|
|
@@ -8622,8 +8909,15 @@ program.name("oriro").description("ORIRO \u2014 a free, on-device-friendly termi
|
|
|
8622
8909
|
process.exitCode = 1;
|
|
8623
8910
|
return;
|
|
8624
8911
|
}
|
|
8625
|
-
|
|
8912
|
+
const resume = {
|
|
8913
|
+
continue: options.continue,
|
|
8914
|
+
resumeId: options.resume,
|
|
8915
|
+
forkId: options.fork,
|
|
8916
|
+
ephemeral: options.session === false
|
|
8917
|
+
};
|
|
8918
|
+
await runRepl({ resume });
|
|
8626
8919
|
});
|
|
8920
|
+
registerSessionsCommand(program);
|
|
8627
8921
|
registerRoutersCommand(program);
|
|
8628
8922
|
registerScribeCommand(program);
|
|
8629
8923
|
registerConnectorsCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oriro/orirocli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.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": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dev": "tsx src/cli.ts",
|
|
24
24
|
"build": "tsup",
|
|
25
25
|
"typecheck": "tsc --noEmit",
|
|
26
|
-
"test:unit": "tsx scripts/test-tool-sanitize.ts && tsx scripts/test-guardian.ts && tsx scripts/test-scribe.ts && tsx scripts/test-race.ts && tsx scripts/test-weights.ts && tsx scripts/test-output.ts && tsx scripts/test-connectors.ts && tsx scripts/test-artifacts.ts && tsx scripts/test-project-md.ts && tsx scripts/test-compact.ts",
|
|
26
|
+
"test:unit": "tsx scripts/test-tool-sanitize.ts && tsx scripts/test-guardian.ts && tsx scripts/test-scribe.ts && tsx scripts/test-race.ts && tsx scripts/test-weights.ts && tsx scripts/test-output.ts && tsx scripts/test-connectors.ts && tsx scripts/test-artifacts.ts && tsx scripts/test-project-md.ts && tsx scripts/test-compact.ts && tsx scripts/test-init.ts && tsx scripts/test-sessions.ts",
|
|
27
27
|
"smoke": "npm run build && node scripts/smoke.mjs",
|
|
28
28
|
"prepublishOnly": "npm run build && npm run test:unit && node scripts/smoke.mjs && node scripts/prepublish-check.mjs",
|
|
29
29
|
"start": "node dist/cli.js"
|