@reegaviljoen/eldlock 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/README.md +285 -0
- package/bin/eldlock +11 -0
- package/docs/architecture.md +164 -0
- package/docs/threat-model.md +47 -0
- package/eldlock-cli/README.md +56 -0
- package/eldlock-cli/bin/eldlock +3 -0
- package/eldlock-cli/package-lock.json +805 -0
- package/eldlock-cli/package.json +71 -0
- package/eldlock-cli/src/api.ts +250 -0
- package/eldlock-cli/src/cli.ts +490 -0
- package/eldlock-cli/src/main.ts +10 -0
- package/eldlock-cli/src/tui.ts +676 -0
- package/eldlock-cli/tsconfig.json +13 -0
- package/eldlock-cli/vendor/npm/ansi-regex-6.2.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/bun-ffi-structs-0.2.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/diff-9.0.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/emoji-regex-10.6.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-darwin-arm64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-darwin-x64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-linux-arm64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-linux-x64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/fsevents-2.3.3.tgz +0 -0
- package/eldlock-cli/vendor/npm/get-east-asian-width-1.6.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/marked-17.0.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-darwin-arm64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-darwin-x64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-linux-arm64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-linux-x64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/string-width-7.2.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/strip-ansi-7.1.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/tsx-4.22.4.tgz +0 -0
- package/eldlock-cli/vendor/npm/types-node-22.19.19.tgz +0 -0
- package/eldlock-cli/vendor/npm/typescript-5.9.3.tgz +0 -0
- package/eldlock-cli/vendor/npm/undici-types-6.21.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/web-tree-sitter-0.25.10.tgz +0 -0
- package/eldlock-cli/vendor/npm/yoga-layout-3.2.1.tgz +0 -0
- package/eldlock-server/cmd/eldlock-server/main.go +132 -0
- package/eldlock-server/go.mod +10 -0
- package/eldlock-server/go.sum +11 -0
- package/eldlock-server/internal/api/README.md +14 -0
- package/eldlock-server/internal/api/core.go +126 -0
- package/eldlock-server/internal/api/exec.go +97 -0
- package/eldlock-server/internal/api/secrets.go +358 -0
- package/eldlock-server/internal/api/server.go +72 -0
- package/eldlock-server/internal/api/service_test.go +416 -0
- package/eldlock-server/internal/api/types.go +48 -0
- package/eldlock-server/internal/api/vault.go +69 -0
- package/eldlock-server/internal/api/vendor.go +44 -0
- package/eldlock-server/internal/libfido2/LICENSE +21 -0
- package/eldlock-server/internal/libfido2/README.md +127 -0
- package/eldlock-server/internal/libfido2/examples_test.go +614 -0
- package/eldlock-server/internal/libfido2/fido2.go +1234 -0
- package/eldlock-server/internal/libfido2/fido2_darwin.go +7 -0
- package/eldlock-server/internal/libfido2/fido2_other.go +9 -0
- package/eldlock-server/internal/libfido2/fido2_test.go +101 -0
- package/eldlock-server/internal/libfido2/go.mod +10 -0
- package/eldlock-server/internal/libfido2/go.sum +16 -0
- package/eldlock-server/internal/libfido2/log.go +87 -0
- package/eldlock-server/internal/store/README.md +7 -0
- package/eldlock-server/internal/store/store.go +434 -0
- package/eldlock-server/internal/store/store_test.go +125 -0
- package/eldlock-server/internal/yubikey/README.md +25 -0
- package/eldlock-server/internal/yubikey/default_fido2.go +7 -0
- package/eldlock-server/internal/yubikey/default_stub.go +7 -0
- package/eldlock-server/internal/yubikey/fido2_disabled.go +9 -0
- package/eldlock-server/internal/yubikey/fido2_libfido2.go +225 -0
- package/eldlock-server/internal/yubikey/fido2_libfido2_test.go +66 -0
- package/eldlock-server/internal/yubikey/passkey.go +139 -0
- package/eldlock-server/internal/yubikey/passkey_test.go +36 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/LICENSE +21 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/README.md +127 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2.go +1234 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2_darwin.go +7 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2_other.go +9 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/log.go +87 -0
- package/eldlock-server/vendor/github.com/pkg/errors/.travis.yml +10 -0
- package/eldlock-server/vendor/github.com/pkg/errors/LICENSE +23 -0
- package/eldlock-server/vendor/github.com/pkg/errors/Makefile +44 -0
- package/eldlock-server/vendor/github.com/pkg/errors/README.md +59 -0
- package/eldlock-server/vendor/github.com/pkg/errors/appveyor.yml +32 -0
- package/eldlock-server/vendor/github.com/pkg/errors/errors.go +288 -0
- package/eldlock-server/vendor/github.com/pkg/errors/go113.go +38 -0
- package/eldlock-server/vendor/github.com/pkg/errors/stack.go +177 -0
- package/eldlock-server/vendor/modules.txt +7 -0
- package/examples/eldlock.toml +17 -0
- package/install.sh +66 -0
- package/package.json +66 -0
- package/scripts/build-production.mjs +177 -0
- package/scripts/postinstall-production.mjs +23 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
package errors
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"io"
|
|
6
|
+
"path"
|
|
7
|
+
"runtime"
|
|
8
|
+
"strconv"
|
|
9
|
+
"strings"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// Frame represents a program counter inside a stack frame.
|
|
13
|
+
// For historical reasons if Frame is interpreted as a uintptr
|
|
14
|
+
// its value represents the program counter + 1.
|
|
15
|
+
type Frame uintptr
|
|
16
|
+
|
|
17
|
+
// pc returns the program counter for this frame;
|
|
18
|
+
// multiple frames may have the same PC value.
|
|
19
|
+
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
|
20
|
+
|
|
21
|
+
// file returns the full path to the file that contains the
|
|
22
|
+
// function for this Frame's pc.
|
|
23
|
+
func (f Frame) file() string {
|
|
24
|
+
fn := runtime.FuncForPC(f.pc())
|
|
25
|
+
if fn == nil {
|
|
26
|
+
return "unknown"
|
|
27
|
+
}
|
|
28
|
+
file, _ := fn.FileLine(f.pc())
|
|
29
|
+
return file
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// line returns the line number of source code of the
|
|
33
|
+
// function for this Frame's pc.
|
|
34
|
+
func (f Frame) line() int {
|
|
35
|
+
fn := runtime.FuncForPC(f.pc())
|
|
36
|
+
if fn == nil {
|
|
37
|
+
return 0
|
|
38
|
+
}
|
|
39
|
+
_, line := fn.FileLine(f.pc())
|
|
40
|
+
return line
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// name returns the name of this function, if known.
|
|
44
|
+
func (f Frame) name() string {
|
|
45
|
+
fn := runtime.FuncForPC(f.pc())
|
|
46
|
+
if fn == nil {
|
|
47
|
+
return "unknown"
|
|
48
|
+
}
|
|
49
|
+
return fn.Name()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Format formats the frame according to the fmt.Formatter interface.
|
|
53
|
+
//
|
|
54
|
+
// %s source file
|
|
55
|
+
// %d source line
|
|
56
|
+
// %n function name
|
|
57
|
+
// %v equivalent to %s:%d
|
|
58
|
+
//
|
|
59
|
+
// Format accepts flags that alter the printing of some verbs, as follows:
|
|
60
|
+
//
|
|
61
|
+
// %+s function name and path of source file relative to the compile time
|
|
62
|
+
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
|
63
|
+
// %+v equivalent to %+s:%d
|
|
64
|
+
func (f Frame) Format(s fmt.State, verb rune) {
|
|
65
|
+
switch verb {
|
|
66
|
+
case 's':
|
|
67
|
+
switch {
|
|
68
|
+
case s.Flag('+'):
|
|
69
|
+
io.WriteString(s, f.name())
|
|
70
|
+
io.WriteString(s, "\n\t")
|
|
71
|
+
io.WriteString(s, f.file())
|
|
72
|
+
default:
|
|
73
|
+
io.WriteString(s, path.Base(f.file()))
|
|
74
|
+
}
|
|
75
|
+
case 'd':
|
|
76
|
+
io.WriteString(s, strconv.Itoa(f.line()))
|
|
77
|
+
case 'n':
|
|
78
|
+
io.WriteString(s, funcname(f.name()))
|
|
79
|
+
case 'v':
|
|
80
|
+
f.Format(s, 's')
|
|
81
|
+
io.WriteString(s, ":")
|
|
82
|
+
f.Format(s, 'd')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// MarshalText formats a stacktrace Frame as a text string. The output is the
|
|
87
|
+
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
|
|
88
|
+
func (f Frame) MarshalText() ([]byte, error) {
|
|
89
|
+
name := f.name()
|
|
90
|
+
if name == "unknown" {
|
|
91
|
+
return []byte(name), nil
|
|
92
|
+
}
|
|
93
|
+
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
|
97
|
+
type StackTrace []Frame
|
|
98
|
+
|
|
99
|
+
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
|
100
|
+
//
|
|
101
|
+
// %s lists source files for each Frame in the stack
|
|
102
|
+
// %v lists the source file and line number for each Frame in the stack
|
|
103
|
+
//
|
|
104
|
+
// Format accepts flags that alter the printing of some verbs, as follows:
|
|
105
|
+
//
|
|
106
|
+
// %+v Prints filename, function, and line number for each Frame in the stack.
|
|
107
|
+
func (st StackTrace) Format(s fmt.State, verb rune) {
|
|
108
|
+
switch verb {
|
|
109
|
+
case 'v':
|
|
110
|
+
switch {
|
|
111
|
+
case s.Flag('+'):
|
|
112
|
+
for _, f := range st {
|
|
113
|
+
io.WriteString(s, "\n")
|
|
114
|
+
f.Format(s, verb)
|
|
115
|
+
}
|
|
116
|
+
case s.Flag('#'):
|
|
117
|
+
fmt.Fprintf(s, "%#v", []Frame(st))
|
|
118
|
+
default:
|
|
119
|
+
st.formatSlice(s, verb)
|
|
120
|
+
}
|
|
121
|
+
case 's':
|
|
122
|
+
st.formatSlice(s, verb)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// formatSlice will format this StackTrace into the given buffer as a slice of
|
|
127
|
+
// Frame, only valid when called with '%s' or '%v'.
|
|
128
|
+
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
|
|
129
|
+
io.WriteString(s, "[")
|
|
130
|
+
for i, f := range st {
|
|
131
|
+
if i > 0 {
|
|
132
|
+
io.WriteString(s, " ")
|
|
133
|
+
}
|
|
134
|
+
f.Format(s, verb)
|
|
135
|
+
}
|
|
136
|
+
io.WriteString(s, "]")
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// stack represents a stack of program counters.
|
|
140
|
+
type stack []uintptr
|
|
141
|
+
|
|
142
|
+
func (s *stack) Format(st fmt.State, verb rune) {
|
|
143
|
+
switch verb {
|
|
144
|
+
case 'v':
|
|
145
|
+
switch {
|
|
146
|
+
case st.Flag('+'):
|
|
147
|
+
for _, pc := range *s {
|
|
148
|
+
f := Frame(pc)
|
|
149
|
+
fmt.Fprintf(st, "\n%+v", f)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
func (s *stack) StackTrace() StackTrace {
|
|
156
|
+
f := make([]Frame, len(*s))
|
|
157
|
+
for i := 0; i < len(f); i++ {
|
|
158
|
+
f[i] = Frame((*s)[i])
|
|
159
|
+
}
|
|
160
|
+
return f
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func callers() *stack {
|
|
164
|
+
const depth = 32
|
|
165
|
+
var pcs [depth]uintptr
|
|
166
|
+
n := runtime.Callers(3, pcs[:])
|
|
167
|
+
var st stack = pcs[0:n]
|
|
168
|
+
return &st
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// funcname removes the path prefix component of a function's name reported by func.Name().
|
|
172
|
+
func funcname(name string) string {
|
|
173
|
+
i := strings.LastIndex(name, "/")
|
|
174
|
+
name = name[i+1:]
|
|
175
|
+
i = strings.Index(name, ".")
|
|
176
|
+
return name[i+1:]
|
|
177
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Example per-project Eldlock config.
|
|
2
|
+
# This file maps normal env var names to secrets stored in the local vault.
|
|
3
|
+
|
|
4
|
+
[env]
|
|
5
|
+
OPENAI_API_KEY = "personal/openai/api_key"
|
|
6
|
+
GITHUB_TOKEN = "personal/github/token"
|
|
7
|
+
DATABASE_URL = "work/project/database_url"
|
|
8
|
+
|
|
9
|
+
[policy]
|
|
10
|
+
approval = "per-session"
|
|
11
|
+
ttl = "15m"
|
|
12
|
+
require_yubikey = true
|
|
13
|
+
|
|
14
|
+
[allow]
|
|
15
|
+
cwd = "."
|
|
16
|
+
processes = ["bin/dev", "rails", "bundle", "npm", "node", "python"]
|
|
17
|
+
|
package/install.sh
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
repo="${ELDLOCK_REPO:-reeganviljoen/eldlock}"
|
|
5
|
+
ref="${ELDLOCK_REF:-main}"
|
|
6
|
+
install_dir="${ELDLOCK_INSTALL_DIR:-$HOME/.local/share/eldlock}"
|
|
7
|
+
bin_dir="${ELDLOCK_BIN_DIR:-$HOME/.local/bin}"
|
|
8
|
+
|
|
9
|
+
need() {
|
|
10
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
11
|
+
echo "eldlock install: missing required command: $1" >&2
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
need curl
|
|
17
|
+
need tar
|
|
18
|
+
need node
|
|
19
|
+
need npm
|
|
20
|
+
if ! command -v go >/dev/null 2>&1 && [ -z "${ELDLOCK_GO_PATH:-}" ]; then
|
|
21
|
+
echo "eldlock install: missing required command: go" >&2
|
|
22
|
+
echo "Set ELDLOCK_GO_PATH=/path/to/go if Go is installed outside PATH." >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
case "$(uname -s)" in
|
|
27
|
+
Darwin|Linux) ;;
|
|
28
|
+
*)
|
|
29
|
+
echo "eldlock install: only macOS and Linux are supported right now" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
;;
|
|
32
|
+
esac
|
|
33
|
+
|
|
34
|
+
tmp="$(mktemp -d)"
|
|
35
|
+
cleanup() {
|
|
36
|
+
rm -rf "$tmp"
|
|
37
|
+
}
|
|
38
|
+
trap cleanup EXIT
|
|
39
|
+
|
|
40
|
+
archive="$tmp/eldlock.tar.gz"
|
|
41
|
+
src="$tmp/src"
|
|
42
|
+
mkdir -p "$src"
|
|
43
|
+
|
|
44
|
+
url="https://api.github.com/repos/$repo/tarball/$ref"
|
|
45
|
+
echo "Downloading eldlock from $repo@$ref"
|
|
46
|
+
|
|
47
|
+
if [ -n "${GITHUB_TOKEN:-}" ]; then
|
|
48
|
+
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" "$url" -o "$archive"
|
|
49
|
+
else
|
|
50
|
+
curl -fsSL "$url" -o "$archive"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
tar -xzf "$archive" -C "$src" --strip-components=1
|
|
54
|
+
|
|
55
|
+
cd "$src"
|
|
56
|
+
node scripts/build-production.mjs --install-dir "$install_dir" --bin-dir "$bin_dir"
|
|
57
|
+
|
|
58
|
+
echo ""
|
|
59
|
+
echo "Eldlock installed."
|
|
60
|
+
echo ""
|
|
61
|
+
echo "Add this to your shell profile if it is not already there:"
|
|
62
|
+
echo " export PATH=\"$bin_dir:\$PATH\""
|
|
63
|
+
echo ""
|
|
64
|
+
echo "Then run:"
|
|
65
|
+
echo " eldlock start"
|
|
66
|
+
echo " eldlock init"
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reegaviljoen/eldlock",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"bin": {
|
|
6
|
+
"eldlock": "bin/eldlock"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"README.md",
|
|
10
|
+
"install.sh",
|
|
11
|
+
"bin/",
|
|
12
|
+
"docs/",
|
|
13
|
+
"examples/",
|
|
14
|
+
"scripts/build-production.mjs",
|
|
15
|
+
"scripts/postinstall-production.mjs",
|
|
16
|
+
"eldlock-cli/bin/",
|
|
17
|
+
"eldlock-cli/package.json",
|
|
18
|
+
"eldlock-cli/package-lock.json",
|
|
19
|
+
"eldlock-cli/src/",
|
|
20
|
+
"eldlock-cli/tsconfig.json",
|
|
21
|
+
"eldlock-cli/vendor/npm/ansi-regex-6.2.2.tgz",
|
|
22
|
+
"eldlock-cli/vendor/npm/bun-ffi-structs-0.2.2.tgz",
|
|
23
|
+
"eldlock-cli/vendor/npm/diff-9.0.0.tgz",
|
|
24
|
+
"eldlock-cli/vendor/npm/emoji-regex-10.6.0.tgz",
|
|
25
|
+
"eldlock-cli/vendor/npm/esbuild-0.28.0.tgz",
|
|
26
|
+
"eldlock-cli/vendor/npm/esbuild-darwin-arm64-0.28.0.tgz",
|
|
27
|
+
"eldlock-cli/vendor/npm/esbuild-darwin-x64-0.28.0.tgz",
|
|
28
|
+
"eldlock-cli/vendor/npm/esbuild-linux-arm64-0.28.0.tgz",
|
|
29
|
+
"eldlock-cli/vendor/npm/esbuild-linux-x64-0.28.0.tgz",
|
|
30
|
+
"eldlock-cli/vendor/npm/fsevents-2.3.3.tgz",
|
|
31
|
+
"eldlock-cli/vendor/npm/get-east-asian-width-1.6.0.tgz",
|
|
32
|
+
"eldlock-cli/vendor/npm/marked-17.0.1.tgz",
|
|
33
|
+
"eldlock-cli/vendor/npm/opentui-core-0.3.1.tgz",
|
|
34
|
+
"eldlock-cli/vendor/npm/opentui-core-darwin-arm64-0.3.1.tgz",
|
|
35
|
+
"eldlock-cli/vendor/npm/opentui-core-darwin-x64-0.3.1.tgz",
|
|
36
|
+
"eldlock-cli/vendor/npm/opentui-core-linux-arm64-0.3.1.tgz",
|
|
37
|
+
"eldlock-cli/vendor/npm/opentui-core-linux-x64-0.3.1.tgz",
|
|
38
|
+
"eldlock-cli/vendor/npm/string-width-7.2.0.tgz",
|
|
39
|
+
"eldlock-cli/vendor/npm/strip-ansi-7.1.2.tgz",
|
|
40
|
+
"eldlock-cli/vendor/npm/tsx-4.22.4.tgz",
|
|
41
|
+
"eldlock-cli/vendor/npm/types-node-22.19.19.tgz",
|
|
42
|
+
"eldlock-cli/vendor/npm/typescript-5.9.3.tgz",
|
|
43
|
+
"eldlock-cli/vendor/npm/undici-types-6.21.0.tgz",
|
|
44
|
+
"eldlock-cli/vendor/npm/web-tree-sitter-0.25.10.tgz",
|
|
45
|
+
"eldlock-cli/vendor/npm/yoga-layout-3.2.1.tgz",
|
|
46
|
+
"eldlock-server/cmd/",
|
|
47
|
+
"eldlock-server/go.mod",
|
|
48
|
+
"eldlock-server/go.sum",
|
|
49
|
+
"eldlock-server/internal/",
|
|
50
|
+
"eldlock-server/vendor/"
|
|
51
|
+
],
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "npm --prefix eldlock-cli run build --",
|
|
57
|
+
"build:prod": "node scripts/build-production.mjs",
|
|
58
|
+
"build:dev": "npm --prefix eldlock-cli run build:dev --",
|
|
59
|
+
"dev": "npm --prefix eldlock-cli run dev --",
|
|
60
|
+
"install:local": "node scripts/build-production.mjs --install-dir \"$HOME/.local/share/eldlock\" --bin-dir \"$HOME/.local/bin\"",
|
|
61
|
+
"install:vendored": "npm --prefix eldlock-cli run install:vendored",
|
|
62
|
+
"postinstall": "node scripts/postinstall-production.mjs",
|
|
63
|
+
"release": "node scripts/release.mjs",
|
|
64
|
+
"typecheck": "npm --prefix eldlock-cli run typecheck"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
|
|
8
|
+
const repoDir = path.resolve(import.meta.dirname, "..");
|
|
9
|
+
const serverDir = path.join(repoDir, "eldlock-server");
|
|
10
|
+
const cliDir = path.join(repoDir, "eldlock-cli");
|
|
11
|
+
const installDir = path.resolve(valueAfter("--install-dir") ?? path.join(repoDir, "dist", "eldlock"));
|
|
12
|
+
const binDir = path.resolve(valueAfter("--bin-dir") ?? path.join(installDir, "bin"));
|
|
13
|
+
const runtimeDir = path.join(installDir, "lib", "eldlock-cli");
|
|
14
|
+
const serverBin = path.join(installDir, "bin", "eldlock-server");
|
|
15
|
+
const commandPath = path.join(binDir, "eldlock");
|
|
16
|
+
|
|
17
|
+
assertPlatform();
|
|
18
|
+
const goBin = resolveCommand("go", process.env.ELDLOCK_GO_PATH);
|
|
19
|
+
resolveCommand("node", process.env.ELDLOCK_NODE_PATH);
|
|
20
|
+
const npmBin = resolveCommand("npm", process.env.ELDLOCK_NPM_PATH);
|
|
21
|
+
const env = buildEnv();
|
|
22
|
+
|
|
23
|
+
console.log("==> installing vendored npm dependencies");
|
|
24
|
+
run(npmBin, ["ci", "--offline"], { cwd: cliDir, env });
|
|
25
|
+
|
|
26
|
+
console.log("==> building eldlock-cli");
|
|
27
|
+
run(path.join(cliDir, "node_modules", ".bin", "tsc"), [], { cwd: cliDir, env });
|
|
28
|
+
|
|
29
|
+
console.log("==> building eldlock-server");
|
|
30
|
+
fs.mkdirSync(path.dirname(serverBin), { recursive: true });
|
|
31
|
+
run(goBin, ["build", "-mod=vendor", "-o", serverBin, "./cmd/eldlock-server"], { cwd: serverDir, env });
|
|
32
|
+
|
|
33
|
+
console.log("==> installing runtime files");
|
|
34
|
+
fs.rmSync(runtimeDir, { recursive: true, force: true });
|
|
35
|
+
fs.mkdirSync(runtimeDir, { recursive: true });
|
|
36
|
+
copyDir(path.join(cliDir, "dist"), path.join(runtimeDir, "dist"));
|
|
37
|
+
copyDir(path.join(cliDir, "node_modules"), path.join(runtimeDir, "node_modules"));
|
|
38
|
+
fs.copyFileSync(path.join(cliDir, "package.json"), path.join(runtimeDir, "package.json"));
|
|
39
|
+
|
|
40
|
+
console.log("==> installing eldlock command");
|
|
41
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
42
|
+
fs.writeFileSync(commandPath, commandScript({ installDir }), { mode: 0o755 });
|
|
43
|
+
fs.chmodSync(serverBin, 0o755);
|
|
44
|
+
|
|
45
|
+
console.log("");
|
|
46
|
+
console.log(`Installed eldlock to ${commandPath}`);
|
|
47
|
+
console.log(`Installed eldlock-server to ${serverBin}`);
|
|
48
|
+
console.log("");
|
|
49
|
+
console.log("Make sure this is on PATH:");
|
|
50
|
+
console.log(` export PATH="${binDir}:$PATH"`);
|
|
51
|
+
|
|
52
|
+
function valueAfter(flag) {
|
|
53
|
+
const index = process.argv.indexOf(flag);
|
|
54
|
+
if (index < 0) return undefined;
|
|
55
|
+
const value = process.argv[index + 1];
|
|
56
|
+
if (!value || value.startsWith("--")) {
|
|
57
|
+
throw new Error(`${flag} requires a value`);
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function assertPlatform() {
|
|
63
|
+
if (!["darwin", "linux"].includes(process.platform)) {
|
|
64
|
+
throw new Error(`production install supports macOS and Linux, got ${process.platform}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function buildEnv() {
|
|
69
|
+
const env = { ...process.env };
|
|
70
|
+
if (!env.ELDLOCK_PASSKEY_PROVIDER) {
|
|
71
|
+
env.ELDLOCK_PASSKEY_PROVIDER = env.ELDLOCK_STUB_PASSKEY ? "stub" : "fido";
|
|
72
|
+
}
|
|
73
|
+
if (env.ELDLOCK_PASSKEY_PROVIDER === "fido") {
|
|
74
|
+
requireLibfido2();
|
|
75
|
+
env.GOFLAGS = addGoBuildTag(env.GOFLAGS, "fido2");
|
|
76
|
+
env.PKG_CONFIG_PATH = [
|
|
77
|
+
env.PKG_CONFIG_PATH,
|
|
78
|
+
"/opt/homebrew/opt/libfido2/lib/pkgconfig",
|
|
79
|
+
"/opt/homebrew/opt/openssl@3/lib/pkgconfig",
|
|
80
|
+
].filter(Boolean).join(":");
|
|
81
|
+
}
|
|
82
|
+
return env;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function requireLibfido2() {
|
|
86
|
+
const result = spawnSync("pkg-config", ["--exists", "libfido2"], { encoding: "utf8" });
|
|
87
|
+
if (result.status !== 0) {
|
|
88
|
+
const hint = process.platform === "darwin"
|
|
89
|
+
? "Install libfido2 with: brew install keys-pub/tap/libfido2"
|
|
90
|
+
: "Install libfido2 development headers with your package manager, for example: sudo apt install libfido2-dev pkg-config";
|
|
91
|
+
throw new Error(`libfido2 was not found by pkg-config. ${hint}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function resolveCommand(name, configured) {
|
|
96
|
+
if (configured) {
|
|
97
|
+
if (isExecutable(configured)) return configured;
|
|
98
|
+
throw new Error(`${name} path is not executable: ${configured}`);
|
|
99
|
+
}
|
|
100
|
+
for (const dir of (process.env.PATH ?? "").split(path.delimiter)) {
|
|
101
|
+
if (!dir) continue;
|
|
102
|
+
const candidate = path.join(dir, name);
|
|
103
|
+
if (isExecutable(candidate)) return candidate;
|
|
104
|
+
}
|
|
105
|
+
if (name === "go") {
|
|
106
|
+
const miseGo = findMiseGo();
|
|
107
|
+
if (miseGo) return miseGo;
|
|
108
|
+
}
|
|
109
|
+
throw new Error(`could not find ${name} on PATH`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function findMiseGo() {
|
|
113
|
+
const installsDir = path.join(process.env.HOME ?? "", ".local", "share", "mise", "installs", "go");
|
|
114
|
+
if (!fs.existsSync(installsDir)) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
const versions = fs.readdirSync(installsDir).sort().reverse();
|
|
118
|
+
for (const version of versions) {
|
|
119
|
+
const candidate = path.join(installsDir, version, "bin", "go");
|
|
120
|
+
if (isExecutable(candidate)) {
|
|
121
|
+
return candidate;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isExecutable(candidate) {
|
|
128
|
+
try {
|
|
129
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
130
|
+
return fs.statSync(candidate).isFile();
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function addGoBuildTag(goFlags = "", tag) {
|
|
137
|
+
if (goFlags.includes(`-tags=${tag}`) || goFlags.includes(`-tags ${tag}`)) {
|
|
138
|
+
return goFlags;
|
|
139
|
+
}
|
|
140
|
+
return `${goFlags} -tags=${tag}`.trim();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function run(command, args, options) {
|
|
144
|
+
const result = spawnSync(command, args, {
|
|
145
|
+
cwd: options.cwd,
|
|
146
|
+
env: options.env,
|
|
147
|
+
stdio: "inherit",
|
|
148
|
+
});
|
|
149
|
+
if (result.status !== 0) {
|
|
150
|
+
throw new Error(`${command} ${args.join(" ")} exited with ${result.status}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function copyDir(from, to) {
|
|
155
|
+
fs.rmSync(to, { recursive: true, force: true });
|
|
156
|
+
fs.cpSync(from, to, { recursive: true });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function commandScript({ installDir }) {
|
|
160
|
+
return `#!/usr/bin/env sh
|
|
161
|
+
set -eu
|
|
162
|
+
export ELDLOCK_SERVER_PATH="${installDir}/bin/eldlock-server"
|
|
163
|
+
export ELDLOCK_STATE_DIR="\${ELDLOCK_STATE_DIR:-$HOME/.eldlock}"
|
|
164
|
+
if [ "\${1:-}" = "" ] || [ "\${1:-}" = "tui" ]; then
|
|
165
|
+
if [ -n "\${ELDLOCK_TUI_RUNTIME:-}" ]; then
|
|
166
|
+
exec "\$ELDLOCK_TUI_RUNTIME" "${installDir}/lib/eldlock-cli/dist/main.js" "\$@"
|
|
167
|
+
fi
|
|
168
|
+
if command -v bun >/dev/null 2>&1; then
|
|
169
|
+
exec bun "${installDir}/lib/eldlock-cli/dist/main.js" "\$@"
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
if [ -n "\${ELDLOCK_NODE_PATH:-}" ]; then
|
|
173
|
+
exec "\$ELDLOCK_NODE_PATH" "${installDir}/lib/eldlock-cli/dist/main.js" "\$@"
|
|
174
|
+
fi
|
|
175
|
+
exec node "${installDir}/lib/eldlock-cli/dist/main.js" "\$@"
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
|
|
7
|
+
const packageDir = path.resolve(import.meta.dirname, "..");
|
|
8
|
+
const installDir = path.join(packageDir, ".eldlock-runtime");
|
|
9
|
+
const binDir = path.join(packageDir, "bin");
|
|
10
|
+
|
|
11
|
+
const result = spawnSync(
|
|
12
|
+
"node",
|
|
13
|
+
["scripts/build-production.mjs", "--install-dir", installDir, "--bin-dir", binDir],
|
|
14
|
+
{
|
|
15
|
+
cwd: packageDir,
|
|
16
|
+
env: process.env,
|
|
17
|
+
stdio: "inherit",
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
if (result.status !== 0) {
|
|
22
|
+
throw new Error(`build production runtime exited with ${result.status}`);
|
|
23
|
+
}
|