@beunec/rivine 0.1.0
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/README.md +423 -0
- package/action.yml +165 -0
- package/dist/api/workspace.js +152 -0
- package/dist/auth/device.js +73 -0
- package/dist/auth/keychain.js +88 -0
- package/dist/auth/pkce.js +142 -0
- package/dist/auth/session.js +101 -0
- package/dist/config.js +37 -0
- package/dist/index.js +300 -0
- package/dist/mcp/config.js +73 -0
- package/dist/mcp/manager.js +154 -0
- package/dist/mcp/oauth.js +234 -0
- package/dist/mcp/relevance.js +74 -0
- package/dist/protocol/edit.js +247 -0
- package/dist/protocol/fsExecutor.js +471 -0
- package/dist/protocol/git.js +157 -0
- package/dist/protocol/permissions.js +210 -0
- package/dist/protocol/runner.js +325 -0
- package/dist/protocol/sessionKey.js +57 -0
- package/dist/protocol/shell.js +260 -0
- package/dist/protocol/snapshot.js +84 -0
- package/dist/protocol/stream.js +41 -0
- package/dist/runHeadless.js +140 -0
- package/dist/session.js +57 -0
- package/dist/ui/App.js +444 -0
- package/dist/ui/components.js +92 -0
- package/dist/ui/headless.js +130 -0
- package/dist/ui/render.js +109 -0
- package/dist/ui/slashCommands.js +57 -0
- package/dist/ui/theme.js +82 -0
- package/package.json +45 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Rivine Code CLI
|
|
3
|
+
* Licensed and owned by Beunec Technologies, Inc.
|
|
4
|
+
*
|
|
5
|
+
* https://aselius.beunec.com
|
|
6
|
+
*
|
|
7
|
+
* This software is proprietary. Use is governed by the licence agreement
|
|
8
|
+
* accompanying it; it is not open source and is not licensed for
|
|
9
|
+
* redistribution or derivative works.
|
|
10
|
+
*/
|
|
11
|
+
const FLUSH_MS = 42;
|
|
12
|
+
export class TokenBuffer {
|
|
13
|
+
onFlush;
|
|
14
|
+
pending = '';
|
|
15
|
+
timer = null;
|
|
16
|
+
constructor(onFlush) {
|
|
17
|
+
this.onFlush = onFlush;
|
|
18
|
+
}
|
|
19
|
+
push(text) {
|
|
20
|
+
if (!text)
|
|
21
|
+
return;
|
|
22
|
+
this.pending += text;
|
|
23
|
+
if (this.timer)
|
|
24
|
+
return;
|
|
25
|
+
this.timer = setTimeout(() => {
|
|
26
|
+
this.timer = null;
|
|
27
|
+
this.flush();
|
|
28
|
+
}, FLUSH_MS);
|
|
29
|
+
this.timer.unref?.();
|
|
30
|
+
}
|
|
31
|
+
flush() {
|
|
32
|
+
if (this.timer) {
|
|
33
|
+
clearTimeout(this.timer);
|
|
34
|
+
this.timer = null;
|
|
35
|
+
}
|
|
36
|
+
if (!this.pending)
|
|
37
|
+
return;
|
|
38
|
+
const text = this.pending;
|
|
39
|
+
this.pending = '';
|
|
40
|
+
this.onFlush(text);
|
|
41
|
+
}
|
|
42
|
+
get buffered() {
|
|
43
|
+
return this.pending;
|
|
44
|
+
}
|
|
45
|
+
dispose() {
|
|
46
|
+
if (this.timer)
|
|
47
|
+
clearTimeout(this.timer);
|
|
48
|
+
this.timer = null;
|
|
49
|
+
this.pending = '';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function terminalWidth() {
|
|
53
|
+
const columns = process.stdout.columns;
|
|
54
|
+
return Math.max(48, Math.min(columns && columns > 0 ? columns : 80, 120));
|
|
55
|
+
}
|
|
56
|
+
export function terminalHeight() {
|
|
57
|
+
const rows = process.stdout.rows;
|
|
58
|
+
return Math.max(10, rows && rows > 0 ? rows : 24);
|
|
59
|
+
}
|
|
60
|
+
export function fit(text, width, middle = false) {
|
|
61
|
+
const clean = text.replace(/\s+/g, ' ').trim();
|
|
62
|
+
if (clean.length <= width)
|
|
63
|
+
return clean;
|
|
64
|
+
if (width <= 3)
|
|
65
|
+
return clean.slice(0, width);
|
|
66
|
+
if (!middle)
|
|
67
|
+
return `${clean.slice(0, width - 1)}…`;
|
|
68
|
+
const head = Math.ceil((width - 1) / 2);
|
|
69
|
+
const tail = width - 1 - head;
|
|
70
|
+
return `${clean.slice(0, head)}…${clean.slice(clean.length - tail)}`;
|
|
71
|
+
}
|
|
72
|
+
export function wrap(text, width) {
|
|
73
|
+
const out = [];
|
|
74
|
+
for (const paragraph of text.split('\n')) {
|
|
75
|
+
if (paragraph.length <= width) {
|
|
76
|
+
out.push(paragraph);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
let line = '';
|
|
80
|
+
for (const word of paragraph.split(' ')) {
|
|
81
|
+
if (word.length > width) {
|
|
82
|
+
if (line) {
|
|
83
|
+
out.push(line);
|
|
84
|
+
line = '';
|
|
85
|
+
}
|
|
86
|
+
for (let i = 0; i < word.length; i += width)
|
|
87
|
+
out.push(word.slice(i, i + width));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (!line)
|
|
91
|
+
line = word;
|
|
92
|
+
else if (line.length + 1 + word.length <= width)
|
|
93
|
+
line += ` ${word}`;
|
|
94
|
+
else {
|
|
95
|
+
out.push(line);
|
|
96
|
+
line = word;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (line)
|
|
100
|
+
out.push(line);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
export function tail(text, max) {
|
|
105
|
+
const lines = text.split('\n');
|
|
106
|
+
if (lines.length <= max)
|
|
107
|
+
return { lines, hidden: 0 };
|
|
108
|
+
return { lines: lines.slice(-max), hidden: lines.length - max };
|
|
109
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Rivine Code CLI
|
|
3
|
+
* Licensed and owned by Beunec Technologies, Inc.
|
|
4
|
+
*
|
|
5
|
+
* https://aselius.beunec.com
|
|
6
|
+
*
|
|
7
|
+
* This software is proprietary. Use is governed by the licence agreement
|
|
8
|
+
* accompanying it; it is not open source and is not licensed for
|
|
9
|
+
* redistribution or derivative works.
|
|
10
|
+
*/
|
|
11
|
+
export const SLASH_COMMANDS = [
|
|
12
|
+
{ name: '/help', summary: 'show these commands' },
|
|
13
|
+
{
|
|
14
|
+
name: '/mode',
|
|
15
|
+
summary: 'show or cycle the permission mode',
|
|
16
|
+
detail: 'Shift+Tab cycles plan → default → accept edits.',
|
|
17
|
+
},
|
|
18
|
+
{ name: '/plan', summary: 'switch to plan mode (reads only)' },
|
|
19
|
+
{ name: '/accept', summary: 'switch to accept-edits mode' },
|
|
20
|
+
{ name: '/status', summary: 'account, project and balance' },
|
|
21
|
+
{ name: '/diff', summary: 'what changed in the working tree' },
|
|
22
|
+
{
|
|
23
|
+
name: '/pr',
|
|
24
|
+
summary: 'open a pull request',
|
|
25
|
+
detail: 'Uses your own gh CLI and its credentials. Needs a branch that is not main. ' +
|
|
26
|
+
'A branch that has never been pushed stops with a message instead: ' +
|
|
27
|
+
'`/pr push` is the form that writes to the remote.',
|
|
28
|
+
},
|
|
29
|
+
{ name: '/mcp', summary: 'MCP servers and their tools' },
|
|
30
|
+
{ name: '/credits', summary: 'open the credits page in your browser' },
|
|
31
|
+
{ name: '/clear', summary: 'clear the transcript on screen' },
|
|
32
|
+
{ name: '/exit', summary: 'end the session' },
|
|
33
|
+
];
|
|
34
|
+
export function completionsFor(input) {
|
|
35
|
+
if (!input.startsWith('/') || input.includes(' '))
|
|
36
|
+
return [];
|
|
37
|
+
const prefix = input.toLowerCase();
|
|
38
|
+
return SLASH_COMMANDS.filter((c) => c.name.startsWith(prefix));
|
|
39
|
+
}
|
|
40
|
+
export function completeInput(input) {
|
|
41
|
+
const matches = completionsFor(input);
|
|
42
|
+
if (matches.length === 0)
|
|
43
|
+
return input;
|
|
44
|
+
if (matches.length === 1)
|
|
45
|
+
return matches[0].name;
|
|
46
|
+
let common = matches[0].name;
|
|
47
|
+
for (const match of matches.slice(1)) {
|
|
48
|
+
let i = 0;
|
|
49
|
+
while (i < common.length && i < match.name.length && common[i] === match.name[i])
|
|
50
|
+
i += 1;
|
|
51
|
+
common = common.slice(0, i);
|
|
52
|
+
}
|
|
53
|
+
return common.length > input.length ? common : input;
|
|
54
|
+
}
|
|
55
|
+
export function isSlashCommand(input) {
|
|
56
|
+
return input.trimStart().startsWith('/');
|
|
57
|
+
}
|
package/dist/ui/theme.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Rivine Code CLI
|
|
3
|
+
* Licensed and owned by Beunec Technologies, Inc.
|
|
4
|
+
*
|
|
5
|
+
* https://aselius.beunec.com
|
|
6
|
+
*
|
|
7
|
+
* This software is proprietary. Use is governed by the licence agreement
|
|
8
|
+
* accompanying it; it is not open source and is not licensed for
|
|
9
|
+
* redistribution or derivative works.
|
|
10
|
+
*/
|
|
11
|
+
export const BRAND_DEEP = '#00477d';
|
|
12
|
+
const PALETTE = {
|
|
13
|
+
brand: { hex: '#4d9fd6', basic: 'blue' },
|
|
14
|
+
text: { hex: '#e8e8ea', basic: 'white' },
|
|
15
|
+
muted: { hex: '#9aa0a6', basic: 'gray' },
|
|
16
|
+
faint: { hex: '#6b7178', basic: 'gray' },
|
|
17
|
+
accent: { hex: '#7cc4f5', basic: 'cyan' },
|
|
18
|
+
success: { hex: '#3fb98f', basic: 'green' },
|
|
19
|
+
warn: { hex: '#e0a33e', basic: 'yellow' },
|
|
20
|
+
danger: { hex: '#e5646d', basic: 'red' },
|
|
21
|
+
user: { hex: '#7cc4f5', basic: 'cyan' },
|
|
22
|
+
};
|
|
23
|
+
function depth() {
|
|
24
|
+
if (process.env.NO_COLOR !== undefined)
|
|
25
|
+
return 0;
|
|
26
|
+
if (process.env.FORCE_COLOR === '0')
|
|
27
|
+
return 0;
|
|
28
|
+
const term = process.env.COLORTERM ?? '';
|
|
29
|
+
if (/truecolor|24bit/i.test(term))
|
|
30
|
+
return 2;
|
|
31
|
+
if (process.env.FORCE_COLOR === '3')
|
|
32
|
+
return 2;
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
const DEPTH = depth();
|
|
36
|
+
export function color(role) {
|
|
37
|
+
if (DEPTH === 0)
|
|
38
|
+
return undefined;
|
|
39
|
+
const ink = PALETTE[role];
|
|
40
|
+
return DEPTH === 2 ? ink.hex : ink.basic;
|
|
41
|
+
}
|
|
42
|
+
export const colorsEnabled = DEPTH > 0;
|
|
43
|
+
const GLYPHS_UNICODE = {
|
|
44
|
+
user: '›',
|
|
45
|
+
agent: '⏺',
|
|
46
|
+
tool: '·',
|
|
47
|
+
file: '✎',
|
|
48
|
+
run: '$',
|
|
49
|
+
notice: '!',
|
|
50
|
+
error: '✗',
|
|
51
|
+
system: '◆',
|
|
52
|
+
};
|
|
53
|
+
const GLYPHS_ASCII = {
|
|
54
|
+
user: '>',
|
|
55
|
+
agent: '*',
|
|
56
|
+
tool: '-',
|
|
57
|
+
file: '~',
|
|
58
|
+
run: '$',
|
|
59
|
+
notice: '!',
|
|
60
|
+
error: 'x',
|
|
61
|
+
system: '#',
|
|
62
|
+
};
|
|
63
|
+
export const ASCII_ONLY = process.env.RIVINE_ASCII === '1';
|
|
64
|
+
export function glyph(kind) {
|
|
65
|
+
return (ASCII_ONLY ? GLYPHS_ASCII : GLYPHS_UNICODE)[kind];
|
|
66
|
+
}
|
|
67
|
+
const LINE_ROLE = {
|
|
68
|
+
user: 'user',
|
|
69
|
+
agent: 'text',
|
|
70
|
+
tool: 'faint',
|
|
71
|
+
file: 'success',
|
|
72
|
+
run: 'muted',
|
|
73
|
+
notice: 'warn',
|
|
74
|
+
error: 'danger',
|
|
75
|
+
system: 'brand',
|
|
76
|
+
};
|
|
77
|
+
export function lineColor(kind) {
|
|
78
|
+
return color(LINE_ROLE[kind]);
|
|
79
|
+
}
|
|
80
|
+
export const BOX = ASCII_ONLY
|
|
81
|
+
? { h: '-', v: '|', tl: '+', tr: '+', bl: '+', br: '+', dot: '*' }
|
|
82
|
+
: { h: '─', v: '│', tl: '╭', tr: '╮', bl: '╰', br: '╯', dot: '●' };
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@beunec/rivine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Rivine Code in your terminal — Beunec's software engineering runtime constructs, working on the files you already have open.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"author": "Beunec Technologies",
|
|
7
|
+
"homepage": "https://aselius.beunec.com",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"rivine": "dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"!dist/**/*.test.js",
|
|
18
|
+
"!dist/mcp/fixtures",
|
|
19
|
+
"action.yml",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -p tsconfig.json && node scripts/banner.mjs && mkdir -p dist/mcp/fixtures && cp src/mcp/fixtures/*.mjs dist/mcp/fixtures/",
|
|
24
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
25
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
26
|
+
"test": "node --test \"dist/**/*.test.js\"",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@inkjs/ui": "^2.0.0",
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
32
|
+
"commander": "^12.1.0",
|
|
33
|
+
"ink": "^5.1.0",
|
|
34
|
+
"react": "^18.3.1"
|
|
35
|
+
},
|
|
36
|
+
"optionalDependencies": {
|
|
37
|
+
"@napi-rs/keyring": "^1.1.0",
|
|
38
|
+
"node-pty": "^1.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.10.0",
|
|
42
|
+
"@types/react": "^18.3.12",
|
|
43
|
+
"typescript": "^5.7.2"
|
|
44
|
+
}
|
|
45
|
+
}
|