@terminalhere/cli 0.2.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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/apps.d.ts +4 -0
- package/dist/apps.js +88 -0
- package/dist/apps.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +53 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +8 -0
- package/dist/platform.js +134 -0
- package/dist/platform.js.map +1 -0
- package/dist/registry.d.ts +9 -0
- package/dist/registry.js +37 -0
- package/dist/registry.js.map +1 -0
- package/dist/service.d.ts +12 -0
- package/dist/service.js +59 -0
- package/dist/service.js.map +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TerminalHere Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# TerminalHere for npm
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mehmetduran932/terminalhere-npm/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
Cross-platform file-manager integration for opening a folder in a terminal.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```powershell
|
|
11
|
+
npm install --global @terminalhere/cli
|
|
12
|
+
terminalhere install
|
|
13
|
+
terminalhere status
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Windows adds Explorer entries beneath `HKCU\\Software\\Classes` without administrator privileges. macOS installs a Finder Quick Action, while Linux installs a Nautilus Script; restart Nautilus after installation if it does not appear immediately.
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
terminalhere install | uninstall | status
|
|
22
|
+
terminalhere enable <terminal|powershell|cmd|gitbash|vscode>
|
|
23
|
+
terminalhere disable <terminal|powershell|cmd|gitbash|vscode>
|
|
24
|
+
terminalhere enable-all | disable-all
|
|
25
|
+
terminalhere open [directory]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Add `--json` to `status`, `install`, or enable/disable commands for automation.
|
|
29
|
+
|
|
30
|
+
## Security and project rules
|
|
31
|
+
|
|
32
|
+
- No administrator privileges are required or requested.
|
|
33
|
+
- The package changes only the current user's supported file-manager integration locations.
|
|
34
|
+
- Report vulnerabilities privately; see [SECURITY.md](SECURITY.md).
|
|
35
|
+
- Contributions follow the fork/PR, branch naming, review, and CI requirements in
|
|
36
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
37
|
+
- This repository is public, but no npm package has been published from it.
|
|
38
|
+
|
|
39
|
+
## Development
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install
|
|
43
|
+
npm test
|
|
44
|
+
npm run build
|
|
45
|
+
npm pack --dry-run
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
No npm release has been published from this repository.
|
|
49
|
+
|
|
50
|
+
## Package migration
|
|
51
|
+
|
|
52
|
+
`@mdsoft2026/terminalhere` has been replaced by `@terminalhere/cli`. The
|
|
53
|
+
`terminalhere` command is unchanged; reinstall the package under its new name.
|
package/dist/apps.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AppInfo, TerminalTarget } from './types.js';
|
|
2
|
+
export declare function detectApp(target: TerminalTarget): Promise<AppInfo>;
|
|
3
|
+
export declare const targets: TerminalTarget[];
|
|
4
|
+
export declare function commandFor(target: TerminalTarget, executablePath: string, token: '%V' | '%1'): string;
|
package/dist/apps.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
import { delimiter, join } from 'node:path';
|
|
3
|
+
const targetDetails = {
|
|
4
|
+
terminal: {
|
|
5
|
+
target: 'terminal',
|
|
6
|
+
displayName: 'Open in Terminal',
|
|
7
|
+
registryKeyName: 'TerminalHere.WindowsTerminal',
|
|
8
|
+
},
|
|
9
|
+
powershell: {
|
|
10
|
+
target: 'powershell',
|
|
11
|
+
displayName: 'Open in PowerShell',
|
|
12
|
+
registryKeyName: 'TerminalHere.PowerShell',
|
|
13
|
+
},
|
|
14
|
+
cmd: {
|
|
15
|
+
target: 'cmd',
|
|
16
|
+
displayName: 'Open in Command Prompt',
|
|
17
|
+
registryKeyName: 'TerminalHere.CommandPrompt',
|
|
18
|
+
},
|
|
19
|
+
gitbash: {
|
|
20
|
+
target: 'gitbash',
|
|
21
|
+
displayName: 'Open in Git Bash',
|
|
22
|
+
registryKeyName: 'TerminalHere.GitBash',
|
|
23
|
+
},
|
|
24
|
+
vscode: {
|
|
25
|
+
target: 'vscode',
|
|
26
|
+
displayName: 'Open in VS Code',
|
|
27
|
+
registryKeyName: 'TerminalHere.VSCode',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const candidates = {
|
|
31
|
+
terminal: [],
|
|
32
|
+
powershell: [],
|
|
33
|
+
cmd: process.env.ComSpec ? [process.env.ComSpec] : [],
|
|
34
|
+
gitbash: ['C:\\Program Files\\Git\\git-bash.exe', 'C:\\Program Files (x86)\\Git\\git-bash.exe'],
|
|
35
|
+
vscode: [
|
|
36
|
+
join(process.env.LOCALAPPDATA ?? '', 'Programs', 'Microsoft VS Code', 'Code.exe'),
|
|
37
|
+
'C:\\Program Files\\Microsoft VS Code\\Code.exe',
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
async function exists(path) {
|
|
41
|
+
try {
|
|
42
|
+
await access(path);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function findOnPath(names) {
|
|
50
|
+
const paths = (process.env.PATH ?? '').split(delimiter).filter(Boolean);
|
|
51
|
+
for (const directory of paths) {
|
|
52
|
+
for (const name of names) {
|
|
53
|
+
const candidate = join(directory, name);
|
|
54
|
+
if (await exists(candidate))
|
|
55
|
+
return candidate;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
export async function detectApp(target) {
|
|
61
|
+
const details = targetDetails[target];
|
|
62
|
+
const pathNames = {
|
|
63
|
+
terminal: ['wt.exe'],
|
|
64
|
+
powershell: ['powershell.exe'],
|
|
65
|
+
cmd: ['cmd.exe'],
|
|
66
|
+
gitbash: ['git-bash.exe'],
|
|
67
|
+
vscode: ['Code.exe'],
|
|
68
|
+
};
|
|
69
|
+
const executablePath = (await findOnPath(pathNames[target])) ??
|
|
70
|
+
(await Promise.all(candidates[target].map(async (path) => ((await exists(path)) ? path : undefined)))).find(Boolean);
|
|
71
|
+
return { ...details, executablePath, iconPath: executablePath };
|
|
72
|
+
}
|
|
73
|
+
export const targets = Object.keys(targetDetails);
|
|
74
|
+
export function commandFor(target, executablePath, token) {
|
|
75
|
+
switch (target) {
|
|
76
|
+
case 'terminal':
|
|
77
|
+
return `wt.exe -d "${token}"`;
|
|
78
|
+
case 'powershell':
|
|
79
|
+
return `powershell.exe -NoExit -Command "Set-Location -LiteralPath '${token}'"`;
|
|
80
|
+
case 'cmd':
|
|
81
|
+
return `cmd.exe /K cd /d "${token}"`;
|
|
82
|
+
case 'gitbash':
|
|
83
|
+
return `"${executablePath}" --cd="${token}"`;
|
|
84
|
+
case 'vscode':
|
|
85
|
+
return `"${executablePath}" "${token}"`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=apps.js.map
|
package/dist/apps.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apps.js","sourceRoot":"","sources":["../src/apps.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { PlatformIntegration } from './platform.js';
|
|
3
|
+
import { targets } from './apps.js';
|
|
4
|
+
const commands = [
|
|
5
|
+
'install',
|
|
6
|
+
'uninstall',
|
|
7
|
+
'status',
|
|
8
|
+
'enable',
|
|
9
|
+
'disable',
|
|
10
|
+
'enable-all',
|
|
11
|
+
'disable-all',
|
|
12
|
+
'open',
|
|
13
|
+
];
|
|
14
|
+
function usage() {
|
|
15
|
+
return `TerminalHere\n\nUsage: terminalhere <${commands.join('|')}> [target] [--json]\nTargets: ${targets.join(', ')}`;
|
|
16
|
+
}
|
|
17
|
+
function isTarget(value) {
|
|
18
|
+
return Boolean(value && targets.includes(value));
|
|
19
|
+
}
|
|
20
|
+
function print(statuses, json) {
|
|
21
|
+
if (json)
|
|
22
|
+
console.log(JSON.stringify(statuses, null, 2));
|
|
23
|
+
else
|
|
24
|
+
for (const item of statuses)
|
|
25
|
+
console.log(`${item.displayName}: ${item.installed ? 'Available' : 'Not found'} / ${item.enabled ? 'Enabled' : 'Disabled'}`);
|
|
26
|
+
}
|
|
27
|
+
async function main() {
|
|
28
|
+
const [command, target, ...flags] = process.argv.slice(2);
|
|
29
|
+
const json = flags.includes('--json') || target === '--json';
|
|
30
|
+
if (!command || command === '--help' || command === '-h') {
|
|
31
|
+
console.log(usage());
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const service = new PlatformIntegration();
|
|
35
|
+
if (command === 'install' || command === 'enable-all')
|
|
36
|
+
return void print(await service.install(isTarget(target) ? target : 'terminal'), json);
|
|
37
|
+
if (command === 'uninstall' || command === 'disable-all')
|
|
38
|
+
return void print(await service.uninstall(isTarget(target) ? target : 'terminal'), json);
|
|
39
|
+
if (command === 'status')
|
|
40
|
+
return void print(await service.getStatus(isTarget(target) ? target : undefined), json);
|
|
41
|
+
if (command === 'open')
|
|
42
|
+
return service.open(target && target !== '--json' ? target : undefined);
|
|
43
|
+
if ((command === 'enable' || command === 'disable') && isTarget(target)) {
|
|
44
|
+
print(command === 'enable' ? await service.install(target) : await service.uninstall(target), json);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
throw new Error(usage());
|
|
48
|
+
}
|
|
49
|
+
main().catch((error) => {
|
|
50
|
+
console.error(error instanceof Error ? error.message : error);
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TerminalStatus, TerminalTarget } from './types.js';
|
|
2
|
+
export declare class PlatformIntegration {
|
|
3
|
+
private readonly windows;
|
|
4
|
+
install(target?: TerminalTarget): Promise<TerminalStatus[]>;
|
|
5
|
+
uninstall(target?: TerminalTarget): Promise<TerminalStatus[]>;
|
|
6
|
+
getStatus(target?: TerminalTarget): Promise<TerminalStatus[]>;
|
|
7
|
+
open(directory?: string): Promise<void>;
|
|
8
|
+
}
|
package/dist/platform.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { execFile as execFileCallback } from 'node:child_process';
|
|
2
|
+
import { chmod, mkdir, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { ContextMenuService } from './service.js';
|
|
7
|
+
import { RegExeClient } from './registry.js';
|
|
8
|
+
const execFile = promisify(execFileCallback);
|
|
9
|
+
const macWorkflow = join(homedir(), 'Library', 'Services', 'TerminalHere.workflow');
|
|
10
|
+
const linuxScript = join(homedir(), '.local', 'share', 'nautilus', 'scripts', 'TerminalHere');
|
|
11
|
+
const finderWorkflow = [
|
|
12
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
13
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
14
|
+
'<plist version="1.0"><dict><key>AMDocumentVersion</key><string>2</string><key>AMWorkflowType</key><string>Service</string>',
|
|
15
|
+
'<key>actions</key><array><dict><key>action</key><dict><key>AMAccepts</key><dict><key>Container</key><string>List</string><key>Optional</key><true/><key>Types</key><array><string>com.apple.cocoa.path</string></array></dict>',
|
|
16
|
+
'<key>AMActionVersion</key><string>2.0.3</string><key>AMApplication</key><array><string>Automator</string></array><key>AMParameterProperties</key><dict><key>COMMAND_STRING</key><dict><key>default value</key><string>for f in "$@"; do osascript - "$f" <<\'APPLESCRIPT\'\non run argv\n tell application "Terminal"\n activate\n do script "cd " & quoted form of item 1 of argv\n end tell\nend run\nAPPLESCRIPT\ndone</string></dict><key>inputMethod</key><dict><key>default value</key><integer>1</integer></dict><key>shell</key><dict><key>default value</key><string>/bin/zsh</string></dict></dict>',
|
|
17
|
+
'<key>AMProvides</key><dict><key>Container</key><string>List</string><key>Types</key><array><string>com.apple.cocoa.path</string></array></dict><key>ActionBundlePath</key><string>/System/Library/Automator/Run Shell Script.action</string><key>ActionName</key><string>Run Shell Script</string><key>BundleIdentifier</key><string>com.apple.RunShellScript</string><key>CFBundleVersion</key><string>2.0.3</string></dict><key>isViewVisible</key><true/></dict></array>',
|
|
18
|
+
'<key>workflowMetaData</key><dict><key>serviceApplicationBundleID</key><string>com.apple.finder</string><key>serviceInputTypeIdentifier</key><string>com.apple.cocoa.path</string><key>serviceOutputTypeIdentifier</key><string>com.apple.cocoa.path</string></dict></dict></plist>',
|
|
19
|
+
].join('');
|
|
20
|
+
const nautilusScript = [
|
|
21
|
+
'#!/bin/sh',
|
|
22
|
+
'for target in "$@"; do',
|
|
23
|
+
' if command -v xdg-terminal-exec >/dev/null 2>&1; then xdg-terminal-exec --working-directory="$target" && continue; fi',
|
|
24
|
+
' if command -v gnome-terminal >/dev/null 2>&1; then gnome-terminal --working-directory="$target" && continue; fi',
|
|
25
|
+
' if command -v konsole >/dev/null 2>&1; then konsole --workdir "$target" && continue; fi',
|
|
26
|
+
' xterm -e sh -lc \'cd "$1"; exec "$SHELL"\' sh "$target"',
|
|
27
|
+
'done',
|
|
28
|
+
'',
|
|
29
|
+
].join('\n');
|
|
30
|
+
function asStatus(target, displayName, enabled) {
|
|
31
|
+
return { target, displayName, registryKeyName: 'TerminalHere', installed: true, enabled };
|
|
32
|
+
}
|
|
33
|
+
async function fileExists(path) {
|
|
34
|
+
try {
|
|
35
|
+
await stat(path);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export class PlatformIntegration {
|
|
43
|
+
windows = new ContextMenuService(new RegExeClient());
|
|
44
|
+
async install(target = 'terminal') {
|
|
45
|
+
if (process.platform === 'win32')
|
|
46
|
+
return target === 'terminal' ? this.windows.enableAll() : [await this.windows.enable(target)];
|
|
47
|
+
if (target !== 'terminal')
|
|
48
|
+
throw new Error(`${target} is currently supported on Windows only.`);
|
|
49
|
+
if (process.platform === 'darwin') {
|
|
50
|
+
const document = join(macWorkflow, 'Contents', 'document.wflow');
|
|
51
|
+
await mkdir(dirname(document), { recursive: true });
|
|
52
|
+
await writeFile(document, finderWorkflow);
|
|
53
|
+
return [asStatus('terminal', 'Finder Quick Action: TerminalHere', true)];
|
|
54
|
+
}
|
|
55
|
+
if (process.platform === 'linux') {
|
|
56
|
+
await mkdir(dirname(linuxScript), { recursive: true });
|
|
57
|
+
await writeFile(linuxScript, nautilusScript);
|
|
58
|
+
await chmod(linuxScript, 0o755);
|
|
59
|
+
return [asStatus('terminal', 'Nautilus Script: TerminalHere', true)];
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
62
|
+
}
|
|
63
|
+
async uninstall(target = 'terminal') {
|
|
64
|
+
if (process.platform === 'win32')
|
|
65
|
+
return target === 'terminal'
|
|
66
|
+
? this.windows.disableAll()
|
|
67
|
+
: [await this.windows.disable(target)];
|
|
68
|
+
if (target !== 'terminal')
|
|
69
|
+
throw new Error(`${target} is currently supported on Windows only.`);
|
|
70
|
+
const path = process.platform === 'darwin'
|
|
71
|
+
? macWorkflow
|
|
72
|
+
: process.platform === 'linux'
|
|
73
|
+
? linuxScript
|
|
74
|
+
: undefined;
|
|
75
|
+
if (!path)
|
|
76
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
77
|
+
await rm(path, { recursive: true, force: true });
|
|
78
|
+
return [
|
|
79
|
+
asStatus('terminal', process.platform === 'darwin'
|
|
80
|
+
? 'Finder Quick Action: TerminalHere'
|
|
81
|
+
: 'Nautilus Script: TerminalHere', false),
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
async getStatus(target) {
|
|
85
|
+
if (process.platform === 'win32')
|
|
86
|
+
return target ? [await this.windows.status(target)] : this.windows.allStatuses();
|
|
87
|
+
if (target && target !== 'terminal')
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
target,
|
|
91
|
+
displayName: target,
|
|
92
|
+
registryKeyName: 'TerminalHere',
|
|
93
|
+
installed: false,
|
|
94
|
+
enabled: false,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
if (process.platform === 'darwin')
|
|
98
|
+
return [
|
|
99
|
+
asStatus('terminal', 'Finder Quick Action: TerminalHere', await fileExists(macWorkflow)),
|
|
100
|
+
];
|
|
101
|
+
if (process.platform === 'linux')
|
|
102
|
+
return [asStatus('terminal', 'Nautilus Script: TerminalHere', await fileExists(linuxScript))];
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
target: 'terminal',
|
|
106
|
+
displayName: 'TerminalHere',
|
|
107
|
+
registryKeyName: 'TerminalHere',
|
|
108
|
+
installed: false,
|
|
109
|
+
enabled: false,
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
async open(directory = process.cwd()) {
|
|
114
|
+
if (process.platform === 'win32') {
|
|
115
|
+
await execFile('wt.exe', ['-d', directory]);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (process.platform === 'darwin') {
|
|
119
|
+
await execFile('open', ['-a', 'Terminal', directory]);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (process.platform === 'linux') {
|
|
123
|
+
try {
|
|
124
|
+
await execFile('xdg-terminal-exec', [`--working-directory=${directory}`]);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
await execFile('gnome-terminal', [`--working-directory=${directory}`]);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RegistryClient } from './types.js';
|
|
2
|
+
/** Windows Registry client using argument arrays: values are never shell-interpolated. */
|
|
3
|
+
export declare class RegExeClient implements RegistryClient {
|
|
4
|
+
private run;
|
|
5
|
+
keyExists(path: string): Promise<boolean>;
|
|
6
|
+
setDefaultValue(path: string, value: string): Promise<void>;
|
|
7
|
+
setStringValue(path: string, name: string, value: string): Promise<void>;
|
|
8
|
+
deleteTree(path: string): Promise<void>;
|
|
9
|
+
}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { execFile as execFileCallback } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const execFile = promisify(execFileCallback);
|
|
4
|
+
const HKCU = 'HKCU\\';
|
|
5
|
+
function registryPath(path) {
|
|
6
|
+
return `${HKCU}${path}`;
|
|
7
|
+
}
|
|
8
|
+
/** Windows Registry client using argument arrays: values are never shell-interpolated. */
|
|
9
|
+
export class RegExeClient {
|
|
10
|
+
async run(args) {
|
|
11
|
+
await execFile('reg.exe', args, { windowsHide: true });
|
|
12
|
+
}
|
|
13
|
+
async keyExists(path) {
|
|
14
|
+
try {
|
|
15
|
+
await this.run(['QUERY', registryPath(path)]);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async setDefaultValue(path, value) {
|
|
23
|
+
await this.run(['ADD', registryPath(path), '/ve', '/t', 'REG_SZ', '/d', value, '/f']);
|
|
24
|
+
}
|
|
25
|
+
async setStringValue(path, name, value) {
|
|
26
|
+
await this.run(['ADD', registryPath(path), '/v', name, '/t', 'REG_SZ', '/d', value, '/f']);
|
|
27
|
+
}
|
|
28
|
+
async deleteTree(path) {
|
|
29
|
+
try {
|
|
30
|
+
await this.run(['DELETE', registryPath(path), '/f']);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Deleting a missing key is idempotent.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,QAAQ,GAAG,SAAS,CAAC,gBAAgB,CAAC,CAAC;AAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC;AAEtB,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,0FAA0F;AAC1F,MAAM,OAAO,YAAY;IACf,KAAK,CAAC,GAAG,CAAC,IAAc;QAC9B,MAAM,QAAQ,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,KAAa;QAC/C,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,IAAY,EAAE,KAAa;QAC5D,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IAC7F,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RegistryClient, TerminalStatus, TerminalTarget } from './types.js';
|
|
2
|
+
export declare class ContextMenuService {
|
|
3
|
+
private readonly registry;
|
|
4
|
+
constructor(registry: RegistryClient);
|
|
5
|
+
enable(target: TerminalTarget): Promise<TerminalStatus>;
|
|
6
|
+
disable(target: TerminalTarget): Promise<TerminalStatus>;
|
|
7
|
+
status(target: TerminalTarget): Promise<TerminalStatus>;
|
|
8
|
+
enableAll(): Promise<TerminalStatus[]>;
|
|
9
|
+
disableAll(): Promise<TerminalStatus[]>;
|
|
10
|
+
allStatuses(): Promise<TerminalStatus[]>;
|
|
11
|
+
private writeEntry;
|
|
12
|
+
}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { commandFor, detectApp, targets } from './apps.js';
|
|
2
|
+
const backgroundPaths = [
|
|
3
|
+
'Software\\Classes\\Directory\\Background\\shell',
|
|
4
|
+
'Software\\Classes\\Drive\\Background\\shell',
|
|
5
|
+
];
|
|
6
|
+
const directoryPaths = ['Software\\Classes\\Directory\\shell', 'Software\\Classes\\Drive\\shell'];
|
|
7
|
+
export class ContextMenuService {
|
|
8
|
+
registry;
|
|
9
|
+
constructor(registry) {
|
|
10
|
+
this.registry = registry;
|
|
11
|
+
}
|
|
12
|
+
async enable(target) {
|
|
13
|
+
const app = await detectApp(target);
|
|
14
|
+
if (!app.executablePath)
|
|
15
|
+
throw new Error(`${app.displayName} could not be found on this computer.`);
|
|
16
|
+
for (const parent of backgroundPaths)
|
|
17
|
+
await this.writeEntry(parent, app, '%V');
|
|
18
|
+
for (const parent of directoryPaths)
|
|
19
|
+
await this.writeEntry(parent, app, '%1');
|
|
20
|
+
return { ...app, installed: true, enabled: true };
|
|
21
|
+
}
|
|
22
|
+
async disable(target) {
|
|
23
|
+
const app = await detectApp(target);
|
|
24
|
+
for (const parent of [...backgroundPaths, ...directoryPaths])
|
|
25
|
+
await this.registry.deleteTree(`${parent}\\${app.registryKeyName}`);
|
|
26
|
+
return { ...app, installed: Boolean(app.executablePath), enabled: false };
|
|
27
|
+
}
|
|
28
|
+
async status(target) {
|
|
29
|
+
const app = await detectApp(target);
|
|
30
|
+
const enabled = await this.registry.keyExists(`${backgroundPaths[0]}\\${app.registryKeyName}`);
|
|
31
|
+
return { ...app, installed: Boolean(app.executablePath), enabled };
|
|
32
|
+
}
|
|
33
|
+
async enableAll() {
|
|
34
|
+
const results = [];
|
|
35
|
+
for (const target of targets) {
|
|
36
|
+
try {
|
|
37
|
+
results.push(await this.enable(target));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
results.push(await this.status(target));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
async disableAll() {
|
|
46
|
+
return Promise.all(targets.map((target) => this.disable(target)));
|
|
47
|
+
}
|
|
48
|
+
async allStatuses() {
|
|
49
|
+
return Promise.all(targets.map((target) => this.status(target)));
|
|
50
|
+
}
|
|
51
|
+
async writeEntry(parent, app, token) {
|
|
52
|
+
const path = `${parent}\\${app.registryKeyName}`;
|
|
53
|
+
await this.registry.setDefaultValue(path, app.displayName);
|
|
54
|
+
if (app.iconPath)
|
|
55
|
+
await this.registry.setStringValue(path, 'Icon', app.iconPath);
|
|
56
|
+
await this.registry.setDefaultValue(`${path}\\command`, commandFor(app.target, app.executablePath, token));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type TerminalTarget = 'terminal' | 'powershell' | 'cmd' | 'gitbash' | 'vscode';
|
|
2
|
+
export interface AppInfo {
|
|
3
|
+
target: TerminalTarget;
|
|
4
|
+
displayName: string;
|
|
5
|
+
registryKeyName: string;
|
|
6
|
+
executablePath?: string;
|
|
7
|
+
iconPath?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TerminalStatus extends AppInfo {
|
|
10
|
+
installed: boolean;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface RegistryClient {
|
|
14
|
+
keyExists(path: string): Promise<boolean>;
|
|
15
|
+
setDefaultValue(path: string, value: string): Promise<void>;
|
|
16
|
+
setStringValue(path: string, name: string, value: string): Promise<void>;
|
|
17
|
+
deleteTree(path: string): Promise<void>;
|
|
18
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@terminalhere/cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Manage Windows Explorer terminal context-menu entries from npm.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"terminalhere": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"exports": "./dist/index.js",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"format": "prettier --write .",
|
|
23
|
+
"format:check": "prettier --check .",
|
|
24
|
+
"prepack": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"windows",
|
|
28
|
+
"macos",
|
|
29
|
+
"linux",
|
|
30
|
+
"context-menu",
|
|
31
|
+
"terminal",
|
|
32
|
+
"explorer",
|
|
33
|
+
"finder",
|
|
34
|
+
"nautilus",
|
|
35
|
+
"cli"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/mehmetduran932/terminalhere-npm.git"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/mehmetduran932/terminalhere-npm/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/mehmetduran932/terminalhere-npm#readme",
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^22.10.2",
|
|
48
|
+
"prettier": "^3.4.2",
|
|
49
|
+
"typescript": "^5.7.2",
|
|
50
|
+
"vitest": "^2.1.8"
|
|
51
|
+
}
|
|
52
|
+
}
|