dsh-remote 0.8.47 → 0.8.49
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/lib/client.js +89 -16
- package/lib/ignore.js +46 -0
- package/lib/index.js +68 -23
- package/lib/remote-shell-child.cjs +305 -0
- package/lib/remote-shell.js +518 -0
- package/lib/search.js +8 -3
- package/package.json +1 -1
- package/node_modules/buildcheck/.eslintrc.js +0 -5
- package/node_modules/buildcheck/.github/workflows/ci.yml +0 -88
- package/node_modules/buildcheck/.github/workflows/lint.yml +0 -27
- package/node_modules/buildcheck/.npmignore +0 -3
- package/node_modules/buildcheck/LICENSE +0 -19
- package/node_modules/buildcheck/README.md +0 -106
- package/node_modules/buildcheck/deps/Find-VisualStudio.cs +0 -250
- package/node_modules/buildcheck/lib/findvs.js +0 -361
- package/node_modules/buildcheck/lib/index.js +0 -738
- package/node_modules/buildcheck/package.json +0 -36
- package/node_modules/buildcheck/test/test.js +0 -8
- package/node_modules/cpu-features/.eslintrc.js +0 -5
- package/node_modules/cpu-features/.github/workflows/ci.yml +0 -74
- package/node_modules/cpu-features/.github/workflows/lint.yml +0 -27
- package/node_modules/cpu-features/LICENSE +0 -19
- package/node_modules/cpu-features/README.md +0 -59
- package/node_modules/cpu-features/binding.gyp +0 -16
- package/node_modules/cpu-features/buildcheck.js +0 -32
- package/node_modules/cpu-features/deps/cpu_features/.clang-format +0 -4
- package/node_modules/cpu-features/deps/cpu_features/.dockerignore +0 -31
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/Dockerfile +0 -5
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/aarch64_linux_cmake.yml +0 -30
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_freebsd_cmake.yml +0 -22
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_linux_bazel.yml +0 -26
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_linux_cmake.yml +0 -31
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_macos_cmake.yml +0 -43
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/amd64_windows_cmake.yml +0 -25
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/arm_linux_cmake.yml +0 -31
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/clang_format.yml +0 -24
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/mips_linux_cmake.yml +0 -30
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/power_linux_cmake.yml +0 -29
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/riscv_linux_cmake.yml +0 -28
- package/node_modules/cpu-features/deps/cpu_features/.github/workflows/s390x_linux_cmake.yml +0 -27
- package/node_modules/cpu-features/deps/cpu_features/.grenrc.yml +0 -21
- package/node_modules/cpu-features/deps/cpu_features/BUILD.bazel +0 -329
- package/node_modules/cpu-features/deps/cpu_features/CMakeLists.txt +0 -261
- package/node_modules/cpu-features/deps/cpu_features/CONTRIBUTING.md +0 -23
- package/node_modules/cpu-features/deps/cpu_features/LICENSE +0 -230
- package/node_modules/cpu-features/deps/cpu_features/README.md +0 -272
- package/node_modules/cpu-features/deps/cpu_features/WORKSPACE +0 -19
- package/node_modules/cpu-features/deps/cpu_features/bazel/ci/README.md +0 -5
- package/node_modules/cpu-features/deps/cpu_features/bazel/platforms.bzl +0 -11
- package/node_modules/cpu-features/deps/cpu_features/cmake/CpuFeaturesConfig.cmake.in +0 -3
- package/node_modules/cpu-features/deps/cpu_features/cmake/CpuFeaturesNdkCompatConfig.cmake.in +0 -3
- package/node_modules/cpu-features/deps/cpu_features/cmake/README.md +0 -30
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/Makefile +0 -252
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/README.md +0 -40
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/doc/docker.dot +0 -64
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/doc/docker.svg +0 -312
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/doc/generate_image.sh +0 -7
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/docker/amd64/Dockerfile +0 -48
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/docker/toolchain/Dockerfile +0 -34
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/sample/CMakeLists.txt +0 -22
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/sample/main.cpp +0 -11
- package/node_modules/cpu-features/deps/cpu_features/cmake/ci/vagrant/freebsd/Vagrantfile +0 -107
- package/node_modules/cpu-features/deps/cpu_features/cmake/googletest.CMakeLists.txt.in +0 -15
- package/node_modules/cpu-features/deps/cpu_features/cpu_features.gyp +0 -105
- package/node_modules/cpu-features/deps/cpu_features/include/cpu_features_cache_info.h +0 -54
- package/node_modules/cpu-features/deps/cpu_features/include/cpu_features_macros.h +0 -384
- package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_aarch64.h +0 -259
- package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_arm.h +0 -121
- package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_mips.h +0 -74
- package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_ppc.h +0 -149
- package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_riscv.h +0 -72
- package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_s390x.h +0 -108
- package/node_modules/cpu-features/deps/cpu_features/include/cpuinfo_x86.h +0 -288
- package/node_modules/cpu-features/deps/cpu_features/include/internal/bit_utils.h +0 -40
- package/node_modules/cpu-features/deps/cpu_features/include/internal/cpuid_x86.h +0 -37
- package/node_modules/cpu-features/deps/cpu_features/include/internal/filesystem.h +0 -39
- package/node_modules/cpu-features/deps/cpu_features/include/internal/hwcaps.h +0 -240
- package/node_modules/cpu-features/deps/cpu_features/include/internal/stack_line_reader.h +0 -49
- package/node_modules/cpu-features/deps/cpu_features/include/internal/string_view.h +0 -110
- package/node_modules/cpu-features/deps/cpu_features/include/internal/windows_utils.h +0 -70
- package/node_modules/cpu-features/deps/cpu_features/ndk_compat/CMakeLists.txt +0 -60
- package/node_modules/cpu-features/deps/cpu_features/ndk_compat/README.md +0 -4
- package/node_modules/cpu-features/deps/cpu_features/ndk_compat/cpu-features.c +0 -205
- package/node_modules/cpu-features/deps/cpu_features/ndk_compat/cpu-features.h +0 -320
- package/node_modules/cpu-features/deps/cpu_features/ndk_compat/ndk-compat-test.c +0 -12
- package/node_modules/cpu-features/deps/cpu_features/patches/0001-Add-Apple-Silicon-Support.patch +0 -456
- package/node_modules/cpu-features/deps/cpu_features/scripts/generate_badges.d +0 -165
- package/node_modules/cpu-features/deps/cpu_features/scripts/make_release.sh +0 -75
- package/node_modules/cpu-features/deps/cpu_features/scripts/run_integration.sh +0 -490
- package/node_modules/cpu-features/deps/cpu_features/scripts/test_integration.sh +0 -79
- package/node_modules/cpu-features/deps/cpu_features/src/copy.inl +0 -19
- package/node_modules/cpu-features/deps/cpu_features/src/define_introspection.inl +0 -86
- package/node_modules/cpu-features/deps/cpu_features/src/define_introspection_and_hwcaps.inl +0 -26
- package/node_modules/cpu-features/deps/cpu_features/src/equals.inl +0 -22
- package/node_modules/cpu-features/deps/cpu_features/src/filesystem.c +0 -62
- package/node_modules/cpu-features/deps/cpu_features/src/hwcaps.c +0 -169
- package/node_modules/cpu-features/deps/cpu_features/src/impl_aarch64__base_implementation.inl +0 -88
- package/node_modules/cpu-features/deps/cpu_features/src/impl_aarch64_linux_or_android.c +0 -79
- package/node_modules/cpu-features/deps/cpu_features/src/impl_aarch64_macos_or_iphone.c +0 -82
- package/node_modules/cpu-features/deps/cpu_features/src/impl_aarch64_windows.c +0 -138
- package/node_modules/cpu-features/deps/cpu_features/src/impl_arm_linux_or_android.c +0 -212
- package/node_modules/cpu-features/deps/cpu_features/src/impl_mips_linux_or_android.c +0 -93
- package/node_modules/cpu-features/deps/cpu_features/src/impl_ppc_linux.c +0 -163
- package/node_modules/cpu-features/deps/cpu_features/src/impl_riscv_linux.c +0 -111
- package/node_modules/cpu-features/deps/cpu_features/src/impl_s390x_linux.c +0 -120
- package/node_modules/cpu-features/deps/cpu_features/src/impl_x86__base_implementation.inl +0 -2055
- package/node_modules/cpu-features/deps/cpu_features/src/impl_x86_freebsd.c +0 -68
- package/node_modules/cpu-features/deps/cpu_features/src/impl_x86_linux_or_android.c +0 -58
- package/node_modules/cpu-features/deps/cpu_features/src/impl_x86_macos.c +0 -57
- package/node_modules/cpu-features/deps/cpu_features/src/impl_x86_windows.c +0 -58
- package/node_modules/cpu-features/deps/cpu_features/src/stack_line_reader.c +0 -132
- package/node_modules/cpu-features/deps/cpu_features/src/string_view.c +0 -192
- package/node_modules/cpu-features/deps/cpu_features/src/utils/list_cpu_features.c +0 -461
- package/node_modules/cpu-features/deps/cpu_features/test/CMakeLists.txt +0 -114
- package/node_modules/cpu-features/deps/cpu_features/test/bit_utils_test.cc +0 -53
- package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_aarch64_test.cc +0 -396
- package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_arm_test.cc +0 -384
- package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_mips_test.cc +0 -176
- package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_ppc_test.cc +0 -131
- package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_riscv_test.cc +0 -180
- package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_s390x_test.cc +0 -82
- package/node_modules/cpu-features/deps/cpu_features/test/cpuinfo_x86_test.cc +0 -1729
- package/node_modules/cpu-features/deps/cpu_features/test/filesystem_for_testing.cc +0 -103
- package/node_modules/cpu-features/deps/cpu_features/test/filesystem_for_testing.h +0 -61
- package/node_modules/cpu-features/deps/cpu_features/test/hwcaps_for_testing.cc +0 -52
- package/node_modules/cpu-features/deps/cpu_features/test/hwcaps_for_testing.h +0 -31
- package/node_modules/cpu-features/deps/cpu_features/test/stack_line_reader_test.cc +0 -132
- package/node_modules/cpu-features/deps/cpu_features/test/string_view_test.cc +0 -202
- package/node_modules/cpu-features/lib/index.js +0 -5
- package/node_modules/cpu-features/package.json +0 -40
- package/node_modules/cpu-features/src/binding.cc +0 -151
- package/node_modules/cpu-features/test/test.js +0 -12
package/lib/client.js
CHANGED
|
@@ -2382,8 +2382,9 @@ window.__ModuleLoader__.load({
|
|
|
2382
2382
|
}
|
|
2383
2383
|
|
|
2384
2384
|
// ── better-sidebar integration (optional) ────────────────────────────────
|
|
2385
|
+
// One descriptor only: the explorer page. The per-file viewer is a native
|
|
2386
|
+
// resource type, never a better-sidebar card (see registerSidebarIntegration).
|
|
2385
2387
|
const SIDEBAR_EXPLORER_ID = 'dsh-remote:explorer'
|
|
2386
|
-
const SIDEBAR_FILE_ID = 'dsh-remote:file'
|
|
2387
2388
|
|
|
2388
2389
|
function fetchRemoteStatus(sessionId) {
|
|
2389
2390
|
return api('GET', withSessionQuery('/dsh-remote/status', sessionId)).catch(() => null)
|
|
@@ -2989,10 +2990,27 @@ window.__ModuleLoader__.load({
|
|
|
2989
2990
|
return () => { clearTimeout(tid); document.removeEventListener('mousedown', close); host.remove() }
|
|
2990
2991
|
}, [menu])
|
|
2991
2992
|
|
|
2993
|
+
// Open one remote file. The viewer is a RESOURCE tab type: the address
|
|
2994
|
+
// names the file, and the HOST's tab registry picks the type that claims
|
|
2995
|
+
// it (ours, `dsh-resource://dsh-remote/**`). That is the same call the
|
|
2996
|
+
// native explorer makes through `onOpenFile`, so one file behaves
|
|
2997
|
+
// identically in both compositions — and it needs no better-sidebar
|
|
2998
|
+
// descriptor, whose only effect would have been a switch that unregisters
|
|
2999
|
+
// the viewer.
|
|
2992
3000
|
const openFile = (p) => {
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
3001
|
+
if (props.onOpenFile) { props.onOpenFile(p); return }
|
|
3002
|
+
const sessionId = (props.scope && props.scope.sessionId) || ''
|
|
3003
|
+
try {
|
|
3004
|
+
const right = props.ctx && props.ctx.get && props.ctx.get('sidebarRight')
|
|
3005
|
+
if (right && typeof right.openResource === 'function') {
|
|
3006
|
+
right.openResource(remoteFileAddress(sessionId, p))
|
|
3007
|
+
return
|
|
3008
|
+
}
|
|
3009
|
+
} catch (e) {
|
|
3010
|
+
console.warn('[dsh-remote] remote file open failed:', e)
|
|
3011
|
+
return
|
|
3012
|
+
}
|
|
3013
|
+
console.warn('[dsh-remote] no right-sidebar resource opener; remote file not opened')
|
|
2996
3014
|
}
|
|
2997
3015
|
|
|
2998
3016
|
const doFs = (op, payload, refreshParent) => {
|
|
@@ -3302,13 +3320,14 @@ window.__ModuleLoader__.load({
|
|
|
3302
3320
|
// Issue #32: own the expansion state (see RemoteExplorerTabOwned).
|
|
3303
3321
|
component: RemoteExplorerTabOwned,
|
|
3304
3322
|
}))
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3323
|
+
// No descriptor for the FILE viewer on purpose. It is a resource tab
|
|
3324
|
+
// type (`dsh-remote/file`, registered with `patterns` on the native
|
|
3325
|
+
// registry), not a page: it only means something once an address names
|
|
3326
|
+
// the file, so it has no card to draw and no page to open. A
|
|
3327
|
+
// descriptor here would put a card in better-sidebar's 侧边栏内容
|
|
3328
|
+
// inventory whose switch could only ever unregister the viewer — the
|
|
3329
|
+
// "switch does nothing" report this replaced. The tree hands the HOST
|
|
3330
|
+
// an address instead and the host routes it to that type (openFile).
|
|
3312
3331
|
} catch (e) {
|
|
3313
3332
|
console.warn('[dsh-remote] better-sidebar integration skipped:', e)
|
|
3314
3333
|
disposers.forEach((d) => { try { d() } catch {} })
|
|
@@ -3522,11 +3541,65 @@ window.__ModuleLoader__.load({
|
|
|
3522
3541
|
// built-in local preview. This is a warm-up, not a dependency: a failure
|
|
3523
3542
|
// just leaves the conservative behaviour in place.
|
|
3524
3543
|
try { ensureMirrors() } catch { /* the registry is best-effort */ }
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3544
|
+
// ── Who owns the 开始 card ────────────────────────────────────────────
|
|
3545
|
+
// The 远程文件 card on the right sidebar's start page (开始) is the
|
|
3546
|
+
// `guide` entry of a native tab type, and TWO providers can publish one:
|
|
3547
|
+
// this plugin (below) and dsh-better-sidebar, which registers a native
|
|
3548
|
+
// type per tab descriptor it hosts — our `dsh-remote:explorer` gets a
|
|
3549
|
+
// guide row of its own there, governed by its side-card settings switch.
|
|
3550
|
+
//
|
|
3551
|
+
// Publishing both is what made that switch look broken: turning the type
|
|
3552
|
+
// off removed better-sidebar's row and left ours, so the card never went
|
|
3553
|
+
// away. The card therefore belongs to exactly ONE provider:
|
|
3554
|
+
// · better-sidebar in the composition → ITS descriptor owns the card
|
|
3555
|
+
// (its switch IS the switch for it), and this type is registered
|
|
3556
|
+
// without a guide entry. The type and its body stay: a tab opened by
|
|
3557
|
+
// kind still renders, and nothing else about the panel changes.
|
|
3558
|
+
// · plain install → the card is ours, always, exactly as before.
|
|
3559
|
+
//
|
|
3560
|
+
// `guide` is captured at registration time and an id may not be
|
|
3561
|
+
// registered twice, so a handover means dispose-then-register inside one
|
|
3562
|
+
// synchronous block: no render can observe the gap.
|
|
3563
|
+
let betterSidebar = false
|
|
3564
|
+
let disposed = false
|
|
3565
|
+
let disposeExplorer = null
|
|
3566
|
+
let guideListed = null
|
|
3567
|
+
const syncExplorerType = () => {
|
|
3568
|
+
const withGuide = !betterSidebar
|
|
3569
|
+
if (guideListed === withGuide && disposeExplorer) return
|
|
3570
|
+
const previous = disposeExplorer
|
|
3571
|
+
disposeExplorer = null
|
|
3572
|
+
if (previous) { try { previous() } catch { /* already released */ } }
|
|
3573
|
+
disposeExplorer = tabs.register({
|
|
3574
|
+
id: NATIVE_EXPLORER, kind: NATIVE_EXPLORER,
|
|
3575
|
+
title: () => tr('explorer.tabTitle'),
|
|
3576
|
+
...(withGuide ? { guide: [{ order: 55, title: () => tr('explorer.tabTitle') }] } : {}),
|
|
3577
|
+
})
|
|
3578
|
+
guideListed = withGuide
|
|
3579
|
+
}
|
|
3580
|
+
inner.effect(() => {
|
|
3581
|
+
syncExplorerType()
|
|
3582
|
+
return () => {
|
|
3583
|
+
disposed = true
|
|
3584
|
+
const dispose = disposeExplorer
|
|
3585
|
+
disposeExplorer = null
|
|
3586
|
+
guideListed = null
|
|
3587
|
+
if (dispose) { try { dispose() } catch { /* already released */ } }
|
|
3588
|
+
}
|
|
3589
|
+
}, 'dsh-remote.native-explorer-type')
|
|
3590
|
+
// The two plugins may activate in EITHER order, so the service has to be
|
|
3591
|
+
// followed rather than read once: the card moves to better-sidebar when
|
|
3592
|
+
// it arrives, and comes back here if it goes away.
|
|
3593
|
+
inner.inject(['betterSidebar'], (withSidebar) => {
|
|
3594
|
+
betterSidebar = true
|
|
3595
|
+
try { syncExplorerType() } catch (e) { console.warn('[dsh-remote] start-page card handover skipped:', e) }
|
|
3596
|
+
if (!withSidebar || typeof withSidebar.effect !== 'function') return
|
|
3597
|
+
withSidebar.effect(() => () => {
|
|
3598
|
+
betterSidebar = false
|
|
3599
|
+
if (disposed) return
|
|
3600
|
+
try { syncExplorerType() } catch (e) { console.warn('[dsh-remote] start-page card handover skipped:', e) }
|
|
3601
|
+
}, 'dsh-remote.start-card-owner')
|
|
3602
|
+
})
|
|
3530
3603
|
inner.effect(() => tabs.register({
|
|
3531
3604
|
id: NATIVE_FILE, kind: NATIVE_FILE,
|
|
3532
3605
|
patterns: ['dsh-resource://dsh-remote/**'],
|
package/lib/ignore.js
CHANGED
|
@@ -139,6 +139,52 @@ export const DEFAULT_SEARCH_IGNORE = [
|
|
|
139
139
|
'Library/Caches/',
|
|
140
140
|
]
|
|
141
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Whether a normalized remote path is a FILESYSTEM ROOT: POSIX `/`, a Windows
|
|
144
|
+
* drive root (`D:\`), or a UNC share root (`\\server\share`).
|
|
145
|
+
*
|
|
146
|
+
* Root workspaces are legal (issue #60) but dangerous to walk naively: `/proc`
|
|
147
|
+
* and `/sys` are synthetic (new pids appear between readdirs, `/proc/kcore`
|
|
148
|
+
* advertises 128 TB), `/dev` holds never-ending character devices, and
|
|
149
|
+
* `/var/lib/docker` is tens of GB of image layers. ROOT_IGNORE exists so a
|
|
150
|
+
* root workspace skips exactly those trees.
|
|
151
|
+
*/
|
|
152
|
+
export function isFilesystemRoot(p) {
|
|
153
|
+
const s = String(p || '')
|
|
154
|
+
return s === '/' || /^[a-zA-Z]:\\?$/.test(s) || /^\\\\[^\\]+\\[^\\]+$/.test(s)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Patterns merged in ONLY when the workspace is the filesystem root
|
|
159
|
+
* (issue #60). Every entry is root-anchored AND directory-only, so even when
|
|
160
|
+
* merged they can only match at the workspace top level: `/` skips `/proc`,
|
|
161
|
+
* while a hypothetical `/home/u/proc` under a non-root workspace is ordinary
|
|
162
|
+
* content (and non-root workspaces never receive these patterns at all, so
|
|
163
|
+
* their behaviour is byte-identical to before).
|
|
164
|
+
*/
|
|
165
|
+
export const ROOT_IGNORE = [
|
|
166
|
+
'/proc/',
|
|
167
|
+
'/sys/',
|
|
168
|
+
'/dev/',
|
|
169
|
+
'/run/',
|
|
170
|
+
'/boot/',
|
|
171
|
+
'/var/lib/docker/',
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Effective ignore patterns for a workspace rooted at `root`:
|
|
176
|
+
* the defaults, plus ROOT_IGNORE only for a root workspace, plus the user's
|
|
177
|
+
* own `.dsh-remote-ignore` lines (always last, they stack on top).
|
|
178
|
+
*
|
|
179
|
+
* @param {string} root - workspace root as passed to sync/push/search.
|
|
180
|
+
* @param {{search?: boolean, fromFile?: string[]}} [opts]
|
|
181
|
+
*/
|
|
182
|
+
export function ignorePatternsFor(root, { search = false, fromFile = [] } = {}) {
|
|
183
|
+
const base = search ? DEFAULT_SEARCH_IGNORE : DEFAULT_IGNORE
|
|
184
|
+
const extra = isFilesystemRoot(root) ? ROOT_IGNORE : []
|
|
185
|
+
return base.concat(extra, fromFile)
|
|
186
|
+
}
|
|
187
|
+
|
|
142
188
|
/**
|
|
143
189
|
* Load the user ignore file (gitignore syntax) and merge with defaults.
|
|
144
190
|
* Returns `{ patterns, fromFile }`.
|
package/lib/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { execFile } from 'node:child_process'
|
|
|
24
24
|
import { readFileSync, mkdirSync, writeFileSync, existsSync, readdirSync, statSync, renameSync, copyFileSync, appendFileSync, watch, rmSync } from 'node:fs'
|
|
25
25
|
import { homedir } from 'node:os'
|
|
26
26
|
import path from 'node:path'
|
|
27
|
+
import { fileURLToPath } from 'node:url'
|
|
27
28
|
import iconv from 'iconv-lite'
|
|
28
29
|
|
|
29
30
|
import {
|
|
@@ -31,13 +32,14 @@ import {
|
|
|
31
32
|
toShellPath, toDisplayPath, remotePathBase, truncate, shortHash,
|
|
32
33
|
} from './paths.js'
|
|
33
34
|
import { createHostKeyGuard, isHostKeyKnown as _isHostKeyKnown } from './hostkey.js'
|
|
34
|
-
import { compileIgnore,
|
|
35
|
+
import { compileIgnore, ignorePatternsFor, DEFAULT_IGNORE } from './ignore.js'
|
|
35
36
|
import { friendlyMessage } from './errors.js'
|
|
36
37
|
import { importableEntries, sshConfigPath, readSshConfigText, resolveMachineSshConfig } from './sshconfig.js'
|
|
37
38
|
import { createRemoteFileReference, installFileReferenceOverlay, DEFAULT_EXCLUDED_DIRECTORIES } from './file-reference.js'
|
|
38
39
|
import { getSecret, deleteSecret, platformBackend, persistPassword } from './credential.js'
|
|
39
40
|
import { syncTree, pushTree, loadSyncState, saveSyncState, pushOneFile } from './sync.js'
|
|
40
41
|
import { searchRemote } from './search.js'
|
|
42
|
+
import { installTerminalShell } from './remote-shell.js'
|
|
41
43
|
import { resolveMirror, poolKey, lookupSessionCwd, encodeSegmentSafe, readSessionHeaderCwd, requestSessionHint } from './binding.js'
|
|
42
44
|
import { TaskManager } from './tasks.js'
|
|
43
45
|
import { DeployTasks } from './deploy-tasks.js'
|
|
@@ -256,6 +258,30 @@ function mirrorRootFor(host, user, port) {
|
|
|
256
258
|
return path.join(remoteWorkspacesRoot(), tag)
|
|
257
259
|
}
|
|
258
260
|
|
|
261
|
+
/** Generated terminal launchers. Deliberately NOT under `remote-workspaces`: a
|
|
262
|
+
* launcher there would sit next to the mirrors, where anything that scans the
|
|
263
|
+
* root for checkouts could pick it up as one. */
|
|
264
|
+
function remoteTerminalRoot() {
|
|
265
|
+
return path.join(dshBase(), 'remote-terminal')
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Every path the remote-shell contribution needs, resolved for this install.
|
|
270
|
+
*
|
|
271
|
+
* Split out from the registration itself so the wiring at the bottom of the
|
|
272
|
+
* plugin stays one readable line, and so the launcher path is a pure function
|
|
273
|
+
* of where the plugin lives.
|
|
274
|
+
*
|
|
275
|
+
* @returns {{ dir: string, childPath: string, machinesFile: string }}
|
|
276
|
+
*/
|
|
277
|
+
function remoteShellSpec() {
|
|
278
|
+
return {
|
|
279
|
+
dir: remoteTerminalRoot(),
|
|
280
|
+
childPath: fileURLToPath(new URL('./remote-shell-child.cjs', import.meta.url)),
|
|
281
|
+
machinesFile: machinesFile(),
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
259
285
|
/** Local mirror dir for a specific remote path (idempotent → returns same dir). */
|
|
260
286
|
function mirrorDirFor(remotePath, host, user, port) {
|
|
261
287
|
const base = remotePathBase(remotePath)
|
|
@@ -927,26 +953,21 @@ export async function apply(ctx, config) {
|
|
|
927
953
|
// NOTE: this matcher is shared by mirror sync (rw_sync/rw_push) and search.
|
|
928
954
|
// Do NOT add search-only cache excludes here: widening it would silently stop
|
|
929
955
|
// syncing directories a user may rely on. Search uses searchIgnoreMatcher().
|
|
930
|
-
const
|
|
956
|
+
const readUserIgnore = () => {
|
|
931
957
|
try {
|
|
932
|
-
|
|
933
|
-
return compileIgnore(DEFAULT_IGNORE.concat(fromFile))
|
|
958
|
+
return readFileSync(ignoreFile(), 'utf8').split('\n').map((l) => l.trim()).filter((l) => l && !l.startsWith('#'))
|
|
934
959
|
} catch {
|
|
935
|
-
return
|
|
960
|
+
return []
|
|
936
961
|
}
|
|
937
962
|
}
|
|
963
|
+
/** Mirror-sync matcher for the workspace rooted at `root` (ROOT_IGNORE is
|
|
964
|
+
* merged only for a filesystem-root workspace — issue #60). */
|
|
965
|
+
const ignoreMatcher = (root) => compileIgnore(ignorePatternsFor(root, { fromFile: readUserIgnore() }))
|
|
938
966
|
/** Search-only matcher: same as ignoreMatcher() plus machine-local cache trees
|
|
939
967
|
* (`~/.npm`, `~/.cache`, …) that are huge, almost never what a human is
|
|
940
968
|
* looking for, and the main cause of the issue #44 hang. Mirror sync is
|
|
941
969
|
* deliberately unaffected. An explicit `path` still searches anywhere. */
|
|
942
|
-
const searchIgnoreMatcher = () => {
|
|
943
|
-
try {
|
|
944
|
-
const fromFile = readFileSync(ignoreFile(), 'utf8').split('\n').map((l) => l.trim()).filter((l) => l && !l.startsWith('#'))
|
|
945
|
-
return compileIgnore(DEFAULT_SEARCH_IGNORE.concat(fromFile))
|
|
946
|
-
} catch {
|
|
947
|
-
return compileIgnore(DEFAULT_SEARCH_IGNORE)
|
|
948
|
-
}
|
|
949
|
-
}
|
|
970
|
+
const searchIgnoreMatcher = (root) => compileIgnore(ignorePatternsFor(root, { search: true, fromFile: readUserIgnore() }))
|
|
950
971
|
|
|
951
972
|
// ── task + forward managers ───────────────────────────────────────────────
|
|
952
973
|
const tasks = new TaskManager()
|
|
@@ -991,7 +1012,7 @@ export async function apply(ctx, config) {
|
|
|
991
1012
|
const target = poolForMachine(machine)
|
|
992
1013
|
let sftp
|
|
993
1014
|
try { sftp = await target.sftp() } catch { return }
|
|
994
|
-
const matcher = ignoreMatcher()
|
|
1015
|
+
const matcher = ignoreMatcher(ws)
|
|
995
1016
|
const state = loadSyncState(localDir)
|
|
996
1017
|
const next = { ...state }
|
|
997
1018
|
if (rels.includes('*')) {
|
|
@@ -1711,8 +1732,15 @@ export async function apply(ctx, config) {
|
|
|
1711
1732
|
},
|
|
1712
1733
|
output: textOut,
|
|
1713
1734
|
async execute(args, exec) {
|
|
1714
|
-
|
|
1715
|
-
|
|
1735
|
+
// Blank input is the only upfront rejection: normalizeRemotePath('')
|
|
1736
|
+
// returns '/', so the old `!p || p === '/'` guard actually meant "no
|
|
1737
|
+
// root" — but a filesystem root is a legal workspace (issue #60), and
|
|
1738
|
+
// its synthetic-tree risk is handled by ROOT_IGNORE in sync/search.
|
|
1739
|
+
// Any non-blank path, root included, is answered by the real directory
|
|
1740
|
+
// probe below (a missing path still fails there).
|
|
1741
|
+
const rawPath = String(args.path || '').trim()
|
|
1742
|
+
if (!rawPath) throw new Error('rw_pick_workspace: path must be an absolute directory')
|
|
1743
|
+
const p = normalizeRemotePath(rawPath)
|
|
1716
1744
|
// Picking a workspace targets the machine this session is already bound
|
|
1717
1745
|
// to when it has one. A LOCAL session deliberately falls back to the
|
|
1718
1746
|
// active machine here: this is the setup flow that runs right after
|
|
@@ -1755,7 +1783,7 @@ export async function apply(ctx, config) {
|
|
|
1755
1783
|
mkdirSync(local, { recursive: true })
|
|
1756
1784
|
const depth = Math.min(Math.max(Number(args.depth) || 8, 1), 16)
|
|
1757
1785
|
const maxFiles = Math.min(Math.max(Number(args.maxFiles) || 2000, 1), 20000)
|
|
1758
|
-
const isIgnored = ignoreMatcher()
|
|
1786
|
+
const isIgnored = ignoreMatcher(p)
|
|
1759
1787
|
const body = { depth, maxFiles, dryRun: !!args.dryRun, force: !!args.force, isIgnored }
|
|
1760
1788
|
const runSync = async () => {
|
|
1761
1789
|
let sftp
|
|
@@ -1805,7 +1833,7 @@ export async function apply(ctx, config) {
|
|
|
1805
1833
|
const local = b.mirrorDir || mirrorDirFor(p, b.host, b.username, b.port)
|
|
1806
1834
|
if (!existsSync(local)) throw new Error(`rw_push: local mirror does not exist — run rw_sync first (${local})`)
|
|
1807
1835
|
const maxFiles = Math.min(Math.max(Number(args.maxFiles) || 2000, 1), 20000)
|
|
1808
|
-
const isIgnored = ignoreMatcher()
|
|
1836
|
+
const isIgnored = ignoreMatcher(p)
|
|
1809
1837
|
const body = { maxFiles, dryRun: !!args.dryRun, force: !!args.force, isIgnored }
|
|
1810
1838
|
const runPush = async () => {
|
|
1811
1839
|
let sftp
|
|
@@ -2227,7 +2255,7 @@ export async function apply(ctx, config) {
|
|
|
2227
2255
|
// `running` and cancel/steer did nothing (issue #44).
|
|
2228
2256
|
const maxFiles = Math.min(Math.max(Number(args.maxEntries) || config.searchMaxEntries, 1), 500000)
|
|
2229
2257
|
const maxDurationMs = Math.min(Math.max(Number(args.maxDurationMs) || config.searchTimeoutMs, 1000), 600000)
|
|
2230
|
-
const matcher = searchIgnoreMatcher()
|
|
2258
|
+
const matcher = searchIgnoreMatcher(dir)
|
|
2231
2259
|
const { matches, scanned, truncated, cancelled } = await searchRemote(b.pool, dir, {
|
|
2232
2260
|
pattern,
|
|
2233
2261
|
regex,
|
|
@@ -2832,8 +2860,11 @@ export async function apply(ctx, config) {
|
|
|
2832
2860
|
if (req.method !== 'POST') return sendJson(res, 405, { error: 'method not allowed' })
|
|
2833
2861
|
try {
|
|
2834
2862
|
const payload = JSON.parse((await readBody(req)) || '{}')
|
|
2835
|
-
|
|
2836
|
-
|
|
2863
|
+
// Blank-only guard: a filesystem root is a legal workspace (issue
|
|
2864
|
+
// #60); the real directory probe below answers everything else.
|
|
2865
|
+
const rawPath = String(payload.path || '').trim()
|
|
2866
|
+
if (!rawPath) return sendJson(res, 400, { error: 'path must be an absolute directory' })
|
|
2867
|
+
const p = normalizeRemotePath(rawPath)
|
|
2837
2868
|
const okDir = await isRemoteDir(p)
|
|
2838
2869
|
if (!okDir) return sendJson(res, 400, { ok: false, error: `not a directory: ${p}` })
|
|
2839
2870
|
persistWorkspace(p)
|
|
@@ -2852,8 +2883,11 @@ export async function apply(ctx, config) {
|
|
|
2852
2883
|
if (req.method !== 'POST') return sendJson(res, 405, { error: 'method not allowed' })
|
|
2853
2884
|
try {
|
|
2854
2885
|
const payload = JSON.parse((await readBody(req)) || '{}')
|
|
2855
|
-
|
|
2856
|
-
|
|
2886
|
+
// Blank-only guard: a filesystem root is a legal workspace (issue
|
|
2887
|
+
// #60); the real directory probe below answers everything else.
|
|
2888
|
+
const rawPath = String(payload.path || '').trim()
|
|
2889
|
+
if (!rawPath) return sendJson(res, 400, { ok: false, error: 'path must be an absolute directory' })
|
|
2890
|
+
const p = normalizeRemotePath(rawPath)
|
|
2857
2891
|
if (!config.host) return sendJson(res, 400, { ok: false, error: 'no remote host configured/connected — connect first' })
|
|
2858
2892
|
// The picker names the machine it was browsing. Re-assert it so a
|
|
2859
2893
|
// switch made elsewhere between the listing and this commit cannot
|
|
@@ -3723,6 +3757,17 @@ export async function apply(ctx, config) {
|
|
|
3723
3757
|
registerDeploySkill(skillCtx)
|
|
3724
3758
|
})
|
|
3725
3759
|
|
|
3760
|
+
// ── remote terminal: one extra shell in the host terminal's own list ──────
|
|
3761
|
+
// Additive and optional: a composition without `terminalController` simply
|
|
3762
|
+
// keeps the terminal it already had.
|
|
3763
|
+
//
|
|
3764
|
+
// The callback argument is a *new context*, not the service — reading the
|
|
3765
|
+
// service off it (as if it were the controller) is a silent no-op, because a
|
|
3766
|
+
// context has no `config.shellCandidates`.
|
|
3767
|
+
ctx.inject(['terminalController'], (inner) => {
|
|
3768
|
+
installTerminalShell(ctx, inner.get('terminalController'), remoteShellSpec())
|
|
3769
|
+
})
|
|
3770
|
+
|
|
3726
3771
|
// ── auto-update: check on load + on an interval; apply silently ───────────
|
|
3727
3772
|
// Failures are swallowed (never break the plugin). The persisted override
|
|
3728
3773
|
// (settings UI) wins over the profile-config default, which wins over
|