pinokiod 8.0.31 → 8.0.33
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/kernel/plugin_sources.js +2 -0
- package/package.json +1 -1
- package/server/index.js +4 -0
- package/server/public/nav.js +81 -1
- package/server/public/style.css +71 -0
- package/server/views/connect/x.ejs +1 -0
- package/server/views/create.ejs +1 -0
- package/server/views/index.ejs +4 -0
- package/server/views/partials/app_common_scripts.ejs +1 -0
- package/system/plugin/grok/grok.png +0 -0
- package/system/plugin/grok/pinokio.js +44 -0
- package/system/plugin/grok-auto/grok.png +0 -0
- package/system/plugin/grok-auto/pinokio.js +20 -0
- package/test/directory-readme-rendering.test.js +43 -2
- package/test/grok-plugin.test.js +78 -0
- package/test/header-collapse.test.js +33 -0
- package/test/plugin-sources.test.js +19 -1
package/kernel/plugin_sources.js
CHANGED
|
@@ -16,6 +16,8 @@ const FUNCTION_KEYS = new Set([...ACTION_KEYS, ...STATUS_KEYS])
|
|
|
16
16
|
const BUILTIN_TOOL_ALIASES = {
|
|
17
17
|
claude: "pinokio/run/plugin/claude",
|
|
18
18
|
codex: "pinokio/run/plugin/codex",
|
|
19
|
+
grok: "pinokio/run/plugin/grok",
|
|
20
|
+
"grok-build": "pinokio/run/plugin/grok",
|
|
19
21
|
antigravity: "pinokio/run/plugin/antigravity-cli",
|
|
20
22
|
"antigravity-cli": "pinokio/run/plugin/antigravity-cli",
|
|
21
23
|
"code/claude": "pinokio/run/plugin/claude",
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -4286,6 +4286,8 @@ class Server {
|
|
|
4286
4286
|
home_sort: homeSortMode,
|
|
4287
4287
|
running,
|
|
4288
4288
|
notRunning,
|
|
4289
|
+
// Keep legacy custom views compatible without loading or rendering README files.
|
|
4290
|
+
readme: undefined,
|
|
4289
4291
|
filepath,
|
|
4290
4292
|
mode: null,
|
|
4291
4293
|
kernel: this.kernel,
|
|
@@ -4317,6 +4319,8 @@ class Server {
|
|
|
4317
4319
|
ishome: meta,
|
|
4318
4320
|
running,
|
|
4319
4321
|
notRunning,
|
|
4322
|
+
// Keep legacy custom views compatible without loading or rendering README files.
|
|
4323
|
+
readme: undefined,
|
|
4320
4324
|
filepath,
|
|
4321
4325
|
mode: null,
|
|
4322
4326
|
kernel: this.kernel,
|
package/server/public/nav.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const headerCollapseOnly = document.currentScript?.hasAttribute("data-header-collapse-only");
|
|
1
2
|
document.addEventListener("DOMContentLoaded", () => {
|
|
2
3
|
// Logging disabled for production
|
|
3
4
|
const log = () => {};
|
|
@@ -5,7 +6,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
5
6
|
|
|
6
7
|
const newWindowButton = document.querySelector("#new-window");
|
|
7
8
|
const agent = document.body.getAttribute("data-agent");
|
|
8
|
-
if (newWindowButton) {
|
|
9
|
+
if (newWindowButton && !headerCollapseOnly) {
|
|
9
10
|
newWindowButton.addEventListener("click", (event) => {
|
|
10
11
|
if (agent === "electron") {
|
|
11
12
|
window.open("/", "_blank", "pinokio");
|
|
@@ -15,6 +16,85 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
15
16
|
});
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
(() => {
|
|
20
|
+
const header = document.querySelector("body > header.navheader");
|
|
21
|
+
if (!header) return;
|
|
22
|
+
|
|
23
|
+
const body = document.body;
|
|
24
|
+
const row = header.querySelector("h1") || header;
|
|
25
|
+
const collapseMinWidth = (Number(window.PinokioAppMobileBreakpoint) || 768) + 1;
|
|
26
|
+
const collapseMedia = matchMedia(`(min-width: ${collapseMinWidth}px) and (hover: hover) and (pointer: fine)`);
|
|
27
|
+
const reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
|
|
28
|
+
|
|
29
|
+
const toggle = document.createElement("button");
|
|
30
|
+
toggle.type = "button";
|
|
31
|
+
toggle.className = "btn2 header-collapse-toggle";
|
|
32
|
+
toggle.id = "header-collapse-toggle";
|
|
33
|
+
toggle.setAttribute("aria-label", "Hide toolbar");
|
|
34
|
+
toggle.setAttribute("title", "Hide toolbar");
|
|
35
|
+
toggle.innerHTML = '<div><i class="fa-solid fa-compress" aria-hidden="true"></i></div>';
|
|
36
|
+
row.insertBefore(toggle, row.querySelector("#new-window, #close-window"));
|
|
37
|
+
|
|
38
|
+
const edge = document.createElement("button");
|
|
39
|
+
edge.type = "button";
|
|
40
|
+
edge.className = "header-collapse-edge";
|
|
41
|
+
edge.setAttribute("aria-label", "Show toolbar");
|
|
42
|
+
edge.setAttribute("title", "Show toolbar");
|
|
43
|
+
edge.innerHTML = '<span aria-hidden="true"></span>';
|
|
44
|
+
body.appendChild(edge);
|
|
45
|
+
|
|
46
|
+
let collapseTimer;
|
|
47
|
+
let cueTimer;
|
|
48
|
+
const clearCue = () => {
|
|
49
|
+
clearTimeout(collapseTimer);
|
|
50
|
+
clearTimeout(cueTimer);
|
|
51
|
+
body.classList.remove("header-collapse-cue");
|
|
52
|
+
};
|
|
53
|
+
const setCollapsed = (collapsed) => {
|
|
54
|
+
body.classList.toggle("header-collapse-moving", collapseMedia.matches && !reducedMotion.matches);
|
|
55
|
+
body.classList.toggle("header-collapsed", collapsed);
|
|
56
|
+
header.inert = collapsed;
|
|
57
|
+
header.setAttribute("aria-hidden", collapsed ? "true" : "false");
|
|
58
|
+
document.dispatchEvent(new CustomEvent("pinokio:header-state", { detail: { collapsed, minimized: collapsed } }));
|
|
59
|
+
};
|
|
60
|
+
const finishMotion = (event) => {
|
|
61
|
+
if (event.target === header && event.propertyName === "grid-template-rows") {
|
|
62
|
+
body.classList.remove("header-collapse-moving");
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
header.addEventListener("transitionend", finishMotion);
|
|
66
|
+
header.addEventListener("transitioncancel", finishMotion);
|
|
67
|
+
|
|
68
|
+
toggle.addEventListener("click", (event) => {
|
|
69
|
+
if (!collapseMedia.matches) return;
|
|
70
|
+
clearCue();
|
|
71
|
+
body.classList.add("header-collapse-cue");
|
|
72
|
+
const keyboard = event.detail === 0;
|
|
73
|
+
collapseTimer = setTimeout(() => {
|
|
74
|
+
setCollapsed(true);
|
|
75
|
+
if (keyboard) edge.focus({ preventScroll: true });
|
|
76
|
+
cueTimer = setTimeout(() => body.classList.remove("header-collapse-cue"), reducedMotion.matches ? 360 : 520);
|
|
77
|
+
}, reducedMotion.matches ? 0 : 140);
|
|
78
|
+
});
|
|
79
|
+
edge.addEventListener("click", (event) => {
|
|
80
|
+
clearCue();
|
|
81
|
+
setCollapsed(false);
|
|
82
|
+
if (event.detail === 0) toggle.focus({ preventScroll: true });
|
|
83
|
+
});
|
|
84
|
+
const syncMedia = () => {
|
|
85
|
+
body.classList.toggle("header-collapse-enabled", collapseMedia.matches);
|
|
86
|
+
if (!collapseMedia.matches) {
|
|
87
|
+
clearCue();
|
|
88
|
+
if (body.classList.contains("header-collapsed")) setCollapsed(false);
|
|
89
|
+
body.classList.remove("header-collapse-moving");
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
collapseMedia.addEventListener("change", syncMedia);
|
|
93
|
+
syncMedia();
|
|
94
|
+
})();
|
|
95
|
+
|
|
96
|
+
if (headerCollapseOnly) return;
|
|
97
|
+
|
|
18
98
|
const header = document.querySelector("header.navheader");
|
|
19
99
|
const minimizeButton = document.querySelector("#minimize-header");
|
|
20
100
|
const homeLink = header ? header.querySelector(".home") : null;
|
package/server/public/style.css
CHANGED
|
@@ -4650,6 +4650,77 @@ body.dark #dropdown-portal .dropdown-content .btn2 {
|
|
|
4650
4650
|
background: white;
|
|
4651
4651
|
}
|
|
4652
4652
|
|
|
4653
|
+
.header-collapse-edge {
|
|
4654
|
+
display: none;
|
|
4655
|
+
}
|
|
4656
|
+
|
|
4657
|
+
@media (min-width: 769px) and (hover: hover) and (pointer: fine) {
|
|
4658
|
+
body.header-collapse-enabled > header.navheader:not(.minimized) {
|
|
4659
|
+
display: grid !important;
|
|
4660
|
+
grid-template-rows: minmax(0, 1fr);
|
|
4661
|
+
overflow: visible;
|
|
4662
|
+
transition: grid-template-rows 240ms cubic-bezier(0.25, 1, 0.5, 1), padding-top 240ms cubic-bezier(0.25, 1, 0.5, 1), padding-bottom 240ms cubic-bezier(0.25, 1, 0.5, 1);
|
|
4663
|
+
}
|
|
4664
|
+
body.header-collapse-enabled > header.navheader:not(.minimized) > h1 {
|
|
4665
|
+
min-height: 0;
|
|
4666
|
+
overflow: visible;
|
|
4667
|
+
}
|
|
4668
|
+
body.header-collapse-moving > header.navheader:not(.minimized) > h1,
|
|
4669
|
+
body.header-collapsed > header.navheader:not(.minimized) > h1 {
|
|
4670
|
+
overflow: hidden;
|
|
4671
|
+
}
|
|
4672
|
+
body.header-collapsed > header.navheader:not(.minimized) {
|
|
4673
|
+
grid-template-rows: minmax(0, 0fr);
|
|
4674
|
+
padding-top: 0 !important;
|
|
4675
|
+
padding-bottom: 0 !important;
|
|
4676
|
+
border-top-width: 0 !important;
|
|
4677
|
+
border-bottom-width: 0 !important;
|
|
4678
|
+
overflow: hidden;
|
|
4679
|
+
box-shadow: none !important;
|
|
4680
|
+
pointer-events: none;
|
|
4681
|
+
}
|
|
4682
|
+
body.header-collapse-cue > .header-collapse-edge,
|
|
4683
|
+
body.header-collapsed > .header-collapse-edge {
|
|
4684
|
+
position: fixed;
|
|
4685
|
+
inset: 0 0 auto;
|
|
4686
|
+
z-index: 10000050;
|
|
4687
|
+
display: block;
|
|
4688
|
+
width: 100%;
|
|
4689
|
+
height: 8px;
|
|
4690
|
+
padding: 0;
|
|
4691
|
+
border: 0;
|
|
4692
|
+
background: transparent;
|
|
4693
|
+
cursor: pointer;
|
|
4694
|
+
-webkit-app-region: no-drag;
|
|
4695
|
+
}
|
|
4696
|
+
.header-collapse-edge > span {
|
|
4697
|
+
position: absolute;
|
|
4698
|
+
inset: 1px 0 auto;
|
|
4699
|
+
height: 3px;
|
|
4700
|
+
background: royalblue;
|
|
4701
|
+
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7), 0 2px 4px rgba(15, 23, 42, 0.35);
|
|
4702
|
+
opacity: 0;
|
|
4703
|
+
transform: translateY(-4px);
|
|
4704
|
+
transition: opacity 120ms, transform 160ms;
|
|
4705
|
+
}
|
|
4706
|
+
body.header-collapse-cue .header-collapse-edge > span {
|
|
4707
|
+
opacity: 1;
|
|
4708
|
+
transform: none;
|
|
4709
|
+
box-shadow: 0 0 10px 2px rgba(65, 105, 225, 0.7);
|
|
4710
|
+
}
|
|
4711
|
+
body.header-collapsed .header-collapse-edge:is(:hover, :focus-visible) > span {
|
|
4712
|
+
opacity: 0.92;
|
|
4713
|
+
transform: none;
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
|
|
4717
|
+
body:not(.header-collapse-enabled) .header-collapse-toggle { display: none !important; }
|
|
4718
|
+
|
|
4719
|
+
@media (prefers-reduced-motion: reduce) {
|
|
4720
|
+
body.header-collapse-enabled > header.navheader:not(.minimized),
|
|
4721
|
+
.header-collapse-edge > span { transition-duration: 0.01ms !important; }
|
|
4722
|
+
}
|
|
4723
|
+
|
|
4653
4724
|
|
|
4654
4725
|
header.navheader.minimized {
|
|
4655
4726
|
position: fixed;
|
|
@@ -186,6 +186,7 @@ pre {
|
|
|
186
186
|
<script src="/hotkeys.min.js"></script>
|
|
187
187
|
<script src="/sweetalert2.js"></script>
|
|
188
188
|
<script src="/common.js"></script>
|
|
189
|
+
<script src="/nav.js" data-header-collapse-only></script>
|
|
189
190
|
<script src="/opener.js"></script>
|
|
190
191
|
<script src="/highlight.js"></script>
|
|
191
192
|
<script src="/highlight-js.js"></script>
|
package/server/views/create.ejs
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<% if (agent === "electron") { %>
|
|
16
16
|
<link href="/electron.css" rel="stylesheet" />
|
|
17
17
|
<% } %>
|
|
18
|
+
<script src="/nav.js" data-header-collapse-only></script>
|
|
18
19
|
</head>
|
|
19
20
|
<body class="<%= theme %>" data-agent="<%= agent %>">
|
|
20
21
|
<header class="navheader grabbable">
|
package/server/views/index.ejs
CHANGED
|
@@ -2998,6 +2998,10 @@ const setHomeActionsDrawerOffset = () => {
|
|
|
2998
2998
|
const top = header ? Math.max(0, Math.round(header.getBoundingClientRect().bottom)) : 0
|
|
2999
2999
|
document.body.style.setProperty('--home-actions-drawer-top', `${top}px`)
|
|
3000
3000
|
}
|
|
3001
|
+
const homeActionsDrawerHeader = document.querySelector('body.is-home > header.navheader')
|
|
3002
|
+
if (homeActionsDrawerHeader && typeof ResizeObserver === 'function') {
|
|
3003
|
+
new ResizeObserver(setHomeActionsDrawerOffset).observe(homeActionsDrawerHeader)
|
|
3004
|
+
}
|
|
3001
3005
|
const setHomeActionsTriggerExpanded = (dialog, expanded) => {
|
|
3002
3006
|
if (!dialog || !dialog.id) {
|
|
3003
3007
|
return
|
|
Binary file
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
title: "Grok Build",
|
|
3
|
+
icon: "grok.png",
|
|
4
|
+
link: "https://github.com/xai-org/grok-build",
|
|
5
|
+
run: [{
|
|
6
|
+
when: "{{platform === 'win32'}}",
|
|
7
|
+
id: "run",
|
|
8
|
+
method: "shell.run",
|
|
9
|
+
params: {
|
|
10
|
+
shell: "{{kernel.path('bin/miniforge/Library/bin/bash.exe')}}",
|
|
11
|
+
conda: {
|
|
12
|
+
skip: true
|
|
13
|
+
},
|
|
14
|
+
message: {
|
|
15
|
+
_: [
|
|
16
|
+
"npx",
|
|
17
|
+
"-y",
|
|
18
|
+
"@xai-official/grok@latest",
|
|
19
|
+
"{{args.prompt || undefined}}"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
path: "{{args.cwd}}",
|
|
23
|
+
input: true,
|
|
24
|
+
buffer: 1024
|
|
25
|
+
}
|
|
26
|
+
}, {
|
|
27
|
+
when: "{{platform !== 'win32'}}",
|
|
28
|
+
id: "run",
|
|
29
|
+
method: "shell.run",
|
|
30
|
+
params: {
|
|
31
|
+
message: {
|
|
32
|
+
_: [
|
|
33
|
+
"npx",
|
|
34
|
+
"-y",
|
|
35
|
+
"@xai-official/grok@latest",
|
|
36
|
+
"{{args.prompt || undefined}}"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
path: "{{args.cwd}}",
|
|
40
|
+
input: true,
|
|
41
|
+
buffer: 1024
|
|
42
|
+
}
|
|
43
|
+
}]
|
|
44
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const grok = require("../grok/pinokio")
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
...grok,
|
|
5
|
+
title: "Grok Build Auto",
|
|
6
|
+
description: "Grok Build with tool permission prompts automatically approved unless explicitly denied.",
|
|
7
|
+
run: grok.run.map((step) => ({
|
|
8
|
+
...step,
|
|
9
|
+
params: {
|
|
10
|
+
...step.params,
|
|
11
|
+
message: {
|
|
12
|
+
_: [
|
|
13
|
+
...step.params.message._.slice(0, 3),
|
|
14
|
+
"--always-approve",
|
|
15
|
+
...step.params.message._.slice(3)
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}))
|
|
20
|
+
}
|
|
@@ -9,7 +9,7 @@ const Server = require('../server')
|
|
|
9
9
|
|
|
10
10
|
const viewsPath = path.resolve(__dirname, '..', 'server', 'views')
|
|
11
11
|
|
|
12
|
-
async function createDirectoryRendererFixture() {
|
|
12
|
+
async function createDirectoryRendererFixture({ legacyCustomViews = false } = {}) {
|
|
13
13
|
const homedir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pinokio-directory-readme-'))
|
|
14
14
|
const apiRoot = path.resolve(homedir, 'api')
|
|
15
15
|
const appRoot = path.resolve(apiRoot, 'demo')
|
|
@@ -18,6 +18,23 @@ async function createDirectoryRendererFixture() {
|
|
|
18
18
|
await fs.promises.writeFile(path.resolve(appRoot, 'README.md'), '# APP README SENTINEL\n')
|
|
19
19
|
await fs.promises.writeFile(path.resolve(appRoot, 'keep.txt'), 'keep\n')
|
|
20
20
|
|
|
21
|
+
const customViewsPath = path.resolve(homedir, 'web', 'views')
|
|
22
|
+
if (legacyCustomViews) {
|
|
23
|
+
await fs.promises.mkdir(customViewsPath, { recursive: true })
|
|
24
|
+
const legacyTemplate = [
|
|
25
|
+
'<% if (readme) { %>',
|
|
26
|
+
'<div class="readme markdown-body"><%- readme %></div>',
|
|
27
|
+
'<% } %>',
|
|
28
|
+
'<% items.forEach((item) => { %>',
|
|
29
|
+
'<div data-name="<%= item.name %>"></div>',
|
|
30
|
+
'<% }) %>'
|
|
31
|
+
].join('\n')
|
|
32
|
+
await Promise.all([
|
|
33
|
+
fs.promises.writeFile(path.resolve(customViewsPath, 'index.ejs'), legacyTemplate),
|
|
34
|
+
fs.promises.writeFile(path.resolve(customViewsPath, 'file_explorer.ejs'), legacyTemplate)
|
|
35
|
+
])
|
|
36
|
+
}
|
|
37
|
+
|
|
21
38
|
const renderer = Object.create(Server.prototype)
|
|
22
39
|
renderer.kernel = {
|
|
23
40
|
homedir,
|
|
@@ -60,7 +77,7 @@ async function createDirectoryRendererFixture() {
|
|
|
60
77
|
renderer.getPeers = () => []
|
|
61
78
|
|
|
62
79
|
const app = express()
|
|
63
|
-
app.set('views', viewsPath)
|
|
80
|
+
app.set('views', [customViewsPath, viewsPath])
|
|
64
81
|
app.set('view engine', 'ejs')
|
|
65
82
|
app.get('/home', (req, res, next) => {
|
|
66
83
|
renderer.render(req, res, [], {}).catch(next)
|
|
@@ -123,3 +140,27 @@ test('directory pages list README files without auto-rendering their contents',
|
|
|
123
140
|
await fixture.close()
|
|
124
141
|
}
|
|
125
142
|
})
|
|
143
|
+
|
|
144
|
+
test('legacy custom directory views can reference readme without crashing', async () => {
|
|
145
|
+
const fixture = await createDirectoryRendererFixture({ legacyCustomViews: true })
|
|
146
|
+
try {
|
|
147
|
+
const responses = await Promise.all([
|
|
148
|
+
fetch(`${fixture.baseUrl}/home`),
|
|
149
|
+
fetch(`${fixture.baseUrl}/_api/demo`),
|
|
150
|
+
fetch(`${fixture.baseUrl}/api/demo`)
|
|
151
|
+
])
|
|
152
|
+
const html = await Promise.all(responses.map((response) => response.text()))
|
|
153
|
+
|
|
154
|
+
responses.forEach((response, index) => {
|
|
155
|
+
assert.equal(response.status, 200, html[index])
|
|
156
|
+
})
|
|
157
|
+
assert.doesNotMatch(html[0], /ROOT README SENTINEL/)
|
|
158
|
+
for (const page of html.slice(1)) {
|
|
159
|
+
assert.doesNotMatch(page, /APP README SENTINEL/)
|
|
160
|
+
assert.match(page, /data-name="README\.md"/)
|
|
161
|
+
assert.match(page, /data-name="keep\.txt"/)
|
|
162
|
+
}
|
|
163
|
+
} finally {
|
|
164
|
+
await fixture.close()
|
|
165
|
+
}
|
|
166
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const assert = require("node:assert/strict")
|
|
2
|
+
const fs = require("node:fs")
|
|
3
|
+
const path = require("node:path")
|
|
4
|
+
const test = require("node:test")
|
|
5
|
+
|
|
6
|
+
const grok = require("../system/plugin/grok/pinokio")
|
|
7
|
+
const grokAuto = require("../system/plugin/grok-auto/pinokio")
|
|
8
|
+
|
|
9
|
+
test("Grok Build exposes a minimal npx-backed terminal plugin", () => {
|
|
10
|
+
assert.equal(grok.title, "Grok Build")
|
|
11
|
+
assert.equal(grok.icon, "grok.png")
|
|
12
|
+
assert.equal(grok.link, "https://github.com/xai-org/grok-build")
|
|
13
|
+
assert.equal(fs.existsSync(path.join(__dirname, "..", "system", "plugin", "grok", grok.icon)), true)
|
|
14
|
+
|
|
15
|
+
for (const action of ["install", "update", "uninstall", "installed"]) {
|
|
16
|
+
assert.equal(Object.prototype.hasOwnProperty.call(grok, action), false)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
assert.equal(grok.run.length, 2)
|
|
20
|
+
for (const step of grok.run) {
|
|
21
|
+
assert.equal(step.id, "run")
|
|
22
|
+
assert.equal(step.method, "shell.run")
|
|
23
|
+
assert.deepEqual(step.params.message, {
|
|
24
|
+
_: [
|
|
25
|
+
"npx",
|
|
26
|
+
"-y",
|
|
27
|
+
"@xai-official/grok@latest",
|
|
28
|
+
"{{args.prompt || undefined}}"
|
|
29
|
+
]
|
|
30
|
+
})
|
|
31
|
+
assert.equal(step.params.path, "{{args.cwd}}")
|
|
32
|
+
assert.equal(step.params.input, true)
|
|
33
|
+
assert.equal(step.params.buffer, 1024)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
assert.match(grok.run[0].when, /win32/)
|
|
37
|
+
assert.match(grok.run[0].params.shell, /bash\.exe/)
|
|
38
|
+
assert.equal(grok.run[0].params.conda.skip, true)
|
|
39
|
+
assert.equal(Object.prototype.hasOwnProperty.call(grok.run[1].params, "shell"), false)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test("Grok Build Auto shares the base wrapper and enables always-approve", () => {
|
|
43
|
+
assert.equal(grokAuto.title, "Grok Build Auto")
|
|
44
|
+
assert.equal(grokAuto.icon, grok.icon)
|
|
45
|
+
assert.equal(grokAuto.link, grok.link)
|
|
46
|
+
assert.match(grokAuto.description, /automatically approved/)
|
|
47
|
+
assert.equal(fs.existsSync(path.join(__dirname, "..", "system", "plugin", "grok-auto", grokAuto.icon)), true)
|
|
48
|
+
|
|
49
|
+
for (const action of ["install", "update", "uninstall", "installed"]) {
|
|
50
|
+
assert.equal(Object.prototype.hasOwnProperty.call(grokAuto, action), false)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
assert.equal(grokAuto.run.length, grok.run.length)
|
|
54
|
+
for (let index = 0; index < grok.run.length; index += 1) {
|
|
55
|
+
const baseStep = grok.run[index]
|
|
56
|
+
const autoStep = grokAuto.run[index]
|
|
57
|
+
|
|
58
|
+
assert.equal(autoStep.when, baseStep.when)
|
|
59
|
+
assert.equal(autoStep.id, baseStep.id)
|
|
60
|
+
assert.equal(autoStep.method, baseStep.method)
|
|
61
|
+
assert.equal(autoStep.params.path, baseStep.params.path)
|
|
62
|
+
assert.equal(autoStep.params.input, baseStep.params.input)
|
|
63
|
+
assert.equal(autoStep.params.buffer, baseStep.params.buffer)
|
|
64
|
+
assert.equal(autoStep.params.shell, baseStep.params.shell)
|
|
65
|
+
assert.deepEqual(autoStep.params.conda, baseStep.params.conda)
|
|
66
|
+
assert.deepEqual(autoStep.params.message, {
|
|
67
|
+
_: [
|
|
68
|
+
"npx",
|
|
69
|
+
"-y",
|
|
70
|
+
"@xai-official/grok@latest",
|
|
71
|
+
"--always-approve",
|
|
72
|
+
"{{args.prompt || undefined}}"
|
|
73
|
+
]
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
assert.equal(grok.run.every((step) => !step.params.message._.includes("--always-approve")), true)
|
|
78
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const fs = require('node:fs/promises')
|
|
3
|
+
const path = require('node:path')
|
|
4
|
+
const test = require('node:test')
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(__dirname, '..')
|
|
7
|
+
|
|
8
|
+
test('header collapse preserves expanded and legacy minimized header styles', async () => {
|
|
9
|
+
const style = await fs.readFile(path.join(root, 'server/public/style.css'), 'utf8')
|
|
10
|
+
const expanded = style.match(/body\.header-collapse-enabled > header\.navheader:not\(\.minimized\) \{[\s\S]*?\n \}/)?.[0] || ''
|
|
11
|
+
const collapsed = style.match(/body\.header-collapsed > header\.navheader:not\(\.minimized\) \{[\s\S]*?\n \}/)?.[0] || ''
|
|
12
|
+
|
|
13
|
+
assert.match(expanded, /grid-template-rows: minmax\(0, 1fr\)/)
|
|
14
|
+
assert.doesNotMatch(expanded, /(?:height|padding|border): .*!important/)
|
|
15
|
+
assert.match(collapsed, /grid-template-rows: minmax\(0, 0fr\)/)
|
|
16
|
+
assert.match(collapsed, /padding-top: 0 !important/)
|
|
17
|
+
assert.match(collapsed, /padding-bottom: 0 !important/)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('collapse-only script includes do not activate legacy navigation behavior', async () => {
|
|
21
|
+
const [nav, connect, create, common] = await Promise.all([
|
|
22
|
+
fs.readFile(path.join(root, 'server/public/nav.js'), 'utf8'),
|
|
23
|
+
fs.readFile(path.join(root, 'server/views/connect/x.ejs'), 'utf8'),
|
|
24
|
+
fs.readFile(path.join(root, 'server/views/create.ejs'), 'utf8'),
|
|
25
|
+
fs.readFile(path.join(root, 'server/views/partials/app_common_scripts.ejs'), 'utf8')
|
|
26
|
+
])
|
|
27
|
+
const controller = nav.slice(nav.indexOf('(() => {'), nav.indexOf('if (headerCollapseOnly) return;'))
|
|
28
|
+
|
|
29
|
+
assert.ok([connect, create, common].every((source) => /nav\.js" data-header-collapse-only/.test(source)))
|
|
30
|
+
assert.match(nav, /document\.currentScript\?\.hasAttribute\("data-header-collapse-only"\)/)
|
|
31
|
+
assert.match(nav, /if \(headerCollapseOnly\) return;/)
|
|
32
|
+
assert.doesNotMatch(controller, /localStorage|sessionStorage/)
|
|
33
|
+
})
|
|
@@ -18,6 +18,14 @@ test("resolveLauncherPluginSelection returns app-dev plugin query paths", () =>
|
|
|
18
18
|
PluginSources.resolveLauncherPluginSelection("codex-auto"),
|
|
19
19
|
"/plugin/codex-auto/pinokio.js"
|
|
20
20
|
)
|
|
21
|
+
assert.strictEqual(
|
|
22
|
+
PluginSources.resolveLauncherPluginSelection("grok"),
|
|
23
|
+
"/pinokio/run/plugin/grok/pinokio.js"
|
|
24
|
+
)
|
|
25
|
+
assert.strictEqual(
|
|
26
|
+
PluginSources.resolveLauncherPluginSelection("grok-build"),
|
|
27
|
+
"/pinokio/run/plugin/grok/pinokio.js"
|
|
28
|
+
)
|
|
21
29
|
assert.strictEqual(
|
|
22
30
|
PluginSources.resolveLauncherPluginSelection("plugin/local-tool"),
|
|
23
31
|
"/plugin/local-tool/pinokio.js"
|
|
@@ -208,10 +216,20 @@ module.exports = {
|
|
|
208
216
|
const systemPlugins = menu.filter((item) => item.source === "system")
|
|
209
217
|
const localPlugins = menu.filter((item) => item.source === "local")
|
|
210
218
|
|
|
211
|
-
assert.strictEqual(systemPlugins.length,
|
|
219
|
+
assert.strictEqual(systemPlugins.length, 14)
|
|
212
220
|
assert.ok(systemPlugins.every((item) => item.system === true))
|
|
213
221
|
assert.ok(systemPlugins.every((item) => item.href.startsWith("/pinokio/run/plugin/")))
|
|
214
222
|
assert.ok(systemPlugins.every((item) => item.image.startsWith("/pinokio/asset/plugin/")))
|
|
223
|
+
assert.ok(systemPlugins.some((item) => (
|
|
224
|
+
item.title === "Grok Build" &&
|
|
225
|
+
item.href === "/pinokio/run/plugin/grok/pinokio.js" &&
|
|
226
|
+
item.image === "/pinokio/asset/plugin/grok/grok.png"
|
|
227
|
+
)))
|
|
228
|
+
assert.ok(systemPlugins.some((item) => (
|
|
229
|
+
item.title === "Grok Build Auto" &&
|
|
230
|
+
item.href === "/pinokio/run/plugin/grok-auto/pinokio.js" &&
|
|
231
|
+
item.image === "/pinokio/asset/plugin/grok-auto/grok.png"
|
|
232
|
+
)))
|
|
215
233
|
|
|
216
234
|
assert.deepStrictEqual(localPlugins.map((item) => item.title), ["Local Tool"])
|
|
217
235
|
assert.strictEqual(localPlugins[0].href, "/run/plugin/local-tool/pinokio.js")
|