pinokiod 7.3.1 → 7.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/kernel/api/github/index.js +444 -0
- package/kernel/api/index.js +199 -11
- package/kernel/api/process/index.js +124 -44
- package/kernel/api/shell_run_template.js +273 -0
- package/kernel/api/uri/index.js +51 -0
- package/kernel/bin/git.js +9 -10
- package/kernel/bin/huggingface.js +1 -1
- package/kernel/bin/zip.js +9 -1
- package/kernel/connect/providers/github/README.md +5 -4
- package/kernel/environment.js +195 -92
- package/kernel/git.js +126 -19
- package/kernel/gitconfig_template +7 -0
- package/kernel/gpu/amd.js +72 -0
- package/kernel/gpu/apple.js +8 -0
- package/kernel/gpu/common.js +12 -0
- package/kernel/gpu/intel.js +47 -0
- package/kernel/gpu/nvidia.js +8 -0
- package/kernel/index.js +11 -1
- package/kernel/managed_skills.js +871 -0
- package/kernel/plugin.js +6 -58
- package/kernel/plugin_sources.js +316 -0
- package/kernel/resource_usage/gpu.js +349 -0
- package/kernel/resource_usage/index.js +322 -0
- package/kernel/resource_usage/macos_footprint.js +197 -0
- package/kernel/resource_usage/preferences.js +92 -0
- package/kernel/resource_usage/process_tree.js +303 -0
- package/kernel/scripts/git/create +4 -4
- package/kernel/scripts/git/fork +7 -8
- package/kernel/shell.js +23 -2
- package/kernel/shells.js +41 -0
- package/kernel/sysinfo.js +62 -9
- package/kernel/util.js +60 -0
- package/package.json +1 -1
- package/server/index.js +984 -156
- package/server/lib/app_log_report.js +543 -0
- package/server/lib/content_validation.js +55 -33
- package/server/lib/launcher_instruction_bootstrap.js +4 -96
- package/server/lib/terminal_session_helpers.js +0 -3
- package/server/public/common.js +77 -31
- package/server/public/create-launcher.js +4 -32
- package/server/public/logs.js +1428 -0
- package/server/public/nav.js +7 -0
- package/server/public/plugin-detail.js +93 -10
- package/server/public/privacy_filter_worker.js +391 -0
- package/server/public/style.css +1104 -154
- package/server/public/task-launcher.js +8 -29
- package/server/public/universal-launcher.css +8 -6
- package/server/public/universal-launcher.js +3 -27
- package/server/routes/apps.js +195 -1
- package/server/views/app.ejs +3041 -717
- package/server/views/autolaunch.ejs +917 -0
- package/server/views/bootstrap.ejs +7 -1
- package/server/views/d.ejs +408 -65
- package/server/views/editor.ejs +85 -19
- package/server/views/index.ejs +661 -111
- package/server/views/init/index.ejs +1 -1
- package/server/views/install.ejs +1 -1
- package/server/views/logs.ejs +164 -86
- package/server/views/net.ejs +7 -1
- package/server/views/partials/d_terminal_column.ejs +2 -2
- package/server/views/partials/d_terminal_options.ejs +0 -8
- package/server/views/partials/fs_status.ejs +47 -0
- package/server/views/partials/home_action_modal.ejs +86 -0
- package/server/views/partials/home_run_menu.ejs +87 -0
- package/server/views/partials/main_sidebar.ejs +2 -0
- package/server/views/partials/menu.ejs +1 -1
- package/server/views/plugin_detail.ejs +19 -4
- package/server/views/plugins.ejs +201 -3
- package/server/views/pre.ejs +1 -1
- package/server/views/pro.ejs +1 -1
- package/server/views/shell.ejs +40 -18
- package/server/views/skills.ejs +506 -0
- package/server/views/terminal.ejs +45 -19
- package/spec/INSTRUCTION_SYNC.md +20 -10
- package/system/plugin/antigravity-cli/antigravity.png +0 -0
- package/system/plugin/antigravity-cli/common.js +155 -0
- package/system/plugin/antigravity-cli/install.js +272 -0
- package/system/plugin/antigravity-cli/pinokio.js +13 -0
- package/system/plugin/antigravity-cli-auto/antigravity.png +0 -0
- package/system/plugin/antigravity-cli-auto/pinokio.js +13 -0
- package/system/plugin/claude/claude.png +0 -0
- package/system/plugin/claude/pinokio.js +47 -0
- package/system/plugin/claude-auto/claude.png +0 -0
- package/system/plugin/claude-auto/pinokio.js +58 -0
- package/system/plugin/claude-desktop/icon.jpeg +0 -0
- package/system/plugin/claude-desktop/pinokio.js +23 -0
- package/system/plugin/codex/openai.webp +0 -0
- package/system/plugin/codex/pinokio.js +42 -0
- package/system/plugin/codex-auto/openai.webp +0 -0
- package/system/plugin/codex-auto/pinokio.js +49 -0
- package/system/plugin/codex-desktop/icon.png +0 -0
- package/system/plugin/codex-desktop/pinokio.js +23 -0
- package/system/plugin/crush/crush.png +0 -0
- package/system/plugin/crush/pinokio.js +15 -0
- package/system/plugin/cursor/cursor.jpeg +0 -0
- package/system/plugin/cursor/pinokio.js +23 -0
- package/system/plugin/qwen/pinokio.js +34 -0
- package/system/plugin/qwen/qwen.png +0 -0
- package/system/plugin/vscode/pinokio.js +20 -0
- package/system/plugin/vscode/vscode.png +0 -0
- package/system/plugin/windsurf/pinokio.js +23 -0
- package/system/plugin/windsurf/windsurf.png +0 -0
- package/test/antigravity-cli-plugin.test.js +185 -0
- package/test/app-api.test.js +239 -0
- package/test/app-log-report.test.js +67 -0
- package/test/environment-cache-preflight.test.js +98 -0
- package/test/git-bin.test.js +59 -0
- package/test/git-defaults.test.js +150 -0
- package/test/github-api.test.js +158 -0
- package/test/github-connection.test.js +117 -0
- package/test/huggingface-bin.test.js +25 -0
- package/test/managed-skills.test.js +351 -0
- package/test/plugin-action-functions.test.js +337 -0
- package/test/plugin-dev-iframe.test.js +17 -0
- package/test/plugin-sources.test.js +203 -0
- package/test/privacy-filter-worker-heuristics.test.js +69 -0
- package/test/process-wait.test.js +169 -0
- package/test/script-api.test.js +97 -0
- package/test/shell-api.test.js +134 -0
- package/test/shell-run-template.test.js +209 -0
- package/test/storage-api.test.js +137 -0
- package/test/uri-api.test.js +100 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const assert = require("assert")
|
|
2
|
+
const fs = require("node:fs/promises")
|
|
3
|
+
const os = require("node:os")
|
|
4
|
+
const path = require("node:path")
|
|
5
|
+
const test = require("node:test")
|
|
6
|
+
const PluginSources = require("../kernel/plugin_sources")
|
|
7
|
+
|
|
8
|
+
test("resolveLauncherPluginSelection returns app-dev plugin query paths", () => {
|
|
9
|
+
assert.strictEqual(
|
|
10
|
+
PluginSources.resolveLauncherPluginSelection("claude"),
|
|
11
|
+
"/pinokio/run/plugin/claude/pinokio.js"
|
|
12
|
+
)
|
|
13
|
+
assert.strictEqual(
|
|
14
|
+
PluginSources.resolveLauncherPluginSelection("antigravity-cli"),
|
|
15
|
+
"/pinokio/run/plugin/antigravity-cli/pinokio.js"
|
|
16
|
+
)
|
|
17
|
+
assert.strictEqual(
|
|
18
|
+
PluginSources.resolveLauncherPluginSelection("codex-auto"),
|
|
19
|
+
"/plugin/codex-auto/pinokio.js"
|
|
20
|
+
)
|
|
21
|
+
assert.strictEqual(
|
|
22
|
+
PluginSources.resolveLauncherPluginSelection("plugin/local-tool"),
|
|
23
|
+
"/plugin/local-tool/pinokio.js"
|
|
24
|
+
)
|
|
25
|
+
assert.strictEqual(
|
|
26
|
+
PluginSources.resolveLauncherPluginSelection("api/my-app/plugins/helper"),
|
|
27
|
+
"/api/my-app/plugins/helper/pinokio.js"
|
|
28
|
+
)
|
|
29
|
+
assert.strictEqual(
|
|
30
|
+
PluginSources.resolveLauncherPluginSelection("pinokio/run/plugin/codex-auto"),
|
|
31
|
+
"/pinokio/run/plugin/codex-auto/pinokio.js"
|
|
32
|
+
)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test("normalizeActionPathComponents maps action URLs back to filesystem roots", () => {
|
|
36
|
+
assert.deepStrictEqual(
|
|
37
|
+
PluginSources.normalizeActionPathComponents(["pinokio", "run", "plugin", "antigravity-cli", "pinokio.js"]),
|
|
38
|
+
{
|
|
39
|
+
system: true,
|
|
40
|
+
pathComponents: ["plugin", "antigravity-cli", "pinokio.js"],
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
assert.deepStrictEqual(
|
|
44
|
+
PluginSources.normalizeActionPathComponents(["plugin", "local-tool", "pinokio.js"]),
|
|
45
|
+
{
|
|
46
|
+
system: false,
|
|
47
|
+
pathComponents: ["plugin", "local-tool", "pinokio.js"],
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
assert.deepStrictEqual(
|
|
51
|
+
PluginSources.normalizeActionPathComponents(["run", "plugin", "local-tool", "pinokio.js"]),
|
|
52
|
+
{
|
|
53
|
+
system: false,
|
|
54
|
+
pathComponents: ["plugin", "local-tool", "pinokio.js"],
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
assert.deepStrictEqual(
|
|
58
|
+
PluginSources.normalizeActionPathComponents(["api", "my-app", "plugins", "helper", "pinokio.js"]),
|
|
59
|
+
{
|
|
60
|
+
system: false,
|
|
61
|
+
pathComponents: ["api", "my-app", "plugins", "helper", "pinokio.js"],
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test("normalizeLauncherSuccessPlugin rewrites prototype plugin redirects", () => {
|
|
67
|
+
assert.strictEqual(
|
|
68
|
+
PluginSources.normalizeLauncherSuccessPlugin(
|
|
69
|
+
"/p/example/dev?plugin=/plugin/pinokio/run/plugin/codex-auto/pinokio.js&prompt=build",
|
|
70
|
+
"pinokio/run/plugin/codex-auto"
|
|
71
|
+
),
|
|
72
|
+
"/p/example/dev?plugin=%2Fpinokio%2Frun%2Fplugin%2Fcodex-auto%2Fpinokio.js&prompt=build"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
assert.strictEqual(
|
|
76
|
+
PluginSources.normalizeLauncherSuccessPlugin(
|
|
77
|
+
"/p/example/dev?plugin=/plugin/api/my-app/plugins/helper/pinokio.js",
|
|
78
|
+
"api/my-app/plugins/helper"
|
|
79
|
+
),
|
|
80
|
+
"/p/example/dev?plugin=%2Fapi%2Fmy-app%2Fplugins%2Fhelper%2Fpinokio.js"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
assert.strictEqual(
|
|
84
|
+
PluginSources.normalizeLauncherSuccessPlugin("/p/example/dev", "pinokio/run/plugin/codex-auto"),
|
|
85
|
+
"/p/example/dev"
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test("isValidPluginConfig accepts only action functions and valid standalone plugin paths", () => {
|
|
90
|
+
assert.strictEqual(
|
|
91
|
+
PluginSources.isValidPluginConfig({ run: async () => [] }),
|
|
92
|
+
true
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert.strictEqual(
|
|
96
|
+
PluginSources.isValidPluginConfig({
|
|
97
|
+
run: [{ method: "shell.run", params: { message: "echo ok" } }],
|
|
98
|
+
install: async () => [],
|
|
99
|
+
update: async () => [],
|
|
100
|
+
uninstall: async () => [],
|
|
101
|
+
installed: async () => true,
|
|
102
|
+
}),
|
|
103
|
+
true
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
assert.strictEqual(
|
|
107
|
+
PluginSources.isValidPluginConfig({
|
|
108
|
+
run: [{ method: "shell.run", params: { message: "echo ok" } }],
|
|
109
|
+
helper: () => [],
|
|
110
|
+
}),
|
|
111
|
+
false
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
assert.strictEqual(
|
|
115
|
+
PluginSources.isValidPluginConfig({
|
|
116
|
+
run: [{ method: "shell.run", params: { message: "echo ok" } }],
|
|
117
|
+
install: "install.js",
|
|
118
|
+
}),
|
|
119
|
+
false
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
assert.strictEqual(
|
|
123
|
+
PluginSources.isValidPluginConfig({
|
|
124
|
+
run: [{ method: "shell.run", params: { message: "echo ok" } }],
|
|
125
|
+
installed: true,
|
|
126
|
+
}),
|
|
127
|
+
false
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
assert.strictEqual(
|
|
131
|
+
PluginSources.isValidPluginConfig(
|
|
132
|
+
{ path: "plugin", run: async () => [] },
|
|
133
|
+
{ standalone: true }
|
|
134
|
+
),
|
|
135
|
+
true
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
assert.strictEqual(
|
|
139
|
+
PluginSources.isValidPluginConfig(
|
|
140
|
+
{ path: "api/example/plugins/tool", run: async () => [] },
|
|
141
|
+
{ standalone: true }
|
|
142
|
+
),
|
|
143
|
+
false
|
|
144
|
+
)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test("loadPluginMenu merges bundled plugins and valid standalone local plugins", async () => {
|
|
148
|
+
const homedir = await fs.mkdtemp(path.join(os.tmpdir(), "pinokio-plugin-menu-"))
|
|
149
|
+
try {
|
|
150
|
+
await fs.mkdir(path.join(homedir, "plugin", "local-tool"), { recursive: true })
|
|
151
|
+
await fs.writeFile(path.join(homedir, "plugin", "local-tool", "pinokio.js"), `
|
|
152
|
+
module.exports = {
|
|
153
|
+
path: "plugin",
|
|
154
|
+
title: "Local Tool",
|
|
155
|
+
icon: "local.png",
|
|
156
|
+
run: [{ method: "shell.run", params: { message: "echo local" } }]
|
|
157
|
+
}
|
|
158
|
+
`)
|
|
159
|
+
|
|
160
|
+
await fs.mkdir(path.join(homedir, "plugin", "code", "legacy"), { recursive: true })
|
|
161
|
+
await fs.writeFile(path.join(homedir, "plugin", "code", "legacy", "pinokio.js"), `
|
|
162
|
+
module.exports = {
|
|
163
|
+
path: "plugin",
|
|
164
|
+
title: "Legacy Code Plugin",
|
|
165
|
+
run: [{ method: "shell.run", params: { message: "echo legacy" } }]
|
|
166
|
+
}
|
|
167
|
+
`)
|
|
168
|
+
|
|
169
|
+
await fs.mkdir(path.join(homedir, "plugin", "app-owned"), { recursive: true })
|
|
170
|
+
await fs.writeFile(path.join(homedir, "plugin", "app-owned", "pinokio.js"), `
|
|
171
|
+
module.exports = {
|
|
172
|
+
path: "api/example/plugins/tool",
|
|
173
|
+
title: "App Owned",
|
|
174
|
+
run: [{ method: "shell.run", params: { message: "echo app" } }]
|
|
175
|
+
}
|
|
176
|
+
`)
|
|
177
|
+
|
|
178
|
+
const repoRoot = path.resolve(__dirname, "..")
|
|
179
|
+
const kernel = {
|
|
180
|
+
homedir,
|
|
181
|
+
systemPath: (...parts) => path.join(repoRoot, "system", ...parts),
|
|
182
|
+
require: async (filepath) => {
|
|
183
|
+
delete require.cache[require.resolve(filepath)]
|
|
184
|
+
return require(filepath)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const menu = await PluginSources.loadPluginMenu(kernel)
|
|
189
|
+
const systemPlugins = menu.filter((item) => item.source === "system")
|
|
190
|
+
const localPlugins = menu.filter((item) => item.source === "local")
|
|
191
|
+
|
|
192
|
+
assert.strictEqual(systemPlugins.length, 13)
|
|
193
|
+
assert.ok(systemPlugins.every((item) => item.system === true))
|
|
194
|
+
assert.ok(systemPlugins.every((item) => item.href.startsWith("/pinokio/run/plugin/")))
|
|
195
|
+
assert.ok(systemPlugins.every((item) => item.image.startsWith("/pinokio/asset/plugin/")))
|
|
196
|
+
|
|
197
|
+
assert.deepStrictEqual(localPlugins.map((item) => item.title), ["Local Tool"])
|
|
198
|
+
assert.strictEqual(localPlugins[0].href, "/run/plugin/local-tool/pinokio.js")
|
|
199
|
+
assert.strictEqual(localPlugins[0].image, "/asset/plugin/local-tool/local.png")
|
|
200
|
+
} finally {
|
|
201
|
+
await fs.rm(homedir, { recursive: true, force: true })
|
|
202
|
+
}
|
|
203
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
const vm = require('node:vm')
|
|
6
|
+
|
|
7
|
+
function loadWorkerHelpers() {
|
|
8
|
+
const workerPath = path.resolve(__dirname, '..', 'server', 'public', 'privacy_filter_worker.js')
|
|
9
|
+
const code = `${fs.readFileSync(workerPath, 'utf8')}\n;globalThis.__test = { prepareMaskEntities, maskText }`
|
|
10
|
+
const sandbox = {
|
|
11
|
+
URL,
|
|
12
|
+
console,
|
|
13
|
+
performance: { now: () => 0 },
|
|
14
|
+
self: {
|
|
15
|
+
postMessage: () => {},
|
|
16
|
+
addEventListener: () => {}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
vm.runInNewContext(code, sandbox, { filename: workerPath })
|
|
20
|
+
return sandbox.__test
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test('privacy filter preserves URLs by default and masks URL credentials only', () => {
|
|
24
|
+
const { prepareMaskEntities, maskText } = loadWorkerHelpers()
|
|
25
|
+
|
|
26
|
+
const publicText = [
|
|
27
|
+
'+ mlx==0.31.3 (from git+https://github.com/ml-explore/mlx.git)',
|
|
28
|
+
'+ tool==1.0.0 (from https://example.org/packages/tool.tar.gz)',
|
|
29
|
+
'+ sshpkg (from git@gitlab.com:group/project.git)'
|
|
30
|
+
].join('\n')
|
|
31
|
+
const publicStart = publicText.indexOf('https://github.com')
|
|
32
|
+
assert.equal(
|
|
33
|
+
prepareMaskEntities(publicText, [{
|
|
34
|
+
label: 'private_url',
|
|
35
|
+
start: publicStart,
|
|
36
|
+
end: publicText.indexOf(')', publicStart)
|
|
37
|
+
}]).length,
|
|
38
|
+
0
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
const credentialText = 'from https://x-access-token:ghp_secret@github.com/org/private.git'
|
|
42
|
+
const credentialStart = credentialText.indexOf('https://')
|
|
43
|
+
const credentialMasked = maskText(credentialText, prepareMaskEntities(credentialText, [{
|
|
44
|
+
label: 'private_url',
|
|
45
|
+
start: credentialStart,
|
|
46
|
+
end: credentialText.length
|
|
47
|
+
}])).masked
|
|
48
|
+
assert.equal(credentialMasked, 'from https://[url_credential]@github.com/org/private.git')
|
|
49
|
+
|
|
50
|
+
const tokenText = 'from https://github.com/org/repo.git?token=secret'
|
|
51
|
+
const tokenStart = tokenText.indexOf('https://')
|
|
52
|
+
const tokenMasked = maskText(tokenText, prepareMaskEntities(tokenText, [{
|
|
53
|
+
label: 'private_url',
|
|
54
|
+
start: tokenStart,
|
|
55
|
+
end: tokenText.length
|
|
56
|
+
}])).masked
|
|
57
|
+
assert.equal(tokenMasked, 'from https://github.com/org/repo.git?token=[url_secret]')
|
|
58
|
+
|
|
59
|
+
const privateHostText = 'callback https://customer.example.test/install'
|
|
60
|
+
const privateHostStart = privateHostText.indexOf('https://')
|
|
61
|
+
assert.equal(
|
|
62
|
+
prepareMaskEntities(privateHostText, [{
|
|
63
|
+
label: 'private_url',
|
|
64
|
+
start: privateHostStart,
|
|
65
|
+
end: privateHostText.length
|
|
66
|
+
}]).length,
|
|
67
|
+
0
|
|
68
|
+
)
|
|
69
|
+
})
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const assert = require("node:assert/strict")
|
|
2
|
+
const test = require("node:test")
|
|
3
|
+
|
|
4
|
+
const Process = require("../kernel/api/process")
|
|
5
|
+
|
|
6
|
+
test("process.wait tracks active waits with metadata and clears them when done", async () => {
|
|
7
|
+
const processApi = new Process()
|
|
8
|
+
const waitPath = "/pinokio/api/demo/start.js"
|
|
9
|
+
const kernel = {
|
|
10
|
+
activeProcessWaits: {},
|
|
11
|
+
procs: {}
|
|
12
|
+
}
|
|
13
|
+
const events = []
|
|
14
|
+
const req = {
|
|
15
|
+
parent: { path: waitPath },
|
|
16
|
+
params: {
|
|
17
|
+
sec: 0.001,
|
|
18
|
+
title: "Launching",
|
|
19
|
+
description: "Waiting for app",
|
|
20
|
+
message: "Hold on"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const waitPromise = processApi.wait(req, (data, type) => {
|
|
25
|
+
events.push({ data, type })
|
|
26
|
+
}, kernel)
|
|
27
|
+
|
|
28
|
+
assert.equal(kernel.activeProcessWaits[waitPath].path, waitPath)
|
|
29
|
+
assert.equal(kernel.activeProcessWaits[waitPath].title, "Launching")
|
|
30
|
+
assert.equal(kernel.activeProcessWaits[waitPath].description, "Waiting for app")
|
|
31
|
+
assert.equal(kernel.activeProcessWaits[waitPath].message, "Hold on")
|
|
32
|
+
assert.equal(kernel.activeProcessWaits[waitPath].params, req.params)
|
|
33
|
+
|
|
34
|
+
await waitPromise
|
|
35
|
+
|
|
36
|
+
assert.equal(kernel.activeProcessWaits[waitPath], undefined)
|
|
37
|
+
assert.deepEqual(events.map((event) => event.type), [
|
|
38
|
+
"process.wait.start",
|
|
39
|
+
"process.wait.end"
|
|
40
|
+
])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("process.wait preserves existing no-metadata waits without footer events", async () => {
|
|
44
|
+
const processApi = new Process()
|
|
45
|
+
const waitPath = "/pinokio/api/demo/start.js"
|
|
46
|
+
const kernel = {
|
|
47
|
+
activeProcessWaits: {},
|
|
48
|
+
procs: {}
|
|
49
|
+
}
|
|
50
|
+
const events = []
|
|
51
|
+
const req = {
|
|
52
|
+
parent: { path: waitPath },
|
|
53
|
+
params: { sec: 0.001 }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const waitPromise = processApi.wait(req, (data, type) => {
|
|
57
|
+
events.push({ data, type })
|
|
58
|
+
}, kernel)
|
|
59
|
+
|
|
60
|
+
assert.equal(kernel.activeProcessWaits[waitPath].path, waitPath)
|
|
61
|
+
await waitPromise
|
|
62
|
+
|
|
63
|
+
assert.equal(kernel.activeProcessWaits[waitPath], undefined)
|
|
64
|
+
assert.deepEqual(events, [])
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test("process.wait shows default footer metadata for indefinite waits", async () => {
|
|
68
|
+
const processApi = new Process()
|
|
69
|
+
const waitPath = "/pinokio/api/demo/start.js"
|
|
70
|
+
const kernel = {
|
|
71
|
+
activeProcessWaits: {},
|
|
72
|
+
procs: {}
|
|
73
|
+
}
|
|
74
|
+
const events = []
|
|
75
|
+
const req = {
|
|
76
|
+
parent: { path: waitPath }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const waitPromise = processApi.wait(req, (data, type) => {
|
|
80
|
+
events.push({ data, type })
|
|
81
|
+
}, kernel)
|
|
82
|
+
|
|
83
|
+
assert.equal(req.params.title, "Waiting")
|
|
84
|
+
assert.equal(req.params.description, "Click Stop when done.")
|
|
85
|
+
assert.equal(kernel.activeProcessWaits[waitPath].title, "Waiting")
|
|
86
|
+
assert.equal(kernel.activeProcessWaits[waitPath].description, "Click Stop when done.")
|
|
87
|
+
assert.equal(kernel.procs[waitPath], processApi)
|
|
88
|
+
assert.deepEqual(events.map((event) => event.type), ["process.wait.start"])
|
|
89
|
+
|
|
90
|
+
processApi.resolve()
|
|
91
|
+
await waitPromise
|
|
92
|
+
|
|
93
|
+
assert.equal(kernel.activeProcessWaits[waitPath], undefined)
|
|
94
|
+
assert.deepEqual(events.map((event) => event.type), [
|
|
95
|
+
"process.wait.start",
|
|
96
|
+
"process.wait.end"
|
|
97
|
+
])
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test("process.wait shows message-only indefinite waits in the footer", async () => {
|
|
101
|
+
const processApi = new Process()
|
|
102
|
+
const waitPath = "/pinokio/api/demo/start.js"
|
|
103
|
+
const kernel = {
|
|
104
|
+
activeProcessWaits: {},
|
|
105
|
+
procs: {}
|
|
106
|
+
}
|
|
107
|
+
const events = []
|
|
108
|
+
const req = {
|
|
109
|
+
parent: { path: waitPath },
|
|
110
|
+
params: {
|
|
111
|
+
message: "Waiting for external app"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const waitPromise = processApi.wait(req, (data, type) => {
|
|
116
|
+
events.push({ data, type })
|
|
117
|
+
}, kernel)
|
|
118
|
+
|
|
119
|
+
assert.equal(req.params.title, undefined)
|
|
120
|
+
assert.equal(req.params.description, undefined)
|
|
121
|
+
assert.equal(kernel.activeProcessWaits[waitPath].message, "Waiting for external app")
|
|
122
|
+
assert.deepEqual(events.map((event) => event.type), ["process.wait.start"])
|
|
123
|
+
|
|
124
|
+
processApi.resolve()
|
|
125
|
+
await waitPromise
|
|
126
|
+
|
|
127
|
+
assert.equal(kernel.activeProcessWaits[waitPath], undefined)
|
|
128
|
+
assert.deepEqual(events.map((event) => event.type), [
|
|
129
|
+
"process.wait.start",
|
|
130
|
+
"process.wait.end"
|
|
131
|
+
])
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test("process.wait dispatches app presence waits through AppAPI", async () => {
|
|
135
|
+
const processApi = new Process()
|
|
136
|
+
const waitPath = "/pinokio/api/demo/start.js"
|
|
137
|
+
const kernel = {
|
|
138
|
+
activeProcessWaits: {},
|
|
139
|
+
procs: {}
|
|
140
|
+
}
|
|
141
|
+
const calls = []
|
|
142
|
+
processApi.appApi = {
|
|
143
|
+
waitForAppPresence: async (req, ondata, kernelArg) => {
|
|
144
|
+
calls.push({ req, kernel: kernelArg })
|
|
145
|
+
return { id: req.params.app }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const req = {
|
|
149
|
+
parent: { path: waitPath },
|
|
150
|
+
params: {
|
|
151
|
+
app: "Native Tool",
|
|
152
|
+
title: "Waiting for Native Tool"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const events = []
|
|
156
|
+
|
|
157
|
+
await processApi.wait(req, (data, type) => {
|
|
158
|
+
events.push({ data, type })
|
|
159
|
+
}, kernel)
|
|
160
|
+
|
|
161
|
+
assert.equal(calls.length, 1)
|
|
162
|
+
assert.equal(calls[0].req, req)
|
|
163
|
+
assert.equal(calls[0].kernel, kernel)
|
|
164
|
+
assert.equal(kernel.activeProcessWaits[waitPath], undefined)
|
|
165
|
+
assert.deepEqual(events.map((event) => event.type), [
|
|
166
|
+
"process.wait.start",
|
|
167
|
+
"process.wait.end"
|
|
168
|
+
])
|
|
169
|
+
})
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const path = require('node:path')
|
|
3
|
+
const test = require('node:test')
|
|
4
|
+
|
|
5
|
+
const Script = require('../kernel/api/script')
|
|
6
|
+
|
|
7
|
+
test('script.restart stops explicit target and schedules start with explicit params', async () => {
|
|
8
|
+
const script = new Script()
|
|
9
|
+
const scheduled = []
|
|
10
|
+
script.scheduleStart = (req) => {
|
|
11
|
+
scheduled.push(req)
|
|
12
|
+
}
|
|
13
|
+
const stopped = []
|
|
14
|
+
const kernel = {
|
|
15
|
+
memory: { args: {} },
|
|
16
|
+
api: {
|
|
17
|
+
filePath: (uri, cwd) => path.resolve(cwd, uri),
|
|
18
|
+
stop: async (req) => {
|
|
19
|
+
stopped.push(req)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const events = []
|
|
24
|
+
|
|
25
|
+
const result = await script.restart({
|
|
26
|
+
cwd: '/pinokio/api/demo',
|
|
27
|
+
parent: {
|
|
28
|
+
path: '/pinokio/api/demo/main.js',
|
|
29
|
+
id: 'main-session'
|
|
30
|
+
},
|
|
31
|
+
params: {
|
|
32
|
+
uri: 'child.js',
|
|
33
|
+
params: { prompt: 'hello' }
|
|
34
|
+
}
|
|
35
|
+
}, (data, type) => {
|
|
36
|
+
events.push({ data, type })
|
|
37
|
+
}, kernel)
|
|
38
|
+
|
|
39
|
+
assert.deepEqual(stopped, [
|
|
40
|
+
{ params: { uri: '/pinokio/api/demo/child.js' } }
|
|
41
|
+
])
|
|
42
|
+
assert.equal(scheduled.length, 1)
|
|
43
|
+
assert.equal(scheduled[0].params.uri, 'child.js')
|
|
44
|
+
assert.deepEqual(scheduled[0].params.params, { prompt: 'hello' })
|
|
45
|
+
assert.deepEqual(result, {
|
|
46
|
+
uri: 'child.js',
|
|
47
|
+
scheduled: true,
|
|
48
|
+
self: false,
|
|
49
|
+
params: { prompt: 'hello' }
|
|
50
|
+
})
|
|
51
|
+
assert.equal(events[0].type, 'restart')
|
|
52
|
+
assert.equal(events[0].data.id, '/pinokio/api/demo/child.js')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('script.restart self restart preserves session id and parent args', async () => {
|
|
56
|
+
const script = new Script()
|
|
57
|
+
const scheduled = []
|
|
58
|
+
script.scheduleStart = (req) => {
|
|
59
|
+
scheduled.push(req)
|
|
60
|
+
}
|
|
61
|
+
const stopped = []
|
|
62
|
+
const kernel = {
|
|
63
|
+
memory: { args: {} },
|
|
64
|
+
api: {
|
|
65
|
+
filePath: (uri, cwd) => path.resolve(cwd, uri),
|
|
66
|
+
stop: async (req) => {
|
|
67
|
+
stopped.push(req)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const result = await script.restart({
|
|
73
|
+
cwd: '/pinokio/api/demo',
|
|
74
|
+
parent: {
|
|
75
|
+
path: '/pinokio/api/demo/main.js',
|
|
76
|
+
id: 'main-session',
|
|
77
|
+
caller: 'caller-session',
|
|
78
|
+
args: { prompt: 'again' }
|
|
79
|
+
},
|
|
80
|
+
params: {}
|
|
81
|
+
}, () => {}, kernel)
|
|
82
|
+
|
|
83
|
+
assert.deepEqual(stopped, [
|
|
84
|
+
{ params: { id: 'main-session' } }
|
|
85
|
+
])
|
|
86
|
+
assert.equal(scheduled.length, 1)
|
|
87
|
+
assert.equal(scheduled[0].id, 'main-session')
|
|
88
|
+
assert.equal(scheduled[0].caller, 'caller-session')
|
|
89
|
+
assert.equal(scheduled[0].params.uri, '/pinokio/api/demo/main.js')
|
|
90
|
+
assert.deepEqual(scheduled[0].params.params, { prompt: 'again' })
|
|
91
|
+
assert.deepEqual(result, {
|
|
92
|
+
uri: '/pinokio/api/demo/main.js',
|
|
93
|
+
scheduled: true,
|
|
94
|
+
self: true,
|
|
95
|
+
params: { prompt: 'again' }
|
|
96
|
+
})
|
|
97
|
+
})
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const test = require('node:test')
|
|
2
|
+
const assert = require('node:assert/strict')
|
|
3
|
+
const path = require('node:path')
|
|
4
|
+
const ShellAPI = require('../kernel/api/shell')
|
|
5
|
+
|
|
6
|
+
function createKernel () {
|
|
7
|
+
const calls = []
|
|
8
|
+
return {
|
|
9
|
+
calls,
|
|
10
|
+
homedir: '/tmp/pinokio-home',
|
|
11
|
+
shell: {
|
|
12
|
+
start: async (params, options) => {
|
|
13
|
+
calls.push({ method: 'start', params: structuredClone(params), options: structuredClone(options) })
|
|
14
|
+
return 'shell-id'
|
|
15
|
+
},
|
|
16
|
+
enter: async (params) => {
|
|
17
|
+
calls.push({ method: 'enter', params: structuredClone(params) })
|
|
18
|
+
return 'entered'
|
|
19
|
+
},
|
|
20
|
+
write: async (params) => {
|
|
21
|
+
calls.push({ method: 'write', params: structuredClone(params) })
|
|
22
|
+
return 'written'
|
|
23
|
+
},
|
|
24
|
+
run: async (params, options) => {
|
|
25
|
+
calls.push({ method: 'run', params: structuredClone(params), options: structuredClone(options) })
|
|
26
|
+
return { stdout: 'ok' }
|
|
27
|
+
},
|
|
28
|
+
kill: async (params) => {
|
|
29
|
+
calls.push({ method: 'kill', params: structuredClone(params) })
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test('shell.start forwards defaults, client size, parent, and shell options', async () => {
|
|
36
|
+
const api = new ShellAPI()
|
|
37
|
+
const kernel = createKernel()
|
|
38
|
+
const cwd = path.join('/tmp', 'pinokio-shell-api')
|
|
39
|
+
const result = await api.start({
|
|
40
|
+
cwd,
|
|
41
|
+
parent: {
|
|
42
|
+
id: 'parent-request',
|
|
43
|
+
body: { title: 'Parent Title' }
|
|
44
|
+
},
|
|
45
|
+
client: {
|
|
46
|
+
rows: 33,
|
|
47
|
+
cols: 120
|
|
48
|
+
}
|
|
49
|
+
}, () => {}, kernel)
|
|
50
|
+
|
|
51
|
+
assert.equal(result, 'shell-id')
|
|
52
|
+
assert.deepEqual(kernel.calls, [{
|
|
53
|
+
method: 'start',
|
|
54
|
+
params: {
|
|
55
|
+
id: cwd,
|
|
56
|
+
path: cwd,
|
|
57
|
+
rows: 33,
|
|
58
|
+
cols: 120,
|
|
59
|
+
$parent: {
|
|
60
|
+
id: 'parent-request',
|
|
61
|
+
body: { title: 'Parent Title' }
|
|
62
|
+
},
|
|
63
|
+
bluefairy: 'off'
|
|
64
|
+
},
|
|
65
|
+
options: {
|
|
66
|
+
cwd,
|
|
67
|
+
group: 'parent-request',
|
|
68
|
+
title: 'Parent Title'
|
|
69
|
+
}
|
|
70
|
+
}])
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('shell.enter, shell.write, and shell.stop target current cwd by default', async () => {
|
|
74
|
+
const api = new ShellAPI()
|
|
75
|
+
const kernel = createKernel()
|
|
76
|
+
const cwd = path.join('/tmp', 'pinokio-shell-api')
|
|
77
|
+
const parent = { path: path.join(cwd, 'script.js') }
|
|
78
|
+
|
|
79
|
+
assert.equal(await api.enter({ cwd, parent, params: { message: 'echo hi' } }, () => {}, kernel), 'entered')
|
|
80
|
+
assert.equal(await api.write({ cwd, params: { message: 'typed' } }, () => {}, kernel), 'written')
|
|
81
|
+
await api.stop({ cwd, params: {} }, () => {}, kernel)
|
|
82
|
+
|
|
83
|
+
assert.deepEqual(kernel.calls, [{
|
|
84
|
+
method: 'enter',
|
|
85
|
+
params: {
|
|
86
|
+
message: 'echo hi',
|
|
87
|
+
id: cwd,
|
|
88
|
+
$parent: parent
|
|
89
|
+
}
|
|
90
|
+
}, {
|
|
91
|
+
method: 'write',
|
|
92
|
+
params: {
|
|
93
|
+
message: 'typed',
|
|
94
|
+
id: cwd
|
|
95
|
+
}
|
|
96
|
+
}, {
|
|
97
|
+
method: 'kill',
|
|
98
|
+
params: {
|
|
99
|
+
id: cwd
|
|
100
|
+
}
|
|
101
|
+
}])
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('shell.run applies deterministic defaults and forwards execution options', async () => {
|
|
105
|
+
const api = new ShellAPI()
|
|
106
|
+
const kernel = createKernel()
|
|
107
|
+
const cwd = path.join('/tmp', 'pinokio-shell-api')
|
|
108
|
+
const result = await api.run({
|
|
109
|
+
cwd,
|
|
110
|
+
parent: {
|
|
111
|
+
path: path.join(cwd, 'script.js')
|
|
112
|
+
},
|
|
113
|
+
params: {
|
|
114
|
+
message: 'echo ok'
|
|
115
|
+
}
|
|
116
|
+
}, () => {}, kernel)
|
|
117
|
+
|
|
118
|
+
assert.deepEqual(result, { stdout: 'ok' })
|
|
119
|
+
assert.deepEqual(kernel.calls, [{
|
|
120
|
+
method: 'run',
|
|
121
|
+
params: {
|
|
122
|
+
message: 'echo ok',
|
|
123
|
+
path: cwd,
|
|
124
|
+
$parent: {
|
|
125
|
+
path: path.join(cwd, 'script.js')
|
|
126
|
+
},
|
|
127
|
+
bluefairy: 'off'
|
|
128
|
+
},
|
|
129
|
+
options: {
|
|
130
|
+
cwd,
|
|
131
|
+
group: path.join(cwd, 'script.js')
|
|
132
|
+
}
|
|
133
|
+
}])
|
|
134
|
+
})
|