@textcortex/zenocode 0.1.2 → 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.
- package/README.md +31 -0
- package/package.json +8 -8
- package/scripts/branding-patch.mjs +112 -3
- package/scripts/branding-patch.test.mjs +17 -0
- package/scripts/build-branded-opencode.mjs +520 -78
- package/scripts/build-branded-opencode.test.mjs +25 -0
- package/scripts/{run-codecortex.mjs → run-zenocode.mjs} +323 -80
- package/scripts/run-zenocode.test.mjs +224 -0
- package/scripts/run-codecortex.test.mjs +0 -59
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Zenocode
|
|
2
|
+
|
|
3
|
+
Zenocode is a powerful AI coding agent for TextCortex customers. Built for the terminal.
|
|
4
|
+
|
|
5
|
+
It gives you the OpenCode-style terminal experience with TextCortex authentication, TextCortex model access, and a TextCortex-branded runtime already wired in.
|
|
6
|
+
|
|
7
|
+
## What This Package Does
|
|
8
|
+
|
|
9
|
+
- Launches Zenocode from your terminal with the `zenocode` or `codecortex` command
|
|
10
|
+
- Connects the runtime to your TextCortex account
|
|
11
|
+
- Discovers the models available to your TextCortex workspace
|
|
12
|
+
- Configures the underlying coding agent automatically so you can start coding instead of hand-wiring providers and credentials
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g @textcortex/zenocode
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
zenocode login
|
|
24
|
+
zenocode
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If you already have an API key, you can also start Zenocode by setting `TEXTCORTEX_API_KEY` or `TEXTCORTEX_API_TOKEN`.
|
|
28
|
+
|
|
29
|
+
## Who It Is For
|
|
30
|
+
|
|
31
|
+
Zenocode is for TextCortex customers who want to use a terminal-native AI coding agent with their existing TextCortex account, models, and authentication flow.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@textcortex/zenocode",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "A powerful AI coding agent for TextCortex customers, with TextCortex auth and models built in.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"type": "module",
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
"scripts"
|
|
13
13
|
],
|
|
14
14
|
"bin": {
|
|
15
|
-
"zenocode": "scripts/run-
|
|
16
|
-
"codecortex": "scripts/run-
|
|
15
|
+
"zenocode": "scripts/run-zenocode.mjs",
|
|
16
|
+
"codecortex": "scripts/run-zenocode.mjs"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build-branded-opencode": "node scripts/build-branded-opencode.mjs",
|
|
20
|
-
"prepare-config": "node scripts/run-
|
|
21
|
-
"dev": "node scripts/run-
|
|
22
|
-
"login": "node scripts/run-
|
|
23
|
-
"logout": "node scripts/run-
|
|
20
|
+
"prepare-config": "node scripts/run-zenocode.mjs --prepare-only",
|
|
21
|
+
"dev": "node scripts/run-zenocode.mjs",
|
|
22
|
+
"login": "node scripts/run-zenocode.mjs login",
|
|
23
|
+
"logout": "node scripts/run-zenocode.mjs logout"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -1,5 +1,26 @@
|
|
|
1
|
+
export const zenocodeLogo = {
|
|
2
|
+
left: [
|
|
3
|
+
" ",
|
|
4
|
+
" \\u2580\\u2580\\u2588 \\u2588\\u2580\\u2580 \\u2588\\u2584 \\u2588 \\u2588\\u2580\\u2588",
|
|
5
|
+
" \\u2588\\u2584\\u2584 \\u2588\\u2588\\u2584 \\u2588 \\u2580\\u2588 \\u2588\\u2584\\u2588",
|
|
6
|
+
" ",
|
|
7
|
+
],
|
|
8
|
+
right: [
|
|
9
|
+
" ",
|
|
10
|
+
" \\u2588\\u2580\\u2580 \\u2588\\u2580\\u2588 \\u2588\\u2580\\u2584 \\u2588\\u2580\\u2580",
|
|
11
|
+
" \\u2588\\u2584\\u2584 \\u2588\\u2584\\u2588 \\u2588\\u2584\\u2580 \\u2588\\u2588\\u2584",
|
|
12
|
+
" ",
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const opencodeLogoSnippet = `var logo = {
|
|
17
|
+
left: [" ", "\\u2588\\u2580\\u2580\\u2588 \\u2588\\u2580\\u2580\\u2588 \\u2588\\u2580\\u2580\\u2588 \\u2588\\u2580\\u2580\\u2584", "\\u2588__\\u2588 \\u2588__\\u2588 \\u2588^^^ \\u2588__\\u2588", "\\u2580\\u2580\\u2580\\u2580 \\u2588\\u2580\\u2580\\u2580 \\u2580\\u2580\\u2580\\u2580 \\u2580~~\\u2580"],
|
|
18
|
+
right: [" \\u2584 ", "\\u2588\\u2580\\u2580\\u2580 \\u2588\\u2580\\u2580\\u2588 \\u2588\\u2580\\u2580\\u2588 \\u2588\\u2580\\u2580\\u2588", "\\u2588___ \\u2588__\\u2588 \\u2588__\\u2588 \\u2588^^^", "\\u2580\\u2580\\u2580\\u2580 \\u2580\\u2580\\u2580\\u2580 \\u2580\\u2580\\u2580\\u2580 \\u2580\\u2580\\u2580\\u2580"]
|
|
19
|
+
};
|
|
20
|
+
var marks = "_^~";`;
|
|
21
|
+
|
|
1
22
|
const openCodeVersionFooterSnippetPattern =
|
|
2
|
-
/insertNode\(([$\w]+), createTextNode\(`Open`\)\);\s*insertNode\(([$\w]+), ([$\w]+)\);\s*insertNode\(\3, createTextNode\(`Code`\)\);\s*insert\(([$\w]+), \(\) => Installation\.VERSION\);/g;
|
|
23
|
+
/insertNode\(([$\w]+), createTextNode\(`Open`\)\);\s*insertNode\(([$\w]+), ([$\w]+)\);\s*insertNode\(\3, createTextNode\(`Code`\)\);\s*((?:insert\d*))\(([$\w]+), \(\) => Installation\.VERSION\);/g;
|
|
3
24
|
|
|
4
25
|
export function padBinaryReplacement(target, replacement) {
|
|
5
26
|
if (replacement.length > target.length) {
|
|
@@ -12,17 +33,85 @@ export function padBinaryReplacement(target, replacement) {
|
|
|
12
33
|
return `${replacement}${" ".repeat(paddingLength)}`;
|
|
13
34
|
}
|
|
14
35
|
|
|
36
|
+
function _buildLogoSourceArray(lines) {
|
|
37
|
+
return `[${lines.map((line) => `"${line.replaceAll('"', '\\"')}"`).join(", ")}]`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const doubleEscapedZenocodeTextLogoSnippetCore = `var logo = {
|
|
41
|
+
left: ${JSON.stringify(zenocodeLogo.left)},
|
|
42
|
+
right: ${JSON.stringify(zenocodeLogo.right)}
|
|
43
|
+
};
|
|
44
|
+
var marks = "_^~";`;
|
|
45
|
+
|
|
46
|
+
const zenocodeTextLogoSnippetCore = `var logo = {
|
|
47
|
+
left: ${_buildLogoSourceArray(zenocodeLogo.left)},
|
|
48
|
+
right: ${_buildLogoSourceArray(zenocodeLogo.right)}
|
|
49
|
+
};
|
|
50
|
+
var marks = "_^~";`;
|
|
51
|
+
|
|
52
|
+
const legacyZenocodeTextLogoSnippetCores = [
|
|
53
|
+
doubleEscapedZenocodeTextLogoSnippetCore,
|
|
54
|
+
`var logo = {
|
|
55
|
+
left: [" ", " Zenocode ", " Zenocode ", " "],
|
|
56
|
+
right: [" ", " ", " ", " "]
|
|
57
|
+
};
|
|
58
|
+
var marks = "_^~";`,
|
|
59
|
+
`var logo = {
|
|
60
|
+
left: [" ZZZZZ EEEEE N N OOO CCCC OOO DDDD EEEEE", " ZZ EE NN N O O C O O D D EE ", " ZZ EEEE N N N O O C O O D D EEEE ", " ZZZZZ EEEEE N N OOO CCCC OOO DDDD EEEEE"],
|
|
61
|
+
right: ["", "", "", ""]
|
|
62
|
+
};
|
|
63
|
+
var marks = "_^~";`,
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
export function buildPaddedLogoSnippet(coreSnippet) {
|
|
67
|
+
return padBinaryReplacement(opencodeLogoSnippet, coreSnippet);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _buildZenocodeLogoSnippet() {
|
|
71
|
+
return buildPaddedLogoSnippet(zenocodeTextLogoSnippetCore);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function patchLogoSnippetText(text) {
|
|
75
|
+
const replacement = _buildZenocodeLogoSnippet();
|
|
76
|
+
if (!replacement || text.includes(replacement)) {
|
|
77
|
+
return { patched: false, text };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const patchTargets = [
|
|
81
|
+
opencodeLogoSnippet,
|
|
82
|
+
...legacyZenocodeTextLogoSnippetCores.map((coreSnippet) =>
|
|
83
|
+
buildPaddedLogoSnippet(coreSnippet),
|
|
84
|
+
),
|
|
85
|
+
].filter(Boolean);
|
|
86
|
+
let logoOffset = -1;
|
|
87
|
+
let matchedTargetLength = replacement.length;
|
|
88
|
+
for (const target of patchTargets) {
|
|
89
|
+
logoOffset = text.indexOf(target);
|
|
90
|
+
if (logoOffset !== -1) {
|
|
91
|
+
matchedTargetLength = target.length;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (logoOffset === -1) {
|
|
96
|
+
return { patched: false, text };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const nextText = `${text.slice(0, logoOffset)}${replacement}${text.slice(logoOffset + matchedTargetLength)}`;
|
|
100
|
+
return { patched: true, text: nextText };
|
|
101
|
+
}
|
|
102
|
+
|
|
15
103
|
function _buildZenocodeVersionFooterSnippet(
|
|
16
104
|
openTextRef,
|
|
17
105
|
codeContainerRef,
|
|
18
106
|
codeTextRef,
|
|
107
|
+
versionInsertRef,
|
|
19
108
|
versionRef,
|
|
20
109
|
) {
|
|
21
110
|
return [
|
|
22
111
|
`insertNode(${openTextRef}, createTextNode(\`Zeno\`));`,
|
|
23
112
|
`insertNode(${codeContainerRef}, ${codeTextRef});`,
|
|
24
113
|
`insertNode(${codeTextRef}, createTextNode(\`code\`));`,
|
|
25
|
-
|
|
114
|
+
`${versionInsertRef}(${versionRef}, () => "");`,
|
|
26
115
|
].join(" ");
|
|
27
116
|
}
|
|
28
117
|
|
|
@@ -31,11 +120,12 @@ export function patchOpenCodeVersionFooterText(text) {
|
|
|
31
120
|
let patched = false;
|
|
32
121
|
const next = text.replace(
|
|
33
122
|
openCodeVersionFooterSnippetPattern,
|
|
34
|
-
(match, openTextRef, codeContainerRef, codeTextRef, versionRef) => {
|
|
123
|
+
(match, openTextRef, codeContainerRef, codeTextRef, versionInsertRef, versionRef) => {
|
|
35
124
|
const replacement = _buildZenocodeVersionFooterSnippet(
|
|
36
125
|
openTextRef,
|
|
37
126
|
codeContainerRef,
|
|
38
127
|
codeTextRef,
|
|
128
|
+
versionInsertRef,
|
|
39
129
|
versionRef,
|
|
40
130
|
);
|
|
41
131
|
const padded = replacement.length <= match.length ? replacement.padEnd(match.length, " ") : null;
|
|
@@ -48,3 +138,22 @@ export function patchOpenCodeVersionFooterText(text) {
|
|
|
48
138
|
);
|
|
49
139
|
return { patched, text: next };
|
|
50
140
|
}
|
|
141
|
+
|
|
142
|
+
export function patchZenocodeBinaryText(text) {
|
|
143
|
+
let patched = false;
|
|
144
|
+
let nextText = text;
|
|
145
|
+
|
|
146
|
+
const logoPatch = patchLogoSnippetText(nextText);
|
|
147
|
+
if (logoPatch.patched) {
|
|
148
|
+
nextText = logoPatch.text;
|
|
149
|
+
patched = true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const footerPatch = patchOpenCodeVersionFooterText(nextText);
|
|
153
|
+
if (footerPatch.patched) {
|
|
154
|
+
nextText = footerPatch.text;
|
|
155
|
+
patched = true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return { patched, text: nextText };
|
|
159
|
+
}
|
|
@@ -25,6 +25,23 @@ test("patchOpenCodeVersionFooterText rewrites OpenCode + version footer", () =>
|
|
|
25
25
|
assert.doesNotMatch(result.text, /Installation\.VERSION/);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
test("patchOpenCodeVersionFooterText rewrites the compiled 1.3.2 footer variant", () => {
|
|
29
|
+
const input = [
|
|
30
|
+
"insertNode(_el$74, createTextNode(`Open`));",
|
|
31
|
+
" insertNode(_el$76, _el$77);",
|
|
32
|
+
" insertNode(_el$77, createTextNode(`Code`));",
|
|
33
|
+
" insert3(_el$80, () => Installation.VERSION);",
|
|
34
|
+
].join("\n");
|
|
35
|
+
|
|
36
|
+
const result = patchOpenCodeVersionFooterText(input);
|
|
37
|
+
assert.equal(result.patched, true);
|
|
38
|
+
assert.equal(result.text.length, input.length);
|
|
39
|
+
assert.match(result.text, /createTextNode\(`Zeno`\)/);
|
|
40
|
+
assert.match(result.text, /createTextNode\(`code`\)/);
|
|
41
|
+
assert.match(result.text, /insert3\(_el\$80, \(\) => ""\);/);
|
|
42
|
+
assert.doesNotMatch(result.text, /Installation\.VERSION/);
|
|
43
|
+
});
|
|
44
|
+
|
|
28
45
|
test("patchOpenCodeVersionFooterText is a no-op when already branded", () => {
|
|
29
46
|
const branded = [
|
|
30
47
|
"insertNode(_el$74, createTextNode(`Zeno`));",
|