pinokiod 7.3.15 → 7.4.0
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 +111 -15
- package/kernel/api/script/index.js +10 -0
- package/kernel/autolaunch.js +111 -0
- package/kernel/environment.js +89 -1
- package/kernel/git.js +9 -19
- package/kernel/index.js +142 -43
- package/kernel/launch_requirements.js +1115 -0
- package/kernel/ready.js +231 -0
- package/kernel/script.js +16 -0
- package/kernel/shells.js +9 -1
- package/kernel/workspace_status.js +111 -45
- package/package.json +1 -1
- package/server/autolaunch.js +725 -0
- package/server/index.js +244 -411
- package/server/views/app.ejs +256 -152
- package/server/views/autolaunch.ejs +363 -75
- package/server/views/index.ejs +550 -26
- package/server/views/partials/app_autolaunch_dependency_events.ejs +99 -0
- package/server/views/partials/app_autolaunch_dependency_save.ejs +10 -0
- package/server/views/partials/app_autolaunch_dependency_styles.ejs +357 -0
- package/server/views/partials/app_autolaunch_modal_helpers.ejs +347 -0
- package/server/views/partials/autolaunch_dependency_helpers.ejs +204 -0
- package/server/views/partials/autolaunch_dependency_save.ejs +13 -0
- package/server/views/partials/autolaunch_dependency_styles.ejs +347 -0
- package/server/views/partials/home_action_modal.ejs +4 -1
- package/server/views/partials/launch_requirements_status_client.ejs +271 -0
- package/server/views/partials/launch_requirements_status_styles.ejs +171 -0
- package/server/views/partials/launch_settings_dependency_save_factory.ejs +45 -0
- package/server/views/partials/launch_settings_dependency_script_loader_factory.ejs +25 -0
- package/server/views/terminal.ejs +196 -2
- package/test/home-autolaunch-live-ui.test.js +455 -0
- package/test/launch-requirements-browser.test.js +579 -0
- package/test/launch-requirements-contract-coverage.test.js +627 -0
- package/test/launch-requirements-real-browser.js +625 -0
- package/test/launch-requirements-status-client.test.js +132 -0
- package/test/launch-requirements.test.js +1806 -0
- package/test/launch-settings-ui.test.js +370 -0
- package/test/ready-state.test.js +49 -0
- package/test/server-autolaunch.test.js +1052 -0
- package/test/startup-git-index-benchmark.js +409 -0
- package/test/startup-git-index-browser.js +320 -0
- package/test/startup-git-index-performance.test.js +380 -0
- package/test/startup-git-index-refactor.test.js +450 -0
- package/test/startup-git-index-route.test.js +588 -0
- package/test/universal-launcher.smoke.spec.js +10 -9
- package/test/workspace-gitignore-benchmark.js +815 -0
- package/test/workspace-gitignore-path-scoped.test.js +256 -0
- package/spec/INSTRUCTION_SYNC.md +0 -432
package/kernel/api/index.js
CHANGED
|
@@ -424,6 +424,9 @@ class Api {
|
|
|
424
424
|
}
|
|
425
425
|
this.running[id] = true
|
|
426
426
|
this.done[id] = done
|
|
427
|
+
if (this.kernel && typeof this.kernel.markAppLaunchStarted === "function") {
|
|
428
|
+
this.kernel.markAppLaunchStarted(request.path)
|
|
429
|
+
}
|
|
427
430
|
}
|
|
428
431
|
isActionCandidate(action) {
|
|
429
432
|
return (Array.isArray(action) && action.length > 0) || typeof action === "function"
|
|
@@ -509,6 +512,10 @@ class Api {
|
|
|
509
512
|
let fullpath = path.resolve(cwd, ...args)
|
|
510
513
|
return this.running[fullpath]
|
|
511
514
|
},
|
|
515
|
+
ready: (...args) => {
|
|
516
|
+
let fullpath = path.resolve(cwd, ...args)
|
|
517
|
+
return this.kernel.isScriptReady(fullpath)
|
|
518
|
+
},
|
|
512
519
|
name,
|
|
513
520
|
self: script,
|
|
514
521
|
port,
|
|
@@ -554,7 +561,7 @@ class Api {
|
|
|
554
561
|
}
|
|
555
562
|
return steps
|
|
556
563
|
}
|
|
557
|
-
async stop(req, ondata) {
|
|
564
|
+
async stop(req, ondata, options = {}) {
|
|
558
565
|
// 1. set the "stop" flag for the uri, so the next execution in the queue for the uri will NOT queue another task
|
|
559
566
|
// 2. stream a message closing the socket
|
|
560
567
|
|
|
@@ -586,10 +593,11 @@ class Api {
|
|
|
586
593
|
|
|
587
594
|
|
|
588
595
|
// stop all shell processes connected to the uri
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
596
|
+
const stopGroups = new Set()
|
|
597
|
+
if (req.params.id) stopGroups.add(req.params.id)
|
|
598
|
+
stopGroups.add(requestPath)
|
|
599
|
+
for (const group of stopGroups) {
|
|
600
|
+
this.kernel.shell.kill({ group })
|
|
593
601
|
}
|
|
594
602
|
|
|
595
603
|
// if any process is in a "wait" state, resume it
|
|
@@ -608,12 +616,17 @@ class Api {
|
|
|
608
616
|
this.waiter[requestPath].reject()
|
|
609
617
|
delete this.waiter[requestPath]
|
|
610
618
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
delete this.running[
|
|
616
|
-
delete this.kernel.memory.local[
|
|
619
|
+
const stoppedKeys = new Set()
|
|
620
|
+
if (req.params.id) stoppedKeys.add(req.params.id)
|
|
621
|
+
stoppedKeys.add(requestPath)
|
|
622
|
+
for (const key of stoppedKeys) {
|
|
623
|
+
delete this.running[key]
|
|
624
|
+
delete this.kernel.memory.local[key]
|
|
625
|
+
}
|
|
626
|
+
if (this.kernel && typeof this.kernel.markAppLaunchStopped === "function") {
|
|
627
|
+
this.kernel.markAppLaunchStopped(requestPath, {
|
|
628
|
+
internal_completion: !!(options && options.internal_completion)
|
|
629
|
+
})
|
|
617
630
|
}
|
|
618
631
|
this.ondata({
|
|
619
632
|
id: req.params.id || requestPath,
|
|
@@ -1083,6 +1096,10 @@ class Api {
|
|
|
1083
1096
|
let fullpath = path.resolve(cwd, ...args)
|
|
1084
1097
|
return this.running[fullpath]
|
|
1085
1098
|
},
|
|
1099
|
+
ready: (...args) => {
|
|
1100
|
+
let fullpath = path.resolve(cwd, ...args)
|
|
1101
|
+
return this.kernel.isScriptReady(fullpath)
|
|
1102
|
+
},
|
|
1086
1103
|
name,
|
|
1087
1104
|
self: script,
|
|
1088
1105
|
port,
|
|
@@ -1235,6 +1252,9 @@ class Api {
|
|
|
1235
1252
|
|
|
1236
1253
|
this.kernel.memory.rpc[id].current = rpc.current
|
|
1237
1254
|
this.kernel.memory.rpc[id].total = rpc.total
|
|
1255
|
+
if (this.kernel && typeof this.kernel.markAppLaunchProgress === "function") {
|
|
1256
|
+
this.kernel.markAppLaunchProgress(request.path, rpc.current, rpc.total)
|
|
1257
|
+
}
|
|
1238
1258
|
|
|
1239
1259
|
// this.kernel.memory.rpc[request.path] = rpc
|
|
1240
1260
|
// this.kernel.memory.args[request.path] = args
|
|
@@ -1314,13 +1334,13 @@ class Api {
|
|
|
1314
1334
|
params: {
|
|
1315
1335
|
id: request.id
|
|
1316
1336
|
}
|
|
1317
|
-
})
|
|
1337
|
+
}, null, { internal_completion: true })
|
|
1318
1338
|
} else {
|
|
1319
1339
|
await this.stop({
|
|
1320
1340
|
params: {
|
|
1321
1341
|
uri: request.path
|
|
1322
1342
|
}
|
|
1323
|
-
})
|
|
1343
|
+
}, null, { internal_completion: true })
|
|
1324
1344
|
}
|
|
1325
1345
|
}
|
|
1326
1346
|
return { request, input: null, step: rpc.next, total: totalSteps, args }
|
|
@@ -1545,13 +1565,13 @@ class Api {
|
|
|
1545
1565
|
params: {
|
|
1546
1566
|
id: request.id
|
|
1547
1567
|
}
|
|
1548
|
-
})
|
|
1568
|
+
}, null, { internal_completion: true })
|
|
1549
1569
|
} else {
|
|
1550
1570
|
await this.stop({
|
|
1551
1571
|
params: {
|
|
1552
1572
|
uri: request.path
|
|
1553
1573
|
}
|
|
1554
|
-
})
|
|
1574
|
+
}, null, { internal_completion: true })
|
|
1555
1575
|
}
|
|
1556
1576
|
return { request, input: result, step: rpc.next, total: totalSteps, args }
|
|
1557
1577
|
}
|
|
@@ -1631,6 +1651,9 @@ class Api {
|
|
|
1631
1651
|
this.queue(response.request, response.rawrpc, response.input, response.step, response.total, cwd, args)
|
|
1632
1652
|
} else {
|
|
1633
1653
|
let id = response.request.id || response.request.path
|
|
1654
|
+
if (this.kernel && typeof this.kernel.markAppLaunchReady === "function") {
|
|
1655
|
+
this.kernel.markAppLaunchReady(response.request.path)
|
|
1656
|
+
}
|
|
1634
1657
|
if (response.request.caller) {
|
|
1635
1658
|
if (this.done[response.request.path]) {
|
|
1636
1659
|
this.done[response.request.path]({
|
|
@@ -1655,6 +1678,9 @@ class Api {
|
|
|
1655
1678
|
// this.kernel.refresh(true)
|
|
1656
1679
|
} catch (e) {
|
|
1657
1680
|
this.clearResolvedAction(request)
|
|
1681
|
+
if (this.kernel && typeof this.kernel.markAppLaunchFailed === "function") {
|
|
1682
|
+
this.kernel.markAppLaunchFailed(request.path, e)
|
|
1683
|
+
}
|
|
1658
1684
|
ondata({ raw: e.toString() })
|
|
1659
1685
|
}
|
|
1660
1686
|
}, concurrency)
|
|
@@ -1838,8 +1864,72 @@ class Api {
|
|
|
1838
1864
|
|
|
1839
1865
|
// 3. Check if the resolved endpoint has the requested action attribute and resolve it to steps.
|
|
1840
1866
|
if (this.isActionCandidate(action)) {
|
|
1867
|
+
const emitLaunchRequirementsControl = (result) => {
|
|
1868
|
+
if (!result || !result.action || result.action === "continue") {
|
|
1869
|
+
return
|
|
1870
|
+
}
|
|
1871
|
+
this.ondata({
|
|
1872
|
+
id: request.id || request.path,
|
|
1873
|
+
type: "launch.requirements.control",
|
|
1874
|
+
data: result
|
|
1875
|
+
})
|
|
1876
|
+
}
|
|
1877
|
+
let launchOperation = null
|
|
1878
|
+
const releaseLaunchOperation = () => {
|
|
1879
|
+
if (launchOperation && this.kernel && typeof this.kernel.endLaunchOperation === "function") {
|
|
1880
|
+
this.kernel.endLaunchOperation(launchOperation)
|
|
1881
|
+
}
|
|
1882
|
+
launchOperation = null
|
|
1883
|
+
}
|
|
1884
|
+
const shouldCheckLaunchRequirements = !request.skip_requirements &&
|
|
1885
|
+
this.kernel &&
|
|
1886
|
+
typeof this.kernel.ensureLaunchRequirements === "function" &&
|
|
1887
|
+
(typeof this.kernel.hasLaunchRequirementConfig === "function"
|
|
1888
|
+
? await this.kernel.hasLaunchRequirementConfig(request.path)
|
|
1889
|
+
: true)
|
|
1890
|
+
if (shouldCheckLaunchRequirements && request.path && this.kernel && typeof this.kernel.beginLaunchOperation === "function") {
|
|
1891
|
+
launchOperation = this.kernel.beginLaunchOperation(request.path, { request })
|
|
1892
|
+
}
|
|
1893
|
+
if (shouldCheckLaunchRequirements) {
|
|
1894
|
+
let requirementResult
|
|
1895
|
+
try {
|
|
1896
|
+
requirementResult = await this.kernel.ensureLaunchRequirements(request.path, {
|
|
1897
|
+
request,
|
|
1898
|
+
owner: launchOperation
|
|
1899
|
+
})
|
|
1900
|
+
} catch (e) {
|
|
1901
|
+
releaseLaunchOperation()
|
|
1902
|
+
if (e && (e.cancelled || e.blocked_reason)) {
|
|
1903
|
+
const control = {
|
|
1904
|
+
action: e.cancelled ? "cancelled" : "blocked",
|
|
1905
|
+
app_id: e.app_id || "",
|
|
1906
|
+
reason: e.blocked_reason || ""
|
|
1907
|
+
}
|
|
1908
|
+
emitLaunchRequirementsControl(control)
|
|
1909
|
+
return {
|
|
1910
|
+
request,
|
|
1911
|
+
launch_requirements: control
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
this.ondata({
|
|
1915
|
+
id: request.id || request.path,
|
|
1916
|
+
type: "error",
|
|
1917
|
+
data: e && e.stack ? e.stack : String(e || "Launch requirements failed"),
|
|
1918
|
+
})
|
|
1919
|
+
return
|
|
1920
|
+
}
|
|
1921
|
+
if (requirementResult && requirementResult.action && requirementResult.action !== "continue") {
|
|
1922
|
+
releaseLaunchOperation()
|
|
1923
|
+
emitLaunchRequirementsControl(requirementResult)
|
|
1924
|
+
return {
|
|
1925
|
+
request,
|
|
1926
|
+
launch_requirements: requirementResult
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1841
1930
|
|
|
1842
1931
|
this.setRunning(request, done)
|
|
1932
|
+
releaseLaunchOperation()
|
|
1843
1933
|
|
|
1844
1934
|
// set DNS
|
|
1845
1935
|
|
|
@@ -1865,6 +1955,9 @@ class Api {
|
|
|
1865
1955
|
} catch (e) {
|
|
1866
1956
|
this.clearResolvedAction(request)
|
|
1867
1957
|
delete this.running[this.requestId(request)]
|
|
1958
|
+
if (this.kernel && typeof this.kernel.markAppLaunchFailed === "function") {
|
|
1959
|
+
this.kernel.markAppLaunchFailed(request.path, e)
|
|
1960
|
+
}
|
|
1868
1961
|
this.ondata({
|
|
1869
1962
|
id: request.id || request.path,
|
|
1870
1963
|
type: "error",
|
|
@@ -1876,6 +1969,9 @@ class Api {
|
|
|
1876
1969
|
if (!Array.isArray(steps) || steps.length === 0) {
|
|
1877
1970
|
this.clearResolvedAction(request)
|
|
1878
1971
|
delete this.running[this.requestId(request)]
|
|
1972
|
+
if (this.kernel && typeof this.kernel.markAppLaunchFailed === "function") {
|
|
1973
|
+
this.kernel.markAppLaunchFailed(request.path, new Error(`missing or invalid attribute: ${actionKey}`))
|
|
1974
|
+
}
|
|
1879
1975
|
this.ondata({
|
|
1880
1976
|
id: request.id || request.path,
|
|
1881
1977
|
type: "error",
|
|
@@ -160,6 +160,16 @@ class Script {
|
|
|
160
160
|
ondata({ raw: `\r\nStopped ${uri}\r\n` })
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
+
async ready(req, ondata, kernel) {
|
|
164
|
+
const params = req && req.params
|
|
165
|
+
let uri = this.currentPath(req)
|
|
166
|
+
if (typeof params === "string") {
|
|
167
|
+
uri = params
|
|
168
|
+
} else if (params && typeof params === "object") {
|
|
169
|
+
uri = params.uri || params.path || params.script || uri
|
|
170
|
+
}
|
|
171
|
+
return kernel.isScriptReady(kernel.resolveReadyScriptPath(uri, req && req.cwd))
|
|
172
|
+
}
|
|
163
173
|
async run(req, ondata, kernel) {
|
|
164
174
|
/*
|
|
165
175
|
{
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const Environment = require('./environment')
|
|
3
|
+
|
|
4
|
+
class Autolaunch {
|
|
5
|
+
constructor(kernel) {
|
|
6
|
+
this.kernel = kernel
|
|
7
|
+
this.kernel.autolaunch_status = this.startupStatus()
|
|
8
|
+
}
|
|
9
|
+
startupStatus() {
|
|
10
|
+
return this.kernel.launchRequirements && typeof this.kernel.launchRequirements.startupHomeStatus === "function"
|
|
11
|
+
? this.kernel.launchRequirements.startupHomeStatus()
|
|
12
|
+
: (this.kernel.autolaunch_status || { running: false, apps: {} })
|
|
13
|
+
}
|
|
14
|
+
setStatus(status) {
|
|
15
|
+
const nextStatus = this.kernel.launchRequirements && typeof this.kernel.launchRequirements.replaceStartupHomeStatus === "function"
|
|
16
|
+
? this.kernel.launchRequirements.replaceStartupHomeStatus(status)
|
|
17
|
+
: status
|
|
18
|
+
this.kernel.autolaunch_status = nextStatus
|
|
19
|
+
return nextStatus
|
|
20
|
+
}
|
|
21
|
+
async collectCandidates() {
|
|
22
|
+
const apis = this.kernel.i && Array.isArray(this.kernel.i.api) ? this.kernel.i.api : []
|
|
23
|
+
const candidates = []
|
|
24
|
+
for (const api of apis) {
|
|
25
|
+
if (!api || !api.path) {
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
const envPath = path.resolve(this.kernel.api.userdir, api.path)
|
|
29
|
+
const env = await Environment.get(envPath, this.kernel)
|
|
30
|
+
const script = Environment.getScriptAutolaunch(env)
|
|
31
|
+
if (!script || !Environment.getScriptAutolaunchEnabled(env)) {
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
const autolaunchPath = path.resolve(this.kernel.api.userdir, api.path, script)
|
|
35
|
+
const exists = !!(autolaunchPath && await this.kernel.exists(autolaunchPath))
|
|
36
|
+
const dependencies = Environment.getScriptRequirements(env)
|
|
37
|
+
candidates.push({
|
|
38
|
+
id: api.path,
|
|
39
|
+
title: api.title || api.path,
|
|
40
|
+
script,
|
|
41
|
+
autolaunchPath,
|
|
42
|
+
dependencies,
|
|
43
|
+
exists
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
return candidates
|
|
47
|
+
}
|
|
48
|
+
async runScheduler() {
|
|
49
|
+
const startedAt = Date.now()
|
|
50
|
+
const candidates = await this.collectCandidates()
|
|
51
|
+
const status = this.kernel.launchRequirements && typeof this.kernel.launchRequirements.seedStartupHomeStatus === "function"
|
|
52
|
+
? await this.kernel.launchRequirements.seedStartupHomeStatus(candidates, startedAt)
|
|
53
|
+
: this.setStatus({
|
|
54
|
+
running: true,
|
|
55
|
+
started_at: startedAt,
|
|
56
|
+
apps: {}
|
|
57
|
+
})
|
|
58
|
+
this.kernel.autolaunch_status = status
|
|
59
|
+
for (const candidate of candidates) {
|
|
60
|
+
if (!status.apps[candidate.id]) {
|
|
61
|
+
status.apps[candidate.id] = {
|
|
62
|
+
id: candidate.id,
|
|
63
|
+
title: candidate.title,
|
|
64
|
+
script: candidate.script,
|
|
65
|
+
launch_path: candidate.autolaunchPath,
|
|
66
|
+
state: "pending",
|
|
67
|
+
dependencies: candidate.dependencies,
|
|
68
|
+
waiting_for: candidate.dependencies,
|
|
69
|
+
startup_root: true
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!candidate.exists) {
|
|
73
|
+
status.apps[candidate.id].state = "blocked"
|
|
74
|
+
status.apps[candidate.id].blocked_reason = candidate.script ? "Launch script does not exist" : "No launch script selected"
|
|
75
|
+
status.apps[candidate.id].waiting_for = []
|
|
76
|
+
}
|
|
77
|
+
status.apps[candidate.id].startup_root = true
|
|
78
|
+
}
|
|
79
|
+
const launchable = candidates.filter((candidate) => {
|
|
80
|
+
const row = status.apps[candidate.id]
|
|
81
|
+
if (!row || !candidate.exists) {
|
|
82
|
+
if (!candidate.exists) {
|
|
83
|
+
console.log("SCRIPT DOES NOT EXIST. Ignoring.", candidate.autolaunchPath)
|
|
84
|
+
}
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
return row.state !== "blocked"
|
|
88
|
+
})
|
|
89
|
+
await Promise.all(launchable.map((candidate) => {
|
|
90
|
+
return this.kernel.api.process({
|
|
91
|
+
uri: candidate.autolaunchPath,
|
|
92
|
+
input: {},
|
|
93
|
+
startup: true
|
|
94
|
+
}).catch((err) => {
|
|
95
|
+
console.warn('[Kernel.init] startup process failed:', err && err.message ? err.message : err)
|
|
96
|
+
const row = status.apps[candidate.id]
|
|
97
|
+
if (row && row.state !== "ready") {
|
|
98
|
+
delete status.apps[candidate.id]
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
}))
|
|
102
|
+
status.running = false
|
|
103
|
+
status.completed_at = Date.now()
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
this.kernel.launch_complete = true
|
|
106
|
+
console.log("SETTING launch complete", this.kernel.launch_complete)
|
|
107
|
+
}, 2000)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = Autolaunch
|
package/kernel/environment.js
CHANGED
|
@@ -6,6 +6,81 @@ const ManagedSkills = require('./managed_skills')
|
|
|
6
6
|
const TEMP_ENV_KEYS = ["TMP", "TEMP", "TMPDIR", "PIP_TMPDIR"]
|
|
7
7
|
const CACHE_ENV_KEYS = ["UV_CACHE_DIR", "PIP_CACHE_DIR"]
|
|
8
8
|
const CACHE_PREFLIGHT_KEYS = TEMP_ENV_KEYS.concat(CACHE_ENV_KEYS)
|
|
9
|
+
const SCRIPT_AUTOLAUNCH_KEY = "PINOKIO_SCRIPT_AUTOLAUNCH"
|
|
10
|
+
const SCRIPT_AUTOLAUNCH_ENABLED_KEY = "PINOKIO_SCRIPT_AUTOLAUNCH_ENABLED"
|
|
11
|
+
const SCRIPT_REQUIREMENTS_KEY = "PINOKIO_SCRIPT_REQUIRES"
|
|
12
|
+
|
|
13
|
+
const parseBooleanFlag = (value) => {
|
|
14
|
+
if (typeof value !== "string") {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
const normalized = value.trim().toLowerCase()
|
|
18
|
+
if (!normalized) {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const getScriptAutolaunch = (env) => {
|
|
31
|
+
return typeof (env && env[SCRIPT_AUTOLAUNCH_KEY]) === "string"
|
|
32
|
+
? env[SCRIPT_AUTOLAUNCH_KEY].trim()
|
|
33
|
+
: ""
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const getScriptAutolaunchEnabled = (env) => {
|
|
37
|
+
const script = getScriptAutolaunch(env)
|
|
38
|
+
if (!script) {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
const explicit = parseBooleanFlag(env && env[SCRIPT_AUTOLAUNCH_ENABLED_KEY])
|
|
42
|
+
return explicit === null ? true : explicit
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const parseAutolaunchList = (value) => {
|
|
46
|
+
if (typeof value !== "string") {
|
|
47
|
+
return []
|
|
48
|
+
}
|
|
49
|
+
const seen = new Set()
|
|
50
|
+
const values = []
|
|
51
|
+
for (const part of value.split(",")) {
|
|
52
|
+
const raw = part.trim()
|
|
53
|
+
if (!raw) {
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
let id = raw
|
|
57
|
+
try {
|
|
58
|
+
id = decodeURIComponent(raw)
|
|
59
|
+
} catch (_) {}
|
|
60
|
+
id = String(id || "").trim()
|
|
61
|
+
if (!id || seen.has(id)) {
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
seen.add(id)
|
|
65
|
+
values.push(id)
|
|
66
|
+
}
|
|
67
|
+
return values
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const formatAutolaunchList = (ids) => {
|
|
71
|
+
if (!Array.isArray(ids)) {
|
|
72
|
+
return ""
|
|
73
|
+
}
|
|
74
|
+
return ids
|
|
75
|
+
.map((id) => String(id || "").trim())
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.map((id) => encodeURIComponent(id))
|
|
78
|
+
.join(",")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const getScriptRequirements = (env) => {
|
|
82
|
+
return parseAutolaunchList(env && env[SCRIPT_REQUIREMENTS_KEY])
|
|
83
|
+
}
|
|
9
84
|
|
|
10
85
|
const formatCachePreflightError = (error) => {
|
|
11
86
|
if (!error) {
|
|
@@ -279,6 +354,19 @@ const ENVS = async () => {
|
|
|
279
354
|
"#",
|
|
280
355
|
"##########################################################################",
|
|
281
356
|
]
|
|
357
|
+
}, {
|
|
358
|
+
type: ["app"],
|
|
359
|
+
key: SCRIPT_REQUIREMENTS_KEY,
|
|
360
|
+
val: "",
|
|
361
|
+
comment: [
|
|
362
|
+
"##########################################################################",
|
|
363
|
+
"#",
|
|
364
|
+
"# PINOKIO_SCRIPT_REQUIRES",
|
|
365
|
+
"# comma-separated local app folder IDs this app requires before launch",
|
|
366
|
+
"# requirements do not automatically enable run-at-startup for those apps",
|
|
367
|
+
"#",
|
|
368
|
+
"##########################################################################",
|
|
369
|
+
]
|
|
282
370
|
}, {
|
|
283
371
|
type: ["system"],
|
|
284
372
|
key: "PINOKIO_SHARE_VAR",
|
|
@@ -946,4 +1034,4 @@ const init = async (options, kernel) => {
|
|
|
946
1034
|
env_path: current
|
|
947
1035
|
}
|
|
948
1036
|
}
|
|
949
|
-
module.exports = { ENV, get, get2, init_folders, ensurePinokioCacheDirs, requirements, init, get_root }
|
|
1037
|
+
module.exports = { ENV, get, get2, init_folders, ensurePinokioCacheDirs, requirements, init, get_root, SCRIPT_AUTOLAUNCH_KEY, SCRIPT_AUTOLAUNCH_ENABLED_KEY, SCRIPT_REQUIREMENTS_KEY, getScriptAutolaunch, getScriptAutolaunchEnabled, parseAutolaunchList, formatAutolaunchList, getScriptRequirements }
|
package/kernel/git.js
CHANGED
|
@@ -228,16 +228,6 @@ class Git {
|
|
|
228
228
|
return fs.promises.copyFile(src, dest)
|
|
229
229
|
}))
|
|
230
230
|
|
|
231
|
-
// best-effort: clear any stale index.lock files across all known repos at startup
|
|
232
|
-
try {
|
|
233
|
-
const apiRoot = this.kernel.path("api")
|
|
234
|
-
const repos = await this.repos(apiRoot)
|
|
235
|
-
for (const repo of repos) {
|
|
236
|
-
if (repo && repo.dir) {
|
|
237
|
-
await this.clearStaleLock(repo.dir)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
} catch (_) {}
|
|
241
231
|
}
|
|
242
232
|
checkpointsDir() {
|
|
243
233
|
return path.resolve(this.kernel.homedir, "checkpoints")
|
|
@@ -1326,6 +1316,7 @@ class Git {
|
|
|
1326
1316
|
const gitParentPath = path.dirname(gitPath)
|
|
1327
1317
|
const gitParentRelPath = path.relative(this.kernel.path("api"), gitParentPath)
|
|
1328
1318
|
let dir = path.dirname(gitPath)
|
|
1319
|
+
await this.clearStaleLock(dir)
|
|
1329
1320
|
let display_name
|
|
1330
1321
|
let main
|
|
1331
1322
|
if (gitRelPathSansGit === ".") {
|
|
@@ -1457,15 +1448,6 @@ class Git {
|
|
|
1457
1448
|
async findRepoRootForPath(targetPath) {
|
|
1458
1449
|
if (!targetPath || typeof targetPath !== "string") return null
|
|
1459
1450
|
const absoluteTarget = path.resolve(targetPath)
|
|
1460
|
-
const cachedRoots = Array.from(this.dirs)
|
|
1461
|
-
.map((dir) => path.resolve(dir))
|
|
1462
|
-
.sort((a, b) => b.length - a.length)
|
|
1463
|
-
for (const root of cachedRoots) {
|
|
1464
|
-
if (absoluteTarget === root || absoluteTarget.startsWith(`${root}${path.sep}`)) {
|
|
1465
|
-
return root
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
1451
|
let probe = await this.findExistingAncestor(absoluteTarget)
|
|
1470
1452
|
if (!probe) return null
|
|
1471
1453
|
try {
|
|
@@ -1488,6 +1470,14 @@ class Git {
|
|
|
1488
1470
|
this.dirs.add(probe)
|
|
1489
1471
|
return probe
|
|
1490
1472
|
}
|
|
1473
|
+
const cachedRoots = Array.from(this.dirs)
|
|
1474
|
+
.map((dir) => path.resolve(dir))
|
|
1475
|
+
.sort((a, b) => b.length - a.length)
|
|
1476
|
+
for (const root of cachedRoots) {
|
|
1477
|
+
if (absoluteTarget === root || absoluteTarget.startsWith(`${root}${path.sep}`)) {
|
|
1478
|
+
return root
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1491
1481
|
return null
|
|
1492
1482
|
}
|
|
1493
1483
|
getManagedRepairTarget(targetPath) {
|