dsh-gitbash-shell 0.39.0 → 0.39.2
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/CHANGELOG.md +78 -0
- package/dsh.plugin.json +1 -1
- package/package.json +3 -2
- package/src/bash-tool.js +28 -17
- package/src/client.js +36 -25
- package/src/compositions.js +11 -7
- package/src/fastctx-bridge.js +125 -39
- package/src/index.js +163 -141
- package/src/inline-tools.js +151 -64
- package/src/shell.js +4 -3
package/src/index.js
CHANGED
|
@@ -1168,7 +1168,13 @@ const CODE_LITERAL_SAFE = /^[A-Za-z0-9._@+~\-/ ]+$/
|
|
|
1168
1168
|
/** Previous significant character that lets a '/' start a regex literal. */
|
|
1169
1169
|
function regexCanStart(previous) {
|
|
1170
1170
|
if (previous === '') return true
|
|
1171
|
-
|
|
1171
|
+
if (['return', 'throw', 'yield', 'await', 'case', 'delete', 'void', 'typeof', 'in', 'instanceof', 'of', 'else', 'do', 'break', 'continue', 'debugger'].includes(previous)) return true
|
|
1172
|
+
return previous.length === 1 && '(,=:[!&|?{;+-*%<>~^'.includes(previous)
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
/** Read an identifier as ONE token: keyword-led regexes are not division. */
|
|
1176
|
+
function codeWordAt(code, index) {
|
|
1177
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*/.exec(code.slice(index))?.[0]
|
|
1172
1178
|
}
|
|
1173
1179
|
|
|
1174
1180
|
/** Index after the closing quote of the literal at `index`, or -1. */
|
|
@@ -1239,6 +1245,15 @@ function skipBracedExpression(code, index) {
|
|
|
1239
1245
|
last = 'x'
|
|
1240
1246
|
continue
|
|
1241
1247
|
}
|
|
1248
|
+
// A slash after a closing group may start a statement regex OR division.
|
|
1249
|
+
// Without a parser that distinction is uncertain: abort the WHOLE scan.
|
|
1250
|
+
if (c === '/' && (last === ')' || last === '}')) return -1
|
|
1251
|
+
const word = codeWordAt(code, i)
|
|
1252
|
+
if (word) {
|
|
1253
|
+
last = last === '.' ? 'x' : word
|
|
1254
|
+
i += word.length
|
|
1255
|
+
continue
|
|
1256
|
+
}
|
|
1242
1257
|
if (c === '/' && regexCanStart(last)) {
|
|
1243
1258
|
const end = skipRegexLiteral(code, i)
|
|
1244
1259
|
if (end === -1) return -1
|
|
@@ -1320,6 +1335,15 @@ export function scanCodeLiterals(code) {
|
|
|
1320
1335
|
last = 'x'
|
|
1321
1336
|
continue
|
|
1322
1337
|
}
|
|
1338
|
+
// A slash after a closing group may start a statement regex OR division.
|
|
1339
|
+
// Without a parser that distinction is uncertain: abort the WHOLE scan.
|
|
1340
|
+
if (c === '/' && (last === ')' || last === '}')) return null
|
|
1341
|
+
const word = codeWordAt(code, i)
|
|
1342
|
+
if (word) {
|
|
1343
|
+
last = last === '.' ? 'x' : word
|
|
1344
|
+
i += word.length
|
|
1345
|
+
continue
|
|
1346
|
+
}
|
|
1323
1347
|
if (c === '/' && regexCanStart(last)) {
|
|
1324
1348
|
const end = skipRegexLiteral(code, i)
|
|
1325
1349
|
if (end === -1) return null
|
|
@@ -1380,8 +1404,8 @@ export function rewriteCodePaths(code, env) {
|
|
|
1380
1404
|
// is written into, and a quote inside it (a home directory with an
|
|
1381
1405
|
// apostrophe) would end that literal early — refuse rather than corrupt.
|
|
1382
1406
|
const quote = code[span.start - 1]
|
|
1383
|
-
if (translated.indexOf(quote) >= 0)
|
|
1384
|
-
if (quote === '`' && translated.indexOf('${') >= 0)
|
|
1407
|
+
if (translated.indexOf(quote) >= 0) return code
|
|
1408
|
+
if (quote === '`' && translated.indexOf('${') >= 0) return code
|
|
1385
1409
|
const source = translated.replace(/\\/g, '\\\\')
|
|
1386
1410
|
out += code.slice(cursor, span.start) + source
|
|
1387
1411
|
cursor = span.end
|
|
@@ -1469,9 +1493,6 @@ const SIDEBAR_NS = 'dsh-better-sidebar'
|
|
|
1469
1493
|
const SIDEBAR_TRIES = 12
|
|
1470
1494
|
const SIDEBAR_RETRY_MS = 1500
|
|
1471
1495
|
|
|
1472
|
-
// Reconciler installed by adoptSidebarShell; a no-op until then.
|
|
1473
|
-
let reconcileSidebar = () => {}
|
|
1474
|
-
|
|
1475
1496
|
/**
|
|
1476
1497
|
* Reconcile the better-sidebar `terminalShell` takeover with the adoptSidebar
|
|
1477
1498
|
* setting (v0.15.0, default ON). ON writes our bashPath (remembering what was
|
|
@@ -1491,95 +1512,69 @@ function adoptSidebarShell(ctx, bashPath, readAdopt) {
|
|
|
1491
1512
|
let previous = ''
|
|
1492
1513
|
let tried = 0
|
|
1493
1514
|
let timer = null
|
|
1494
|
-
|
|
1495
|
-
|
|
1515
|
+
let closed = false
|
|
1516
|
+
let serial = Promise.resolve()
|
|
1496
1517
|
const readShell = () => {
|
|
1497
1518
|
const settings = ctx.get('settings')
|
|
1498
1519
|
if (!settings || typeof settings.update !== 'function') return null
|
|
1499
1520
|
try {
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1521
|
+
if (typeof settings.describe !== 'function') return null
|
|
1522
|
+
const entry = settings.describe().find((row) => row && row.ns === SIDEBAR_NS)
|
|
1523
|
+
const value = entry?.value
|
|
1524
|
+
return value && typeof value.terminalShell === 'string' ? value.terminalShell : ''
|
|
1525
|
+
} catch { return null }
|
|
1526
|
+
}
|
|
1527
|
+
const restore = async () => {
|
|
1528
|
+
if (!adopted) return
|
|
1529
|
+
const now = readShell()
|
|
1530
|
+
if (now === null) return
|
|
1531
|
+
// The user may have selected another shell since our takeover.
|
|
1532
|
+
if (now === bashPath) {
|
|
1533
|
+
await ctx.get('settings').update(SIDEBAR_NS, { terminalShell: previous })
|
|
1534
|
+
console.log(TAG + " better-sidebar shell restored ('" + previous + "')")
|
|
1511
1535
|
}
|
|
1536
|
+
adopted = false
|
|
1512
1537
|
}
|
|
1513
|
-
|
|
1514
|
-
reconcileSidebar = (desired) => {
|
|
1538
|
+
const reconcileSidebar = async (desired) => {
|
|
1515
1539
|
const current = readShell()
|
|
1516
1540
|
if (current === null) return false
|
|
1517
|
-
|
|
1518
|
-
if (
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
adopted = true
|
|
1524
|
-
console.log(
|
|
1525
|
-
`${TAG} dsh-better-sidebar terminal shell -> Git Bash (${bashPath})` +
|
|
1526
|
-
(was ? ` (took over from '${was}')` : ''),
|
|
1527
|
-
)
|
|
1528
|
-
})
|
|
1529
|
-
.catch((error) => {
|
|
1530
|
-
console.log(`${TAG} better-sidebar shell adoption unavailable: ${error?.message ?? error}`)
|
|
1531
|
-
})
|
|
1532
|
-
return true
|
|
1533
|
-
}
|
|
1534
|
-
if (desired && current === bashPath) {
|
|
1535
|
-
adopted = true
|
|
1536
|
-
return true
|
|
1537
|
-
}
|
|
1538
|
-
if (!desired && adopted && current === bashPath) {
|
|
1539
|
-
settings.update(SIDEBAR_NS, { terminalShell: previous })
|
|
1540
|
-
.then(() => {
|
|
1541
|
-
adopted = false
|
|
1542
|
-
console.log(`${TAG} dsh-better-sidebar terminal shell restored ('${previous || 'sidebar default'}') — adoptSidebar off`)
|
|
1543
|
-
})
|
|
1544
|
-
.catch(() => {})
|
|
1545
|
-
return true
|
|
1546
|
-
}
|
|
1541
|
+
if (!desired) { await restore(); return true }
|
|
1542
|
+
if (adopted || current === bashPath) return true
|
|
1543
|
+
previous = current
|
|
1544
|
+
await ctx.get('settings').update(SIDEBAR_NS, { terminalShell: bashPath })
|
|
1545
|
+
adopted = true
|
|
1546
|
+
console.log(TAG + ' dsh-better-sidebar terminal shell -> Git Bash (' + bashPath + ')')
|
|
1547
1547
|
return true
|
|
1548
1548
|
}
|
|
1549
|
-
|
|
1550
1549
|
const run = () => {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
(
|
|
1567
|
-
if (
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
}
|
|
1580
|
-
},
|
|
1581
|
-
'dsh-gitbash-shell: sidebar shell revert',
|
|
1582
|
-
)
|
|
1550
|
+
if (closed) return serial
|
|
1551
|
+
if (timer) { clearTimeout(timer); timer = null }
|
|
1552
|
+
serial = serial.then(async () => {
|
|
1553
|
+
if (closed) return
|
|
1554
|
+
tried += 1
|
|
1555
|
+
const ok = await reconcileSidebar(readAdopt())
|
|
1556
|
+
if (ok || closed) return
|
|
1557
|
+
if (tried < SIDEBAR_TRIES) timer = setTimeout(run, SIDEBAR_RETRY_MS)
|
|
1558
|
+
else console.log(TAG + ' better-sidebar shell adoption skipped: settings unavailable')
|
|
1559
|
+
}).catch((error) => {
|
|
1560
|
+
console.log(TAG + ' better-sidebar shell adoption unavailable: ' + (error?.message ?? error))
|
|
1561
|
+
})
|
|
1562
|
+
return serial
|
|
1563
|
+
}
|
|
1564
|
+
try {
|
|
1565
|
+
ctx.on('loader/volatile-update', (paths) => {
|
|
1566
|
+
if (!Array.isArray(paths) || !paths.some((path) => Array.isArray(path) && path[0] === 'adoptSidebar')) return
|
|
1567
|
+
tried = 0
|
|
1568
|
+
void run()
|
|
1569
|
+
})
|
|
1570
|
+
} catch { /* a diagnostic context may have no event service */ }
|
|
1571
|
+
ctx.effect(() => async () => {
|
|
1572
|
+
closed = true
|
|
1573
|
+
if (timer) clearTimeout(timer)
|
|
1574
|
+
await serial
|
|
1575
|
+
await restore()
|
|
1576
|
+
}, 'dsh-gitbash-shell: sidebar shell revert')
|
|
1577
|
+
return run()
|
|
1583
1578
|
}
|
|
1584
1579
|
|
|
1585
1580
|
// ── plugin ──────────────────────────────────────────────────────────────────
|
|
@@ -1759,6 +1754,38 @@ export function minimalPtyBashPath(readResolution) {
|
|
|
1759
1754
|
* failures stay visible through the roster's broken diagnostic instead of
|
|
1760
1755
|
* failing the boot.
|
|
1761
1756
|
*/
|
|
1757
|
+
async function migrateVariantDefault(ctx, readVariants) {
|
|
1758
|
+
if (readVariants() === true) return true
|
|
1759
|
+
const readDefault = () => { try { return ctx.agentPresets.defaultId } catch { return undefined } }
|
|
1760
|
+
const current = readDefault()
|
|
1761
|
+
if (typeof current !== 'string' || !PRESET_IDS.includes(current)) return true
|
|
1762
|
+
const target = VARIANT_DEFAULT_SUCCESSORS[current]
|
|
1763
|
+
try {
|
|
1764
|
+
const settings = ctx.get('settings')
|
|
1765
|
+
if (!settings || typeof settings.update !== 'function') return false
|
|
1766
|
+
const roster = await ctx.agentPresets.list()
|
|
1767
|
+
if (!Array.isArray(roster) || !roster.some((row) => row && row.id === target)) {
|
|
1768
|
+
console.warn(TAG + ' default-preset migration stood down: successor "' + target + '" is not registered. Set an OFFICIAL default or turn variantPresets on.')
|
|
1769
|
+
return false
|
|
1770
|
+
}
|
|
1771
|
+
// A roster read is asynchronous: never overwrite a newer user selection.
|
|
1772
|
+
if (readVariants() === true || readDefault() !== current) return true
|
|
1773
|
+
await settings.update('agent-preset-registry', { selectedDefault: target })
|
|
1774
|
+
const after = readDefault()
|
|
1775
|
+
if (after !== target) {
|
|
1776
|
+
console.warn(TAG + ' default-preset migration did not stick (read back "' + after + '"). Set an OFFICIAL default or turn variantPresets on.')
|
|
1777
|
+
return false
|
|
1778
|
+
}
|
|
1779
|
+
console.log(TAG + ' deployment default migrated: "' + current + '" -> "' + target
|
|
1780
|
+
+ '" (variantPresets is off). Pinned sessions are unchanged; agent-preset/locked still requires a NEW session.')
|
|
1781
|
+
return true
|
|
1782
|
+
} catch (error) {
|
|
1783
|
+
console.warn(TAG + ' default-preset migration failed: ' + (error?.message ?? error)
|
|
1784
|
+
+ '. Set an OFFICIAL default or turn variantPresets on; variants were not retired.')
|
|
1785
|
+
return false
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1762
1789
|
async function registerVariant(ctx, presetId, { gitBashActive, skillsDir, pythonActive, hostExtras = {}, readBashResolution }) {
|
|
1763
1790
|
const meta = PRESET_META[presetId]
|
|
1764
1791
|
if (!meta) return undefined
|
|
@@ -1811,6 +1838,17 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress, readBashResolutio
|
|
|
1811
1838
|
/** The CPython fact the LIVE registrations were built with. */
|
|
1812
1839
|
let registeredPython = false
|
|
1813
1840
|
let serial = Promise.resolve()
|
|
1841
|
+
let closed = false
|
|
1842
|
+
ctx.effect(() => async () => {
|
|
1843
|
+
closed = true
|
|
1844
|
+
await serial
|
|
1845
|
+
const failures = []
|
|
1846
|
+
for (const [presetId, off] of [...live]) {
|
|
1847
|
+
live.delete(presetId)
|
|
1848
|
+
try { await off() } catch (error) { failures.push(error) }
|
|
1849
|
+
}
|
|
1850
|
+
if (failures.length) throw new AggregateError(failures, 'Git Bash preset cleanup failed')
|
|
1851
|
+
}, 'dsh-gitbash-shell: declarative preset registrations')
|
|
1814
1852
|
|
|
1815
1853
|
// v0.35.0: the variant roster is opt-in (`variantPresets`, default OFF).
|
|
1816
1854
|
// OFF means an EMPTY desired set — the cleanup above still runs, so a
|
|
@@ -1820,8 +1858,16 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress, readBashResolutio
|
|
|
1820
1858
|
: [])
|
|
1821
1859
|
|
|
1822
1860
|
const reconcile = () => {
|
|
1861
|
+
if (closed) return serial
|
|
1823
1862
|
serial = serial.then(async () => {
|
|
1863
|
+
if (closed) return
|
|
1824
1864
|
const want = desired()
|
|
1865
|
+
const pythonActive = peerPython
|
|
1866
|
+
// Dynamic OFF repairs the deployment default BEFORE retiring its id.
|
|
1867
|
+
// Keep registrations if the official migration cannot be verified.
|
|
1868
|
+
if (live.size > 0 && readVariants() !== true
|
|
1869
|
+
&& !await migrateVariantDefault(ctx, readVariants)) return
|
|
1870
|
+
if (closed) return
|
|
1825
1871
|
// The peer's EFFECTIVE CPython backend rewrites the workflow rows of
|
|
1826
1872
|
// EVERY variant (the official Python composition disables them, and
|
|
1827
1873
|
// workflow-ptc refuses a non-TypeScript backend), so a flip re-registers
|
|
@@ -1829,14 +1875,16 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress, readBashResolutio
|
|
|
1829
1875
|
// as the backend swap itself: the peer's patch is evaluated at boot. A
|
|
1830
1876
|
// bare INTENT without the effective backend changes nothing here — the
|
|
1831
1877
|
// composition still runs Node, so the workflow rows must stay live.
|
|
1832
|
-
if (live.size > 0 && registeredPython !==
|
|
1878
|
+
if (live.size > 0 && registeredPython !== pythonActive) {
|
|
1833
1879
|
for (const [presetId, off] of [...live]) {
|
|
1834
1880
|
live.delete(presetId)
|
|
1835
1881
|
try {
|
|
1836
1882
|
await off()
|
|
1837
1883
|
console.log(TAG + " preset '" + presetId + "' retired (peer CPython backend changed; rows are rebuilt)")
|
|
1838
1884
|
} catch (error) {
|
|
1885
|
+
live.set(presetId, off)
|
|
1839
1886
|
console.log(TAG + " preset '" + presetId + "' retirement failed: " + (error && error.message ? error.message : error))
|
|
1887
|
+
return // never register replacements after an unverified retirement
|
|
1840
1888
|
}
|
|
1841
1889
|
}
|
|
1842
1890
|
}
|
|
@@ -1847,14 +1895,18 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress, readBashResolutio
|
|
|
1847
1895
|
await off()
|
|
1848
1896
|
console.log(TAG + " preset '" + presetId + "' retired (dsh-ptc-cordis-preset covers Creation mode on Git Bash)")
|
|
1849
1897
|
} catch (error) {
|
|
1898
|
+
live.set(presetId, off)
|
|
1850
1899
|
console.log(TAG + " preset '" + presetId + "' retirement failed: " + (error && error.message ? error.message : error))
|
|
1900
|
+
return
|
|
1851
1901
|
}
|
|
1852
1902
|
}
|
|
1853
1903
|
for (const presetId of want) {
|
|
1904
|
+
if (closed) break
|
|
1854
1905
|
if (live.has(presetId)) continue
|
|
1855
1906
|
try {
|
|
1856
|
-
const unregister = await registerVariant(ctx, presetId, { gitBashActive, skillsDir, pythonActive
|
|
1907
|
+
const unregister = await registerVariant(ctx, presetId, { gitBashActive, skillsDir, pythonActive, hostExtras, readBashResolution })
|
|
1857
1908
|
if (unregister) {
|
|
1909
|
+
if (closed) { await unregister(); break }
|
|
1858
1910
|
live.set(presetId, unregister)
|
|
1859
1911
|
console.log(TAG + " preset '" + presetId + "' registered declaratively")
|
|
1860
1912
|
}
|
|
@@ -1862,7 +1914,7 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress, readBashResolutio
|
|
|
1862
1914
|
console.log(TAG + " preset '" + presetId + "' registration failed: " + (error && error.message ? error.message : error))
|
|
1863
1915
|
}
|
|
1864
1916
|
}
|
|
1865
|
-
registeredPython =
|
|
1917
|
+
registeredPython = pythonActive
|
|
1866
1918
|
}).catch((error) => {
|
|
1867
1919
|
console.log(TAG + ' preset reconcile failed: ' + (error && error.message ? error.message : error))
|
|
1868
1920
|
})
|
|
@@ -1870,6 +1922,7 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress, readBashResolutio
|
|
|
1870
1922
|
}
|
|
1871
1923
|
|
|
1872
1924
|
await reconcile()
|
|
1925
|
+
if (closed) return
|
|
1873
1926
|
console.log(TAG + ' registered ' + live.size + ' preset(s) declaratively (' + [...live.keys()].join(', ') + ')'
|
|
1874
1927
|
+ (live.size === 0 ? ' — variantPresets is off: the official presets carry Git Bash through the inline tool patch (see the CHANGELOG)'
|
|
1875
1928
|
: '')
|
|
@@ -1932,7 +1985,8 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress, readBashResolutio
|
|
|
1932
1985
|
console.log(TAG + ' dedupe switch wiring failed: ' + (error && error.message ? error.message : error))
|
|
1933
1986
|
}
|
|
1934
1987
|
|
|
1935
|
-
|
|
1988
|
+
// If settings appears after an OFF transition failed closed, retry it.
|
|
1989
|
+
ctx.inject(['settings'], () => { void reconcile() })
|
|
1936
1990
|
}
|
|
1937
1991
|
|
|
1938
1992
|
export async function apply(ctx, config = {}) {
|
|
@@ -2611,8 +2665,8 @@ export async function apply(ctx, config = {}) {
|
|
|
2611
2665
|
// official tool surface mirrored call-for-call) plus a HIDE on the real pwsh
|
|
2612
2666
|
// tool while the pwsh switch is off (route B: the official executor stays in
|
|
2613
2667
|
// place, so pwsh is real PowerShell and hiding it is a product choice). The
|
|
2614
|
-
//
|
|
2615
|
-
//
|
|
2668
|
+
// Full Windows variants keep the same pwsh-only face as official presets,
|
|
2669
|
+
// so they receive this same private-executor patch. Minimal stays persistent.
|
|
2616
2670
|
if (process.platform === 'win32') {
|
|
2617
2671
|
try {
|
|
2618
2672
|
mountInlineTools(ctx, {
|
|
@@ -2661,55 +2715,10 @@ export async function apply(ctx, config = {}) {
|
|
|
2661
2715
|
// With variantPresets ON nothing here runs: the variants stay registered,
|
|
2662
2716
|
// so a variant default keeps working as-is.
|
|
2663
2717
|
try {
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
const settings = settingsCtx.settings
|
|
2669
|
-
if (!settings || typeof settings.update !== 'function') return
|
|
2670
|
-
// The switch is the whole contract: keep-old-modes ON means a
|
|
2671
|
-
// variant default is a legitimate choice and stays untouched.
|
|
2672
|
-
if (liveSettings.variantPresets() === true) return
|
|
2673
|
-
const readDefault = () => { try { return ctx.agentPresets.defaultId } catch { return undefined } }
|
|
2674
|
-
const current = readDefault()
|
|
2675
|
-
if (typeof current !== 'string' || !PRESET_IDS.includes(current)) return
|
|
2676
|
-
const target = VARIANT_DEFAULT_SUCCESSORS[current]
|
|
2677
|
-
if (typeof target !== 'string') return
|
|
2678
|
-
// The successor must exist in the CURRENT roster: official ids
|
|
2679
|
-
// renamed once already (code→ptc), and writing an unknown id
|
|
2680
|
-
// would trade a known failure for a worse one.
|
|
2681
|
-
const roster = await ctx.agentPresets.list()
|
|
2682
|
-
if (!Array.isArray(roster) || !roster.some((row) => row && row.id === target)) {
|
|
2683
|
-
console.warn(TAG + ' default-preset migration stood down: the deployment default is the variant "'
|
|
2684
|
-
+ current + '" but the successor "' + target + '" is not registered on this host.'
|
|
2685
|
-
+ ' Set the default manually among the OFFICIAL presets, or turn variantPresets on.')
|
|
2686
|
-
return
|
|
2687
|
-
}
|
|
2688
|
-
try {
|
|
2689
|
-
await settings.update('agent-preset-registry', { selectedDefault: target })
|
|
2690
|
-
} catch (error) {
|
|
2691
|
-
console.warn(TAG + ' default-preset migration write failed (' + (error?.message ?? error)
|
|
2692
|
-
+ '): the deployment default is still the variant "' + current
|
|
2693
|
-
+ '" — NEW sessions would fail with agent-preset/not-found.'
|
|
2694
|
-
+ ' Fix: set an OFFICIAL default in the preset picker, or turn variantPresets on.')
|
|
2695
|
-
return
|
|
2696
|
-
}
|
|
2697
|
-
const after = readDefault()
|
|
2698
|
-
if (after === target) {
|
|
2699
|
-
console.log(TAG + ' deployment default migrated: "' + current + '" -> "' + target
|
|
2700
|
-
+ '" (variantPresets is off). Old sessions pinned to a variant still need a NEW'
|
|
2701
|
-
+ ' session — the official agent-preset/locked rule blocks a mid-session switch.')
|
|
2702
|
-
} else {
|
|
2703
|
-
console.warn(TAG + ' default-preset migration did not stick (read back "'
|
|
2704
|
-
+ (after === undefined ? 'unreadable' : after) + '"). Set the default manually in'
|
|
2705
|
-
+ ' Settings -> agent preset, or turn variantPresets on.')
|
|
2706
|
-
}
|
|
2707
|
-
} catch (error) {
|
|
2708
|
-
console.log(TAG + ' default-preset migration failed: ' + (error?.message ?? error))
|
|
2709
|
-
}
|
|
2710
|
-
})()
|
|
2711
|
-
})
|
|
2712
|
-
}
|
|
2718
|
+
ctx.inject(['settings'], () => {
|
|
2719
|
+
// Boot migration is detached: registry.list() may await the Loader boot.
|
|
2720
|
+
void migrateVariantDefault(ctx, () => liveSettings.variantPresets())
|
|
2721
|
+
})
|
|
2713
2722
|
} catch (error) {
|
|
2714
2723
|
console.log(TAG + ' default-preset migration wiring failed: ' + (error?.message ?? error))
|
|
2715
2724
|
}
|
|
@@ -2727,8 +2736,21 @@ export async function apply(ctx, config = {}) {
|
|
|
2727
2736
|
enabled: () => liveSettings.fastctxTools(),
|
|
2728
2737
|
createRequireFrom: (base) => createRequire(base ?? import.meta.url),
|
|
2729
2738
|
tools: (c) => { try { return c.get('tools') } catch { return undefined } },
|
|
2730
|
-
msysToWin: (p) =>
|
|
2731
|
-
|
|
2739
|
+
msysToWin: (p, execution) => {
|
|
2740
|
+
const dialect = liveSettings.dialect()
|
|
2741
|
+
if (dialect.posixPaths !== true || !dialectApplies(dialect, execution?.agent)) return p
|
|
2742
|
+
if (!dialect.virtualMounts) {
|
|
2743
|
+
return typeof p === 'string' && (MSYS_DRIVE_ROOT.test(p) || MSYS_BARE_DRIVE.test(p))
|
|
2744
|
+
? translateMsysPath(p, null) : p
|
|
2745
|
+
}
|
|
2746
|
+
return translateMsysPath(p, buildTranslateEnv(readConfiguredBashPath()))
|
|
2747
|
+
},
|
|
2748
|
+
echo: (text, execution) => {
|
|
2749
|
+
const dialect = liveSettings.dialect()
|
|
2750
|
+
if (dialect.posixPaths !== true || !dialectApplies(dialect, execution?.agent)) return text
|
|
2751
|
+
const env = dialect.virtualMounts ? buildTranslateEnv(readConfiguredBashPath()) : null
|
|
2752
|
+
return env ? msysEcho(text, env) : windowsToMsys(text)
|
|
2753
|
+
},
|
|
2732
2754
|
log: (m) => console.log(TAG + ' ' + m),
|
|
2733
2755
|
})
|
|
2734
2756
|
} catch (error) {
|