kamakiri 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexis Tabary
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,28 @@
1
+ # kamakiri
2
+
3
+ `kamakiri` is the command-line client for Kamakiri Pages, a static-site hosting
4
+ service. You build your site locally, deploy the output directory, and it goes
5
+ live over HTTPS on a `kamakiri-pages.jp` subdomain or on a domain of your own.
6
+
7
+ ```sh
8
+ npm install -g kamakiri
9
+ kamakiri --help
10
+ ```
11
+
12
+ `npx kamakiri@latest` runs it without installing anything, and
13
+ `npm install -g kamakiri@latest` is how an installed copy is updated.
14
+
15
+ This package is a small launcher. The CLI itself is a single static Go binary,
16
+ published one per platform as `@kamakiri-labs/cli-<platform>-<arch>`; installing
17
+ `kamakiri` brings in the one package that matches your machine, as an optional
18
+ dependency npm skips everywhere else. The `kamakiri` command resolves that
19
+ binary and runs it, passing your arguments, the standard streams and the exit
20
+ code straight through.
21
+
22
+ Kamakiri Pages is an invitation-only private beta today, so `kamakiri login`
23
+ only succeeds for an email that has been invited. To request an invitation,
24
+ email beta@kamakiri-labs.jp.
25
+
26
+ The full documentation, the other ways to install the CLI, and the source are
27
+ at [github.com/kamakiri-labs/kamakiri](https://github.com/kamakiri-labs/kamakiri).
28
+ The CLI is MIT licensed.
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+ // The command npm installs as `kamakiri`. It resolves the platform package
3
+ // carrying this machine's binary and runs it, passing everything through: the
4
+ // arguments as given, the three standard streams, the exit code, and the
5
+ // signal a run died of. An interrupt is the binary's to answer, so this sits
6
+ // out SIGINT, SIGTERM and SIGHUP for as long as the binary is running.
7
+ //
8
+ // The shebang is what runs this on macOS and Linux, where npm links the file
9
+ // itself. Windows gets generated .cmd and .ps1 shims that invoke node
10
+ // explicitly and never read the line.
11
+ //
12
+ // It downloads nothing and runs no install script. The binary is already on
13
+ // disk when this runs, because npm installed the platform package as an
14
+ // optional dependency whose os/cpu fields matched this machine.
15
+ "use strict";
16
+
17
+ const { spawnSync } = require("child_process");
18
+
19
+ // The six platforms a release publishes a binary for, spelled the way
20
+ // process.platform and process.arch spell them. The packaging side writes the
21
+ // same six names on the packages themselves, so this list and that one are the
22
+ // two halves of one string and have to agree.
23
+ const SUPPORTED = [
24
+ "darwin-arm64",
25
+ "darwin-x64",
26
+ "linux-arm64",
27
+ "linux-x64",
28
+ "win32-arm64",
29
+ "win32-x64",
30
+ ];
31
+
32
+ const RELEASES_URL = "https://github.com/kamakiri-labs/kamakiri/releases";
33
+ const INSTALL_SCRIPT_URL = "https://get.kamakiri-labs.jp/install.sh";
34
+
35
+ // The three signals this stands aside for while the binary runs. A Ctrl-C and
36
+ // a hangup are aimed at the whole foreground job, so each reaches this process
37
+ // alongside the binary it started. SIGTERM is here because the CLI answers that
38
+ // one itself and prints how to pick an interrupted run back up, and this
39
+ // process dying on it first is what would cut that short.
40
+ //
41
+ // The set stops there on purpose. SIGQUIT stays the way out of a binary that
42
+ // traps the others and will not stop, and a listener on SIGTSTP that does
43
+ // nothing would swallow a Ctrl-Z instead of suspending the job.
44
+ const JOB_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
45
+
46
+ // Where this machine's binary lives, or null for a platform no release covers.
47
+ // A pure function of the pair rather than a read of process, so the two win32
48
+ // answers can be checked from any machine: every end-to-end run of this file
49
+ // happens on the host's own platform, and the .exe half of the split would
50
+ // otherwise first be exercised on a user's Windows box.
51
+ function platformTarget(platform, arch) {
52
+ const pair = platform + "-" + arch;
53
+ if (SUPPORTED.indexOf(pair) === -1) {
54
+ return null;
55
+ }
56
+ return {
57
+ packageName: "@kamakiri-labs/cli-" + pair,
58
+ // The packaging side appends the same .exe to the two win32 packages. A
59
+ // shim that forgot it would report a package that is present and correct
60
+ // as missing, and only on Windows.
61
+ binarySubpath: platform === "win32" ? "bin/kamakiri.exe" : "bin/kamakiri",
62
+ };
63
+ }
64
+
65
+ function say(lines) {
66
+ process.stderr.write(lines.join("\n") + "\n");
67
+ }
68
+
69
+ function main(args) {
70
+ const target = platformTarget(process.platform, process.arch);
71
+ if (target === null) {
72
+ say([
73
+ "kamakiri: there is no kamakiri binary for " + process.platform + "-" + process.arch + ".",
74
+ "The platforms a release covers are listed on the releases page, " + RELEASES_URL + ".",
75
+ "The install script at " + INSTALL_SCRIPT_URL + " installs from that same set, so it",
76
+ "has nothing for this machine either.",
77
+ ]);
78
+ return 1;
79
+ }
80
+
81
+ const request = target.packageName + "/" + target.binarySubpath;
82
+ let binary;
83
+ try {
84
+ // The subpath is resolved rather than joined onto a guessed directory, so
85
+ // the binary is found wherever the install put the platform package: under
86
+ // the launcher, hoisted to the top of a project's node_modules, or in a
87
+ // store a package manager links from.
88
+ binary = require.resolve(request);
89
+ } catch {
90
+ say([
91
+ "kamakiri: the platform package " + target.packageName + " is not installed,",
92
+ "so there is no binary to run. Optional dependencies are the likely reason: an install",
93
+ "run with --no-optional, or with optional dependencies omitted in an npm configuration,",
94
+ "skips it. Installing again with them enabled brings it in, with",
95
+ "npm install -g kamakiri@latest for a global install, or npm install in the project that",
96
+ "depends on kamakiri.",
97
+ ]);
98
+ return 1;
99
+ }
100
+
101
+ // Ignored for as long as the binary is running, so the interrupt is the
102
+ // binary's to answer. Node kills a process outright on any of these when
103
+ // nothing is listening, and a Ctrl-C reaches this process at the same moment
104
+ // it reaches the binary, so without this the shim would die first and hand
105
+ // the shell its prompt back over whatever the CLI was still printing about
106
+ // the run it was interrupted in the middle of. The listener does nothing on
107
+ // purpose: it exists to displace that default, not to act.
108
+ const ignoreWhileRunning = () => {};
109
+ for (const signal of JOB_SIGNALS) {
110
+ process.on(signal, ignoreWhileRunning);
111
+ }
112
+
113
+ const result = spawnSync(binary, args, { stdio: "inherit" });
114
+
115
+ // Dropped again the moment the binary is gone, which puts the default back.
116
+ // The re-raise below counts on that: with the listener still registered it
117
+ // would run the listener instead, and this process would survive a death it
118
+ // is supposed to be passing on.
119
+ for (const signal of JOB_SIGNALS) {
120
+ process.removeListener(signal, ignoreWhileRunning);
121
+ }
122
+
123
+ // spawnSync answers in one of three ways, and each one means something
124
+ // different to whoever ran the command.
125
+ if (result.error) {
126
+ // The binary was never started, so there is no exit code to pass on and
127
+ // reporting one would say the CLI ran and failed.
128
+ say([
129
+ "kamakiri: the binary at " + binary + " could not be started (" +
130
+ (result.error.code || result.error.message) + ").",
131
+ "Installing the package again replaces it, with npm install -g kamakiri@latest for a",
132
+ "global install, or npm install in the project that depends on kamakiri.",
133
+ ]);
134
+ return 1;
135
+ }
136
+ if (result.signal) {
137
+ // Re-raised on this process rather than turned into a number, so a caller
138
+ // sees the same death the binary died of: a command killed by a signal has
139
+ // to look killed to the shell above it, and a number in that position
140
+ // would read as a run that finished and failed. Windows reports no signal
141
+ // at all, so nothing there reaches this and the status below is what a
142
+ // killed child comes back as.
143
+ process.kill(process.pid, result.signal);
144
+ }
145
+ if (typeof result.status === "number") {
146
+ return result.status;
147
+ }
148
+ // Reached only if the re-raised signal did not end this process, or if
149
+ // spawnSync answered in none of its three ways. Neither is a run that
150
+ // finished, so neither reports success.
151
+ return 1;
152
+ }
153
+
154
+ // Guarded so that requiring this file yields its functions without running a
155
+ // command, which is what lets the platform-pair choice be checked directly.
156
+ //
157
+ // The code is set rather than exited on. A write to stderr that lands on a pipe
158
+ // is asynchronous, and process.exit ends the process with whatever is still
159
+ // queued unwritten; these messages are short enough to fit the pipe buffer and
160
+ // would come through either way, so setting the code is the spelling that does
161
+ // not rest on their length. Nothing else is pending once spawnSync has
162
+ // returned, so the process ends on this line regardless.
163
+ if (require.main === module) {
164
+ process.exitCode = main(process.argv.slice(2));
165
+ }
166
+
167
+ module.exports = { platformTarget };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "kamakiri",
3
+ "version": "0.1.0",
4
+ "description": "The kamakiri CLI, the command-line client for Kamakiri Pages",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/kamakiri-labs/kamakiri.git"
9
+ },
10
+ "engines": {
11
+ "node": ">=18"
12
+ },
13
+ "bin": {
14
+ "kamakiri": "bin/kamakiri.js"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "optionalDependencies": {
20
+ "@kamakiri-labs/cli-darwin-x64": "0.1.0",
21
+ "@kamakiri-labs/cli-darwin-arm64": "0.1.0",
22
+ "@kamakiri-labs/cli-linux-x64": "0.1.0",
23
+ "@kamakiri-labs/cli-linux-arm64": "0.1.0",
24
+ "@kamakiri-labs/cli-win32-x64": "0.1.0",
25
+ "@kamakiri-labs/cli-win32-arm64": "0.1.0"
26
+ }
27
+ }