pinokiod 7.2.6 → 7.2.8
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/index.js +2 -0
- package/kernel/api/shell_run_template.js +273 -0
- package/kernel/shell.js +40 -2
- package/package.json +1 -1
- package/server/index.js +65 -5
- package/server/lib/drafts.js +376 -0
- package/server/lib/workspace_catalog.js +151 -0
- package/server/lib/workspace_runtime.js +390 -0
- package/server/public/common.js +8 -0
- package/server/public/drafts.js +632 -0
- package/server/routes/draft_import.js +469 -0
- package/server/routes/workspaces.js +44 -0
- package/server/socket.js +22 -11
- package/server/views/app.ejs +13 -0
- package/server/views/partials/main_sidebar.ejs +1 -0
- package/server/views/partials/workspace_row.ejs +61 -0
- package/server/views/terminal.ejs +6 -0
- package/server/views/terminals.ejs +1 -0
- package/server/views/workspaces.ejs +812 -0
package/kernel/api/index.js
CHANGED
|
@@ -11,6 +11,7 @@ const fastq = require('fastq')
|
|
|
11
11
|
const Loader = require("../loader")
|
|
12
12
|
const Environment = require("../environment")
|
|
13
13
|
const Util = require('../util')
|
|
14
|
+
const ShellRunTemplate = require('./shell_run_template')
|
|
14
15
|
|
|
15
16
|
class Api {
|
|
16
17
|
constructor(kernel) {
|
|
@@ -1010,6 +1011,7 @@ class Api {
|
|
|
1010
1011
|
}
|
|
1011
1012
|
// replace {{{ }}} with {{ }}
|
|
1012
1013
|
rpc = this.kernel.template.flatten(rpc)
|
|
1014
|
+
rpc = ShellRunTemplate.renderEnvArgs(this.kernel, rpc, memory)
|
|
1013
1015
|
|
|
1014
1016
|
// 6. rpc must have method names
|
|
1015
1017
|
if (rpc.method) {
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
function isCmdShellName(shellName) {
|
|
2
|
+
const name = (shellName || '').toLowerCase()
|
|
3
|
+
return name.includes('cmd.exe') || name === 'cmd'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function isPowerShellName(shellName) {
|
|
7
|
+
const name = (shellName || '').toLowerCase()
|
|
8
|
+
return name.includes('powershell') || name.includes('pwsh')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ENV_ARG_MARKER_RE = /__PINOKIO_ENVARG_(\d+)__/g
|
|
12
|
+
|
|
13
|
+
function envArgMarker(index) {
|
|
14
|
+
return `__PINOKIO_ENVARG_${index}__`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isPinokioEnvArgKey(key) {
|
|
18
|
+
return /^PINOKIO_ARG_\d+$/.test(key || "")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hasEnvArgMarker(value) {
|
|
22
|
+
ENV_ARG_MARKER_RE.lastIndex = 0
|
|
23
|
+
return ENV_ARG_MARKER_RE.test(String(value))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function quotePosixLiteral(value) {
|
|
27
|
+
const input = value == null ? "" : String(value)
|
|
28
|
+
return `'${input.split("'").join("'\"'\"'")}'`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function quotePowerShellComposite(value) {
|
|
32
|
+
const input = value == null ? "" : String(value)
|
|
33
|
+
ENV_ARG_MARKER_RE.lastIndex = 0
|
|
34
|
+
let output = '"'
|
|
35
|
+
let lastIndex = 0
|
|
36
|
+
for (const match of input.matchAll(ENV_ARG_MARKER_RE)) {
|
|
37
|
+
const literal = input.slice(lastIndex, match.index)
|
|
38
|
+
output += literal.replace(/[`"$]/g, (char) => "`" + char)
|
|
39
|
+
output += "${env:PINOKIO_ARG_" + match[1] + "}"
|
|
40
|
+
lastIndex = match.index + match[0].length
|
|
41
|
+
}
|
|
42
|
+
output += input.slice(lastIndex).replace(/[`"$]/g, (char) => "`" + char)
|
|
43
|
+
output += '"'
|
|
44
|
+
return output
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function quoteCmdComposite(value) {
|
|
48
|
+
const input = value == null ? "" : String(value)
|
|
49
|
+
ENV_ARG_MARKER_RE.lastIndex = 0
|
|
50
|
+
let output = '"'
|
|
51
|
+
let lastIndex = 0
|
|
52
|
+
for (const match of input.matchAll(ENV_ARG_MARKER_RE)) {
|
|
53
|
+
const literal = input.slice(lastIndex, match.index)
|
|
54
|
+
output += literal.replace(/([()%!^"<>&|])/g, '^$1')
|
|
55
|
+
output += "!PINOKIO_ARG_" + match[1] + "!"
|
|
56
|
+
lastIndex = match.index + match[0].length
|
|
57
|
+
}
|
|
58
|
+
output += input.slice(lastIndex).replace(/([()%!^"<>&|])/g, '^$1')
|
|
59
|
+
output += '"'
|
|
60
|
+
return output
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function quoteEnvArgComposite(value, shellName) {
|
|
64
|
+
const input = value == null ? "" : String(value)
|
|
65
|
+
if (isCmdShellName(shellName)) {
|
|
66
|
+
return quoteCmdComposite(input)
|
|
67
|
+
}
|
|
68
|
+
if (isPowerShellName(shellName)) {
|
|
69
|
+
return quotePowerShellComposite(input)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
ENV_ARG_MARKER_RE.lastIndex = 0
|
|
73
|
+
const parts = []
|
|
74
|
+
let lastIndex = 0
|
|
75
|
+
for (const match of input.matchAll(ENV_ARG_MARKER_RE)) {
|
|
76
|
+
const literal = input.slice(lastIndex, match.index)
|
|
77
|
+
if (literal) {
|
|
78
|
+
parts.push(quotePosixLiteral(literal))
|
|
79
|
+
}
|
|
80
|
+
parts.push('"$PINOKIO_ARG_' + match[1] + '"')
|
|
81
|
+
lastIndex = match.index + match[0].length
|
|
82
|
+
}
|
|
83
|
+
const tail = input.slice(lastIndex)
|
|
84
|
+
if (tail) {
|
|
85
|
+
parts.push(quotePosixLiteral(tail))
|
|
86
|
+
}
|
|
87
|
+
return parts.length > 0 ? parts.join("") : "''"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function shellNameFor(kernel, params) {
|
|
91
|
+
let shellName = kernel && kernel.platform === "win32" ? "cmd.exe" : "bash"
|
|
92
|
+
if (params && typeof params.shell === "string" && params.shell.trim()) {
|
|
93
|
+
shellName = params.shell
|
|
94
|
+
}
|
|
95
|
+
return shellName
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isPlainObject(value) {
|
|
99
|
+
return value && value.constructor === Object
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function hasMultiline(value) {
|
|
103
|
+
return typeof value === "string" && /[\r\n]/.test(value)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isStructuredArgvMessage(value) {
|
|
107
|
+
return isPlainObject(value) && Array.isArray(value._)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function hasStructuredArgvMessage(value) {
|
|
111
|
+
if (isStructuredArgvMessage(value)) {
|
|
112
|
+
return true
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(value)) {
|
|
115
|
+
return value.some((item) => isStructuredArgvMessage(item))
|
|
116
|
+
}
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function protectStructuredString(value, state) {
|
|
121
|
+
if (!hasMultiline(value)) {
|
|
122
|
+
return value
|
|
123
|
+
}
|
|
124
|
+
const name = `PINOKIO_ARG_${state.args.length}`
|
|
125
|
+
state.args.push({
|
|
126
|
+
name,
|
|
127
|
+
value: value == null ? "" : String(value)
|
|
128
|
+
})
|
|
129
|
+
return envArgMarker(state.args.length - 1)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function protectStructuredValue(value, state) {
|
|
133
|
+
if (typeof value === "string") {
|
|
134
|
+
return protectStructuredString(value, state)
|
|
135
|
+
}
|
|
136
|
+
if (Array.isArray(value)) {
|
|
137
|
+
return value.map((item) => protectStructuredValue(item, state))
|
|
138
|
+
}
|
|
139
|
+
if (isPlainObject(value)) {
|
|
140
|
+
const rendered = {}
|
|
141
|
+
for (const [key, item] of Object.entries(value)) {
|
|
142
|
+
rendered[key] = protectStructuredValue(item, state)
|
|
143
|
+
}
|
|
144
|
+
return rendered
|
|
145
|
+
}
|
|
146
|
+
return value
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function protectStructuredMessage(value, state) {
|
|
150
|
+
if (isStructuredArgvMessage(value)) {
|
|
151
|
+
return protectStructuredValue(value, state)
|
|
152
|
+
}
|
|
153
|
+
if (Array.isArray(value)) {
|
|
154
|
+
return value.map((item) => isStructuredArgvMessage(item) ? protectStructuredValue(item, state) : item)
|
|
155
|
+
}
|
|
156
|
+
return value
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function renderEnvArgs(kernel, rpc, memory) {
|
|
160
|
+
if (!rpc || rpc.method !== "shell.run" || !rpc.params || !hasStructuredArgvMessage(rpc.params.message)) {
|
|
161
|
+
return rpc
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const shellName = shellNameFor(kernel, rpc.params)
|
|
165
|
+
const state = { args: [] }
|
|
166
|
+
const message = protectStructuredMessage(rpc.params.message, state)
|
|
167
|
+
|
|
168
|
+
if (state.args.length === 0) {
|
|
169
|
+
return rpc
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const env = Object.assign({}, rpc.params.env || {})
|
|
173
|
+
for (const arg of state.args) {
|
|
174
|
+
env[arg.name] = arg.value
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
...rpc,
|
|
179
|
+
params: {
|
|
180
|
+
...rpc.params,
|
|
181
|
+
message,
|
|
182
|
+
env,
|
|
183
|
+
_pinokio_env_args: state.args,
|
|
184
|
+
_pinokio_cmd_delayed_expansion: isCmdShellName(shellName)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function envArgDetails(value) {
|
|
190
|
+
const normalized = String(value == null ? "" : value)
|
|
191
|
+
.replace(/\r\n/g, "\n")
|
|
192
|
+
.replace(/\r/g, "\n")
|
|
193
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "?")
|
|
194
|
+
const lines = normalized.split("\n")
|
|
195
|
+
const previewLines = []
|
|
196
|
+
const maxLines = 8
|
|
197
|
+
const maxChars = 800
|
|
198
|
+
let used = 0
|
|
199
|
+
|
|
200
|
+
for (const line of lines.slice(0, maxLines)) {
|
|
201
|
+
const remaining = maxChars - used
|
|
202
|
+
if (remaining <= 0) {
|
|
203
|
+
break
|
|
204
|
+
}
|
|
205
|
+
if (line.length > remaining) {
|
|
206
|
+
previewLines.push(line.slice(0, remaining) + "...")
|
|
207
|
+
used = maxChars
|
|
208
|
+
break
|
|
209
|
+
}
|
|
210
|
+
previewLines.push(line)
|
|
211
|
+
used += line.length + 1
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const preview = previewLines.join("\n")
|
|
215
|
+
const truncated = lines.length > previewLines.length || normalized.length > preview.length
|
|
216
|
+
if (truncated && previewLines[previewLines.length - 1] !== "...") {
|
|
217
|
+
previewLines.push("...")
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
lineCount: normalized.length === 0 ? 0 : lines.length,
|
|
222
|
+
previewLines,
|
|
223
|
+
truncated
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function formatEnvArgsPreview(args) {
|
|
228
|
+
if (!Array.isArray(args) || args.length === 0) {
|
|
229
|
+
return ""
|
|
230
|
+
}
|
|
231
|
+
const lines = ["\r\nPinokio shell args", ""]
|
|
232
|
+
for (const arg of args) {
|
|
233
|
+
const details = envArgDetails(arg.value)
|
|
234
|
+
lines.push(`${arg.name} ${details.lineCount} lines`)
|
|
235
|
+
for (const previewLine of details.previewLines) {
|
|
236
|
+
lines.push(` ${previewLine}`)
|
|
237
|
+
}
|
|
238
|
+
lines.push("")
|
|
239
|
+
}
|
|
240
|
+
return lines.join("\r\n") + "\r\n"
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function envArgSummary(value) {
|
|
244
|
+
const details = envArgDetails(value)
|
|
245
|
+
return {
|
|
246
|
+
type: "pinokio env arg",
|
|
247
|
+
lines: details.lineCount,
|
|
248
|
+
preview: details.previewLines.join("\n"),
|
|
249
|
+
truncated: details.truncated
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function redactEnvArgs(env) {
|
|
254
|
+
if (!env || typeof env !== "object") {
|
|
255
|
+
return env
|
|
256
|
+
}
|
|
257
|
+
const redacted = { ...env }
|
|
258
|
+
for (const key of Object.keys(redacted)) {
|
|
259
|
+
if (isPinokioEnvArgKey(key)) {
|
|
260
|
+
redacted[key] = envArgSummary(redacted[key])
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return redacted
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
renderEnvArgs,
|
|
268
|
+
quoteEnvArgComposite,
|
|
269
|
+
hasEnvArgMarker,
|
|
270
|
+
isPinokioEnvArgKey,
|
|
271
|
+
formatEnvArgsPreview,
|
|
272
|
+
redactEnvArgs
|
|
273
|
+
}
|
package/kernel/shell.js
CHANGED
|
@@ -20,6 +20,7 @@ const { applyWindowsNodePackageManagerEnv } = require('./windows_node_package_ma
|
|
|
20
20
|
const ShellParser = require('./shell_parser')
|
|
21
21
|
const AnsiStreamTracker = require('./ansi_stream_tracker')
|
|
22
22
|
const ShellStateSync = require('./shell_state_sync')
|
|
23
|
+
const ShellRunTemplate = require('./api/shell_run_template')
|
|
23
24
|
const home = os.homedir()
|
|
24
25
|
|
|
25
26
|
function normalizeComparablePath(filePath, platform) {
|
|
@@ -67,6 +68,18 @@ function stripInheritedCondaActivationState(env) {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
function setDefaultEnvValue(env, key, value) {
|
|
72
|
+
const existingKey = Object.keys(env).find((envKey) => envKey.toLowerCase() === key.toLowerCase())
|
|
73
|
+
if (!existingKey) {
|
|
74
|
+
env[key] = value
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
const existingValue = env[existingKey]
|
|
78
|
+
if (typeof existingValue !== "string" || !existingValue.trim()) {
|
|
79
|
+
env[existingKey] = value
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
70
83
|
// xterm.js currently ignores DECSYNCTERM (CSI ? 2026 h/l) and renders it as text on Windows.
|
|
71
84
|
// filterDecsync() removes these sequences so they do not pollute the terminal output.
|
|
72
85
|
class Shell {
|
|
@@ -281,6 +294,13 @@ class Shell {
|
|
|
281
294
|
}
|
|
282
295
|
}
|
|
283
296
|
|
|
297
|
+
if (this.platform === "win32") {
|
|
298
|
+
// Hugging Face file symlinks regularly fail on non-admin Windows setups.
|
|
299
|
+
// Default to no-symlink cache mode unless the user/app explicitly overrides it.
|
|
300
|
+
setDefaultEnvValue(this.env, "HF_HUB_DISABLE_SYMLINKS", "1")
|
|
301
|
+
setDefaultEnvValue(this.env, "HF_HUB_DISABLE_SYMLINKS_WARNING", "1")
|
|
302
|
+
}
|
|
303
|
+
|
|
284
304
|
stripInheritedCondaActivationState(this.env)
|
|
285
305
|
this.env[PATH_KEY] = stripBluefairyShimPaths(this.env[PATH_KEY], this.platform)
|
|
286
306
|
|
|
@@ -289,7 +309,7 @@ class Shell {
|
|
|
289
309
|
delete this.env[key]
|
|
290
310
|
}
|
|
291
311
|
let val = this.env[key]
|
|
292
|
-
if (/[\r\n]/.test(val)) {
|
|
312
|
+
if (!ShellRunTemplate.isPinokioEnvArgKey(key) && /[\r\n]/.test(val)) {
|
|
293
313
|
const replaced = val.replaceAll(/[\r\n]+/g, ' ');
|
|
294
314
|
this.env[key] = replaced
|
|
295
315
|
// delete this.env[key]
|
|
@@ -343,6 +363,9 @@ class Shell {
|
|
|
343
363
|
}
|
|
344
364
|
quoteArgForShell(value, shellName=this.shell) {
|
|
345
365
|
const input = value == null ? "" : String(value)
|
|
366
|
+
if (ShellRunTemplate.hasEnvArgMarker(input)) {
|
|
367
|
+
return ShellRunTemplate.quoteEnvArgComposite(input, shellName)
|
|
368
|
+
}
|
|
346
369
|
if (this.isCmdShell(shellName)) {
|
|
347
370
|
return `"${input.replace(/([()%!^"<>&|])/g, '^$1')}"`
|
|
348
371
|
}
|
|
@@ -390,6 +413,7 @@ class Shell {
|
|
|
390
413
|
this.userActive = false
|
|
391
414
|
this.decsyncBuffer = ''
|
|
392
415
|
this.stateSync.reset()
|
|
416
|
+
this.envArgsPreviewed = false
|
|
393
417
|
|
|
394
418
|
/*
|
|
395
419
|
params := {
|
|
@@ -521,6 +545,9 @@ class Shell {
|
|
|
521
545
|
//}
|
|
522
546
|
}
|
|
523
547
|
}
|
|
548
|
+
if (params._pinokio_cmd_delayed_expansion && this.isCmdShell(this.shell) && !this.args.includes("/V:ON")) {
|
|
549
|
+
this.args.push("/V:ON")
|
|
550
|
+
}
|
|
524
551
|
|
|
525
552
|
// 3. path => path can be http, relative, absolute
|
|
526
553
|
this.path = params.path
|
|
@@ -738,6 +765,16 @@ class Shell {
|
|
|
738
765
|
}
|
|
739
766
|
this.stateSync.invalidate({ clearTail: true })
|
|
740
767
|
}
|
|
768
|
+
emitEnvArgsPreview(params) {
|
|
769
|
+
if (!params || !Array.isArray(params._pinokio_env_args) || params._pinokio_env_args.length === 0 || this.envArgsPreviewed) {
|
|
770
|
+
return
|
|
771
|
+
}
|
|
772
|
+
this.envArgsPreviewed = true
|
|
773
|
+
const raw = ShellRunTemplate.formatEnvArgsPreview(params._pinokio_env_args)
|
|
774
|
+
if (raw && this.ondata) {
|
|
775
|
+
this.ondata({ raw })
|
|
776
|
+
}
|
|
777
|
+
}
|
|
741
778
|
async run(params, cb) {
|
|
742
779
|
let r = await this.request(params, cb)
|
|
743
780
|
return r
|
|
@@ -1343,6 +1380,7 @@ class Shell {
|
|
|
1343
1380
|
}
|
|
1344
1381
|
async exec(params) {
|
|
1345
1382
|
this.parser = new ShellParser()
|
|
1383
|
+
this.emitEnvArgsPreview(params)
|
|
1346
1384
|
params = await this.activate(params)
|
|
1347
1385
|
this.cmd = this.build(params)
|
|
1348
1386
|
let res = await new Promise((resolve, reject) => {
|
|
@@ -1710,7 +1748,7 @@ class Shell {
|
|
|
1710
1748
|
cmd: this.cmd,
|
|
1711
1749
|
index: this.index,
|
|
1712
1750
|
group: this.group,
|
|
1713
|
-
env: this.env,
|
|
1751
|
+
env: ShellRunTemplate.redactEnvArgs(this.env),
|
|
1714
1752
|
done: this.done,
|
|
1715
1753
|
ready: this.ready,
|
|
1716
1754
|
id: this.id,
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -28,6 +28,8 @@ const system = require('systeminformation')
|
|
|
28
28
|
const serveIndex = require('./serveIndex')
|
|
29
29
|
const registerFileRoutes = require('./routes/files')
|
|
30
30
|
const registerAppRoutes = require('./routes/apps')
|
|
31
|
+
const registerWorkspacesRoutes = require('./routes/workspaces')
|
|
32
|
+
const registerDraftImportRoutes = require('./routes/draft_import')
|
|
31
33
|
const Git = require("../kernel/git")
|
|
32
34
|
const TerminalApi = require('../kernel/api/terminal')
|
|
33
35
|
|
|
@@ -78,6 +80,9 @@ const { createDesktopEventRouter } = require("./lib/desktop_event_router")
|
|
|
78
80
|
const { createInjectRouter, resolveInjectList } = require("./lib/inject_router")
|
|
79
81
|
const { createTaskPackageService } = require("./lib/task_packages")
|
|
80
82
|
const { createTaskWorkspaceLinkService } = require("./lib/task_workspace_links")
|
|
83
|
+
const { createDraftService } = require("./lib/drafts")
|
|
84
|
+
const { createWorkspaceRuntimeService } = require("./lib/workspace_runtime")
|
|
85
|
+
const { createWorkspaceCatalogService } = require("./lib/workspace_catalog")
|
|
81
86
|
const { createContentValidationService } = require("./lib/content_validation")
|
|
82
87
|
const { buildSecureRouterDebugSnapshot, createSecureRouterDebugStore } = require("./lib/secure_router_debug")
|
|
83
88
|
const AppRegistryService = require("./lib/app_registry")
|
|
@@ -8403,6 +8408,35 @@ class Server {
|
|
|
8403
8408
|
const taskWorkspaceLinks = createTaskWorkspaceLinkService({
|
|
8404
8409
|
kernel: this.kernel
|
|
8405
8410
|
})
|
|
8411
|
+
const workspaceRuntime = createWorkspaceRuntimeService({
|
|
8412
|
+
kernel: this.kernel
|
|
8413
|
+
})
|
|
8414
|
+
this.workspaceRuntime = workspaceRuntime
|
|
8415
|
+
const drafts = createDraftService({
|
|
8416
|
+
kernel: this.kernel,
|
|
8417
|
+
taskWorkspaceLinks
|
|
8418
|
+
})
|
|
8419
|
+
this.drafts = drafts
|
|
8420
|
+
const workspaceCatalog = createWorkspaceCatalogService({
|
|
8421
|
+
kernel: this.kernel,
|
|
8422
|
+
workspaceRuntime,
|
|
8423
|
+
drafts
|
|
8424
|
+
})
|
|
8425
|
+
drafts.start().catch((error) => {
|
|
8426
|
+
console.warn("[drafts] failed to start", error && error.message ? error.message : error)
|
|
8427
|
+
})
|
|
8428
|
+
registerDraftImportRoutes(this.app, {
|
|
8429
|
+
drafts,
|
|
8430
|
+
defaultRegistryUrl: DEFAULT_REGISTRY_URL
|
|
8431
|
+
})
|
|
8432
|
+
registerWorkspacesRoutes(this.app, {
|
|
8433
|
+
workspaceCatalog,
|
|
8434
|
+
composePeerAccessPayload: () => this.composePeerAccessPayload(),
|
|
8435
|
+
getTheme: () => this.theme,
|
|
8436
|
+
getPeers: () => this.getPeers(),
|
|
8437
|
+
getCurrentHost: () => this.kernel.peer.host,
|
|
8438
|
+
getPortal: () => this.portal
|
|
8439
|
+
})
|
|
8406
8440
|
const TASK_INPUT_NAME_PATTERN = /^[a-zA-Z0-9_][a-zA-Z0-9_.-]*$/
|
|
8407
8441
|
const suggestTaskFolderName = async (rootDir, preferredName) => {
|
|
8408
8442
|
const normalizedRoot = path.resolve(rootDir)
|
|
@@ -9836,6 +9870,9 @@ class Server {
|
|
|
9836
9870
|
}
|
|
9837
9871
|
try {
|
|
9838
9872
|
await taskWorkspaceLinks.touchTaskWorkspace(task.id, workspaceRef)
|
|
9873
|
+
drafts.trackWorkspace({ taskId: task.id, ref: workspaceRef }).catch((error) => {
|
|
9874
|
+
console.warn("[drafts] failed to track workspace", error && error.message ? error.message : error)
|
|
9875
|
+
})
|
|
9839
9876
|
} catch (error) {
|
|
9840
9877
|
await taskPackages.deleteTaskPackage(task.id).catch(() => {})
|
|
9841
9878
|
throw error
|
|
@@ -10179,6 +10216,9 @@ class Server {
|
|
|
10179
10216
|
}
|
|
10180
10217
|
try {
|
|
10181
10218
|
await taskWorkspaceLinks.touchTaskWorkspace(task.id, workspaceRef)
|
|
10219
|
+
drafts.trackWorkspace({ taskId: task.id, ref: workspaceRef }).catch((error) => {
|
|
10220
|
+
console.warn("[drafts] failed to track workspace", error && error.message ? error.message : error)
|
|
10221
|
+
})
|
|
10182
10222
|
} catch (error) {
|
|
10183
10223
|
await renderTaskLaunchPage(req, res, task, {
|
|
10184
10224
|
selectedTool,
|
|
@@ -10198,6 +10238,20 @@ class Server {
|
|
|
10198
10238
|
})
|
|
10199
10239
|
this.app.post("/task/start", handleTaskStartRequest)
|
|
10200
10240
|
|
|
10241
|
+
this.app.get("/drafts", ex(async (req, res) => {
|
|
10242
|
+
const cwd = typeof req.query.cwd === "string" ? req.query.cwd : ""
|
|
10243
|
+
const items = await drafts.listPending({ cwd })
|
|
10244
|
+
res.json({
|
|
10245
|
+
ok: true,
|
|
10246
|
+
items
|
|
10247
|
+
})
|
|
10248
|
+
}))
|
|
10249
|
+
|
|
10250
|
+
this.app.post("/drafts/:id/dismiss", ex(async (req, res) => {
|
|
10251
|
+
const ok = await drafts.dismiss(req.params.id)
|
|
10252
|
+
res.json({ ok })
|
|
10253
|
+
}))
|
|
10254
|
+
|
|
10201
10255
|
this.app.get("/home", renderHomePage)
|
|
10202
10256
|
|
|
10203
10257
|
const normalizePathForComparison = (value) => {
|
|
@@ -10594,6 +10648,9 @@ class Server {
|
|
|
10594
10648
|
}
|
|
10595
10649
|
|
|
10596
10650
|
await taskWorkspaceLinks.touchTaskWorkspace(task.id, workspaceRef)
|
|
10651
|
+
drafts.trackWorkspace({ taskId: task.id, ref: workspaceRef }).catch((error) => {
|
|
10652
|
+
console.warn("[drafts] failed to track workspace", error && error.message ? error.message : error)
|
|
10653
|
+
})
|
|
10597
10654
|
|
|
10598
10655
|
const params = new URLSearchParams()
|
|
10599
10656
|
params.set("cwd", targetPath)
|
|
@@ -11851,11 +11908,14 @@ class Server {
|
|
|
11851
11908
|
// but allow it when the request originates from the local machine
|
|
11852
11909
|
if (payload.audience === 'device' && typeof payload.device_id === 'string' && payload.device_id) {
|
|
11853
11910
|
try {
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11911
|
+
const remoteAddress = req.socket && req.socket.remoteAddress ? req.socket.remoteAddress : req.ip
|
|
11912
|
+
const isLocalRequest = this.socket && typeof this.socket.isLocalAddress === 'function'
|
|
11913
|
+
? this.socket.isLocalAddress(remoteAddress)
|
|
11914
|
+
: false
|
|
11915
|
+
const isLocalDevice = this.socket && typeof this.socket.isLocalDevice === 'function'
|
|
11916
|
+
? this.socket.isLocalDevice(payload.device_id)
|
|
11917
|
+
: false
|
|
11918
|
+
payload.host = !!(isLocalRequest || isLocalDevice)
|
|
11859
11919
|
} catch (_) {
|
|
11860
11920
|
payload.host = false
|
|
11861
11921
|
}
|