pinokiod 7.5.21 → 7.5.24

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.
@@ -0,0 +1,238 @@
1
+ const assert = require('node:assert/strict')
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
+
7
+ const Socket = require('../server/socket')
8
+ const Util = require('../kernel/util')
9
+
10
+ function createSocket(home) {
11
+ const kernel = {
12
+ homedir: home,
13
+ path: (...parts) => path.resolve(home, ...parts),
14
+ exists: async (targetPath) => {
15
+ try {
16
+ await fs.access(targetPath)
17
+ return true
18
+ } catch (_) {
19
+ return false
20
+ }
21
+ }
22
+ }
23
+ return Object.assign(Object.create(Socket.prototype), {
24
+ parent: { kernel },
25
+ rawLog: {},
26
+ logMeta: {},
27
+ sessions: {}
28
+ })
29
+ }
30
+
31
+ test('plugin source without cwd writes to home-relative logs', async () => {
32
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
33
+ try {
34
+ const socket = createSocket(root)
35
+ const key = path.join(root, 'plugin', 'oc', 'pinokio.js')
36
+
37
+ assert.equal(
38
+ await socket.resolveLogDir(key),
39
+ path.join(root, 'logs', 'plugin', 'oc', 'pinokio.js')
40
+ )
41
+ } finally {
42
+ await fs.rm(root, { recursive: true, force: true })
43
+ }
44
+ })
45
+
46
+ test('plugin source with cwd=undefined does not create repo-relative undefined logs', async () => {
47
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
48
+ try {
49
+ const socket = createSocket(root)
50
+ const key = `${path.join(root, 'plugin', 'oc', 'pinokio.js')}?cwd=undefined`
51
+
52
+ assert.equal(
53
+ await socket.resolveLogDir(key),
54
+ path.join(root, 'logs', 'plugin', 'oc', 'pinokio.js')
55
+ )
56
+ } finally {
57
+ await fs.rm(root, { recursive: true, force: true })
58
+ }
59
+ })
60
+
61
+ test('plugin source with app cwd writes app-local dev logs', async () => {
62
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
63
+ try {
64
+ const socket = createSocket(root)
65
+ const appRoot = path.join(root, 'api', 'example')
66
+ const key = `${path.join(root, 'plugin', 'oc', 'pinokio.js')}?cwd=${encodeURIComponent(appRoot)}`
67
+
68
+ assert.equal(
69
+ await socket.resolveLogDir(key),
70
+ path.join(appRoot, 'logs', 'dev', 'plugin', 'oc', 'pinokio.js')
71
+ )
72
+ } finally {
73
+ await fs.rm(root, { recursive: true, force: true })
74
+ }
75
+ })
76
+
77
+ test('future top-level source with app cwd writes app-local dev logs', async () => {
78
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
79
+ try {
80
+ const socket = createSocket(root)
81
+ const appRoot = path.join(root, 'api', 'example')
82
+ const key = `${path.join(root, 'tasks', 'build', 'run.js')}?cwd=${encodeURIComponent(appRoot)}`
83
+
84
+ assert.equal(
85
+ await socket.resolveLogDir(key),
86
+ path.join(appRoot, 'logs', 'dev', 'tasks', 'build', 'run.js')
87
+ )
88
+ } finally {
89
+ await fs.rm(root, { recursive: true, force: true })
90
+ }
91
+ })
92
+
93
+ test('global source with non-app cwd writes home-relative logs', async () => {
94
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
95
+ try {
96
+ const socket = createSocket(root)
97
+ const workspace = path.join(root, 'workspaces', 'tmp')
98
+ const key = `${path.join(root, 'plugin', 'oc', 'pinokio.js')}?cwd=${encodeURIComponent(workspace)}`
99
+
100
+ assert.equal(
101
+ await socket.resolveLogDir(key),
102
+ path.join(root, 'logs', 'plugin', 'oc', 'pinokio.js')
103
+ )
104
+ } finally {
105
+ await fs.rm(root, { recursive: true, force: true })
106
+ }
107
+ })
108
+
109
+ test('app script source writes app-local api logs', async () => {
110
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
111
+ try {
112
+ const socket = createSocket(root)
113
+ const key = path.join(root, 'api', 'example', 'start.js')
114
+
115
+ assert.equal(
116
+ await socket.resolveLogDir(key),
117
+ path.join(root, 'api', 'example', 'logs', 'api', 'start.js')
118
+ )
119
+ } finally {
120
+ await fs.rm(root, { recursive: true, force: true })
121
+ }
122
+ })
123
+
124
+ test('app script source uses nested pinokio app root when present', async () => {
125
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
126
+ try {
127
+ const socket = createSocket(root)
128
+ await fs.mkdir(path.join(root, 'api', 'example', 'pinokio'), { recursive: true })
129
+ const key = path.join(root, 'api', 'example', 'start.js')
130
+
131
+ assert.equal(
132
+ await socket.resolveLogDir(key),
133
+ path.join(root, 'api', 'example', 'pinokio', 'logs', 'api', 'start.js')
134
+ )
135
+ } finally {
136
+ await fs.rm(root, { recursive: true, force: true })
137
+ }
138
+ })
139
+
140
+ test('future top-level source writes home-relative logs without a special case', async () => {
141
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
142
+ try {
143
+ const socket = createSocket(root)
144
+ const key = path.join(root, 'tasks', 'build', 'run.js')
145
+
146
+ assert.equal(
147
+ await socket.resolveLogDir(key),
148
+ path.join(root, 'logs', 'tasks', 'build', 'run.js')
149
+ )
150
+ } finally {
151
+ await fs.rm(root, { recursive: true, force: true })
152
+ }
153
+ })
154
+
155
+ test('api-like top-level source is not treated as an app script', async () => {
156
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
157
+ try {
158
+ const socket = createSocket(root)
159
+ const key = path.join(root, 'apiary', 'run.js')
160
+
161
+ assert.equal(
162
+ await socket.resolveLogDir(key),
163
+ path.join(root, 'logs', 'apiary', 'run.js')
164
+ )
165
+ } finally {
166
+ await fs.rm(root, { recursive: true, force: true })
167
+ }
168
+ })
169
+
170
+ test('event and output logging use the same terminal log resolver', async () => {
171
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
172
+ try {
173
+ const socket = createSocket(root)
174
+ const key = path.join(root, 'plugin', 'oc', 'pinokio.js')
175
+
176
+ await socket.appendEventLog(key, { source: 'api', method: 'notify' }, 'event')
177
+ socket.sessions[key] = 'session-1'
178
+ await socket.log_buffer(key, 'hello\n', { source: 'api', method: 'shell.run' })
179
+
180
+ const dir = await socket.resolveLogDir(key)
181
+ assert.match(await fs.readFile(path.join(dir, 'events'), 'utf8'), /event/)
182
+ assert.match(await fs.readFile(path.join(dir, 'latest'), 'utf8'), /hello/)
183
+ } finally {
184
+ await fs.rm(root, { recursive: true, force: true })
185
+ }
186
+ })
187
+
188
+ test('plugin output with invalid cwd writes latest under home-relative logs', async () => {
189
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
190
+ try {
191
+ const socket = createSocket(root)
192
+ const key = `${path.join(root, 'plugin', 'oc', 'pinokio.js')}?cwd=undefined`
193
+ socket.sessions[key] = 'session-1'
194
+
195
+ await socket.log_buffer(key, 'hello\n', { source: 'api', method: 'shell.run' })
196
+
197
+ const latest = await fs.readFile(path.join(root, 'logs', 'plugin', 'oc', 'pinokio.js', 'latest'), 'utf8')
198
+ assert.match(latest, /\[api shell\.run\]/)
199
+ assert.match(latest, /hello/)
200
+ } finally {
201
+ await fs.rm(root, { recursive: true, force: true })
202
+ }
203
+ })
204
+
205
+ test('absolute sources outside PINOKIO_HOME do not get guessed log paths', async () => {
206
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
207
+ const outside = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-external-'))
208
+ try {
209
+ const socket = createSocket(root)
210
+
211
+ assert.equal(
212
+ await socket.resolveLogDir(path.join(outside, 'run.js')),
213
+ null
214
+ )
215
+ } finally {
216
+ await fs.rm(root, { recursive: true, force: true })
217
+ await fs.rm(outside, { recursive: true, force: true })
218
+ }
219
+ })
220
+
221
+ test('legacy shell workspace logs remain workspace-owned', async () => {
222
+ const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-terminal-log-'))
223
+ const workspace = process.platform === 'win32'
224
+ ? await fs.mkdtemp(path.join(os.tmpdir(), 'pinokioterminalworkspace-'))
225
+ : await fs.mkdtemp('/tmp/pinokioterminalworkspace-')
226
+ try {
227
+ const socket = createSocket(root)
228
+ const key = `shell/${Util.p2u(workspace)}_0`
229
+
230
+ assert.equal(
231
+ await socket.resolveLogDir(key),
232
+ path.join(workspace, 'logs', 'shell')
233
+ )
234
+ } finally {
235
+ await fs.rm(root, { recursive: true, force: true })
236
+ await fs.rm(workspace, { recursive: true, force: true })
237
+ }
238
+ })
@@ -136,4 +136,54 @@ test.describe('universal launcher smoke', () => {
136
136
  await deleteTask(request, tempPluginTask && tempPluginTask.id);
137
137
  }
138
138
  });
