kiriya 0.1.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/CHANGELOG.md +58 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/src/config/clean-rules.js +39 -0
- package/dist/src/config/config-keys.js +24 -0
- package/dist/src/config/dependency-directories.js +16 -0
- package/dist/src/config/modules.js +38 -0
- package/dist/src/config/protected-paths.js +32 -0
- package/dist/src/config/rebuildable-directories.js +20 -0
- package/dist/src/core/application/command-registry.js +57 -0
- package/dist/src/core/application/config-values.js +33 -0
- package/dist/src/core/application/path-guard.js +69 -0
- package/dist/src/core/application/paths.js +77 -0
- package/dist/src/core/application/plugin-loader.js +100 -0
- package/dist/src/core/application/process-ending.js +28 -0
- package/dist/src/core/application/root-scope.js +123 -0
- package/dist/src/core/application/safety.js +26 -0
- package/dist/src/core/application/text-input.js +44 -0
- package/dist/src/core/application/walk.js +34 -0
- package/dist/src/core/domain/command.js +8 -0
- package/dist/src/core/domain/encodings.js +70 -0
- package/dist/src/core/domain/errors.js +49 -0
- package/dist/src/core/domain/glob.js +57 -0
- package/dist/src/core/domain/global-options.js +10 -0
- package/dist/src/core/domain/input-schema.js +54 -0
- package/dist/src/core/domain/message.js +7 -0
- package/dist/src/core/domain/module.js +2 -0
- package/dist/src/core/domain/names.js +22 -0
- package/dist/src/core/domain/ports/clipboard.js +2 -0
- package/dist/src/core/domain/ports/clock.js +2 -0
- package/dist/src/core/domain/ports/command-catalog.js +2 -0
- package/dist/src/core/domain/ports/compression.js +2 -0
- package/dist/src/core/domain/ports/config-store.js +2 -0
- package/dist/src/core/domain/ports/confirmation.js +2 -0
- package/dist/src/core/domain/ports/environment.js +2 -0
- package/dist/src/core/domain/ports/file-content.js +2 -0
- package/dist/src/core/domain/ports/file-system.js +2 -0
- package/dist/src/core/domain/ports/hasher.js +2 -0
- package/dist/src/core/domain/ports/network.js +2 -0
- package/dist/src/core/domain/ports/opener.js +2 -0
- package/dist/src/core/domain/ports/passthrough.js +2 -0
- package/dist/src/core/domain/ports/plugin-inventory.js +2 -0
- package/dist/src/core/domain/ports/plugin-source.js +2 -0
- package/dist/src/core/domain/ports/port-table.js +2 -0
- package/dist/src/core/domain/ports/process-runner.js +2 -0
- package/dist/src/core/domain/ports/process-table.js +2 -0
- package/dist/src/core/domain/ports/protected-paths.js +2 -0
- package/dist/src/core/domain/ports/random-source.js +2 -0
- package/dist/src/core/domain/ports/standard-input.js +2 -0
- package/dist/src/core/domain/ports/system-info.js +2 -0
- package/dist/src/core/domain/ports/trash.js +2 -0
- package/dist/src/core/domain/secrets.js +59 -0
- package/dist/src/core/domain/targets.js +41 -0
- package/dist/src/core/domain/text-case.js +39 -0
- package/dist/src/core/domain/values/bytes.js +13 -0
- package/dist/src/core/domain/view.js +2 -0
- package/dist/src/core/infrastructure/node/can-listen.js +10 -0
- package/dist/src/core/infrastructure/node/closed-standard-input.adapter.js +12 -0
- package/dist/src/core/infrastructure/node/config-location.js +27 -0
- package/dist/src/core/infrastructure/node/fs-errors.js +22 -0
- package/dist/src/core/infrastructure/node/json-config-store.adapter.js +69 -0
- package/dist/src/core/infrastructure/node/node-compression.adapter.js +82 -0
- package/dist/src/core/infrastructure/node/node-environment.adapter.js +21 -0
- package/dist/src/core/infrastructure/node/node-file-content.adapter.js +99 -0
- package/dist/src/core/infrastructure/node/node-file-system.adapter.js +169 -0
- package/dist/src/core/infrastructure/node/node-hasher.adapter.js +32 -0
- package/dist/src/core/infrastructure/node/node-network.adapter.js +168 -0
- package/dist/src/core/infrastructure/node/node-plugin-source.adapter.js +89 -0
- package/dist/src/core/infrastructure/node/node-process-runner.adapter.js +48 -0
- package/dist/src/core/infrastructure/node/node-random-source.adapter.js +7 -0
- package/dist/src/core/infrastructure/node/node-standard-input.adapter.js +24 -0
- package/dist/src/core/infrastructure/node/node-system-info.adapter.js +63 -0
- package/dist/src/core/infrastructure/node/process-signals.js +51 -0
- package/dist/src/core/infrastructure/node/run-program.js +65 -0
- package/dist/src/core/infrastructure/node/system-clock.adapter.js +22 -0
- package/dist/src/core/infrastructure/node/typed-programs.js +45 -0
- package/dist/src/core/infrastructure/platform/linux/freedesktop-trash.adapter.js +115 -0
- package/dist/src/core/infrastructure/platform/linux/linux-clipboard.adapter.js +70 -0
- package/dist/src/core/infrastructure/platform/linux/linux-opener.adapter.js +21 -0
- package/dist/src/core/infrastructure/platform/linux/linux-port-table.adapter.js +97 -0
- package/dist/src/core/infrastructure/platform/linux/linux-process-table.adapter.js +61 -0
- package/dist/src/core/infrastructure/platform/macos/macos-clipboard.adapter.js +29 -0
- package/dist/src/core/infrastructure/platform/macos/macos-opener.adapter.js +13 -0
- package/dist/src/core/infrastructure/platform/macos/macos-port-table.adapter.js +86 -0
- package/dist/src/core/infrastructure/platform/macos/macos-process-table.adapter.js +62 -0
- package/dist/src/core/infrastructure/platform/macos/macos-trash.adapter.js +64 -0
- package/dist/src/core/infrastructure/platform/windows/system32.js +6 -0
- package/dist/src/core/infrastructure/platform/windows/windows-clipboard.adapter.js +50 -0
- package/dist/src/core/infrastructure/platform/windows/windows-opener.adapter.js +11 -0
- package/dist/src/core/infrastructure/platform/windows/windows-port-table.adapter.js +38 -0
- package/dist/src/core/infrastructure/platform/windows/windows-process-table.adapter.js +134 -0
- package/dist/src/core/infrastructure/platform/windows/windows-trash.adapter.js +86 -0
- package/dist/src/core/presentation/cli/argv.js +83 -0
- package/dist/src/core/presentation/cli/cli-application.js +201 -0
- package/dist/src/core/presentation/cli/cli-view-format.js +46 -0
- package/dist/src/core/presentation/cli/help.js +202 -0
- package/dist/src/core/presentation/cli/json-output.js +28 -0
- package/dist/src/core/presentation/cli/style.js +21 -0
- package/dist/src/core/presentation/cli/suggest.js +25 -0
- package/dist/src/core/presentation/cli/terminal-confirmation.js +63 -0
- package/dist/src/core/presentation/ended-processes.js +18 -0
- package/dist/src/core/presentation/i18n/translator.js +23 -0
- package/dist/src/core/presentation/list-preview.js +10 -0
- package/dist/src/core/presentation/mcp/elicitation.js +55 -0
- package/dist/src/core/presentation/mcp/mcp-application.js +60 -0
- package/dist/src/core/presentation/mcp/mcp-confirmation.js +59 -0
- package/dist/src/core/presentation/mcp/mcp-server.js +355 -0
- package/dist/src/core/presentation/mcp/protocol.js +32 -0
- package/dist/src/core/presentation/mcp/request-state.js +66 -0
- package/dist/src/core/presentation/mcp/tool-definitions.js +149 -0
- package/dist/src/core/presentation/mcp/tool-results.js +56 -0
- package/dist/src/i18n/locales/en.js +827 -0
- package/dist/src/main.js +154 -0
- package/dist/src/modules/archive/application/archive-sources.js +55 -0
- package/dist/src/modules/archive/application/create-tar.use-case.js +123 -0
- package/dist/src/modules/archive/application/create-zip.use-case.js +106 -0
- package/dist/src/modules/archive/application/extract-tar.use-case.js +240 -0
- package/dist/src/modules/archive/application/extract-zip.use-case.js +181 -0
- package/dist/src/modules/archive/application/tar-reader.js +45 -0
- package/dist/src/modules/archive/application/zip-reader.js +57 -0
- package/dist/src/modules/archive/archive.module.js +27 -0
- package/dist/src/modules/archive/domain/crc32.js +18 -0
- package/dist/src/modules/archive/domain/tar-format.js +97 -0
- package/dist/src/modules/archive/domain/tar-stream.js +223 -0
- package/dist/src/modules/archive/domain/zip-format.js +158 -0
- package/dist/src/modules/archive/presentation/tar.view.js +17 -0
- package/dist/src/modules/archive/presentation/untar.view.js +30 -0
- package/dist/src/modules/archive/presentation/unzip.view.js +24 -0
- package/dist/src/modules/archive/presentation/zip.view.js +17 -0
- package/dist/src/modules/clip/application/copy-text.use-case.js +43 -0
- package/dist/src/modules/clip/application/paste-text.use-case.js +25 -0
- package/dist/src/modules/clip/clip.module.js +16 -0
- package/dist/src/modules/clip/presentation/clip.views.js +7 -0
- package/dist/src/modules/completion/application/print-script.use-case.js +38 -0
- package/dist/src/modules/completion/application/suggest-words.use-case.js +37 -0
- package/dist/src/modules/completion/completion.module.js +14 -0
- package/dist/src/modules/completion/domain/scripts.js +91 -0
- package/dist/src/modules/completion/domain/suggest.js +134 -0
- package/dist/src/modules/completion/presentation/completion.views.js +6 -0
- package/dist/src/modules/config/application/config-commands.js +14 -0
- package/dist/src/modules/config/application/get-setting.use-case.js +25 -0
- package/dist/src/modules/config/application/list-config-keys.use-case.js +26 -0
- package/dist/src/modules/config/application/list-settings.use-case.js +28 -0
- package/dist/src/modules/config/application/set-setting.use-case.js +53 -0
- package/dist/src/modules/config/application/show-config-path.use-case.js +21 -0
- package/dist/src/modules/config/application/unset-setting.use-case.js +28 -0
- package/dist/src/modules/config/config.module.js +28 -0
- package/dist/src/modules/config/presentation/config.views.js +33 -0
- package/dist/src/modules/convert/application/convert-case.use-case.js +38 -0
- package/dist/src/modules/convert/application/convert-codecs.use-case.js +92 -0
- package/dist/src/modules/convert/application/convert-input.js +16 -0
- package/dist/src/modules/convert/application/convert-json.use-case.js +51 -0
- package/dist/src/modules/convert/application/convert-time.use-case.js +39 -0
- package/dist/src/modules/convert/application/decode-jwt.use-case.js +52 -0
- package/dist/src/modules/convert/convert.module.js +29 -0
- package/dist/src/modules/convert/domain/json-syntax.js +135 -0
- package/dist/src/modules/convert/domain/jwt.js +29 -0
- package/dist/src/modules/convert/domain/moments.js +20 -0
- package/dist/src/modules/convert/presentation/convert.views.js +44 -0
- package/dist/src/modules/docker/application/clean-engine.use-case.js +77 -0
- package/dist/src/modules/docker/application/compose-project.js +45 -0
- package/dist/src/modules/docker/application/docker-engine.js +40 -0
- package/dist/src/modules/docker/application/list-containers.use-case.js +69 -0
- package/dist/src/modules/docker/application/run-compose.use-case.js +83 -0
- package/dist/src/modules/docker/application/show-logs.use-case.js +51 -0
- package/dist/src/modules/docker/application/stop-project.use-case.js +56 -0
- package/dist/src/modules/docker/docker.module.js +23 -0
- package/dist/src/modules/docker/domain/compose-file.js +105 -0
- package/dist/src/modules/docker/domain/engine-output.js +65 -0
- package/dist/src/modules/docker/presentation/docker.views.js +63 -0
- package/dist/src/modules/doctor/application/run-checks.use-case.js +128 -0
- package/dist/src/modules/doctor/doctor.module.js +13 -0
- package/dist/src/modules/doctor/domain/versions.js +22 -0
- package/dist/src/modules/doctor/presentation/doctor.view.js +30 -0
- package/dist/src/modules/env/application/check-dotenv.use-case.js +116 -0
- package/dist/src/modules/env/application/inspect-path.use-case.js +71 -0
- package/dist/src/modules/env/application/path-entries.js +28 -0
- package/dist/src/modules/env/application/show-variables.use-case.js +47 -0
- package/dist/src/modules/env/domain/dotenv.js +62 -0
- package/dist/src/modules/env/env.module.js +17 -0
- package/dist/src/modules/env/presentation/env.views.js +43 -0
- package/dist/src/modules/files/application/clean-builds.use-case.js +149 -0
- package/dist/src/modules/files/application/compare-paths.use-case.js +127 -0
- package/dist/src/modules/files/application/copy-paths.use-case.js +101 -0
- package/dist/src/modules/files/application/create-paths.use-case.js +85 -0
- package/dist/src/modules/files/application/delete-paths.use-case.js +127 -0
- package/dist/src/modules/files/application/find-duplicates.use-case.js +105 -0
- package/dist/src/modules/files/application/find-files.use-case.js +131 -0
- package/dist/src/modules/files/application/hash-files.use-case.js +73 -0
- package/dist/src/modules/files/application/list-entries.use-case.js +87 -0
- package/dist/src/modules/files/application/measure-folders.use-case.js +102 -0
- package/dist/src/modules/files/application/move-entry.js +19 -0
- package/dist/src/modules/files/application/move-paths.use-case.js +82 -0
- package/dist/src/modules/files/application/read-text.use-case.js +92 -0
- package/dist/src/modules/files/application/rename-paths.use-case.js +184 -0
- package/dist/src/modules/files/application/replace-text.use-case.js +146 -0
- package/dist/src/modules/files/application/search-text.use-case.js +115 -0
- package/dist/src/modules/files/application/show-info.use-case.js +80 -0
- package/dist/src/modules/files/application/show-tree.use-case.js +75 -0
- package/dist/src/modules/files/application/sync-folders.use-case.js +162 -0
- package/dist/src/modules/files/application/text-files.js +46 -0
- package/dist/src/modules/files/application/transfers.js +35 -0
- package/dist/src/modules/files/domain/case-style.js +18 -0
- package/dist/src/modules/files/domain/filters.js +9 -0
- package/dist/src/modules/files/domain/sync-plan.js +35 -0
- package/dist/src/modules/files/domain/text-encoding.js +75 -0
- package/dist/src/modules/files/domain/values.js +28 -0
- package/dist/src/modules/files/files.module.js +70 -0
- package/dist/src/modules/files/presentation/clean.view.js +15 -0
- package/dist/src/modules/files/presentation/compare.view.js +50 -0
- package/dist/src/modules/files/presentation/copy.view.js +17 -0
- package/dist/src/modules/files/presentation/delete.view.js +16 -0
- package/dist/src/modules/files/presentation/dupes.view.js +20 -0
- package/dist/src/modules/files/presentation/find.view.js +16 -0
- package/dist/src/modules/files/presentation/grep.view.js +27 -0
- package/dist/src/modules/files/presentation/hash.view.js +15 -0
- package/dist/src/modules/files/presentation/info.view.js +36 -0
- package/dist/src/modules/files/presentation/list.view.js +17 -0
- package/dist/src/modules/files/presentation/move.view.js +14 -0
- package/dist/src/modules/files/presentation/new.view.js +8 -0
- package/dist/src/modules/files/presentation/read.view.js +6 -0
- package/dist/src/modules/files/presentation/rename.view.js +16 -0
- package/dist/src/modules/files/presentation/replace.view.js +26 -0
- package/dist/src/modules/files/presentation/size.view.js +24 -0
- package/dist/src/modules/files/presentation/sync.view.js +37 -0
- package/dist/src/modules/files/presentation/tree.view.js +22 -0
- package/dist/src/modules/gen/application/gen-options.js +18 -0
- package/dist/src/modules/gen/application/generate-passwords.use-case.js +39 -0
- package/dist/src/modules/gen/application/generate-tokens.use-case.js +38 -0
- package/dist/src/modules/gen/application/generate-ulids.use-case.js +40 -0
- package/dist/src/modules/gen/application/generate-uuids.use-case.js +32 -0
- package/dist/src/modules/gen/domain/identifiers.js +65 -0
- package/dist/src/modules/gen/domain/secrets.js +43 -0
- package/dist/src/modules/gen/gen.module.js +20 -0
- package/dist/src/modules/git/application/fetch-repositories.use-case.js +50 -0
- package/dist/src/modules/git/application/git-client.js +62 -0
- package/dist/src/modules/git/application/pull-repositories.use-case.js +67 -0
- package/dist/src/modules/git/application/repositories.js +67 -0
- package/dist/src/modules/git/application/show-status.use-case.js +49 -0
- package/dist/src/modules/git/application/switch-branch.use-case.js +118 -0
- package/dist/src/modules/git/domain/repository-status.js +37 -0
- package/dist/src/modules/git/git.module.js +25 -0
- package/dist/src/modules/git/presentation/git.views.js +100 -0
- package/dist/src/modules/net/application/check-connection.use-case.js +71 -0
- package/dist/src/modules/net/application/list-addresses.use-case.js +29 -0
- package/dist/src/modules/net/application/lookup-name.use-case.js +55 -0
- package/dist/src/modules/net/net.module.js +17 -0
- package/dist/src/modules/net/presentation/net.views.js +39 -0
- package/dist/src/modules/open/application/open-target.use-case.js +53 -0
- package/dist/src/modules/open/domain/targets.js +45 -0
- package/dist/src/modules/open/open.module.js +12 -0
- package/dist/src/modules/open/presentation/open.view.js +6 -0
- package/dist/src/modules/port/application/end-port-owners.use-case.js +68 -0
- package/dist/src/modules/port/application/find-free-port.use-case.js +40 -0
- package/dist/src/modules/port/application/find-listeners.use-case.js +34 -0
- package/dist/src/modules/port/application/port-owners.js +25 -0
- package/dist/src/modules/port/domain/port-number.js +8 -0
- package/dist/src/modules/port/port.module.js +17 -0
- package/dist/src/modules/port/presentation/port.views.js +27 -0
- package/dist/src/modules/proc/application/end-processes.use-case.js +77 -0
- package/dist/src/modules/proc/application/find-processes.use-case.js +50 -0
- package/dist/src/modules/proc/application/list-processes.use-case.js +41 -0
- package/dist/src/modules/proc/application/show-tree.use-case.js +40 -0
- package/dist/src/modules/proc/domain/process-tree.js +55 -0
- package/dist/src/modules/proc/presentation/proc.views.js +28 -0
- package/dist/src/modules/proc/proc.module.js +19 -0
- package/dist/src/modules/sys/application/create-report.use-case.js +30 -0
- package/dist/src/modules/sys/application/find-tools.use-case.js +20 -0
- package/dist/src/modules/sys/application/machine-facts.js +44 -0
- package/dist/src/modules/sys/application/show-info.use-case.js +29 -0
- package/dist/src/modules/sys/domain/tools.js +17 -0
- package/dist/src/modules/sys/presentation/sys.views.js +67 -0
- package/dist/src/modules/sys/sys.module.js +17 -0
- package/dist/src/modules/wait/application/poll.js +26 -0
- package/dist/src/modules/wait/application/wait-file.use-case.js +55 -0
- package/dist/src/modules/wait/application/wait-port.use-case.js +76 -0
- package/dist/src/modules/wait/application/wait-url.use-case.js +71 -0
- package/dist/src/modules/wait/domain/reasons.js +14 -0
- package/dist/src/modules/wait/domain/wait-input.js +50 -0
- package/dist/src/modules/wait/presentation/wait.views.js +31 -0
- package/dist/src/modules/wait/wait.module.js +21 -0
- package/package.json +57 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { done } from "../../../core/domain/command.js";
|
|
2
|
+
import { UsageError } from "../../../core/domain/errors.js";
|
|
3
|
+
import { RawReader } from "../../../core/domain/input-schema.js";
|
|
4
|
+
import { message } from "../../../core/domain/message.js";
|
|
5
|
+
import { formatTarget, parseTarget } from "../../../core/domain/targets.js";
|
|
6
|
+
const MAX_TIMEOUT_SECONDS = 120;
|
|
7
|
+
export const checkSpec = {
|
|
8
|
+
id: "net.check",
|
|
9
|
+
summary: "net.check.summary",
|
|
10
|
+
examples: ["kiriya net check localhost:5432", "kiriya net check https://example.com", "kiriya net check [::1]:8080"],
|
|
11
|
+
safety: "read",
|
|
12
|
+
idempotent: true,
|
|
13
|
+
usesNetwork: true,
|
|
14
|
+
runsUserCommands: false,
|
|
15
|
+
input: {
|
|
16
|
+
positionals: [{ name: "target", description: "net.check.arg.target", required: true, variadic: false }],
|
|
17
|
+
options: { timeout: { type: "string", description: "net.check.option.timeout", valueName: "<seconds>" } },
|
|
18
|
+
parse(raw) {
|
|
19
|
+
const reader = new RawReader(raw);
|
|
20
|
+
const text = reader.positional(0) ?? "";
|
|
21
|
+
const target = parseTarget(text);
|
|
22
|
+
if (target === null)
|
|
23
|
+
throw new UsageError("net.check.target-invalid", { target: text });
|
|
24
|
+
const timeoutSeconds = reader.positiveInteger("timeout", 5);
|
|
25
|
+
if (timeoutSeconds > MAX_TIMEOUT_SECONDS) {
|
|
26
|
+
throw new UsageError("net.check.timeout-range", { max: MAX_TIMEOUT_SECONDS });
|
|
27
|
+
}
|
|
28
|
+
return { target, timeoutSeconds };
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const FAILURE_KEYS = {
|
|
33
|
+
refused: "net.check.refused",
|
|
34
|
+
timeout: "net.check.timeout",
|
|
35
|
+
"not-found": "net.check.not-found",
|
|
36
|
+
unreachable: "net.check.unreachable",
|
|
37
|
+
failed: "net.check.failed",
|
|
38
|
+
};
|
|
39
|
+
function failureMessage(input, outcome) {
|
|
40
|
+
return message(FAILURE_KEYS[outcome.failure], {
|
|
41
|
+
target: formatTarget(input.target),
|
|
42
|
+
host: input.target.host,
|
|
43
|
+
seconds: input.timeoutSeconds,
|
|
44
|
+
code: outcome.code ?? outcome.failure,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export class CheckConnection {
|
|
48
|
+
network;
|
|
49
|
+
spec = checkSpec;
|
|
50
|
+
constructor(network) {
|
|
51
|
+
this.network = network;
|
|
52
|
+
}
|
|
53
|
+
async execute(input, context) {
|
|
54
|
+
const { host, port } = input.target;
|
|
55
|
+
const outcome = await this.network.connect(host, port, input.timeoutSeconds * 1000, context.signal);
|
|
56
|
+
if (outcome.ok) {
|
|
57
|
+
return done({ host, port, reachable: true, address: outcome.address, ms: outcome.ms, failure: null, code: null });
|
|
58
|
+
}
|
|
59
|
+
const data = {
|
|
60
|
+
host,
|
|
61
|
+
port,
|
|
62
|
+
reachable: false,
|
|
63
|
+
address: null,
|
|
64
|
+
ms: outcome.ms,
|
|
65
|
+
failure: outcome.failure,
|
|
66
|
+
code: outcome.code,
|
|
67
|
+
};
|
|
68
|
+
return done(data, { failures: [failureMessage(input, outcome)] });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=check-connection.use-case.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { done } from "../../../core/domain/command.js";
|
|
2
|
+
import { RawReader } from "../../../core/domain/input-schema.js";
|
|
3
|
+
export const addressesSpec = {
|
|
4
|
+
id: "net.ip",
|
|
5
|
+
summary: "net.ip.summary",
|
|
6
|
+
examples: ["kiriya net ip", "kiriya net ip --all --json"],
|
|
7
|
+
safety: "read",
|
|
8
|
+
// Addresses change when the machine joins another network.
|
|
9
|
+
idempotent: false,
|
|
10
|
+
usesNetwork: false,
|
|
11
|
+
runsUserCommands: false,
|
|
12
|
+
input: {
|
|
13
|
+
positionals: [],
|
|
14
|
+
options: { all: { type: "boolean", description: "net.ip.option.all" } },
|
|
15
|
+
parse: (raw) => ({ all: new RawReader(raw).flag("all") }),
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
export class ListAddresses {
|
|
19
|
+
network;
|
|
20
|
+
spec = addressesSpec;
|
|
21
|
+
constructor(network) {
|
|
22
|
+
this.network = network;
|
|
23
|
+
}
|
|
24
|
+
execute(input) {
|
|
25
|
+
const addresses = this.network.addresses().filter((address) => input.all || !address.internal);
|
|
26
|
+
return Promise.resolve(done({ addresses }));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=list-addresses.use-case.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { done } from "../../../core/domain/command.js";
|
|
2
|
+
import { UsageError } from "../../../core/domain/errors.js";
|
|
3
|
+
import { RawReader } from "../../../core/domain/input-schema.js";
|
|
4
|
+
import { message } from "../../../core/domain/message.js";
|
|
5
|
+
import { DNS_RECORD_TYPES, } from "../../../core/domain/ports/network.js";
|
|
6
|
+
const LOOKUP_TYPES = ["system", ...DNS_RECORD_TYPES];
|
|
7
|
+
const DNS_TIMEOUT_MS = 5_000;
|
|
8
|
+
export const dnsSpec = {
|
|
9
|
+
id: "net.dns",
|
|
10
|
+
summary: "net.dns.summary",
|
|
11
|
+
examples: ["kiriya net dns example.com", "kiriya net dns example.com --type mx", "kiriya net dns localhost --json"],
|
|
12
|
+
safety: "read",
|
|
13
|
+
idempotent: true,
|
|
14
|
+
usesNetwork: true,
|
|
15
|
+
runsUserCommands: false,
|
|
16
|
+
input: {
|
|
17
|
+
positionals: [{ name: "name", description: "net.dns.arg.name", required: true, variadic: false }],
|
|
18
|
+
options: {
|
|
19
|
+
type: { type: "string", description: "net.dns.option.type", valueName: `<${LOOKUP_TYPES.join("|")}>` },
|
|
20
|
+
},
|
|
21
|
+
parse(raw) {
|
|
22
|
+
const reader = new RawReader(raw);
|
|
23
|
+
const name = reader.positional(0)?.trim() ?? "";
|
|
24
|
+
if (name === "")
|
|
25
|
+
throw new UsageError("net.dns.name-missing");
|
|
26
|
+
return { name, type: reader.choice("type", LOOKUP_TYPES, "system") };
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const FAILURE_KEYS = {
|
|
31
|
+
"not-found": "net.dns.not-found",
|
|
32
|
+
timeout: "net.dns.timeout",
|
|
33
|
+
failed: "net.dns.failed",
|
|
34
|
+
};
|
|
35
|
+
export class LookupName {
|
|
36
|
+
network;
|
|
37
|
+
spec = dnsSpec;
|
|
38
|
+
constructor(network) {
|
|
39
|
+
this.network = network;
|
|
40
|
+
}
|
|
41
|
+
async execute(input) {
|
|
42
|
+
const outcome = input.type === "system"
|
|
43
|
+
? await this.network.lookup(input.name)
|
|
44
|
+
: await this.network.resolve(input.name, input.type, DNS_TIMEOUT_MS);
|
|
45
|
+
if (outcome.ok)
|
|
46
|
+
return done({ name: input.name, type: input.type, records: outcome.records, failure: null });
|
|
47
|
+
const params = {
|
|
48
|
+
name: input.name,
|
|
49
|
+
type: input.type === "system" ? "address" : input.type.toUpperCase(),
|
|
50
|
+
code: outcome.code ?? outcome.failure,
|
|
51
|
+
};
|
|
52
|
+
return done({ name: input.name, type: input.type, records: [], failure: outcome.failure }, { failures: [message(FAILURE_KEYS[outcome.failure], params)] });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=lookup-name.use-case.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CheckConnection } from "./application/check-connection.use-case.js";
|
|
2
|
+
import { ListAddresses } from "./application/list-addresses.use-case.js";
|
|
3
|
+
import { LookupName } from "./application/lookup-name.use-case.js";
|
|
4
|
+
import { addressesView, checkView, dnsView } from "./presentation/net.views.js";
|
|
5
|
+
export const netModule = {
|
|
6
|
+
id: "net",
|
|
7
|
+
summary: "net.summary",
|
|
8
|
+
about: "net.about",
|
|
9
|
+
examples: ["kiriya net ip", "kiriya net check localhost:5432", "kiriya net dns example.com --type mx"],
|
|
10
|
+
guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/net.md",
|
|
11
|
+
register(registrar, ports) {
|
|
12
|
+
registrar.add(new ListAddresses(ports.network), addressesView);
|
|
13
|
+
registrar.add(new CheckConnection(ports.network), checkView);
|
|
14
|
+
registrar.add(new LookupName(ports.network), dnsView);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=net.module.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { message } from "../../../core/domain/message.js";
|
|
2
|
+
import { formatTarget } from "../../../core/domain/targets.js";
|
|
3
|
+
const NO_MAC = "00:00:00:00:00:00";
|
|
4
|
+
/** Addresses under the interface they belong to. */
|
|
5
|
+
export const addressesView = (output, format) => {
|
|
6
|
+
if (output.addresses.length === 0)
|
|
7
|
+
return [format.dim(format.text(message("net.ip.none")))];
|
|
8
|
+
const macLabel = format.text(message("net.ip.mac"));
|
|
9
|
+
const width = Math.max(4, macLabel.length);
|
|
10
|
+
const lines = [];
|
|
11
|
+
for (const name of new Set(output.addresses.map((address) => address.interfaceName))) {
|
|
12
|
+
const entries = output.addresses.filter((address) => address.interfaceName === name);
|
|
13
|
+
lines.push(format.bold(name));
|
|
14
|
+
for (const entry of entries)
|
|
15
|
+
lines.push(` ${entry.family.padEnd(width)} ${entry.cidr ?? entry.address}`);
|
|
16
|
+
const mac = entries[0]?.mac;
|
|
17
|
+
if (mac !== undefined && mac !== NO_MAC)
|
|
18
|
+
lines.push(format.dim(` ${macLabel.padEnd(width)} ${mac}`));
|
|
19
|
+
}
|
|
20
|
+
return lines;
|
|
21
|
+
};
|
|
22
|
+
/** An unreachable target arrives as a failure. */
|
|
23
|
+
export const checkView = (output, format) => output.reachable
|
|
24
|
+
? [
|
|
25
|
+
format.green(format.text(message("net.check.reachable", {
|
|
26
|
+
target: formatTarget(output),
|
|
27
|
+
ms: output.ms,
|
|
28
|
+
address: output.address ?? output.host,
|
|
29
|
+
}))),
|
|
30
|
+
]
|
|
31
|
+
: [];
|
|
32
|
+
export const dnsView = (output) => {
|
|
33
|
+
const width = Math.max(0, ...output.records.map((record) => record.type.length));
|
|
34
|
+
return output.records.map((record) => {
|
|
35
|
+
const priority = record.priority === null ? "" : `${record.priority} `;
|
|
36
|
+
return ` ${record.type.padEnd(width)} ${priority}${record.value}`;
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=net.views.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { done } from "../../../core/domain/command.js";
|
|
3
|
+
import { NotFoundError, RefusedError } from "../../../core/domain/errors.js";
|
|
4
|
+
import { RawReader } from "../../../core/domain/input-schema.js";
|
|
5
|
+
import { classifyTarget, runsWhenOpened } from "../domain/targets.js";
|
|
6
|
+
export const openSpec = {
|
|
7
|
+
id: "open",
|
|
8
|
+
summary: "open.summary",
|
|
9
|
+
examples: ["kiriya open .", "kiriya open report.pdf", "kiriya open https://example.com/docs"],
|
|
10
|
+
safety: "read",
|
|
11
|
+
idempotent: false,
|
|
12
|
+
usesNetwork: false,
|
|
13
|
+
runsUserCommands: false,
|
|
14
|
+
// A web address can carry whatever an agent puts in it to any site.
|
|
15
|
+
sensitive: true,
|
|
16
|
+
input: {
|
|
17
|
+
positionals: [{ name: "target", description: "open.arg.target", required: true, variadic: false, path: true }],
|
|
18
|
+
options: {},
|
|
19
|
+
parse: (raw) => ({ target: new RawReader(raw).positional(0) ?? "" }),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
/** Opening never runs a program: other schemes and files that would run as programs are refused. */
|
|
23
|
+
export class OpenTarget {
|
|
24
|
+
opener;
|
|
25
|
+
fileSystem;
|
|
26
|
+
environment;
|
|
27
|
+
spec = openSpec;
|
|
28
|
+
constructor(opener, fileSystem, environment) {
|
|
29
|
+
this.opener = opener;
|
|
30
|
+
this.fileSystem = fileSystem;
|
|
31
|
+
this.environment = environment;
|
|
32
|
+
}
|
|
33
|
+
async execute(input, context) {
|
|
34
|
+
const target = classifyTarget(input.target);
|
|
35
|
+
if (target.kind === "refused")
|
|
36
|
+
throw new RefusedError("open.scheme-refused", { scheme: target.scheme });
|
|
37
|
+
if (target.kind === "url") {
|
|
38
|
+
await this.opener.open(target.url, context.signal);
|
|
39
|
+
return done({ target: target.url, kind: "url" });
|
|
40
|
+
}
|
|
41
|
+
// An absolute path never starts with a dash, so no opener mistakes it for an option.
|
|
42
|
+
const absolute = path.resolve(context.cwd, target.path);
|
|
43
|
+
const stat = await this.fileSystem.stat(absolute);
|
|
44
|
+
if (stat === null)
|
|
45
|
+
throw new NotFoundError("core.fs.not-found", { path: absolute });
|
|
46
|
+
if (runsWhenOpened(path.basename(absolute), this.environment.os)) {
|
|
47
|
+
throw new RefusedError("open.runs-program", { path: absolute });
|
|
48
|
+
}
|
|
49
|
+
await this.opener.open(absolute, context.signal);
|
|
50
|
+
return done({ target: absolute, kind: stat.kind === "directory" ? "folder" : "file" });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=open-target.use-case.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Web and mail addresses; every other scheme can start programs or reach places a file name should not. */
|
|
2
|
+
export const OPENABLE_SCHEMES = ["http", "https", "mailto"];
|
|
3
|
+
/** A scheme at the start makes an address; a Windows drive such as C:\ does not. */
|
|
4
|
+
export function classifyTarget(text) {
|
|
5
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(text)?.[1];
|
|
6
|
+
if (scheme === undefined || /^[a-z]:([\\/]|$)/i.test(text))
|
|
7
|
+
return { kind: "path", path: text };
|
|
8
|
+
const lower = scheme.toLowerCase();
|
|
9
|
+
return OPENABLE_SCHEMES.includes(lower) ? { kind: "url", url: text } : { kind: "refused", scheme: lower };
|
|
10
|
+
}
|
|
11
|
+
/** Names that each OS runs as a program, or as a script or installer, when they are opened. */
|
|
12
|
+
const RUNS_WHEN_OPENED = {
|
|
13
|
+
windows: [
|
|
14
|
+
".exe",
|
|
15
|
+
".com",
|
|
16
|
+
".bat",
|
|
17
|
+
".cmd",
|
|
18
|
+
".ps1",
|
|
19
|
+
".psm1",
|
|
20
|
+
".vbs",
|
|
21
|
+
".vbe",
|
|
22
|
+
".js",
|
|
23
|
+
".jse",
|
|
24
|
+
".wsf",
|
|
25
|
+
".wsh",
|
|
26
|
+
".msi",
|
|
27
|
+
".msc",
|
|
28
|
+
".scr",
|
|
29
|
+
".lnk",
|
|
30
|
+
".url",
|
|
31
|
+
".hta",
|
|
32
|
+
".cpl",
|
|
33
|
+
".reg",
|
|
34
|
+
".jar",
|
|
35
|
+
".pif",
|
|
36
|
+
".appref-ms",
|
|
37
|
+
],
|
|
38
|
+
macos: [".app", ".command", ".tool", ".terminal", ".pkg", ".workflow", ".jar"],
|
|
39
|
+
linux: [".desktop", ".appimage", ".jar"],
|
|
40
|
+
};
|
|
41
|
+
export function runsWhenOpened(name, os) {
|
|
42
|
+
const lower = name.toLowerCase();
|
|
43
|
+
return RUNS_WHEN_OPENED[os].some((extension) => lower.endsWith(extension));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { OpenTarget } from "./application/open-target.use-case.js";
|
|
2
|
+
import { openView } from "./presentation/open.view.js";
|
|
3
|
+
export const openModule = {
|
|
4
|
+
id: "open",
|
|
5
|
+
summary: "open.summary",
|
|
6
|
+
about: "open.about",
|
|
7
|
+
guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/open.md",
|
|
8
|
+
register(registrar, ports) {
|
|
9
|
+
registrar.add(new OpenTarget(ports.opener, ports.fileSystem, ports.environment), openView);
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=open.module.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { message } from "../../../core/domain/message.js";
|
|
2
|
+
export const openView = (output, format) => {
|
|
3
|
+
const target = output.kind === "url" ? output.target : format.path(output.target);
|
|
4
|
+
return [format.dim(format.text(message("open.done", { target })))];
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=open.view.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { processLabels, reportEnded } from "../../../core/application/process-ending.js";
|
|
2
|
+
import { requireTypedConfirmation } from "../../../core/application/safety.js";
|
|
3
|
+
import { done } from "../../../core/domain/command.js";
|
|
4
|
+
import { NotFoundError, RefusedError } from "../../../core/domain/errors.js";
|
|
5
|
+
import { RawReader } from "../../../core/domain/input-schema.js";
|
|
6
|
+
import { message } from "../../../core/domain/message.js";
|
|
7
|
+
import { ownedListeners, readPort } from "./port-owners.js";
|
|
8
|
+
export const killSpec = {
|
|
9
|
+
id: "port.kill",
|
|
10
|
+
summary: "port.kill.summary",
|
|
11
|
+
examples: ["kiriya port kill 3000", "kiriya port kill 3000 --force --confirm=3000"],
|
|
12
|
+
safety: "destroy",
|
|
13
|
+
idempotent: false,
|
|
14
|
+
usesNetwork: false,
|
|
15
|
+
runsUserCommands: false,
|
|
16
|
+
input: {
|
|
17
|
+
positionals: [{ name: "port", description: "port.kill.arg.port", required: true, variadic: false }],
|
|
18
|
+
options: {
|
|
19
|
+
force: { type: "boolean", description: "port.kill.option.force" },
|
|
20
|
+
confirm: { type: "string", description: "port.kill.option.confirm", valueName: "<port>", terminalOnly: true },
|
|
21
|
+
},
|
|
22
|
+
parse(raw) {
|
|
23
|
+
const reader = new RawReader(raw);
|
|
24
|
+
return {
|
|
25
|
+
port: readPort(reader.positional(0) ?? ""),
|
|
26
|
+
force: reader.flag("force"),
|
|
27
|
+
confirm: reader.string("confirm"),
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export class EndPortOwners {
|
|
33
|
+
ports;
|
|
34
|
+
processes;
|
|
35
|
+
spec = killSpec;
|
|
36
|
+
constructor(ports, processes) {
|
|
37
|
+
this.ports = ports;
|
|
38
|
+
this.processes = processes;
|
|
39
|
+
}
|
|
40
|
+
async execute(input, context) {
|
|
41
|
+
const { port } = input;
|
|
42
|
+
const listeners = await ownedListeners(this.ports, this.processes, port, context.signal);
|
|
43
|
+
if (listeners.length === 0)
|
|
44
|
+
throw new NotFoundError("port.kill.nothing", { port });
|
|
45
|
+
const pids = [...new Set(listeners.flatMap((listener) => (listener.pid === null ? [] : [listener.pid])))];
|
|
46
|
+
// kiriya never elevates, so another user's listener stays out of reach.
|
|
47
|
+
if (pids.length === 0)
|
|
48
|
+
throw new RefusedError("port.kill.hidden", { port });
|
|
49
|
+
const names = new Map();
|
|
50
|
+
for (const listener of listeners) {
|
|
51
|
+
if (listener.pid !== null && listener.name !== null)
|
|
52
|
+
names.set(listener.pid, listener.name);
|
|
53
|
+
}
|
|
54
|
+
const guarded = this.processes.protectedPids();
|
|
55
|
+
const refused = pids.find((pid) => guarded.has(pid));
|
|
56
|
+
if (refused !== undefined) {
|
|
57
|
+
throw new RefusedError("port.kill.protected", { name: names.get(refused) ?? "?", pid: refused });
|
|
58
|
+
}
|
|
59
|
+
const warning = message("port.kill.warn", { port, processes: processLabels(pids, names) });
|
|
60
|
+
await requireTypedConfirmation(context, warning, String(port), input.confirm);
|
|
61
|
+
const report = reportEnded(await this.processes.end(pids, input.force), names);
|
|
62
|
+
const hidden = listeners.some((listener) => listener.pid === null)
|
|
63
|
+
? [message("port.kill.some-hidden", { port })]
|
|
64
|
+
: [];
|
|
65
|
+
return done({ port, processes: report.processes }, { failures: report.failures, warnings: [...hidden, ...report.warnings] });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=end-port-owners.use-case.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { done } from "../../../core/domain/command.js";
|
|
2
|
+
import { OperationFailedError } from "../../../core/domain/errors.js";
|
|
3
|
+
import { RawReader } from "../../../core/domain/input-schema.js";
|
|
4
|
+
import { readPort } from "./port-owners.js";
|
|
5
|
+
const DEFAULT_FROM = 3000;
|
|
6
|
+
const LAST_PORT = 65_535;
|
|
7
|
+
export const freeSpec = {
|
|
8
|
+
id: "port.free",
|
|
9
|
+
summary: "port.free.summary",
|
|
10
|
+
examples: ["kiriya port free", "kiriya port free --from 8080", "PORT=$(kiriya port free)"],
|
|
11
|
+
safety: "read",
|
|
12
|
+
idempotent: false,
|
|
13
|
+
usesNetwork: false,
|
|
14
|
+
runsUserCommands: false,
|
|
15
|
+
input: {
|
|
16
|
+
positionals: [],
|
|
17
|
+
options: { from: { type: "string", description: "port.free.option.from", valueName: "<port>" } },
|
|
18
|
+
parse(raw) {
|
|
19
|
+
const from = new RawReader(raw).string("from");
|
|
20
|
+
return { from: from === undefined ? DEFAULT_FROM : readPort(from) };
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export class FindFreePort {
|
|
25
|
+
ports;
|
|
26
|
+
spec = freeSpec;
|
|
27
|
+
constructor(ports) {
|
|
28
|
+
this.ports = ports;
|
|
29
|
+
}
|
|
30
|
+
async execute(input, context) {
|
|
31
|
+
const taken = new Set((await this.ports.listeners(context.signal)).map((listener) => listener.port));
|
|
32
|
+
for (let port = input.from; port <= LAST_PORT; port += 1) {
|
|
33
|
+
// The listener table catches a port held on one address that a test bind could still share on some systems.
|
|
34
|
+
if (!taken.has(port) && (await this.ports.canListen(port)))
|
|
35
|
+
return done({ port });
|
|
36
|
+
}
|
|
37
|
+
throw new OperationFailedError("port.free.none", { from: input.from });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=find-free-port.use-case.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { done } from "../../../core/domain/command.js";
|
|
2
|
+
import { RawReader } from "../../../core/domain/input-schema.js";
|
|
3
|
+
import { ownedListeners, readPort } from "./port-owners.js";
|
|
4
|
+
export const whoSpec = {
|
|
5
|
+
id: "port.who",
|
|
6
|
+
summary: "port.who.summary",
|
|
7
|
+
examples: ["kiriya port who 3000", "kiriya port who", "kiriya port who 5432 --json"],
|
|
8
|
+
safety: "read",
|
|
9
|
+
idempotent: true,
|
|
10
|
+
usesNetwork: false,
|
|
11
|
+
runsUserCommands: false,
|
|
12
|
+
input: {
|
|
13
|
+
positionals: [{ name: "port", description: "port.who.arg.port", required: false, variadic: false }],
|
|
14
|
+
options: {},
|
|
15
|
+
parse(raw) {
|
|
16
|
+
const text = new RawReader(raw).positional(0);
|
|
17
|
+
return { port: text === undefined ? null : readPort(text) };
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export class FindListeners {
|
|
22
|
+
ports;
|
|
23
|
+
processes;
|
|
24
|
+
spec = whoSpec;
|
|
25
|
+
constructor(ports, processes) {
|
|
26
|
+
this.ports = ports;
|
|
27
|
+
this.processes = processes;
|
|
28
|
+
}
|
|
29
|
+
async execute(input, context) {
|
|
30
|
+
const listeners = await ownedListeners(this.ports, this.processes, input.port, context.signal);
|
|
31
|
+
return done({ port: input.port, listeners });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=find-listeners.use-case.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { UsageError } from "../../../core/domain/errors.js";
|
|
2
|
+
import { byCodePoint } from "../../../core/domain/names.js";
|
|
3
|
+
import { parsePort } from "../domain/port-number.js";
|
|
4
|
+
export function readPort(text) {
|
|
5
|
+
const port = parsePort(text);
|
|
6
|
+
if (port === null)
|
|
7
|
+
throw new UsageError("port.invalid", { port: text });
|
|
8
|
+
return port;
|
|
9
|
+
}
|
|
10
|
+
/** Listeners on one port, or on all when `port` is null, with their owners' names, sorted by port, address and id. */
|
|
11
|
+
export async function ownedListeners(ports, processes, port, signal) {
|
|
12
|
+
const listeners = (await ports.listeners(signal)).filter((listener) => port === null || listener.port === port);
|
|
13
|
+
const names = new Map();
|
|
14
|
+
if (listeners.some((listener) => listener.pid !== null)) {
|
|
15
|
+
for (const entry of (await processes.list(false, signal)).processes)
|
|
16
|
+
names.set(entry.pid, entry.name);
|
|
17
|
+
}
|
|
18
|
+
const unique = new Map();
|
|
19
|
+
for (const listener of listeners) {
|
|
20
|
+
const name = listener.pid === null ? null : (names.get(listener.pid) ?? null);
|
|
21
|
+
unique.set(`${listener.port}|${listener.address}|${listener.pid ?? ""}`, { ...listener, name });
|
|
22
|
+
}
|
|
23
|
+
return [...unique.values()].sort((first, second) => first.port - second.port || byCodePoint(first.address, second.address) || (first.pid ?? -1) - (second.pid ?? -1));
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=port-owners.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** A TCP port from 1 to 65535 written in digits, or null. */
|
|
2
|
+
export function parsePort(text) {
|
|
3
|
+
if (!/^\d{1,5}$/.test(text.trim()))
|
|
4
|
+
return null;
|
|
5
|
+
const port = Number(text.trim());
|
|
6
|
+
return port >= 1 && port <= 65_535 ? port : null;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=port-number.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EndPortOwners } from "./application/end-port-owners.use-case.js";
|
|
2
|
+
import { FindFreePort } from "./application/find-free-port.use-case.js";
|
|
3
|
+
import { FindListeners } from "./application/find-listeners.use-case.js";
|
|
4
|
+
import { freeView, killView, whoView } from "./presentation/port.views.js";
|
|
5
|
+
export const portModule = {
|
|
6
|
+
id: "port",
|
|
7
|
+
summary: "port.summary",
|
|
8
|
+
about: "port.about",
|
|
9
|
+
examples: ["kiriya port who 3000", "kiriya port kill 3000", "kiriya port free --from 8000"],
|
|
10
|
+
guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/port.md",
|
|
11
|
+
register(registrar, ports) {
|
|
12
|
+
registrar.add(new FindListeners(ports.portTable, ports.processTable), whoView);
|
|
13
|
+
registrar.add(new EndPortOwners(ports.portTable, ports.processTable), killView);
|
|
14
|
+
registrar.add(new FindFreePort(ports.portTable), freeView);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=port.module.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { message } from "../../../core/domain/message.js";
|
|
2
|
+
import { columns, endedLines } from "../../../core/presentation/ended-processes.js";
|
|
3
|
+
export const whoView = (output, format) => {
|
|
4
|
+
if (output.listeners.length === 0) {
|
|
5
|
+
const empty = output.port === null ? message("port.who.none-at-all") : message("port.who.none", { port: output.port });
|
|
6
|
+
return [format.dim(format.text(empty))];
|
|
7
|
+
}
|
|
8
|
+
const hidden = format.text(message("port.who.hidden"));
|
|
9
|
+
const header = [
|
|
10
|
+
format.text(message("port.column.port")),
|
|
11
|
+
format.text(message("port.column.address")),
|
|
12
|
+
format.text(message("port.column.pid")),
|
|
13
|
+
format.text(message("port.column.process")),
|
|
14
|
+
];
|
|
15
|
+
const rows = output.listeners.map((listener) => [
|
|
16
|
+
String(listener.port),
|
|
17
|
+
listener.address,
|
|
18
|
+
listener.pid === null ? "" : String(listener.pid),
|
|
19
|
+
listener.name ?? (listener.pid === null ? hidden : ""),
|
|
20
|
+
]);
|
|
21
|
+
const [first = "", ...rest] = columns([header, ...rows]);
|
|
22
|
+
return [format.bold(first), ...rest];
|
|
23
|
+
};
|
|
24
|
+
export const killView = (output, format) => endedLines(output.processes, format);
|
|
25
|
+
/** The number alone, so `$(kiriya port free)` works in a script. */
|
|
26
|
+
export const freeView = (output) => [String(output.port)];
|
|
27
|
+
//# sourceMappingURL=port.views.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { processLabels, reportEnded } from "../../../core/application/process-ending.js";
|
|
2
|
+
import { requireTypedConfirmation } from "../../../core/application/safety.js";
|
|
3
|
+
import { done } from "../../../core/domain/command.js";
|
|
4
|
+
import { NotFoundError, RefusedError, UsageError } from "../../../core/domain/errors.js";
|
|
5
|
+
import { RawReader } from "../../../core/domain/input-schema.js";
|
|
6
|
+
import { message } from "../../../core/domain/message.js";
|
|
7
|
+
import { programKey } from "../domain/process-tree.js";
|
|
8
|
+
export const killSpec = {
|
|
9
|
+
id: "proc.kill",
|
|
10
|
+
summary: "proc.kill.summary",
|
|
11
|
+
examples: ["kiriya proc kill 4100", "kiriya proc kill node", "kiriya proc kill node --force --confirm=node"],
|
|
12
|
+
safety: "destroy",
|
|
13
|
+
idempotent: false,
|
|
14
|
+
usesNetwork: false,
|
|
15
|
+
runsUserCommands: false,
|
|
16
|
+
input: {
|
|
17
|
+
positionals: [{ name: "target", description: "proc.kill.arg.target", required: true, variadic: false }],
|
|
18
|
+
options: {
|
|
19
|
+
force: { type: "boolean", description: "proc.kill.option.force" },
|
|
20
|
+
confirm: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "proc.kill.option.confirm",
|
|
23
|
+
valueName: "<id-or-name>",
|
|
24
|
+
terminalOnly: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
parse(raw) {
|
|
28
|
+
const reader = new RawReader(raw);
|
|
29
|
+
const target = reader.positional(0)?.trim() ?? "";
|
|
30
|
+
if (target === "")
|
|
31
|
+
throw new UsageError("proc.kill.target-missing");
|
|
32
|
+
return { target, force: reader.flag("force"), confirm: reader.string("confirm") };
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export class EndProcesses {
|
|
37
|
+
table;
|
|
38
|
+
spec = killSpec;
|
|
39
|
+
constructor(table) {
|
|
40
|
+
this.table = table;
|
|
41
|
+
}
|
|
42
|
+
async execute(input, context) {
|
|
43
|
+
const { processes } = await this.table.list(false, context.signal);
|
|
44
|
+
const guarded = this.table.protectedPids();
|
|
45
|
+
const warnings = [];
|
|
46
|
+
let targets;
|
|
47
|
+
if (/^\d+$/.test(input.target)) {
|
|
48
|
+
const pid = Number(input.target);
|
|
49
|
+
const found = processes.find((entry) => entry.pid === pid);
|
|
50
|
+
if (found === undefined)
|
|
51
|
+
throw new NotFoundError("proc.kill.no-pid", { pid });
|
|
52
|
+
if (guarded.has(pid))
|
|
53
|
+
throw new RefusedError("proc.kill.protected", { name: found.name, pid });
|
|
54
|
+
targets = [found];
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const key = programKey(input.target);
|
|
58
|
+
const matches = processes.filter((entry) => programKey(entry.name) === key);
|
|
59
|
+
if (matches.length === 0)
|
|
60
|
+
throw new NotFoundError("proc.kill.no-name", { name: input.target });
|
|
61
|
+
// Ending every node would end kiriya itself, and perhaps the program that started it.
|
|
62
|
+
targets = matches.filter((entry) => !guarded.has(entry.pid));
|
|
63
|
+
if (targets.length === 0)
|
|
64
|
+
throw new RefusedError("proc.kill.only-protected", { name: input.target });
|
|
65
|
+
const skipped = matches.length - targets.length;
|
|
66
|
+
if (skipped > 0)
|
|
67
|
+
warnings.push(message("proc.kill.skipped", { count: skipped }));
|
|
68
|
+
}
|
|
69
|
+
const names = new Map(targets.map((entry) => [entry.pid, entry.name]));
|
|
70
|
+
const pids = targets.map((entry) => entry.pid).sort((first, second) => first - second);
|
|
71
|
+
const warning = message("proc.kill.warn", { processes: processLabels(pids, names) });
|
|
72
|
+
await requireTypedConfirmation(context, warning, input.target, input.confirm);
|
|
73
|
+
const report = reportEnded(await this.table.end(pids, input.force), names);
|
|
74
|
+
return done({ target: input.target, processes: report.processes }, { failures: report.failures, warnings: [...warnings, ...report.warnings] });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=end-processes.use-case.js.map
|