139
+
140
+ test('plugins page create and download buttons open plugin launcher on expected tabs', async ({ page }) => {
141
+ const pageErrors = [];
142
+ const consoleErrors = [];
143
+
144
+ page.on('pageerror', (error) => {
145
+ pageErrors.push(String(error));
146
+ });
147
+
148
+ page.on('console', (msg) => {
149
+ if (msg.type() === 'error') {
150
+ consoleErrors.push(msg.text());
151
+ }
152
+ });
153
+
154
+ await page.goto(`${BASE_URL}/plugins`, { waitUntil: 'networkidle' });
155
+
156
+ const createTrigger = page.locator('.task-shell-header [data-universal-launcher-open="create_plugin"]:not([data-universal-launcher-mode])').first();
157
+ await expect(createTrigger).toBeVisible();
158
+ await createTrigger.click();
159
+
160
+ const overlay = page.locator('.universal-launcher-overlay:not([hidden])').first();
161
+ await expect(overlay).toBeVisible();
162
+ await expect(overlay.locator('.universal-launcher-title')).toHaveText('Create Plugin');
163
+ await expect(overlay.locator('.universal-launcher-mode[data-mode="primary"]')).toHaveClass(/active/);
164
+ await expect(overlay.locator('.universal-launcher-section-prompt')).toBeVisible();
165
+ await expect(overlay.locator('.universal-launcher-section-tools')).toBeVisible();
166
+ await expect(overlay.locator('.universal-launcher-section-download')).toBeHidden();
167
+
168
+ await overlay.locator('.universal-launcher-close').click();
169
+ await expect(overlay).toBeHidden();
170
+
171
+ const downloadTrigger = page.locator('.task-shell-header [data-universal-launcher-open="create_plugin"][data-universal-launcher-mode="download"]').first();
172
+ await expect(downloadTrigger).toBeVisible();
173
+ await downloadTrigger.click();
174
+
175
+ await expect(overlay).toBeVisible();
176
+ await expect(overlay.locator('.universal-launcher-title')).toHaveText('Create Plugin');
177
+ await expect(overlay.locator('.universal-launcher-mode[data-mode="download"]')).toHaveClass(/active/);
178
+ await expect(overlay.locator('.universal-launcher-section-download')).toBeVisible();
179
+ await expect(overlay.locator('.universal-launcher-section-prompt')).toBeHidden();
180
+ await expect(overlay.locator('.universal-launcher-section-tools')).toBeHidden();
181
+ await expect(overlay.locator('.universal-launcher-download-input').first()).toBeFocused();
182
+
183
+ expect(pageErrors, `page errors: ${pageErrors.join('\n')}`).toEqual([]);
184
+ expect(
185
+ consoleErrors.filter((line) => !line.includes('favicon')),
186
+ `console errors: ${consoleErrors.join('\n')}`
187
+ ).toEqual([]);
188
+ });
139
189
  });